我有一个包含许多不同 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
您可以通过
#define BOOST_TEST_MODULE "C++ Unit Tests <...>"
命令执行
target_compile_definitions
操作,这在这里很有用,因为您想为用户提供是否对单个可执行文件或单个可执行文件一起构建测试的选项。
这样做的问题是每个测试都需要一些时间来编译。我知道如果我合并所有这些 cpp 文件,总体编译速度会快得多。 [...] 据说 gch 预编译标头会有所帮助,但我从未弄清楚如何将它们与 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()
等命令添加到目标的源文件的顺序将保留在生成的 Unity 源文件中。这可用于根据target_sources()
目标属性手动强制执行特定分组。UNITY_BUILD_BATCH_SIZE
感兴趣,它将许多测试链接到一个可执行文件中。