From ce5904092d327584e2041f38b69bcae3be68ac14 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Fri, 26 Oct 2018 21:04:21 +0000 Subject: [PATCH 1/3] Package non-system libraries inside the framework This automatically copies libssl & libcrypto inside our framework bundle. --- .../project.pbxproj | 15 +++ script/repackage-dylibs.rb | 110 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100755 script/repackage-dylibs.rb diff --git a/ObjectiveGitFramework.xcodeproj/project.pbxproj b/ObjectiveGitFramework.xcodeproj/project.pbxproj index 66b9339a3..5043f44c4 100644 --- a/ObjectiveGitFramework.xcodeproj/project.pbxproj +++ b/ObjectiveGitFramework.xcodeproj/project.pbxproj @@ -1225,6 +1225,7 @@ 79262F0F13C697BE00A4B1EA /* Copy git2 Headers */, BEF7E4DF1A3A47450035BB8E /* Copy git2 Headers again */, 8DC2EF500486A6940098B216 /* Headers */, + 4D751E9D215D765D003CD3CE /* Package external libraries */, ); buildRules = ( ); @@ -1356,6 +1357,20 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 4D751E9D215D765D003CD3CE /* Package external libraries */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Package external libraries"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "./script/repackage-dylibs.rb"; + }; 6A28265317C69CB400C6A948 /* OpenSSL-iOS */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/script/repackage-dylibs.rb b/script/repackage-dylibs.rb new file mode 100755 index 000000000..c594a76ac --- /dev/null +++ b/script/repackage-dylibs.rb @@ -0,0 +1,110 @@ +#!/usr/bin/ruby + +# This script looks up an executable's list of shared libraries, copies +# non-standard ones (ie. anything not under /usr or /System/) into the target's +# bundle and updates the executable install_name to point to the "packaged" +# version. + +# Usage: +# Add the script as a Run Script build phase in the target using Xcode. + +# FIXMEs: +# - only handles dylibs +# - only tested against a framework target +# - doesn't care about codesigning + + +require 'fileutils' +require 'ostruct' + +def err(msg) + puts "error: " + msg + exit 1 +end + +def warn(msg) + puts "warning: " + msg +end + +def note(msg) + puts "note: " + msg +end + +envvars = %w( + TARGET_BUILD_DIR + EXECUTABLE_PATH + FRAMEWORKS_FOLDER_PATH +) + +envvars.each do |var| + raise "Must be run in an Xcode Run Phase" unless ENV[var] + Kernel.const_set var, ENV[var] +end + +TARGET_EXECUTABLE_PATH = File.join(TARGET_BUILD_DIR, EXECUTABLE_PATH) +TARGET_FRAMEWORKS_PATH = File.join(TARGET_BUILD_DIR, FRAMEWORKS_FOLDER_PATH) + +def extract_link_dependencies + deps = `otool -L #{TARGET_EXECUTABLE_PATH}` + + lines = deps.split("\n").map(&:strip) + lines.shift + lines.shift + lines.map do |dep| + path, compat, current = /^(.*) \(compatibility version (.*), current version (.*)\)$/.match(dep)[1..3] + err "Failed to parse #{dep}" if path.nil? + + dep = OpenStruct.new + dep.install_name = path + dep.current_version = current + dep.compat_version = compat + dep.type = File.extname(path) + dep.name = File.basename(path) + dep.is_packaged = (dep.install_name =~ /^@rpath/) + dep.path = if dep.install_name =~ /^@rpath/ + File.join(TARGET_FRAMEWORKS_PATH, dep.name) + else + dep.install_name + end + + dep + end +end + +def repackage_dependency(dep) + return if dep.is_packaged or dep.path =~ /^(\/usr\/lib|\/System\/Library)/ + + note "Packaging #{dep.name}…" + + FileUtils.mkdir(TARGET_FRAMEWORKS_PATH) unless Dir.exist?(TARGET_FRAMEWORKS_PATH) + + case dep.type + when ".dylib" + if File.exist?(File.join(TARGET_FRAMEWORKS_PATH, dep.name)) + warn "#{dep.path} already in Frameworks directory, removing" + FileUtils.rm File.join(TARGET_FRAMEWORKS_PATH, dep.name) + end + + note "Copying #{dep[:path]} to TARGET_FRAMEWORKS_PATH" + FileUtils.cp dep[:path], TARGET_FRAMEWORKS_PATH + + out = `install_name_tool -change #{dep.path} "@rpath/#{dep.name}" #{TARGET_EXECUTABLE_PATH}` + if $? != 0 + err "install_name_tool failed with error #{$?}:\n#{out}" + end + + dep.path = File.join(TARGET_FRAMEWORKS_PATH, dep.name) + dep.install_name = "@rpath/#{dep.name}" + dep.is_packaged = true + + else + warn "Unhandled type #{dep.type} for #{dep.path}, ignoring" + end +end + +extract_link_dependencies.each do |dep| + repackage_dependency dep +end + +note "Packaging done" +exit 0 From 7966189f48e4c4598ae14d29854d6b6d8d6d550e Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Tue, 20 Nov 2018 21:33:54 +0100 Subject: [PATCH 2/3] Also fixup dependencies names & ids --- script/repackage-dylibs.rb | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/script/repackage-dylibs.rb b/script/repackage-dylibs.rb index c594a76ac..6f2a34719 100755 --- a/script/repackage-dylibs.rb +++ b/script/repackage-dylibs.rb @@ -44,23 +44,25 @@ def note(msg) TARGET_EXECUTABLE_PATH = File.join(TARGET_BUILD_DIR, EXECUTABLE_PATH) TARGET_FRAMEWORKS_PATH = File.join(TARGET_BUILD_DIR, FRAMEWORKS_FOLDER_PATH) -def extract_link_dependencies - deps = `otool -L #{TARGET_EXECUTABLE_PATH}` +def extract_link_dependencies(executable) + deps = `otool -L #{executable}` lines = deps.split("\n").map(&:strip) lines.shift - lines.shift + # lines.shift lines.map do |dep| path, compat, current = /^(.*) \(compatibility version (.*), current version (.*)\)$/.match(dep)[1..3] err "Failed to parse #{dep}" if path.nil? dep = OpenStruct.new + dep.is_self = (File.basename(path) == File.basename(executable)) + dep.executable = executable dep.install_name = path dep.current_version = current dep.compat_version = compat dep.type = File.extname(path) dep.name = File.basename(path) - dep.is_packaged = (dep.install_name =~ /^@rpath/) + dep.is_packaged = !!(dep.install_name =~ /^@rpath/) dep.path = if dep.install_name =~ /^@rpath/ File.join(TARGET_FRAMEWORKS_PATH, dep.name) else @@ -72,7 +74,7 @@ def extract_link_dependencies end def repackage_dependency(dep) - return if dep.is_packaged or dep.path =~ /^(\/usr\/lib|\/System\/Library)/ + return if dep.is_self or dep.is_packaged or dep.path =~ /^(\/usr\/lib|\/System\/Library)/ note "Packaging #{dep.name}…" @@ -88,7 +90,7 @@ def repackage_dependency(dep) note "Copying #{dep[:path]} to TARGET_FRAMEWORKS_PATH" FileUtils.cp dep[:path], TARGET_FRAMEWORKS_PATH - out = `install_name_tool -change #{dep.path} "@rpath/#{dep.name}" #{TARGET_EXECUTABLE_PATH}` + out = `install_name_tool -change #{dep.path} "@rpath/#{dep.name}" #{dep.executable}` if $? != 0 err "install_name_tool failed with error #{$?}:\n#{out}" end @@ -96,14 +98,24 @@ def repackage_dependency(dep) dep.path = File.join(TARGET_FRAMEWORKS_PATH, dep.name) dep.install_name = "@rpath/#{dep.name}" dep.is_packaged = true - else warn "Unhandled type #{dep.type} for #{dep.path}, ignoring" end end -extract_link_dependencies.each do |dep| +def fix_install_id(dep) + note "Fixing #{dep.name} install_name id…" + out = `install_name_tool -id @rpath/#{dep.name} #{dep.executable}` + if $? != 0 + err "install_name_tool failed with error #{$?}:\n#{out}" + end +end + +deps = extract_link_dependencies(TARGET_EXECUTABLE_PATH) +while (dep = deps.shift) do repackage_dependency dep + fix_install_id dep if dep.is_self and dep.executable != TARGET_EXECUTABLE_PATH + deps += extract_link_dependencies(dep[:path]) if dep.is_packaged and not dep.is_self end note "Packaging done" From da034f6e7e82f2c4fae17488b08dd4154b60c974 Mon Sep 17 00:00:00 2001 From: Etienne Samson Date: Mon, 28 Jan 2019 17:56:41 +0100 Subject: [PATCH 3/3] Fix permissions while packaging binaries --- script/repackage-dylibs.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script/repackage-dylibs.rb b/script/repackage-dylibs.rb index 6f2a34719..f6b35a300 100755 --- a/script/repackage-dylibs.rb +++ b/script/repackage-dylibs.rb @@ -79,23 +79,25 @@ def repackage_dependency(dep) note "Packaging #{dep.name}…" FileUtils.mkdir(TARGET_FRAMEWORKS_PATH) unless Dir.exist?(TARGET_FRAMEWORKS_PATH) + packaged_path = File.join(TARGET_FRAMEWORKS_PATH, dep.name) case dep.type when ".dylib" - if File.exist?(File.join(TARGET_FRAMEWORKS_PATH, dep.name)) + if File.exist? packaged_path warn "#{dep.path} already in Frameworks directory, removing" - FileUtils.rm File.join(TARGET_FRAMEWORKS_PATH, dep.name) + FileUtils.rm packaged_path end note "Copying #{dep[:path]} to TARGET_FRAMEWORKS_PATH" FileUtils.cp dep[:path], TARGET_FRAMEWORKS_PATH + FileUtils.chmod "u=rw", packaged_path out = `install_name_tool -change #{dep.path} "@rpath/#{dep.name}" #{dep.executable}` if $? != 0 err "install_name_tool failed with error #{$?}:\n#{out}" end - dep.path = File.join(TARGET_FRAMEWORKS_PATH, dep.name) + dep.path = packaged_path dep.install_name = "@rpath/#{dep.name}" dep.is_packaged = true else