"Mapping" lists of dependents and dependees files?

Is there a way to “map” a list of dependants and dependees files?

Like below:

set(IF_f <file1.h file2.h>)

add_custom_command(
OUTPUT <file1.obj file2.obj>
COMMAND ${CMAKE_CXX_COMPILER} -debug -nofpu -output=obj=<file1.obj file2.obj> <file1.c file2.c>
DEPENDS ${IF_f}
VERBATIM
)

add_custom_target(
what ALL
DEPENDS <file1.obj file2.obj>
VERBATIM
)

Sorry about the wrong use of <>, it is just fiducial to represent a list of files which are to be mapped

What I really want is:
When file1.h changes, the command will be executed and ONLY file1.obj is generated (not file2.obj)
Likewise:
When file2.h changes, the command will be executed and ONLY file2.obj is generated (not file1.obj)

Is it possible in CMake?

Can I do the following:

foreach(fileN_list)
set(IF_f each_file_in_the_list)
add_custom_command(…)
add_custom_target(…)
endforeach(…)

Which means if I have 10 files, then there will be 10 sets of add_custom_command() and add_custom_target(), kind of brute-force method

I believe the correct solution is half-way between the alternatives you have posted: one custom command per object file, and then one custom target to depend on all the object files. Very similar to how CMake handles native (non-custom) compilation:

set(object_list "")
foreach(file IN LISTS file_list)
  add_custom_command(
    OUTPUT ${file}.obj
    COMMAND ${CMAKE_CXX_COMPILER} -debug -nofpu -output=obj=${file}.obj ${file}.c
    DEPENDS ${file}.c ${other_dependencies_if_any}
    VERBATIM
  )
  list(APPEND object_list ${file}.obj)
endforeach()

add_custom_target(
  what ALL
  DEPENDS ${object_list}
)

That way, building the custom target what will always check that all the object files are up-to-date, but since each is produced by its own custom command, only those out of date will be recompiled.


However, this smells a bit of an XY problem. CMake has native support for compiling C[++] sources; it’s its primary purpose of existence, after all. Even if your desired final artifact are object files and not a library/executable, you should be able to achieve building them "CMake-natively’ using an OBJECT library. Are you sure you need to use custom commands in your situation?

Dear Petr,

Thanks for explaining. I will try the method you mentioned

you should be able to achieve building them "CMake-natively’ using an OBJECT library

I will look it up!

Thanks!

Dear Petr,

Just one more question. Why wouldn’t the following work?

add_executable(${SOURCE} file1.h file2.h)

?

Whether that works or not depends on the contents of the variable SOURCE.

The first argument to add_executable() must be the name of the target being created (e.g. MyExe). The other arguments are source (and optionally header) files that constitute the target. So typical usage of add_executable() could look like this:

add_executable(
  MyExe
  main.c file1.c file2.c
  file1.h file2.h
)

That would create an executable named MyExe which is composed of 3 object files (which in turn are each compiled from one C file).

Thanks Petr,

My bad. You’re right, I meant to ask:

Why add_executable(MyExe ${SOURCE} file1.h file2.h) won’t work?
My expectation is that with above command, if file1.h is changed, “make all” will rebuild the target. This is not happening
Conversely, if anyone of the *.c file is changed, “make all” will rebuild the target. This works fine
Or is my expectation wrong?

where SOURCE just consists of all *.c file

Does add_executable() ignore header files (I read somewhere)?

Listing header files as add_executable arguments has no effect on whether changing them causes the executable to rebuild. However, if the header files are #include-d normally by the source files of the exe, the dependency on them should be picked up automatically by CMake (and I can assure you that in normal cases, it works. I’ve verified that countless times in many projects, both work and personal).

Is the dependency on the header files somehow hidden in the source files? (Such as being produced by macro expansion)?

Dear Petr,

if the header files are #include -d normally by the source files of the exe, the dependency on them should be picked up automatically by CMake

Could what you said above be compiler dependant? My project is an embedded project and uses compiler from the MCU vendor. Unfortunately, the “automatic pick up by CMake” seems not supported

Is the dependency on the header files somehow hidden in the source files? (Such as being produced by macro expansion)?

I think I know what you are getting at. If I build the project normally with the MCU’s IDE (which has a built in builder, and GNU Make), then dependency files will be generated. In this case, file1.c will have a corresponding file1.d generated, where the content of file1.d looks like this:

#makefile format
file1.obj file1.d: …/file1.c
file1.obj file1.d: …/file1.h
file1.h:
file1.obj file1.d: …/file1_1.h
file1_1.h:

file1.d therefore contains all dependencies information of file1.obj, which depends on:
file1.c, file1.h, file1_1.h

Unfortunately, I do not know how to pass this dependencies information contained in file1.d to CMake

I am afraid I don’t know enough about the internals of CMake’s header dependency discovery to answer that, sorry.

1 Like