add_custom_command doesn't rebuild when dependencies change - c++

I run a python script over each of my source files in order to generte additional code. The generated code can then be included in the source file and its content be used.
#include <Main.cpp.generated.hpp>
In order to make it work with CMake, I've added this python script as a custom cmake, because it seems to be the common way to make it work.
My CMake script looks like this (ignore the hardcoded paths, it's a proof of concept).
cmake_minimum_required (VERSION 3.20)
project(test)
add_executable(target Main.cpp test.h for_each.hpp ${CMAKE_CURRENT_BINARY_DIR}/gen/Main.cpp.generated.hpp)
add_custom_command(
OUTPUT gen/Main.cpp.generated.hpp
COMMAND python D:/Projects/clang-plugins/test-gen/test-gen.py ${CMAKE_CURRENT_SOURCE_DIR}/Main.cpp ${CMAKE_CURRENT_BINARY_DIR}/gen/Main.cpp.generated.hpp
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Main.cpp)
target_compile_features(target PRIVATE cxx_std_17)
target_include_directories(target PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen)
Everytime I modify my Main.cpp file, the generated file is rebuilt. Which is good. However, if a file included by Main.cpp changes, the generated file is not rebuilt. Which is a problem because then the generated code can be outdated. I can fix it by adding those headers into the DEPENDS arg, but I don't want to do this my hand.
I don't want to trigger my custom command everytime the file Main.cpp changes, but everytime the file Main.cpp is rebuilt.

Related

Make headers appear like they are in a different folder

So I have a fancy-organized project that references headers from other projects like this:
#include <GeographicLib/Geocentric.hpp>
The thing is:
there is no GeographicLib folder
the project does compile with make
when I try to import it in CLion, it doesn't compile as it complains about missing headers
Is there a way to include a folder with CMakeLists.txt (the standard used by CLion) to make that .hpp file reference-able like it is in that GeographicLib folder without having to move files around or change the actual code?
Edit : (.hpp file is in a folder like ../other_proj/src/geolib)
One possible approach would be as follows: create a dummy target library (grouped_includes below) and link it to all your other targets (using target_link_libraries(target grouped_includes))
execute_process(
COMMAND ${CMAKE_COMMAND} -E create_symlink
"${CMAKE_SOURCE_DIR}/other_proj/src/geolib"
"${CMAKE_CURRENT_BINARY_DIR}/GeographicLib")
add_library(grouped_includes INTERFACE)
target_include_directories(grouped_includes INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)

How to display files generated to build space in QtCreator

I use a CMakeLists.txt like the following
cmake_minimum_required(VERSION 3.5)
project(generate_files_from_tool)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_custom_command(
OUTPUT generated_config.cpp
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/writeSourceFileFromConfigXml.sh ${CMAKE_CURRENT_SOURCE_DIR}/config.xml > ${CMAKE_CURRENT_BINARY_DIR}/generated_config.cpp
DEPENDS config.xml writeSourceFileFromConfigXml.sh
)
add_executable(tool_with_generated_file main.cpp generated_config.cpp)
which generates a file (here generated-config.cpp) in the build process using a custom script (writeSourceFileFromConfigXml.sh).
I can open it using QtCreator 4.4.1 just fine: Compile and run works -- but I cannot see the file generated_config.cpp in the Projects view, only CMakeLists.txt and main.cpp is visible there.
If I open the file "manually", QtCreator displays a warning "This file is not part of any project." -- but it uses the file for the compilation. And after opening the file, I'm able to switch between definition and implementation using Ctrl+Klick or F2 (and get to/come from generated_config.cpp)
So my question is: How can I make QtCreator understand, that this generated file is part of the project and add it to the Projects view?
Note: changing the last line to add_executable(tool_with_generated_file main.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated_config.cpp) did not change anything.
I am using a workaround to show generated sources located outside of build directory:
I have symlinks created in PROJECT_SOURCE_DIR to directories where generated sources are located, say src1, src2 (it is important to not use real paths). Then I use :
file(GLOB SOURCE_FILES ${PROJECT_SOURCE_DIR}/src1/*.[ch] ${PROJECT_SOURCE_DIR}/src2/*.[ch])
add_custom_target(
all_src
SOURCES ${SOURCE_FILES}
)

CMake run custom command before build?

Example source of a binary I want to run before each build, once per add_executable:
#include <stdio.h>
int main(int argc, char *argv[]) {
for(int i=0; i<argc; ++i)
printf("argv[%d] = %s\n", i, argv[i]);
fclose(fopen("foo.hh", "a"));
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(foo_proj)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
set(SOURCE_FILES main.cpp)
# ---- this line changes ----
add_executable(foo_proj ${SOURCE_FILES})
Attempts:
add_custom_target(create_foo_hh COMMAND /tmp/bin/create_foo_hh)
add_dependencies(${SOURCE_FILES} create_foo_hh)
Error:Cannot add target-level dependencies to non-existent target "main.cpp".
The add_dependencies works for top-level logical targets created by the add_executable, add_library, or add_custom_target commands. If you want to add file-level dependencies see the DEPENDS option of the add_custom_target and add_custom_command commands.
execute_process(COMMAND /tmp/bin/create_foo_hh main.cpp)
No error, but foo.hh isn't created.
How do I automate the running of this command?
execute_process() is invoked at configuration time.
You can use add_custom_command():
add_custom_command(
OUTPUT foo.hh
COMMAND /tmp/bin/create_foo_h main.cpp
DEPENDS ${SOURCE_FILES} /tmp/bin/create_foo_hh main.cpp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
add_executable(foo_proj ${SOURCE_FILES} foo.hh)
That way, foo.hh is a dependency of foo_proj: and your command will be invoked when building foo_proj. It depends on ${SOURCE_FILES} and /tmp/bin/create_foo_hh main.cpp so that it is generated again if one of those files changes.
Regarding paths, add_custom_command() is configured to run in the current build directory to generate the file there, and include_directories() is used to add the build directory to the include dirs.
You probably don't want the custom target to depend on your source files (because they aren't targets themselves and are therefore never "run"), but on the target you create with them:
target_add_dependencies(foo_proj create_foo_hh)
I think that the cleanest is to add two new project() (targets) and then add the resulting file to your final executable. This is how cmake can build a valid dependency tree so when your source files change they get recompiled, the command run, as necessary to get everything up to date.
Build Executable
First, as you do in your example, I create an executable from some .cpp file:
(example extracted from the as2js project)
project(unicode-characters)
add_executable(${PROJECT_NAME}
unicode_characters.cpp
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${ICU_INCLUDE_DIRS}
${SNAPDEV_INCLUDE_DIRS}
)
target_link_libraries(${PROJECT_NAME}
${ICU_LIBRARIES}
${ICU_I18N_LIBRARIES}
)
As we can see,m we can add specific include paths (-I) and library paths (-L). It is specific to that one target so you can have a set of paths that is different from the one used with your other executables.
Generate Additional File
Next, you create a custom command to run your executable like so:
project(unicode-character-types)
set(UNICODE_CHARACTER_TYPES_CI ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.ci)
add_custom_command(
OUTPUT
${UNICODE_CHARACTER_TYPES_CI}
COMMAND
unicode-characters >${UNICODE_CHARACTER_TYPES_CI}
WORKING_DIRECTORY
${PROJECT_BINARY_DIR}
DEPENDS
unicode-characters
)
add_custom_target(${PROJECT_NAME}
DEPENDS
${UNICODE_CHARACTER_TYPES_CI}
)
Notice a couple of things:
I set a variable (UNICODE_CHARACTER_TYPES_CI) because I am going to reference that one file multiple times
a. Notice how I put the destination in the binary (cmake output folder) using the ${PROJECT_BINARY_DIR}/... prefix. This is best to avoid generating those files in your source tree (and possibly ending up adding that file to your source tracking system like svn or git).
b. An important aspect of the add_custom_command() is the DEPENDS section which includes the name of your special command, the one we defined in the previous step.
The add_custom_target() is what allows cmake to find your target and execute the corresponding command whenever one of the source files (a.k.a. dependency) changes; notice the DEPENDS definition.
Use the Output
Finally, here is the main project (a library in my case) that makes use of the file we generated in the step above.
Notice that I reference that file using the variable I defined in the previous step. That way, when I feel like changing that name, I can do it by simply editing that one variable.
project(as2js)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
${CMAKE_CURRENT_BINARY_DIR}/version.h
)
add_library(${PROJECT_NAME} SHARED
compiler/compiler.cpp
...
parser/parser_variable.cpp
${UNICODE_CHARACTER_TYPES_CI}
file/database.cpp
...
)
(Note: the ... represent a list of files, shorten for display here as these are not important, the link above will take you to the file with the complete list.)
By having the filename inside the list of files defined in the add_library() (or the add_executable() in your case), you create a dependency which will find your custom_target(), because of the filename defined in the OUTPUT section of the add_custom_command()¹.
¹ It is possible to defined multiple outputs for an add_custom_command(). For example, some of my generators output a .cpp and a .h. In that case, I simply define both files in the OUTPUT section.
Results
Important points about the final results with this solution:
the output files of your generator are saved in the binary output path instead of your current working directory
the Makefile generated by cmake includes all the necessary targets/dependencies which means changing any of the input files regenerate everything as expected (even if you just update a comment)
if the generator fails, the build fails as expected
the files are generated by the build step (make time) instead of the generation step (cmake time, like the execute_process() would do)

CMake: conditionally generate protobuf `*pb.{h|cpp}` files when *.proto files change

I am part of a project that is using protobufs to communicate between a Python based client and a c++ based server. We are also using CMake.
With CMake, I am looking for a way to conditionally call the protoc program only when *.proto files change. My current directory structure (this seems to be part of the problem) has a directory for the *.proto files, and separate directories for the generated *.pb.{h|cc} and *_pb2.py files:
build/
Messages/proto/ <--- .proto files are here
Messages/cpp/ <--- would like the auto generated c++ files here
Messages/py/ <--- would like the auto generated Python files here
Server/Main.cpp
Client/Main.py
CMakeLists.txt
The (root) CMakeLists.txt file (below) runs the protoc program when the build/cmake .. command is executed:
project(AAA)
MESSAGE("Protobuf autogeneration STARTED")
file(GLOB proto_packages "${AAA_SOURCE_DIR}/Messages/proto/*.proto")
execute_process(COMMAND protoc -I=${AAA_SOURCE_DIR}/Messages/proto --cpp_out=${AAA_SOURCE_DIR}/Messages/cpp/ --python_out=${AAA_SOURCE_DIR}/Messages/py/ ${proto_packages})
MESSAGE("Protobuf autogeneration COMPLETED")
cmake_minimum_required(VERSION 2.8)
find_package(Boost)
find_package(Protobuf REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${PROTOBUF_INCLUDE_DIR})
add_subdirectory(Messages/proto)
add_subdirectory(Messages/cpp)
add_subdirectory(Server)
The Messages/proto/CMakeLists.txt file (which I'm not sure has any effect):
file(GLOB proto_packages "${AAA_SOURCE_DIR}/Messages/proto/*.proto")
execute_process(COMMAND protoc -I=${AAA_SOURCE_DIR}/Messages/proto --cpp_out=../cpp --python_out=../py ${proto_packages}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
Ideally, the protoc program should run as part of the build/make command, and only (re)generate the *.pb.{h|cc} and *_pb2.py files when a *.proto file changes.
Some files in the Server/ directory have #include <Messages/cpp/Xxxx.pb.h> directives.
For bonus points, I would prefer to have the *.pb.{h|cc} and *_pb2.py files be generated into their respective directories (Messages/cpp/ and Messages/py/ respectively). However, if someone is able to help with the dependency part of the problem, I am happy to have the *.pb.{h|cc} and *_pb2.py files co-exist with the *.proto files.
Thanks in advance for any suggestions !
This is untested, but you need to add a custom command a custom target.
You'd need to create some variables for the output line, but without knowing how to turn *.proto into the *.pb.{h|cc} and *_pb2.py files I can't help there. See the proto_packages_cpp and proto_packages_python variables for where those should go.
file(GLOB proto_packages "${AAA_SOURCE_DIR}/Messages/proto/*.proto")
add_custom_command(
COMMAND protoc -I=${AAA_SOURCE_DIR}/Messages/proto --cpp_out=../cpp --python_out=../py ${proto_packages}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${proto_packages}
OUTPUT ${proto_packages_cpp} ${proto_packages_python}
)
add_custom_target(protobuf_autogeneration_target ALL
DEPENDS ${proto_packages}
)

Recursive CMake search for header and source files

I am new to CMake and would like to ask if somebody can help in the following problem.
I have C++ source and header files in their respective folders and now, I want to make a CMake text file that recursively searches for them.
Currently, I am doing it in this way:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(CarDetectorDAISY)
file(GLOB_RECURSE SRCS *.cpp)
file(GLOB_RECURSE HDRS *.h)
ADD_EXECUTABLE(stereo_framework ${SRCS} ${HDRS})
TARGET_LINK_LIBRARIES(stereo_framework)
This creates my CarDetectorDAISY.sln solution file and when I try to build it, it shows
an error that header files are not found (No such file or directory).
It would be really grateful if someone can please help me. Thanks.
You're probably missing one or more include_directories calls. Adding headers to the list of files in the add_executable call doesn't actually add then to the compiler's search path - it's a convenience feature whereby they are only added to the project's folder structure in IDEs.
So, in your root, say you have /my_lib/foo.h, and you want to include that in a source file as
#include "my_lib/foo.h"
Then in your CMakeLists.txt, you need to do:
include_directories(${CMAKE_SOURCE_DIR})
If, instead you just want to do
#include "foo.h"
then in the CMakeLists.txt, do
include_directories(${CMAKE_SOURCE_DIR}/my_lib)
I should mention that file(GLOB...) is not the recommended way to gather your list of sources - you should really just add each file explicitly in the CMakeLists.txt. By doing this, if you add or remove a source file later, the CMakeLists.txt is modified, and CMake automatically reruns the next time you try and build. From the docs for file:
We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate.
My answer is mostly a hack, but I find it useful if you don't want to add all dir manually.
This macro will recursively scan all sub-directories (and their sub-directories, etc ...). If a directory contains a header (.h) file, it will append its path to the return_list. This list can then be used with target_include_directories.
MACRO(HEADER_DIRECTORIES return_list)
FILE(GLOB_RECURSE new_list *.h)
SET(dir_list "")
FOREACH(file_path ${new_list})
GET_FILENAME_COMPONENT(dir_path ${file_path} PATH)
SET(dir_list ${dir_list} ${dir_path})
ENDFOREACH()
LIST(REMOVE_DUPLICATES dir_list)
SET(${return_list} ${dir_list})
ENDMACRO()
Usage:
HEADER_DIRECTORIES(header_dir_list)
list(LENGTH header_dir_list header_dir_list_count)
message(STATUS "[INFO] Found ${header_dir_list_count} header directories.")
target_include_directories(
my_program
PUBLIC
${header_dir_list} # Recursive
)
Macro credit: Christoph
Tested with Cmake 3.10
Just to further clarify one point in Fraser's answer:
Headers should not be passed to ADD_EXECUTABLE.
The reason is that the intended compilation command on Linux for example is just:
gcc main.c mylib.c
and not:
gcc main.c mylib.c mylib.h
The C pre-processor then parses mylib.c, and sees a #include "mylib.h", and uses it's search path for those files.
By using include_directories instead, we modify the cpp preprocessor search path instead, which is the correct approach. In GCC, this translates to adding the -I flag to the command line:
gcc -Inew/path/to/search/for/headers main.c mylib.c