From 681f3a2f012dcd17f63faa3c6f932ae92278e3bd Mon Sep 17 00:00:00 2001 From: Domen Vrankar Date: Sun, 29 Mar 2015 20:55:20 +0200 Subject: CPackRPM: Add basic symlink support (#15209) RPM packages can contain symbolic links to relative paths - including support for multiple relocation paths through generation of post install relocation scripts. Add basic support with limitations described in documentation. --- Modules/CPackRPM.cmake | 362 +++++++++++++++++++-- Tests/CMakeLists.txt | 1 + Tests/CPackComponentsForAll/CMakeLists.txt | 41 ++- .../RunCPackVerifyResult.cmake | 91 +++++- .../symlink_postinstall_expected.txt | 57 ++++ 5 files changed, 526 insertions(+), 26 deletions(-) create mode 100644 Tests/CPackComponentsForAll/symlink_postinstall_expected.txt diff --git a/Modules/CPackRPM.cmake b/Modules/CPackRPM.cmake index 162eba7..b8d518c 100644 --- a/Modules/CPackRPM.cmake +++ b/Modules/CPackRPM.cmake @@ -418,6 +418,41 @@ # # May be used to remove CPACK_PACKAGING_INSTALL_PREFIX and CPACK_RPM__PACKAGE_PREFIX # from relocatable RPM prefix paths. +# +# Packaging of Symbolic Links +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# CPackRPM supports packaging of symbolic links:: +# +# execute_process(COMMAND ${CMAKE_COMMAND} +# -E create_symlink ) +# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ +# DESTINATION COMPONENT libraries) +# +# Symbolic links will be optimized (paths will be shortened if possible) +# before being added to the package or if multiple relocation paths are +# detected, a post install symlink relocation script will be generated. +# +# Symbolic links may point to locations that are not packaged by the same +# package (either a different component or even not packaged at all) but +# those locations will be treated as if they were a part of the package +# while determining if symlink should be either created or present in a +# post install script - depending on relocation paths. +# +# Currenty there are a few limitations though: +# +# * Only symbolic links with relative path can be packaged. +# +# * For component based packaging component interdependency is not checked +# when processing symbolic links. Symbolic links pointing to content of +# a different component are treated the same way as if pointing to location +# that will not be packaged. +# +# * Symbolic links pointing to a location through one or more intermediate +# symbolic links will not be handled differently - if the intermediate +# symbolic link(s) is also on a relocatable path, relocating it during +# package installation may cause initial symbolic link to point to an +# invalid location. #============================================================================= # Copyright 2007-2009 Kitware, Inc. @@ -503,6 +538,301 @@ function(cpack_rpm_prepare_relocation_paths) set(TMP_RPM_PREFIXES "${TMP_RPM_PREFIXES}" PARENT_SCOPE) endfunction() +function(cpack_rpm_symlink_get_relocation_prefixes LOCATION PACKAGE_PREFIXES RETURN_VARIABLE) + foreach(PKG_PREFIX IN LISTS PACKAGE_PREFIXES) + string(REGEX MATCH "^${PKG_PREFIX}/.*" FOUND_ "${LOCATION}") + if(FOUND_) + list(APPEND TMP_PREFIXES "${PKG_PREFIX}") + endif() + endforeach() + + set(${RETURN_VARIABLE} "${TMP_PREFIXES}" PARENT_SCOPE) +endfunction() + +function(cpack_rpm_symlink_create_relocation_script PACKAGE_PREFIXES) + list(LENGTH PACKAGE_PREFIXES LAST_INDEX) + set(SORTED_PACKAGE_PREFIXES "${PACKAGE_PREFIXES}") + list(SORT SORTED_PACKAGE_PREFIXES) + list(REVERSE SORTED_PACKAGE_PREFIXES) + math(EXPR LAST_INDEX ${LAST_INDEX}-1) + + foreach(SYMLINK_INDEX RANGE ${LAST_INDEX}) + list(GET SORTED_PACKAGE_PREFIXES ${SYMLINK_INDEX} SRC_PATH) + list(FIND PACKAGE_PREFIXES "${SRC_PATH}" SYMLINK_INDEX) # reverse magic + string(LENGTH "${SRC_PATH}" SRC_PATH_LEN) + + set(PARTS_CNT 0) + set(SCRIPT_PART "if [ \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}\" != \"${SRC_PATH}\" ]; then\n") + + # both paths relocated + foreach(POINT_INDEX RANGE ${LAST_INDEX}) + list(GET SORTED_PACKAGE_PREFIXES ${POINT_INDEX} POINT_PATH) + list(FIND PACKAGE_PREFIXES "${POINT_PATH}" POINT_INDEX) # reverse magic + string(LENGTH "${POINT_PATH}" POINT_PATH_LEN) + + if(_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}) + if("${SYMLINK_INDEX}" EQUAL "${POINT_INDEX}") + set(INDENT "") + else() + set(SCRIPT_PART "${SCRIPT_PART} if [ \"$RPM_INSTALL_PREFIX${POINT_INDEX}\" != \"${POINT_PATH}\" ]; then\n") + set(INDENT " ") + endif() + + foreach(RELOCATION_NO IN LISTS _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}) + math(EXPR PARTS_CNT ${PARTS_CNT}+1) + + math(EXPR RELOCATION_INDEX ${RELOCATION_NO}-1) + list(GET _RPM_RELOCATION_SCRIPT_PAIRS ${RELOCATION_INDEX} RELOCATION_SCRIPT_PAIR) + string(FIND "${RELOCATION_SCRIPT_PAIR}" ":" SPLIT_INDEX) + + math(EXPR SRC_PATH_END ${SPLIT_INDEX}-${SRC_PATH_LEN}) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${SRC_PATH_LEN} ${SRC_PATH_END} SYMLINK_) + + math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1+${POINT_PATH_LEN}) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) + + set(SCRIPT_PART "${SCRIPT_PART} ${INDENT}if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") + set(SCRIPT_PART "${SCRIPT_PART} ${INDENT}ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n") + set(SCRIPT_PART "${SCRIPT_PART} ${INDENT}CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") + set(SCRIPT_PART "${SCRIPT_PART} ${INDENT}fi\n") + endforeach() + + if(NOT "${SYMLINK_INDEX}" EQUAL "${POINT_INDEX}") + set(SCRIPT_PART "${SCRIPT_PART} fi\n") + endif() + endif() + endforeach() + + # source path relocated + if(_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X) + foreach(RELOCATION_NO IN LISTS _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X) + math(EXPR PARTS_CNT ${PARTS_CNT}+1) + + math(EXPR RELOCATION_INDEX ${RELOCATION_NO}-1) + list(GET _RPM_RELOCATION_SCRIPT_PAIRS ${RELOCATION_INDEX} RELOCATION_SCRIPT_PAIR) + string(FIND "${RELOCATION_SCRIPT_PAIR}" ":" SPLIT_INDEX) + + math(EXPR SRC_PATH_END ${SPLIT_INDEX}-${SRC_PATH_LEN}) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${SRC_PATH_LEN} ${SRC_PATH_END} SYMLINK_) + + math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) + + set(SCRIPT_PART "${SCRIPT_PART} if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") + set(SCRIPT_PART "${SCRIPT_PART} ln -s \"${POINT_}\" \"$RPM_INSTALL_PREFIX${SYMLINK_INDEX}${SYMLINK_}\"\n") + set(SCRIPT_PART "${SCRIPT_PART} CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") + set(SCRIPT_PART "${SCRIPT_PART} fi\n") + endforeach() + endif() + + if(PARTS_CNT) + set(SCRIPT "${SCRIPT_PART}") + set(SCRIPT "${SCRIPT}fi\n") + endif() + endforeach() + + # point path relocated + foreach(POINT_INDEX RANGE ${LAST_INDEX}) + list(GET SORTED_PACKAGE_PREFIXES ${POINT_INDEX} POINT_PATH) + list(FIND PACKAGE_PREFIXES "${POINT_PATH}" POINT_INDEX) # reverse magic + string(LENGTH "${POINT_PATH}" POINT_PATH_LEN) + + if(_RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}) + set(SCRIPT "${SCRIPT}if [ \"$RPM_INSTALL_PREFIX${POINT_INDEX}\" != \"${POINT_PATH}\" ]; then\n") + + foreach(RELOCATION_NO IN LISTS _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}) + math(EXPR RELOCATION_INDEX ${RELOCATION_NO}-1) + list(GET _RPM_RELOCATION_SCRIPT_PAIRS ${RELOCATION_INDEX} RELOCATION_SCRIPT_PAIR) + string(FIND "${RELOCATION_SCRIPT_PAIR}" ":" SPLIT_INDEX) + + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} 0 ${SPLIT_INDEX} SYMLINK_) + + math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1+${POINT_PATH_LEN}) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) + + set(SCRIPT "${SCRIPT} if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") + set(SCRIPT "${SCRIPT} ln -s \"$RPM_INSTALL_PREFIX${POINT_INDEX}${POINT_}\" \"${SYMLINK_}\"\n") + set(SCRIPT "${SCRIPT} CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}=true\n") + set(SCRIPT "${SCRIPT} fi\n") + endforeach() + + set(SCRIPT "${SCRIPT}fi\n") + endif() + endforeach() + + # no path relocated + if(_RPM_RELOCATION_SCRIPT_X_X) + foreach(RELOCATION_NO IN LISTS _RPM_RELOCATION_SCRIPT_X_X) + math(EXPR RELOCATION_INDEX ${RELOCATION_NO}-1) + list(GET _RPM_RELOCATION_SCRIPT_PAIRS ${RELOCATION_INDEX} RELOCATION_SCRIPT_PAIR) + string(FIND "${RELOCATION_SCRIPT_PAIR}" ":" SPLIT_INDEX) + + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} 0 ${SPLIT_INDEX} SYMLINK_) + + math(EXPR POINT_PATH_START ${SPLIT_INDEX}+1) + string(SUBSTRING ${RELOCATION_SCRIPT_PAIR} ${POINT_PATH_START} -1 POINT_) + + set(SCRIPT "${SCRIPT}if [ -z \"$CPACK_RPM_RELOCATED_SYMLINK_${RELOCATION_INDEX}\" ]; then\n") + set(SCRIPT "${SCRIPT} ln -s \"${POINT_}\" \"${SYMLINK_}\"\n") + set(SCRIPT "${SCRIPT}fi\n") + endforeach() + endif() + + set(RPM_SYMLINK_POSTINSTALL "${SCRIPT}" PARENT_SCOPE) +endfunction() + +function(cpack_rpm_symlink_add_for_relocation_script PACKAGE_PREFIXES SYMLINK SYMLINK_RELOCATION_PATHS POINT POINT_RELOCATION_PATHS) + list(LENGTH SYMLINK_RELOCATION_PATHS SYMLINK_PATHS_COUTN) + list(LENGTH POINT_RELOCATION_PATHS POINT_PATHS_COUNT) + + list(APPEND _RPM_RELOCATION_SCRIPT_PAIRS "${SYMLINK}:${POINT}") + list(LENGTH _RPM_RELOCATION_SCRIPT_PAIRS PAIR_NO) + + if(SYMLINK_PATHS_COUTN) + foreach(SYMLINK_RELOC_PATH IN LISTS SYMLINK_RELOCATION_PATHS) + list(FIND PACKAGE_PREFIXES "${SYMLINK_RELOC_PATH}" SYMLINK_INDEX) + + # source path relocated + list(APPEND _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X "${PAIR_NO}") + list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_X") + + foreach(POINT_RELOC_PATH IN LISTS POINT_RELOCATION_PATHS) + list(FIND PACKAGE_PREFIXES "${POINT_RELOC_PATH}" POINT_INDEX) + + # both paths relocated + list(APPEND _RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX} "${PAIR_NO}") + list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_${SYMLINK_INDEX}_${POINT_INDEX}") + + # point path relocated + list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} "${PAIR_NO}") + list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}") + endforeach() + endforeach() + elseif(POINT_PATHS_COUNT) + foreach(POINT_RELOC_PATH IN LISTS POINT_RELOCATION_PATHS) + list(FIND PACKAGE_PREFIXES "${POINT_RELOC_PATH}" POINT_INDEX) + + # point path relocated + list(APPEND _RPM_RELOCATION_SCRIPT_X_${POINT_INDEX} "${PAIR_NO}") + list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_${POINT_INDEX}") + endforeach() + endif() + + # no path relocated + list(APPEND _RPM_RELOCATION_SCRIPT_X_X "${PAIR_NO}") + list(APPEND RELOCATION_VARS "_RPM_RELOCATION_SCRIPT_X_X") + + # place variables into parent scope + foreach(VAR IN LISTS RELOCATION_VARS) + set(${VAR} "${${VAR}}" PARENT_SCOPE) + endforeach() + set(_RPM_RELOCATION_SCRIPT_PAIRS "${_RPM_RELOCATION_SCRIPT_PAIRS}" PARENT_SCOPE) + set(REQUIRES_SYMLINK_RELOCATION_SCRIPT "true" PARENT_SCOPE) + set(DIRECTIVE "%ghost " PARENT_SCOPE) +endfunction() + +function(cpack_rpm_prepare_install_files 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 does + # 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}") + string(LENGTH "${WDIR}" WDR_LEN_) + + list(SORT INSTALL_FILES_LIST) # make file order consistent on all platforms + + foreach(F IN LISTS INSTALL_FILES_LIST) + unset(DIRECTIVE) + + 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_ + OUTPUT_STRIP_TRAILING_WHITESPACE) + + string(FIND "${SYMLINK_POINT_}" "->" SYMLINK_POINT_INDEX_ REVERSE) + math(EXPR SYMLINK_POINT_INDEX_ ${SYMLINK_POINT_INDEX_}+3) + string(LENGTH "${SYMLINK_POINT_}" SYMLINK_POINT_LENGTH_) + + # get destination path + string(SUBSTRING "${SYMLINK_POINT_}" ${SYMLINK_POINT_INDEX_} ${SYMLINK_POINT_LENGTH_} SYMLINK_POINT_) + + # check if path is relative or absolute + string(SUBSTRING "${SYMLINK_POINT_}" 0 1 SYMLINK_IS_ABSOLUTE_) + + if(${SYMLINK_IS_ABSOLUTE_} STREQUAL "/") + # prevent absolute paths from having /../ or /./ section inside of them + get_filename_component(SYMLINK_POINT_ "${SYMLINK_POINT_}" ABSOLUTE) + else() + # handle relative path + get_filename_component(SYMLINK_POINT_ "${SYMLINK_LOCATION_}/${SYMLINK_POINT_}" ABSOLUTE) + endif() + + string(SUBSTRING "${SYMLINK_POINT_}" ${WDR_LEN_} -1 SYMLINK_POINT_WD_) + + cpack_rpm_symlink_get_relocation_prefixes("${F}" "${PACKAGE_PREFIXES}" "SYMLINK_RELOCATIONS") + cpack_rpm_symlink_get_relocation_prefixes("${SYMLINK_POINT_WD_}" "${PACKAGE_PREFIXES}" "POINT_RELOCATIONS") + + list(LENGTH SYMLINK_RELOCATIONS SYMLINK_RELOCATIONS_COUNT) + list(LENGTH POINT_RELOCATIONS POINT_RELOCATIONS_COUNT) + + if(SYMLINK_RELOCATIONS_COUNT AND POINT_RELOCATIONS_COUNT) + # find matching + foreach(SYMLINK_RELOCATION_PREFIX IN LISTS SYMLINK_RELOCATIONS) + list(FIND POINT_RELOCATIONS "${SYMLINK_RELOCATION_PREFIX}" FOUND_INDEX) + if(NOT ${FOUND_INDEX} EQUAL -1) + break() + endif() + endforeach() + + if(NOT ${FOUND_INDEX} EQUAL -1) + # symlinks have the same subpath + if(${SYMLINK_RELOCATIONS_COUNT} EQUAL 1 AND ${POINT_RELOCATIONS_COUNT} EQUAL 1) + # permanent symlink + get_filename_component(SYMLINK_LOCATION_ "${F}" DIRECTORY) + file(RELATIVE_PATH FINAL_PATH_ ${SYMLINK_LOCATION_} ${SYMLINK_POINT_WD_}) + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${FINAL_PATH_}" "${WDIR}/${F}") + else() + # relocation subpaths + cpack_rpm_symlink_add_for_relocation_script("${PACKAGE_PREFIXES}" "${F}" "${SYMLINK_RELOCATIONS}" + "${SYMLINK_POINT_WD_}" "${POINT_RELOCATIONS}") + endif() + else() + # not on the same relocation path + cpack_rpm_symlink_add_for_relocation_script("${PACKAGE_PREFIXES}" "${F}" "${SYMLINK_RELOCATIONS}" + "${SYMLINK_POINT_WD_}" "${POINT_RELOCATIONS}") + endif() + elseif(POINT_RELOCATIONS_COUNT) + # point is relocatable + cpack_rpm_symlink_add_for_relocation_script("${PACKAGE_PREFIXES}" "${F}" "${SYMLINK_RELOCATIONS}" + "${SYMLINK_POINT_WD_}" "${POINT_RELOCATIONS}") + else() + # is not relocatable or points to non relocatable path - permanent symlink + execute_process(COMMAND "${CMAKE_COMMAND}" -E create_symlink "${SYMLINK_POINT_WD_}" "${WDIR}/${F}") + endif() + endif() + elseif(IS_DIRECTORY "${WDIR}/${F}") + set(DIRECTIVE "%dir ") + endif() + + set(INSTALL_FILES "${INSTALL_FILES}${DIRECTIVE}\"${F}\"\n") + endforeach() + + if(REQUIRES_SYMLINK_RELOCATION_SCRIPT) + cpack_rpm_symlink_create_relocation_script("${PACKAGE_PREFIXES}") + endif() + + set(RPM_SYMLINK_POSTINSTALL "${RPM_SYMLINK_POSTINSTALL}" PARENT_SCOPE) + 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() @@ -963,9 +1293,10 @@ function(cpack_rpm_generate_package) # destinct parent paths of other relocation paths and remove the # final element (so the install-prefix dir itself is not omitted # from the RPM's content-list) - list(SORT RPM_USED_PACKAGE_PREFIXES) + set(SORTED_RPM_USED_PACKAGE_PREFIXES "${RPM_USED_PACKAGE_PREFIXES}") + list(SORT SORTED_RPM_USED_PACKAGE_PREFIXES) set(_DISTINCT_PATH "NOT_SET") - foreach(_RPM_RELOCATION_PREFIX ${RPM_USED_PACKAGE_PREFIXES}) + foreach(_RPM_RELOCATION_PREFIX ${SORTED_RPM_USED_PACKAGE_PREFIXES}) if(NOT "${_RPM_RELOCATION_PREFIX}" MATCHES "${_DISTINCT_PATH}/.*") set(_DISTINCT_PATH "${_RPM_RELOCATION_PREFIX}") @@ -1142,25 +1473,13 @@ function(cpack_rpm_generate_package) 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 + cpack_rpm_prepare_install_files( + "${CPACK_RPM_INSTALL_FILES}" + "${WDIR}" + "${RPM_USED_PACKAGE_PREFIXES}" + "${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") @@ -1246,6 +1565,7 @@ mv \"\@CPACK_TOPLEVEL_DIRECTORY\@/tmpBBroot\" $RPM_BUILD_ROOT %clean %post +\@RPM_SYMLINK_POSTINSTALL\@ \@CPACK_RPM_SPEC_POSTINSTALL\@ %postun diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f2df4af..5944d08 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -991,6 +991,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release ${build_generator_args} --build-project CPackComponentsForAll --build-options ${build_options} + -DCPACK_GENERATOR:STRING=${CPackGen} -DCPACK_BINARY_${CPackGen}:BOOL=ON ${CPackRun_CPackComponentWay} ${CPackComponentsForAll_BUILD_OPTIONS} diff --git a/Tests/CPackComponentsForAll/CMakeLists.txt b/Tests/CPackComponentsForAll/CMakeLists.txt index 51af297..1cc34b0 100644 --- a/Tests/CPackComponentsForAll/CMakeLists.txt +++ b/Tests/CPackComponentsForAll/CMakeLists.txt @@ -49,6 +49,44 @@ install(FILES mylib.h DESTINATION include COMPONENT headers) +if("${CPACK_GENERATOR}" MATCHES "RPM") + # Package symbolic links + install(DIRECTORY DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two/depth_three COMPONENT libraries) + install(DIRECTORY DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_two/depth_two/different_relocatable/bar COMPONENT libraries) + install(DIRECTORY DESTINATION other_relocatable/depth_two COMPONENT libraries) + install(DIRECTORY DESTINATION non_relocatable/depth_two COMPONENT libraries) + # test symbolic links to same dir + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink depth_three symlink_samedir_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_samedir_path DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two COMPONENT libraries) + # test symbolic links to same dir with current dir ./ prefix + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ./depth_three symlink_samedir_path_current_dir) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_samedir_path_current_dir DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two COMPONENT libraries) + # test symbolic links to same dir with longer relative path + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../../${CMAKE_INSTALL_LIBDIR}/.././${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/./depth_two/depth_three symlink_samedir_path_longer) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_samedir_path_longer DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two COMPONENT libraries) + # test symbolic links to sub dir + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two/depth_three symlink_subdir_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_subdir_path DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one COMPONENT libraries) + # test symbolic links to parent dir + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink .././../../../${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/./depth_two symlink_parentdir_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_parentdir_path DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two/depth_three COMPONENT libraries) + # test symbolic link to another relocatable path + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink .././.././../other_relocatable/./depth_two symlink_other_relocatable_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_other_relocatable_path DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_two/depth_two COMPONENT libraries) + # test symbolic link to non relocatable path + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink .././../../non_relocatable/./depth_two symlink_to_non_relocatable_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_to_non_relocatable_path DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_two/depth_two COMPONENT libraries) + # test symbolic link from non relocatable path + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink .././../${CMAKE_INSTALL_LIBDIR}/inside_relocatable_two/depth_two symlink_from_non_relocatable_path) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_from_non_relocatable_path DESTINATION non_relocatable/depth_two COMPONENT libraries) + # test symbolic link relocatable path to its relocatable subpath + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ../../inside_relocatable_two/depth_two/different_relocatable/bar symlink_relocatable_subpath) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_relocatable_subpath DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two COMPONENT libraries) + # test symbolic link to location outside package + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ./outside_package symlink_outside_package) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/symlink_outside_package DESTINATION ${CMAKE_INSTALL_LIBDIR}/inside_relocatable_one/depth_two COMPONENT libraries) +endif() + # CPack boilerplate for this project set(CPACK_PACKAGE_NAME "MyLib") set(CPACK_PACKAGE_CONTACT "None") @@ -114,7 +152,8 @@ set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full) # can not be used in CPack scripts due to CMAKE_SIZEOF_VOID_P # variable not being set set(CPACK_RPM_RELOCATION_PATHS "${CMAKE_INSTALL_INCLUDEDIR}" - "${CMAKE_INSTALL_LIBDIR}" "${CMAKE_INSTALL_BINDIR}") + "${CMAKE_INSTALL_LIBDIR}" "${CMAKE_INSTALL_BINDIR}" "other_relocatable" + "${CMAKE_INSTALL_LIBDIR}/inside_relocatable_two/depth_two/different_relocatable") # We may use the CPack specific config file in order # to tailor CPack behavior on a CPack generator specific way diff --git a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake index cf4da74..e747052 100644 --- a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake +++ b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake @@ -1,3 +1,7 @@ +# prevent older policies from interfearing with this script +cmake_policy(PUSH) +cmake_policy(VERSION ${CMAKE_VERSION}) + message(STATUS "=============================================================================") message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)") message(STATUS "") @@ -138,6 +142,7 @@ if(CPackGen MATCHES "RPM") "An extremely useful application that makes use of MyLib") set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION "Static libraries used to build programs with MyLib") + set(LIB_SUFFIX "6?4?") # test package info if(${CPackComponentWay} STREQUAL "IgnoreGroup") @@ -174,10 +179,32 @@ if(CPackGen MATCHES "RPM") if(check_file_libraries_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_COMPONENT_LIBRARIES_DESCRIPTION}.*") - set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}${LIB_SUFFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/other_relocatable${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}${LIB_SUFFIX}/inside_relocatable_two/depth_two/different_relocatable") set(check_file_match_expected_architecture "") # we don't explicitly set this value so it is different on each platform - ignore it set(spec_regex "*libraries*") - set(check_content_list "^/usr/foo/bar/lib.*\n/usr/foo/bar/lib.*/libmylib.a$") + set(check_content_list "^/usr/foo/bar/lib${LIB_SUFFIX} +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/depth_three +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/depth_three/symlink_parentdir_path +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/symlink_outside_package +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/symlink_relocatable_subpath +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/symlink_samedir_path +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/symlink_samedir_path_current_dir +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/depth_two/symlink_samedir_path_longer +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_one/symlink_subdir_path +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two/depth_two +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two/depth_two/different_relocatable +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two/depth_two/different_relocatable/bar +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two/depth_two/symlink_other_relocatable_path +/usr/foo/bar/lib${LIB_SUFFIX}/inside_relocatable_two/depth_two/symlink_to_non_relocatable_path +/usr/foo/bar/lib${LIB_SUFFIX}/libmylib.a +/usr/foo/bar/non_relocatable +/usr/foo/bar/non_relocatable/depth_two +/usr/foo/bar/non_relocatable/depth_two/symlink_from_non_relocatable_path +/usr/foo/bar/other_relocatable +/usr/foo/bar/other_relocatable/depth_two$") elseif(check_file_headers_match) set(check_file_match_expected_summary ".*${CPACK_RPM_headers_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_RPM_headers_PACKAGE_DESCRIPTION}.*") @@ -188,10 +215,12 @@ if(CPackGen MATCHES "RPM") elseif(check_file_applications_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*${CPACK_COMPONENT_APPLICATIONS_DESCRIPTION}.*") - set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}.*") + set(check_file_match_expected_relocation_path "Relocations${whitespaces}:${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") set(check_file_match_expected_architecture "armv7hf") set(spec_regex "*applications*") - set(check_content_list "^/usr/foo/bar\n/usr/foo/bar/bin\n/usr/foo/bar/bin/mylibapp$") + set(check_content_list "^/usr/foo/bar +/usr/foo/bar/bin +/usr/foo/bar/bin/mylibapp$") elseif(check_file_Unspecified_match) set(check_file_match_expected_summary ".*${CPACK_RPM_PACKAGE_SUMMARY}.*") set(check_file_match_expected_description ".*DESCRIPTION.*") @@ -269,5 +298,59 @@ if(CPackGen MATCHES "RPM") message(FATAL_ERROR "error: '${check_file}' rpm package content does not match expected value - regex '${check_content_list}'; RPM output: '${check_package_content}'; generated spec file: '${spec_file_content}'") endif() endforeach() + + ####################### + # verify generated symbolic links + ####################### + file(GLOB_RECURSE symlink_files RELATIVE "${CPackComponentsForAll_BINARY_DIR}" "${CPackComponentsForAll_BINARY_DIR}/*/symlink_*") + + foreach(check_symlink IN LISTS symlink_files) + get_filename_component(symlink_name "${check_symlink}" NAME) + execute_process(COMMAND ls -la "${check_symlink}" + WORKING_DIRECTORY "${CPackComponentsForAll_BINARY_DIR}" + OUTPUT_VARIABLE SYMLINK_POINT_ + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if("${symlink_name}" STREQUAL "symlink_samedir_path" + OR "${symlink_name}" STREQUAL "symlink_samedir_path_current_dir" + OR "${symlink_name}" STREQUAL "symlink_samedir_path_longer") + string(REGEX MATCH "^.*${whitespaces}->${whitespaces}depth_three$" check_symlink "${SYMLINK_POINT_}") + elseif("${symlink_name}" STREQUAL "symlink_subdir_path") + string(REGEX MATCH "^.*${whitespaces}->${whitespaces}depth_two/depth_three$" check_symlink "${SYMLINK_POINT_}") + elseif("${symlink_name}" STREQUAL "symlink_parentdir_path") + string(REGEX MATCH "^.*${whitespaces}->${whitespaces}../$" check_symlink "${SYMLINK_POINT_}") + elseif("${symlink_name}" STREQUAL "symlink_to_non_relocatable_path") + string(REGEX MATCH "^.*${whitespaces}->${whitespaces}${CPACK_PACKAGING_INSTALL_PREFIX}/non_relocatable/depth_two$" check_symlink "${SYMLINK_POINT_}") + elseif("${symlink_name}" STREQUAL "symlink_outside_package") + string(REGEX MATCH "^.*${whitespaces}->${whitespaces}outside_package$" check_symlink "${SYMLINK_POINT_}") + elseif("${symlink_name}" STREQUAL "symlink_other_relocatable_path" + OR "${symlink_name}" STREQUAL "symlink_from_non_relocatable_path" + OR "${symlink_name}" STREQUAL "symlink_relocatable_subpath") + # these links were not canged - post install script only - ignore them + else() + message(FATAL_ERROR "error: unexpected rpm symbolic link '${check_symlink}'") + endif() + + if(NOT check_symlink) + message(FATAL_ERROR "symlink points to unexpected location '${SYMLINK_POINT_}'") + endif() + endforeach() + + # verify post install symlink relocation script + file(GLOB_RECURSE spec_file "${CPackComponentsForAll_BINARY_DIR}/*libraries*.spec") + file(READ ${spec_file} spec_file_content) + file(READ "${CMAKE_CURRENT_LIST_DIR}/symlink_postinstall_expected.txt" symlink_postinstall_expected) + # prepare regex + string(STRIP "${symlink_postinstall_expected}" symlink_postinstall_expected) + string(REPLACE "[" "\\[" symlink_postinstall_expected "${symlink_postinstall_expected}") + string(REPLACE "$" "\\$" symlink_postinstall_expected "${symlink_postinstall_expected}") + string(REPLACE "lib" "lib${LIB_SUFFIX}" symlink_postinstall_expected "${symlink_postinstall_expected}") + # compare + string(REGEX MATCH ".*${symlink_postinstall_expected}.*" symlink_postinstall_expected_matches "${spec_file_content}") + if(NOT symlink_postinstall_expected_matches) + message(FATAL_ERROR "error: unexpected rpm symbolic link postinstall script! generated spec file: '${spec_file_content}'") + endif() endif() endif() + +cmake_policy(POP) diff --git a/Tests/CPackComponentsForAll/symlink_postinstall_expected.txt b/Tests/CPackComponentsForAll/symlink_postinstall_expected.txt new file mode 100644 index 0000000..ba46792 --- /dev/null +++ b/Tests/CPackComponentsForAll/symlink_postinstall_expected.txt @@ -0,0 +1,57 @@ +if [ "$RPM_INSTALL_PREFIX0" != "/usr/foo/bar/lib" ]; then + if [ "$RPM_INSTALL_PREFIX1" != "/usr/foo/bar/other_relocatable" ]; then + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_1" ]; then + ln -s "$RPM_INSTALL_PREFIX1/depth_two" "$RPM_INSTALL_PREFIX0/inside_relocatable_two/depth_two/symlink_other_relocatable_path" + CPACK_RPM_RELOCATED_SYMLINK_1=true + fi + fi + if [ "$RPM_INSTALL_PREFIX2" != "/usr/foo/bar/lib/inside_relocatable_two/depth_two/different_relocatable" ]; then + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "$RPM_INSTALL_PREFIX2/bar" "$RPM_INSTALL_PREFIX0/inside_relocatable_one/depth_two/symlink_relocatable_subpath" + CPACK_RPM_RELOCATED_SYMLINK_0=true + fi + fi + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "$RPM_INSTALL_PREFIX0/inside_relocatable_two/depth_two/different_relocatable/bar" "$RPM_INSTALL_PREFIX0/inside_relocatable_one/depth_two/symlink_relocatable_subpath" + CPACK_RPM_RELOCATED_SYMLINK_0=true + fi + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "/usr/foo/bar/lib/inside_relocatable_two/depth_two/different_relocatable/bar" "$RPM_INSTALL_PREFIX0/inside_relocatable_one/depth_two/symlink_relocatable_subpath" + CPACK_RPM_RELOCATED_SYMLINK_0=true + fi + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_1" ]; then + ln -s "/usr/foo/bar/other_relocatable/depth_two" "$RPM_INSTALL_PREFIX0/inside_relocatable_two/depth_two/symlink_other_relocatable_path" + CPACK_RPM_RELOCATED_SYMLINK_1=true + fi +fi +if [ "$RPM_INSTALL_PREFIX1" != "/usr/foo/bar/other_relocatable" ]; then + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_1" ]; then + ln -s "$RPM_INSTALL_PREFIX1/depth_two" "/usr/foo/bar/lib/inside_relocatable_two/depth_two/symlink_other_relocatable_path" + CPACK_RPM_RELOCATED_SYMLINK_1=true + fi +fi +if [ "$RPM_INSTALL_PREFIX2" != "/usr/foo/bar/lib/inside_relocatable_two/depth_two/different_relocatable" ]; then + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "$RPM_INSTALL_PREFIX2/bar" "/usr/foo/bar/lib/inside_relocatable_one/depth_two/symlink_relocatable_subpath" + CPACK_RPM_RELOCATED_SYMLINK_0=true + fi +fi +if [ "$RPM_INSTALL_PREFIX0" != "/usr/foo/bar/lib" ]; then + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "$RPM_INSTALL_PREFIX0/inside_relocatable_two/depth_two/different_relocatable/bar" "/usr/foo/bar/lib/inside_relocatable_one/depth_two/symlink_relocatable_subpath" + CPACK_RPM_RELOCATED_SYMLINK_0=true + fi + if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_2" ]; then + ln -s "$RPM_INSTALL_PREFIX0/inside_relocatable_two/depth_two" "/usr/foo/bar/non_relocatable/depth_two/symlink_from_non_relocatable_path" + CPACK_RPM_RELOCATED_SYMLINK_2=true + fi +fi +if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_0" ]; then + ln -s "/usr/foo/bar/lib/inside_relocatable_two/depth_two/different_relocatable/bar" "/usr/foo/bar/lib/inside_relocatable_one/depth_two/symlink_relocatable_subpath" +fi +if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_1" ]; then + ln -s "/usr/foo/bar/other_relocatable/depth_two" "/usr/foo/bar/lib/inside_relocatable_two/depth_two/symlink_other_relocatable_path" +fi +if [ -z "$CPACK_RPM_RELOCATED_SYMLINK_2" ]; then + ln -s "/usr/foo/bar/lib/inside_relocatable_two/depth_two" "/usr/foo/bar/non_relocatable/depth_two/symlink_from_non_relocatable_path" +fi -- cgit v0.12