Evaluate $<JOIN:> with another generator expression as the glue

Consider the following project which tries to create a JOIN genex which uses $<TARGET_PROPERTY> as glue

cmake_minimum_required(VERSION 3.16)

project(proj LANGUAGES CXX)

set(source "${CMAKE_CURRENT_BINARY_DIR}/source.cpp")
file(WRITE "${source}" "void foo() {}")
add_library(lib1 ${source})

set(source "${CMAKE_CURRENT_BINARY_DIR}/source2.cpp")
file(WRITE "${source}" "void foo() {}")
add_library(lib2 ${source})

set_target_properties(lib1 PROPERTIES _prop "plug1;plug2")
set_target_properties(lib2 PROPERTIES _prop "plug3;plug4")

set(pre_genex "$<TARGET_PROPERTY:")
set(post_genex ",_prop>")
set(glue "${post_genex}\n${pre_genex}")

set(join "${pre_genex}$<JOIN:lib1;lib2,${glue}>${post_genex}")

# What this is supposed to do is surround each lib with $<TARGET_PROPERTY:,_prop>
# to extract the _prop from each lib in the given list.
# The genex looks kinda like this
# $<TARGET_PROPERTY: $<JOIN:lib1;lib2, ,_prop> \n $<TARGET_PROPERTY: >   ,_prop>

# Desired intermediate content
# $<TARGET_PROPERTY:lib1,_prop> \n $<TARGET_PROPERTY:lib2,_prop>

# Desired final content
# plug1;plug2
# plug3;plug4


message(">>>> join:\n${join}")
file(GENERATE
     OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/out.txt"
     CONTENT "join_content:\n${join}"
)

This fails generation with Error evaluating generator expression: $<TARGET_PROPERTY:>
I tried sprinkling escaping characters, bracket arguments, GENEX_EVALs to no avail.
Is something like this doable?

Have you tried using string(JOIN ...) instead of the $<JOIN:...> generator expression?

The key is to prevent CMake from interpreting some things as generator expressions too early, then evaluating them later when you need them to be. This should give you the result you want:

set(pre_genex "$$<1:<TARGET_PROPERTY:>")
set(post_genex "$<COMMA>_prop$<ANGLE-R>")
set(glue "${post_genex}\n${pre_genex}")

set(join "$<GENEX_EVAL:${pre_genex}$<JOIN:lib1;lib2,${glue}>${post_genex}>")

Is $$ the canonical (documented) way to escape dollar?
My hack looked like this: $<1:$>

I don’t think it’s really an escape. It’s just that the first $ is not followed by < and so doesn’t get interpreted as a genex start during the first evaluation (the second $ is, forming $<1). Then, at the second evaluation, the left-as-is $ combines with the <TARGET_PROPERTY: produced by the $<1: genex, yielding $<TARGET_PROPERTY:, which is a normal genex start and thus treated as such.

Thanks, that works nicely!

As @Angew mentioned, it isn’t so much about escaping the $, it’s about preventing CMake from seeing $<... and treating it as the start of a generator expression. Your alternative of $<1:$> should also work.

Thanks for the explanation, I did read $$<TARGET_PROPERTY which is not what you wrote. :roll_eyes: