Consider the following reduced and seemingly silly example. We require four files:
.
├── CMakeLists.txt
├── ex1.cxx
├── ex2.cxx
└── gen_linker.py
CMakeLists.txt:
cmake_minimum_required(VERSION 3.24)
project(linking)
add_library(ex1-compiled OBJECT ex1.cxx)
add_library(ex2-compiled OBJECT ex2.cxx)
add_custom_command(
OUTPUT
linker.ld
COMMAND
python3 ${CMAKE_CURRENT_SOURCE_DIR}/gen_linker.py --dir ${CMAKE_CURRENT_SOURCE_DIR} > linker.ld
DEPENDS
ex1-compiled $<TARGET_OBJECTS:ex1-compiled>
ex2-compiled $<TARGET_OBJECTS:ex2-compiled>
)
add_custom_target(linker.ld-target DEPENDS linker.ld)
add_executable(ex1 $<TARGET_OBJECTS:ex1-compiled> linker.ld)
add_dependencies(ex1 linker.ld-target)
target_link_options(ex1 PRIVATE "-g" "LINKER:@linker.ld")
add_executable(ex2 $<TARGET_OBJECTS:ex2-compiled> linker.ld)
add_dependencies(ex2 linker.ld-target)
target_link_options(ex2 PRIVATE "-g" "LINKER:@linker.ld")
The python gen_linker.py
simply counts the number of lines in ex1.cxx
and ex2.cxx
and outputs them:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--dir")
args = parser.parse_args()
def num_lines(filename):
return open(f"{args.dir}/{filename}").read().count('\n')
lines1 = num_lines("ex1.cxx")
lines2 = num_lines("ex2.cxx")
print(f"--defsym=lines1={lines1} --defsym=lines2={lines2}")
And ex1.cxx
and ex2.cxx
are both initially:
#include <cstdio>
#include <cstdint>
extern int lines1;
extern int lines2;
int main(int, char** argv) {
printf("%s] lines1=%ld lines2=%ld\n", argv[0], (intptr_t)&lines1, (intptr_t)&lines2);
}
If we try to compile this (same behavior with both Ninja and Unix Makefiles):
$ mkdir build && cd build
$ cmake -G Ninja ..
$ ninja ex1 ex2
$ ./ex1
./ex1] lines1=9 lines2=9
$ ./ex2
./ex2] lines1=9 lines2=9
Now, if we change ex2.cxx
to just add another printf
:
#include <cstdio>
#include <cstdint>
extern int lines1;
extern int lines2;
int main(int, char** argv) {
printf("%s] lines1=%ld lines2=%ld\n", argv[0], (intptr_t)&lines1, (intptr_t)&lines2);
+ printf("ex2 rocks\n");
}
Then:
$ ninja ex1 ex2
$ ./ex1
./ex1] lines1=9 lines2=9
$ ./ex2
./ex2] lines1=9 lines2=10
ex2 rocks
ex1
should print that lines2=10
though, same as ex2
, since ex2.cxx
was updated which caused linker.ld
to be updated updated (it does correctly read --defsym=lines2=10
), but it doesn’t. ex1
was not re-linked.
What needs to change in order to get this to work? That is: how do I convinced CMake to actually relink both ex1
and ex2
when linker.ld
is regenerated?