pod-builder-y 2.3.1
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 +7 -0
- data/.gitignore +19 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +13 -0
- data/README.md +385 -0
- data/Rakefile +2 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/exe/pod_builder_y +406 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/pod_builder/analyze.rb +59 -0
- data/lib/pod_builder/analyzer.rb +16 -0
- data/lib/pod_builder/command.rb +14 -0
- data/lib/pod_builder/command/build.rb +228 -0
- data/lib/pod_builder/command/build_all.rb +15 -0
- data/lib/pod_builder/command/clean.rb +75 -0
- data/lib/pod_builder/command/deintegrate.rb +101 -0
- data/lib/pod_builder/command/generate_lldbinit.rb +128 -0
- data/lib/pod_builder/command/generate_podspec.rb +22 -0
- data/lib/pod_builder/command/info.rb +18 -0
- data/lib/pod_builder/command/init.rb +148 -0
- data/lib/pod_builder/command/install_sources.rb +79 -0
- data/lib/pod_builder/command/none.rb +16 -0
- data/lib/pod_builder/command/restore_all.rb +33 -0
- data/lib/pod_builder/command/switch.rb +224 -0
- data/lib/pod_builder/command/sync_podfile.rb +34 -0
- data/lib/pod_builder/command/update.rb +43 -0
- data/lib/pod_builder/configuration.rb +300 -0
- data/lib/pod_builder/core.rb +222 -0
- data/lib/pod_builder/info.rb +90 -0
- data/lib/pod_builder/install.rb +505 -0
- data/lib/pod_builder/licenses.rb +73 -0
- data/lib/pod_builder/podfile.rb +700 -0
- data/lib/pod_builder/podfile/pre_actions_swizzles.rb +84 -0
- data/lib/pod_builder/podfile_cp.rb +99 -0
- data/lib/pod_builder/podfile_item.rb +530 -0
- data/lib/pod_builder/podspec.rb +269 -0
- data/lib/pod_builder/rome/post_install.rb +446 -0
- data/lib/pod_builder/rome/pre_install.rb +6 -0
- data/lib/pod_builder/templates/build_podfile.template +70 -0
- data/lib/pod_builder/templates/build_podspec.template +19 -0
- data/lib/pod_builder/version.rb +4 -0
- data/pod-builder.gemspec +37 -0
- metadata +240 -0
@@ -0,0 +1,269 @@
|
|
1
|
+
# This file contains the logic that generates the .podspec files that are placed
|
2
|
+
# along to the prebuild frameworks under PodBuilder/Prebuilt
|
3
|
+
|
4
|
+
module PodBuilder
|
5
|
+
class Podspec
|
6
|
+
def self.generate(all_buildable_items, analyzer, install_using_frameworks)
|
7
|
+
unless all_buildable_items.count > 0
|
8
|
+
return
|
9
|
+
end
|
10
|
+
|
11
|
+
puts "Generating PodBuilder's local podspec".yellow
|
12
|
+
|
13
|
+
platform = analyzer.instance_variable_get("@result").targets.first.platform
|
14
|
+
generate_podspec_from(all_buildable_items, platform, install_using_frameworks)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.generate_spec_keys_for(item, name, all_buildable_items, install_using_frameworks)
|
20
|
+
podspec = ""
|
21
|
+
valid = false
|
22
|
+
|
23
|
+
slash_count = name.count("/") + 1
|
24
|
+
indentation = " " * slash_count
|
25
|
+
spec_var = "p#{slash_count}"
|
26
|
+
|
27
|
+
if spec_var == "p1" && item.default_subspecs.count > 0
|
28
|
+
podspec += "#{indentation}#{spec_var}.default_subspecs = '#{item.default_subspecs.join("', '")}'\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
if item.name == name
|
32
|
+
if_exists = lambda { |t| File.exist?(PodBuilder::prebuiltpath("#{item.root_name}/#{t}") || "") }
|
33
|
+
|
34
|
+
vendored_frameworks = item.vendored_frameworks
|
35
|
+
if item.default_subspecs.reject { |t| "#{item.root_name}/#{t}" == item.name }.count == 0 && install_using_frameworks
|
36
|
+
vendored_frameworks += ["#{item.module_name}.framework", "#{item.module_name}.xcframework"].select(&if_exists)
|
37
|
+
end
|
38
|
+
|
39
|
+
existing_vendored_frameworks = vendored_frameworks.select(&if_exists)
|
40
|
+
existing_vendored_frameworks_basename = vendored_frameworks.map { |t| "#{item.root_name}/#{File.basename(t)}" }.select(&if_exists)
|
41
|
+
vendored_frameworks = (existing_vendored_frameworks + existing_vendored_frameworks_basename).uniq
|
42
|
+
|
43
|
+
vendored_libraries = item.vendored_libraries
|
44
|
+
existing_vendored_libraries_basename = vendored_libraries.map { |t| "#{item.root_name}/#{File.basename(t)}" }.select(&if_exists)
|
45
|
+
vendored_libraries += existing_vendored_libraries_basename
|
46
|
+
|
47
|
+
if install_using_frameworks
|
48
|
+
existing_vendored_libraries = vendored_libraries.map { |t| "#{item.module_name}/#{t}" }.select(&if_exists)
|
49
|
+
existing_vendored_libraries_basename = vendored_libraries.map { |t| File.basename(t) }.select(&if_exists)
|
50
|
+
vendored_libraries = (existing_vendored_libraries + existing_vendored_libraries_basename).uniq
|
51
|
+
|
52
|
+
# .a are static libraries and should not be included again in the podspec to prevent duplicated symbols (in the app and in the prebuilt framework)
|
53
|
+
vendored_libraries.reject! { |t| t.end_with?(".a") }
|
54
|
+
|
55
|
+
public_headers = []
|
56
|
+
resources = []
|
57
|
+
exclude_files = []
|
58
|
+
vendored_frameworks.each do |vendored_framework|
|
59
|
+
binary_path = Dir.glob(PodBuilder::prebuiltpath("#{item.root_name}/#{vendored_framework}/**/#{File.basename(vendored_framework, ".*")}")).first
|
60
|
+
|
61
|
+
next if binary_path.nil?
|
62
|
+
|
63
|
+
is_static = `file '#{binary_path}'`.include?("current ar archive")
|
64
|
+
if is_static
|
65
|
+
parent_folder = File.expand_path("#{binary_path}/..")
|
66
|
+
rel_path = Pathname.new(parent_folder).relative_path_from(Pathname.new(PodBuilder::prebuiltpath(item.root_name))).to_s
|
67
|
+
|
68
|
+
resources.push("#{rel_path}/*.{nib,bundle,xcasset,strings,png,jpg,tif,tiff,otf,ttf,ttc,plist,json,caf,wav,p12,momd}")
|
69
|
+
exclude_files.push("#{rel_path}/Info.plist")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
public_headers = Dir.glob(PodBuilder::prebuiltpath("#{item.root_name}/#{item.root_name}/Headers/**/*.h"))
|
74
|
+
vendored_libraries += ["#{item.root_name}/lib#{item.root_name}.a"]
|
75
|
+
vendored_libraries = vendored_libraries.select(&if_exists)
|
76
|
+
|
77
|
+
resources = ["#{item.root_name}/*.{nib,bundle,xcasset,strings,png,jpg,tif,tiff,otf,ttf,ttc,plist,json,caf,wav,p12,momd}"]
|
78
|
+
|
79
|
+
exclude_files = ["*.modulemap"]
|
80
|
+
unless item.swift_version.nil?
|
81
|
+
exclude_files += ["Swift Compatibility Header/*", "*.swiftmodule"]
|
82
|
+
end
|
83
|
+
exclude_files.map! { |t| "#{item.root_name}/#{t}" }
|
84
|
+
end
|
85
|
+
|
86
|
+
entries = lambda { |spec_key, spec_value|
|
87
|
+
key = "#{indentation}#{spec_var}.#{spec_key}"
|
88
|
+
joined_values = spec_value.map { |t| "#{t}" }.uniq.sort.join("', '")
|
89
|
+
"#{key} = '#{joined_values}'\n"
|
90
|
+
}
|
91
|
+
|
92
|
+
if vendored_frameworks.count > 0
|
93
|
+
podspec += entries.call("vendored_frameworks", vendored_frameworks)
|
94
|
+
end
|
95
|
+
if vendored_libraries.count > 0
|
96
|
+
podspec += entries.call("vendored_libraries", vendored_libraries)
|
97
|
+
end
|
98
|
+
if item.frameworks.count > 0
|
99
|
+
podspec += entries.call("frameworks", item.frameworks)
|
100
|
+
end
|
101
|
+
if item.libraries.count > 0
|
102
|
+
podspec += entries.call("libraries", item.libraries)
|
103
|
+
end
|
104
|
+
if resources.count > 0
|
105
|
+
podspec += entries.call("resources", resources)
|
106
|
+
end
|
107
|
+
if exclude_files.count > 0
|
108
|
+
podspec += entries.call("exclude_files", exclude_files)
|
109
|
+
end
|
110
|
+
if public_headers.count > 0
|
111
|
+
podspec += "#{indentation}#{spec_var}.public_header_files = '#{item.root_name}/Headers/**/*.h'\n"
|
112
|
+
end
|
113
|
+
if !item.header_dir.nil? && !install_using_frameworks
|
114
|
+
podspec += "#{indentation}#{spec_var}.header_dir = '#{item.header_dir}'\n"
|
115
|
+
podspec += "#{indentation}#{spec_var}.header_mappings_dir = '#{item.root_name}/Headers/#{item.header_dir}'\n"
|
116
|
+
end
|
117
|
+
|
118
|
+
if item.xcconfig.keys.count > 0
|
119
|
+
xcconfig = Hash.new
|
120
|
+
item.xcconfig.each do |k, v|
|
121
|
+
unless v != "$(inherited)"
|
122
|
+
xcconfig[k] = item.xcconfig[k]
|
123
|
+
next
|
124
|
+
end
|
125
|
+
unless k == "OTHER_LDFLAGS"
|
126
|
+
next # For the time being limit to OTHER_LDFLAGS key
|
127
|
+
end
|
128
|
+
|
129
|
+
if podspec_values = item.xcconfig[k]
|
130
|
+
podspec_values_arr = podspec_values.split(" ")
|
131
|
+
podspec_values_arr.push(v)
|
132
|
+
v = podspec_values_arr.join(" ")
|
133
|
+
end
|
134
|
+
|
135
|
+
xcconfig[k] = item.xcconfig[k]
|
136
|
+
end
|
137
|
+
|
138
|
+
if xcconfig.keys.count > 0
|
139
|
+
podspec += "#{indentation}#{spec_var}.xcconfig = #{xcconfig.to_s}\n"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
if !install_using_frameworks && spec_var == "p1" && vendored_libraries.map { |t| File.basename(t) }.include?("lib#{item.root_name}.a" )
|
143
|
+
module_path_files = Dir.glob(PodBuilder.prebuiltpath("#{item.root_name}/**/#{item.root_name}.modulemap"))
|
144
|
+
raise "\n\nToo many module maps found for #{item.root_name}".red if module_path_files.count > 1
|
145
|
+
|
146
|
+
rel_path = Pathname.new(PodBuilder::prebuiltpath).relative_path_from(Pathname.new(PodBuilder::project_path("Pods"))).to_s
|
147
|
+
prebuilt_root_var = "#{item.root_name.upcase.gsub("-", "_")}_PREBUILT_ROOT"
|
148
|
+
|
149
|
+
static_cfg = Hash.new
|
150
|
+
if module_path_file = module_path_files.first
|
151
|
+
module_map_rel = module_path_file.gsub(PodBuilder::prebuiltpath("#{item.root_name}/#{item.root_name}/"), "")
|
152
|
+
static_cfg = { "SWIFT_INCLUDE_PATHS" => "$(inherited) \"$(#{prebuilt_root_var})/#{item.root_name}/#{item.root_name}\"",
|
153
|
+
"OTHER_CFLAGS" => "$(inherited) -fmodule-map-file=\"$(#{prebuilt_root_var})/#{item.root_name}/#{item.root_name}/#{module_map_rel}\"",
|
154
|
+
"OTHER_SWIFT_FLAGS" => "$(inherited) -Xcc -fmodule-map-file=\"$(#{prebuilt_root_var})/#{item.root_name}/#{item.root_name}/#{module_map_rel}\""
|
155
|
+
}
|
156
|
+
end
|
157
|
+
static_cfg[prebuilt_root_var] = "$(PODS_ROOT)/#{rel_path}"
|
158
|
+
|
159
|
+
podspec += "#{indentation}#{spec_var}.xcconfig = #{static_cfg.to_s}\n"
|
160
|
+
# This seems to be a viable workaround to https://github.com/CocoaPods/CocoaPods/issues/9559 and https://github.com/CocoaPods/CocoaPods/issues/8454
|
161
|
+
podspec += "#{indentation}#{spec_var}.user_target_xcconfig = { \"OTHER_LDFLAGS\" => \"$(inherited) -L\\\"$(#{prebuilt_root_var})/#{item.root_name}/#{item.root_name}\\\" -l\\\"#{item.root_name}\\\"\" }\n"
|
162
|
+
end
|
163
|
+
|
164
|
+
deps = item.dependency_names.sort
|
165
|
+
if name == item.root_name
|
166
|
+
deps.reject! { |t| t.split("/").first == item.root_name }
|
167
|
+
end
|
168
|
+
|
169
|
+
deps.reject! { |t| t == item.name }
|
170
|
+
all_buildable_items_name = all_buildable_items.map(&:name)
|
171
|
+
deps.select! { |t| all_buildable_items_name.include?(t) }
|
172
|
+
|
173
|
+
if deps.count > 0
|
174
|
+
if podspec.count("\n") > 1
|
175
|
+
podspec += "\n"
|
176
|
+
end
|
177
|
+
deps.each do |dependency|
|
178
|
+
podspec += "#{indentation}#{spec_var}.dependency '#{dependency}'\n"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
valid = valid || (install_using_frameworks ? vendored_frameworks.count > 0 : vendored_libraries.count > 0)
|
183
|
+
end
|
184
|
+
|
185
|
+
subspec_base = name.split("/").first(slash_count).join("/")
|
186
|
+
subspec_items = all_buildable_items.select { |t| t.name.start_with?("#{subspec_base}/") }
|
187
|
+
|
188
|
+
subspec_names = subspec_items.map { |t| t.name.split("/").drop(slash_count).first }.uniq
|
189
|
+
subspec_names.map! { |t| "#{subspec_base}/#{t}" }
|
190
|
+
|
191
|
+
subspec_names.each do |subspec|
|
192
|
+
subspec_item = all_buildable_items.detect { |t| t.name == subspec } || item
|
193
|
+
|
194
|
+
if podspec.length > 0
|
195
|
+
podspec += "\n"
|
196
|
+
end
|
197
|
+
|
198
|
+
subspec_keys, subspec_valid = generate_spec_keys_for(subspec_item, subspec, all_buildable_items, install_using_frameworks)
|
199
|
+
valid = valid || subspec_valid
|
200
|
+
|
201
|
+
if subspec_keys.length > 0
|
202
|
+
podspec += "#{indentation}#{spec_var}.subspec '#{subspec.split("/").last}' do |p#{slash_count + 1}|\n"
|
203
|
+
podspec += subspec_keys
|
204
|
+
podspec += "#{indentation}end\n"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
return podspec, valid
|
209
|
+
end
|
210
|
+
|
211
|
+
def self.generate_podspec_from(all_buildable_items, platform, install_using_frameworks)
|
212
|
+
prebuilt_podspec_path = all_buildable_items.map(&:prebuilt_podspec_path)
|
213
|
+
prebuilt_podspec_path.each do |path|
|
214
|
+
if File.exist?(path)
|
215
|
+
FileUtils.rm(path)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
all_buildable_items.each do |item|
|
220
|
+
if item.is_prebuilt
|
221
|
+
next
|
222
|
+
end
|
223
|
+
|
224
|
+
if item.name != item.root_name
|
225
|
+
if all_buildable_items.map(&:name).include?(item.root_name)
|
226
|
+
next # will process root spec, skip subspecs
|
227
|
+
end
|
228
|
+
end
|
229
|
+
if File.exist?(item.prebuilt_podspec_path)
|
230
|
+
next # skip if podspec was already generated
|
231
|
+
end
|
232
|
+
|
233
|
+
podspec = "Pod::Spec.new do |p1|\n"
|
234
|
+
|
235
|
+
podspec += " p1.name = '#{item.root_name}'\n"
|
236
|
+
podspec += " p1.version = '#{item.version}'\n"
|
237
|
+
podspec += " p1.summary = '#{item.summary.gsub("'", "\\'")}'\n"
|
238
|
+
podspec += " p1.homepage = '#{item.homepage}'\n"
|
239
|
+
podspec += " p1.author = 'PodBuilder'\n"
|
240
|
+
podspec += " p1.source = { 'git' => '#{item.source['git']}'}\n"
|
241
|
+
podspec += " p1.license = { :type => '#{item.license}' }\n"
|
242
|
+
|
243
|
+
podspec += "\n"
|
244
|
+
podspec += " p1.#{platform.safe_string_name.downcase}.deployment_target = '#{platform.deployment_target.version}'\n"
|
245
|
+
podspec += "\n"
|
246
|
+
|
247
|
+
main_keys, valid = generate_spec_keys_for(item, item.root_name, all_buildable_items, install_using_frameworks)
|
248
|
+
if !valid
|
249
|
+
next
|
250
|
+
end
|
251
|
+
|
252
|
+
podspec += main_keys
|
253
|
+
podspec += "end"
|
254
|
+
|
255
|
+
spec_path = item.prebuilt_podspec_path
|
256
|
+
if File.directory?(File.dirname(spec_path))
|
257
|
+
File.write(spec_path, podspec)
|
258
|
+
else
|
259
|
+
message = "Prebuilt podspec destination not found for #{File.basename(spec_path)}".red
|
260
|
+
if ENV['DEBUGGING']
|
261
|
+
puts message
|
262
|
+
else
|
263
|
+
raise message
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
@@ -0,0 +1,446 @@
|
|
1
|
+
require 'fourflusher'
|
2
|
+
require 'colored'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module PodBuilder
|
6
|
+
class XcodeBuildSettings
|
7
|
+
attr_reader :platform_name
|
8
|
+
attr_reader :build_destination
|
9
|
+
|
10
|
+
def initialize(platform_name)
|
11
|
+
@platform_name = platform_name
|
12
|
+
|
13
|
+
case platform_name
|
14
|
+
when "iphoneos" then @build_destination = "generic/platform=iOS"
|
15
|
+
when "iphonesimulator" then @build_destination = "generic/platform=iOS Simulator"
|
16
|
+
when "catalyst" then @build_destination = "platform=macOS,arch=x86_64,variant=Mac Catalyst"
|
17
|
+
when "macos" then @build_destination = "generic/platform=OS X"
|
18
|
+
when "tvos" then @build_destination = "generic/platform=tvOS"
|
19
|
+
when "tvossimulator" then @build_destination = "generic/platform=tvOS Simulator"
|
20
|
+
when "watchos" then @build_destination = "generic/platform=watchOS"
|
21
|
+
when "watchossimulator" then @build_destination = "generic/platform=watchOS Simulator"
|
22
|
+
else raise "\n\nUnknown platform '#{platform_name}'".red end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.build_for_iosish_platform_framework(sandbox, build_dir, target, device, simulator, configuration, deterministic_build)
|
27
|
+
dsym_device_folder = File.join(build_dir, "dSYM", device)
|
28
|
+
dsym_simulator_folder = File.join(build_dir, "dSYM", simulator)
|
29
|
+
FileUtils.mkdir_p(dsym_device_folder)
|
30
|
+
FileUtils.mkdir_p(dsym_simulator_folder)
|
31
|
+
|
32
|
+
deployment_target = target.platform_deployment_target
|
33
|
+
target_label = target.cocoapods_target_label
|
34
|
+
|
35
|
+
xcodebuild(sandbox, target_label, device, deployment_target, configuration, deterministic_build, [], {})
|
36
|
+
excluded_archs = ["i386"] # Fixes https://github.com/Subito-it/PodBuilder/issues/17
|
37
|
+
excluded_archs += ["arm64"] # Exclude apple silicon slice
|
38
|
+
xcodebuild(sandbox, target_label, simulator, deployment_target, configuration, deterministic_build, excluded_archs, {})
|
39
|
+
|
40
|
+
spec_names = target.specs.map { |spec| [spec.root.name, spec.root.module_name] }.uniq
|
41
|
+
spec_names.each do |root_name, module_name|
|
42
|
+
device_base = "#{build_dir}/#{configuration}-#{device}/#{root_name}"
|
43
|
+
device_lib = "#{device_base}/#{module_name}.framework/#{module_name}"
|
44
|
+
device_dsym = "#{device_base}/#{module_name}.framework.dSYM"
|
45
|
+
device_framework_lib = File.dirname(device_lib)
|
46
|
+
device_swift_header_path = "#{device_framework_lib}/Headers/#{module_name}-Swift.h"
|
47
|
+
|
48
|
+
simulator_base = "#{build_dir}/#{configuration}-#{simulator}/#{root_name}"
|
49
|
+
simulator_lib = "#{simulator_base}/#{module_name}.framework/#{module_name}"
|
50
|
+
simulator_dsym = "#{simulator_base}/#{module_name}.framework.dSYM"
|
51
|
+
simulator_framework_lib = File.dirname(simulator_lib)
|
52
|
+
simulator_swift_header_path = "#{simulator_framework_lib}/Headers/#{module_name}-Swift.h"
|
53
|
+
|
54
|
+
next unless File.file?(device_lib) && File.file?(simulator_lib)
|
55
|
+
|
56
|
+
# Starting with Xcode 12b3 the simulator binary contains an arm64 slice as well which conflict with the one in the device_lib
|
57
|
+
# when creating the fat library. A naive workaround is to remove the arm64 from the simulator_lib however this is wrong because
|
58
|
+
# we might actually need to have 2 separated arm64 slices, one for simulator and one for device each built with different
|
59
|
+
# compile time directives (e.g #if targetEnvironment(simulator))
|
60
|
+
#
|
61
|
+
# For the time being we remove the arm64 slice bacause otherwise the `xcrun lipo -create -output ...` would fail.
|
62
|
+
if `xcrun lipo -info #{simulator_lib}`.include?("arm64")
|
63
|
+
`xcrun lipo -remove arm64 #{simulator_lib} -o #{simulator_lib}`
|
64
|
+
end
|
65
|
+
|
66
|
+
raise "Lipo failed on #{device_lib}" unless system("xcrun lipo -create -output #{device_lib} #{device_lib} #{simulator_lib}")
|
67
|
+
|
68
|
+
merge_header_into(device_swift_header_path, simulator_swift_header_path)
|
69
|
+
|
70
|
+
# Merge device framework into simulator framework (so that e.g swift Module folder is merged)
|
71
|
+
# letting device framework files overwrite simulator ones
|
72
|
+
FileUtils.cp_r(File.join(device_framework_lib, "."), simulator_framework_lib)
|
73
|
+
source_lib = File.dirname(simulator_framework_lib)
|
74
|
+
|
75
|
+
FileUtils.mv(device_dsym, dsym_device_folder) if File.exist?(device_dsym)
|
76
|
+
FileUtils.mv(simulator_dsym, dsym_simulator_folder) if File.exist?(simulator_dsym)
|
77
|
+
|
78
|
+
FileUtils.mv(source_lib, build_dir)
|
79
|
+
|
80
|
+
# Remove frameworks leaving dSYMs
|
81
|
+
FileUtils.rm_rf(device_framework_lib)
|
82
|
+
FileUtils.rm_rf(simulator_framework_lib)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.build_for_iosish_platform_lib(sandbox, build_dir, target, device, simulator, configuration, deterministic_build, prebuilt_root_paths)
|
87
|
+
deployment_target = target.platform_deployment_target
|
88
|
+
target_label = target.cocoapods_target_label
|
89
|
+
|
90
|
+
spec_names = target.specs.map { |spec| [spec.root.name, spec.root.module_name] }.uniq
|
91
|
+
|
92
|
+
xcodebuild(sandbox, target_label, device, deployment_target, configuration, deterministic_build, [], prebuilt_root_paths)
|
93
|
+
excluded_archs = ["arm64"] # Exclude Apple silicon slice
|
94
|
+
xcodebuild(sandbox, target_label, simulator, deployment_target, configuration, deterministic_build, excluded_archs, prebuilt_root_paths)
|
95
|
+
|
96
|
+
spec_names.each do |root_name, module_name|
|
97
|
+
simulator_base = "#{build_dir}/#{configuration}-#{simulator}/#{root_name}"
|
98
|
+
simulator_lib = "#{simulator_base}/lib#{root_name}.a"
|
99
|
+
|
100
|
+
device_base = "#{build_dir}/#{configuration}-#{device}/#{root_name}"
|
101
|
+
device_lib = "#{device_base}/lib#{root_name}.a"
|
102
|
+
|
103
|
+
unless File.file?(device_lib) && File.file?(simulator_lib)
|
104
|
+
next
|
105
|
+
end
|
106
|
+
|
107
|
+
# Starting with Xcode 12b3 the simulator binary contains an arm64 slice as well which conflict with the one in the device_lib
|
108
|
+
# when creating the fat library. A naive workaround is to remove the arm64 from the simulator_lib however this is wrong because
|
109
|
+
# we might actually need to have 2 separated arm64 slices, one for simulator and one for device each built with different
|
110
|
+
# compile time directives (e.g #if targetEnvironment(simulator))
|
111
|
+
#
|
112
|
+
# For the time being we remove the arm64 slice bacause otherwise the `xcrun lipo -create -output ...` would fail.
|
113
|
+
if `xcrun lipo -info #{simulator_lib}`.include?("arm64")
|
114
|
+
`xcrun lipo -remove arm64 #{simulator_lib} -o #{simulator_lib}`
|
115
|
+
end
|
116
|
+
|
117
|
+
raise "Lipo failed on #{device_lib}" unless system("xcrun lipo -create -output #{device_lib} #{device_lib} #{simulator_lib}")
|
118
|
+
|
119
|
+
device_headers = Dir.glob("#{device_base}/**/*.h")
|
120
|
+
simulator_headers = Dir.glob("#{simulator_base}/**/*.h")
|
121
|
+
device_headers.each do |device_path|
|
122
|
+
simulator_path = device_path.gsub(device_base, simulator_base)
|
123
|
+
|
124
|
+
merge_header_into(device_path, simulator_path)
|
125
|
+
end
|
126
|
+
simulator_only_headers = simulator_headers - device_headers.map { |t| t.gsub(device_base, simulator_base) }
|
127
|
+
simulator_only_headers.each do |path|
|
128
|
+
add_simulator_conditional(path)
|
129
|
+
dir_name = File.dirname(path)
|
130
|
+
destination_folder = dir_name.gsub(simulator_base, device_base)
|
131
|
+
FileUtils.mkdir_p(destination_folder)
|
132
|
+
FileUtils.cp(path, destination_folder)
|
133
|
+
end
|
134
|
+
|
135
|
+
swiftmodule_path = "#{simulator_base}/#{root_name}.swiftmodule"
|
136
|
+
if File.directory?(swiftmodule_path)
|
137
|
+
FileUtils.cp_r("#{swiftmodule_path}/.", "#{device_base}/#{root_name}.swiftmodule")
|
138
|
+
end
|
139
|
+
|
140
|
+
if File.exist?("#{device_base}/#{root_name}.swiftmodule")
|
141
|
+
# This is a swift pod with a swiftmodule in the root of the prebuilt folder
|
142
|
+
else
|
143
|
+
# Objective-C pods have the swiftmodule generated under Pods/Headers/Public
|
144
|
+
public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{root_name}"
|
145
|
+
module_public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{module_name}"
|
146
|
+
if public_headers_path.downcase != module_public_headers_path.downcase && File.directory?(public_headers_path) && File.directory?(module_public_headers_path)
|
147
|
+
# For pods with module_name != name we have to move the modulemap files to the root_name one
|
148
|
+
module_public_headers_path = "#{Configuration.build_path}/Pods/Headers/Public/#{module_name}"
|
149
|
+
FileUtils.cp_r("#{module_public_headers_path}/.", public_headers_path, :remove_destination => true)
|
150
|
+
end
|
151
|
+
Dir.glob("#{public_headers_path}/**/*.*").each do |path|
|
152
|
+
destination_folder = "#{device_base}/Headers" + path.gsub(public_headers_path, "")
|
153
|
+
destination_folder = File.dirname(destination_folder)
|
154
|
+
FileUtils.mkdir_p(destination_folder)
|
155
|
+
FileUtils.cp(path, destination_folder)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
destination_path = "#{build_dir}/#{root_name}"
|
160
|
+
if Dir.glob("#{device_base}/**/*.{a,framework,h}").count > 0
|
161
|
+
FileUtils.mv(device_base, destination_path)
|
162
|
+
|
163
|
+
module_maps = Dir.glob("#{destination_path}/**/*.modulemap")
|
164
|
+
module_map_device_base = device_base.gsub(/^\/private/, "") + "/"
|
165
|
+
module_maps.each do |module_map|
|
166
|
+
content = File.read(module_map)
|
167
|
+
content.gsub!(module_map_device_base, "")
|
168
|
+
File.write(module_map, content)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.merge_header_into(device_file, simulator_file)
|
175
|
+
unless File.exist?(device_file) || File.exist?(simulator_file)
|
176
|
+
return
|
177
|
+
end
|
178
|
+
|
179
|
+
device_content = File.file?(device_file) ? File.read(device_file) : ""
|
180
|
+
simulator_content = File.file?(simulator_file) ? File.read(simulator_file) : ""
|
181
|
+
merged_content = %{
|
182
|
+
#if TARGET_OS_SIMULATOR
|
183
|
+
// ->
|
184
|
+
|
185
|
+
#{simulator_content}
|
186
|
+
|
187
|
+
// ->
|
188
|
+
#else
|
189
|
+
// ->
|
190
|
+
|
191
|
+
#{device_content}
|
192
|
+
|
193
|
+
// ->
|
194
|
+
#endif
|
195
|
+
}
|
196
|
+
File.write(device_file, merged_content)
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.add_simulator_conditional(path)
|
200
|
+
file_content = File.read(path)
|
201
|
+
content = %{
|
202
|
+
#if TARGET_OS_SIMULATOR
|
203
|
+
#{file_content}
|
204
|
+
#endif
|
205
|
+
}
|
206
|
+
File.write(path, content)
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.xcodebuild(sandbox, target, sdk='macosx', deployment_target=nil, configuration, deterministic_build, exclude_archs, prebuilt_root_paths)
|
210
|
+
args = %W(-project #{sandbox.project_path.realdirpath} -scheme #{target} -configuration #{configuration} -sdk #{sdk})
|
211
|
+
supported_platforms = { 'iphonesimulator' => 'iOS', 'appletvsimulator' => 'tvOS', 'watchsimulator' => 'watchOS' }
|
212
|
+
if platform = supported_platforms[sdk]
|
213
|
+
args += Fourflusher::SimControl.new.destination(:oldest, platform, deployment_target) unless platform.nil?
|
214
|
+
end
|
215
|
+
|
216
|
+
xcodebuild_version = `xcodebuild -version | head -n1 | awk '{print $2}'`.strip().to_f
|
217
|
+
if exclude_archs.count > 0 && xcodebuild_version >= 12.0
|
218
|
+
args += ["EXCLUDED_ARCHS=#{exclude_archs.join(" ")}"]
|
219
|
+
end
|
220
|
+
prebuilt_root_paths.each do |k, v|
|
221
|
+
args += ["#{k.upcase.gsub("-", "_")}_PREBUILT_ROOT=#{v.gsub(/ /, '\ ')}"]
|
222
|
+
end
|
223
|
+
|
224
|
+
environmental_variables = {}
|
225
|
+
if deterministic_build
|
226
|
+
environmental_variables["ZERO_AR_DATE"] = "1"
|
227
|
+
end
|
228
|
+
|
229
|
+
execute_command 'xcodebuild', args, true, environmental_variables
|
230
|
+
end
|
231
|
+
|
232
|
+
# Copy paste implementation from CocoaPods internals to be able to call poopen3 passing environmental variables
|
233
|
+
def self.execute_command(executable, command, raise_on_failure = true, environmental_variables = {})
|
234
|
+
bin = Pod::Executable.which!(executable)
|
235
|
+
|
236
|
+
command = command.map(&:to_s)
|
237
|
+
full_command = "#{bin} #{command.join(' ')}"
|
238
|
+
|
239
|
+
stdout = Pod::Executable::Indenter.new
|
240
|
+
stderr = Pod::Executable::Indenter.new
|
241
|
+
|
242
|
+
status = popen3(bin, command, stdout, stderr, environmental_variables)
|
243
|
+
stdout = stdout.join
|
244
|
+
stderr = stderr.join
|
245
|
+
output = stdout + stderr
|
246
|
+
unless status.success?
|
247
|
+
if raise_on_failure
|
248
|
+
raise "#{full_command}\n\n#{output}"
|
249
|
+
else
|
250
|
+
UI.message("[!] Failed: #{full_command}".red)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
output
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.popen3(bin, command, stdout, stderr, environmental_variables)
|
258
|
+
require 'open3'
|
259
|
+
Open3.popen3(environmental_variables, bin, *command) do |i, o, e, t|
|
260
|
+
Pod::Executable::reader(o, stdout)
|
261
|
+
Pod::Executable::reader(e, stderr)
|
262
|
+
i.close
|
263
|
+
|
264
|
+
status = t.value
|
265
|
+
|
266
|
+
o.flush
|
267
|
+
e.flush
|
268
|
+
sleep(0.01)
|
269
|
+
|
270
|
+
status
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.enable_debug_information(project_path, configuration)
|
275
|
+
project = Xcodeproj::Project.open(project_path)
|
276
|
+
project.targets.each do |target|
|
277
|
+
config = target.build_configurations.find { |config| config.name.eql? configuration }
|
278
|
+
config.build_settings["DEBUG_INFORMATION_FORMAT"] = "dwarf-with-dsym"
|
279
|
+
config.build_settings["ONLY_ACTIVE_ARCH"] = "NO"
|
280
|
+
end
|
281
|
+
project.save
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
Pod::HooksManager.register('podbuilder-rome', :post_install) do |installer_context, user_options|
|
286
|
+
enable_dsym = user_options.fetch('dsym', true)
|
287
|
+
configuration = user_options.fetch('configuration', 'Debug')
|
288
|
+
uses_frameworks = user_options.fetch('uses_frameworks', true)
|
289
|
+
if user_options["pre_compile"]
|
290
|
+
user_options["pre_compile"].call(installer_context)
|
291
|
+
end
|
292
|
+
build_catalyst = user_options.fetch('build_catalyst', false)
|
293
|
+
build_xcframeworks = user_options.fetch('build_xcframeworks', false)
|
294
|
+
|
295
|
+
prebuilt_root_paths = JSON.parse(user_options["prebuilt_root_paths"].gsub('=>', ':'))
|
296
|
+
|
297
|
+
sandbox_root = Pathname(installer_context.sandbox_root)
|
298
|
+
sandbox = Pod::Sandbox.new(sandbox_root)
|
299
|
+
|
300
|
+
PodBuilder::enable_debug_information(sandbox.project_path, configuration)
|
301
|
+
|
302
|
+
build_dir = sandbox_root.parent + 'build'
|
303
|
+
base_destination = sandbox_root.parent + 'Prebuilt'
|
304
|
+
|
305
|
+
build_dir.rmtree if build_dir.directory?
|
306
|
+
|
307
|
+
targets = installer_context.umbrella_targets.select { |t| t.specs.any? }
|
308
|
+
raise "\n\nUnsupported target count".red unless targets.count == 1
|
309
|
+
target = targets.first
|
310
|
+
|
311
|
+
if build_xcframeworks
|
312
|
+
project_path = sandbox_root.parent + 'Pods/Pods.xcodeproj'
|
313
|
+
|
314
|
+
case target.platform_name
|
315
|
+
when :ios then
|
316
|
+
xcodebuild_settings = [PodBuilder::XcodeBuildSettings.new("iphoneos"), PodBuilder::XcodeBuildSettings.new("iphonesimulator")]
|
317
|
+
if build_catalyst
|
318
|
+
xcodebuild_settings += [PodBuilder::XcodeBuildSettings.new("catalyst")]
|
319
|
+
end
|
320
|
+
when :osx then xcodebuild_settings = [PodBuilder::XcodeBuildSettings.new("macos")]
|
321
|
+
when :tvos then xcodebuild_settings = [PodBuilder::XcodeBuildSettings.new("tvos"), PodBuilder::XcodeBuildSettings.new("tvossimulator")]
|
322
|
+
when :watchos then xcodebuild_settings = [PodBuilder::XcodeBuildSettings.new("watchos"), PodBuilder::XcodeBuildSettings.new("watchossimulator")]
|
323
|
+
else raise "\n\nUnknown platform '#{target.platform_name}'".red end
|
324
|
+
|
325
|
+
xcodebuild_settings.each do |xcodebuild_setting|
|
326
|
+
puts "Building xcframeworks for #{xcodebuild_setting.platform_name}".yellow
|
327
|
+
raise "\n\n#{build_destination} xcframework archive failed!".red if !system("xcodebuild archive -project #{project_path.to_s} -scheme Pods-DummyTarget -destination '#{xcodebuild_setting.build_destination}' -archivePath '#{build_dir}/#{xcodebuild_setting.platform_name}' SKIP_INSTALL=NO > /dev/null 2>&1")
|
328
|
+
end
|
329
|
+
|
330
|
+
built_items = Dir.glob("#{build_dir}/#{xcodebuild_settings[0].platform_name}.xcarchive/Products/Library/Frameworks/*").reject { |t| File.basename(t, ".*") == "Pods_DummyTarget" }
|
331
|
+
|
332
|
+
built_items.each do |built_item|
|
333
|
+
built_item_paths = [built_item]
|
334
|
+
xcodebuild_settings.drop(1).each do |xcodebuild_setting|
|
335
|
+
path = "#{build_dir}/#{xcodebuild_setting.platform_name}.xcarchive/Products/Library/Frameworks/#{File.basename(built_item)}"
|
336
|
+
if File.directory?(path)
|
337
|
+
built_item_paths.push(path)
|
338
|
+
else
|
339
|
+
built_item_paths = []
|
340
|
+
break
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
next if built_item_paths.count == 0
|
345
|
+
|
346
|
+
framework_name = File.basename(built_item_paths.first, ".*")
|
347
|
+
xcframework_path = "#{base_destination}/#{framework_name}/#{framework_name}.xcframework"
|
348
|
+
framework_params = built_item_paths.map { |t| "-framework '#{t}'"}.join(" ")
|
349
|
+
raise "\n\nFailed packing xcframework!".red if !system("xcodebuild -create-xcframework #{framework_params} -output '#{xcframework_path}' > /dev/null 2>&1")
|
350
|
+
|
351
|
+
if enable_dsym
|
352
|
+
xcodebuild_settings.each do |xcodebuild_setting|
|
353
|
+
dsym_source = "#{build_dir}/#{xcodebuild_setting.platform_name}.xcarchive/dSYMs/"
|
354
|
+
if File.directory?(dsym_source)
|
355
|
+
destination = sandbox_root.parent + "dSYMs"
|
356
|
+
FileUtils.mkdir_p(destination)
|
357
|
+
FileUtils.mv(dsym_source, destination)
|
358
|
+
FileUtils.mv("#{destination}/dSYMs", "#{destination}/#{xcodebuild_setting.platform_name}")
|
359
|
+
end
|
360
|
+
end
|
361
|
+
else
|
362
|
+
raise "Not implemented"
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
built_count = built_items.count
|
367
|
+
Pod::UI.puts "Built #{built_count} #{'item'.pluralize(built_count)}"
|
368
|
+
else
|
369
|
+
case [target.platform_name, uses_frameworks]
|
370
|
+
when [:ios, true] then PodBuilder::build_for_iosish_platform_framework(sandbox, build_dir, target, 'iphoneos', 'iphonesimulator', configuration, PodBuilder::Configuration.deterministic_build)
|
371
|
+
when [:osx, true] then PodBuilder::xcodebuild(sandbox, target.cocoapods_target_label, configuration, PodBuilder::Configuration.deterministic_build, {})
|
372
|
+
when [:tvos, true] then PodBuilder::build_for_iosish_platform_framework(sandbox, build_dir, target, 'appletvos', 'appletvsimulator', configuration, PodBuilder::Configuration.deterministic_build)
|
373
|
+
when [:watchos, true] then PodBuilder::build_for_iosish_platform_framework(sandbox, build_dir, target, 'watchos', 'watchsimulator', configuration, PodBuilder::Configuration.deterministic_build)
|
374
|
+
when [:ios, false] then PodBuilder::build_for_iosish_platform_lib(sandbox, build_dir, target, 'iphoneos', 'iphonesimulator', configuration, PodBuilder::Configuration.deterministic_build, prebuilt_root_paths)
|
375
|
+
when [:osx, false] then PodBuilder::xcodebuild(sandbox, target.cocoapods_target_label, configuration, PodBuilder::Configuration.deterministic_build, prebuilt_root_paths)
|
376
|
+
when [:tvos, false] then PodBuilder::build_for_iosish_platform_lib(sandbox, build_dir, target, 'appletvos', 'appletvsimulator', configuration, PodBuilder::Configuration.deterministic_build, prebuilt_root_paths)
|
377
|
+
when [:watchos, false] then PodBuilder::build_for_iosish_platform_lib(sandbox, build_dir, target, 'watchos', 'watchsimulator', configuration, PodBuilder::Configuration.deterministic_build, prebuilt_root_paths)
|
378
|
+
else raise "\n\nUnknown platform '#{target.platform_name}'".red end
|
379
|
+
|
380
|
+
raise Pod::Informative, 'The build directory was not found in the expected location.' unless build_dir.directory?
|
381
|
+
|
382
|
+
specs = installer_context.umbrella_targets.map { |t| t.specs.map(&:name) }.flatten.map { |t| t.split("/").first }.uniq
|
383
|
+
built_count = Dir["#{build_dir}/*"].select { |t| specs.include?(File.basename(t)) }.count
|
384
|
+
Pod::UI.puts "Built #{built_count} #{'item'.pluralize(built_count)}, copying..."
|
385
|
+
|
386
|
+
base_destination.rmtree if base_destination.directory?
|
387
|
+
|
388
|
+
installer_context.umbrella_targets.each do |umbrella|
|
389
|
+
umbrella.specs.each do |spec|
|
390
|
+
root_name = spec.name.split("/").first
|
391
|
+
|
392
|
+
if uses_frameworks
|
393
|
+
destination = File.join(base_destination, root_name)
|
394
|
+
else
|
395
|
+
destination = File.join(base_destination, root_name, root_name)
|
396
|
+
end
|
397
|
+
# Make sure the device target overwrites anything in the simulator build, otherwise iTunesConnect
|
398
|
+
# can get upset about Info.plist containing references to the simulator SDK
|
399
|
+
files = Pathname.glob("build/#{root_name}/*").reject { |f| f.to_s =~ /Pods[^.]+\.framework/ }
|
400
|
+
|
401
|
+
consumer = spec.consumer(umbrella.platform_name)
|
402
|
+
file_accessor = Pod::Sandbox::FileAccessor.new(sandbox.pod_dir(spec.root.name), consumer)
|
403
|
+
files += file_accessor.vendored_libraries
|
404
|
+
files += file_accessor.vendored_frameworks
|
405
|
+
files += file_accessor.resources
|
406
|
+
|
407
|
+
FileUtils.mkdir_p(destination)
|
408
|
+
files.each do |file|
|
409
|
+
FileUtils.cp_r(file, destination)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# Depending on the resource it may happen that it is present twice, both in the .framework and in the parent folder
|
415
|
+
Dir.glob("#{base_destination}/*") do |path|
|
416
|
+
unless File.directory?(path)
|
417
|
+
return
|
418
|
+
end
|
419
|
+
|
420
|
+
files = Dir.glob("#{path}/*")
|
421
|
+
framework_files = Dir.glob("#{path}/*.framework/**/*").map { |t| File.basename(t) }
|
422
|
+
|
423
|
+
files.each do |file|
|
424
|
+
filename = File.basename(file.gsub(/\.xib$/, ".nib"))
|
425
|
+
if framework_files.include?(filename)
|
426
|
+
FileUtils.rm_rf(file)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
if enable_dsym
|
432
|
+
dsym_source = "#{build_dir}/dSYM"
|
433
|
+
if File.directory?(dsym_source)
|
434
|
+
FileUtils.mv(dsym_source, sandbox_root.parent)
|
435
|
+
end
|
436
|
+
else
|
437
|
+
raise "Not implemented"
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
build_dir.rmtree if build_dir.directory?
|
442
|
+
|
443
|
+
if user_options["post_compile"]
|
444
|
+
user_options["post_compile"].call(installer_context)
|
445
|
+
end
|
446
|
+
end
|