xcframework_converter 0.3.3 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|