Skip to content

Commit

Permalink
Copy files instead of hard-linking on Windows
Browse files Browse the repository at this point in the history
Fixes an issue on Windows where when source and build directory are on different drives hard-linking
to files or directory fails as it doesn't work across filesystem boundaries. Note that symlinking is also
not possible because it requires administrator privileges on Windows.

The solution copies the files using the built-in cmake `configure_file(src dest COPYONLY)` command.
As this command only operates on files, if a directory is specified the files will be globbed recursively
and through symlinks.

Signed-off-by: Dominik Gschwind <dominik.gschwind99@gmail.com>
  • Loading branch information
N3xed authored and laukik-hase committed Aug 11, 2022
1 parent 17c0cf3 commit 7b428b1
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 21 deletions.
40 changes: 19 additions & 21 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,33 @@ endif()

# Create a symbolic link from ${base_name} in the binary directory
# to the corresponding path in the source directory.
# Note: Copies the file(s) on Windows.
function(link_to_source base_name)
# Get OS dependent path to use in `execute_process`
if (CMAKE_HOST_WIN32)
#mklink is an internal command of cmd.exe it can only work with \
string(REPLACE "/" "\\" link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
string(REPLACE "/" "\\" target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
else()
set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")
endif()
set(link "${CMAKE_CURRENT_BINARY_DIR}/${base_name}")
set(target "${CMAKE_CURRENT_SOURCE_DIR}/${base_name}")

if (NOT EXISTS ${link})
# Linking to non-existent file is not desirable. At best you will have a
# dangling link, but when building in tree, this can create a symbolic link
# to itself.
if (EXISTS ${target} AND NOT EXISTS ${link})
if (CMAKE_HOST_UNIX)
set(command ln -s ${target} ${link})
execute_process(COMMAND ln -s ${target} ${link}
RESULT_VARIABLE result
ERROR_VARIABLE output)

if (NOT ${result} EQUAL 0)
message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
endif()
else()
if (IS_DIRECTORY ${target})
set(command cmd.exe /c mklink /j ${link} ${target})
file(GLOB_RECURSE files FOLLOW_SYMLINKS LIST_DIRECTORIES false RELATIVE ${target} "${target}/*")
foreach(file IN LISTS files)
configure_file("${target}/${file}" "${link}/${file}" COPYONLY)
endforeach(file)
else()
set(command cmd.exe /c mklink /h ${link} ${target})
configure_file(${target} ${link} COPYONLY)
endif()
endif()

execute_process(COMMAND ${command}
RESULT_VARIABLE result
ERROR_VARIABLE output)

if (NOT ${result} EQUAL 0)
message(FATAL_ERROR "Could not create symbolic link for: ${target} --> ${output}")
endif()
endif()
endfunction(link_to_source)

Expand Down
3 changes: 3 additions & 0 deletions ChangeLog.d/fix_hard_link_across_drives
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Bugfix
* Fix a build issue on Windows where the source and build directory could not be on
different drives (#5751).

0 comments on commit 7b428b1

Please sign in to comment.