Reputation: 13846
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?
Upvotes: 8
Views: 31095
Reputation: 20818
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.
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, 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.
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.
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.
Important points about the final results with this solution:
execute_process()
would do)Upvotes: 1
Reputation: 5346
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.
Upvotes: 10
Reputation: 739
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)
Upvotes: 1