pod-builder-y 2.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|