2014-11-10 17:44 GMT+01:00 Brad King <[email protected]>:
> On 11/08/2014 04:17 PM, Domen Vrankar wrote:
>> 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.
>
> Thanks.  Please also look at extending the test suite with a
> case for this.  There are several CPack RPM tests already.

I'll add the tests to the patch.

> Also, since the rpm packaging tools are posix-specific anyway,
> perhaps it will be easier to maintain this transformation as a
> standard shell script.  I don't feel strongly about this, but
> we have the option to do it that way.  The script could be
> launched with execute_process.

I've just noticed that I can remove a large part of the patch
(attached) and replace it with a single file command. I'll try to
shorten the patch even further and than if it will still be too long
and complex I'll consider replacing it with a shell script.

Domen
From 5676e78235e62bd7a17e5933887d9c3108b29902 Mon Sep 17 00:00:00 2001
From: Domen Vrankar <[email protected]>
Date: Mon, 10 Nov 2014 23:22:15 +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 | 182 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 164 insertions(+), 18 deletions(-)

diff --git a/Modules/CPackRPM.cmake b/Modules/CPackRPM.cmake
index 56d9b66..57a9a92 100644
--- a/Modules/CPackRPM.cmake
+++ b/Modules/CPackRPM.cmake
@@ -395,6 +395,168 @@
 
 # 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)
+                file(RELATIVE_PATH FINAL_PATH_ ${SYMLINK_LOCATION_} ${SYMLINK_POINT_})
+
+                # override symlink with new relative path if original point location is not equal to generated point location
+                if(NOT ${SYMLINK_POINT_RAW_} STREQUAL ${FINAL_PATH_})
+                  execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FINAL_PATH_}" "${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 +1187,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