CMake and GTest linking tests - c++

I want build and test a small project of mine with cmake and gtest. In the project I have a function readModel(...) where the definition and implementation is separated. The project builds fine but I get the following linker error for the test of readModel(...)
cd /home/kain88/Desktop/example/build/test && /usr/bin/cmake -E cmake_link_script CMakeFiles/example_test.dir/link.txt --verbose=1
/usr/bin/c++ -Wall -std=c++14 CMakeFiles/example_test.dir/pdb_test.cpp.o -o example_test -rdynamic ../ext/gtest-1.7.0/libgtest.a ../ext/gtest-1.7.0/libgtest_main.a ../ext/gtest-1.7.0/libgtest.a -lpthread
CMakeFiles/example_test.dir/pdb_test.cpp.o: In function `PDB_TEST_readModel_Test::TestBody()':
pdb_test.cpp:(.text+0x13): undefined reference to `readModel(std::string const&)'
collect2: error: ld returned 1 exit status
It seems that cmake is not including the *.o file generated in the src folder for the test. How can I tell cmake that it should include the *.o files from the src folder also for the test?
This is the CMakeLists.txt I use in the test folder.
# enable GTest
enable_testing()
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/src)
# Unit Test
set(PROJECT_TEST_NAME ${PROJECT_NAME_STR}_test)
file(GLOB TEST_SOURCES "*.cpp")
add_executable(${PROJECT_TEST_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_TEST_NAME} gtest gtest_main)
add_test(test ${PROJECT_TEST_NAME})
A complete striped down example case can be found here

You may create an object library target that compiles the .o object files:
ADD_LIBRARY(${PROJECT_NAME}_objects OBJECT ${SRC_FILES} ${INCL_FILES})
Use the result of it on both your lib/exe and test:
ADD_EXECUTABLE(${PROJECT_NAME} $<TARGET_OBJECTS:${PROJECT_NAME}_objects>)
ADD_EXECUTABLE(${PROJECT_TEST_NAME} ${TEST_SOURCES} $<TARGET_OBJECTS:${PROJECT_NAME}_objects>)
PS: You can use the same trick to compile the objects shared by multiple targets only once, for example, a shared and a static library target.

Related

Disable automatic archive generation for executable target in CMake

I have a C++ project that I want to build using CMake with Makefile Generators. I created a simple CMakeLists that only contains executable and directory configuration. The project was built and run successfully.
cmake_minimum_required(VERSION 3.17)
project(proj)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
add_executable(proj
${PROJECT_SOURCE_DIR}/catch2/catch.cpp
${PROJECT_SOURCE_DIR}/test.cpp
)
The linking process generates an archive file under the executable target folder CMakeFiles/proj.dir. The auto-generated link.txt file that is used in the link process contains these lines.
"cmake.exe" -E rm -f CMakeFiles\proj.dir/objects.a
ar.exe cr CMakeFiles\proj.dir/objects.a #CMakeFiles\proj.dir\objects1.rsp
g++.exe -g -Wl,--whole-archive CMakeFiles\proj.dir/objects.a -Wl,--no-whole-archive -o ..\bin\proj.exe -Wl,--out-implib,libproj.dll.a -Wl,--major-image-version,0,--minor-image-version,0 #CMakeFiles\proj.dir\linklibs.rsp
As far as I understand, by default CMake generates an archive and then creates an executable from that archive. Is there a way to simplify this step? I just want to link objects as an executable not generate an additional archive file. The expected link.txt file should be like that:
g++.exe -g -o ..\bin\proj.exe #CMakeFiles\proj.dir\objects1.rsp

Is it possible to force CMake to reorder arguments when calling the linker? [duplicate]

This question already has an answer here:
Flag '-l' in CMAKE_CXX_FLAGS doesn't work
(1 answer)
Closed 4 years ago.
I have a strange behavior where CMake is not able to link any executable if dynamic linking is required. g++ is not able to find any of the symbols defined in the libraries.
I found out it might have to do with the order of the arguments CMake passes to g++ when linking.
Here is the verbose output of the build process (which fails linking):
[ 50%] Building CXX object CMakeFiles/chargen.dir/test.cpp.obj
g++ -std=c++11 -o CMakeFiles/test.dir/test.cpp.obj -c /f/test/test.cpp
[100%] Linking CXX executable mytest
/usr/bin/cmake -E cmake_link_script CMakeFiles/test.dir/link.txt --verbose=1
g++ -std=c++11 -lSDL2 CMakeFiles/test.dir/test.cpp.obj -o mytest
Indeed if I try to link using that command I get undefined references, however if I compile with the library flags at the end:
g++ -std=c++11 CMakeFiles/test.dir/test.cpp.obj -o mytest -lSDL2
it runs just fine. How can I force CMake to use that order of arguments instead?
The contents of the CMakeLists.txt:
cmake_minimum_required(VERSION 3.6)
project(mytest)
set(COMPILER_PATH "/usr/bin/")
set(CMAKE_MAKE_PROGRAM "make")
set(CMAKE_C_COMPILER "gcc")
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lSDL2")
set(SOURCE_FILES test.cpp)
add_executable(mytest ${SOURCE_FILES})
You have probably added -lSDL2 into CMAKE_CXX_FLAGS. In that case it will appear withn compile/link flags, in front of source file name in actual link command, as you showed. Don't do that.
You should use _target_link_libraries_ cmake command in order to define libraries to link against. In brief, the skeleton of your CMakeLists.txt should look like this:
project (project_name)
add_executable (mytest test.cpp)
target_link_libraries( mytest SDL2)
In this case, cmake will put libraries on correct place. Note that you may use target_link_libraries after adding target mytest
[EDIT]
After seeing your CMakeLists it is obvious that your problem is in wrong CMAKE_EXE_LINKER_FLAGS. Simply delete the line where you are setting it, and add target_link_libraries after add_executable.
Regarding your question about handling circular dependencies among static libs, you handle it the sam way as you would if you didn't use cmake: mention it twice:
target_link_libraries(
mytest
circular_dep1
circular_dep2
circular_dep1
circular_dep2
)
regarding your question about specific linker flags, just place them into target_link_libraries command, it accepts linker flags as well. You can find documentation on following link:
https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Avoid automatically added "lib" prefix when using target_link_libraries

I'm trying to link a C++ project to the RCpp library; the file is called Rcpp.so, not the linux-default libRcpp.so. Furthermore, the library resides at the non-standard location /usr/lib/R/site-library/Rcpp/libs.
So I tried using a combination of find_library and target_link_libraries:
cmake_minimum_required(VERSION 3.8)
project("R-Tests")
find_library(RCPP
NAMES Rcpp.so
HINTS /usr/lib/R/site-library/Rcpp/libs
)
if (NOT RCPP)
message(FATAL_ERROR "Could not find Rcpp - exiting.")
else()
message("Found Rcpp: " ${RCPP})
endif()
# test target
add_executable(rcpptest main.cpp)
target_link_libraries(rcpptest ${RCPP})
Configuring works fine, CMake outputs:
Found Rcpp: /usr/lib/R/site-library/Rcpp/libs/Rcpp.so
However, during build, CMake passes -lRcpp to the compiler, which causes the compilation to fail, since the library file is not named libRcpp.so but instead Rcpp.so:
[100%] Linking CXX executable rcpptest
/usr/bin/cmake -E cmake_link_script CMakeFiles/rcpptest.dir/link.txt --verbose=1
c++ CMakeFiles/rcpptest.dir/main.cpp.o -o rcpptest -L/usr/lib/R/site-library/Rcpp/libs -Wl,-rpath,/usr/lib/R/site-library/Rcpp/libs -lRcpp
/usr/bin/ld: cannot find -lRcpp
collect2: error: ld returned 1 exit status
Since the message line prints the full path to the Rcpp.so file just fine, is there any way to let target_link_libraries just add this path to the compiler instead of a combination of -L and -l?
According to this question, this should be disabled by adding cmake_policy(SET CMP0060 NEW); however, I can't see any change in the behavior of CMake if I set this to NEW or OLD.
You may have been bitten by the OLD (default) behavior of CMP0060, which converts absolute paths back to -lfoo.
Alternatively, define and use an IMPORTED target:
add_library(Rcpp SHARED IMPORTED)
set_property(TARGET Rcpp PROPERTY IMPORTED_LOCATION /usr/lib/R/site-library/Rcpp/libs/Rcpp.so)
target_link_libraries(rcpptest Rcpp)

CMake-based build of CUDA app fails - no files passed to linker

I'm trying to use CMake with a CUDA project of mine, but I'm having trouble getting it to build the executable when compiled on a system that has a CUDA-enabled device.
The CMakeLists.txt in question is below. It supports systems with and without CUDA-enabled devices, and builds just fine on my Macbook which doesn't have CUDA.
cmake_minimum_required (VERSION 2.8)
message(STATUS "CMake version: ${CMAKE_VERSION}")
project(stockModel)
# Grab the CUDA package
find_package(CUDA)
set(GPU_ACCELERATED ${CUDA_FOUND})
# Set directory and compilation flags for both g++ and nvcc
set(CMAKE_BINARY_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread")
set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}
-gencode arch=compute_50,code=sm_50; -std=c++11; -lcurand;"
)
set(CUDA_PROPAGATE_HOST_FLAGS off)
# Add directories
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/build/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/core/)
if (${GPU_ACCELERATED})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/support/)
endif()
# Setup environments, depending on GPU accel. status
set(SRCS build/main.cpp core/callModels.cpp)
set(INCS core/callModels.h)
if (${GPU_ACCELERATED})
set(SRCS ${SRCS} support/prng.cu support/debugCFP.cu)
set(INCS ${INCS} support/prng.h support/debugCFP.h)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/core/callModels.cpp
PROPERTIES CUDA_SOURCE_PROPERTY_FORMAT OBJ
)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}
-L/usr/local/cuda/lib64 -lcuda -lcudart"
)
endif()
# Create executable
message(STATUS "Sources: ${SRCS}")
message(STATUS "Includes: ${INCS}")
cuda_add_executable(stockModel ${SRCS} ${INCS})
The error I get when I attempt to build on my Jetson TX1 is as follows:
...
[ 80%] Building CXX object CMakeFiles/stockModel.dir/main.cpp.o
[100%] Linking CXX executable stockModel
c++: fatal error: no input files
compilation terminated.
...
Any ideas as to what is going wrong here? Obviously it has something to do with the CUDA 'extras', but I'm at a loss as to what is causing this.
Let me know if you need more details.
Here is the relevant part of the verbose output:
...
[100%] Linking CXX executable stockModel
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/stockModel.dir/link.txt --verbose=1
/usr/bin/c++ -std=c++11 -pthread
c++: fatal error: no input files
compilation terminated.
I've uploaded the full make VERBOSE=1 output to this gist on GitHub.
CMake is sometimes finicky about spaces and list combinations. I know that doesn't sound like much of an explanation, but I'm not much of an expert.
What you need to do is replace this:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}
-L/usr/local/cuda/lib64 -lcuda -lcudart"
with this:
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L/usr/local/cuda/lib64 -lcuda -lcudart")
(single line). That should do it. At least - it does on my system (I created dummy source files with your files' names to try this out).

Add wiringPi lib to cmake on RaspberryPi

for my project in c++ I wanted to create a cmake file to compile and link everything together.
This is my dir structure so far:
"quadro/minimu9-ahrs" alias home
|-build
|-include
|-src
in my home dir I have this 'CMakeLists.txt' file:
cmake_minimum_required (VERSION 2.6)
project(minimu)
set(HEADER_FILES $("include/*.h")
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_executable(minimu ${SOURCES})
add_definitions(-std=c++0x -lwiringPi -lpthread)
#install(TARGETS minimu DESTINATION /usr/lib)
in my 'src' dir I got all the .cpp files, and in 'include' all my headers. When I'm comiling, I go into my build dir, delete everything existing in there (from previous builds) and type
cmake ..
make
my problem in now, that my main.cpp in src uses '#include wiringPi.h', but when I make the project it gives me the following error:
pi#raspberrypi ~/quadro/minimu9-ahrs/build $ make
Scanning dependencies of target minimu
[ 20%] Building CXX object CMakeFiles/minimu.dir/src/L3G.cpp.o
[ 40%] Building CXX object CMakeFiles/minimu.dir/src/LSM303.cpp.o
[ 60%] Building CXX object CMakeFiles/minimu.dir/src/main.cpp.o
[ 80%] Building CXX object CMakeFiles/minimu.dir/src/I2CBus.cpp.o
[100%] Building CXX object CMakeFiles/minimu.dir/src/MinIMU9.cpp.o
Linking CXX executable minimu
CMakeFiles/minimu.dir/src/main.cpp.o: In function `frequency_thread(void*)':
main.cpp:(.text+0x1c): undefined reference to `digitalWrite'
main.cpp:(.text+0x38): undefined reference to `digitalWrite'
CMakeFiles/minimu.dir/src/main.cpp.o: In function `signalHandler(int)':
main.cpp:(.text+0xd4): undefined reference to `digitalWrite'
main.cpp:(.text+0xe8): undefined reference to `digitalWrite'
CMakeFiles/minimu.dir/src/main.cpp.o: In function `main':
main.cpp:(.text+0x14c): undefined reference to `wiringPiSetup'
main.cpp:(.text+0x184): undefined reference to `pinMode'
main.cpp:(.text+0x1e8): undefined reference to `pthread_create'
main.cpp:(.text+0x29c): undefined reference to `pthread_join'
collect2: ld returned 1 exit status
CMakeFiles/minimu.dir/build.make:185: recipe for target 'minimu' failed
make[2]: *** [minimu] Error 1
CMakeFiles/Makefile2:60: recipe for target 'CMakeFiles/minimu.dir/all' failed
make[1]: *** [CMakeFiles/minimu.dir/all] Error 2
Makefile:72: recipe for target 'all' failed
make: *** [all] Error 2
so how do I tell the compiler in cmake where to find and how to use the wiringPi lib? And is there an easier way, instead of deleting everything in my build folder before cmaking? Like it compiles all the 'static' files one time, and only adds the changing file (=main.cpp) everytime again.
Further I want to execute my program everywhere like
sudo minimu
instead of going into the 'build' dir and type
sudo ./minimu
Thanks if you can help me guys!
And sorry if my english isn't that good :)
Have a nice day.
I just figured this out today thanks to Sebastian over at github.
Assuming that you have wiringPi installed, the easiest thing to do is to make a FindWiringPi.cmake with the following:
find_library(WIRINGPI_LIBRARIES NAMES wiringPi)
find_path(WIRINGPI_INCLUDE_DIRS NAMES wiringPi.h)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(wiringPi DEFAULT_MSG WIRINGPI_LIBRARIES WIRINGPI_INCLUDE_DIRS)
And then put this in the folder where cmake looks for it's modules. For Ubuntu it's /usr/share/cmake-2.x/Modules. It's basically the same path for pi too.
Then in your CMakeLists.txt file use:
# Locate libraries and headers
find_package(WiringPi REQUIRED)
find_package(Threads REQUIRED)
# Include headers
include_directories(${WIRINGPI_INCLUDE_DIRS})
# Link against libraries
target_link_libraries(<yourProjectName> ${WIRINGPI_LIBRARIES})
target_link_libraries(<yourProjectName> ${CMAKE_THREAD_LIBS_INIT})
The pthread module is a stock package and should be on your system already. So then navigate to your folder and do a cmake ./ and then make. If this doesn't work the first time, then remove your CMakeCache.txt with rm CMakeCache.txt and try again. MAKE SURE THAT YOU REMOVE THE CACHE AND NOT THE LIST!!
Thanks for the QA, I figured out this can be done in a simpler manner (Raspberry Pi 3 Model B+) without altering anything in /usr/share/cmake-x.y/Modules. After your add_executable line, add the following
find_library(WIRINGPI_LIBRARIES NAMES wiringPi)
target_link_libraries(<executable_name> ${WIRINGPI_LIBRARIES})
For example:
cmake_minimum_required(VERSION 3.5)
project(OpenInsulin)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_VERBOSE_MAKEFILE ON)
# Include headers
include_directories(.)
add_executable(OpenInsulin
main.cpp
MCP9600.cpp
MCP9600.h)
# Link against wiringPi
find_library(WIRINGPI_LIBRARIES NAMES wiringPi)
target_link_libraries(OpenInsulin ${WIRINGPI_LIBRARIES})
Provide a flag -L <path> to compiler flags.
add_definitions(-std=c++0x -L/path/to/libwringPi.???.(so|a) -lwiringPi -lpthread)
I do not understand, why you clear the build dir before running cmake (this is not critique. I really do not understand, There might be a reason I dont know) . make checks if the sources are newer than the last compile output and compiles only the files, that are affected. But this works only, if do not delete the build targets.
To install the resulting program uncomment the install directive in you cmake and setup a target dir that's in you PATH. You can also create a dir /home//bin or so. Prepend it to the PATH environment variable and configure the target of you install directive with the new path. Then, in addition to cmake and make you have to perform a make install.