You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake JOIN生成器表达式引号处理行为差异及正确引用方法

行为差异的核心原因

两个$<JOIN>表达式的求值逻辑完全一致,差异来自CMake对add_custom_command中COMMAND参数的词法解析规则,和你写的参数外层引号的位置直接相关。

关键解析规则

CMake处理命令参数时,引号相关的核心逻辑只有两条:

  1. 首尾引号自动剥离:如果一个参数的第一个非空白字符是未转义双引号,且最后一个非空白字符是对应闭合的未转义双引号,这对首尾双引号会被识别为参数分隔符,直接丢弃,不会出现在最终参数值中;其他所有位置的双引号(包括转义得到的\"、不在参数首尾位置的未转义引号)都会作为普通字面量保留。
  2. 含空格参数自动补引号:如果生成器表达式求值完成后,参数值包含空格且没有被引号包裹,CMake会自动给整个参数外层补一对双引号,避免空格把参数拆分成多个部分;如果参数值里本身已经包含引号,CMake不会再自动补外层引号。

逐案例拆解

1. 包含目录的JOIN为什么输出正常

你写的包含目录参数如下:

-I"$<JOIN:$<TARGET_PROPERTY:Tutorial,INCLUDE_DIRECTORIES>,\" -I\">"

这个参数的第一个字符是-,不是双引号,不满足首尾引号剥离规则,所以-I后面、表达式末尾的两个双引号都会作为普通字面量保留。
参数内的转义引号\"会被解析为字面量双引号,传入JOIN的分隔符为" -I",最终拼接结果为:

-I"include files/a" -I"include files/b"

结果首尾不是配对双引号,CMake不会做额外修改,输出完全符合预期。

2. 初版编译选项JOIN为什么丢首尾引号

你最开始写的编译选项参数如下:

"$<JOIN:$<TARGET_PROPERTY:Tutorial,COMPILE_OPTIONS>,\" \">"

这个参数的第一个字符是未转义双引号,最后一个字符是对应闭合的未转义双引号,完全满足首尾引号剥离规则,最外层的这对引号会被直接丢弃,不会进入后续求值流程。
参数内的转义引号解析为字面量双引号,传入JOIN的分隔符为" ",最终拼接结果为:

/DC=1" "/DD=2

结果本身已经包含引号,CMake不会自动补外层引号,就出现了你看到的首尾引号丢失的问题。

3. 改空格分隔符后为什么整个字符串被包在引号里

你把分隔符改成普通空格后的参数如下:

"$<JOIN:$<TARGET_PROPERTY:Tutorial,COMPILE_OPTIONS>, >"

这个参数同样满足首尾引号剥离规则,最外层引号先被丢弃。JOIN用空格拼接两个编译选项后得到/DC=1 /DD=2,结果包含空格且没有任何引号,触发CMake的自动补引号逻辑,最终输出为:

"/DC=1 /DD=2"

正确实现方案

要实现每个编译选项单独包裹引号的效果,只要规避参数首尾是未转义双引号的情况即可,和包含目录的写法逻辑对齐:

add_custom_command(
   OUTPUT Tutorial.i
   COMMAND "${CMAKE_C_COMPILER}" 
           -I"$<JOIN:$<TARGET_PROPERTY:Tutorial,INCLUDE_DIRECTORIES>,\" -I\">"
           "\"$<JOIN:$<TARGET_PROPERTY:Tutorial,COMPILE_OPTIONS>,\" \" >\""
           /P ${CMAKE_CURRENT_SOURCE_DIR}/Tutorial.cxx
   MAIN_DEPENDENCY "Tutorial.cxx"
   COMMENT "Preprocessing Tutorial.cxx"
)

如果使用CMake 3.20及以上版本,也可以用内置的$<QUOTE>生成器表达式代替手动转义引号,写法更清晰不易出错:

"$<QUOTE>$<JOIN:$<TARGET_PROPERTY:Tutorial,COMPILE_OPTIONS>,$<QUOTE> $<QUOTE>>$<QUOTE>"

内容的提问来源于stack exchange,提问作者Brad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:27:24