I have a library that has many different cpp test files.
They look like this, A.cpp
, B.cpp
, C.pp
, etc.
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) {
...
}
I have the following Cmake code to compile and test
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()
The problem with this is that each test takes sometime to compile. I know that if I merge all these cpp files it will compile much faster overall.
(For example, I have 40 cpp test files, each takes 10 second, total compilation is 400 second. If I merge all it takes perhaps 20 seconds. Even if I could parallelize the build of individual files a 20x factor is hard to achieve. Obviously the compiler is doing a lot of repeated work. Supposedly gch pre-compiled headers would help but I never figured out how to use them with cmake).
Is there a way to force the compilation of these test to work on a single merged file?
I tried replacing the loop with this:
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 )
However, it still has two problems: First, it is still slow to compile because each cpp is used to generate individual .o files (later link into the same executable). The second problem is that each .o will contain a main function (defined by Boost.Test) and therefore there will be a linker error.
Is there a change I can make to make to compile several cpp files as if it was a single cpp file? (I would like to avoid generating a temporary merged file manually) Is there a Boost.Test flag that can help me with this?
I would like a solution where each test source file can still be compiled into its own executable.
As an illustration, I was able to do this process to obtain a single file.
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
You can do the
#define BOOST_TEST_MODULE "C++ Unit Tests <...>"
s via thetarget_compile_definitions
command, which is useful here since you want to give the user the option of whether to build tests to individual executables, or one single executable together.For using precompiled headers in CMake, see the
target_precompile_headers
command. But what you're looking for with concatenating source files to compile as if they were a single source file is a thing and has a name: "Unity Build". CMake even comes with a feature to support it: itsUNITY_BUILD
target property. Do read the docs and learn about how it is suggested to be used properly, and what general caveats there are with respect to possible ODR violations when using the unity build technique with any build tool.The solution might look something like this (in block quotes because it's mostly taken from your question post and what you wrote in chat):
Note that
file(GLOB)
is discouraged for use by the maintainers of CMake, and by various long-time users of CMake on Stack Overflow, as seen here and here. You can instead define the list of files manually likeset(TEST_SRCS A.cpp B.cpp C.cpp ...)
.Also note that if you use
file(GLOB)
, the order files in the result variable in unspecified for versions older than CMake v3.6, and specified as lexicographical for versions 3.6 and above. (see thefile(GLOB)
docs), which has relevance to CMake's unity build feature. Quoting from theUNITY_BUILD
docs:You may also be interested in
create_test_sourcelist
, which links many tests into a single executable.