diff --git a/scripts/cmake/z_vcpkg_fixup_rpath.cmake b/scripts/cmake/z_vcpkg_fixup_rpath.cmake index c61bda5a5dedbb..48db0fbdeedc39 100644 --- a/scripts/cmake/z_vcpkg_fixup_rpath.cmake +++ b/scripts/cmake/z_vcpkg_fixup_rpath.cmake @@ -1,3 +1,65 @@ +function(z_vcpkg_calculate_corrected_rpath) + cmake_parse_arguments(PARSE_ARGV 0 "arg" + "" + "ELF_FILE_DIR;ORG_RPATH;OUT_NEW_RPATH_VAR" + "") + + set(elf_file_dir "${arg_ELF_FILE_DIR}") + set(org_rpath "${arg_ORG_RPATH}") + + set(current_prefix "${CURRENT_PACKAGES_DIR}") + set(current_installed_prefix "${CURRENT_INSTALLED_DIR}") + if(elf_file_dir MATCHES "debug/") + set(current_prefix "${CURRENT_PACKAGES_DIR}/debug") + set(current_installed_prefix "${CURRENT_INSTALLED_DIR}/debug") + endif() + + # compute path relative to lib + file(RELATIVE_PATH relative_to_lib "${elf_file_dir}" "${current_prefix}/lib") + # compute path relative to prefix + file(RELATIVE_PATH relative_to_prefix "${elf_file_dir}" "${current_prefix}") + + set(rpath_norm "") + if(NOT org_rpath STREQUAL "") + cmake_path(CONVERT "${org_rpath}" TO_CMAKE_PATH_LIST rpath_norm) + list(TRANSFORM rpath_norm REPLACE "${elf_file_dir}" "\$ORIGIN") + # Remove unnecessary up/down ; don't use normalize $ORIGIN/../ will be removed otherwise + list(TRANSFORM rpath_norm REPLACE "/lib/pkgconfig/../.." "") + # lib relative corrections + list(TRANSFORM rpath_norm REPLACE "${current_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/") + list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/") + list(TRANSFORM rpath_norm REPLACE "${current_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/") + list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}/lib/?" "\$ORIGIN/${relative_to_lib}/") + # prefix relativ + list(TRANSFORM rpath_norm REPLACE "${current_prefix}" "\$ORIGIN/${relative_to_prefix}/") + list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}" "\$ORIGIN/${relative_to_prefix}/") + list(TRANSFORM rpath_norm REPLACE "${current_prefix}" "\$ORIGIN/${relative_to_prefix}/") + list(TRANSFORM rpath_norm REPLACE "${current_installed_prefix}" "\$ORIGIN/${relative_to_prefix}/") + + # Path normalization + list(TRANSFORM rpath_norm REPLACE "/+" "/") + list(TRANSFORM rpath_norm REPLACE "/^" "") + + # duplication removal + list(REMOVE_ITEM rpath_norm "\$ORIGIN") + list(REMOVE_ITEM rpath_norm "\$ORIGIN/${relative_to_lib}") + list(REMOVE_DUPLICATES rpath_norm) + + if(NOT X_VCPKG_RPATH_KEEP_SYSTEM_PATHS) + list(FILTER rpath_norm INCLUDE REGEX "\\\$ORIGIN.+") # Only keep paths relativ to ORIGIN + endif() + endif() + + if(NOT relative_to_lib STREQUAL "") + list(PREPEND rpath_norm "\$ORIGIN/${relative_to_lib}") + endif() + list(PREPEND rpath_norm "\$ORIGIN") # Make ORIGIN the first entry + list(TRANSFORM rpath_norm REPLACE "/$" "") + cmake_path(CONVERT "${rpath_norm}" TO_NATIVE_PATH_LIST new_rpath) + + set("${arg_OUT_NEW_RPATH_VAR}" "${new_rpath}" PARENT_SCOPE) +endfunction() + function(z_vcpkg_fixup_rpath_in_dir) vcpkg_find_acquire_program(PATCHELF) @@ -21,37 +83,41 @@ function(z_vcpkg_fixup_rpath_in_dir) endif() file(GLOB_RECURSE elf_files LIST_DIRECTORIES FALSE "${folder}/*") + list(FILTER elf_files EXCLUDE REGEX "\\\.(cpp|cc|cxx|c|hpp|h|hh|hxx|inc|json|toml|yaml|man|m4|ac|am|in|log|txt|pyi?|pyc|pyx|pxd|pc|cmake|f77|f90|f03|fi|f|cu|mod|ini|whl|cat|csv|rst|md|npy|npz|template|build)$") + list(FILTER elf_files EXCLUDE REGEX "/(copyright|LICENSE|METADATA)$") + foreach(elf_file IN LISTS elf_files) if(IS_SYMLINK "${elf_file}") continue() endif() - get_filename_component(elf_file_dir "${elf_file}" DIRECTORY) - - set(current_prefix "${CURRENT_PACKAGES_DIR}") - if(elf_file_dir MATCHES "debug/") - set(current_prefix "${CURRENT_PACKAGES_DIR}/debug") + # If this fails, the file is not an elf + execute_process( + COMMAND "${PATCHELF}" --print-rpath "${elf_file}" + OUTPUT_VARIABLE readelf_output + ERROR_VARIABLE read_rpath_error + ) + string(REPLACE "\n" "" readelf_output "${readelf_output}") + if(NOT "${read_rpath_error}" STREQUAL "" OR "${readelf_output}" STREQUAL "") + continue() endif() - # compute path relative to lib - file(RELATIVE_PATH relative_to_lib "${elf_file_dir}" "${current_prefix}/lib") - if(relative_to_lib STREQUAL "") - set(rpath "\$ORIGIN") - else() - set(rpath "\$ORIGIN:\$ORIGIN/${relative_to_lib}") - endif() + get_filename_component(elf_file_dir "${elf_file}" DIRECTORY) + + Z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_file_dir}" + ORG_RPATH "${readelf_output}" + OUT_NEW_RPATH_VAR new_rpath + ) - # If this fails, the file is not an elf execute_process( - COMMAND "${PATCHELF}" --set-rpath "${rpath}" "${elf_file}" + COMMAND "${PATCHELF}" --set-rpath "${new_rpath}" "${elf_file}" OUTPUT_QUIET ERROR_VARIABLE set_rpath_error ) - if("${set_rpath_error}" STREQUAL "") - message(STATUS "Fixed rpath: ${elf_file} (${rpath})") - endif() + + message(STATUS "Adjusted RPATH of '${elf_file}' (From '${org_rpath}' -> To '${new_rpath}')") + endforeach() endforeach() endfunction() - -z_vcpkg_fixup_rpath_in_dir() diff --git a/scripts/ports.cmake b/scripts/ports.cmake index e18ee37bde5ef7..0b907aa04a9b7d 100644 --- a/scripts/ports.cmake +++ b/scripts/ports.cmake @@ -87,6 +87,8 @@ include("${SCRIPTS}/cmake/z_vcpkg_get_cmake_vars.cmake") include("${SCRIPTS}/cmake/z_vcpkg_prettify_command_line.cmake") include("${SCRIPTS}/cmake/z_vcpkg_setup_pkgconfig_path.cmake") +include("${SCRIPTS}/cmake/z_vcpkg_fixup_rpath.cmake") + function(debug_message) if(PORT_DEBUG) z_vcpkg_function_arguments(ARGS) @@ -169,8 +171,8 @@ if(CMD STREQUAL "BUILD") include("${CURRENT_PORT_DIR}/portfile.cmake") if(DEFINED PORT) - if(VCPKG_FIXUP_ELF_RPATH) - include("${SCRIPTS}/cmake/z_vcpkg_fixup_rpath.cmake") + if(VCPKG_FIXUP_ELF_RPATH OR VCPKG_TARGET_IS_LINUX) + z_vcpkg_fixup_rpath_in_dir() endif() include("${SCRIPTS}/build_info.cmake") endif() diff --git a/scripts/test_ports/vcpkg-find-acquire-program/portfile.cmake b/scripts/test_ports/vcpkg-find-acquire-program/portfile.cmake index f6ce20fefa8b76..af8a834dc8c7f2 100644 --- a/scripts/test_ports/vcpkg-find-acquire-program/portfile.cmake +++ b/scripts/test_ports/vcpkg-find-acquire-program/portfile.cmake @@ -47,11 +47,12 @@ endif() set(missing "") foreach(variable IN LISTS variables) - list(POP_BACK "${variable}" program) + set(var_contents "${${variable}}") + list(POP_BACK var_contents program) if(NOT EXISTS "${program}") list(APPEND missing "${variable}: ${program}") endif() - list(POP_FRONT "${variable}" interpreter) + list(POP_FRONT var_contents interpreter) if(interpreter AND NOT EXISTS "${interpreter}") list(APPEND missing "${variable} (interpreter): ${interpreter}") endif() diff --git a/scripts/test_ports/vcpkg-find-acquire-program/vcpkg.json b/scripts/test_ports/vcpkg-find-acquire-program/vcpkg.json index fb9166ef5b82c8..0b622ebbbf5470 100644 --- a/scripts/test_ports/vcpkg-find-acquire-program/vcpkg.json +++ b/scripts/test_ports/vcpkg-find-acquire-program/vcpkg.json @@ -1,7 +1,7 @@ { "name": "vcpkg-find-acquire-program", "version-string": "0", - "port-version": 1, + "port-version": 2, "description": "Test port to exercise vcpkg_find_acquire_program", "supports": "native" } diff --git a/scripts/test_ports/vcpkg-fix-rpath/portfile.cmake b/scripts/test_ports/vcpkg-fix-rpath/portfile.cmake new file mode 100644 index 00000000000000..ca2f5066463f8c --- /dev/null +++ b/scripts/test_ports/vcpkg-fix-rpath/portfile.cmake @@ -0,0 +1,89 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) + + +# Simple replacement and outside path test +set(elf_dir "${CURRENT_PACKAGES_DIR}/lib") +set(test_rpath "${CURRENT_PACKAGES_DIR}/lib:/usr/lib/") +set(expected "$ORIGIN") + +z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_dir}" + ORG_RPATH "${test_rpath}" + OUT_NEW_RPATH_VAR new_rpath +) + +if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") + message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ") +else() + message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ") +endif() + +# Simple pkgconfig path and outside path test +set(elf_dir "${CURRENT_PACKAGES_DIR}/lib/") +set(test_rpath "${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../lib:/usr/lib/") +set(expected "$ORIGIN") + +z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_dir}" + ORG_RPATH "${test_rpath}" + OUT_NEW_RPATH_VAR new_rpath +) + +if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") + message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ") +else() + message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ") +endif() + +# elf dir in subdir +set(elf_dir "${CURRENT_PACKAGES_DIR}/lib/somesubdir") +set(test_rpath "${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../lib:/usr/lib/") +set(expected "$ORIGIN:$ORIGIN/..") + +z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_dir}" + ORG_RPATH "${test_rpath}" + OUT_NEW_RPATH_VAR new_rpath +) + +if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") + message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ") +else() + message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ") +endif() + +# Getting more complex +set(elf_dir "${CURRENT_PACKAGES_DIR}/plugins/notlib/extrasubdir") +set(test_rpath "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/../../lib/someotherdir2:${CURRENT_INSTALLED_DIR}/lib/pkgconfig/../../someotherdir1:/usr/lib/") +set(expected "$ORIGIN:$ORIGIN/../../../lib:$ORIGIN/../../../lib/someotherdir2:$ORIGIN/../../../someotherdir1") + +z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_dir}" + ORG_RPATH "${test_rpath}" + OUT_NEW_RPATH_VAR new_rpath +) + +if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") + message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ") +else() + message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ") +endif() + + +set(X_VCPKG_RPATH_KEEP_SYSTEM_PATHS ON) +# Simple replacement and outside path test +set(elf_dir "${CURRENT_PACKAGES_DIR}/lib") +set(test_rpath "${CURRENT_PACKAGES_DIR}/lib:/usr/lib/") +set(expected "$ORIGIN:/usr/lib") + +z_vcpkg_calculate_corrected_rpath( + ELF_FILE_DIR "${elf_dir}" + ORG_RPATH "${test_rpath}" + OUT_NEW_RPATH_VAR new_rpath +) + +if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") + message(FATAL_ERROR "--- Calculated rpath does not agree with expected rpath: '${new_rpath}' != '${expected}' ") +else() + message(STATUS "--- Calculated rpath agrees with expected rpath: '${new_rpath}' ") +endif() \ No newline at end of file diff --git a/scripts/test_ports/vcpkg-fix-rpath/vcpkg.json b/scripts/test_ports/vcpkg-fix-rpath/vcpkg.json new file mode 100644 index 00000000000000..c39dc3920311bd --- /dev/null +++ b/scripts/test_ports/vcpkg-fix-rpath/vcpkg.json @@ -0,0 +1,6 @@ +{ + "name": "vcpkg-fix-rpath", + "version-date": "2024-02-10", + "description": "Test port to check the string replacement in z_vcpkg_fixup_rpath", + "supports": "native & linux" +}