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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b2d19f2b78a5ddedb9c26708a8ead42cfe3a76a21dfeeb244f6f9e49f835ef2
4
- data.tar.gz: 5334b7a2cea325d36a5e15ca30cd14aafd47c49a40f99fb0ff5e2ff45094edd9
3
+ metadata.gz: 77324350e0b69d68739d71e4e2bd510b2c46e82076d82c066eb1af2ab7019ff0
4
+ data.tar.gz: dd8c817ba142772da87f18c46fffe50f32e2bad22d4bb8a9e3101650b38818a0
5
5
  SHA512:
6
- metadata.gz: 519151d17e2cb3aa6c4140ee9bf4c05b55b4deb1582508271d99e6d9fb26a4f8c450b60b9f9b3c9ed590d8fd7c7b352ec1624e5a294b3a31700bf74a626c4205
7
- data.tar.gz: fc7b29d598afe23fc1d3047dcc2446bb160986da8b8297b81ef9b8103e18635104f0ea043898c4c7ff3d27b03e1a38c673ece942c8b084bbc1292d0ab9eccd40
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
- var section = section
80
- if section.flags != S_ZEROFILL {
81
- section.offset += UInt32(offset)
82
- section.reloff += section.reloff > 0 ? UInt32(offset) : 0
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: &section, count: MemoryLayout<section_64>.stride)
92
102
  })
93
-
103
+
94
104
  return datas.merge()
95
105
  }
96
-
97
- private static func updateVersionMin(_ data: Data, _ offset: UInt32, minos: UInt32, sdk: UInt32) -> Data {
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
- static func processBinary(atPath path: String, minos: UInt32 = 13, sdk: UInt32 = 13) {
122
- guard CommandLine.arguments.count > 1 else {
123
- fatalError("Please add a path to command!")
124
- }
125
- let (headerData, loadCommandsData, programData) = readBinary(atPath: path)
126
-
127
- // `offset` is kind of a magic number here, since we know that's the only meaningful change to binary size
128
- // having a dynamic `offset` requires two passes over the load commands and is left as an exercise to the reader
129
- let offset = UInt32(abs(MemoryLayout<build_version_command>.stride - MemoryLayout<version_min_command>.stride))
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
- .map { (lc) -> Data in
133
- switch Int32(lc.loadCommand) {
134
- case LC_SEGMENT_64:
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
- file = MachO::MachOFile.new(extracted_path)
39
- sdk_version = file[:LC_VERSION_MIN_IPHONEOS].first.version_string
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
- Pathname.new(__FILE__).dirname.join('../arm2sim.swift')
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
- `cd \"#{extracted_path_dir}\" ; ar x \"#{extracted_path}\"`
55
- Dir[extracted_path_dir.join('*.o')].each do |object_file|
56
- file = MachO::MachOFile.new(object_file)
57
- sdk_version = file[:LC_VERSION_MIN_IPHONEOS].first.version_string.to_i
58
- `xcrun swift \"#{arm2sim_path}\" \"#{object_file}\" \"#{sdk_version}\" \"#{sdk_version}\"`
59
- $stderr.printf '.'
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module XCFrameworkConverter
4
- VERSION = '0.3.3'
4
+ VERSION = '0.6.0'
5
5
  end
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.3.3
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-01-30 00:00:00.000000000 Z
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/arm2sim.swift
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