Generator expression not evaluated

Is this supposed to work:

cmake_minimum_required(VERSION 3.25)
 
project(TEST)
 
add_executable(test_ext dummy.cpp)
add_library(lib_a STATIC dummy.cpp)
add_library(lib_b STATIC dummy.cpp)
 
target_link_libraries(lib_a
    PUBLIC
        $<1:lib_b>
)
 
target_link_libraries(test_ext
    PUBLIC
        $<TARGET_PROPERTY:lib_a,LINK_LIBRARIES>
)

Running commands:

touch dummy.cpp
cmake -S . -B out -G Ninja
cmake --build out

Fails with:

ninja: error: build.ninja:63: bad $-escape (literal $ must be written as $$)
   LINK_LIBRARIES = $<1:lib_b>
                    ^ near here

It looks like test_ext’s link line isn’t recursively expanding genexes; lib_a is fine. Full stanza:

build test_ext: CXX_EXECUTABLE_LINKER__test_ext_ CMakeFiles/test_ext.dir/dummy.cpp.o
  LINK_LIBRARIES = $<1:lib_b>
  OBJECT_DIR = CMakeFiles/test_ext.dir
  POST_BUILD = :
  PRE_LINK = :
  TARGET_COMPILE_PDB = CMakeFiles/test_ext.dir/
  TARGET_FILE = test_ext
  TARGET_PDB = test_ext.pdb

Cc: @brad.king

No.

It isn’t supposed to. See the GENEX_EVAL expression.

This is not typical usage. The LINK_LIBRARIES of a target are meant to be private. Use just

target_link_libraries(test_ext PUBLIC lib_a)

This looks like a x-y problem. Please describe what you are really trying to do, perhaps in a new topic dedicated to your use case rather than the genex error.

Thanks for the information. Even after reading the linked documentation it’s not easy to understand how generator expressions are evaluated.

I discovered this problem by adding:

target_link_libraries(lib_a
    PUBLIC
        $<1:lib_b>
)

It took me a while to realise that the build failed because someone had used in a completely different place (scope was actually PRIVATE and not PUBLIC as in my first message):

target_link_libraries(test_ext
    PRIVATE
        $<TARGET_PROPERTY:lib_a,LINK_LIBRARIES>
)

I agree that this is not typical usage and I don’t understand why it was used in this case. But there it was and it made the build fail. Luckily I can modify that part of the code too and remove this redundant generator expression. It would be nice if CMake could detect this kind of errors and print a useful error message instead of creating invalid build.ninja file.

Part of the confusion is that in this example all generator expressions are evaluated:

cmake_minimum_required(VERSION 3.25)

project(TEST)

add_executable(test_ext dummy.cpp)
add_library(lib_a STATIC dummy.cpp)
add_library(lib_b STATIC dummy.cpp)

target_include_directories(lib_a
    PUBLIC
        $<1:${CMAKE_CURRENT_LIST_DIR}/foo_dir>
)

target_include_directories(test_ext
    PRIVATE
        $<TARGET_PROPERTY:lib_a,INTERFACE_INCLUDE_DIRECTORIES>
)

After running commands:

touch dummy.cpp
mkdir foo_dir
cmake -S . -B out -G Ninja

There’s this in build.ninja:

build CMakeFiles/test_ext.dir/dummy.cpp.o: CXX_COMPILER__test_ext_ /host/workdir/tmp/cmake/gen_exp_inc/dummy.cpp || cmake_object_order_depends_target_test_ext
  DEP_FILE = CMakeFiles/test_ext.dir/dummy.cpp.o.d
  INCLUDES = -I/host/workdir/tmp/cmake/gen_exp_inc/foo_dir
  OBJECT_DIR = CMakeFiles/test_ext.dir
  OBJECT_FILE_DIR = CMakeFiles/test_ext.dir
  TARGET_COMPILE_PDB = CMakeFiles/test_ext.dir/
  TARGET_PDB = test_ext.pdb

There’s foo_dir in INCLUDES as one expects. It’s not easy to understand why generator expressions are evaluated here but not in the first example.