Hi,

Patch add support for symlink handling in RPM packages.
Symlinks to directories and files are correctly recognized as such so
directory symlinks are no longer prefixed with %dir.
In case of relocatable RPM packages the patch also tries to make
symlink as relocatable as possible by making a relative path symlink
if needed.

Please review the patch and post the feedback.

If the patch is OK add it to repository.


Thanks,
Domen
From 0a3098dbeb79c90ac3882b13afa77e6e8ff98d62 Mon Sep 17 00:00:00 2001
From: Domen Vrankar <domen.vran...@gmail.com>
Date: Sat, 8 Nov 2014 22:01:19 +0100
Subject: [PATCH] CPackRPM handling of symbolic links

Try to make symbolic links as relocatable as possible for relocatable
rpm packages and to prevent directory symlinks from being added with
dir prefix to generated spec files
---
 Modules/CPackRPM.cmake | 217 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 199 insertions(+), 18 deletions(-)

diff --git a/Modules/CPackRPM.cmake b/Modules/CPackRPM.cmake
index 56d9b66..5e19d7c 100644
--- a/Modules/CPackRPM.cmake
+++ b/Modules/CPackRPM.cmake
@@ -395,6 +395,203 @@
 
 # Author: Eric Noulard with the help of Alexander Neundorf.
 
+function(CPackRPM_PrepareInstallFiles INSTALL_FILES_LIST WDIR PACKAGE_PREFIXES IS_RELOCATABLE)
+  # Prepend directories in ${CPACK_RPM_INSTALL_FILES} with %dir
+  # This is necessary to avoid duplicate files since rpmbuild do
+  # recursion on its own when encountering a pathname which is a directory
+  # which is not flagged as %dir
+  string(STRIP "${INSTALL_FILES_LIST}" INSTALL_FILES_LIST)
+  string(REPLACE "\n" ";" INSTALL_FILES_LIST
+                          "${INSTALL_FILES_LIST}")
+  string(REPLACE "\"" "" INSTALL_FILES_LIST
+                          "${INSTALL_FILES_LIST}")
+
+  foreach(F IN LISTS INSTALL_FILES_LIST)
+    if(IS_SYMLINK "${WDIR}/${F}")
+      if(IS_RELOCATABLE)
+        # check that symlink has relocatable format
+        get_filename_component(SYMLINK_LOCATION_ "${WDIR}/${F}" DIRECTORY)
+        execute_process(COMMAND ls -la "${WDIR}/${F}"
+                  WORKING_DIRECTORY "${WDIR}"
+                  OUTPUT_VARIABLE SYMLINK_POINT_)
+
+        string(FIND ${SYMLINK_POINT_} "->" SYMLINK_POINT_INDEX_ REVERSE)
+        math(EXPR SYMLINK_POINT_INDEX_ ${SYMLINK_POINT_INDEX_}+3)
+        string(LENGTH ${SYMLINK_POINT_} SYMLINK_POINT_LENGTH_)
+        # substract additional -1 to remove new line
+        math(EXPR SYMLINK_POINT_LENGTH_ ${SYMLINK_POINT_LENGTH_}-${SYMLINK_POINT_INDEX_}-1)
+        # get destination path
+        string(SUBSTRING ${SYMLINK_POINT_} ${SYMLINK_POINT_INDEX_} ${SYMLINK_POINT_LENGTH_} SYMLINK_POINT_)
+        set(SYMLINK_POINT_RAW_ ${SYMLINK_POINT_})
+        # check if path is relative or absolute
+        if(${SYMLINK_POINT_LENGTH_} GREATER 1)
+          string(SUBSTRING ${SYMLINK_POINT_} 0 2 SYMLINK_IS_ABSOLUTE_)
+
+          if(${SYMLINK_IS_ABSOLUTE_} STREQUAL "./")
+            # handle relative path
+            get_filename_component(SYMLINK_POINT_ "${SYMLINK_LOCATION_}/${SYMLINK_POINT_}" ABSOLUTE)
+          elseif(${SYMLINK_POINT_LENGTH_} GREATER 2)
+            string(SUBSTRING ${SYMLINK_POINT_} 0 3 SYMLINK_IS_ABSOLUTE_)
+
+            if(${SYMLINK_IS_ABSOLUTE_} STREQUAL "../")
+              # handle relative path
+              get_filename_component(SYMLINK_POINT_ "${SYMLINK_LOCATION_}/${SYMLINK_POINT_}" ABSOLUTE)
+            endif()
+          endif()
+        endif()
+
+        # prevent absolute paths from having /../ or /./ section inside of them
+        get_filename_component(SYMLINK_POINT_ "${SYMLINK_POINT_}" ABSOLUTE)
+
+        # check if path is local directory
+        string(SUBSTRING ${SYMLINK_POINT_} 0 1 SYMLINK_IS_ABSOLUTE_)
+        if(${SYMLINK_IS_ABSOLUTE_} STREQUAL "/")
+          string(LENGTH ${WDIR} WDR_LEN_)
+          string(LENGTH ${SYMLINK_POINT_} POINT_LEN_)
+          unset(SKIP_SYMLINK_)
+          if(${WDR_LEN_} GREATER ${POINT_LEN_})
+            if(EXISTS "${WDIR}/${SYMLINK_POINT_}")
+              # file is part of this rpm so we can convert it
+              get_filename_component(SYMLINK_POINT_ "${WDIR}/${SYMLINK_POINT_}" ABSOLUTE)
+            else()
+              message(WARNING "Symlink '${F}' points to location that is not part of the package!")
+              set(SKIP_SYMLINK_ TRUE)
+              set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+            endif()
+          endif()
+
+          if(NOT SKIP_SYMLINK_)
+            string(LENGTH ${SYMLINK_POINT_} SYMLINK_POINT_LEN)
+            string(LENGTH ${SYMLINK_LOCATION_} SYMLINK_LOCATION_LEN)
+
+            # make sure that both paths are at least long enough that substrings can be created
+            if(${SYMLINK_POINT_LEN} GREATER ${WDR_LEN_} AND ${SYMLINK_LOCATION_LEN} GREATER ${WDR_LEN_})
+              string(SUBSTRING ${SYMLINK_POINT_} 0 ${WDR_LEN_} SYMLINK_POINT_WD_)
+              string(SUBSTRING ${SYMLINK_LOCATION_} 0 ${WDR_LEN_} SYMLINK_LOCATION_WD_)
+            else()
+              set(SKIP_SYMLINK_ TRUE)
+            endif()
+
+            if(SKIP_SYMLINK_ OR NOT ${SYMLINK_POINT_WD_} STREQUAL ${SYMLINK_LOCATION_WD_})
+              message(WARNING "Symlink '${F}' points to location that is not part of the package!")
+              set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+            else()
+              # check if symlink and point location are in the same relocation path
+              unset(IDENTICAL_RELOCATION_PATH)
+              foreach(PKG_PREFIX ${PACKAGE_PREFIXES})
+                unset(SKIP_SYMLINK_)
+                get_filename_component(RELOCATION_PATH_ "${WDIR}/${PKG_PREFIX}" ABSOLUTE)
+                string(LENGTH ${RELOCATION_PATH_} RELOCATION_PATH_LENGTH_)
+                math(EXPR RELOCATION_PATH_LENGTH_ ${RELOCATION_PATH_LENGTH_}+1) # add +1 to length so that we check that location ends with /
+
+                # make sure that both paths are at least long enough that substrings can be created
+                if(${SYMLINK_POINT_LEN} GREATER ${RELOCATION_PATH_LENGTH_} AND ${SYMLINK_LOCATION_LEN} GREATER ${RELOCATION_PATH_LENGTH_})
+                  string(SUBSTRING ${SYMLINK_POINT_} 0 ${RELOCATION_PATH_LENGTH_} SYMLINK_POINT_RELOCATIONPATH_)
+                  string(SUBSTRING ${SYMLINK_LOCATION_} 0 ${RELOCATION_PATH_LENGTH_} SYMLINK_LOCATION_RELOCATIONPATH_)
+                else()
+                  set(SKIP_SYMLINK_ TRUE)
+                endif()
+
+                if(NOT SKIP_SYMLINK_ AND ${SYMLINK_POINT_RELOCATIONPATH_} STREQUAL ${SYMLINK_LOCATION_RELOCATIONPATH_})
+                  set(IDENTICAL_RELOCATION_PATH TRUE)
+                  break()
+                endif()
+              endforeach()
+
+              if(IDENTICAL_RELOCATION_PATH)
+                # construct relative path
+                string(LENGTH ${SYMLINK_POINT_} POINT_LEN)
+                math(EXPR POINT_LEN ${POINT_LEN}-1)
+                string(SUBSTRING ${SYMLINK_POINT_} 1 ${POINT_LEN} SYMLINK_POINT_)
+                string(LENGTH ${SYMLINK_LOCATION_} LOCATION_LEN)
+                math(EXPR LOCATION_LEN ${LOCATION_LEN}-1)
+                string(SUBSTRING ${SYMLINK_LOCATION_} 1 ${LOCATION_LEN} SYMLINK_LOCATION_)
+                string(REPLACE "/" ";" SYMLINK_POINT_SPLIT_ ${SYMLINK_POINT_})
+                string(REPLACE "/" ";" SYMLINK_LOCATION_SPLIT_ ${SYMLINK_LOCATION_})
+
+                set(SYMLINK_POINT_PART_ "")
+                set(SYMLINK_LOCATION_PART_ "")
+                list(LENGTH SYMLINK_POINT_SPLIT_ SYMLINK_POINT_PART_LEN_)
+                list(LENGTH SYMLINK_LOCATION_SPLIT_ SYMLINK_LOCATION_PART_LEN_)
+
+                while("${SYMLINK_POINT_PART_}" STREQUAL "${SYMLINK_LOCATION_PART_}"
+                  AND ${SYMLINK_POINT_PART_LEN_} AND ${SYMLINK_LOCATION_PART_LEN_})
+                  list(GET SYMLINK_POINT_SPLIT_ 0 SYMLINK_POINT_PART_)
+                  list(GET SYMLINK_LOCATION_SPLIT_ 0 SYMLINK_LOCATION_PART_)
+                  list(REMOVE_AT SYMLINK_POINT_SPLIT_ 0)
+                  list(REMOVE_AT SYMLINK_LOCATION_SPLIT_ 0)
+                  list(LENGTH SYMLINK_POINT_SPLIT_ SYMLINK_POINT_PART_LEN_)
+                  list(LENGTH SYMLINK_LOCATION_SPLIT_ SYMLINK_LOCATION_PART_LEN_)
+                endwhile()
+
+                list(LENGTH SYMLINK_LOCATION_SPLIT_ LIST_LEN)
+                if(NOT "${SYMLINK_POINT_PART_}" STREQUAL "${SYMLINK_LOCATION_PART_}")
+                  list(INSERT SYMLINK_POINT_SPLIT_ 0 ".." "${SYMLINK_POINT_PART_}")
+                endif()
+
+                while(${LIST_LEN} GREATER 0)
+                  list(INSERT SYMLINK_POINT_SPLIT_ 0 "..")
+                  math(EXPR LIST_LEN ${LIST_LEN}-1)
+                endwhile()
+
+                string(REPLACE ";" "/" FINAL "${SYMLINK_POINT_SPLIT_}")
+
+                # override symlink with new relative path if original point location is not equal to generated point location
+                if(NOT ${SYMLINK_POINT_RAW_} STREQUAL ${FINAL})
+                  execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FINAL}" "${WDIR}/${F}" RESULT_VARIABLE res)
+                endif()
+              else()
+                # handle case when symlink and point don't have same relocation path
+                unset(HAS_RELOCATION_PATH)
+                foreach(PKG_PREFIX ${PACKAGE_PREFIXES})
+                  unset(SKIP_SYMLINK_)
+                  get_filename_component(RELOCATION_PATH_ "${WDIR}/${PKG_PREFIX}" ABSOLUTE)
+                  string(LENGTH ${RELOCATION_PATH_} RELOCATION_PATH_LENGTH_)
+                  math(EXPR RELOCATION_PATH_LENGTH_ ${RELOCATION_PATH_LENGTH_}+1) # add +1 to length so that we check that location ends with /
+
+                  # make sure that path is at least long enough that substring can be created
+                  if(${SYMLINK_POINT_LEN} GREATER ${RELOCATION_PATH_LENGTH_})
+                    string(SUBSTRING ${SYMLINK_POINT_} 0 ${RELOCATION_PATH_LENGTH_} SYMLINK_POINT_RELOCATION_PATH_)
+                  else()
+                    set(SKIP_SYMLINK_ TRUE)
+                  endif()
+
+                  if(NOT SKIP_SYMLINK_ AND ${RELOCATION_PATH_} STREQUAL ${SYMLINK_POINT_RELOCATION_PATH_})
+                    set(HAS_RELOCATION_PATH ${RELOCATION_PATH_}) # TODO output index as we need to create a script variable (call function twice)
+                    break()
+                  endif()
+                endforeach()
+
+                if(HAS_RELOCATION_PATH)
+                  # TODO prepare post install and pre uninstall symlink scripts
+                  message(WARNING "Multiple relocation paths are currently not supported for symlinks in CPackRPM so symlink '${F}' will not be changed.")
+                else()
+                  # symlink should have absolute path
+                  string(SUBSTRING ${SYMLINK_POINT_} ${WDR_LEN_} -1 FINAL)
+                  execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FINAL}" "${WDIR}/${F}" RESULT_VARIABLE res)
+                endif()
+              endif()
+
+              set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+            endif()
+          endif()
+        else()
+          # no need to convert the symlink
+          set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+        endif()
+      else()
+        set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+      endif()
+    elseif(IS_DIRECTORY "${WDIR}/${F}")
+      set(INSTALL_FILES "${INSTALL_FILES}%dir \"${F}\"\n")
+    else()
+      set(INSTALL_FILES "${INSTALL_FILES}\"${F}\"\n")
+    endif()
+  endforeach()
+
+  set(CPACK_RPM_INSTALL_FILES "${INSTALL_FILES}" PARENT_SCOPE)
+endfunction()
+
 if(CMAKE_BINARY_DIR)
   message(FATAL_ERROR "CPackRPM.cmake may only be used by CPack internally.")
 endif()
@@ -1025,24 +1222,8 @@ else()
   set(CPACK_RPM_ABSOLUTE_INSTALL_FILES "")
 endif()
 
-# Prepend directories in ${CPACK_RPM_INSTALL_FILES} with %dir
-# This is necessary to avoid duplicate files since rpmbuild do
-# recursion on its own when encountering a pathname which is a directory
-# which is not flagged as %dir
-string(STRIP "${CPACK_RPM_INSTALL_FILES}" CPACK_RPM_INSTALL_FILES_LIST)
-string(REPLACE "\n" ";" CPACK_RPM_INSTALL_FILES_LIST
-                        "${CPACK_RPM_INSTALL_FILES_LIST}")
-string(REPLACE "\"" "" CPACK_RPM_INSTALL_FILES_LIST
-                        "${CPACK_RPM_INSTALL_FILES_LIST}")
-set(CPACK_RPM_INSTALL_FILES "")
-foreach(F IN LISTS CPACK_RPM_INSTALL_FILES_LIST)
-  if(IS_DIRECTORY "${WDIR}/${F}")
-    set(CPACK_RPM_INSTALL_FILES "${CPACK_RPM_INSTALL_FILES}%dir \"${F}\"\n")
-  else()
-    set(CPACK_RPM_INSTALL_FILES "${CPACK_RPM_INSTALL_FILES}\"${F}\"\n")
-  endif()
-endforeach()
-set(CPACK_RPM_INSTALL_FILES_LIST "")
+# Prepare install files
+CPackRPM_PrepareInstallFiles("${CPACK_RPM_INSTALL_FILES}" "${WDIR}" "${CPACK_RPM_PACKAGE_PREFIX}" "${CPACK_RPM_PACKAGE_PREFIX}" ${CPACK_RPM_PACKAGE_RELOCATABLE})
 
 # The name of the final spec file to be used by rpmbuild
 set(CPACK_RPM_BINARY_SPECFILE "${CPACK_RPM_ROOTDIR}/SPECS/${CPACK_RPM_PACKAGE_NAME}${CPACK_RPM_PACKAGE_COMPONENT_PART_NAME}.spec")
-- 
2.1.0

-- 

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more 
information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
http://public.kitware.com/mailman/listinfo/cmake-developers

Reply via email to