xcframework_converter 0.1.3 → 0.3.1

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: ed8ad6ac521bd6f26cae4e1ed130e7faf4c861c9367d10a591f65ace5e0d0180
4
- data.tar.gz: 3a214bc46c53d8413a4082783d24d9fc4ebecdcf9bc695edd3a1908561eb952c
3
+ metadata.gz: 2df06815d507bcbbd8d8ea83882fe3c55463dcc9532063d4566f8d196e378ada
4
+ data.tar.gz: f49c4301fb89b90b1e30e77cbee37e175ea1d432a1c104f62131d689857d0a58
5
5
  SHA512:
6
- metadata.gz: 23717de292d647163e52c8c2055878b45c7e79dceb01e000bcb29830edb9c26c44e928c1dea8df1ec33f1f224fc7f9bbeb79ca876cd4a14d80cafe0eddfad87e
7
- data.tar.gz: d00b1bf35a52d2d42bba03f84ce88f35e19edab370465acb08d20bb886ced4e534bf5385e0f036aa3ee2ff5b341f612c080873a200142badc79b517fe2493cf4
6
+ metadata.gz: bdcc77aeb67f7eb84dbb8c469186e27a730616f9fa2c51a9e6a7e9ca91f800b4563858c18fd3c93a91298b911241aad577f9e0ba8086ba198f19932368868701
7
+ data.tar.gz: ea641b22490269b45530b719ca48435092055ed5515b8cce8fe7b9ddc866567a418bdb16d6b463b3482319c664cabd49253f963864a16bfa705fa5dd6ab4faf6
data/bin/xcfpatch ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'xcframework_converter'
6
+
7
+ if ARGV.empty?
8
+ warn 'Usage: xcfpatch <path/to/XCFramework.xcframework>'
9
+ exit 1
10
+ end
11
+
12
+ XCFrameworkConverter.patch_xcframework(Pathname.new(ARGV[0]).realpath)
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods'
4
+ require 'cocoapods/xcode/xcframework'
5
+ require 'fileutils'
6
+ require 'xcodeproj'
7
+
8
+ # rubocop:disable Metrics/AbcSize
9
+
10
+ module XCFrameworkConverter
11
+ # Patches a binary (static or dynamic), turning an arm64-device into an arm64-simualtor.
12
+ # For more info:
13
+ # static libs: https://bogo.wtf/arm64-to-sim.html
14
+ # dylibs: https://bogo.wtf/arm64-to-sim-dylibs.html
15
+ module ArmPatcher
16
+ class << self
17
+ def patch_arm_binary(slice)
18
+ require 'macho'
19
+
20
+ case slice.build_type.linkage
21
+ when :dynamic
22
+ patch_arm_binary_dynamic(slice)
23
+ when :static
24
+ patch_arm_binary_static(slice)
25
+ end
26
+
27
+ slice.path.glob('**/arm64*.swiftinterface').each do |interface_file|
28
+ `sed -i '' -E 's/target arm64-apple-ios([0-9.]+) /target arm64-apple-ios\\1-simulator /g' "#{interface_file}"`
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def patch_arm_binary_dynamic(slice)
35
+ extracted_path = slice.path.join('arm64.dylib')
36
+ `xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
37
+
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}\"`
41
+ `xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
42
+ extracted_path.rmtree
43
+ end
44
+
45
+ def arm2sim_path
46
+ Pathname.new(__FILE__).dirname.join('../arm2sim.swift')
47
+ end
48
+
49
+ def patch_arm_binary_static(slice)
50
+ extracted_path = slice.path.join('arm64.a')
51
+ `xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
52
+ extracted_path_dir = slice.path.join('arm64-objects')
53
+ 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
+ end
60
+ `cd \"#{extracted_path_dir}\" ; ar crv \"#{extracted_path}\" *.o`
61
+
62
+ `xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
63
+ extracted_path_dir.rmtree
64
+ extracted_path.rmtree
65
+ end
66
+
67
+ public
68
+
69
+ def cleanup_unused_archs(slice)
70
+ supported_archs = slice.supported_archs
71
+ unsupported_archs = `xcrun lipo \"#{slice.binary_path}\" -archs`.split - supported_archs
72
+ unsupported_archs.each do |arch|
73
+ `xcrun lipo \"#{slice.binary_path}\" -remove \"#{arch}\" -output \"#{slice.binary_path}\"`
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'arm_patcher'
4
+ require_relative 'xcframework_ext'
5
+
6
+ require 'cocoapods'
7
+ require 'cocoapods/xcode/xcframework'
8
+ require 'fileutils'
9
+ require 'xcodeproj'
10
+
11
+ # rubocop:disable Metrics/AbcSize
12
+
13
+ # Converts a framework (static or dynamic) to an XCFramework, adding an arm64 simulator patch.
14
+ # For more info:
15
+ # static libs: https://bogo.wtf/arm64-to-sim.html
16
+ # dylibs: https://bogo.wtf/arm64-to-sim-dylibs.html
17
+ module XCFrameworkConverter
18
+ class << self
19
+ def plist_template_path
20
+ Pathname.new(__FILE__).dirname.join('../xcframework_template.plist')
21
+ end
22
+
23
+ def convert_framework_to_xcframework(path)
24
+ plist = Xcodeproj::Plist.read_from_path(plist_template_path)
25
+ xcframework_path = Pathname.new(path).sub_ext('.xcframework')
26
+ xcframework_path.mkdir
27
+ plist['AvailableLibraries'].each do |slice|
28
+ slice_path = xcframework_path.join(slice['LibraryIdentifier'])
29
+ slice_path.mkdir
30
+ slice['LibraryPath'] = File.basename(path)
31
+ FileUtils.cp_r(path, slice_path)
32
+ end
33
+ Xcodeproj::Plist.write_to_path(plist, xcframework_path.join('Info.plist'))
34
+ FileUtils.rm_rf(path)
35
+ final_framework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
36
+ final_framework.slices.each do |slice|
37
+ ArmPatcher.patch_arm_binary(slice) if slice.platform == :ios && slice.platform_variant == :simulator
38
+ ArmPatcher.cleanup_unused_archs(slice)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'arm_patcher'
4
+ require_relative 'xcframework_ext'
5
+
6
+ require 'cocoapods'
7
+ require 'cocoapods/xcode/xcframework'
8
+ require 'fileutils'
9
+ require 'xcodeproj'
10
+
11
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
12
+
13
+ # Converts a framework (static or dynamic) to an XCFramework, adding an arm64 simulator patch.
14
+ # For more info:
15
+ # static libs: https://bogo.wtf/arm64-to-sim.html
16
+ # dylibs: https://bogo.wtf/arm64-to-sim-dylibs.html
17
+ module XCFrameworkConverter
18
+ class << self
19
+ def patch_xcframework(xcframework_path)
20
+ xcframework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
21
+
22
+ return nil if xcframework.slices.any? do |slice|
23
+ slice.platform == :ios &&
24
+ slice.platform_variant == :simulator &&
25
+ slice.supported_archs.include?('arm64')
26
+ end
27
+
28
+ original_arm_slice_identifier = xcframework.slices.find do |slice|
29
+ slice.platform == :ios && slice.supported_archs.include?('arm64')
30
+ end.identifier
31
+
32
+ patched_arm_slice_identifier = 'ios-arm64-simulator'
33
+
34
+ warn "Will patch #{xcframework_path}: #{original_arm_slice_identifier} -> #{patched_arm_slice_identifier}"
35
+
36
+ plist = xcframework.plist
37
+ slice_plist_to_add = plist['AvailableLibraries'].find { |s| s['LibraryIdentifier'] == original_arm_slice_identifier }.dup
38
+ slice_plist_to_add['LibraryIdentifier'] = patched_arm_slice_identifier
39
+ slice_plist_to_add['SupportedArchitectures'] = ['arm64']
40
+ slice_plist_to_add['SupportedPlatformVariant'] = 'simulator'
41
+ plist['AvailableLibraries'] << slice_plist_to_add
42
+
43
+ FileUtils.rm_rf(xcframework_path.join(patched_arm_slice_identifier))
44
+ FileUtils.cp_r(xcframework_path.join(original_arm_slice_identifier), xcframework_path.join(patched_arm_slice_identifier))
45
+
46
+ Xcodeproj::Plist.write_to_path(plist, xcframework_path.join('Info.plist'))
47
+
48
+ xcframework = Pod::Xcode::XCFramework.open_xcframework(xcframework_path)
49
+
50
+ slice = xcframework.slices.find { |s| s.identifier == patched_arm_slice_identifier }
51
+
52
+ ArmPatcher.patch_arm_binary(slice)
53
+ ArmPatcher.cleanup_unused_archs(slice)
54
+
55
+ xcframework_path
56
+ end
57
+ end
58
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module XCFrameworkConverter
4
- VERSION = '0.1.3'
4
+ VERSION = '0.3.1'
5
5
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cocoapods'
4
+ require 'cocoapods/xcode/xcframework'
5
+
6
+ module Pod
7
+ module Xcode
8
+ # open XCFramework
9
+ class XCFramework
10
+ def self.open_xcframework(xcframework_path)
11
+ if instance_method(:initialize).arity == 2
12
+ new(File.basename(xcframework_path), xcframework_path.realpath)
13
+ else
14
+ new(xcframework_path.realpath)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'xcframework_converter/creation'
4
+ require_relative 'xcframework_converter/patching'
3
5
  require_relative 'xcframework_converter/version'
4
6
 
5
7
  require 'cocoapods'
@@ -7,7 +9,7 @@ require 'cocoapods/xcode/xcframework'
7
9
  require 'fileutils'
8
10
  require 'xcodeproj'
9
11
 
10
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/ModuleLength, Metrics/PerceivedComplexity
12
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
11
13
 
12
14
  # Converts a framework (static or dynamic) to XCFrameworks, adding an arm64 simulator patch.
13
15
  # For more info:
@@ -19,6 +21,16 @@ module XCFrameworkConverter
19
21
  installer.analysis_result.specifications.each do |spec|
20
22
  next if spec.source && spec.local?
21
23
 
24
+ pod_path = installer.sandbox.pod_dir(Pod::Specification.root_name(spec.name))
25
+
26
+ xcframeworks_to_patch = spec.available_platforms.map do |platform|
27
+ consumer = Pod::Specification::Consumer.new(spec, platform)
28
+ consumer.vendored_frameworks.select { |f| File.extname(f) == '.xcframework' }
29
+ .map { |f| pod_path.join(f) }
30
+ end.flatten.uniq
31
+
32
+ patch_xcframeworks_if_needed(xcframeworks_to_patch)
33
+
22
34
  frameworks_to_convert = spec.available_platforms.map do |platform|
23
35
  consumer = Pod::Specification::Consumer.new(spec, platform)
24
36
  before_rename = consumer.vendored_frameworks.select { |f| File.extname(f) == '.framework' }
@@ -27,18 +39,12 @@ module XCFrameworkConverter
27
39
  after_rename = before_rename.map { |f| Pathname.new(f).sub_ext('.xcframework').to_s }
28
40
  proxy = Pod::Specification::DSL::PlatformProxy.new(spec, platform.symbolic_name)
29
41
  proxy.vendored_frameworks = consumer.vendored_frameworks - before_rename + after_rename
30
- before_rename
42
+ before_rename.map { |f| pod_path.join(f) }
31
43
  end.flatten.uniq
32
44
 
33
- next if frameworks_to_convert.empty?
34
-
35
- pod_path = installer.sandbox.pod_dir(Pod::Specification.root_name(spec.name))
36
- convert_xcframeworks_if_present(frameworks_to_convert.map { |f| pod_path.join(f) })
45
+ convert_xcframeworks_if_present(frameworks_to_convert)
37
46
 
38
- # some pods put these as a way to NOT support arm64 sim
39
- # may stop working if a pod decides to put these in a platform proxy
40
- spec.attributes_hash['pod_target_xcconfig']&.delete('EXCLUDED_ARCHS[sdk=iphonesimulator*]')
41
- spec.attributes_hash['user_target_xcconfig']&.delete('EXCLUDED_ARCHS[sdk=iphonesimulator*]')
47
+ remove_troublesome_xcconfig_items(spec)
42
48
  end
43
49
  end
44
50
 
@@ -48,91 +54,19 @@ module XCFrameworkConverter
48
54
  end
49
55
  end
50
56
 
51
- def plist_template_path
52
- Pathname.new(__FILE__).dirname.join('xcframework_template.plist')
53
- end
54
-
55
- def convert_framework_to_xcframework(path)
56
- plist = Xcodeproj::Plist.read_from_path(plist_template_path)
57
- xcframework_path = Pathname.new(path).sub_ext('.xcframework')
58
- xcframework_path.mkdir
59
- plist['AvailableLibraries'].each do |slice|
60
- slice_path = xcframework_path.join(slice['LibraryIdentifier'])
61
- slice_path.mkdir
62
- slice['LibraryPath'] = File.basename(path)
63
- FileUtils.cp_r(path, slice_path)
64
- end
65
- Xcodeproj::Plist.write_to_path(plist, xcframework_path.join('Info.plist'))
66
- FileUtils.rm_rf(path)
67
- final_framework = open_xcframework(xcframework_path)
68
- final_framework.slices.each do |slice|
69
- patch_arm_binary(slice) if slice.platform == :ios && slice.platform_variant == :simulator
70
- cleanup_unused_archs(slice)
71
- end
72
- end
73
-
74
- def open_xcframework(xcframework_path)
75
- if Pod::Xcode::XCFramework.instance_method(:initialize).arity == 2
76
- Pod::Xcode::XCFramework.new(File.basename(xcframework_path), xcframework_path.realpath)
77
- else
78
- Pod::Xcode::XCFramework.new(xcframework_path.realpath)
79
- end
80
- end
81
-
82
- def patch_arm_binary(slice)
83
- require 'macho'
84
-
85
- case slice.build_type.linkage
86
- when :dynamic
87
- patch_arm_binary_dynamic(slice)
88
- when :static
89
- patch_arm_binary_static(slice)
90
- end
91
-
92
- slice.path.glob('**/arm64*.swiftinterface').each do |interface_file|
93
- `sed -i '' -E 's/target arm64-apple-ios([0-9.]+) /target arm64-apple-ios\\1-simulator /g' "#{interface_file}"`
57
+ def patch_xcframeworks_if_needed(xcframeworks)
58
+ xcframeworks.each do |path|
59
+ patch_xcframework(path) if Dir.exist?(path)
94
60
  end
95
61
  end
96
62
 
97
- def patch_arm_binary_dynamic(slice)
98
- extracted_path = slice.path.join('arm64.dylib')
99
- `xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
100
-
101
- file = MachO::MachOFile.new(extracted_path)
102
- sdk_version = file[:LC_VERSION_MIN_IPHONEOS].first.version_string
103
- `xcrun vtool -arch arm64 -set-build-version 7 #{sdk_version} #{sdk_version} -replace -output \"#{extracted_path}\" \"#{extracted_path}\"`
104
- `xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
105
- extracted_path.rmtree
106
- end
107
-
108
- def arm2sim_path
109
- Pathname.new(__FILE__).dirname.join('arm2sim.swift')
110
- end
111
-
112
- def patch_arm_binary_static(slice)
113
- extracted_path = slice.path.join('arm64.a')
114
- `xcrun lipo \"#{slice.binary_path}\" -thin arm64 -output \"#{extracted_path}\"`
115
- extracted_path_dir = slice.path.join('arm64-objects')
116
- extracted_path_dir.mkdir
117
- `cd \"#{extracted_path_dir}\" ; ar x \"#{extracted_path}\"`
118
- Dir[extracted_path_dir.join('*.o')].each do |object_file|
119
- file = MachO::MachOFile.new(object_file)
120
- sdk_version = file[:LC_VERSION_MIN_IPHONEOS].first.version_string.to_i
121
- `xcrun swift \"#{arm2sim_path}\" \"#{object_file}\" \"#{sdk_version}\" \"#{sdk_version}\"`
122
- end
123
- `cd \"#{extracted_path_dir}\" ; ar crv \"#{extracted_path}\" *.o`
124
-
125
- `xcrun lipo \"#{slice.binary_path}\" -replace arm64 \"#{extracted_path}\" -output \"#{slice.binary_path}\"`
126
- extracted_path_dir.rmtree
127
- extracted_path.rmtree
128
- end
129
-
130
- def cleanup_unused_archs(slice)
131
- supported_archs = slice.supported_archs
132
- unsupported_archs = `xcrun lipo \"#{slice.binary_path}\" -archs`.split - supported_archs
133
- unsupported_archs.each do |arch|
134
- `xcrun lipo \"#{slice.binary_path}\" -remove \"#{arch}\" -output \"#{slice.binary_path}\"`
135
- end
63
+ def remove_troublesome_xcconfig_items(spec)
64
+ # some pods put these as a way to NOT support arm64 sim
65
+ # may stop working if a pod decides to put these in a platform proxy
66
+ spec.attributes_hash['pod_target_xcconfig']&.delete('EXCLUDED_ARCHS[sdk=iphonesimulator*]')
67
+ spec.attributes_hash['user_target_xcconfig']&.delete('EXCLUDED_ARCHS[sdk=iphonesimulator*]')
68
+ spec.attributes_hash['pod_target_xcconfig']&.delete('VALID_ARCHS[sdk=iphonesimulator*]')
69
+ spec.attributes_hash['user_target_xcconfig']&.delete('VALID_ARCHS[sdk=iphonesimulator*]')
136
70
  end
137
71
  end
138
72
  end
metadata CHANGED
@@ -1,69 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xcframework_converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Makarov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-20 00:00:00.000000000 Z
11
+ date: 2022-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocoapods
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.10.0
20
17
  - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: '1'
19
+ version: '1.10'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.10.0
30
24
  - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: '1'
26
+ version: '1.10'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: xcodeproj
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 1.20.0
40
31
  - - "~>"
41
32
  - !ruby/object:Gem::Version
42
- version: '1'
33
+ version: '1.20'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
46
37
  requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 1.20.0
50
38
  - - "~>"
51
39
  - !ruby/object:Gem::Version
52
- version: '1'
40
+ version: '1.20'
53
41
  description: Convert an ancient .framework (dynamic or static) to an .xcframework.
54
42
  Add an arm64 Simulator patch.
55
43
  email:
56
44
  - igormaka@gmail.com
57
45
  executables:
58
46
  - xcfconvert
47
+ - xcfpatch
59
48
  extensions: []
60
49
  extra_rdoc_files: []
61
50
  files:
62
51
  - README.md
63
52
  - bin/xcfconvert
53
+ - bin/xcfpatch
64
54
  - lib/arm2sim.swift
65
55
  - lib/xcframework_converter.rb
56
+ - lib/xcframework_converter/arm_patcher.rb
57
+ - lib/xcframework_converter/creation.rb
58
+ - lib/xcframework_converter/patching.rb
66
59
  - lib/xcframework_converter/version.rb
60
+ - lib/xcframework_converter/xcframework_ext.rb
67
61
  - lib/xcframework_template.plist
68
62
  homepage: https://github.com/igor-makarov/XCFrameworkConverter
69
63
  licenses: