Propagation of compile definitions

Hi,

I’ve got a static library target L where I do:

target_compile_definitions(L PUBLIC SOMESYMBOL)

In an application A, I’m linking against L:

target_link_library(A PRIVATE L)

I’m then expecting A to inherit SOMESYMBOL but:

get_target_property(ouputvar A <INTERFACE_>COMPILE_DEFINITIONS)
message(${ouputvar})

displays only

outputvar-NOTFOUND

What is happening?

Regards
A.

They do propagate, you can see it when running the build.

cmake_minimum_required(VERSION 3.15)
project(mypkg CXX)


add_library(L src/mypkg.cpp)
target_compile_definitions(L PUBLIC SOMESYMBOL)
add_library(A src/mypkg.cpp)
target_link_libraries(A PRIVATE L)

Output (note SOMESYMBOL present in both commands):

# ninja -v -n
[1/4] L:\Software\MICROS~2\2022\COMMUN~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\cl.exe  /nologo /TP -DSOMESYMBOL  /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd /showIncludes /FoCMakeFiles\L.dir\src\mypkg.cpp.obj /FdCMakeFiles\L.dir\L.pdb /FS -c L:\oblivion\conan-test\src\mypkg.cpp
[2/4] L:\Software\MICROS~2\2022\COMMUN~1\VC\Tools\MSVC\1444~1.352\bin\Hostx64\x64\cl.exe  /nologo /TP -DSOMESYMBOL  /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd /showIncludes /FoCMakeFiles\A.dir\src\mypkg.cpp.obj /FdCMakeFiles\A.dir\A.pdb /FS -c L:\oblivion\conan-test\src\mypkg.cpp

When you do target_link_library you don’t copy properties of all linked targets to another, you just reference the target as linked to another and it’s properties then inherited later during project build files generation.

Thanks Andrej,

There is no way to test for the symbol from A CMakeLists.txt at configuration time?

At worse, can I display a message at build time, based on the existence of the symbol?

Regards,
A.

There are various ways that checks can be performed at configuration time, but it depends on what you are ultimately trying to achieve.
If your `L` target is visible (IOW added to the cmake processing) before the `A` target is processed then you can acquire that property from `L`. I.e. change the `get_target_property` to acquire from `L` instead.

As for at build time, generally this is the point of having compiler definitions, right?
That is, they are typically used for `#ifdef` condition blocks, so presumably somewhere you are making use of the definition. So you can emit a diagnostic from your particular compiler (e.g. `#pragma message …`).
If you need to make it more compiler-agnostic then a dependent custom target can be added that performs an echo (via cmake’s `-E echo`).

What do you mean by “test for the symbol”? You mean whether A library has definition for some symbol or you’re referring to SOMESYMBOL flag being passed to compilation of A?..

Either way, sounds like https://xyproblem.info/

Hi,

One use case is for L to test if some OpenCV functionality is available, which I do with a try_run on a minimal test program. If it succeeds, I define a symbol to pass to the code (through target_compile_definitions).

My goal is to let the client A also know if the functionality is available without running the try_run again (the test program being not necessarily available to the client) and emit a log at configuration time.

So far, the symbol is correctly defines also inside A but it allows to emit the message only at run-time or, possibly, compile-time, through a non-standard pragma.

I hope my intent is clearer. Regards,
A.

It seems you need try_compile.

The approach might be driven by how tightly coupled your lib and client are.

If your client is always being built within the same cmake configuration (hierarchy) as your library then using a property, as described above, would be one approach. Although you could insulate the components more by using your own custom property, rather than a built-in cmake one.
E.g. in the lib’s cmake configuration: set_target_properties(mylib PROPERTIES MY_LIB_HAS_FEATURE_X ON)
and in the client’s cmake configuration: get_target_property(hasFeatureX mylib MY_LIB_HAS_FEATURE_X)
then test hasFeatureX.

If your client is being built independently from the lib, i.e. in another cmake hierarchy, then as you’ve said the decision that the feature is available has been determined by the lib (via try_run) at configuration time, that suggests the client is being built for the same platform with the same capabilities - so it must honour the lib. In that case, the lib is like a prebuilt distribution, so it can provide something in that distribution that advertises its capabilities.
For example, the lib’s configuration step could generate a header file (e.g. via cmake’s file or configure_file). The client can use that file to do what it needs to do.