summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeniz Bahadir <deniz@code.bahadir.email>2024-05-30 14:13:46 (GMT)
committerDeniz Bahadir <deniz@code.bahadir.email>2024-05-30 14:13:46 (GMT)
commit27d161eac39f547a45655c47e6353a1dcecf5b89 (patch)
tree71a9eae2a396b5831da94322e04e67879511d102
parentc024b5cf9a8d96dcb1e289f086ef3d8760e316c4 (diff)
downloadCMake-27d161eac39f547a45655c47e6353a1dcecf5b89.zip
CMake-27d161eac39f547a45655c47e6353a1dcecf5b89.tar.gz
CMake-27d161eac39f547a45655c47e6353a1dcecf5b89.tar.bz2
CPackDeb: dpkg-shlibdeps shall consider dependency components, too
When using `dpkg-shlibdeps` to automatically determine package dependencies it considers the RUNPATH/RPATH of executables in order to find all required shared libraries of such executables. If the RUNPATH/RPATH contains a verbatim `$ORIGIN` (respective `${ORIGIN}`), it will now be substituted by the packaging-paths of other components that are marked as dependency and those paths will then be used as additional search directories for `dpkg-shlibdeps`. Associated tests were added as well. Fixes: #21838
-rw-r--r--Modules/Internal/CPack/CPackDeb.cmake101
-rw-r--r--Tests/CMakeLists.txt10
-rw-r--r--Tests/CPackComponentsDEB/CMakeLists.txt31
-rw-r--r--Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in23
-rw-r--r--Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in22
-rw-r--r--Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake19
-rw-r--r--Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake75
-rw-r--r--Tests/CPackComponentsDEB/mylibapp.cpp17
-rw-r--r--Tests/CPackComponentsDEB/subdir/CMakeLists.txt5
-rw-r--r--Tests/CPackComponentsDEB/subdir/myotherlib.cpp8
-rw-r--r--Tests/CPackComponentsDEB/subdir/myotherlib.h1
11 files changed, 294 insertions, 18 deletions
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 5a78e3f..d4a47e4 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -56,6 +56,65 @@ function(extract_so_info shared_object libname version)
endif()
endfunction()
+#extract RUNPATH and RPATH for given shared object or executable
+function(extract_runpath_and_rpath shared_object_or_executable runpath rpath)
+ if(CPACK_READELF_EXECUTABLE)
+ execute_process(COMMAND "${CPACK_READELF_EXECUTABLE}" -d "${shared_object_or_executable}"
+ WORKING_DIRECTORY "${CPACK_TEMPORARY_DIRECTORY}"
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(result EQUAL 0)
+ string(REGEX MATCH "\\(?RUNPATH\\)?[^\n]*\\[([^\n]+)\\]" found_runpath "${output}")
+ string(REPLACE ":" ";" found_runpath "${CMAKE_MATCH_1}")
+ list(REMOVE_DUPLICATES found_runpath)
+ string(REGEX MATCH "\\(?RPATH\\)?[^\n]*\\[([^\n]+)\\]" found_rpath "${output}")
+ string(REPLACE ":" ";" found_rpath "${CMAKE_MATCH_1}")
+ list(REMOVE_DUPLICATES found_rpath)
+ set(${runpath} "${found_runpath}" PARENT_SCOPE)
+ set(${rpath} "${found_rpath}" PARENT_SCOPE)
+ else()
+ message(WARNING "Error running readelf for \"${shared_object_or_executable}\"")
+ endif()
+ else()
+ message(FATAL_ERROR "Readelf utility is not available.")
+ endif()
+endfunction()
+
+#sanitizes the given directory name if required
+function(get_sanitized_dirname dirname outvar)
+ # NOTE: This pattern has to stay in sync with the 'prohibited_chars' variable
+ # defined in the C++ function `CPackGenerator::GetSanitizedDirOrFileName`!
+ set(prohibited_chars_pattern "[<]|[>]|[\"]|[/]|[\\]|[|]|[?]|[*]|[`]")
+ if("${dirname}" MATCHES "${prohibited_chars_pattern}")
+ string(MD5 santized_dirname "${dirname}")
+ set(${outvar} "${sanitized_dirname}" PARENT_SCOPE)
+ else()
+ set(${outvar} "${dirname}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+#retrieve packaging directories of components the current component depends on
+# Note: May only be called from within 'cpack_deb_prepare_package_var'!
+function(get_packaging_dirs_of_dependencies outvar)
+ if(CPACK_DEB_PACKAGE_COMPONENT)
+ if(NOT DEFINED WDIR OR NOT DEFINED _local_component_name)
+ message(FATAL_ERROR "CPackDeb: Function '${CMAKE_CURRENT_FUNCTION}' not called from correct function scope!")
+ endif()
+ set(result_list)
+ foreach(dependency_name IN LISTS CPACK_COMPONENT_${_local_component_name}_DEPENDS)
+ get_sanitized_dirname("${dependency_name}" dependency_name)
+ cmake_path(APPEND_STRING WDIR "/../${dependency_name}" OUTPUT_VARIABLE dependency_packaging_dir)
+ cmake_path(NORMAL_PATH dependency_packaging_dir)
+ list(APPEND result_list "${dependency_packaging_dir}")
+ endforeach()
+ set(${outvar} "${result_list}" PARENT_SCOPE) # Set return variable.
+ else()
+ set(${outvar} "" PARENT_SCOPE) # Clear return variable.
+ endif()
+endfunction()
+
function(cpack_deb_check_description SUMMARY LINES RESULT_VARIABLE)
set(_result TRUE)
@@ -310,16 +369,44 @@ function(cpack_deb_prepare_package_vars)
set(IGNORE_MISSING_INFO_FLAG "--ignore-missing-info")
endif()
- if(CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS)
+ # Add -l option if the tool supports it?
+ if(DEFINED SHLIBDEPS_EXECUTABLE_VERSION AND SHLIBDEPS_EXECUTABLE_VERSION VERSION_GREATER_EQUAL 1.17.0)
unset(PRIVATE_SEARCH_DIR_OPTIONS)
- # Add -l option if the tool supports it
- if(DEFINED SHLIBDEPS_EXECUTABLE_VERSION AND SHLIBDEPS_EXECUTABLE_VERSION VERSION_GREATER_EQUAL 1.17.0)
- foreach(dir IN LISTS CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS)
- list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${dir}")
+
+ # Use directories provided via CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS
+ if(NOT "${CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS}" STREQUAL "")
+ foreach(path IN LISTS CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS)
+ cmake_path(NORMAL_PATH path) # Required for dpkg-shlibdeps!
+ list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${path}")
endforeach()
- else()
- message(WARNING "CPackDeb: dkpg-shlibdeps is too old. \"CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS\" is therefore ignored.")
endif()
+
+ # Use directories extracted from RUNPATH/RPATH
+ get_packaging_dirs_of_dependencies(deps_packaging_dirs)
+ foreach(exe IN LISTS CPACK_DEB_BINARY_FILES)
+ cmake_path(GET exe PARENT_PATH exe_dir)
+ extract_runpath_and_rpath(${exe} runpath rpath)
+ # If RUNPATH is available, RPATH will be ignored. Therefore we have to do the same here!
+ if (NOT "${runpath}" STREQUAL "")
+ set(selected_rpath "${runpath}")
+ else()
+ set(selected_rpath "${rpath}")
+ endif()
+ foreach(search_path IN LISTS selected_rpath)
+ if ("${search_path}" MATCHES "^[$]ORIGIN" OR "${search_path}" MATCHES "^[$][{]ORIGIN[}]")
+ foreach(deps_pkgdir IN LISTS deps_packaging_dirs)
+ string(REPLACE "\$ORIGIN" "${deps_pkgdir}/${exe_dir}" path "${search_path}")
+ string(REPLACE "\${ORIGIN}" "${deps_pkgdir}/${exe_dir}" path "${path}")
+ cmake_path(NORMAL_PATH path) # Required for dpkg-shlibdeps!
+ list(APPEND PRIVATE_SEARCH_DIR_OPTIONS "-l${path}")
+ endforeach()
+ endif()
+ endforeach()
+ endforeach()
+
+ list(REMOVE_DUPLICATES PRIVATE_SEARCH_DIR_OPTIONS)
+ elseif(NOT "${CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS}" STREQUAL "")
+ message(WARNING "CPackDeb: dkpg-shlibdeps is too old. \"CPACK_DEBIAN_PACKAGE_SHLIBDEPS_PRIVATE_DIRS\" is therefore ignored.")
endif()
# Execute dpkg-shlibdeps
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 51e60fe..4048bfe 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1199,13 +1199,17 @@ if(BUILD_TESTING)
else()
unset(SHLIBDEPS_EXECUTABLE_VERSION)
endif()
+ # Check if distro has symbols or shlibs data
+ file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols")
if(NOT SHLIBDEPS_EXECUTABLE_VERSION VERSION_LESS 1.19 OR
(NOT SHLIBDEPS_EXECUTABLE_VERSION VERSION_LESS 1.17 AND NOT CMAKE_BINARY_DIR MATCHES ".*[ ].*"))
list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-private-lib-failure"
- "shlibdeps-with-private-lib-success")
+ "shlibdeps-with-private-lib-success"
+ "shlibdeps-with-ORIGIN-RPATH-failure")
+ if(SHLIBS_FILES_EXIST)
+ list(APPEND DEB_CONFIGURATIONS_TO_TEST "shlibdeps-with-ORIGIN-RPATH-success")
+ endif()
endif()
- # Check if distro has symbols or shlibs data
- file(GLOB SHLIBS_FILES_EXIST "/var/lib/dpkg/info/*.shlibs" "/var/lib/dpkg/info/*.symbols")
if(SHLIBS_FILES_EXIST)
list(APPEND DEB_CONFIGURATIONS_TO_TEST "components-depend2")
endif()
diff --git a/Tests/CPackComponentsDEB/CMakeLists.txt b/Tests/CPackComponentsDEB/CMakeLists.txt
index b2e2106..a3eb337 100644
--- a/Tests/CPackComponentsDEB/CMakeLists.txt
+++ b/Tests/CPackComponentsDEB/CMakeLists.txt
@@ -5,6 +5,10 @@
# which supports CPack components.
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
+# Make sure to properly escape RPATH/RUNPATH entries.
+if (POLICY CMP0095)
+ cmake_policy(SET CMP0095 NEW)
+endif()
project(CPackComponentsDEB VERSION 1.0.3)
# Use GNUInstallDirs in order to enforce lib64 if needed
@@ -29,6 +33,14 @@ if (CPackDEBConfiguration MATCHES "shlibdeps-with-private-lib")
target_link_libraries(mylibapp3 myprivatelib)
endif()
+if (CPackDEBConfiguration MATCHES "shlibdeps-with-ORIGIN-RPATH")
+ add_subdirectory("subdir")
+ add_executable(mylibapp4 mylibapp.cpp)
+ target_compile_definitions(mylibapp4 PRIVATE -DSHLIBDEPS_OTHER)
+ target_link_libraries(mylibapp4 PUBLIC myotherlib)
+ set_target_properties(mylibapp4 PROPERTIES INSTALL_RPATH "\${ORIGIN};$ORIGIN/../lib")
+endif()
+
# Create installation targets. Note that we put each kind of file
# into a different component via COMPONENT. These components will
# be used to create the installation components.
@@ -39,17 +51,28 @@ install(TARGETS mylib
install(TARGETS mylibapp
RUNTIME
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT applications)
install(FILES mylib.h
- DESTINATION include
- COMPONENT headers)
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+ COMPONENT headers)
if (CPackDEBConfiguration MATCHES "shlibdeps-with-private-lib")
install(TARGETS mylibapp3
RUNTIME
- DESTINATION bin
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
+ COMPONENT applications)
+endif()
+
+if (CPackDEBConfiguration MATCHES "shlibdeps-with-ORIGIN-RPATH")
+ install(TARGETS myotherlib
+ LIBRARY
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ COMPONENT libraries)
+ install(TARGETS mylibapp4
+ RUNTIME
+ DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT applications)
endif()
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in
new file mode 100644
index 0000000..0794fbd
--- /dev/null
+++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-failure.cmake.in
@@ -0,0 +1,23 @@
+#
+# Activate component packaging
+#
+
+if(CPACK_GENERATOR MATCHES "DEB")
+ set(CPACK_DEB_COMPONENT_INSTALL "ON")
+ set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS "ON")
+endif()
+
+#
+# Choose grouping way
+#
+#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE)
+#set(CPACK_COMPONENTS_GROUPING)
+set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
+#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
+
+# we set shlibdeps to on
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
+# setting dependencies
+# Note: We explicitly do NOT declare dependency to "libraries" component!
+#set(CPACK_COMPONENT_APPLICATIONS_DEPENDS "libraries")
diff --git a/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in
new file mode 100644
index 0000000..81d6c7b
--- /dev/null
+++ b/Tests/CPackComponentsDEB/MyLibCPackConfig-shlibdeps-with-ORIGIN-RPATH-success.cmake.in
@@ -0,0 +1,22 @@
+#
+# Activate component packaging
+#
+
+if(CPACK_GENERATOR MATCHES "DEB")
+ set(CPACK_DEB_COMPONENT_INSTALL "ON")
+ set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS "ON")
+endif()
+
+#
+# Choose grouping way
+#
+#set(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE)
+#set(CPACK_COMPONENTS_GROUPING)
+set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
+#set(CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE 1)
+
+# we set shlibdeps to on
+set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
+
+# setting dependencies
+set(CPACK_COMPONENT_APPLICATIONS_DEPENDS "libraries")
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake
new file mode 100644
index 0000000..dddcb4a
--- /dev/null
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-failure.cmake
@@ -0,0 +1,19 @@
+if(NOT CPackComponentsDEB_SOURCE_DIR)
+ message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set")
+endif()
+
+include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake)
+
+
+set(actual_output)
+run_cpack(actual_output
+ CPack_output
+ CPack_error
+ EXPECT_FAILURE
+ CONFIG_ARGS ${config_args}
+ CONFIG_VERBOSE ${config_verbose})
+
+string(REGEX MATCH "dpkg-shlibdeps: error: (cannot|couldn't) find[ \n\t]+library[ \n\t]+libmyotherlib.so.1[ \n\t]+needed[ \n\t]+by[ \n\t]+./usr/bin/mylibapp4" expected_error ${CPack_error})
+if(NOT expected_error)
+ message(FATAL_ERROR "Did not get the expected error-message!")
+endif()
diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake
new file mode 100644
index 0000000..6eff3db
--- /dev/null
+++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-shlibdeps-with-ORIGIN-RPATH-success.cmake
@@ -0,0 +1,75 @@
+if(NOT CPackComponentsDEB_SOURCE_DIR)
+ message(FATAL_ERROR "CPackComponentsDEB_SOURCE_DIR not set")
+endif()
+
+include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake)
+
+
+
+# requirements
+
+# debian now produces lower case names
+set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.3_*.deb")
+set(expected_count 3)
+
+
+set(actual_output)
+run_cpack(actual_output
+ CPack_output
+ CPack_error
+ EXPECTED_FILE_MASK "${expected_file_mask}"
+ CONFIG_ARGS ${config_args}
+ CONFIG_VERBOSE ${config_verbose})
+
+message(STATUS "expected_count='${expected_count}'")
+message(STATUS "expected_file_mask='${expected_file_mask}'")
+message(STATUS "actual_output_files='${actual_output}'")
+
+if(NOT actual_output)
+ message(FATAL_ERROR "error: expected_files do not exist: CPackComponentsDEB test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error}")
+endif()
+
+list(LENGTH actual_output actual_count)
+message(STATUS "actual_count='${actual_count}'")
+if(NOT actual_count EQUAL expected_count)
+ message(FATAL_ERROR "error: expected_count=${expected_count} does not match actual_count=${actual_count}: CPackComponents test fails. (CPack_output=${CPack_output}, CPack_error=${CPack_error})")
+endif()
+
+
+# dpkg-deb checks for the summary of the packages
+find_program(DPKGDEB_EXECUTABLE dpkg-deb)
+if(DPKGDEB_EXECUTABLE)
+ set(dpkgdeb_output_errors_all "")
+ foreach(_f IN LISTS actual_output)
+
+ # extracts the metadata from the package
+ run_dpkgdeb(dpkg_output
+ FILENAME ${_f}
+ )
+
+ dpkgdeb_return_specific_metaentry(dpkg_package_name
+ DPKGDEB_OUTPUT "${dpkg_output}"
+ METAENTRY "Package:")
+
+ message(STATUS "package='${dpkg_package_name}'")
+
+ if(dpkg_package_name STREQUAL "mylib-applications")
+ # pass
+ elseif(dpkg_package_name STREQUAL "mylib-headers")
+ # pass
+ elseif(dpkg_package_name STREQUAL "mylib-libraries")
+ # pass
+ else()
+ set(dpkgdeb_output_errors_all ${dpkgdeb_output_errors_all}
+ "dpkg-deb: ${_f}: component name not found: ${dpkg_package_name}\n")
+ endif()
+
+ endforeach()
+
+
+ if(NOT dpkgdeb_output_errors_all STREQUAL "")
+ message(FATAL_ERROR "dpkg-deb checks failed:\n${dpkgdeb_output_errors_all}")
+ endif()
+else()
+ message("dpkg-deb executable not found - skipping dpkg-deb test")
+endif()
diff --git a/Tests/CPackComponentsDEB/mylibapp.cpp b/Tests/CPackComponentsDEB/mylibapp.cpp
index bb45831..6d63803 100644
--- a/Tests/CPackComponentsDEB/mylibapp.cpp
+++ b/Tests/CPackComponentsDEB/mylibapp.cpp
@@ -1,13 +1,13 @@
-#ifndef SHLIBDEPS_PRIVATE
+#if defined SHLIBDEPS_OTHER
-# include "mylib.h"
+# include "subdir/myotherlib.h"
int main()
{
- mylib_function();
+ myotherlib_function();
}
-#else
+#elif defined SHLIBDEPS_PRIVATE
# include "shlibdeps-with-private-lib/myprivatelib.h"
@@ -16,4 +16,13 @@ int main()
myprivatelib_function();
}
+#else
+
+# include "mylib.h"
+
+int main()
+{
+ mylib_function();
+}
+
#endif
diff --git a/Tests/CPackComponentsDEB/subdir/CMakeLists.txt b/Tests/CPackComponentsDEB/subdir/CMakeLists.txt
new file mode 100644
index 0000000..71b3fdd
--- /dev/null
+++ b/Tests/CPackComponentsDEB/subdir/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_library(myotherlib SHARED myotherlib.cpp)
+set_target_properties( myotherlib PROPERTIES
+ VERSION 1.2.3
+ SOVERSION 1
+)
diff --git a/Tests/CPackComponentsDEB/subdir/myotherlib.cpp b/Tests/CPackComponentsDEB/subdir/myotherlib.cpp
new file mode 100644
index 0000000..eec270c
--- /dev/null
+++ b/Tests/CPackComponentsDEB/subdir/myotherlib.cpp
@@ -0,0 +1,8 @@
+#include "myotherlib.h"
+
+#include "stdio.h"
+
+void myotherlib_function()
+{
+ printf("This is myotherlib");
+}
diff --git a/Tests/CPackComponentsDEB/subdir/myotherlib.h b/Tests/CPackComponentsDEB/subdir/myotherlib.h
new file mode 100644
index 0000000..71848a0
--- /dev/null
+++ b/Tests/CPackComponentsDEB/subdir/myotherlib.h
@@ -0,0 +1 @@
+void myotherlib_function();