CMAKE: Circular ... dependency dropped - cmake

I have a 3rd-party (prebuilt) executable (named other) located in imported folder of my project's source tree. Basically, the project's structure looks like this:
.
├── CMakeLists.txt
├── imported
│   ├── libother.so
│   └── other
└── main.cpp
In order for my application to operate properly, that other executable must be copied next to my executable, what I achieve with this script:
cmake_minimum_required(VERSION 3.21)
project(myproject)
add_executable(myexe main.cpp)
set(path_in ${CMAKE_SOURCE_DIR}/imported/other)
set(path_out ${CMAKE_BINARY_DIR}/other)
add_executable(other_i IMPORTED)
set_target_properties(other_i PROPERTIES IMPORTED_LOCATION ${path_in})
add_custom_command(OUTPUT ${path_out}
COMMAND ${CMAKE_COMMAND} -E copy ${path_in} ${path_out}
DEPENDS other_i
)
add_custom_target(other DEPENDS ${path_out})
add_dependencies(myexe other)
The problem is whenever the project is being built, on Linux, I am getting weird messages from gmake:
$ cmake -S . -B .build
...
$ cmake --build .build
gmake[2]: Circular CMakeFiles/other <- other dependency dropped.
gmake[2]: Circular other <- other dependency dropped.
While the other is copied all right, it is being copied ALWAYS whenever I re-issue a build command (even without any changes). This is undesirable and what is bothering me most is that it works perfectly with MSVC on Windows and also with shared libraries on Linux. E.g. this works fine:
cmake_minimum_required(VERSION 3.21)
project(myproject)
add_executable(myexe main.cpp)
set(path_in ${CMAKE_SOURCE_DIR}/imported/libother.so)
set(path_out ${CMAKE_BINARY_DIR}/libother.so)
add_library(other_i SHARED IMPORTED)
set_target_properties(other_i PROPERTIES IMPORTED_LOCATION ${path_in})
add_custom_command(OUTPUT ${path_out}
COMMAND ${CMAKE_COMMAND} -E copy ${path_in} ${path_out}
DEPENDS other_i
)
add_custom_target(other DEPENDS ${path_out})
add_dependencies(myexe other)
I am at wits end on what is going on. Is it a CMAKE's bug or I am missing something?
Any insights would be greatly appreciated.
PS: Please do not suggest using POST_BUILD on a main target, as this approach has it's own drawbacks.

The line
add_custom_target(other DEPENDS ${path_out})
creates a dependency of other from the ${CMAKE_BINARY_DIR}/other.
But from the view of Make these are the same things: both refers to the file other under the build directory (where Makefile is located). Because of that you get the message
gmake[2]: Circular other <- other dependency dropped.
The core of the problem is that unlike to CMake, Make doesn't have separation of targets (pure names) and files (located on the disk). From the view of Make all targets (even .PHONY ones) are files.
Using another name for target other (e.g. other_exe) would eliminate the problem.

This appears to be a bug in CMake! I worked around it like this:
cmake_minimum_required(VERSION 3.21)
project(myproject)
set(path_in "${CMAKE_CURRENT_SOURCE_DIR}/imported/other")
set(path_out "${CMAKE_CURRENT_BINARY_DIR}/other")
add_custom_command(
OUTPUT "${path_out}"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${path_in}" "${path_out}"
DEPENDS "${path_in}"
)
add_custom_target(other_update DEPENDS "${path_out}")
add_executable(myproject::other IMPORTED)
set_target_properties(myproject::other PROPERTIES IMPORTED_LOCATION "${path_out}")
add_dependencies(myproject::other other_update)
add_executable(myexe main.cpp)
add_dependencies(myexe myproject::other)
There were a couple other issues with specifying your dependencies, so I cleaned those up. Even so, I observe that naming an executable target (even if imported) the same as the output file trips the bug. You should open an issue with CMake upstream.
In general, it is a good idea to utilize the fact that CMake always treats names containing :: as target names.

Related

In CMake, how would one run a custom command only if any subdirectory needed to be rebuild

I have a CMake-based project, and I need to run a command after build, if anything changed, including in any subprojects.
Simplified structure
project_root/
CMakeLists.txt (project root)
executable_1/
CMakeLists.txt (project tool1)
library_1/
CMakeLists.txt (project lib1)
executable_2/
CMakeLists.txt (project tool2)
library_2/
CMakeLists.txt (project lib2)
library_3/
CMakeLists.txt (project lib3)
The project root CMakeLists.txt looks like
cmake_minimum_required(VERSION 3.17)
add_subdirectory(tool1)
add_subdirectory(tool2)
add_subdirectory(lib3)
project(root C)
add_custom_target(root ALL
COMMENT
"root target"
DEPENDS
${DEPLOY_PATH}/deploy.tar.xz
)
add_custom_command(
OUTPUT
${DEPLOY_PATH}/deploy.tar.xz
COMMAND
tar cJf ${DEPLOY_PATH}/deploy.tar.xz *
COMMENT
"taring"
DEPENDS
root
)
The tool CMakeLists.txt look about like
cmake_minimum_required(VERSION 3.17)
add_subdirectory(lib1)
project(tool1 C)
add_executable(tool1
src/tool1.c
)
target_include_directories(tool1
PRIVATE
./
)
target_link_libraries(tool1
PRIVATE
lib1
lib3
)
install(TARGETS tool1 DESTINATION ${DEPLOY_PATH})
with the lib's CMakeLists.txt files omitting the install line and providing appropriate public include directories, etc.
I can get the behavior I want if I explicitly list every single subproject in the DEPENDS line of the custom target for "root", but that's tedious and error-prone. In the real project there are dozens of sub-libraries and executables.
I can also easily just re-build the archive every time, but that's slow.
How do I rebuild the archive if (and only if) any of the sub-projects of the root project or their descendants has changed?
List the actual dependencies of the tool.
file(GLOB_RECURSE srcs *) # I hope that's correct
add_custom_command(
OUTPUT
${DEPLOY_PATH}/deploy.tar.xz
COMMAND
# * generally is invalid, * depends on shell filename expansion
tar cJf ${DEPLOY_PATH}/deploy.tar.xz *
COMMENT "taring"
DEPENDS ${srcs}
)
But that's wrong anyway - cmake is meant to be run once and it scans your tree once. If you add a new file, you'll have to reconfigure cmake. Rather consider building the archive only when you actually need it - when deploying.

GBenchmark and CMake

From relevant search on the web, I got an impression that
google-benchmark is not easily incorporated into a CMake-project.
One way I could do that is adding as external project,
replicating verbatim the corresponding text for GTest in google-test's readme:
# adding google-benchmark as external git project
#=======================================================
configure_file(extern/googlebenchmark/CMakeLists.txt.in googlebenchmark-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download)
if(result)
message(FATAL_ERROR "CMake step for googlebenchmark failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-download )
if(result)
message(FATAL_ERROR "Build step for googlebenchmark failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gbenchmark_force_shared_crt ON CACHE BOOL "" FORCE)
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-src
${CMAKE_CURRENT_BINARY_DIR}/googlebenchmark-build
EXCLUDE_FROM_ALL)
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gbenchmark_SOURCE_DIR}/include")
endif()
and having CMakeLists.txt.in with the contents as in
How to build and link google benchmark using cmake in windows
This however has a huge downside: every time we change something in the CMakeLists.txt -- the topmost one -- that hosts all this, it starts building google-benchmark from scratch,
and running all the "tests", whatever they are. Thus compilation times become longer.
Without a root access to a Linux server, is there any more-less portable way
of having the benchmark code installed in one's home directory, while being
able to link towards it in a CMake-project?
EDIT: I must say I've been able to git clone the benchmark code and successfully built it in my home directory.
EDIT: Answering my own question. I am not sure if this warrants a close, and leave it to the patrons to decide, but I've solved the problem as follows. In the CMakeLists.txt one can have the following contents:
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
add_executable(sample_bench sample_bench.cpp)
target_link_libraries(sample_bench PUBLIC benchmark benchmark_main pthread)
target_link_directories(sample_bench PUBLIC ~/local/benchmark/build/src)
target_include_directories(sample_bench PUBLIC
~/local/benchmark/include)
The key here is target_link_directories, which is specified with -L in the example in https://github.com/google/benchmark
Answering my own question. I've solved the problem as follows. In the CMakeLists.txt one can have the following contents:
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
add_executable(sample_bench sample_bench.cpp)
target_link_libraries(sample_bench PUBLIC benchmark benchmark_main pthread)
target_link_directories(sample_bench PUBLIC ~/local/benchmark/build/src)
target_include_directories(sample_bench PUBLIC
~/local/benchmark/include)
The key here is target_link_directories, which is specified with -L in the example in https://github.com/google/benchmark
This is enough to run the sample benchmark, at least -- haven't tried others.
That is, once you have built benchmark somewhere -- even in your home directory, as in the example -- you can point CMake to the designated locations,
in order for your code to compile.

CMake and dependencies

I have the following directory structure and library dependencies:
./lib-a
./lib-b (depending on lib-a)
Each directory contains a CMakeLists.txt file for generating its own library.
I am using an out-of-source building policy.
In order to say that lib-b depends on lib-a, I have put the command add_subdirectory(../lib-a lib-a) in ./lib-b/CMakeLists.txt, according to what is taught by the official CMake tutorial. This way I obtain that a subdirectory lib-a is created in ./lib-b/build dir.
This is not the behaviour I desire. What I would like to obtain is CMake making reference to lib-a in generating lib-b and, if lib-a has not been already generated, CMake should generate it inside ./lib-a/build by using the CMakeLists.txt of lib-a (in a behaviour similar to the one of the make tool when dealing with dependencies).
I would also like to add that I am not using a root CMakeLists.txt in my example, but what I would like to have is the lib-b/CMakeLists.txt declaring the dependency on lib-a, thus making lib-a to be compiled, if not already, by using its own lib-a/CMakeLists.txt.
Here is the dirs structure and their contents:
lib-a/
CMakeLists.txt (for compiling lib-a)
src/
test.cpp
include/
test.hpp
lib-b/
main.cpp
CMakeLists.txt (for compiling lib-b; here is the point where I would like to make reference to lib-a, that I need for the generation of lib-b)
lib-b/main.cpp contains also an include of test.hpp of lib-a, because it is using a function of lib-a. This should be taken into consideration in the specification of lib-b/CMakeLists.txt.
What should the content of the two lib-a/CMakeLists.txt and lib-b/CMakeLists.txt files be?
I think you misunderstand the tutorial. The thing that links the libraries together is target_link_library(lib_b lib_a). If the name of the dependency is a library that is part of the build, CMake will magically make the dependencies work. It has nothing to do with subdirectories. In fact, if I have the following directory structure:
./
./a.hpp
./a.cpp
./b.hpp
./b.cpp
./CMakeLists.txt
The following will set the dependencies just fine:
PROJECT(lib_a)
ADD_LIBRARY(lib_a a.hpp a.cpp)
PROJECT(lib_b)
ADD_LIBRARY(lib_b b.hpp b.cpp)
TARGET_LINK_LIBRARIES(lib_b lib_a)
This will work across subdirectory projects as well:
./
./CMakeLists.txt
./lib_a/CMakeLists.txt
./lib_a/a.hpp
./lib_a/a.cpp
./lib_b/CMakeLists.txt
./lib_b/b.hpp
./lib_b/b.cpp
And the list files:
# ./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
ADD_SUBDIRECTORY(lib_a)
ADD_SUBDIRECTORY(lib_b)
# ./lib_a/CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(lib_a)
ADD_LIBRARY(lib_a a.hpp a.cpp)
# ./lib_b/CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(lib_b)
INCLUDE_DIRECTORIES(${lib_a_SOURCE_DIR})
ADD_LIBRARY(lib_b b.hpp b.cpp)
TARGET_LINK_LIBRARIES(lib_b lib_a)

What use is find_package() when you need to specify CMAKE_MODULE_PATH?

I'm trying to get a cross-plattform build system working using CMake. Now the software has a few dependencies. I compiled them myself and installed them on my system.
Some example files which got installed:
-- Installing: /usr/local/share/SomeLib/SomeDir/somefile
-- Installing: /usr/local/share/SomeLib/SomeDir/someotherfile
-- Installing: /usr/local/lib/SomeLib/somesharedlibrary
-- Installing: /usr/local/lib/SomeLib/cmake/FindSomeLib.cmake
-- Installing: /usr/local/lib/SomeLib/cmake/HelperFile.cmake
Now CMake has a find_package() which opens a Find*.cmake file and searches after the library on the system and defines some variables like SomeLib_FOUND etc.
My CMakeLists.txt contains something like this:
set(CMAKE_MODULE_PATH "/usr/local/lib/SomeLib/cmake/;${CMAKE_MODULE_PATH}")
find_package(SomeLib REQUIRED)
The first command defines where CMake searches after the Find*.cmake and I added the directory of SomeLib where the FindSomeLib.cmake can be found, so find_package() works
as expected.
But this is kind of weird because one of the reasons why find_package() exists is to get away from non-cross-plattform hard coded paths.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
Command find_package has two modes: Module mode and Config mode. You are trying to
use Module mode when you actually need Config mode.
Module mode
Find<package>.cmake file located within your project. Something like this:
CMakeLists.txt
cmake/FindFoo.cmake
cmake/FindBoo.cmake
CMakeLists.txt content:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(Foo REQUIRED) # FOO_INCLUDE_DIR, FOO_LIBRARIES
find_package(Boo REQUIRED) # BOO_INCLUDE_DIR, BOO_LIBRARIES
include_directories("${FOO_INCLUDE_DIR}")
include_directories("${BOO_INCLUDE_DIR}")
add_executable(Bar Bar.hpp Bar.cpp)
target_link_libraries(Bar ${FOO_LIBRARIES} ${BOO_LIBRARIES})
Note that CMAKE_MODULE_PATH has high priority and may be usefull when you need to rewrite standard Find<package>.cmake file.
Config mode (install)
<package>Config.cmake file located outside and produced by install
command of other project (Foo for example).
foo library:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Foo)
add_library(foo Foo.hpp Foo.cpp)
install(FILES Foo.hpp DESTINATION include)
install(TARGETS foo DESTINATION lib)
install(FILES FooConfig.cmake DESTINATION lib/cmake/Foo)
Simplified version of config file:
> cat FooConfig.cmake
add_library(foo STATIC IMPORTED)
find_library(FOO_LIBRARY_PATH foo HINTS "${CMAKE_CURRENT_LIST_DIR}/../../")
set_target_properties(foo PROPERTIES IMPORTED_LOCATION "${FOO_LIBRARY_PATH}")
By default project installed in CMAKE_INSTALL_PREFIX directory:
> cmake -H. -B_builds
> cmake --build _builds --target install
-- Install configuration: ""
-- Installing: /usr/local/include/Foo.hpp
-- Installing: /usr/local/lib/libfoo.a
-- Installing: /usr/local/lib/cmake/Foo/FooConfig.cmake
Config mode (use)
Use find_package(... CONFIG) to include FooConfig.cmake with imported target foo:
> cat CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Boo)
# import library target `foo`
find_package(Foo CONFIG REQUIRED)
add_executable(boo Boo.cpp Boo.hpp)
target_link_libraries(boo foo)
> cmake -H. -B_builds -DCMAKE_VERBOSE_MAKEFILE=ON
> cmake --build _builds
Linking CXX executable Boo
/usr/bin/c++ ... -o Boo /usr/local/lib/libfoo.a
Note that imported target is highly configurable. See my answer.
Update
Example
If you are running cmake to generate SomeLib yourself (say as part of a superbuild), consider using the User Package Registry. This requires no hard-coded paths and is cross-platform. On Windows (including mingw64) it works via the registry. If you examine how the list of installation prefixes is constructed by the CONFIG mode of the find_packages() command, you'll see that the User Package Registry is one of elements.
Brief how-to
Associate the targets of SomeLib that you need outside of that external project by adding them to an export set in the CMakeLists.txt files where they are created:
add_library(thingInSomeLib ...)
install(TARGETS thingInSomeLib Export SomeLib-export DESTINATION lib)
Create a XXXConfig.cmake file for SomeLib in its ${CMAKE_CURRENT_BUILD_DIR} and store this location in the User Package Registry by adding two calls to export() to the CMakeLists.txt associated with SomeLib:
export(EXPORT SomeLib-export NAMESPACE SomeLib:: FILE SomeLibConfig.cmake) # Create SomeLibConfig.cmake
export(PACKAGE SomeLib) # Store location of SomeLibConfig.cmake
Issue your find_package(SomeLib REQUIRED) commmand in the CMakeLists.txt file of the project that depends on SomeLib without the "non-cross-platform hard coded paths" tinkering with the CMAKE_MODULE_PATH.
When it might be the right approach
This approach is probably best suited for situations where you'll never use your software downstream of the build directory (e.g., you're cross-compiling and never install anything on your machine, or you're building the software just to run tests in the build directory), since it creates a link to a .cmake file in your "build" output, which may be temporary.
But if you're never actually installing SomeLib in your workflow, calling EXPORT(PACKAGE <name>) allows you to avoid the hard-coded path. And, of course, if you are installing SomeLib, you probably know your platform, CMAKE_MODULE_PATH, etc, so #user2288008's excellent answer will have you covered.
How is this usually done? Should I copy the cmake/ directory of SomeLib into my project and set the CMAKE_MODULE_PATH relatively?
If you don't trust CMake to have that module, then - yes, do that - sort of: Copy the find_SomeLib.cmake and its dependencies into your cmake/ directory. That's what I do as a fallback. It's an ugly solution though.
Note that the FindFoo.cmake modules are each a sort of a bridge between platform-dependence and platform-independence - they look in various platform-specific places to obtain paths in variables whose names is platform-independent.
You don't need to specify the module path per se. CMake ships with its own set of built-in find_package scripts, and their location is in the default CMAKE_MODULE_PATH.
The more normal use case for dependent projects that have been CMakeified would be to use CMake's external_project command and then include the Use[Project].cmake file from the subproject. If you just need the Find[Project].cmake script, copy it out of the subproject and into your own project's source code, and then you won't need to augment the CMAKE_MODULE_PATH in order to find the subproject at the system level.

"make dist" equivalent in CMake

According to FAQ, CMake doesn't create a make dist target and source package can be created using CPack. But CPack just makes a tarball of the source directory with all files that don't match patterns in CPACK_SOURCE_IGNORE_FILES.
On the other hand, make dist generated by autotools bundles only files it knows about, mostly sources needed for compilation.
Anyone has a smart way of making a source package with only files that are specified in CMakeLists.txt (and its dependencies)?
I've been thinking about this for a while and I won't pretend I can simulate a make dist without having this directly supported by CMake itself.
The problem is that you can add a lot of file dependencies with CMake on the one side (e.g. to pre-build libraries) and on the other side CMake does not know about dependencies directly checked by the generated build environment itself (e.g. any header dependencies).
So here is a code that just collects all CMakeList.txt and source files given with any build targets:
function(make_dist_creator _variable _access _value _current_list_file _stack)
if (_access STREQUAL "MODIFIED_ACCESS")
# Check if we are finished (end of main CMakeLists.txt)
if (NOT _current_list_file)
get_property(_subdirs GLOBAL PROPERTY MAKE_DIST_DIRECTORIES)
list(REMOVE_DUPLICATES _subdirs)
foreach(_subdir IN LISTS _subdirs)
list(APPEND _make_dist_sources "${_subdir}/CMakeLists.txt")
get_property(_targets DIRECTORY "${_subdir}" PROPERTY BUILDSYSTEM_TARGETS)
foreach(_target IN LISTS _targets)
get_property(_sources TARGET "${_target}" PROPERTY SOURCES)
foreach(_source IN LISTS _sources)
list(APPEND _make_dist_sources "${_subdir}/${_source}")
endforeach()
endforeach()
endforeach()
add_custom_target(
dist
COMMAND "${CMAKE_COMMAND}" -E tar zcvf "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.tar.gz" -- ${_make_dist_sources}
COMMENT "Make distribution ${PROJECT_NAME}.tar.gz"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
)
message("_make_dist_sources = ${_make_dist_sources}")
else()
# else collect subdirectories in my source dir
file(RELATIVE_PATH _dir_rel "${CMAKE_SOURCE_DIR}" "${_value}")
if (NOT _dir_rel MATCHES "\.\.")
set_property(GLOBAL APPEND PROPERTY MAKE_DIST_DIRECTORIES "${_value}")
endif()
endif()
endif()
endfunction()
variable_watch("CMAKE_CURRENT_LIST_DIR" make_dist_creator)
Note: The used BUILDSYSTEM_TARGETS property needs at least CMake version 3.7
I see the code above as an starting point and prove of concept. You could add libraries, headers, etc. on a need-by basis, but you should probably just tweak cpack to do your bidding.
As a starting point see e.g. the link #usr1234567 provided in the comments.
References
Get all source files a target depends on in CMake
Simon is correct above but does not give a full answer. With git you can generate a compatible tar ball archive with the git archive command.
This example with the version is compatible with make dist of yesteryear.
git archive --format=tar.gz -o my-repo-0.01.tar.gz --prefix=my-repo-0.01/ master
See: https://gist.github.com/simonw/a44af92b4b255981161eacc304417368