BundleUtilities: Resolve & replace @rpath placeholders
This is done by gathering LC_RPATH commands for main bundle executable and using it for @rpath lookup in dependent frameworks. All functions that need to carry rpaths to now take optional <rpaths> argument. This enabled apps using @rpath to be bundled correctly, which will be necessary for upcoming Qt 5.4 that will use @rpath for all frameworks. Check that install_name_tool has -delete_rpath before using it. Otherwise it will fail with Xcode 3.x on 10.5 which has no -delete_rpath option for install_name_tool command, that was first introduced in 10.6 SDK, even that 10.5 supports LC_RPATH and @rpath.
This commit is contained in:
parent
14bc686fff
commit
f7df82acaa
|
@ -19,6 +19,7 @@
|
|||
# get_bundle_and_executable
|
||||
# get_bundle_all_executables
|
||||
# get_item_key
|
||||
# get_item_rpaths
|
||||
# clear_bundle_keys
|
||||
# set_bundle_key_values
|
||||
# get_bundle_keys
|
||||
|
@ -124,7 +125,7 @@
|
|||
# ::
|
||||
#
|
||||
# SET_BUNDLE_KEY_VALUES(<keys_var> <context> <item> <exepath> <dirs>
|
||||
# <copyflag>)
|
||||
# <copyflag> [<rpaths>])
|
||||
#
|
||||
# Add a key to the list (if necessary) for the given item. If added,
|
||||
# also set all the variables associated with that key.
|
||||
|
@ -408,6 +409,29 @@ function(get_bundle_all_executables bundle exes_var)
|
|||
endfunction()
|
||||
|
||||
|
||||
function(get_item_rpaths item rpaths_var)
|
||||
if(APPLE)
|
||||
find_program(otool_cmd "otool")
|
||||
mark_as_advanced(otool_cmd)
|
||||
endif()
|
||||
|
||||
if(otool_cmd)
|
||||
execute_process(
|
||||
COMMAND "${otool_cmd}" -l "${item}"
|
||||
OUTPUT_VARIABLE load_cmds_ov
|
||||
)
|
||||
string(REGEX REPLACE "[^\n]+cmd LC_RPATH\n[^\n]+\n[^\n]+path ([^\n]+) \\(offset[^\n]+\n" "rpath \\1\n" load_cmds_ov "${load_cmds_ov}")
|
||||
string(REGEX MATCHALL "rpath [^\n]+" load_cmds_ov "${load_cmds_ov}")
|
||||
string(REGEX REPLACE "rpath " "" load_cmds_ov "${load_cmds_ov}")
|
||||
if(load_cmds_ov)
|
||||
gp_append_unique(${rpaths_var} "${load_cmds_ov}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(${rpaths_var} ${${rpaths_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_item_key item key_var)
|
||||
get_filename_component(item_name "${item}" NAME)
|
||||
if(WIN32)
|
||||
|
@ -426,12 +450,14 @@ function(clear_bundle_keys keys_var)
|
|||
set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG PARENT_SCOPE)
|
||||
set(${key}_RPATHS PARENT_SCOPE)
|
||||
endforeach()
|
||||
set(${keys_var} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(set_bundle_key_values keys_var context item exepath dirs copyflag)
|
||||
set(rpaths "${ARGV6}")
|
||||
get_filename_component(item_name "${item}" NAME)
|
||||
|
||||
get_item_key("${item}" key)
|
||||
|
@ -441,10 +467,12 @@ function(set_bundle_key_values keys_var context item exepath dirs copyflag)
|
|||
list(LENGTH ${keys_var} length_after)
|
||||
|
||||
if(NOT length_before EQUAL length_after)
|
||||
gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
||||
gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
|
||||
|
||||
gp_item_default_embedded_path("${item}" default_embedded_path)
|
||||
|
||||
get_item_rpaths("${resolved_item}" item_rpaths)
|
||||
|
||||
if(item MATCHES "[^/]+\\.framework/")
|
||||
# For frameworks, construct the name under the embedded path from the
|
||||
# opening "${item_name}.framework/" to the closing "/${item_name}":
|
||||
|
@ -480,6 +508,8 @@ function(set_bundle_key_values keys_var context item exepath dirs copyflag)
|
|||
set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE)
|
||||
set(${key}_RPATHS "${item_rpaths}" PARENT_SCOPE)
|
||||
set(${key}_RDEP_RPATHS "${rpaths}" PARENT_SCOPE)
|
||||
else()
|
||||
#message("warning: item key '${key}' already in the list, subsequent references assumed identical to first")
|
||||
endif()
|
||||
|
@ -500,18 +530,27 @@ function(get_bundle_keys app libs dirs keys_var)
|
|||
#
|
||||
get_bundle_all_executables("${bundle}" exes)
|
||||
|
||||
# Set keys for main executable first:
|
||||
#
|
||||
set_bundle_key_values(${keys_var} "${executable}" "${executable}" "${exepath}" "${dirs}" 0)
|
||||
|
||||
# Get rpaths specified by main executable:
|
||||
#
|
||||
get_item_key("${executable}" executable_key)
|
||||
set(main_rpaths "${${executable_key}_RPATHS}")
|
||||
|
||||
# For each extra lib, accumulate a key as well and then also accumulate
|
||||
# any of its prerequisites. (Extra libs are typically dynamically loaded
|
||||
# plugins: libraries that are prerequisites for full runtime functionality
|
||||
# but that do not show up in otool -L output...)
|
||||
#
|
||||
foreach(lib ${libs})
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0)
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}")
|
||||
get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}" "${main_rpaths}")
|
||||
foreach(pr ${prereqs})
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1)
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1 "${main_rpaths}")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
|
@ -520,16 +559,27 @@ function(get_bundle_keys app libs dirs keys_var)
|
|||
# binaries in the bundle have been analyzed.
|
||||
#
|
||||
foreach(exe ${exes})
|
||||
# Add the exe itself to the keys:
|
||||
# Main executable is scanned first above:
|
||||
#
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0)
|
||||
if(NOT "${exe}" STREQUAL "${executable}")
|
||||
# Add the exe itself to the keys:
|
||||
#
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0 "${main_rpaths}")
|
||||
|
||||
# Get rpaths specified by executable:
|
||||
#
|
||||
get_item_key("${exe}" exe_key)
|
||||
set(exe_rpaths "${main_rpaths}" "${${exe_key}_RPATHS}")
|
||||
else()
|
||||
set(exe_rpaths "${main_rpaths}")
|
||||
endif()
|
||||
|
||||
# Add each prerequisite to the keys:
|
||||
#
|
||||
set(prereqs "")
|
||||
get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}")
|
||||
get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}" "${exe_rpaths}")
|
||||
foreach(pr ${prereqs})
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1)
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1 "${exe_rpaths}")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
|
@ -543,6 +593,8 @@ function(get_bundle_keys app libs dirs keys_var)
|
|||
set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE)
|
||||
set(${key}_RPATHS "${${key}_RPATHS}" PARENT_SCOPE)
|
||||
set(${key}_RDEP_RPATHS "${${key}_RDEP_RPATHS}" PARENT_SCOPE)
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
@ -648,8 +700,10 @@ function(fixup_bundle_item resolved_embedded_item exepath dirs)
|
|||
message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
|
||||
endif()
|
||||
|
||||
set(rpaths "${${ikey}_RPATHS}" "${${ikey}_RDEP_RPATHS}")
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}")
|
||||
get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}" "${rpaths}")
|
||||
|
||||
set(changes "")
|
||||
|
||||
|
@ -669,12 +723,28 @@ function(fixup_bundle_item resolved_embedded_item exepath dirs)
|
|||
execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
# Only if install_name_tool supports -delete_rpath:
|
||||
#
|
||||
execute_process(COMMAND install_name_tool
|
||||
OUTPUT_VARIABLE install_name_tool_usage
|
||||
ERROR_VARIABLE install_name_tool_usage
|
||||
)
|
||||
if(install_name_tool_usage MATCHES ".*-delete_rpath.*")
|
||||
foreach(rpath ${${ikey}_RPATHS})
|
||||
set(changes ${changes} -delete_rpath "${rpath}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(${ikey}_EMBEDDED_ITEM)
|
||||
set(changes ${changes} -id "${${ikey}_EMBEDDED_ITEM}")
|
||||
endif()
|
||||
|
||||
# Change this item's id and all of its references in one call
|
||||
# to install_name_tool:
|
||||
#
|
||||
execute_process(COMMAND install_name_tool
|
||||
${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}"
|
||||
)
|
||||
if(changes)
|
||||
execute_process(COMMAND install_name_tool ${changes} "${resolved_embedded_item}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
|
@ -765,10 +835,8 @@ function(verify_bundle_prerequisites bundle result_var info_var)
|
|||
|
||||
get_bundle_main_executable("${bundle}" main_bundle_exe)
|
||||
|
||||
file(GLOB_RECURSE file_list "${bundle}/*")
|
||||
get_bundle_all_executables("${bundle}" file_list)
|
||||
foreach(f ${file_list})
|
||||
is_file_executable("${f}" is_executable)
|
||||
if(is_executable)
|
||||
get_filename_component(exepath "${f}" PATH)
|
||||
math(EXPR count "${count} + 1")
|
||||
|
||||
|
@ -807,7 +875,6 @@ function(verify_bundle_prerequisites bundle result_var info_var)
|
|||
set(result 0)
|
||||
set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(result)
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
# ::
|
||||
#
|
||||
# GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
|
||||
# <exepath> <dirs>)
|
||||
# <exepath> <dirs> [<rpaths>])
|
||||
#
|
||||
# Get the list of shared library files required by <target>. The list
|
||||
# in the variable named <prerequisites_var> should be empty on first
|
||||
|
@ -113,7 +113,8 @@
|
|||
#
|
||||
# ::
|
||||
#
|
||||
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>)
|
||||
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>
|
||||
# [<rpaths>])
|
||||
#
|
||||
# Resolve an item into an existing full path file.
|
||||
#
|
||||
|
@ -122,7 +123,8 @@
|
|||
#
|
||||
# ::
|
||||
#
|
||||
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>)
|
||||
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>
|
||||
# [<rpaths>])
|
||||
#
|
||||
# Return the type of <file> with respect to <original_file>. String
|
||||
# describing type of prerequisite is returned in variable named
|
||||
|
@ -321,6 +323,7 @@ endfunction()
|
|||
function(gp_resolve_item context item exepath dirs resolved_item_var)
|
||||
set(resolved 0)
|
||||
set(resolved_item "${item}")
|
||||
set(rpaths "${ARGV5}")
|
||||
|
||||
# Is it already resolved?
|
||||
#
|
||||
|
@ -375,9 +378,9 @@ function(gp_resolve_item context item exepath dirs resolved_item_var)
|
|||
string(REPLACE "@rpath/" "" norpath_item "${item}")
|
||||
|
||||
set(ri "ri-NOTFOUND")
|
||||
find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
|
||||
find_file(ri "${norpath_item}" ${exepath} ${dirs} ${rpaths} NO_DEFAULT_PATH)
|
||||
if(ri)
|
||||
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
|
||||
#message(STATUS "info: 'find_file' in exepath/dirs/rpaths (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
set(ri "ri-NOTFOUND")
|
||||
|
@ -471,6 +474,7 @@ endfunction()
|
|||
|
||||
|
||||
function(gp_resolved_file_type original_file file exepath dirs type_var)
|
||||
set(rpaths "${ARGV5}")
|
||||
#message(STATUS "**")
|
||||
|
||||
if(NOT IS_ABSOLUTE "${original_file}")
|
||||
|
@ -489,7 +493,7 @@ function(gp_resolved_file_type original_file file exepath dirs type_var)
|
|||
|
||||
if(NOT is_embedded)
|
||||
if(NOT IS_ABSOLUTE "${file}")
|
||||
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file)
|
||||
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file "${rpaths}")
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${original_file}" original_lower)
|
||||
|
@ -612,6 +616,7 @@ endfunction()
|
|||
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
|
||||
set(verbose 0)
|
||||
set(eol_char "E")
|
||||
set(rpaths "${ARGV6}")
|
||||
|
||||
if(NOT IS_ABSOLUTE "${target}")
|
||||
message("warning: target '${target}' is not absolute...")
|
||||
|
@ -834,7 +839,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
|
|||
|
||||
if(add_item AND ${exclude_system})
|
||||
set(type "")
|
||||
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type)
|
||||
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type "${rpaths}")
|
||||
|
||||
if("${type}" STREQUAL "system")
|
||||
set(add_item 0)
|
||||
|
@ -855,7 +860,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
|
|||
# that the analysis tools can simply accept it as input.
|
||||
#
|
||||
if(NOT list_length_before_append EQUAL list_length_after_append)
|
||||
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
||||
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item "${rpaths}")
|
||||
set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
|
||||
endif()
|
||||
endif()
|
||||
|
@ -874,7 +879,7 @@ function(get_prerequisites target prerequisites_var exclude_system recurse exepa
|
|||
if(${recurse})
|
||||
set(more_inputs ${unseen_prereqs})
|
||||
foreach(input ${more_inputs})
|
||||
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}")
|
||||
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}" "${rpaths}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
|
Loading…
Reference in New Issue