Mahmoud Mahdi
Mahmoud Mahdi

Reputation: 61

Propagate CMake target definition to linked library

I have a project with multiple targets release and test that is linked against a static library which may be compiled with a TEST macro.

Say I want my release target to be built without the TEST macro and the test target to be built with that macro defined but that macro has to propagate to the linked-against library.

In my main CMakeLists.txt I have created the targets and defined that macro using target_compile_definitions(mytest PUBLIC TEST) but obviously the library core is not recognizing it.

The project tree is as follows:

.
├── CMakeLists.txt
├── core
│   ├── CMakeLists.txt
│   ├── inc
│   │   ├── core_comp1.h
│   │   └── core_comp2.h
│   └── src
│       ├── core_comp1.c
│       └── core_comp2.c
├── lib
│   ├── CMakeLists.txt
│   ├── inc
│   │   ├── ext_comp1.h
│   │   └── ext_comp2.h
│   └── src
│       ├── ext_comp1.c
│       └── ext_comp2.c
├── main.c
├── README.md
└── test.c

The root ./CMakeLists.txt is as follows:

cmake_minimum_required(VERSION 3.10)

# set the project name
project(cmake_test)

# add the subdirectories
add_subdirectory(core)
add_subdirectory(lib)

# define my targets
set(MY_TARGETS myexe mytest)

# create and link against the libs
foreach(target ${MY_TARGETS})
    add_executable(${target})
    target_link_libraries(${target} PRIVATE core ext)
endforeach(target ${MY_TARGETS})

# add target specific sources
target_sources(myexe  PRIVATE main.c)
target_sources(mytest PRIVATE test.c)

# define the TEST macro for mytest
target_compile_definitions(mytest PUBLIC TEST)

The ./core/CMakeLists.txt library folder has:

add_library(core
    ${CMAKE_CURRENT_SOURCE_DIR}/src/core_comp1.c
    ${CMAKE_CURRENT_SOURCE_DIR}/src/core_comp2.c
)

target_include_directories(core PUBLIC 
    ${CMAKE_CURRENT_SOURCE_DIR}/inc
)

target_link_libraries(core PRIVATE ext)

The output of myexe, the release target is correct:

Running the release executable
Original core_comp1_display

While that of mytest is not correct:

Running the test executable
Original core_comp1_display

It should be:

Running the test executable
Test core_comp1_display

I have pushed the project to GitLab at cmake-library-test

The compilation of the myexe target gives the follows:

[build] [9/12   8% :: 0.032] /usr/bin/gcc  -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/myexe.dir/lib/src/ext_comp2.c.o -MF CMakeFiles/myexe.dir/lib/src/ext_comp2.c.o.d -o CMakeFiles/myexe.dir/lib/src/ext_comp2.c.o -c ../../lib/src/ext_comp2.c
[build] [9/12  16% :: 0.034] /usr/bin/gcc  -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/myexe.dir/core/src/core_comp2.c.o -MF CMakeFiles/myexe.dir/core/src/core_comp2.c.o.d -o CMakeFiles/myexe.dir/core/src/core_comp2.c.o -c ../../core/src/core_comp2.c
[build] [9/12  25% :: 0.039] /usr/bin/gcc  -I../../lib/inc -g -MD -MT lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o -MF lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o.d -o lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o -c ../../lib/src/ext_comp2.c
[build] [9/12  33% :: 0.041] /usr/bin/gcc  -I../../lib/inc -g -MD -MT lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o -MF lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o.d -o lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o -c ../../lib/src/ext_comp1.c
[build] [10/12  41% :: 0.043] /usr/bin/gcc  -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/myexe.dir/lib/src/ext_comp1.c.o -MF CMakeFiles/myexe.dir/lib/src/ext_comp1.c.o.d -o CMakeFiles/myexe.dir/lib/src/ext_comp1.c.o -c ../../lib/src/ext_comp1.c
[build] [10/12  50% :: 0.046] /usr/bin/gcc  -I../../core/inc -g -MD -MT core/CMakeFiles/core.dir/src/core_comp2.c.o -MF core/CMakeFiles/core.dir/src/core_comp2.c.o.d -o core/CMakeFiles/core.dir/src/core_comp2.c.o -c ../../core/src/core_comp2.c
[build] [10/12  58% :: 0.049] /usr/bin/gcc  -I../../core/inc -g -MD -MT core/CMakeFiles/core.dir/src/core_comp1.c.o -MF core/CMakeFiles/core.dir/src/core_comp1.c.o.d -o core/CMakeFiles/core.dir/src/core_comp1.c.o -c ../../core/src/core_comp1.c
[build] [11/12  66% :: 0.054] /usr/bin/gcc  -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/myexe.dir/main.c.o -MF CMakeFiles/myexe.dir/main.c.o.d -o CMakeFiles/myexe.dir/main.c.o -c ../../main.c
[build] [11/12  75% :: 0.058] /usr/bin/gcc  -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/myexe.dir/core/src/core_comp1.c.o -MF CMakeFiles/myexe.dir/core/src/core_comp1.c.o.d -o CMakeFiles/myexe.dir/core/src/core_comp1.c.o -c ../../core/src/core_comp1.c
[build] [11/12  83% :: 0.110] : && /usr/bin/cmake -E rm -f lib/libmylib.a && /usr/bin/ar qc lib/libmylib.a  lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o && /usr/bin/ranlib lib/libmylib.a && :
[build] [11/12  91% :: 0.119] : && /usr/bin/cmake -E rm -f core/libcore.a && /usr/bin/ar qc core/libcore.a  core/CMakeFiles/core.dir/src/core_comp1.c.o core/CMakeFiles/core.dir/src/core_comp2.c.o && /usr/bin/ranlib core/libcore.a && :
[build] [12/12 100% :: 0.147] : && /usr/bin/gcc -g  CMakeFiles/myexe.dir/main.c.o CMakeFiles/myexe.dir/core/src/core_comp1.c.o CMakeFiles/myexe.dir/core/src/core_comp2.c.o CMakeFiles/myexe.dir/lib/src/ext_comp1.c.o CMakeFiles/myexe.dir/lib/src/ext_comp2.c.o -o myexe  core/libcore.a  lib/libmylib.a && :
[build] Build finished with exit code 0

Whereas the compilation of mytest give:

[build] [9/12   8% :: 0.035] /usr/bin/gcc  -I../../core/inc -g -MD -MT core/CMakeFiles/core.dir/src/core_comp2.c.o -MF core/CMakeFiles/core.dir/src/core_comp2.c.o.d -o core/CMakeFiles/core.dir/src/core_comp2.c.o -c ../../core/src/core_comp2.c
[build] [9/12  16% :: 0.045] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o -MF CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o.d -o CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o -c ../../lib/src/ext_comp1.c
[build] [9/12  25% :: 0.047] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -MF CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o.d -o CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -c ../../lib/src/ext_comp2.c
[build] [9/12  33% :: 0.049] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/core/src/core_comp2.c.o -MF CMakeFiles/mytest.dir/core/src/core_comp2.c.o.d -o CMakeFiles/mytest.dir/core/src/core_comp2.c.o -c ../../core/src/core_comp2.c
[build] [9/12  41% :: 0.049] /usr/bin/gcc  -I../../core/inc -g -MD -MT core/CMakeFiles/core.dir/src/core_comp1.c.o -MF core/CMakeFiles/core.dir/src/core_comp1.c.o.d -o core/CMakeFiles/core.dir/src/core_comp1.c.o -c ../../core/src/core_comp1.c
[build] [10/12  50% :: 0.052] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/test.c.o -MF CMakeFiles/mytest.dir/test.c.o.d -o CMakeFiles/mytest.dir/test.c.o -c ../../test.c
[build] [10/12  58% :: 0.056] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/core/src/core_comp1.c.o -MF CMakeFiles/mytest.dir/core/src/core_comp1.c.o.d -o CMakeFiles/mytest.dir/core/src/core_comp1.c.o -c ../../core/src/core_comp1.c
[build] [10/12  66% :: 0.065] /usr/bin/gcc  -I../../lib/inc -g -MD -MT lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o -MF lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o.d -o lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o -c ../../lib/src/ext_comp2.c
[build] [10/12  75% :: 0.069] /usr/bin/gcc  -I../../lib/inc -g -MD -MT lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o -MF lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o.d -o lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o -c ../../lib/src/ext_comp1.c
[build] [11/12  83% :: 0.127] : && /usr/bin/cmake -E rm -f core/libcore.a && /usr/bin/ar qc core/libcore.a  core/CMakeFiles/core.dir/src/core_comp1.c.o core/CMakeFiles/core.dir/src/core_comp2.c.o && /usr/bin/ranlib core/libcore.a && :
[build] [11/12  91% :: 0.139] : && /usr/bin/cmake -E rm -f lib/libmylib.a && /usr/bin/ar qc lib/libmylib.a  lib/CMakeFiles/mylib.dir/src/ext_comp1.c.o lib/CMakeFiles/mylib.dir/src/ext_comp2.c.o && /usr/bin/ranlib lib/libmylib.a && :
[build] [12/12 100% :: 0.168] : && /usr/bin/gcc -g  CMakeFiles/mytest.dir/test.c.o CMakeFiles/mytest.dir/core/src/core_comp1.c.o CMakeFiles/mytest.dir/core/src/core_comp2.c.o CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -o mytest  core/libcore.a  lib/libmylib.a && :
[build] Build finished with exit code 0

It is clear that core_comp*.c and ext_comp*.c were compiled without -DTEST

Using the suggested INTERFACE keyword, the flag -DTEST is passed into all files and the compilation is as follows:

[build] [5/6  16% :: 0.024] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/core/src/core_comp2.c.o -MF CMakeFiles/mytest.dir/core/src/core_comp2.c.o.d -o CMakeFiles/mytest.dir/core/src/core_comp2.c.o -c ../../core/src/core_comp2.c
[build] [5/6  33% :: 0.025] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -MF CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o.d -o CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -c ../../lib/src/ext_comp2.c
[build] [5/6  50% :: 0.038] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o -MF CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o.d -o CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o -c ../../lib/src/ext_comp1.c
[build] [5/6  66% :: 0.041] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/core/src/core_comp1.c.o -MF CMakeFiles/mytest.dir/core/src/core_comp1.c.o.d -o CMakeFiles/mytest.dir/core/src/core_comp1.c.o -c ../../core/src/core_comp1.c
[build] [5/6  83% :: 0.058] /usr/bin/gcc -DTEST -I../../core/inc -I../../lib/inc -g -MD -MT CMakeFiles/mytest.dir/test.c.o -MF CMakeFiles/mytest.dir/test.c.o.d -o CMakeFiles/mytest.dir/test.c.o -c ../../test.c
[build] [6/6 100% :: 0.134] : && /usr/bin/gcc -g  CMakeFiles/mytest.dir/test.c.o CMakeFiles/mytest.dir/core/src/core_comp1.c.o CMakeFiles/mytest.dir/core/src/core_comp2.c.o CMakeFiles/mytest.dir/lib/src/ext_comp1.c.o CMakeFiles/mytest.dir/lib/src/ext_comp2.c.o -o mytest   && :
[build] Build finished with exit code 0

The libraries core and mylib are now transparent and the targets myexe and mytest are actually considering the libraries sources are 'their'

Upvotes: 0

Views: 1518

Answers (1)

mydisplayname
mydisplayname

Reputation: 356

One way to achieve similar behavior is to use an interface library and add the source files to the interface library via target_sources().

This doesn't actually propagate the definition down to the library, instead it brings the sources up to the consuming targets. One would want to be sure and only depend on this library via PRIVATE or else the sources would get compiled for each subsequent dependent.

I wouldn't encourage this solution, but I've had need of it before, so thought I would share.

./CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(example)

add_subdirectory(lib)

add_executable(my_exe main.c)
target_link_libraries(my_exe PRIVATE my_lib) 

add_executable(my_test main.c)
target_link_libraries(my_test PRIVATE my_lib) 
target_compile_definitions(my_test PRIVATE TEST) 

./main.c

#include <stdio.h>

const char * get_string();

int main(int argc, char *argv[]) {
    printf("The message is \"%s\"\n", get_string());
    return 0;
}

lib/CMakeLists.txt

add_library(my_lib INTERFACE)
target_sources(my_lib INTERFACE message.c)

lib/message.c

#ifdef TEST
const char * get_string(){
    return "The test string";
}
#else
const char * get_string(){
    return "The normal string";
}
#endif

Upvotes: 1

Related Questions