符号链接 CMake

23

我想要在 CMakeLists.txt 中重命名某些可执行文件,但又希望在新文件中保留旧名称的符号链接以实现向后兼容。如何在支持符号链接的系统上实现此目标?

如果系统不支持符号链接,则有哪些替代方法?


1
还有哪些系统不支持符号链接的替代方案?——复制文件而不是重命名。或者创建一个带有旧名称的脚本/小程序,只需执行新名称的程序即可。 - Tsyvarev
@Tsyvarev 如果我们谈论的是 MS-Windows,那么符号链接自 Win 7 起就已经可用了。任何早于此版本的应该都不再使用(甚至 Win 8 也不应再使用...)。Win 7 的符号链接只在实际可用于创建此类链接的命令行文档中记录...而且由于我已经很长时间没有使用 Windows 了,我不记得这个命令是什么了。 - Alexis Wilke
9个回答

31

另一种方法:

INSTALL(CODE "execute_process( \
    COMMAND ${CMAKE_COMMAND} -E create_symlink \
    ${target} \
    ${link}   \
    )"
)

这样做符号链接将只会在make install期间完成。


这样符号链接将在每次构建时被覆盖。我该如何避免这种情况? - bam
@bam 不会的。make all 是“构建”,而 make install 则取决于你如何处理它:部署、打包步骤、分发步骤或构建工件的形成等等。你可以使用一个微不足道的 IF(NOT EXISTS ${target}) 来跳过在 make install 时重新创建符号链接,但我强烈建议不要这样做。这将是违反 KISS 的“意外智能”;你很可能会被陈旧的符号链接所困扰,而且更有可能是你的同事和/或后继者。不要这样做,找到另一个解决方案。 - ulidtko
@ulidtko 我得试一下,但是运行 make install 多次(在源代码进行了一些更改之后...)可能会比一个过期的符号链接创建更大的问题,除非 cmake 的 create_symlink 命令在已经存在时重新创建链接? - Alexis Wilke

29

您可以创建自定义目标,并使用CMake创建符号链接。

ADD_CUSTOM_TARGET(link_target ALL
                  COMMAND ${CMAKE_COMMAND} -E create_symlink ${target} ${link})

这仅适用于支持符号链接的系统,请参见文档

在CMake v3.14之前,这在Windows上无法工作。在v3.13中,添加了对Windows的支持


从CMake 3.13开始,create_symlink在Windows上可用。https://cmake.org/cmake/help/latest/release/3.13.html?highlight=create_symlink#command-line - user15308163

20

另一种方法略微冗长,并且仅在安装时运行:

macro(install_symlink filepath sympath)
    install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})")
    install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")")
endmacro(install_symlink)

像这样使用它(类似于ln -s):

install_symlink(filepath sympath)

4
假设你需要创建一个链接,将二进制目录中的目标文件链接到源目录中的文件。
自CMake 3.14版本起,你可以尝试使用file CREATE_LINK命令。
自CMake 3.17版本起,Windows系统就可以访问${CMAKE_COMMAND} -E create_symlink命令。
你也可以在早期版本的CMake中使用execute_process命令。
if(WIN32)
  get_filename_component(real_path "${dirname}" REALPATH)
  string(REPLACE "/" "\\" target_path "${real_path}")
  execute_process(
    COMMAND cmd /C mklink /J ${dirname} "${target_path}"
    WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
    )
else()
  execute_process(COMMAND "ln -s ${CMAKE_CURRENT_SOURCE_DIR}/${dirname} ${CMAKE_CURRENT_BINARY_DIR}/${dirname}")
endif()

1
当我构建时,我会创建一个到compile_commands.json的符号链接。这有助于clangd和vscode的智能提示。
以下是一个完整可用的示例。 add_custom_command是创建符号链接的命令。
cmake_minimum_required(VERSION "3.1.0")

# project name
project("a-s-i-o")

set(CMAKE_CXX_FLAGS
    "${CMAKE_CXX_FLAGS} -std=c++17 -pthread -Wall -Wextra -Werror -pedantic -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-private-field"
)

# Create `compile_commands.json` file. 
#
# This is required by `clangd` to find the header files. For this to work, this
# file must be in the root of the project. Therefore, we create a symbolic link in the root that 
# points to the `compile_commands.json` file created by CMake in the `build` directory.
#
# This specific commands creates the file in the build directory. The command `add_custom_command` 
# creates the symbolic link in the root directory.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_executable(x main.cpp)

target_include_directories(x PUBLIC "/usr/local/include/asio-1.20.0/include")

# When target `x` is built, a symlink will be created to
# `build/compile_commands.json`. This is required for clangd to be able to find
# the header files included in the project.
add_custom_command(TARGET x
    COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json" "../compile_commands.json"
    DEPENDS compile_commands.json
    VERBATIM ON
)

# Print success message to the console
add_custom_command(TARGET x POST_BUILD
    COMMAND echo "Created symlink pointing to `compile_commands.json`"
    DEPENDS compile_commands.json
    VERBATIM ON
)

1

1
我已经在 @ulidtko 的方法中添加了检查,因此符号链接不会在每次重建时无条件覆盖:
install(CODE "if (NOT EXISTS ${link})
                  execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \
                                                                ${target} \
                                                                ${link})
              endif()" )

0
我曾以几种不同的方式努力解决这个问题,以上回复中介绍了如何安装引用其他“ .so.#.# ”文件的“ .so.# ”文件。我的成功之处在于没有将链接引入该文件,而是将“ .so.#.# ”文件安装为“ .so.# ”文件。
也就是说,不要像这样:
install(
    FILES
        .../libmpi.so.12.0
    DESTINATION lib
)

install(CODE 
    "EXECUTE_PROCESS( ${CMAKE_COMMAND} -E create_symlink lib/libmpi.so.12.0 lib/libmpi.so.12)")

这对我也不太起作用。我成功的方法是这样的。

install(FILES 
        .../libmpi.so.12.0
    RENAME libmpi.so.12
    DESTINATION lib
)

不是“完全”一样,但足够了。不要打败问题,解决问题。


0

如果您正在寻找根据版本号生成可执行文件和库文件的链接,您可以让CMake来为您处理,通过在目标上设置适当的属性SOVERSIONVERSION

以下是CMakeLists.txt文件内容:

cmake_minimum_required(VERSION 3.20)

project(version_links C)

add_library(mylib SHARED lib.c)
set_target_properties(mylib PROPERTIES
    PUBLIC_HEADER lib.h
    VERSION 3.2.1
    SOVERSION 3.2
)

add_executable(exec exec.c)
target_link_libraries(exec PRIVATE mylib)
set_target_properties(exec PROPERTIES
    VERSION 3.2.1
)

install(TARGETS mylib exec)

会在 CMAKE_INSTALL_PREFIX 目录下生成以下树形结构:

${CMAKE_INSTALL_PREFIX}
├── bin
│   ├── exec -> exec-3.2.1
│   └── exec-3.2.1
├── include
│   └── lib.h
└── lib
    ├── libmylib.so -> libmylib.so.3.2
    ├── libmylib.so.3.2 -> libmylib.so.3.2.1
    └── libmylib.so.3.2.1

参考资料:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接