stillanoob
stillanoob

Reputation: 1379

Symbolic links CMake

I want to rename certain executables in CMakeLists.txt but also want symbolic links from the older names to new files for backward compatibility. How can this be accomplished on systems that support symbolic links?

Also what are the alternatives for system that does not support symbolic links?

Upvotes: 26

Views: 64172

Answers (9)

Eyjafl
Eyjafl

Reputation: 2215

There is file(CREATE_LINK <original> <linkname> SYMBOLIC) since CMake 3.14. It creates a symlink at configure time.

Not sure this is what OP asked for, but this is what I was looking for when found this question.

Upvotes: 1

Philippe Carphin
Philippe Carphin

Reputation: 287

If what you are looking for is links for your executables and library files based on version numbers you can let CMake take care of that for you by setting the appropriate properties SOVERSION and VERSION on your targets:

The following 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)

produces the following tree inside 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

References:

Upvotes: 0

Emil
Emil

Reputation: 526

You can create a custom target and use CMake to create symlinks

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

This will only work on systems that support symlinks, see the documentation.

Before CMake v3.14, this did not work on Windows. In v3.13, support for Windows was added.

Upvotes: 35

Serg Kryvonos
Serg Kryvonos

Reputation: 4677

Lets say you need to create a link in binary dir to a target located in source directory.

You can try file CREATE_LINK since version 3.14

${CMAKE_COMMAND} -E create_symlink is accessible at Windows since 3.17

You can use execute_process since early cmake versions:

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()

Upvotes: 5

lenz
lenz

Reputation: 2425

When I build, I create a symlink to the compile_commands.json. This helps clangd and vscode's intellisense.

Here's a full working example of what worked for me.

add_custom_command is what creates the symlink.

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
)

Upvotes: 2

bam
bam

Reputation: 194

I've added the check to @ulidtko's approach, so symlink doesn't overridden on every rebuild unconditionally:

install(CODE "if (NOT EXISTS ${link})
                  execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \
                                                                ${target} \
                                                                ${link})
              endif()" )

Upvotes: 1

Rinzler
Rinzler

Reputation: 49

I've struggled with this a few different ways with the above responses in order to install '.so.#' files that refer to other '.so.#.#' files. I've had success by not introducing a link to the file, but by installing the '.so.#.#' file as the '.so.#' file.

I.e. instead of

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)")

Which didn't quite work for me even. I have instead had success by doing this.

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

Not 'exactly' the same, but sufficient. Don't defeat the problem, solve the problem.

Upvotes: 0

ulidtko
ulidtko

Reputation: 15642

Another way to do it:

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

This way the symlinking will be done during make install only.

Upvotes: 33

Rian Quinn
Rian Quinn

Reputation: 1996

Another method that is a bit more verbose and only runs on install:

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)

Use it like this (similar to ln -s):

install_symlink(filepath sympath)

Upvotes: 21

Related Questions