aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGleb Popov <6yearold@gmail.com>2016-06-06 20:54:50 +0300
committerl10n daemon script <scripty@kde.org>2016-11-07 14:13:47 +0000
commit5f2dd74fe42120eed4f23b0432b43b749b4e45e9 (patch)
treef9e2bcd0dc6633d8b7102ac725ef690a61177125
parentb60f1116b2cf0401f52060d7bdf0c524b918d23a (diff)
downloadextra-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.cmake144
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()