Multiple executables, thus multiple different builds of the same static lib?

If I have a CMakeLists.txt that encloses a larger project with multiple executables inside it, and each of those executables has different compile macros, and both have target_link_libraries to a static lib, will it automatically build the specializations or variants of that library?

For example, something like:

cmake_minimum_required(VERSION 3.25)
project(cmake_exes_test C)

set(CMAKE_C_STANDARD 11)

add_library(foo STATIC lib/foo.c)

add_executable(app1 app1/app1.c)
target_compile_definitions(app1 PUBLIC MACRO1)
target_link_libraries(app1 PRIVATE foo)

add_executable(app2 app2/app2.c)
target_compile_definitions(app2 PUBLIC MACRO2)
target_link_libraries(app2 PRIVATE foo)

ChatGPT (my only colleague :confounded:) insists that this should work cause foo to be built twice, each time with a different set of macros. But upon testing, it does not seem like that actually happens.

If that is NOT how it actually works … is the expected pattern something like this?

add_library( foo_var1 STATIC lib/foo.c)
target_compile_definitions( foo_var1 PUBLIC MACRO1 )
target_include_directories( foo_var1 PUBLIC lib )

add_library( foo_var2 STATIC lib/foo.c)
target_compile_definitions( foo_var2 PUBLIC MACRO2 )
target_include_directories( foo_var2 PUBLIC lib )

add_executable(app1 app1/app1.c)
target_link_libraries(app1 foo_var1)

add_executable(app2 app2/app2.c)
target_link_libraries(app2 foo_var2)

The obvious downside there is that I would be repeating macros that should have scope for the whole executable for every relevant library.
Feels like I am doing something wrong/missing something.

I am trying to level up with my CMake abilities, applied to embedded code projects.
This is a pattern that comes up quite a lot, where top-level macros are set for a whole executable, and a whole CMake project contains many executables, all shipping in-situ with the SDK and application source (since it produces a monolithic binary).

There is no way to propagate anything from target to it’s dependencies.
If you need something like that, you could use a CMake macro to wrap creating of specialized library builds and link them to the executable.

Other possibility (or maybe just as a helper) would be to create an INTERFACE target for each set of macros (and other common options) and link that to all relevant libraries & executables.

Okay, that is helpful to know.

After some rework, this is what I end up with:

cmake_minimum_required(VERSION 3.25)
project(cmake_exes_test C)

set(CMAKE_C_STANDARD 11)

add_library( macros1 INTERFACE )
target_compile_definitions( macros1 INTERFACE MACRO1 )

add_library( macros2 INTERFACE )
target_compile_definitions( macros2 INTERFACE MACRO2 )

add_library( foo_var1 STATIC lib/foo.c)
target_include_directories( foo_var1 PUBLIC lib )
target_link_libraries( foo_var1 macros1 )

add_library( foo_var2 STATIC lib/foo.c)
target_include_directories( foo_var2 PUBLIC lib )
target_link_libraries( foo_var2 macros2 )

add_executable(app1 app1/app1.c)
target_link_libraries(app1 PUBLIC foo_var1
                           INTERFACE macros1 )

add_executable(app2 app2/app2.c)
target_link_libraries(app2 PUBLIC foo_var2
                           INTERFACE macros2 )

I think I can see how to leverage that - build up INTERFACEs of sets of compile options, combine them with manually created super-INTERFACES, and then link them to both the libraries and executables.

Would this be considered … good and idiomatic cmake practice?

I haven’t yet managed to write a nice, clean, CMake script, so hopefully others will chime in to answer that :wink: