如何在 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。
我正在尝试将基于 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 oneCOMMAND
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 thefile(GENERATE)
command to create it, and then specify aCOMMAND
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。