如何在 Windows 上为 CMake 自定义命令设置运行时路径

How to set runtime PATH for CMake custom command on Windows

我正在尝试将基于 CMake 的 *nix 项目移植到 Windows。主库需要的一个头文件是由自定义程序生成的,所以CMakeLists.txt文件包含这样的内容:

add_executable(TableGenerator "TableGenerator.cpp")
target_link_libraries(TableGenerator ${LibFoo_LIBRARY})

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND TableGenerator "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   DEPENDS TableGenerator)

一个重要的细节是 TableGenerator 使用外部共享库 LibFoo。例如在 Linux 下,一切正常,因为 libfoo.so 安装在 /usr/local/lib 等系统库目录之一中,或者 CMake 甚至在可执行文件中设置 rpath 属性,说明确切位置找到图书馆。

然而,在 Windows 上,这些类型的库通常不会安装到系统中,而只是被提取或编译到构建树中或附近的某个任意目录中。为了 TableGenerator 到 运行,foo.dll 需要可用或复制到 Dynamic-Link Library Search Order 路径之一(比如 %WINDIR%\System32 或构建输出TableGenerator 的目录),这是不可取的。

如何为自定义命令设置 PATH 环境变量,即不是 during the CMake run 而是在实际的自定义构建步骤 运行 期间使用?

虽然仍在进行研究以正确提出问题,但我找到了三个解决方案。考虑到找到这些信息有多么困难,我决定 post 在这里问答。


1。使用全局变量 CMAKE_MSVCIDE_RUN_PATH

有一个专门用于解决这个确切问题的特殊变量 – CMAKE_MSVCIDE_RUN_PATH。如果设置,它会导致像这样的一行被添加到自定义构建步骤脚本中:

set PATH=<CMAKE_MSVCIDE_RUN_PATH>;%PATH%

因此,所需要的只是在一个好地方做这样的事情:

set(CMAKE_MSVCIDE_RUN_PATH ${LibFoo_RUNTIME_LIBRARY_DIRS})

我最初只在 CMake 源代码中注意到这个变量,因为它在 CMake 3.10 之前一直没有记录。所以你可能无法在旧版本 CMake 的文档中找到它,但别担心,它已经 supported since 2006.

优点:
▪ 可以在一个中心位置启用
▪ 无需对其他地方的任何 add_custom_command() 命令进行任何更改
▪ 仅设置路径本身,无需显式写入批处理命令
▪ 明确名称和意图的显而易见的选择

缺点:
▪ 整个 CMake 项目和所有自定义命令的全局
▪ 仅适用于 "Visual Studio 9 2008" 及以上的生成器


2。使用两个 COMMAND 参数

显式设置 PATH

为 Visual Studio 中的自定义构建步骤生成的脚本包含一些序言,然后是命令本身,然后是一些结语。难道不能通过另一个 COMMAND 参数在真正的命令之前简单地添加 set PATH=... 吗?

documentation for add_custom_command() 说:

COMMAND
Specify the command-line(s) to execute at build time. If more than one COMMAND is specified they will be executed in order, but not necessarily composed into a stateful shell or batch script.

所以不,不能保证这会成为可能。但是 Visual Studio 项目生成器实际上 does it like this,即各个命令只是一个接一个地附加,所以下面的工作:

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND set "PATH=${LibFoo_RUNTIME_LIBRARY_DIRS};%PATH%"
                   COMMAND TableGenerator "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   DEPENDS TableGenerator)

优点:
▪ 可以显式更改每个自定义命令的路径

缺点:
▪ 依赖于生成器的未记录行为
▪ 有必要为 Windows 重写整个命令并保持两个版本同步
▪ 必须明确更改每个自定义命令


3。使用 file(GENERATE ...) 创建自定义脚本

上面引用的documentation for add_custom_command()继续:

To run a full script, use the configure_file() command or the file(GENERATE) command to create it, and then specify a COMMAND to launch it.

由于额外的临时文件和命令,这有点乱:

file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/RunTableGenerator.cmd"
              CONTENT "set PATH=${LibFoo_RUNTIME_LIBRARY_DIRS};%PATH%
                       %1 ${CMAKE_CURRENT_BINARY_DIR}/Table.h")
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
                   COMMAND "${CMAKE_CURRENT_BINARY_DIR}/RunTableGenerator.cmd" "$<TARGET_FILE:TableGenerator>"
                   DEPENDS TableGenerator)

请注意将路径作为参数发送到可执行文件的尴尬方式。这是必要的,因为脚本只写一次,但 TableGenerator 可能在不同的位置用于不同的配置(调试和发布)。如果在内容中直接使用生成器表达式,则会打印 CMake 错误,并且除了一种配置外,项目将无法正确构建。

优点:
▪ 可以为每个自定义命令显式更改 PATH
▪ 完整记录和推荐的解决方案

缺点:
▪ CMakefiles
中非常嘈杂 ▪ 有必要为 Windows 重写整个命令并保持两个版本同步
▪ 必须明确更改每个自定义命令


4。通过 CMake wrapper

启动自定义命令

请参阅下面由 Dvir Yitzchaki 提供的其他答案。


我个人选择了解决方案 #1,因为它简洁明了,甚至在 CMake 在 3.10 版中得到正确记录和支持之前。这对你来说也是最好的前进方式,除非你需要做一些更特别的事情。

除了 Yirkha 写的方法之外还有另一种方法,那就是 运行 通过 cmake 的可执行文件并使用 cmake 的 -E 选项来设置环境。

所以在你的情况下它将是:

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
               COMMAND ${CMAKE_COMMAND} -E env "PATH=${LibFoo_RUNTIME_LIBRARY_DIRS}" $<TARGET_FILE:TableGenerator> "${CMAKE_CURRENT_BINARY_DIR}/Table.h"
               DEPENDS TableGenerator)

详情见http://www.cmake.org/pipermail/cmake/2006-March/008522.html