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

Yir*_*kha 14 windows dll path cmake visual-studio

我正在尝试将基于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)
Run Code Online (Sandbox Code Playgroud)

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

但是,在Windows上,这些类通常不会安装到系统中,而是提取或编译到构建树中或附近的某个任意目录中.为了TableGenerator运行,foo.dll需要将其提供或复制到其中一个动态链接库搜索顺序路径(例如,%WINDIR%\System32或构建输出目录TableGenerator),这是不可取的.

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

Yir*_*kha 16

在进行研究以正确提问时,我找到了三种解决方案.考虑到找到这些信息有多难,我决定在这里发布问题和答案.


1.使用全局变量 CMAKE_MSVCIDE_RUN_PATH

有一个特殊的变量专门用于解决这个确切的问题 - CMAKE_MSVCIDE_RUN_PATH.如果设置,则会将这样的行添加到自定义构建步骤脚本中:

set PATH=<CMAKE_MSVCIDE_RUN_PATH>;%PATH%
Run Code Online (Sandbox Code Playgroud)

所以所需要的就是这样的好地方:

set(CMAKE_MSVCIDE_RUN_PATH ${LibFoo_RUNTIME_LIBRARY_DIRS})
Run Code Online (Sandbox Code Playgroud)

我最初只在CMake源中注意到这个变量,因为它曾经在CMake 3.10之前没有记录.因此,您可能无法在旧版本的CMake的文档中找到它,但不要担心,它自2006年以来一直受到支持.

优点:
▪可以在一个中心位置启用
▪在add_custom_command()其他地方的任何命令中根本不需要改变
▪只设置路径本身,不需要明确写入批处理命令
▪具有明确名称和意图的显而易见的选择

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


2.使用两个COMMAND参数显式设置PATH

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

对文件add_custom_command()说:

COMMAND
指定在构建时执行的命令行.如果超过一个COMMAND指定他们将在顺序执行,但并不一定构成为有状态shell或批处理脚本.

所以不,这不能保证是可能的.但Visual Studio项目生成器实际上是这样做的,即各个命令只是一个接一个地附加,所以以下工作:

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)
Run Code Online (Sandbox Code Playgroud)

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

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


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

上述引用的文档add_custom_command()仍在继续:

要运行完整脚本,请使用configure_file()命令或file(GENERATE)命令创建它,然后指定a COMMAND以启动它.

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

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)
Run Code Online (Sandbox Code Playgroud)

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

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

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


4.通过CMake包装器启动自定义命令

请参阅以下由Dvir Yitzchaki提供的其他答案.


我个人已经解决了#1的解决方案,因为它很简单,甚至在它被版本3.10中的CMake正确记录和支持之前.这对你来说应该是最好的方式,除非你需要做一些更特殊的事情.


Dvi*_*aki 11

除了 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)
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅http://www.cmake.org/pipermail/cmake/2006-March/008522.html