vcpkg/scripts/cmake/z_vcpkg_fixup_rpath_macho.cmake
2024-06-27 14:26:23 -07:00

160 lines
6.3 KiB
CMake
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

function(z_vcpkg_calculate_corrected_macho_rpath)
cmake_parse_arguments(PARSE_ARGV 0 "arg"
""
"MACHO_FILE_DIR;OUT_NEW_RPATH_VAR"
"")
if(DEFINED arg_UNPARSED_ARGUMENTS)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION} was passed extra arguments: ${arg_UNPARSED_ARGUMENTS}")
endif()
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/" OR "${relative_from_packages}/" MATCHES "^(manual-)?tools/.*/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
)
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)$]])
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()
debug_message("File `${macho_file}` reported as `${file_output}` is not a Mach-O file")
continue()
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
)
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}"
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()