I have been trying to get the simple use case of getting cmake to to generate some files with a script and then build an executable out of those generated files. After spending hours reading the documentation and various stack overflow answers, it seems like this is how everything should be arranged, yet cmake refuses to configure the project.
The idea is that things are done in following steps:
- During configure time, the generate_files.sh is generated with correct paths
- During build time, the target
gen
is built first and then targettest
is built
Step 1 is being performed properly, however step 2 isn't. Cmake complains that the source files for target test
aren't found, even though it should build gen
first and then it would find the sources.
What am I doing wrong?
Directory structure:
.
├── build
├── CMakeLists.txt
├── config
│ └── generate_files.sh.in
├── extern
│ ├── CMakeLists.txt
│ └── gen
├── scripts
└── src
├── CMakeLists.txt
└── main.cpp
Top level CMakeLists:
cmake_minimum_required(VERSION 3.13)
project(test VERSION 0.1.0.0)
add_subdirectory(src)
add_subdirectory(extern)
configure_file("${CMAKE_SOURCE_DIR}/config/generate_files.sh.in" "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh")
src/CMakeLists:
add_executable(test main.cpp)
extern/CMakeLists:
set(generated_sources
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated.h
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_2.cpp
)
set(generated_directories
${CMAKE_CURRENT_SOURCE_DIR}/gen)
add_custom_command(
OUTPUT ${generated_sources}
COMMAND "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh"
)
add_custom_target(gen
DEPENDS ${generated_sources}
)
add_dependencies(test gen)
target_sources(test
PRIVATE
${generated_sources}
)
target_include_directories(test
PRIVATE
${generated_directories}
)
config/generate_files.sh.in:
#! /bin/bash
echo " const int get_trouble_code();
const int get_higher_trouble_code();" > ${CMAKE_SOURCE_DIR}/extern/gen/generated.h
echo " #include \"generated.h\"
const int get_trouble_code(){return 1;}" > ${CMAKE_SOURCE_DIR}/extern/gen/generated_1.cpp
echo " #include \"generated.h\"
const int get_higher_trouble_code(){return 1+1;}" > ${CMAKE_SOURCE_DIR}/extern/gen/generated_2.cpp
src/main.cpp:
#include "generated.h"
int main()
{
get_trouble_code();
get_higher_trouble_code();
return 0;
}
Edit: I found a way to make it work, but now I am even more confused. Adding the generated files as a library instead of custom target seems to do the trick. The following changes to extern/CMakeLists work, but could someone explain why? :
set(generated_sources
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated.h
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gen/generated_2.cpp
)
set(generated_directories
${CMAKE_CURRENT_SOURCE_DIR}/gen)
add_custom_command(
OUTPUT ${generated_sources}
COMMAND "${CMAKE_SOURCE_DIR}/scripts/generate_files.sh"
)
#comment out this stuff
#add_custom_target(gen
# DEPENDS ${generated_sources}
# )
#
#add_dependencies(test gen)
#
#target_sources(test
# PRIVATE
# ${generated_sources}
#)
#add as library instead
add_library(gen ${generated_sources})
target_link_libraries(test PRIVATE gen)
target_include_directories(test
PRIVATE
${generated_directories}
)