xcframework_converter 0.3.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/arm64-to-sim/Package.swift +28 -0
- data/lib/{arm2sim.swift → arm64-to-sim/Sources/Arm64ToSimLib/Transmogrifier.swift} +131 -61
- data/lib/arm64-to-sim/Sources/Tests/Arm64ToSimTestCase.swift +73 -0
- data/lib/arm64-to-sim/Sources/arm64-to-sim/main.swift +16 -0
- data/lib/xcframework_converter/arm_patcher.rb +65 -14
- data/lib/xcframework_converter/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77324350e0b69d68739d71e4e2bd510b2c46e82076d82c066eb1af2ab7019ff0
|
4
|
+
data.tar.gz: dd8c817ba142772da87f18c46fffe50f32e2bad22d4bb8a9e3101650b38818a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7a7693803108e192822db1f4ab0de16bed32d4dc134dd3a69b1b6e54eab4e185e4d466d0626ecd46adc4c0bee677de8a9b8ca28d99485a0f5975147133cd94b
|
7
|
+
data.tar.gz: 59a9cac8d54309ddd9efea3901425ad5499cccac741cd6d35cb4a493794a8cf35d083ad63ba6e0528065cd717ee1521c7a8794cd445d2093813f65336af9209e
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// swift-tools-version:5.3
|
2
|
+
import PackageDescription
|
3
|
+
|
4
|
+
let package = Package(
|
5
|
+
name: "arm64-to-sim",
|
6
|
+
platforms: [
|
7
|
+
.macOS(.v11)
|
8
|
+
],
|
9
|
+
products: [
|
10
|
+
.executable(name: "arm64-to-sim", targets: ["arm64-to-sim"])
|
11
|
+
],
|
12
|
+
dependencies: [
|
13
|
+
],
|
14
|
+
targets: [
|
15
|
+
.target(
|
16
|
+
name: "arm64-to-sim",
|
17
|
+
dependencies: [ "Arm64ToSimLib" ]),
|
18
|
+
.target(
|
19
|
+
name: "Arm64ToSimLib",
|
20
|
+
dependencies: []),
|
21
|
+
.testTarget(
|
22
|
+
name: "Tests",
|
23
|
+
dependencies: ["Arm64ToSimLib"],
|
24
|
+
resources: [
|
25
|
+
.copy("TestResources"),
|
26
|
+
])
|
27
|
+
]
|
28
|
+
)
|
@@ -7,12 +7,12 @@ extension Data {
|
|
7
7
|
let lc: load_command = withUnsafeBytes { $0.load(as: load_command.self) }
|
8
8
|
return lc.cmd
|
9
9
|
}
|
10
|
-
|
10
|
+
|
11
11
|
var commandSize: Int {
|
12
12
|
let lc: load_command = withUnsafeBytes { $0.load(as: load_command.self) }
|
13
13
|
return Int(lc.cmdsize)
|
14
14
|
}
|
15
|
-
|
15
|
+
|
16
16
|
func asStruct<T>(fromByteOffset offset: Int = 0) -> T {
|
17
17
|
return withUnsafeBytes { $0.load(fromByteOffset: offset, as: T.self) }
|
18
18
|
}
|
@@ -35,134 +35,204 @@ extension FileHandle {
|
|
35
35
|
}
|
36
36
|
}
|
37
37
|
|
38
|
-
enum Transmogrifier {
|
39
|
-
private static func readBinary(atPath path: String) -> (Data, [Data], Data) {
|
38
|
+
public enum Transmogrifier {
|
39
|
+
private static func readBinary(atPath path: String, isDynamic: Bool = false) -> (Data, [Data], Data) {
|
40
40
|
guard let handle = FileHandle(forReadingAtPath: path) else {
|
41
41
|
fatalError("Cannot open a handle for the file at \(path). Aborting.")
|
42
42
|
}
|
43
|
-
|
43
|
+
|
44
44
|
// chop up the file into a relevant number of segments
|
45
45
|
let headerData = try! handle.read(upToCount: MemoryLayout<mach_header_64>.stride)!
|
46
|
-
|
46
|
+
|
47
47
|
let header: mach_header_64 = headerData.asStruct()
|
48
48
|
if header.magic != MH_MAGIC_64 || header.cputype != CPU_TYPE_ARM64 {
|
49
49
|
fatalError("The file is not a correct arm64 binary. Try thinning (via lipo) or unarchiving (via ar) first.")
|
50
50
|
}
|
51
|
-
|
51
|
+
|
52
52
|
let loadCommandsData: [Data] = (0..<header.ncmds).map { _ in
|
53
53
|
let loadCommandPeekData = try! handle.peek(upToCount: MemoryLayout<load_command>.stride)
|
54
54
|
return try! handle.read(upToCount: Int(loadCommandPeekData!.commandSize))!
|
55
55
|
}
|
56
|
-
|
56
|
+
|
57
|
+
if isDynamic {
|
58
|
+
let bytesToDiscard = abs(MemoryLayout<build_version_command>.stride - MemoryLayout<version_min_command>.stride)
|
59
|
+
_ = handle.readData(ofLength: bytesToDiscard)
|
60
|
+
}
|
61
|
+
|
57
62
|
let programData = try! handle.readToEnd()!
|
58
|
-
|
63
|
+
|
59
64
|
try! handle.close()
|
60
|
-
|
65
|
+
|
61
66
|
return (headerData, loadCommandsData, programData)
|
62
67
|
}
|
63
|
-
|
68
|
+
|
64
69
|
private static func updateSegment64(_ data: Data, _ offset: UInt32) -> Data {
|
65
70
|
// decode both the segment_command_64 and the subsequent section_64s
|
66
71
|
var segment: segment_command_64 = data.asStruct()
|
67
|
-
|
72
|
+
|
68
73
|
let sections: [section_64] = (0..<Int(segment.nsects)).map { index in
|
69
74
|
let offset = MemoryLayout<segment_command_64>.stride + index * MemoryLayout<section_64>.stride
|
70
75
|
return data.asStruct(fromByteOffset: offset)
|
71
76
|
}
|
72
|
-
|
77
|
+
|
73
78
|
// shift segment information by the offset
|
74
79
|
segment.fileoff += UInt64(offset)
|
75
80
|
segment.filesize += UInt64(offset)
|
76
81
|
segment.vmsize += UInt64(offset)
|
77
|
-
|
82
|
+
|
78
83
|
let offsetSections = sections.map { section -> section_64 in
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
84
|
+
let sectionType = section.flags & UInt32(SECTION_TYPE)
|
85
|
+
switch Int32(sectionType) {
|
86
|
+
case S_ZEROFILL, S_GB_ZEROFILL, S_THREAD_LOCAL_ZEROFILL:
|
87
|
+
return section
|
88
|
+
case _: break
|
83
89
|
}
|
90
|
+
|
91
|
+
var section = section
|
92
|
+
section.offset += UInt32(offset)
|
93
|
+
section.reloff += section.reloff > 0 ? UInt32(offset) : 0
|
84
94
|
return section
|
85
95
|
}
|
86
|
-
|
96
|
+
|
87
97
|
var datas = [Data]()
|
88
98
|
datas.append(Data(bytes: &segment, count: MemoryLayout<segment_command_64>.stride))
|
89
99
|
datas.append(contentsOf: offsetSections.map { section in
|
90
100
|
var section = section
|
91
101
|
return Data(bytes: §ion, count: MemoryLayout<section_64>.stride)
|
92
102
|
})
|
93
|
-
|
103
|
+
|
94
104
|
return datas.merge()
|
95
105
|
}
|
96
|
-
|
97
|
-
private static func
|
106
|
+
|
107
|
+
private static func createLcBuildVersion(minos: UInt32, sdk: UInt32) -> Data {
|
98
108
|
var command = build_version_command(cmd: UInt32(LC_BUILD_VERSION),
|
99
109
|
cmdsize: UInt32(MemoryLayout<build_version_command>.stride),
|
100
110
|
platform: UInt32(PLATFORM_IOSSIMULATOR),
|
101
111
|
minos: minos << 16 | 0 << 8 | 0,
|
102
112
|
sdk: sdk << 16 | 0 << 8 | 0,
|
103
113
|
ntools: 0)
|
104
|
-
|
114
|
+
|
105
115
|
return Data(bytes: &command, count: MemoryLayout<build_version_command>.stride)
|
106
116
|
}
|
107
|
-
|
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
|
+
|
108
127
|
private static func updateDataInCode(_ data: Data, _ offset: UInt32) -> Data {
|
109
128
|
var command: linkedit_data_command = data.asStruct()
|
110
129
|
command.dataoff += offset
|
111
130
|
return Data(bytes: &command, count: data.commandSize)
|
112
131
|
}
|
113
|
-
|
132
|
+
|
114
133
|
private static func updateSymTab(_ data: Data, _ offset: UInt32) -> Data {
|
115
134
|
var command: symtab_command = data.asStruct()
|
116
135
|
command.stroff += offset
|
117
136
|
command.symoff += offset
|
118
137
|
return Data(bytes: &command, count: data.commandSize)
|
119
138
|
}
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
139
|
+
|
140
|
+
private static func computeLoadCommandsEditor(_ loadCommandsData: [Data], isDynamic: Bool) -> ((Data, UInt32, UInt32) -> Data) {
|
141
|
+
|
142
|
+
if isDynamic {
|
143
|
+
return updateDylibFile
|
144
|
+
}
|
145
|
+
|
146
|
+
var contains_LC_VERSION_MIN_IPHONEOS = false
|
147
|
+
var contains_LC_BUILD_VERSION = false
|
148
|
+
for lc in loadCommandsData {
|
149
|
+
let loadCommand = UInt32(lc.loadCommand)
|
150
|
+
if loadCommand == LC_VERSION_MIN_IPHONEOS {
|
151
|
+
contains_LC_VERSION_MIN_IPHONEOS = true
|
152
|
+
} else if loadCommand == LC_BUILD_VERSION {
|
153
|
+
contains_LC_BUILD_VERSION = true
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
if contains_LC_VERSION_MIN_IPHONEOS == contains_LC_BUILD_VERSION {
|
158
|
+
if contains_LC_BUILD_VERSION == true {
|
159
|
+
fatalError("Bad Mach-O Object file: Both LC_VERSION_MIN_IPHONEOS and LC_BUILD_VERSION are present.\nEither one of them should be present")
|
160
|
+
} else {
|
161
|
+
fatalError("Bad Mach-O Object file: does not contain LC_VERSION_MIN_IPHONEOS or LC_BUILD_VERSION.\nEither one of them should be present")
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
if contains_LC_VERSION_MIN_IPHONEOS {
|
166
|
+
return updatePreiOS12ObjectFile
|
167
|
+
} else {
|
168
|
+
return updatePostiOS12ObjectFile
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
|
173
|
+
static func updatePostiOS12ObjectFile(lc: Data, minos: UInt32, sdk: UInt32) -> Data {
|
174
|
+
let cmd = Int32(bitPattern: lc.loadCommand)
|
175
|
+
switch cmd {
|
176
|
+
case LC_BUILD_VERSION:
|
177
|
+
return updateLcBuildVersion(lc, minos: minos, sdk: sdk)
|
178
|
+
default:
|
179
|
+
return lc
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
static func updatePreiOS12ObjectFile(lc: Data, minos: UInt32, sdk: UInt32) -> Data {
|
184
|
+
// `offset` is kind of a magic number here, since we know that's the only meaningful change to binary size
|
185
|
+
// having a dynamic `offset` requires two passes over the load commands and is left as an exercise to the reader
|
186
|
+
let offset = UInt32(abs(MemoryLayout<build_version_command>.stride - MemoryLayout<version_min_command>.stride))
|
187
|
+
let cmd = Int32(bitPattern: lc.loadCommand)
|
188
|
+
switch cmd {
|
189
|
+
case LC_SEGMENT_64:
|
190
|
+
return updateSegment64(lc, offset)
|
191
|
+
case LC_VERSION_MIN_IPHONEOS:
|
192
|
+
return createLcBuildVersion(minos: minos, sdk: sdk)
|
193
|
+
case LC_DATA_IN_CODE, LC_LINKER_OPTIMIZATION_HINT:
|
194
|
+
return updateDataInCode(lc, offset)
|
195
|
+
case LC_SYMTAB:
|
196
|
+
return updateSymTab(lc, offset)
|
197
|
+
case LC_BUILD_VERSION:
|
198
|
+
fatalError("pre-12 object file shold not contain LC_BUILD_VERSION!")
|
199
|
+
default:
|
200
|
+
return lc
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
static func updateDylibFile(lc: Data, minos: UInt32, sdk: UInt32) -> Data {
|
205
|
+
let cmd = Int32(bitPattern: lc.loadCommand)
|
206
|
+
guard cmd != LC_BUILD_VERSION else {
|
207
|
+
fatalError("This arm64 binary already contains an LC_BUILD_VERSION load command!")
|
208
|
+
}
|
209
|
+
if cmd == LC_VERSION_MIN_IPHONEOS {
|
210
|
+
return createLcBuildVersion(minos: minos, sdk: sdk)
|
211
|
+
}
|
212
|
+
return lc
|
213
|
+
}
|
214
|
+
|
215
|
+
|
216
|
+
public static func processBinary(atPath path: String, minos: UInt32 = 13, sdk: UInt32 = 13, isDynamic: Bool = false) {
|
217
|
+
let (headerData, loadCommandsData, programData) = readBinary(atPath: path, isDynamic: isDynamic)
|
218
|
+
|
219
|
+
let editor = computeLoadCommandsEditor(loadCommandsData, isDynamic: isDynamic)
|
220
|
+
|
131
221
|
let editedCommandsData = loadCommandsData
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
return updateSegment64(lc, offset)
|
136
|
-
case LC_VERSION_MIN_IPHONEOS:
|
137
|
-
return updateVersionMin(lc, offset, minos: minos, sdk: sdk)
|
138
|
-
case LC_DATA_IN_CODE, LC_LINKER_OPTIMIZATION_HINT:
|
139
|
-
return updateDataInCode(lc, offset)
|
140
|
-
case LC_SYMTAB:
|
141
|
-
return updateSymTab(lc, offset)
|
142
|
-
case LC_BUILD_VERSION:
|
143
|
-
fatalError("This arm64 binary already contains an LC_BUILD_VERSION load command!")
|
144
|
-
default:
|
145
|
-
return lc
|
146
|
-
}
|
147
|
-
}
|
148
|
-
.merge()
|
149
|
-
|
222
|
+
.map { return editor($0, minos, sdk) }
|
223
|
+
.merge()
|
224
|
+
|
150
225
|
var header: mach_header_64 = headerData.asStruct()
|
151
226
|
header.sizeofcmds = UInt32(editedCommandsData.count)
|
152
|
-
|
227
|
+
|
153
228
|
// reassemble the binary
|
154
229
|
let reworkedData = [
|
155
230
|
Data(bytes: &header, count: MemoryLayout<mach_header_64>.stride),
|
156
231
|
editedCommandsData,
|
157
232
|
programData
|
158
233
|
].merge()
|
159
|
-
|
234
|
+
|
160
235
|
// save back to disk
|
161
236
|
try! reworkedData.write(to: URL(fileURLWithPath: path))
|
162
237
|
}
|
163
238
|
}
|
164
|
-
|
165
|
-
let binaryPath = CommandLine.arguments[1]
|
166
|
-
let minos = UInt32(CommandLine.arguments[2]) ?? 13
|
167
|
-
let sdk = UInt32(CommandLine.arguments[3]) ?? 13
|
168
|
-
Transmogrifier.processBinary(atPath: binaryPath, minos: minos, sdk: sdk)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import Foundation
|
2
|
+
import XCTest
|
3
|
+
import Arm64ToSimLib
|
4
|
+
|
5
|
+
class Arm64ToSimTestCase: XCTestCase {
|
6
|
+
|
7
|
+
|
8
|
+
var tempDir: URL!
|
9
|
+
override func setUp() {
|
10
|
+
self.tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID.init().uuidString)
|
11
|
+
try! FileManager.default.createDirectory(at: self.tempDir, withIntermediateDirectories: false, attributes: nil)
|
12
|
+
copyFixtures()
|
13
|
+
}
|
14
|
+
|
15
|
+
override func tearDown() {
|
16
|
+
try! FileManager.default.removeItem(at: self.tempDir)
|
17
|
+
}
|
18
|
+
|
19
|
+
private func copyFixtures() {
|
20
|
+
let testResourcesPath = Bundle.module.resourcePath!.appending("/TestResources")
|
21
|
+
if let files = try? FileManager.default.contentsOfDirectory(atPath: testResourcesPath){
|
22
|
+
for file in files {
|
23
|
+
var isDir : ObjCBool = false
|
24
|
+
let fileURL = URL(fileURLWithPath: testResourcesPath).appendingPathComponent(file)
|
25
|
+
if FileManager.default.fileExists(atPath: fileURL.path, isDirectory: &isDir) {
|
26
|
+
if !isDir.boolValue {
|
27
|
+
try! FileManager.default.copyItem(at: fileURL, to: tempDir.appendingPathComponent(fileURL.lastPathComponent.replacingOccurrences(of: ".fixture", with: "")))
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
@discardableResult func runCommand(args: [String]) -> (String, Int32) {
|
35
|
+
let task = Process()
|
36
|
+
task.executableURL = URL(fileURLWithPath: args[0])
|
37
|
+
task.arguments = Array(args.dropFirst())
|
38
|
+
task.currentDirectoryURL = tempDir
|
39
|
+
let pipe = Pipe()
|
40
|
+
task.standardOutput = pipe
|
41
|
+
task.standardError = pipe
|
42
|
+
task.launch()
|
43
|
+
task.waitUntilExit()
|
44
|
+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
45
|
+
let output = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)
|
46
|
+
return (output!, task.terminationStatus)
|
47
|
+
}
|
48
|
+
|
49
|
+
private func testConvert(deviceTarget: String, simulatorTarget:String, file: StaticString = #file, line: UInt = #line) {
|
50
|
+
let (sysroot, _) = runCommand(args: ["/usr/bin/xcrun", "--show-sdk-path", "--sdk", "iphonesimulator"])
|
51
|
+
runCommand(args: ["/usr/bin/clang", "-isysroot", sysroot, "-target", simulatorTarget, "-c", "main.c", "-o", "main.arm64.ios.simulator.o"])
|
52
|
+
runCommand(args: ["/usr/bin/clang", "-isysroot", sysroot, "-target", deviceTarget, "-c", "return2.c", "-o", "return2.ios.device.o"])
|
53
|
+
let (loadCommandsOutput, _) = runCommand(args: ["/usr/bin/otool", "-l", "return2.ios.device.o" ])
|
54
|
+
print("LOAD_COMMANDS:")
|
55
|
+
for lc in loadCommandsOutput.split(separator: "\n").filter({$0.contains("cmd")}) {
|
56
|
+
print(lc)
|
57
|
+
}
|
58
|
+
let (_, link_status_failing) = runCommand(args: ["/usr/bin/clang", "-isysroot", sysroot, "-target", deviceTarget, "main.arm64.ios.simulator.o", "return2.ios.device.o"])
|
59
|
+
XCTAssert(link_status_failing != 0)
|
60
|
+
Transmogrifier.processBinary(atPath: tempDir.appendingPathComponent("return2.ios.device.o").path, minos: 13, sdk: 13, isDynamic: false)
|
61
|
+
let (_, link_status_success) = runCommand(args: ["/usr/bin/clang", "-isysroot", sysroot, "-target", "arm64-apple-ios-simulator", "main.arm64.ios.simulator.o", "return2.ios.device.o"])
|
62
|
+
XCTAssert(link_status_success == 0)
|
63
|
+
}
|
64
|
+
|
65
|
+
func testConvertPreiOS12FileFormatToSim() {
|
66
|
+
testConvert(deviceTarget: "arm64-apple-ios11", simulatorTarget: "arm64-apple-ios12-simulator")
|
67
|
+
}
|
68
|
+
|
69
|
+
func testConvertNewObjectFileFormatToSim() {
|
70
|
+
testConvert(deviceTarget: "arm64-apple-ios12", simulatorTarget: "arm64-apple-ios12-simulator")
|
71
|
+
}
|
72
|
+
|
73
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import Foundation
|
2
|
+
import Arm64ToSimLib
|
3
|
+
|
4
|
+
guard CommandLine.arguments.count > 1 else {
|
5
|
+
fatalError("Please add a path to command!")
|
6
|
+
}
|
7
|
+
|
8
|
+
let binaryPath = CommandLine.arguments[1]
|
9
|
+
let minos = (CommandLine.arguments.count > 2 ? UInt32(CommandLine.arguments[2]) : nil) ?? 12
|
10
|
+
let sdk = (CommandLine.arguments.count > 3 ? UInt32(CommandLine.arguments[3]) : nil) ?? 13
|
11
|
+
let isDynamic = (CommandLine.arguments.count > 4 ? Bool(CommandLine.arguments[4]) : nil) ?? false
|
12
|
+
if isDynamic {
|
13
|
+
print("[arm64-to-sim] notice: running in dynamic framework mode")
|
14
|
+
}
|
15
|
+
|
16
|
+
Transmogrifier.processBinary(atPath: binaryPath, minos: minos, sdk: sdk, isDynamic: isDynamic)
|
@@ -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,15 +37,24 @@ 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
|
44
45
|
|
46
|
+
def gem_path(fragment)
|
47
|
+
Pathname.new(__FILE__).dirname.join('../..').join(fragment)
|
48
|
+
end
|
49
|
+
|
45
50
|
def arm2sim_path
|
46
|
-
|
51
|
+
@arm2sim_path ||= begin
|
52
|
+
warn 'Pre-building `arm64-to-sim` with SwiftPM'
|
53
|
+
Dir.chdir gem_path('lib/arm64-to-sim') do
|
54
|
+
system 'xcrun swift build -c release --arch arm64 --arch x86_64'
|
55
|
+
end
|
56
|
+
gem_path('lib/arm64-to-sim/.build/apple/Products/Release/arm64-to-sim')
|
57
|
+
end
|
47
58
|
end
|
48
59
|
|
49
60
|
def patch_arm_binary_static(slice)
|
@@ -51,21 +62,61 @@ module XCFrameworkConverter
|
|
51
62
|
`xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
|
52
63
|
extracted_path_dir = slice.path.join('arm64-objects')
|
53
64
|
extracted_path_dir.mkdir
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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(' ')}`
|
60
91
|
end
|
61
|
-
$stderr.puts
|
62
|
-
`cd \"#{extracted_path_dir}\" ; ar crv \"#{extracted_path}\" *.o`
|
63
|
-
|
64
92
|
`xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
|
65
93
|
extracted_path_dir.rmtree
|
66
94
|
extracted_path.rmtree
|
67
95
|
end
|
68
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
|
+
|
69
120
|
public
|
70
121
|
|
71
122
|
def cleanup_unused_archs(slice)
|
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.6.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-
|
11
|
+
date: 2022-05-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cocoapods
|
@@ -51,7 +51,10 @@ files:
|
|
51
51
|
- README.md
|
52
52
|
- bin/xcfconvert
|
53
53
|
- bin/xcfpatch
|
54
|
-
- lib/
|
54
|
+
- lib/arm64-to-sim/Package.swift
|
55
|
+
- lib/arm64-to-sim/Sources/Arm64ToSimLib/Transmogrifier.swift
|
56
|
+
- lib/arm64-to-sim/Sources/Tests/Arm64ToSimTestCase.swift
|
57
|
+
- lib/arm64-to-sim/Sources/arm64-to-sim/main.swift
|
55
58
|
- lib/xcframework_converter.rb
|
56
59
|
- lib/xcframework_converter/arm_patcher.rb
|
57
60
|
- lib/xcframework_converter/creation.rb
|