I have a CMakeLists.txt in which I want to generate several source files (namely, versiondata.cpp and version.rc.inc, included by res.rc) that depends on the general environment (current git HEAD, gcc -v output, CMakeCache.txt itself, and so on).
If it depended just on some files, I would generate it using an add_custom_command directive with the relevant DEPENDS and OUTPUT clauses; however, it's a bit tricky to pinpoint exactly its file dependencies; ideally, I'd want to run my script every time I call make, updating the files only if needed; if the generated files have actually been touched, then the targets depending from them should be rebuilt (the script is careful not to overwrite the files if they would have the same content as before).
My first attempt was using an add_custom_command with a fake main output, like this:
add_custom_command(OUTPUT versiondata.cpp.fake versiondata.cpp version.rc.inc
COMMAND my_command my_options
COMMENT "Generating versiondata.cpp"
)
# ...
# explicitly set the dependencies of res.rc, as they are not auto-deduced
set_source_files_properties(res.rc PROPERTIES OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/version.rc.inc;${PROJECT_SOURCE_DIR}/other_stuff.ico")
# ...
add_executable(my_executable WIN32 ALL main.cpp versiondata.cpp res.rc)
versiondata.cpp.fake is never really generated, so the custom command is always run. This worked correctly, but always rebuilt my_executable, as CMake for some reasons automatically touches the output files (if generated) even though my script left them alone.
Then I thought I might make it work using an add_custom_target, that is automatically "never already satisfied":
add_custom_target(versiondata BYPRODUCTS versiondata.cpp version.rc.inc
COMMAND my_command my_options
COMMENT "Generating versiondata.cpp"
)
# ...
# explicitly set the dependencies of res.rc, as they are not auto-deduced
set_source_files_properties(res.rc PROPERTIES OBJECT_DEPENDS "${PROJECT_BINARY_DIR}/version.rc.inc;${PROJECT_SOURCE_DIR}/other_stuff.ico")
# ...
add_executable(my_executable WIN32 ALL main.cpp versiondata.cpp res.rc)
The idea here is that the versiondata target should be "pulled in" from the targets that depend on its BYPRODUCTS, and should be always executed. This seems to work on CMake 3.20, and the BYPRODUCTS seem to have some effect because if I remove the dependencies from my_executable my script doesn't get called.
However, on CMake 3.5 I get
make[2]: *** No rule to make target 'version.rc.inc', needed by 'CMakeFiles/my_executable.dir/res.rc.res'. Stop.
and if I remove the explicit dependency from version.rc.inc it doesn't get generated at all
[ 45%] Building RC object CMakeFiles/my_executable.dir/res.rc.res
/co/my_executable/res.rc:386:26: fatal error: version.rc.inc: No such file or directory
#include "version.rc.inc"
^
compilation terminated.
/opt/mingw32-dw2/bin/i686-w64-mingw32-windres: preprocessing failed.
CMakeFiles/my_executable.dir/build.make:5080: recipe for target 'CMakeFiles/my_executable.dir/res.rc.res' failed
make[2]: *** [CMakeFiles/my_executable.dir/res.rc.res] Error 1
so I suspect that the fact that this works in 3.20 is just by chance.
Long story short: is there some way to make this work as I wish?
In CMake there are two types of dependencies:
Target-level dependency, between targets.
A target can be build only after unconditional building of all targets it depends on.
File-level dependency, between files.
If some file is older than one of its dependencies, the file will be regenerated using corresponded COMMAND.
The key factor is that checking for timestamp of dependent files is performed strictly after building of dependent targets.
For correct regeneration of versiondata.cpp file and executable based on it, one need both dependencies:
Target-level, which would ensure that versiondata custom target
will be built before the executable.
add_dependencies(my_executable versiondata)
File-level, which will ensure that the executable will be rebuilt whenever
file versiondata.cpp will be updated.
This dependency is created automatically by listing versiondata.cpp
among the sources for the executable.
Now about BYPRODUCTS.
Even without explicit add_dependencies, your code works on CMake 3.20 because BYPRODUCTS generates needed target-level dependency automatically.
This could be deduced from the description of DEPENDS option in add_custom_target/add_custom_command:
Changed in version 3.16: A target-level dependency is added if any dependency is a byproduct of a target or any of its build events in the same directory to ensure the byproducts will be available before this target is built.
and noting, that add_executable effectively depends on every of its source files.
Because given comment for DEPENDS is applicable only for CMake 3.16 and later,
in older CMake versions BYPRODUCTS does not create target-level dependency automatically, and one need to resort to explicit add_dependencies.
Related
I have a CMake project that uses an external tool to build special libraries for a certain platform. Running this tool uses a "config file" to generate several files that are injected into the compiler and linker options when the final program is built:
An object library
A linker command file that links in several pre-compiled libs and the above object library
A makefile options file that sets various platform compiler options
whenever any of these files change, the main program must be entirely rebuilt, as they are intrinsic parts of the program and involve things like compiler flags and system includes.
So far, I have something like this, which appears to be a recommended way:
# run the external build tool to generate platform libs
# and compiler/linker option files
add_custom_command(
OUTPUT ${LINKER_CMD_FILE} ${COMPILER_OPTS_FILE} ${PLATFORM_OBJECT_LIB}
COMMAND "${EXTERNAL_BUILD_TOOL}"
ARGS --config ${CFG_FILE}
DEPENDS ${CFG_FILE}
COMMENT "Invoking external build tool for ${CFG_FILE}"
)
add_custom_target(platform_libs
DEPENDS ${LINKER_CMD_FILE} ${COMPILER_OPTS_FILE} ${PLATFORM_OBJECT_LIB}
)
....
add_executable(main_prog
main.c
)
# whenever any of these change, rebuild
add_dependencies(main_prog platform_libs)
# add the platform compiler opts from the generated file
target_compile_options(main_prog PRIVATE
#${COMPILER_OPTS_FILE}
)
This is also pretty much what is done in this question.
When I change the config file, the platform_libs target runs and generates the library and other files as needed. However, although running make main_prog does trigger the build of the platform_libs correctly, it does not appear to "notice" any changes and therefore concludes it doesn't need to actually re-build the main program.
I can always run make clean, but it's not great to have CMake totally blind to fundamental system libraries changing.
How can I force main_prog to rebuild if platform_libs has run?
[This answer's central method comes from #KamilCuk's answer in the comment of the question].
The trick is to use one of:
Set the LINK_DEPENDS property on the downstream target (main_prog in the example) - this means if the file in this property changes, a re-link will be performed.
Set the OBJECT_DEPENDS on every source used for main_prog.
In my case, because the ${COMPILER_OPTS_FILE} affects every file's compilation, I needed the OBJECT_DEPENDS method. If you do this you don't really need LINK_DEPENDS since you'll recompile your sources and re-link anyway, but I did both for clarity of meaning. In theory you could engineer a situation where the linker command file changes, but not the compiler opts, in which case you might miss a re-link.
In my case, I needed to do this for not only main_prog but also all the other libraries main_prog used, so I stored the linker command files and the compiler opts file as target properties on the platform_libs target:
set_property(TARGET platform_libs
PROPERTY MY_LINKER_CMD_FILE ${LINKER_CMD_FILE}
)
set_property(TARGET platform_libs
PROPERTY MY_COMPILER_OPTS_FILE ${COMPILER_OPTS_FILE}
)
This means it's easy to pull them out later, without having to know the exact file names (or even have access to the variables themselves):
# Retrieve the previously-stored options
# To do this, we only need the target name and the (fixed) property name
get_target_property(MY_LINKER_CMD platform_libs MY_LINKER_CMD_FILE)
get_target_property(MY_COMPILER_OPTS platform_libs MY_COMPILER_OPTS_FILE)
set_target_properties(main_prog PROPERTIES
LINK_DEPENDS ${MY_LINKER_CMD}
)
# Also set as the linker cmd on the linker command line
# This depends on the linker, for GCC it's -Wl,T<file>
target_link_libraries(main_prog PRIVATE
-Wl,-T${MY_LINKER_CMD}
)
# these are the sources that depend on the opts file
get_target_property(MAIN_SRCS main_prog SOURCES)
# set the dependency of source files on platform_libs
set_property(SOURCE ${MAIN_SRCS}
PROPERTY OBJECT_DEPENDS ${MY_COMPILER_OPTS}
)
# Set as a compiler opt file
# For GCC: #<opt_file>
target_compile_options(${TARGET_TO_COMPILE} PRIVATE
#${MY_COMPILER_OPTS}
)
# make sure the platform_libs is a dep
# or the compiler opts and linker files won't be generated
add_dependencies(main_prog platform_libs)
Notably, in this Cmake code, the project names main_prog and platform_libs can be variables, and then you can make the whole thing into a function that just needs those two project names. This makes it easy to reuse the code for compiling libraries against platform_libs library.
Description
I have a code generator that takes an XML input file and outputs a c++ header and source file. These auto-generated files are then compiled with static source files to produce a library. Simplified cmake file.
add_library(subdirectory/${MODULE_NAME} ${STATIC_SOURCES})
# Invoke auto-coder
add_custom_command(
OUTPUT ${GEN_HEADER} ${GEN_SOURCE}
COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/my_autocoder ${SOURCE_XML}
DEPENDS ${SOURCE_XML}
)
# Add auto-generated source dependencies
target_sources(${MODULE_NAME}
PUBLIC ${GEN_HEADER}
PRIVATE ${GEN_SOURCE}
)
Problem
A generated header file ${GEN_HEADER_A} includes other generated headers files e.g. ${GEN_HEADER_B}. When running make at the top level everything builds correctly. However when building the library in isolation, it errors because ${GEN_HEADER_B} does not exist.
Question
How do I add all dependencies like ${GEN_HEADER_A} on ${GEN_HEADER_B} without manually specifying each dependency?
Attempts
The IMPLICIT_DEPENDS feature of add_custom_command seems to have the functionality I want. However, my attempts at using it have not resulted in the code generator being invoked for ${GEN_HEADER_B}. My assumption is this is because I already have a rule to build ${MODULE_NAME} when I added the library add_library(subdirectory/${MODULE_NAME} ${STATIC_SOURCES})
add_custom_command(
OUTPUT ${MODULE_NAME}
COMMAND ...
IMPLICIT_DEPENDS ${GEN_HEADER}
)
I do see ${GEN_HEADER_B} in the CXX.includecache under ${GEN_HEADER_A}
If you want file A to be (re)created when file B is built, you should specify this dependency explicitely.
Dependencies discovered by scanning (either automatic in add_library/add_executable commands, or via IMPLICIT_DEPENDS option of add_custom_command) are only checked for modification. CMake doesn't attempt to (re)create dependencies discovered by scanning.
I am trying to add a custom target with CMake that executes one command for each given .cpp file. The command should only be re-executed when the source file itself or one of the included source files changes. AFAIK to achieve this I need a list of all the included files and add them to the DEPENDS option of the add_custom_command() calls that belong to my custom target.
So is there a built-in way to get that list of included files?
I know about the IMPLICIT_DEPENDS option of the add_custom_command() function but it only works for Makefile generators. I would like to make this work for all generators.
Thank you for your time
Edit:
As requested I will post some cmake code to show what I want to achieve.
I want to add a custom target, that runs clang-tidy on all the given .cpp files. When incrementally building the custom target the clang-tidy commands should be re-run whenever a .cpp file or one of its directly or indirectly included header files is changed. Just like re-runs of the compiler are handled.
# ----------------------------------------------------------------------------------------
# mainTargetName The name of the target that shall be analyzed
# files A list of all the main targets .cpp files
#
function( addStaticAnalysisTarget mainTargetName files )
set(targetName runStaticAnalysis_${mainTargetName})
set(command "clang-tidy-4.0 -checks=* -p ${CMAKE_BINARY_DIR}")
foreach( file ${files} )
get_filename_component( baseName ${file} NAME_WE)
set(stampFile ${CMAKE_CURRENT_BINARY_DIR}/analyze_${baseName}.stamp )
set(fullFile ${CMAKE_CURRENT_SOURCE_DIR}/${file})
set(commandWithFile "${command} ${fullFile}")
separate_arguments_for_platform( commandList ${commandWithFile})
add_custom_command(
OUTPUT ${stampFile}
DEPENDS "${fullFile}"
IMPLICIT_DEPENDS CXX "${fullFile}"
COMMAND ${commandList}
COMMAND cmake -E touch "${stampFile}" # without creating a file as a touch-stone the command will always be re-run.
WORKING_DIRECTORY ${CPPCODEBASE_ROOT_DIR}
COMMENT "${commandWithFile}"
VERBATIM
)
list(APPEND stampFiles ${stampFile})
endforeach()
set_source_files_properties(${stampFiles} PROPERTIES GENERATED TRUE) # make the stamp files known to cmake as generated files.
add_custom_target(
${targetName}
DEPENDS ${stampFiles}
)
endfunction()
The problem with that is, that it does not seem to work. When I change included files clang-tidy is not re-run for the affected files.
I used the "Unix Makefile" generator for this example so it should work at least with make. Any hints why it doesn't?
My hopes where that I could achieve the desired behavior for all generators by somehow getting the file-dependencies at cmake time and then adding them to the ''''DEPENDS'''' list. But the dependency scanning must be done each time the command is run, so it can not be done at cmake time. This means that the scanning must be implemented by cmake which it currently is not.
A guy with similar problems:
https://gitlab.kitware.com/cmake/cmake/issues/16830
Edit 2:
I think the problem that the IMPLICIT_DEPENDS option was not working was because I did not use correct filenames. I changed that in the code snipped, but I have not yet tested if it works in the project.
I think the answer to my question is ...
No, you can not use cmakes dependency scanner in the cmake code.
That makes sense, because this problem can not be solved at cmake time, because the dependencies of a .cpp file may change without cmake being re-run.
The problem must be solved within cmake itself at make time. This is done when using the IMPLICIT_DEPENDS option.
Also, I tried to solve a Problem that I did not really have, because at this point I can only run clang-tidy on linux anyways. However, clang-tidy may become available on windows as well and then I may have the problem again.
To sum the comments up:
Tambre stated that CMake is not a compiler and therefore can not do that.
I think this is wrong. According to this article, CMake can parse cpp include dependencies because make has no such dependency searcher itself. That was news to me, but I mostly live on Windows so I am not that familiar with make. It could also be possible that in the meantime make was extended to do its own dependency searching. Also this explains why the IMPLICIT_DEPENDS option is only available for make.
Florian pointed out that it is not necessary to create an own custom target for running clang-tidy. Instead, one can use the CXX_CLANG_TIDY target property to run clang-tidy for each file after compiling it. This means however, that static-analysis can not be separated from the build which could lead to inacceptable buildtimes.
There is the cmake -E cmake_depends command line, that could be used to retrieve dependencies at cmake time. But as stated above, I erroneously thought that I needed the dependencies at cmake time, while I needed them at runtime.
The IMPLICIT_DEPENDS options did not work because I had an error in my cmake code.
I have a c++ cmake project. In this project I build (among other) one example, where I need to use another project, call it Foo. This Foo project does not offer a cmake build system. Instead, it has a pre-made Makefile.custom.in. In order to build an executable that uses Foo's features, one needs to copy this makefile in his project, and modify it (typically setting the SOURCES variable and a few compiler flags). Basically, this Makefile ends up having the sources for your executable and also all the source files for the Foo project. You will not end up using Foo as a library.
Now, this is a design I don't like, but for the sake of the question, let's say we stick with it.
To create my example inside my cmake build I added a custom target:
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/Makefile.custom.in Makefile.custom)
ADD_CUSTOM_TARGET(my_target COMMAND $(MAKE) -f Makefile.custom
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
This works. I can specify some variables to cmake, which get resolved in the call to CONFIGURE_FILE, and I end up with a working Makefile.custom. Then, invoking make my_target from the build directory, I can build the executable. I can even add it to the all target (to save me the effort of typing make my_target) with
SET_TARGET_PROPERTIES(my_target PROPERTIES EXCLUDE_FROM_ALL FALSE)
Sweet. However, cmake appears to assign a single job to the custom target, slowing down my compilation time (the Foo source folder contains a couple dozens cpp files). On top of that, the make clean target does not forward to the custom makefile. I end up having to add another target:
ADD_CUSTOM_TARGET(really-clean COMMAND "$(MAKE)" clean
COMMAND "$(MAKE)" -f Makefile.custom clean
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
which, unlike my_target with all, I can't include in the clean target (can I?).
Now, I know that a cleaner solution would be to have the Foo project be built as an external project, and then link to it. However, I've been 'recommended' to use their Makefile.custom.in makefile, modifying the few lines I need (adding my sources, specifying compiler flags, and few other minor modifications). So, regardless of how neat and clean this design pattern is, my questions are:
is there a way to tell cmake that make should use more than 1 job when making the target my_target?
is there a cleaner way to include a pre-existing makefile in a cmake project? Note that I don't want (can't?) use Foo as a library (and link against it). I want (need?) to compile it together with my executable using a makefile not generated by cmake (well, cmake can help a bit, through CONFIGURE_FILE, by resolving some variables, but that's it).
Note: I am aware of ExternalProject (as suggested also in this answer), but I think it's not exactly what I need here (since it would build Foo and then use it as a library). Also, both my project and Foo are written exclusively in C++ (not sure this matter at all).
I hope the question makes sense (regardless of how ugly/annoying/unsatisfactory the resulting design would be).
Edit: I am using cmake version 3.5.2
First, since you define your own target, you can assign more cores to the build process for the target my_target, directly inside your CMakeLists.txt.
You can include the Cmake module ProcessCount to determine the number of cores in your machine and then use this for a parallel build.
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
# given that cores != 0 you could modify
# math(EXPR N "${N}+1") # modify (increment/decrement) N at your will, in this case, just incrementing N by one
set(JOBS_IN_PARALLEL -j${N})
endif(NOT N EQUAL 0)
and when you define your custom target have something like the following:
ADD_CUSTOM_TARGET(my_target
COMMAND ${CMAKE_MAKE_PROGRAM} ${JOBS_IN_PARALLEL} -f Makefile.custom
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
by the way, I don't think there's the need for you to include also CMAKE_BUILD_TOOL among the COMMANDs in your target.
I believe that instead of modifying the lines as above, you could call
make -j8 my_target
and it might start 8 jobs (just an example) without modifying the CMakeLists.txt, but I cannot guarantee this works having defined the COMMAND the way you have, just try if that's enough.
For the second point, I cannot think right now of a "cleaner" way.
I'm using cmake with C++ project. I want to use precompiled headers in GCC.
I want to run cmake once and then, when running make, I want this actions to happen:
Run my custom tool (python script) on whole source tree. It generates precompiled.h headers in some subdirectories. It's purpose is to scan *.h and *.cpp for #include's and move them to common precompiled.h files.
generate GCC's precompiled headers (g++ -c precompiled.h -o precompiled.h.gch) ONLY when precompiled.h file has changed after step 1.
build outdated targets (after step 2. because I want to use .gch file)
I've managed to add dependencies, so whenever I build any target, my python script's target is executed (this is ugly, because now every target has to depend on single global_init target). Also I can modify my python script, so it doesn't modify precompiled.h when it is not necessary.
But I don't know what to do next. Is there any way to tell cmake that it should run my custom script and AFTER THAT determine if precompiled.h.gch must be created? Now it is always build (this file contains many #include's, so it takes some time). Basically:
Execute python script -> precompiled.h MAY BE updated
Examine precompiled.h timestamp and if it's newer -> build precompiled.h.gch
Standard compilation steps
I've looked in many places, but cmake's (and make's) way of calculating dependencies seems too weak to accomplish this task.
In this question:
lazy c++
compilation always happens after lazycpp is executed. This would not be optimal in my case.
The following CMake commands may serve as a starting point for your project:
add_custom_target(scanner COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scanner.py"
COMMENT "Scanning include files ..."
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
This command covers step 1. It invokes your python script which may update the existing precompiled.h files in CMAKE_CURRENT_SOURCE_DIR.
add_custom_command(OUTPUT precompiled.h.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS}
-c "${CMAKE_CURRENT_SOURCE_DIR}/precompiled.h" -o precompiled.h.gch
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/precompiled.h"
IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/precompiled.h")
add_custom_target(generate_precompiled DEPENDS
"${CMAKE_CURRENT_BINARY_DIR}/precompiled.h.gch")
add_dependencies(generate_precompiled scanner)
These commands cover step 2. The precompiled header is generated with a custom command which depends on precompiled.h and implicitly on other headers that precompiled.h includes.
precompiled.h.gch is generated in the CMAKE_CURRENT_BINARY_DIR directory.
Because the precompiled header generation requires precompiled.h to be updated by the python script, we add a target level dependency on the target scanner.
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
add_executable(main main.cpp)
set_target_properties(main PROPERTIES COMPILE_FLAGS "-include precompiled.h -H")
add_dependencies(main generate_precompiled)
These commands add standard compilation steps which generate an executable main. The precompiled header is included as a default header with gcc's -include switch. The CMAKE_CURRENT_BINARY_DIR which contains precompiled.h.gch is added as an include directory. gcc will pick up precompiled.h.gch from there when precompiled.h is included (see Using Precompiled Headers).
Finally the target dependency on generate_precompiled ensures that the precompiled header is updated if necessary.