diff options
author | Gleb Popov <6yearold@gmail.com> | 2016-06-06 20:54:50 +0300 |
---|---|---|
committer | l10n daemon script <scripty@kde.org> | 2016-11-07 14:13:47 +0000 |
commit | 5f2dd74fe42120eed4f23b0432b43b749b4e45e9 (patch) | |
tree | f9e2bcd0dc6633d8b7102ac725ef690a61177125 | |
parent | b60f1116b2cf0401f52060d7bdf0c524b918d23a (diff) | |
download | extra-cmake-modules-5.28.0-rc2.tar.gz extra-cmake-modules-5.28.0-rc2.tar.bz2 |
Add ecm_win_resolve_symlinks module.v5.28.0-rc2
REVIEW: 128112
(cherry picked from commit 632b8868de38e2f85fb4969bcae3dd89a2a0695c)
-rw-r--r-- | modules/ECMWinResolveSymlinks.cmake | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/modules/ECMWinResolveSymlinks.cmake b/modules/ECMWinResolveSymlinks.cmake new file mode 100644 index 00000000..d9444663 --- /dev/null +++ b/modules/ECMWinResolveSymlinks.cmake @@ -0,0 +1,144 @@ +#.rst: +# ECMWinResolveSymlinks +# -------------------------- +# +# Resolve pseudo-symlinks created by git when cloning on Windows. +# +# :: +# +# ecm_win_resolve_symlinks(<dir>) +# +# When git checks out a repository with UNIX symlinks on Windows machine, +# it creates a text file for each symlink, containing a relative path to the +# real file. +# This function would recursively walk over specified directory and replace +# pseudo-symlinks with corresponding real file's contents. It would then run +# git update-index --assume-unchanged on them to trick git. +# +# This is useful for projects like "breeze-icons" that contain many identical +# icons implemented as symlinks. +# +# Since 5.28 + +#============================================================================= +# Copyright 2016 Gleb Popov <6yearold@gmail.com> +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file COPYING-CMAKE-SCRIPTS for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of extra-cmake-modules, substitute the full +# License text for the above reference.) + +function(ECM_WIN_RESOLVE_SYMLINKS _dir) + get_filename_component(dir ${_dir} ABSOLUTE) + find_program(GIT_EXECUTABLE git) + if(NOT GIT_EXECUTABLE) + message(SEND_ERROR "Git executable not found!") + endif() + + message(STATUS "Resolving symlinks in ${dir}...") + _find_symlinks(${dir} symlinks) + _portioned_list(symlinks ${symlinks}) + foreach(s IN LISTS symlinks) + string(REPLACE ":" ";" s ${s}) + _assume_unchanged(NO ${dir} "${s}") + _checkout_symlinks(${dir} "${s}") + _resolve_symlinks(${dir} "${s}") + _assume_unchanged(YES ${dir} "${s}") + endforeach() + message(STATUS "Resolving symlinks in ${dir}... Done.") + + # touch cache every build to force CMake to re-run these functions everytime + if(NOT TARGET wrs_touch_cache) + add_custom_target(wrs_touch_cache ALL + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/CMakeCache.txt + ) + endif() +endfunction() + +function(_assume_unchanged mode dir symlinks) + if(mode) + set(flag --assume-unchanged) + else() + set(flag --no-assume-unchanged) + endif() + + execute_process(COMMAND ${GIT_EXECUTABLE} update-index ${flag} ${symlinks} + WORKING_DIRECTORY ${dir} + ) +endfunction() + +function(_find_symlinks dir outvar) + execute_process(COMMAND ${GIT_EXECUTABLE} ls-files -s + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE out + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + set(${outvar}) + if(out) + string(REPLACE "\n" ";" out ${out}) + + foreach(f IN LISTS out) + # 120000 0db97052931e18484b6705f3bc644484ef9403b0 0 <tab> icons-dark/actions/16/CVnamespace.svg + string(REPLACE "\t" ";" f "${f}") + string(REPLACE " " ";" f "${f}") + list(GET f 0 mode) + if(mode STREQUAL "120000") + list(GET f 3 symlink) + list(APPEND ${outvar} ${symlink}) + endif() + endforeach() + endif() + set(${outvar} ${${outvar}} PARENT_SCOPE) +endfunction() + +# In functions like _checkout_symlinks() the command line can become too lengthy for Windows. +# So we partition it, but in a hacky way due to CMake doesn't have list of lists. +function(_portioned_list outvar) + list(LENGTH ARGN arglen) + + if(arglen EQUAL 0) + set(${outvar} "" PARENT_SCOPE) + return() + endif() + + set(init) + set(tail) + math(EXPR range "${arglen} - 1") + foreach(i RANGE ${range}) + list(GET ARGN ${i} v) + string(LENGTH "${init}" initlen) + string(LENGTH ${v} vlen) + math(EXPR sumlen "${initlen} + ${vlen}") + if(sumlen LESS 8192) + list(APPEND init ${v}) + else() + list(APPEND tail ${v}) + endif() + endforeach() + + _portioned_list(tail_portioned ${tail}) + string(REPLACE ";" ":" init "${init}") # Generally this is not safe, because filepath can contain ':' character. But not on Windows. Phew. + set(${outvar} ${init} ${tail_portioned} PARENT_SCOPE) +endfunction() + +function(_checkout_symlinks dir symlinks) + execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${symlinks} + WORKING_DIRECTORY ${dir} + ) +endfunction() + +function(_resolve_symlinks dir symlinks) + foreach(s IN LISTS symlinks) + file(READ ${dir}/${s} sdata) + get_filename_component(sdir ${dir}/${s} DIRECTORY) + set(f "${sdir}/${sdata}") + file(READ ${f} fdata) + file(WRITE ${dir}/${s} ${fdata}) + endforeach() +endfunction() |