CMake: how to break a PRE_LINK infinite loop?

622 Views Asked by At

I'm trying to automatically label my application sign-on line with a build number. This application is a plain vanilla C one without graphic UI; it is intended for command line, therefore it is a "simple" one.

The sign-on id is located in a "template" source file which is customized by CMake with a configure_file() command. Recently, I fancied to include a build number in this sign-on id. Consequently, the customization can no longer be statically done at CMake time, but everytime make is invoked.

To achieve that, there are two possibilities in CMake:

  1. add_custom_target(), but it is triggered even when nothing else changes in the source tree which does not reflect the state of the tree;
  2. add_custom_command(), which can be triggered only when the application (target) needs to be linked again.

I opted for the second solution and did not succeed.

Here is an extract of my CMakeLists.txt, the sign-on id being in file ErrAux.c (template in PROJECT_SOURCE_DIR, configured in PROJECT_BINARY_DIR):

add_executable(anathem ... ${PROJECT_BINARY_DIR}/ErrAux.c ...)

add_custom_command(TARGET anathem PRE_LINK
    COMMAND "${CMAKE_COMMAND}"  "-DVERS=${PROJECT_VERSION}"
                                "-DSRC=${PROJECT_SOURCE_DIR}"
                                "-DDST=${PROJECT_BINARY_DIR}"
                                -P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
    COMMENT "Numbering build"
    VERBATIM
)

This launches script BuildNumber.cmake just before the link step. It computes the next build number and customizes ErrAux.c with configure_file().

It works fine, except ...

It happens late in the make sequence and the update to ErrAux.c goes unnoticed. The sign-on id in the executable contains the previous build number.

Next time I run make, make notices the generated ErrAux.c is younger than its object module and causes it to be compiled again, which in turn causes a link which triggers a build number update. This happens even if no other file has changed and this loop can't be broken. This is clearly shown in the compiling log:

Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-45
[ 36%] Built target anathem

The crux seems to be that add_custom_command(TARGET ...) can't specify an output file like add_custom_command(OUTPUT ...) does. But this latter form can't be triggered in PRE_LINK mode.

As a workaround, I forced a compilation to "refresh" the object module with:

add_custom_command(TARGET anathem PRE_LINK
    COMMAND "${CMAKE_COMMAND}"  "-DVERS=${PROJECT_VERSION}"
                                "-DSRC=${PROJECT_SOURCE_DIR}"
                                "-DDST=${PROJECT_BINARY_DIR}"
                                -P "${CMAKE_HOME_DIRECTORY}/BuildNumber.cmake"
    COMMAND echo "Numbering"
    COMMAND echo "${CMAKE_C_COMPILER}" "\$(C_DEFINES)" "\$(C_INCLUDES)" "\$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
    COMMAND "${CMAKE_C_COMPILER}" "\$(C_DEFINES)" "\$(C_INCLUDES)" "\$(C_FLAGS)" -c "${PROJECT_BINARY_DIR}/ErrAux.c"
    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
    COMMENT "Numbering build"
    VERBATIM
)

An explicit compilation is forced after sign-on id customization. It mimics what is found in the various Makefile's and my not be safe for production. It's a cheat trick on both CMake and make.

UPDATE: Option -c is required to postpone link step until the final application liniking process.

This addition creates havoc in the link, as shown by the log, where you see a double compilation (the standard make one and the add_custom_command() one):

Scanning dependencies of target anathem
[ 13%] Building C object AnaThem/CMakeFiles/anathem.dir/ErrAux.c.o
[ 14%] Linking C executable anathem
Numbering build
3.0.0-47
Numbering
/usr/bin/cc -DANA_DEBUG=1 -I/home/prog/projects/AnaLLysis/build/AnaThem -I/home/prog/projects/AnaLLysis/AnaThem -g /home/prog/projects/AnaLLysis/build/AnaThem/ErrAux.c
/usr/lib/gcc/x86_64-redhat-linux/6.3.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
AnaThem/CMakeFiles/anathem.dir/build.make:798: recipe for target 'AnaThem/anathem' failed
make[2]: *** [AnaThem/anathem] Error 1
If I force a full recompilation, to make sure all sources are compiled, *main.c* included, I get the same error on `main`. The only logical explanation is my manual C invocation is faulty and somehow destroys vital information. I checked with *readelf* that `main` is still in the symbol table for *main.c.o* and that it is still taken into account by the link step (from file *link.txt*).

UPDATE: Even with the correct link, I'm still experiencing the infinite loop syndrom. The generated application still has its sign-on id lagging behind the actual build counter.

Can someone give me a clue for the right direction?

FYI I'm quite new to CMake, so I may do things really wrong. Don't hesitate to criticize my mistakes.

1

There are 1 best solutions below

0
On BEST ANSWER

The key to the solution is to put the generated module where make expects to find it. CMake organizes the build tree in a non-trivial way.

The shortcomming in my added compilation in add_custom_command() was to believe that by default the binary will be stored in the "usual" CMake locations. Since I forge manually my compiler command, this is not the case.

I found the module in the source directory, which is a consequence of the WORKING_DIRECTORY option, with name ErrAux.o and not ErrAux.c.o.

To obtain the correct behavior, I force an output location with:

-o "${PROJECT_BINARY_DIR}/CMakeFiles/anathem.dir/ErrAux.c.o"

Now, when I run make again, nothing happens since nothing changed.

Side question

To make the solution portable (if needed), are there CMake variables for CMakeFiles and anathem.dir directories? Or in the latter case, for the current target as "anathem" as the target name in add_custom_command()?