-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Fixup Macho-O rpath for osx-dynamic #39313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
8a3d437
d6501c9
82e282d
fc9c3a7
1f94843
8a79bec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,154 @@ | ||
| function(z_vcpkg_calculate_corrected_macho_rpath) | ||
| cmake_parse_arguments(PARSE_ARGV 0 "arg" | ||
| "" | ||
| "MACHO_FILE_DIR;OUT_NEW_RPATH_VAR" | ||
| "") | ||
|
|
||
|
m-kuhn marked this conversation as resolved.
|
||
| set(current_prefix "${CURRENT_PACKAGES_DIR}") | ||
| set(current_installed_prefix "${CURRENT_INSTALLED_DIR}") | ||
| file(RELATIVE_PATH relative_from_packages "${CURRENT_PACKAGES_DIR}" "${arg_MACHO_FILE_DIR}") | ||
| if("${relative_from_packages}/" 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 "${arg_MACHO_FILE_DIR}" "${current_prefix}/lib") | ||
| # remove trailing slash | ||
| string(REGEX REPLACE "/+$" "" relative_to_lib "${relative_to_lib}") | ||
|
|
||
| if(NOT relative_to_lib STREQUAL "") | ||
| set(new_rpath "@loader_path/${relative_to_lib}") | ||
| else() | ||
| set(new_rpath "@loader_path") | ||
| endif() | ||
|
|
||
| set("${arg_OUT_NEW_RPATH_VAR}" "${new_rpath}" PARENT_SCOPE) | ||
| endfunction() | ||
|
|
||
| function(z_vcpkg_fixup_macho_rpath_in_dir) | ||
| # We need to iterate through everything because we | ||
| # can't predict where a Mach-O file will be located | ||
| file(GLOB root_entries LIST_DIRECTORIES TRUE "${CURRENT_PACKAGES_DIR}/*") | ||
|
|
||
| # Skip some folders for better throughput | ||
| list(APPEND folders_to_skip "include") | ||
| list(JOIN folders_to_skip "|" folders_to_skip_regex) | ||
| set(folders_to_skip_regex "^(${folders_to_skip_regex})$") | ||
|
|
||
| find_program( | ||
| install_name_tool_cmd | ||
| NAMES install_name_tool | ||
| DOC "Absolute path of install_name_tool cmd" | ||
| REQUIRED | ||
| ) | ||
|
|
||
| find_program( | ||
| otool_cmd | ||
| NAMES otool | ||
| DOC "Absolute path of otool cmd" | ||
| REQUIRED | ||
| ) | ||
|
|
||
| find_program( | ||
| file_cmd | ||
| NAMES file | ||
| DOC "Absolute path of file cmd" | ||
| REQUIRED | ||
| ) | ||
|
Comment on lines
+43
to
+62
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are these and what does a user need to do if they aren't present?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tools to modify and change dynamic libraries (install_name tool and otool) or inspect files (file tool). |
||
|
|
||
| foreach(folder IN LISTS root_entries) | ||
| if(NOT IS_DIRECTORY "${folder}") | ||
| continue() | ||
| endif() | ||
|
|
||
| get_filename_component(folder_name "${folder}" NAME) | ||
| if(folder_name MATCHES "${folders_to_skip_regex}") | ||
| continue() | ||
| endif() | ||
|
|
||
| file(GLOB_RECURSE macho_files LIST_DIRECTORIES FALSE "${folder}/*") | ||
| list(FILTER macho_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)$") | ||
|
m-kuhn marked this conversation as resolved.
Outdated
|
||
| list(FILTER macho_files EXCLUDE REGEX "/(copyright|LICENSE|METADATA)$") | ||
|
|
||
| foreach(macho_file IN LISTS macho_files) | ||
| if(IS_SYMLINK "${macho_file}") | ||
| continue() | ||
| endif() | ||
|
|
||
| # Determine if the file is a Mach-O executable or shared library | ||
| execute_process( | ||
| COMMAND "${file_cmd}" -b "${macho_file}" | ||
| OUTPUT_VARIABLE file_output | ||
| OUTPUT_STRIP_TRAILING_WHITESPACE | ||
| ) | ||
| if(file_output MATCHES ".*Mach-O.*shared library.*") | ||
| set(file_type "shared") | ||
| elseif(file_output MATCHES ".*Mach-O.*executable.*") | ||
| set(file_type "executable") | ||
| else() | ||
| continue() | ||
|
m-kuhn marked this conversation as resolved.
|
||
| endif() | ||
|
|
||
| get_filename_component(macho_file_dir "${macho_file}" DIRECTORY) | ||
| get_filename_component(macho_file_name "${macho_file}" NAME) | ||
|
|
||
| z_vcpkg_calculate_corrected_macho_rpath( | ||
| MACHO_FILE_DIR "${macho_file_dir}" | ||
| OUT_NEW_RPATH_VAR new_rpath | ||
| ) | ||
|
m-kuhn marked this conversation as resolved.
|
||
|
|
||
| if("${file_type}" STREQUAL "shared") | ||
| # Set the install name for shared libraries | ||
| execute_process( | ||
| COMMAND "${install_name_tool_cmd}" -id "@rpath/${macho_file_name}" "${macho_file}" | ||
| OUTPUT_QUIET | ||
| ERROR_VARIABLE set_id_error | ||
| ) | ||
| message(STATUS "Set install name id of '${macho_file}' (To '@rpath/${macho_file_name}')") | ||
| if(NOT "${set_id_error}" STREQUAL "") | ||
| message(WARNING "Couldn't adjust install name of '${macho_file}': ${set_id_error}") | ||
| continue() | ||
| endif() | ||
| endif() | ||
|
|
||
| # Clear all existing rpaths | ||
| execute_process( | ||
| COMMAND "${otool_cmd}" -l "${macho_file}" | ||
| OUTPUT_VARIABLE get_rpath_ov | ||
| RESULT_VARIABLE get_rpath_rv | ||
| ) | ||
|
|
||
| if(NOT get_rpath_rv EQUAL 0) | ||
| message(FATAL_ERROR "Could not obtain rpath list from '${macho_file}'") | ||
| endif() | ||
| # Extract the LC_RPATH load commands and extract the paths | ||
| string(REGEX REPLACE "[^\n]+cmd LC_RPATH\n[^\n]+\n[^\n]+path ([^\n]+) \\(offset[^\n]+\n" "rpath \\1\n" get_rpath_ov "${get_rpath_ov}") | ||
| string(REGEX MATCHALL "rpath [^\n]+" get_rpath_ov "${get_rpath_ov}") | ||
| string(REGEX REPLACE "rpath " "" rpath_list "${get_rpath_ov}") | ||
|
|
||
| foreach(rpath IN LISTS rpath_list) | ||
| execute_process( | ||
| COMMAND "${install_name_tool_cmd}" -delete_rpath "${rpath}" "${macho_file}" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have tested this approach with a wide variety of ports (qt, qt5, even python via open-vcpkg) and it worked reliably.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it works reliably, great! No further questions.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @m-kuhn Here the output of a just built ffmpeg library, which depends on other ffmpeg libraries: We can see that the dependencies of the library have a path to the packages directory. I guess the Sadly I didn't have time before to test it before merging. So sorry.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the feedback, much appreciated. I can reproduce this here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the delay. Done #39890 |
||
| OUTPUT_QUIET | ||
| ERROR_VARIABLE delete_rpath_error | ||
| ) | ||
| message(STATUS "Remove RPATH from '${macho_file}' ('${rpath}')") | ||
| endforeach() | ||
|
|
||
| # Set the new rpath | ||
| execute_process( | ||
| COMMAND "${install_name_tool_cmd}" -add_rpath "${new_rpath}" "${macho_file}" | ||
| OUTPUT_QUIET | ||
| ERROR_VARIABLE set_rpath_error | ||
| ) | ||
|
|
||
| if(NOT "${set_rpath_error}" STREQUAL "") | ||
| message(WARNING "Couldn't adjust RPATH of '${macho_file}': ${set_rpath_error}") | ||
| continue() | ||
| endif() | ||
|
|
||
| message(STATUS "Adjusted RPATH of '${macho_file}' (To '${new_rpath}')") | ||
| endforeach() | ||
| endforeach() | ||
| endfunction() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||
|
|
||
| # Test for empty string | ||
| set(macho_dir "${CURRENT_PACKAGES_DIR}/lib") | ||
| set(test_rpath "") | ||
| set(expected "@loader_path") | ||
|
|
||
| z_vcpkg_calculate_corrected_macho_rpath( | ||
| MACHO_FILE_DIR "${macho_dir}" | ||
| OUT_NEW_RPATH_VAR new_rpath | ||
| ) | ||
|
|
||
| if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") | ||
| message(FATAL_ERROR "--- Calculated rpath does not match expected rpath: '${new_rpath}' != '${expected}' ") | ||
| else() | ||
| message(STATUS "--- Calculated rpath matches expected rpath: '${new_rpath}' ") | ||
| endif() | ||
|
|
||
| # Test for empty string in the tools directory | ||
| set(macho_dir "${CURRENT_PACKAGES_DIR}/tools/hdf5") | ||
| set(test_rpath "") | ||
| set(expected "@loader_path/../../lib") | ||
|
|
||
| z_vcpkg_calculate_corrected_macho_rpath( | ||
| MACHO_FILE_DIR "${macho_dir}" | ||
| OUT_NEW_RPATH_VAR new_rpath | ||
| ) | ||
|
|
||
| if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") | ||
| message(FATAL_ERROR "--- Calculated rpath does not match expected rpath: '${new_rpath}' != '${expected}' ") | ||
| else() | ||
| message(STATUS "--- Calculated rpath matches expected rpath: '${new_rpath}' ") | ||
| endif() | ||
|
|
||
| # macho dir in subdir | ||
| set(macho_dir "${CURRENT_PACKAGES_DIR}/lib/somesubdir") | ||
| set(test_rpath "") | ||
| set(expected "@loader_path/..") | ||
|
|
||
| z_vcpkg_calculate_corrected_macho_rpath( | ||
| MACHO_FILE_DIR "${macho_dir}" | ||
| OUT_NEW_RPATH_VAR new_rpath | ||
| ) | ||
|
|
||
| if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") | ||
| message(FATAL_ERROR "--- Calculated rpath for '${macho_dir}' does not match expected rpath: '${new_rpath}' != '${expected}' ") | ||
| else() | ||
| message(STATUS "--- Calculated rpath matches expected rpath: '${new_rpath}' ") | ||
| endif() | ||
|
|
||
| # Getting more complex | ||
| set(macho_dir "${CURRENT_PACKAGES_DIR}/plugins/notlib/extrasubdir") | ||
| set(test_rpath "") | ||
| set(expected "@loader_path/../../../lib") | ||
|
|
||
| z_vcpkg_calculate_corrected_macho_rpath( | ||
| MACHO_FILE_DIR "${macho_dir}" | ||
| OUT_NEW_RPATH_VAR new_rpath | ||
| ) | ||
|
|
||
| if(NOT "x${new_rpath}x" STREQUAL "x${expected}x") | ||
| message(FATAL_ERROR "--- Calculated rpath does not match expected rpath: '${new_rpath}' != '${expected}' ") | ||
| else() | ||
| message(STATUS "--- Calculated rpath matches expected rpath: '${new_rpath}' ") | ||
| endif() | ||
|
m-kuhn marked this conversation as resolved.
Outdated
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "name": "vcpkg-fixup-macho-rpath", | ||
| "version-date": "2024-06-15", | ||
| "description": "Test port to check the string replacement in z_vcpkg_fixup_macho_rpath", | ||
| "supports": "native & osx" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test just does string replacements; shouldn't it work on all platforms rather than only osx?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Follows the pattern of the linux/elf equivalent
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to migrate this to |
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.