使用 CMake 将多个测试源文件编译为单个合并文件

问题描述 投票:0回答:1

我有一个包含许多不同 cpp 测试文件的库。 它们看起来像这样,

A.cpp
B.cpp
C.pp
等等

A.cpp:

#define BOOST_TEST_MODULE "C++ Unit Tests A"
#include<boost/test/unit_test.hpp>

#include <multi/array.hpp>

BOOST_AUTO_TEST_CASE(test_case_A1) {
...
}

B.cpp:

#define BOOST_TEST_MODULE "C++ Unit Tests B"
#include<boost/test/unit_test.hpp>

#include <multi/array.hpp>

BOOST_AUTO_TEST_CASE(test_case_B1) {
...
}

我有以下 Cmake 代码要编译和测试

include(CTest)

file(
    GLOB TEST_SRCS
    RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
    *.cpp
)

foreach(TEST_FILE ${TEST_SRCS})
    set(TEST_EXE "${TEST_FILE}.x")
    add_executable(${TEST_EXE} ${TEST_FILE})

    target_link_libraries(${TEST_EXE} PRIVATE multi)
    target_include_directories(${TEST_EXE}        PRIVATE ${PROJECT_SOURCE_DIR}/include)
    target_include_directories(${TEST_EXE} SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}        )
    target_link_libraries     (${TEST_EXE}        PRIVATE Boost::unit_test_framework   )
endforeach()

这样做的问题是每个测试都需要一些时间来编译。 我知道如果我合并所有这些 cpp 文件,总体编译速度会更快。

(例如,我有 40 个 cpp 测试文件,每个需要 10 秒,总编译时间为 400 秒。如果我合并所有文件,可能需要 20 秒。即使我可以并行化单个文件的构建,也很难实现 20 倍的因子.显然编译器做了很多重复的工作。据说 gch 预编译头会有所帮助,但我从来没有弄清楚如何在 cmake 中使用它们)。

有没有办法强制这些测试的编译在单个合并文件上工作?

我尝试用这个替换循环:

add_executable(multi_test ${TEST_SRCS})
add_test(NAME multi_test COMMAND ./multi_test)
target_include_directories(multi_test        PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_include_directories(multi_test SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}        )
target_link_libraries     (multi_test        PRIVATE Boost::unit_test_framework   )

但是,它仍然存在两个问题: 首先,编译仍然很慢,因为每个 cpp 都用于生成单独的 .o 文件(稍后链接到相同的可执行文件)。 第二个问题是每个 .o 将包含一个 main 函数(由 Boost.Test 定义),因此会出现链接器错误。

我可以进行更改来编译多个 cpp 文件,就好像它是单个 cpp 文件一样吗? (我想避免手动生成临时合并文件) 有 Boost.Test 标志可以帮助我解决这个问题吗?

我想要一个解决方案,其中每个测试源文件仍然可以编译成自己的可执行文件。


作为示例,我能够执行此过程来获取单个文件。

echo '#define BOOST_TEST_MODULE "C++ Unit Tests for Multi, All in one"' > ../test/all.cpp  # Boost Test need a module name
cat ../test/*.cpp | grep -v BOOST_TEST_MODULE >> ../test/all.cpp  # filter macros to avoid warnings
c++ boost cmake ctest boost.test
1个回答
0
投票

您可以通过

#define BOOST_TEST_MODULE "C++ Unit Tests <...>" 命令
执行
target_compile_definitions
操作,这在这里很有用,因为您想为用户提供是否对单个可执行文件或单个可执行文件一起构建测试的选项。

这样做的问题是每个测试都需要一些时间来编译。我知道如果我合并所有这些 cpp 文件,总体编译速度会快得多。 [...] 据说 gch 预编译标头会有所帮助,但我从未弄清楚如何将它们与 CMake 一起使用)。

有关在 CMake 中使用预编译头的信息,请参阅

target_precompile_headers
命令。但是,您正在寻找的连接源文件进行编译的方法,就好像它们是单个源文件一样,并且有一个名称:“Unity Build”。 CMake 甚至还提供了支持它的功能:
UNITY_BUILD
目标属性
。请阅读文档并了解建议如何正确使用它,以及在将统一构建技术与任何构建工具一起使用时可能出现的 ODR 违规的一般注意事项。

解决方案可能看起来像这样(用引号引起来,因为它主要取自您的问题帖子和您在聊天中写的内容

include(CTest)

file(
    GLOB TEST_SRCS
    RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
    *.cpp
)

option(
    TEST_UNITY_BUILD
    "whether to create a single executable for all tests (instead of one per test source file)"
    <default> # replace <default> with desired default value
)

if("${TEST_UNITY_BUILD}")
    foreach(TEST_FILE ${TEST_SRCS})
        set(TEST_EXE "${TEST_FILE}.x")
        add_executable(${TEST_EXE} ${TEST_FILE})

        target_link_libraries(${TEST_EXE} PRIVATE multi)
        target_include_directories(${TEST_EXE}        PRIVATE ${PROJECT_SOURCE_DIR}/include)
        target_include_directories(${TEST_EXE} SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}        )
        target_link_libraries     (${TEST_EXE}        PRIVATE Boost::unit_test_framework   )
        target_compile_definitions(${TEST_EXE}        PRIVATE BOOST_TEST_MODULE="C++ Unit Tests for ${TEST_FILE}")
    endforeach()
else()
    add_executable(multi_test ${TEST_SRCS})
    set_property(TARGET multi_test PROPERTY UNITY_BUILD ON)

    target_include_directories(multi_test PRIVATE ${PROJECT_SOURCE_DIR}/include)
    target_include_directories(multi_test SYSTEM PRIVATE ${Boost_INCLUDE_DIRS} )
    target_link_libraries     (multi_test PRIVATE Boost::unit_test_framework )
    target_compile_definitions(multi_test PRIVATE BOOST_TEST_MODULE="C++ Unit Tests for Multi GLOBAL TEST")

    add_test(NAME multi_test COMMAND ./multi_test)
endif()

请注意,CMake

 的维护者以及 Stack Overflow 上的各种 CMake 长期用户不鼓励使用 
file(GLOB),如此处此处所示。您可以手动定义文件列表,如 set(TEST_SRCS A.cpp B.cpp C.cpp ...)

另请注意,如果您使用

file(GLOB)

,则对于 CMake v3.6 之前的版本,结果变量中的顺序文件未指定,而对于 3.6 及更高版本,则指定为字典顺序。 (参见 
file(GLOB) 文档
),它与 CMake 的统一构建功能相关。引用自
UNITY_BUILD文档

通过
add_library()

add_executable()
target_sources()
等命令添加到目标的源文件的顺序将保留在生成的 Unity 源文件中。这可用于根据
UNITY_BUILD_BATCH_SIZE
目标属性手动强制执行特定分组。

您可能还对

create_test_sourcelist

 感兴趣,它将许多测试链接到一个可执行文件中。

© www.soinside.com 2019 - 2024. All rights reserved.