0

I've got a tool that generates files that contain definitions and declarations. These files need to be included from other source files or headers - they aren't usable standalone.

The obvious thing to do is have a custom command to generate them. My CMakeLists.txt that does this is as follows. I'm currently using this with the GNU makefile generator.

project(test_didl)
cmake_minimum_required(VERSION 3.0)

add_custom_command(
  OUTPUT test_didl_structs.h test_didl_structs.c
  COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/didl.py --decls=test_didl_structs.h --defs=test_didl_structs.c ${CMAKE_CURRENT_SOURCE_DIR}/test_didl_structs.py
  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/didl.py ${CMAKE_CURRENT_SOURCE_DIR}/test_didl_structs.py
  MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/test_didl_structs.py)

add_executable(test_didl test_didl.c)
target_include_directories(test_didl PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(test_didl shared_lib)

test_didl.c is very simple:

#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "test_didl_structs.h"
#include "test_didl_structs.c"

int main(void) {
}

But on the first build, make tries to build test_didl.c, which of course fails, because test_didl_structs.* haven't been generated yet. Naturally, before the first successful build of test_didl.c, the dependency information isn't known, so make doesn't know to run the python command first.

I tried a custom target, but that's no good, because custom targets are assumed to be always dirty. This means the C file is recompiled on every build and the EXE is linked. This approach won't scale.

My eventual solution was to make the output .h file an input to the executable:

add_executable(test_didl test_didl.c test_didl_structs.h)

.h file inputs are treated as dependencies, but don't otherwise do anything interesting for makefile generators. (I am not currently interested in other generators.)

So that works, but it feels a bit ugly. It doesn't actually state explicitly that the custom commands need to be run first, though in practice this seems to happen. I'm not quite sure how, though (but I'm not up to speed on reading the CMake-generated Makefiles just yet).

Is this how it's supposed to work? Or is there something neater I'm supposed to be doing instead?

(What I'm imagining, I suppose, is something like a Visual Studio pre-build step, in that it's considered for running on every build, before the normal dependency checking. But I want this pre-build step to have dependency checking, so that it's skipped if its inputs are older than its outputs.)

Tom Seddon
  • 2,648
  • 1
  • 19
  • 28

2 Answers2

1

My eventual solution was to make the output .h file an input to the executable.

This way is correct.

It actually states, that building executable depends on given file, and, if that file is OUTPUT for some add_custom_command(), this command will be executed before building executable.


Another way is to generate needed headers at configuration stage using execute_process(). In that case there is no need to add header files as sources for add_executable(): CMake has notion of autodetecting dependencies for compiling, so test_didl will be rebuilt after regeneration of test_didl_structs.h.

execute_process(COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/didl.py --decls=test_didl_structs.h --defs=test_didl_structs.c ${CMAKE_CURRENT_SOURCE_DIR}/test_didl_structs.py)
# ...
add_executable(test_didl test_didl.c)

Drawback of this approach is that you need manually rerun configuration stage after changing your .py files. See also that question and answer to it. Another problem is that header file will be updated every time configuration is run.

Community
  • 1
  • 1
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
0

You can try tell cmake that you are using an external source, see docs about set_source_files_properties, see this past post

Community
  • 1
  • 1
Joel
  • 1,805
  • 1
  • 22
  • 22