In GNU make
, if a target A depend on two targets B and C, but using target C to build A requires that target B has already been built (target C itself doesn't depend on B however), I can use order-only prerequisites.
Are there any alternatives in CMake
? I know CMake
is just a configuration tool so the question may be better rephrased as "Can I write CMakeLists.txt in a way so that it generates Makefiles with order-only prerequisites?"
Alternative solutions are also welcome. Any help will be appreciated.
The typical case of using an order-only prerequisite is to make sure a directory is prepared before it is used in some make rule. Usually, cmake takes care of the directories for all the targets that are under it's control. But if you really need some directory, you can use a custom target command like this:
add_custom_target(build-time-make-directory ALL
COMMAND ${CMAKE_COMMAND} -E make_directory ${directory})
This target is "with no output" so it will always be built, but the targets that depend on it won't necessarily be rebuilt.
This is not limited to mkdir
. Here is another example:
add_executable(qqexe qq.c)
add_custom_target(_custom_target COMMAND touch pp.c COMMENT pp)
add_dependencies(qqexe _custom_target)
Each time you run make, the timestamp of pp.c will refresh, but the qqexe will not be rebuilt unless the timestamp of qq.c changes.
Another use case for order-only dependencies, is to include a build timestamp or other build information in a binary, without triggering a relink of the binary if this is the only change. The scons documentation about order-only dependencies explains this use case rather well.
Building on the other answer, I can achieve such a solution with cmake.
I have a build_info
library with a simple object file that contains the build information. It depends on a custom target that keeps this up-to-date:
add_custom_target(update-build-info
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/build_info.cpp.templ build_info.cpp
BYPRODUCTS build_info.cpp)
add_library(build_info OBJECT
${CMAKE_CURRENT_BINARY_DIR}/build_info.cpp)
target_include_directories(build_info PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies(build_info update-build-info)
The build information could be generated by a script (internal or external), but here I simply copy a fixed template file that uses the __DATE__
/ __TIME__
macros:
#include <build_info.h>
const char build_date[] = __DATE__ " " __TIME__;
The build_info.h header makes the build information available for my main program:
#ifndef _build_info_h
#define _build_info_h
extern const char build_date[];
#endif
The build_info.cpp file is always copied and recompiled (filling in the current date/time), but we don't configure build_info
as a link library for main
directly (as would be done with target_link_libraries()
) because that would always trigger a relink. Instead we add a dependency to build_info
manually, and include the object file in the link options for the main
target. Any other effect of target_link_libraries()
, such as importing the interface include directories, we also do manually):
add_executable(main main.cpp ...other...)
add_dependencies(main build_info)
target_link_options(main PRIVATE $<TARGET_OBJECTS:build_info>)
target_include_directories(main
PRIVATE $<TARGET_PROPERTY:build_info,INTERFACE_INCLUDE_DIRECTORIES>)
This achieves the order-only dependency, where we rebuild the build_info
before main
, but it does not render main
out-of-date:
[ 0%] Built target update-build-info
Scanning dependencies of target build_info
[ 33%] Building CXX object build_info/CMakeFiles/build_info.dir/build_info.cpp.o
[ 33%] Built target build_info
[100%] Built target main
Final note: we add the build_info
in the link options as an object file (.o), by using a cmake object library, not as a static library (.a). This ensures that its symbols are included in the link regardless of its position among the other link inputs. With a static library, the position is more critical (see a good explanation here).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With