I'm facing a problem where I build a shared library and a unit-test executable (which is in a sub directory). I want to execute this test as a POST_BUILD operation for the shared library. So I gave
Add_Custom_Command (TARGET ShLibName POST_BUILD COMMAND unit_test_exe)
CMake throws an error message during generation process:
CMake Error: The inter-target dependency graph contains the following strongly connected component (cycle):
"libCUEUtilities" of type SHARED_LIBRARY depends on "UtilitiesUnitTest"
"UtilitiesUnitTest" of type EXECUTABLE depends on "libCUEUtilities"
At least one of these targets is not a STATIC_LIBRARY. Cyclic dependencies are allowed only among static libraries.
So, how can I achieve what I'm trying to do.
I'm using CMake 2.8.1 (RC3) with VS2005 generator.
Sounds like you want to run a unit test every time the shared library is compiled. Since the test executable already depends on the shared library, you can change the add_custom_command to run once the unit test executable has been built. For example:
add_library(CUEUtilities SHARED ${CUEUTILS_LIBRARY_SOURCES})
add_executable(unit_test_exe ${UNIT_TEST_EXE_SOURCES})
target_link_libraries(unit_test_exe CUEUtilities)
add_custom_command(TARGET unit_test_exe POST_BUILD
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/unit_test_exe)
Changing any of the library sources will cause the library to be recompiled. Since the executable has a dependency on the library, the exe will get relinked, and finally the post-build step will be run again.
Related
I have a simple library with unit tests. That library can be built as static linked library or dynamic library. The library builds for Windows, Linux and MacOS.
The library is using CMake (with Qt) and has ctest unit tests.
This is how the library gets build and unit tests are called:
cmake -DCMAKE_BUILD_TYPE=Debug path/to/sources
cmake --build . --target all
cmake --build . --target test
This is all working fine, except for MacOS. When building the library is a dynamic linked library, the unit tests do not find the library.
Start 110: UnitTest
110/119 Test #110: UnitTest.................***Exception: Child aborted 0.01 sec
dyld: Library not loaded: library.dylib
Referenced from: /tmp/build/bin/UnitTest
Reason: image not found
Calling ctest directly or calling the executable directly works fine. Only when calling the test target from cmake, the unit test executable does not find the library.
Setting DYLD_LIBRARY_PATH to the location of the library does not help.
I used install_name_tool to change the path to the library to an absolute path, which fixes the problem.
However, this is only a indicator of the problem, not a solution.
I suspect cmake changes the DYLD_LIBRARY_PATH when calling the test target. Is this true? How should this work?
I suspect cmake changes the DYLD_LIBRARY_PATH when calling the test target. Is this true?
In fact, ctest is used to execute your tests previously described in your CMakeLists.txt and neither cmake or ctest update DYLD_LIBRARY_PATH environment variable.
That said, if you would like to associate a specific test with some environment variable, you could do so by setting the ENVIRONMENT test property. For example:
set_property(TEST ${name} PROPERTY ENVIRONMENT "DYLD_LIBRARY_PATH=/path/to/foo")
To better address your problem, I suggest you do the following:
indicate the version of CMake binaries used
if possible possible, provide a small example allow to reproduce the problem
share the output of these two commands:
(1) ldd /tmp/build/bin/UnitTest
(2) otool -l /tmp/build/bin/UnitTest | grep -A 3 LC_RPATH | grep path
As a side note, consider specifying the type of parameter passed to cmake, prefer -DCMAKE_BUILD_TYPE:STRING=Debug instead of -DCMAKE_BUILD_TYPE=Debug.
I'm faced a problem how to add 3rd-party dependency library to link command using cmake.
Currently, my cmake build does the following :
gets 3rd-party library from git and builds it (ExternalProject_Add)
after building of 3rd-party libarry, it provides custom binary file
(named 'config++') that allows to invoke it with the following
arguments :
'config++ --cppflags' - to get CPPFLAGS being used to
compile with the library
'config++ --ldflags' - to get library path
where libraries are placed during bulding
'config++ --libs' - to get
list of libraries (inluding static and shared dependent libaries) to
be used when linking with the libary.
(Yes, config++ it is very similar to pkg-config.)
As 3rd-party libary build is done during project build and I don't have config++ ready before, is there any way to detect dynamically CXXFLAGS/LDFLAGS (by invoking 'config++') to be passed to compile/linkage command when performing 'cmake build'?
Thanks.
You can use the CMake command execute_process to run the external program and then collect its output in a variable that you can then append to your build flags.
So I had this strange behavior with GitLab CI. I got it working but now I am wondering why it works.
First of all I was starting with GitLab CI. I got a local runner with docker on my machine (Arch Linux), so that I can test without pushing and waiting. I wrote a test with the googletest framework (Just an assert true). I triggered the script locally and everything worked. All tests passed in the local docker image.
So now, when everything was working, I pushed to the repository and a runner took the job. This ran on Ubuntu 16.04. Now it compiled and after the execution it was throwing a segmentation fault.
I got to debugging at the Ubuntu system and after a while I switched the linking order of two libraries:
From:
target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
${QT_LIBRARIES}
${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
${OpenCV_LIBRARIES}
)
To:
target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
${QT_LIBRARIES}
${OpenCV_LIBRARIES}
${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
)
I am using CMake for the build.
Both PCs are running the same version of docker (17.12.0-ce).
The used gcc docker image is: sha256:95d81930694ca9e705b71dc141ddd13f466f4989857f74aebaf1d29ba6553775
Appareantly this question is linked:
Why does the order in which libraries are linked sometimes cause errors in GCC?
Now my question: When both systems run a docker container. Why does changing the linking order in this case fix the problem?
Dependencies. Order of files matters.
The linker processes the static libraries one at a time, and resolves the missing symbols and pulls those into the executable file that is being created.
So if a static library (*.a) has dependencies on another static library, it has to appear before the static library that fulfills its missing symbols.
Object files (*.o) are consumed "wholesale", so order for them is less of a bother.
The proper solution to this in CMake is not to adjust the order manually, but rather to model the inter-dependencies between different targets correctly.
The exact nature of the order dependency here is toolchain dependent (on gcc, dependees have to come before dependencies on the linker command line; MSVC doesn't care; other toolchains may choose different order requirements). The only way for CMake to ensure that it generates the correct order for the given toolchain is by modelling the dependencies explicitly in CMake.
In your example you have modeled a flat list of dependencies:
target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
${QT_LIBRARIES}
${OpenCV_LIBRARIES}
${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a
${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a
)
You have a single target ${PROJECT_NAME}_test that depends on a bunch of libraries. But that is actually wrong! In reality, there is a dependency from gmock to gtest which you didn't tell CMake about. You need to model this dependency explicitly in order for CMake to work correctly. Since dependencies can only be specified between targets, we need to introduce two additional targets for gtest and gmock:
add_library(gtest INTERFACE)
target_link_libraries(gtest INTERFACE ${PROJECT_BINARY_DIR}/googletest-build/googlemock/gtest/libgtest.a)
add_library(gmock INTERFACE)
target_link_libraries(gmock INTERFACE ${PROJECT_BINARY_DIR}/googletest-build/googlemock/libgmock.a)
target_link_libraries(gmock INTERFACE gtest) # now gmock depends on gtest
target_link_libraries(${PROJECT_NAME}_test PRIVATE Threads::Threads -lglog -lpthread
${QT_LIBRARIES}
${OpenCV_LIBRARIES}
gtest
gmock # order doesn't matter here;
# you can even omit gtest completely now
)
Note the target_link_libraries call here that establishes the dependency from gmock to gtest. It is very important to always model direct dependencies between static libraries like this in CMake, otherwise you will get problems like the one you described, which can quickly grow over your head once your build exceeds a certain complexity.
As a side note, try not to hard-code library paths in your CMake files, as that again makes your build non-portable and may break completely on different toolchains.
I'm trying to write a CMakeLists.txt which copies as a post build event required dlls from external libraries to the folder in which the executable is located after building. The OS I'm working on is Win7 and VS2010.
There a quite a lot of external libraries, so I do not want to list them individually within the CMakeLists.txt. My current solution is to use file globbing and create a post build event for each library:
FILE(GLOB files "${LIBRARY_DIR}/lib/$(ConfigurationName)/*dll")
MESSAGE("FILE LIST IS: ${files}")
FOREACH(file ${files})
ADD_CUSTOM_COMMAND(
TARGET mylib
POST_BUILD
COMMENT "Copying dll ${file}..."
COMMAND ${CMAKE_COMMAND} -E copy ${file} "${CMAKE_BINARY_DIR}/$(ConfigurationName)/"
)
ENDFOREACH()
Basically, the code snipped above works file if I replace $(ConfigurationName) with Release or Debug. However, I'd like to take the libraries from the corresponding directory dependent on Release or Debug build mode. In the code snipped above, $(ConfigurationName) does not get substituted by the visual studio build mode. I guess this is due to that this is a VS2010 variable which isn't known at cmake generation time).
Does somebody have a smart idea how to resolve this issue?
ADD_CUSTOM_COMMAND understands generator expressions, so you would be able to use $<CONFIG> rather than $(Configuration) or $(ConfigurationName) there. For the FILE command, however, this won't work. More importantly though, the file globbing approach isn't going to work for the first time you build your project if those DLL's are built as part of the same project, since none of the DLL's would be there when you first run CMake in such an arrangement.
You may want to look into BundleUtilities as a more robust way to solve your problem. Despite its name, it supports all platforms, not just Mac. You would need to invoke the fixup_bundle command as a post-build or install step to achieve what you describe. There are a few variations of this approach already described online (e.g. here and here).
You can also have a look at the source for the DeployQt4.cmake file which should be included in the Modules subdirectory of your CMake installation for ideas.
Lets suppose you have _TARGET_NAME filled with your your project name, and ADDITIONAL_DLL_PATH with a list of folders containing external dlls, Then the following code will copy any external dependencies found using dumpbin on windows.
foreach(CONF_TYPE ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${CONF_TYPE} CONF_TYPE_U)
get_property(TARGET_LOCATION_${CONF_TYPE_U} TARGET ${_TARGET_NAME} PROPERTY LOCATION_${CONF_TYPE_U})
get_filename_component(TARGE_DIR ${TARGET_LOCATION_${CONF_TYPE_U}} PATH)
install(CODE "
if (\"\${BUILD_TYPE}\" STREQUAL \"${CONF_TYPE}\")
include(BundleUtilities)
fixup_bundle(\"${TARGET_LOCATION_${CONF_TYPE_U}}\" \"\" \"${ADDITIONAL_DLL_PATH}\")
endif()"
COMPONENT Runtime)
endforeach()
There are downsides to this solution though, Like the need to build INSTALL target whenever you need to copy those files (which might not be many times), and not detecting delay loaded dlls.
Is there a way to tell the CMake system that I don't want further compilation until the dependencies (external projects) are built and installed correctly?
Say a project depends on other libraries, which may or may not be present in the system. In case they're not, I use the ExternalProject_Add to download and install it. Yet, even though the configure part goes fine, building the external project doesn't happen until you type make. Now it doesn't make sense to compile the code if building the dependencies failed. I found in CMake ExternalProject_Add() and FindPackage() that maybe adding some dependencies would help, but that answer defines the dependency at configuration time, so it's not relevant to my situation.
Any ideas?
ExternalProject_Add will generate a custom target for building the dependency. If you don't want a target to be built in case building the dependency failed, add a dependency from that target to the external project's target.
ExternalProject_Add(my_external_lib [...])
add_executable(my_program [...])
add_dependencies(my_program my_external_lib)