如何正确地将CMake列表(以分号分隔)传递给set_target_properties函数的flags参数?

53

CMake的列表本质上只是由分号分隔的字符串,但如果您将这样的变量传递给一个命令,它会被扩展为多个参数 - 例如:

set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
add_custom_command(
  ...
  COMMAND my_cmd ${FLAGS}
  ...
)

将正确调用my_cmd f1 f2 f3

现在,如果我使用以下方式:

set_target_properties(
  myTarget PROPERTIES
  LINK_FLAGS  "${LD_FLAGS}"
)

扩展并没有发生,结果我只得到了一个包含分号的单个LD_FLAG,毫无用处,而不是将其扩展为一个以空格分隔的字符串。

是否有办法使得当我将列表传递给LINK_FLAGS属性(或任何其他属性)时,它可以被扩展为多个参数,而不仅仅是一个?

7个回答

32

我不认为 set_target_properties 能够自动地进行扩展,但是你可以使用 string (REPLACE ...) 将列表扩展成一个以空格分隔的字符串:

string (REPLACE ";" " " LD_FLAGS_STR "${LD_FLAGS}")
set_target_properties(
  myTarget PROPERTIES
  LINK_FLAGS  "${LD_FLAGS_STR}"
)

42
这真的是最佳方式吗?为什么CMake被设计为使用不兼容其自身set_target_properties命令的列表格式? - David Grayson
这种技术的主要问题在于,如果任何标志包含空格,它们将会引起问题。如果可能的话(例如,如果包括外部提供的文件名,其中可能包含空格),则需要应用一些转义来保留参数中的空格。 - Colin D Bennett
4
B+++T它不起作用。string(REPLACE ";" " " DEST ${SRC})将分号替换为…一个空字符串。只有这个可以工作:foreach(OPT ${LD_FLAGS}) set(LINK_FLAGS "${LINK_FLAGS} ${OPT}") endforeach()。如果这种c+++p(指编译器)不是今天最流行的构建工具,那就真的很有趣了。 - Ethouris
7
@Ethouris 的命令 string (REPLACE ";" " " DEST "${SRC}") 是有效的;你好像忘了在最后一个参数周围加上引号。也许那些“+”妨碍了你。 - Angew is no longer proud of SO
3
好的。这个${SRC}里面包含了分号;,并且以这种形式在调用时进行了扩展(所以替换甚至没有机会生效)。至少值得记住的是,CMake 重复了和 Bash、Perl 相同的愚蠢错误。 - Ethouris
如果一个单独的参数包含';'会发生什么?那么字符串替换(如果有的话)需要使用cmake的内部转义机制来处理。这是一个混乱的解决方案。相比之下,Ethouris的解决方案对我来说更加清晰。 - Edy

21

要将cmake列表用作列表,请使用

${LD_FLAG}

如果要将cmake列表用作字符串(即使用';'分隔列表项),请使用

"${LD_FLAG}"

那么在你的情况下,只需删除 "" 应该就足够了。


4
奇怪的是,这个版本3.9对我没起作用:set_target_properties被调用时参数数量不正确。 - Andry
@Andry,如果你在前面插入“MESSAGE(“$ {LD_FLAG}”)”,它会显示什么? - Ding-Yi Chen
@Ding-Yi Chen,我没有使用那个。相反,我一直在使用一组 LINK_FLAGS_* 变量。这里的想法是尽量避免使用 list as string。而是使用类似于 set(PROP_LIST "${PROP_LIST} ...")set_target_properties(... LINK_FLAGS_* "${PROP_LIST}") 的东西。试图将 PROP_LIST 用作列表会破坏 set_target_properties - Andry

9

Steveire - 我错误地认为它不在2.8.11中,因为我试图将set_target_properties(foo ...)更改为set_properties(TARGET foo ...)。但现在我看到命令名称的复数形式和单数形式有所不同。正确的名称是set_property(以y结尾)。 - Mark Lakata
2
可以按照这个答案的方式使用set_property(),但是CMake将LINK_FLAGS视为普通字符串,而不是列表——这意味着在此答案中,链接传递了单个参数'foo;bar'。这很不幸。还有一个有趣的地方是现在有一个COMPILE_OPTIONS属性,它是一个列表,但是旧的COMPILE_FLAGS是一个普通字符串。 - Colin D Bennett

2

我像使用字符串一样使用它

set(FLAGS " f1 f2 f3")

注意前面的空格,它允许您连接其他标志集。

对于更复杂的项目,您可以使用双重扩展技巧,而不是if-else:

set(GCC_FLAGS " -Wl,--relax")
set(DIAB_FLAGS " -tPPCE500ES:cross")
set(MSVC_FLAGS " /RAINBOW_CRAP)
# ...
# ...LINUX_FLAGS, WINDOWS_FLAGS, etc...


set_target_properties(
  myTarget PROPERTIES
  LINK_FLAGS  "${${COMPILER}_FLAGS} ${${SYSTEM}_FLAGS}"
)

# COMPILER and SYSTEM is set somewhere else, in toolchain files for example

0
从CMake 3.12开始,您可以使用list(JOIN <list> <glue> <output variable>)命令在此处描述
set(FLAGS f1 f2 f3)
# now FLAGS == 'f1;f2;f3'
list(JOIN FLAGS " " FLAGS) # Note: Overwrites FLAGS variable
# now FLAGS == 'f1 f2 f3'
add_custom_command(
  ...
  COMMAND my_cmd ${FLAGS}
  ...
)

这比使用 string(REPLACE ...) 更直接。


0
separate_arguments(LD_FLAGS_AS_LIST  NATIVE_COMMAND  LD_FLAGS)

set_target_properties(
  myTarget PROPERTIES
  LINK_FLAGS  "${LD_FLAGS_AS_LIST}"
)

-1
在cmake 3.x中有几种方法。
顶层的CMakeLists.txt文件看起来像这样:
# The name of the included file could be anything,
# it doesn't have to be called CMakeLists.txt
include(foo/CMakeLists.txt)
include(bar/CMakeLists.txt)

add_executable(myApp ${myApp_SOURCES})

子目录文件结构大致如下:

list(APPEND myApp_SOURCES
    ${CMAKE_CURRENT_LIST_DIR}/foo.cpp
    ${CMAKE_CURRENT_LIST_DIR}/foo_p.cpp
)

使用target_sources()增强源文件处理能力


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接