mirror of
https://github.com/microsoft/vcpkg.git
synced 2024-11-27 22:11:37 +08:00
Fixup Macho-O rpath for osx-dynamic (#39313)
This commit is contained in:
parent
73a17352c6
commit
874fff8a6a
159
scripts/cmake/z_vcpkg_fixup_rpath_macho.cmake
Normal file
159
scripts/cmake/z_vcpkg_fixup_rpath_macho.cmake
Normal file
@ -0,0 +1,159 @@
|
||||
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()
|
@ -91,6 +91,7 @@ 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")
|
||||
include("${SCRIPTS}/cmake/z_vcpkg_fixup_rpath_macho.cmake")
|
||||
|
||||
function(debug_message)
|
||||
if(PORT_DEBUG)
|
||||
@ -190,10 +191,13 @@ target system or to the host system. Use a prefixed variable instead.
|
||||
|
||||
include("${CURRENT_PORT_DIR}/portfile.cmake")
|
||||
if(DEFINED PORT)
|
||||
# Always fixup RPATH on linux unless explicitly disabled.
|
||||
# Always fixup RPATH on linux and osx unless explicitly disabled.
|
||||
if(VCPKG_FIXUP_ELF_RPATH OR (VCPKG_TARGET_IS_LINUX AND NOT DEFINED VCPKG_FIXUP_ELF_RPATH))
|
||||
z_vcpkg_fixup_rpath_in_dir()
|
||||
endif()
|
||||
if(VCPKG_FIXUP_MACHO_RPATH OR (VCPKG_TARGET_IS_OSX AND NOT DEFINED VCPKG_FIXUP_MACHO_RPATH))
|
||||
z_vcpkg_fixup_macho_rpath_in_dir()
|
||||
endif()
|
||||
include("${SCRIPTS}/build_info.cmake")
|
||||
endif()
|
||||
elseif(CMD STREQUAL "CREATE")
|
||||
|
18
scripts/test_ports/rpath-test-binaries/portfile.cmake
Normal file
18
scripts/test_ports/rpath-test-binaries/portfile.cmake
Normal file
@ -0,0 +1,18 @@
|
||||
set(VCPKG_POLICY_EMPTY_INCLUDE_FOLDER enabled)
|
||||
vcpkg_check_linkage(ONLY_DYNAMIC_LIBRARY)
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH "${CURRENT_PORT_DIR}/project"
|
||||
OPTIONS_RELEASE
|
||||
-DTEST_STRING=release
|
||||
OPTIONS_DEBUG
|
||||
-DTEST_STRING=debug
|
||||
)
|
||||
vcpkg_cmake_install()
|
||||
if(NOT VCPKG_BUILD_TYPE)
|
||||
vcpkg_copy_tools(TOOL_NAMES rpath-test-tool
|
||||
SEARCH_DIR "${CURRENT_PACKAGES_DIR}/debug/bin"
|
||||
DESTINATION "${CURRENT_PACKAGES_DIR}/tools/${PORT}/debug"
|
||||
)
|
||||
endif()
|
||||
vcpkg_copy_tools(TOOL_NAMES rpath-test-tool AUTO_CLEAN)
|
||||
file(WRITE "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright" "This test port is part of vcpkg.")
|
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
project(rpath-test CXX)
|
||||
|
||||
set(TEST_STRING "" CACHE STRING "")
|
||||
|
||||
add_library(rpath-test-lib lib.cpp)
|
||||
target_compile_definitions(rpath-test-lib PRIVATE "TEST_STRING=\"${TEST_STRING}\"")
|
||||
|
||||
add_executable(rpath-test-tool main.cpp)
|
||||
target_link_libraries(rpath-test-tool PRIVATE rpath-test-lib)
|
||||
|
||||
install(TARGETS rpath-test-lib rpath-test-tool)
|
4
scripts/test_ports/rpath-test-binaries/project/lib.cpp
Normal file
4
scripts/test_ports/rpath-test-binaries/project/lib.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
const char* getTestString()
|
||||
{
|
||||
return TEST_STRING;
|
||||
}
|
8
scripts/test_ports/rpath-test-binaries/project/main.cpp
Normal file
8
scripts/test_ports/rpath-test-binaries/project/main.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern const char* getTestString();
|
||||
|
||||
int main()
|
||||
{
|
||||
puts(getTestString());
|
||||
}
|
12
scripts/test_ports/rpath-test-binaries/vcpkg.json
Normal file
12
scripts/test_ports/rpath-test-binaries/vcpkg.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "rpath-test-binaries",
|
||||
"version-string": "ci",
|
||||
"description": "Provides installed binaries for rpath fixup test",
|
||||
"supports": "native & !windows",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
}
|
||||
]
|
||||
}
|
23
scripts/test_ports/rpath-test/portfile.cmake
Normal file
23
scripts/test_ports/rpath-test/portfile.cmake
Normal file
@ -0,0 +1,23 @@
|
||||
set(VCPKG_POLICY_EMPTY_PACKAGE enabled)
|
||||
|
||||
vcpkg_execute_in_download_mode(
|
||||
COMMAND "${CURRENT_INSTALLED_DIR}/tools/rpath-test-binaries/rpath-test-tool"
|
||||
WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}"
|
||||
OUTPUT_VARIABLE output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT output STREQUAL "release")
|
||||
message(SEND_ERROR "Actual: '${output}', expected: 'release'")
|
||||
endif()
|
||||
|
||||
if(NOT VCPKG_BUILD_TYPE)
|
||||
vcpkg_execute_in_download_mode(
|
||||
COMMAND "${CURRENT_INSTALLED_DIR}/tools/rpath-test-binaries/debug/rpath-test-tool"
|
||||
WORKING_DIRECTORY "${CURRENT_BUILDTREES_DIR}"
|
||||
OUTPUT_VARIABLE output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT output STREQUAL "debug")
|
||||
message(SEND_ERROR "Actual: '${output}', expected: 'debug'")
|
||||
endif()
|
||||
endif()
|
8
scripts/test_ports/rpath-test/vcpkg.json
Normal file
8
scripts/test_ports/rpath-test/vcpkg.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "rpath-test",
|
||||
"version-string": "ci",
|
||||
"description": "Test rpath fixup",
|
||||
"dependencies": [
|
||||
"rpath-test-binaries"
|
||||
]
|
||||
}
|
@ -190,6 +190,7 @@ if("fixup-pkgconfig" IN_LIST FEATURES)
|
||||
endif()
|
||||
if("fixup-rpath" IN_LIST FEATURES)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/test-z_vcpkg_calculate_corrected_rpath.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/test-z_vcpkg_calculate_corrected_rpath_macho.cmake")
|
||||
endif()
|
||||
|
||||
if(Z_VCPKG_UNIT_TEST_HAS_ERROR)
|
||||
|
@ -0,0 +1,56 @@
|
||||
# z_vcpkg_calculate_corrected_macho_rpath_macho(...)
|
||||
|
||||
block(SCOPE_FOR VARIABLES)
|
||||
|
||||
set(CURRENT_PACKAGES_DIR "/P")
|
||||
set(CURRENT_INSTALLED_DIR "/I")
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/lib")
|
||||
]] out [[@loader_path]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/plugins/group")
|
||||
]] out [[@loader_path/../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/debug/lib")
|
||||
]] out [[@loader_path]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/debug/plugins/group")
|
||||
]] out [[@loader_path/../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/tools/port")
|
||||
]] out [[@loader_path/../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/tools/port/bin")
|
||||
]] out [[@loader_path/../../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/tools/port/debug")
|
||||
]] out [[@loader_path/../../../debug/lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/tools/port/debug/bin")
|
||||
]] out [[@loader_path/../../../../debug/lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/manual-tools/port")
|
||||
]] out [[@loader_path/../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/manual-tools/port/bin")
|
||||
]] out [[@loader_path/../../../lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/manual-tools/port/debug")
|
||||
]] out [[@loader_path/../../../debug/lib]])
|
||||
|
||||
unit_test_check_variable_equal([[
|
||||
z_vcpkg_calculate_corrected_macho_rpath(OUT_NEW_RPATH_VAR "out" MACHO_FILE_DIR "/P/manual-tools/port/debug/bin")
|
||||
]] out [[@loader_path/../../../../debug/lib]])
|
||||
|
||||
endblock()
|
65
scripts/test_ports/vcpkg-fixup-macho-rpath/portfile.cmake
Normal file
65
scripts/test_ports/vcpkg-fixup-macho-rpath/portfile.cmake
Normal file
@ -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()
|
6
scripts/test_ports/vcpkg-fixup-macho-rpath/vcpkg.json
Normal file
6
scripts/test_ports/vcpkg-fixup-macho-rpath/vcpkg.json
Normal file
@ -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"
|
||||
}
|
Loading…
Reference in New Issue
Block a user