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 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