xcframework_converter 0.4.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/xcfconvert +4 -2
- data/lib/arm64-to-sim/Sources/Arm64ToSimLib/Transmogrifier.swift +14 -10
- data/lib/xcframework_converter/arm_patcher.rb +54 -13
- data/lib/xcframework_converter/creation.rb +6 -3
- data/lib/xcframework_converter/patching.rb +18 -3
- data/lib/xcframework_converter/version.rb +1 -1
- data/lib/xcframework_converter.rb +21 -11
- data/lib/xcframework_template.plist +3 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a1169fa0a3c860b9ddffd50b22592d95ba42d5f32a1f97dbc7507827491ac47
|
4
|
+
data.tar.gz: 05b3b160406d9b1db0db39cc0c999e4c5370d08be44e5dbd5f069089751ab890
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4356364e00c34af6f9a817d9a3bea2411f46ab0fee145586bbb48d9f1ffa23335a1728970535ea2e8e94559eae15cb3de4abecce8c983fa1b9b653235878a50e
|
7
|
+
data.tar.gz: a29311d0abee698408d037bbca8ebba0a9603e2eb9c4851c4b74c2581688a324fe529f26a374c04ea29405a45174931eff113362e99edf460b953dfb66364f8a
|
data/bin/xcfconvert
CHANGED
@@ -5,8 +5,10 @@ require 'bundler/setup'
|
|
5
5
|
require 'xcframework_converter'
|
6
6
|
|
7
7
|
if ARGV.empty?
|
8
|
-
warn 'Usage: xcfconvert <path/to/Framework.framework>'
|
8
|
+
warn 'Usage: xcfconvert <path/to/Framework.framework> [ios|tvos|watchos]'
|
9
9
|
exit 1
|
10
10
|
end
|
11
11
|
|
12
|
-
|
12
|
+
path = Pathname.new(ARGV.shift).realpath
|
13
|
+
platform = ARGV.shift&.to_sym || :ios
|
14
|
+
XCFrameworkConverter.convert_framework_to_xcframework(path, platform)
|
@@ -104,7 +104,7 @@ public enum Transmogrifier {
|
|
104
104
|
return datas.merge()
|
105
105
|
}
|
106
106
|
|
107
|
-
private static func
|
107
|
+
private static func createLcBuildVersion(minos: UInt32, sdk: UInt32) -> Data {
|
108
108
|
var command = build_version_command(cmd: UInt32(LC_BUILD_VERSION),
|
109
109
|
cmdsize: UInt32(MemoryLayout<build_version_command>.stride),
|
110
110
|
platform: UInt32(PLATFORM_IOSSIMULATOR),
|
@@ -115,6 +115,15 @@ public enum Transmogrifier {
|
|
115
115
|
return Data(bytes: &command, count: MemoryLayout<build_version_command>.stride)
|
116
116
|
}
|
117
117
|
|
118
|
+
private static func updateLcBuildVersion(_ data: Data, minos: UInt32, sdk: UInt32) -> Data {
|
119
|
+
var command: build_version_command = data.asStruct()
|
120
|
+
command.platform = UInt32(PLATFORM_IOSSIMULATOR)
|
121
|
+
command.minos = minos << 16 | 0 << 8 | 0
|
122
|
+
command.sdk = sdk << 16 | 0 << 8 | 0
|
123
|
+
|
124
|
+
return Data(bytes: &command, count: data.count)
|
125
|
+
}
|
126
|
+
|
118
127
|
private static func updateDataInCode(_ data: Data, _ offset: UInt32) -> Data {
|
119
128
|
var command: linkedit_data_command = data.asStruct()
|
120
129
|
command.dataoff += offset
|
@@ -154,8 +163,6 @@ public enum Transmogrifier {
|
|
154
163
|
}
|
155
164
|
|
156
165
|
if contains_LC_VERSION_MIN_IPHONEOS {
|
157
|
-
// `offset` is kind of a magic number here, since we know that's the only meaningful change to binary size
|
158
|
-
// having a dynamic `offset` requires two passes over the load commands and is left as an exercise to the reader
|
159
166
|
return updatePreiOS12ObjectFile
|
160
167
|
} else {
|
161
168
|
return updatePostiOS12ObjectFile
|
@@ -167,7 +174,7 @@ public enum Transmogrifier {
|
|
167
174
|
let cmd = Int32(bitPattern: lc.loadCommand)
|
168
175
|
switch cmd {
|
169
176
|
case LC_BUILD_VERSION:
|
170
|
-
return
|
177
|
+
return updateLcBuildVersion(lc, minos: minos, sdk: sdk)
|
171
178
|
default:
|
172
179
|
return lc
|
173
180
|
}
|
@@ -182,28 +189,25 @@ public enum Transmogrifier {
|
|
182
189
|
case LC_SEGMENT_64:
|
183
190
|
return updateSegment64(lc, offset)
|
184
191
|
case LC_VERSION_MIN_IPHONEOS:
|
185
|
-
return
|
192
|
+
return createLcBuildVersion(minos: minos, sdk: sdk)
|
186
193
|
case LC_DATA_IN_CODE, LC_LINKER_OPTIMIZATION_HINT:
|
187
194
|
return updateDataInCode(lc, offset)
|
188
195
|
case LC_SYMTAB:
|
189
196
|
return updateSymTab(lc, offset)
|
190
197
|
case LC_BUILD_VERSION:
|
191
|
-
|
198
|
+
fatalError("pre-12 object file shold not contain LC_BUILD_VERSION!")
|
192
199
|
default:
|
193
200
|
return lc
|
194
201
|
}
|
195
202
|
}
|
196
203
|
|
197
204
|
static func updateDylibFile(lc: Data, minos: UInt32, sdk: UInt32) -> Data {
|
198
|
-
// `offset` is kind of a magic number here, since we know that's the only meaningful change to binary size
|
199
|
-
// having a dynamic `offset` requires two passes over the load commands and is left as an exercise to the reader
|
200
|
-
let offset = UInt32(abs(MemoryLayout<build_version_command>.stride - MemoryLayout<version_min_command>.stride))
|
201
205
|
let cmd = Int32(bitPattern: lc.loadCommand)
|
202
206
|
guard cmd != LC_BUILD_VERSION else {
|
203
207
|
fatalError("This arm64 binary already contains an LC_BUILD_VERSION load command!")
|
204
208
|
}
|
205
209
|
if cmd == LC_VERSION_MIN_IPHONEOS {
|
206
|
-
return
|
210
|
+
return createLcBuildVersion(minos: minos, sdk: sdk)
|
207
211
|
}
|
208
212
|
return lc
|
209
213
|
}
|
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'cocoapods'
|
4
4
|
require 'cocoapods/xcode/xcframework'
|
5
|
+
require 'digest/md5'
|
5
6
|
require 'fileutils'
|
7
|
+
require 'shellwords'
|
6
8
|
require 'xcodeproj'
|
7
9
|
|
8
|
-
# rubocop:disable Metrics/AbcSize
|
10
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
9
11
|
|
10
12
|
module XCFrameworkConverter
|
11
13
|
# Patches a binary (static or dynamic), turning an arm64-device into an arm64-simualtor.
|
@@ -35,9 +37,8 @@ module XCFrameworkConverter
|
|
35
37
|
extracted_path = slice.path.join('arm64.dylib')
|
36
38
|
`xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
|
37
39
|
|
38
|
-
|
39
|
-
sdk_version
|
40
|
-
`xcrun vtool -arch arm64 -set-build-version 7 #{sdk_version} #{sdk_version} -replace -output \"#{extracted_path}\" \"#{extracted_path}\"`
|
40
|
+
minos_version, sdk_version = version_strings(extracted_path).map(&:to_i)
|
41
|
+
`xcrun vtool -arch arm64 -set-build-version 7 #{minos_version} #{sdk_version} -replace -output \"#{extracted_path}\" \"#{extracted_path}\"`
|
41
42
|
`xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
|
42
43
|
extracted_path.rmtree
|
43
44
|
end
|
@@ -61,21 +62,61 @@ module XCFrameworkConverter
|
|
61
62
|
`xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
|
62
63
|
extracted_path_dir = slice.path.join('arm64-objects')
|
63
64
|
extracted_path_dir.mkdir
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
65
|
+
if macho_file_type(extracted_path) == :object
|
66
|
+
patch_object_file(extracted_path)
|
67
|
+
else
|
68
|
+
object_files = `ar t \"#{extracted_path}\"`.split("\n").map(&:chomp).sort
|
69
|
+
.select { |o| o.end_with?('.o') }
|
70
|
+
.group_by(&:itself).transform_values(&:count)
|
71
|
+
processed_files = []
|
72
|
+
index = 0
|
73
|
+
while object_files.any?
|
74
|
+
object_files.keys.each do |object_file|
|
75
|
+
file_shard = Digest::MD5.hexdigest(object_file).to_s[0..2]
|
76
|
+
file_dir = extracted_path_dir.join("#{index}-#{file_shard}")
|
77
|
+
file_path = file_dir.join(object_file)
|
78
|
+
file_dir.mkdir unless file_dir.exist?
|
79
|
+
`ar p \"#{extracted_path}\" \"#{object_file}\" > \"#{file_path}\"`
|
80
|
+
patch_object_file(file_path)
|
81
|
+
$stderr.printf '.'
|
82
|
+
processed_files << file_path
|
83
|
+
end
|
84
|
+
`ar d \"#{extracted_path}\" #{object_files.keys.map(&:shellescape).join(' ')}`
|
85
|
+
$stderr.printf '#'
|
86
|
+
object_files.reject! { |_, count| count <= index + 1 }
|
87
|
+
index += 1
|
88
|
+
end
|
89
|
+
$stderr.puts
|
90
|
+
`cd \"#{extracted_path_dir}\" ; ar cqv \"#{extracted_path}\" #{processed_files.map(&:shellescape).join(' ')}`
|
70
91
|
end
|
71
|
-
$stderr.puts
|
72
|
-
`cd \"#{extracted_path_dir}\" ; ar crv \"#{extracted_path}\" *.o`
|
73
|
-
|
74
92
|
`xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
|
75
93
|
extracted_path_dir.rmtree
|
76
94
|
extracted_path.rmtree
|
77
95
|
end
|
78
96
|
|
97
|
+
def macho_file_type(file_path)
|
98
|
+
MachO.open(file_path).filetype
|
99
|
+
rescue MachO::MagicError
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def patch_object_file(file_path)
|
104
|
+
minos_version, sdk_version = version_strings(file_path).map(&:to_i)
|
105
|
+
`\"#{arm2sim_path}\" \"#{file_path}\" \"#{minos_version}\" \"#{sdk_version}\"`
|
106
|
+
end
|
107
|
+
|
108
|
+
def version_strings(file_path)
|
109
|
+
macho_file = MachO::MachOFile.new(file_path)
|
110
|
+
if (version_min_command = macho_file.load_commands.find { |c| c.is_a?(MachO::LoadCommands::VersionMinCommand) })
|
111
|
+
return [version_min_command.version_string, version_min_command.sdk_string]
|
112
|
+
end
|
113
|
+
if (build_version_command = macho_file.load_commands.find { |c| c.is_a?(MachO::LoadCommands::BuildVersionCommand) })
|
114
|
+
return [build_version_command.minos_string, build_version_command.sdk_string]
|
115
|
+
end
|
116
|
+
|
117
|
+
raise "Could not find version strings in #{file_path}"
|
118
|
+
end
|
119
|
+
|
79
120
|
public
|
80
121
|
|
81
122
|
def cleanup_unused_archs(slice)
|
@@ -20,21 +20,24 @@ module XCFrameworkConverter
|
|
20
20
|
Pathname.new(__FILE__).dirname.join('../xcframework_template.plist')
|
21
21
|
end
|
22
22
|
|
23
|
-
def convert_framework_to_xcframework(path)
|
23
|
+
def convert_framework_to_xcframework(path, platform)
|
24
24
|
plist = Xcodeproj::Plist.read_from_path(plist_template_path)
|
25
25
|
xcframework_path = Pathname.new(path).sub_ext('.xcframework')
|
26
26
|
xcframework_path.mkdir
|
27
27
|
plist['AvailableLibraries'].each do |slice|
|
28
|
-
|
28
|
+
slice_library_identifier = slice['LibraryIdentifier'].sub('platform', platform.to_s)
|
29
|
+
slice_path = xcframework_path.join(slice_library_identifier)
|
29
30
|
slice_path.mkdir
|
30
31
|
slice['LibraryPath'] = File.basename(path)
|
32
|
+
slice['SupportedPlatform'] = platform.to_s
|
33
|
+
slice['LibraryIdentifier'] = slice_library_identifier
|
31
34
|
FileUtils.cp_r(path, slice_path)
|
32
35
|
end
|
33
36
|
Xcodeproj::Plist.write_to_path(plist, xcframework_path.join('Info.plist'))
|
34
37
|
FileUtils.rm_rf(path)
|
35
38
|
final_framework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
|
36
39
|
final_framework.slices.each do |slice|
|
37
|
-
ArmPatcher.patch_arm_binary(slice) if slice.platform ==
|
40
|
+
ArmPatcher.patch_arm_binary(slice) if slice.platform == platform && slice.platform_variant == :simulator
|
38
41
|
ArmPatcher.cleanup_unused_archs(slice)
|
39
42
|
end
|
40
43
|
end
|
@@ -18,18 +18,33 @@ module XCFrameworkConverter
|
|
18
18
|
class << self
|
19
19
|
def patch_xcframework(xcframework_path)
|
20
20
|
xcframework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
|
21
|
+
slices_to_convert = xcframework.slices.select do |slice|
|
22
|
+
slice.platform != :osx &&
|
23
|
+
slice.platform_variant != :simulator &&
|
24
|
+
slice.supported_archs.include?('arm64')
|
25
|
+
end
|
26
|
+
|
27
|
+
slices_to_convert.each do |slice|
|
28
|
+
patch_xcframework_impl(xcframework_path, slice.platform.name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def patch_xcframework_impl(xcframework_path, platform)
|
35
|
+
xcframework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
|
21
36
|
|
22
37
|
return nil if xcframework.slices.any? do |slice|
|
23
|
-
slice.platform ==
|
38
|
+
slice.platform == platform &&
|
24
39
|
slice.platform_variant == :simulator &&
|
25
40
|
slice.supported_archs.include?('arm64')
|
26
41
|
end
|
27
42
|
|
28
43
|
original_arm_slice_identifier = xcframework.slices.find do |slice|
|
29
|
-
slice.platform ==
|
44
|
+
slice.platform == platform && slice.supported_archs.include?('arm64')
|
30
45
|
end.identifier
|
31
46
|
|
32
|
-
patched_arm_slice_identifier =
|
47
|
+
patched_arm_slice_identifier = "#{platform}-arm64-simulator"
|
33
48
|
|
34
49
|
warn "Will patch #{xcframework_path}: #{original_arm_slice_identifier} -> #{patched_arm_slice_identifier}"
|
35
50
|
|
@@ -39,8 +39,8 @@ module XCFrameworkConverter
|
|
39
39
|
after_rename = before_rename.map { |f| Pathname.new(f).sub_ext('.xcframework').to_s }
|
40
40
|
proxy = Pod::Specification::DSL::PlatformProxy.new(spec, platform.symbolic_name)
|
41
41
|
proxy.vendored_frameworks = consumer.vendored_frameworks - before_rename + after_rename
|
42
|
-
before_rename.map { |f| pod_path.join(f) }
|
43
|
-
end.flatten.uniq
|
42
|
+
before_rename.map { |f| [pod_path.join(f), platform.symbolic_name] }
|
43
|
+
end.flatten(1).uniq
|
44
44
|
|
45
45
|
convert_xcframeworks_if_present(frameworks_to_convert)
|
46
46
|
|
@@ -53,8 +53,8 @@ module XCFrameworkConverter
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def convert_xcframeworks_if_present(frameworks_to_convert)
|
56
|
-
frameworks_to_convert.each do |path|
|
57
|
-
convert_framework_to_xcframework(path) if Dir.exist?(path)
|
56
|
+
frameworks_to_convert.each do |path, platform|
|
57
|
+
convert_framework_to_xcframework(path, platform) if Dir.exist?(path)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
@@ -65,21 +65,31 @@ module XCFrameworkConverter
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def remove_troublesome_xcconfig_items(spec)
|
68
|
+
# some pods put these as a way to NOT support arm64 sim
|
69
|
+
# may stop working if a pod decides to put these in a platform proxy
|
70
|
+
|
68
71
|
xcconfigs = %w[
|
69
72
|
pod_target_xcconfig
|
70
73
|
user_target_xcconfig
|
71
74
|
].map { |key| spec.attributes_hash[key] }.compact
|
72
75
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
platforms = %w[
|
77
|
+
iphonesimulator
|
78
|
+
appletvsimulator
|
79
|
+
watchsimulator
|
80
|
+
]
|
81
|
+
|
82
|
+
(xcconfigs.product platforms).each do |xcconfig, platform|
|
83
|
+
excluded_archs_key = "EXCLUDED_ARCHS[sdk=#{platform}*]"
|
84
|
+
inlcuded_arch_key = "VALID_ARCHS[sdk=#{platform}*]"
|
85
|
+
|
86
|
+
excluded_arm = xcconfig[excluded_archs_key]&.include?('arm64')
|
87
|
+
not_inlcuded_arm = xcconfig[inlcuded_arch_key] && !xcconfig[inlcuded_arch_key].include?('arm64')
|
78
88
|
|
79
89
|
remember_spec_as_patched(spec) if excluded_arm || not_inlcuded_arm
|
80
90
|
|
81
|
-
xcconfig.delete(
|
82
|
-
xcconfig.delete(
|
91
|
+
xcconfig.delete(excluded_archs_key)
|
92
|
+
xcconfig.delete(inlcuded_arch_key)
|
83
93
|
end
|
84
94
|
end
|
85
95
|
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<array>
|
7
7
|
<dict>
|
8
8
|
<key>LibraryIdentifier</key>
|
9
|
-
<string>
|
9
|
+
<string>platform-arm64_x86_64-simulator</string>
|
10
10
|
<key>LibraryPath</key>
|
11
11
|
<string>Placeholder.framework</string>
|
12
12
|
<key>SupportedArchitectures</key>
|
@@ -21,7 +21,7 @@
|
|
21
21
|
</dict>
|
22
22
|
<dict>
|
23
23
|
<key>LibraryIdentifier</key>
|
24
|
-
<string>
|
24
|
+
<string>platform-arm64_armv7</string>
|
25
25
|
<key>LibraryPath</key>
|
26
26
|
<string>Placeholder.framework</string>
|
27
27
|
<key>SupportedArchitectures</key>
|
@@ -30,7 +30,7 @@
|
|
30
30
|
<string>armv7</string>
|
31
31
|
</array>
|
32
32
|
<key>SupportedPlatform</key>
|
33
|
-
<string>
|
33
|
+
<string>platform</string>
|
34
34
|
</dict>
|
35
35
|
</array>
|
36
36
|
<key>CFBundlePackageType</key>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xcframework_converter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Makarov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cocoapods
|