如何在自定义 CMake 命令中包含文字双引号?

How do I include a literal double quote in a custom CMake command?

我正在尝试创建一个使用一些环境变量运行的自定义命令,例如 LDFLAGS,如果它的值包含空格,则需要用引号引起来:

LDFLAGS="-Lmydir -Lmyotherdir"

由于 CMake 的转义规则,我找不到在 CMake 自定义命令中包含此参数的方法。到目前为止,这是我尝试过的方法:

COMMAND LDFLAGS="-Ldir -Ldir2" echo blah VERBATIM)

产量"LDFLAGS=\"-Ldir -Ldir2\"" echo blah

COMMAND LDFLAGS=\"-Ldir -Ldir2\" echo blah VERBATIM)

产量LDFLAGS=\"-Ldir -Ldir2\" echo blah

似乎我得到了整个字符串的引号,或者转义引号在用作命令的一部分时无法解析。

我希望能有一种方法来包含文字双引号,或者作为一种更好的方法来为命令设置环境变量。请注意,我仍在使用 CMake 2.8,因此我没有 3.2 中可用的新 "env" 命令。

请注意,这不是 的副本,因为这些引用方法中的 none 适用于这种特殊情况。

好的,我删除了原来的答案,因为@Florian 提出的答案更好。多个引用的 args 需要进行一项额外的调整。考虑这样的环境变量列表:

set(my_env_vars LDFLAGS="-Ldir1 -Ldir2" CFLAGS="-Idira -Idirb")

为了产生所需的扩展,转换为字符串,然后将 ; 替换为 space。

set(my_env_string "${my_env_vars}") #produces LDFLAGS="...";CFLAGS="..."
string(REPLACE ";" " " my_env_string "${my_env_string}")

然后您可以继续@Florian 的精彩回答并添加自定义启动规则。如果您的字符串中需要分号,那么您需要先将它们转换为其他内容。

请注意,在这种情况下,我不需要使用 env:

启动
set_target_properties(mytarget PROPERTIES RULE_LAUNCH_CUSTOM "${my_env_string}")

这当然取决于你的shell。

再想一想,我的原始答案如下,因为我也有无法访问目标名称的情况。

set(my_env LDFLAGS=\"-Ldir -Ldir2" CFLAGS=\"-Idira -Idirb\")
add_custom_command(COMMAND sh -c "${my_env} grep LDFLAGS" VERBATIM)

此技术仍然需要替换列表->字符串转换中的分号。

最明显的选择——在达到 COMMAND 的边界时通常被推荐,尤其是对于旧版本的 CMake——是使用外部脚本。

我只是想添加一些简单的 COMMAND 变体,这些变体可以正常工作并且不需要 shell,但是 - 我不得不承认 - 仍然部分依赖于平台。

  • 一个例子是只将引用的部分放入变量中:

    set(vars_as_string "-Ldir -Ldir2")
    add_custom_target(
        QuotedEnvVar
        COMMAND env LD_FLAGS=${vars_as_string} | grep LD_FLAGS
    )
    

    实际上确实转义了 space 而不是引号。

  • 另一个例子是添加转义引号作为 "launcher" 规则:

    add_custom_target(
        LauncherEnvVar
        COMMAND env | grep LD_FLAGS
    )
    set_target_properties(
        LauncherEnvVar 
        PROPERTIES RULE_LAUNCH_CUSTOM "env LD_FLAGS=\"-Ldir -Ldir2\""
    )
    

编辑:添加了无需转义引号的多个引用参数的示例

  • 另一个示例是在函数中 "hide some of the complexity" 并且 - 如果您想将其添加到所有自定义命令调用 - 使用 global/directory RULE_LAUNCH_CUSTOM 属性:

    function(set_env)
        get_property(_env GLOBAL PROPERTY RULE_LAUNCH_CUSTOM)
        if (NOT _env)
            set_property(GLOBAL PROPERTY RULE_LAUNCH_CUSTOM "env")
        endif()
        foreach(_arg IN LISTS ARGN)
            set_property(GLOBAL APPEND_STRING PROPERTY RULE_LAUNCH_CUSTOM " ${_arg}")
        endforeach()
    endfunction(set_env)
    
    set_env(LDFLAGS="-Ldir1 -Ldir2" CFLAGS="-Idira -Idirb")
    
    add_custom_target(
        MultipleEnvVar
        COMMAND env | grep -E 'LDFLAGS|CFLAGS'
    )
    

备选方案(对于 CMake >= 3.0)

  • 我想我们在这里真正要找的东西(除了 cmake -E env ...)被命名为 Bracket Argument 并且允许任何字符而不需要添加反斜杠:

    set_property(
        GLOBAL PROPERTY 
            RULE_LAUNCH_CUSTOM [=[env LDFLAGS="-Ldir1 -Ldir2" CFLAGS="-Idira -Idirb"]=]
    )
    add_custom_target(
        MultipleEnvVarNew
        COMMAND env | grep -E 'LDFLAGS|CFLAGS'
    )
    

参考资料

您需要 三个 个反斜杠。我最近需要这个来从 PkgConfig 获取预处理器定义并将其应用于我的 C++ 标志:

pkg_get_variable(SHADERDIR movit shaderdir)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSHADERDIR=\\"${SHADERDIR}\\"")

一些 folks 建议使用 ${CMAKE_COMMAND} 并将您的可执行文件作为参数传递,例如:

COMMAND ${CMAKE_COMMAND} -E env "$(WindowsSdkDir)/bin/x64/makecert.exe" ...

这对我有用。

Florian 的回答在几个方面是错误的:

  • 将引用部分放在变量中没有区别。
  • 你绝对应该使用 VERBATIM。它修复了特定于平台的引用错误。
  • 你绝对不应该为此使用 RULE_LAUNCH_CUSTOM。它不适用于此,仅适用于某些生成器。
  • 您不应该使用 env 作为命令。它在 Windows.
  • 上不可用

事实证明,OP 代码不起作用的真正原因是 CMake 总是完全引用 COMMAND 之后的第一个单词,因为它应该是可执行文件的名称。你根本不应该把环境变量放在第一位。

例如:

add_custom_command(
    OUTPUT q1.txt
    COMMAND ENV_VAR="a b" echo "hello" > q1.txt
    VERBATIM
)

add_custom_target(q1 ALL DEPENDS q1.txt)
$ VERBOSE=1 make
...
"ENV_VAR=\"a b\"" echo hello > q1.txt
/bin/sh: ENV_VAR="a b": command not found

那么如何传递带空格的环境变量呢?简单。

add_custom_command(
    OUTPUT q1.txt
    COMMAND ${CMAKE_COMMAND} -E env ENV_VAR="a b" echo "hello" > q1.txt
    VERBATIM
)