Issue
I am on a project that I recently added a new feature and accompanying unit tests to. The structure of the project looks like this:
Sim
└── models
├── otherModels
└── myModel
├── CMakeLists.txt
├── model.cpp
├── model.h
├── test
│ ├── CMakeLists.txt
│ ├── geometryTest.cpp
│ └── geometryTest.h
└── util
├── CMakeLists.txt
├── geometry.cpp
├── geometry.h
└── probability.h
Summarize the problem
My goal is to be able to build said project and execute the unit tests both on windows and linux.
Actual Results
On Windows, I am able to build the project and run the unit tests just fine.
On linux, I get an error while building saying that the library being tested cannot be found:
/home/e40056742/projects/xxxx/build/make-Release/Sim/bin/modelTests: error while loading shared libraries: libmathUtil.so: cannot open shared object file: No such file or directory
CMake Error at /projs/xxxx/cmake-3.18.2/share/cmake-3.18/Modules/GoogleTestAddTests.cmake:77 (message):
Error running test executable.
Path: '/home/e40056742/projects/xxxx/build/make-Release/Sim/bin/modelTests'
Result: 127
Output:
Call Stack (most recent call first):
/projs/xxxx/cmake-3.18.2/share/cmake-3.18/Modules/GoogleTestAddTests.cmake:173 (gtest_discover_tests_impl)
make[2]: *** [Sim/bin/modelTests] Error 1
make[2]: *** Deleting file `Sim/bin/modelTests'
make[1]: *** [Sim/src/models/myModel/test/CMakeFiles/modelTests.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
Expected Results
To be able to build on linux.
What I've tried
I googled how to find a file on linux and verified that the file exists:
find . -name libmathUtil.so >>> ./build/make-Release/Sim/lib/libmathUtil.so
I verified that everything builds on linux if I comment out
add_subdirectory(test)
I tried the solution posted here to a different (but I hoped related) question: How to add linker directories to cmake gtest_discover_tests
I added CMake RPATH settings to my test directory's CMakeLists.txt, as suggested in another answer: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath
I tried to correctly set up the CMakeLists.txt files like so:
Top level CMake for my working directory
set( target myCoolModel )
set( sources
model.cpp
)
set( headers
model.h
)
add_library( ${target} ${sources} ${headers} )
target_link_libraries( ${target}
${PROJECT_LIBRARIES}
mathUtil
)
# Group the target library into an IDE folder
set_target_properties( ${target} PROPERTIES FOLDER ${PROJECT_FOLDER} )
# Add the utility and test subdirectory
enable_testing()
add_subdirectory( util )
add_subdirectory( test )
And in the util
sub-directory:
set( target mathUtil)
set( sources geometry.cpp )
set( headers geometry.h probability.h)
add_library( ${target} ${sources} ${headers} )
target_link_libraries( ${target} genMath::genMath )
target_compile_options( ${target} PRIVATE ${PROJECT_CXX_FLAGS} )
target_include_directories( ${target}
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} )
And the test
directory:
enable_testing()
# Set target name, and dependencies
set( target modelTests )
set( sources geometryTest.cpp )
set( headers geometryTest.h)
# Find Google Test
find_package( GTest REQUIRED )
# Make test executable.
add_executable( ${target} ${sources} ${headers} )
target_link_libraries(${target}
mathUtil
GTest::gtest_main
)
# Load GoogleTest and add these tests to the suite
include(GoogleTest)
gtest_discover_tests(${target})
# File unit tests into the project test folder (makes it easier to find in solution explorer)
set( PROJECT_FOLDER ${PROJECT_FOLDER}/test )
set_target_properties( ${target} PROPERTIES FOLDER ${PROJECT_FOLDER} )
Solution
It is likely failing because gtest_discover_tests
by default actually runs the test executable at build stage to get the list of tests available. The environment, especially $LD_LIBRARY_PATH
, may not be properly set up for the executable to run.
If nothing else works, and you have CMake>=3.18, you can delay the discovery of tests (exclude from the build stage), by setting the DISCOVERY_MODE
to PRE_TEST
. Quote from the document:
DISCOVERY_MODE
New in version 3.18.
Provides greater control over when gtest_discover_tests() performs test discovery. By default, POST_BUILD sets up a post-build command to perform test discovery at build time. In certain scenarios, like cross-compiling, this POST_BUILD behavior is not desirable. By contrast, PRE_TEST delays test discovery until just prior to test execution. This way test discovery occurs in the target environment where the test has a better chance at finding appropriate runtime dependencies.
I actually tried to get the test discovery run in POST_BUILD
mode by setting the correct environment variables using the PROPERTIES
of google_discover_tests
, but nothing worked and after examining the source code of GoogleTest
cmake module itself I have concluded that it is not possible to set up the environment variables for the shell session where google_discover_tests
runs the test executable on your behalf.
The only other feasible alternative without using PRE_TEST
is to setup the RPATH
for the executable properly so that it can find the dependencies all by its own. However conventionally in CMake the RPATH
should be set in the install stage rather than build stage. Also extracting the correct paths from a find_package
call and then adding them to the RPATH
is not always trivial and is error-prone. That's why I ended up with PRE_TEST
even though I really prefer POST_BUILD
if it can be done in a simple way.
Answered By - Weijun Zhou Answer Checked By - David Marino (WPSolving Volunteer)