pod-builder-y 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +8 -0
  4. data/LICENSE.txt +13 -0
  5. data/README.md +385 -0
  6. data/Rakefile +2 -0
  7. data/bin/console +16 -0
  8. data/bin/setup +8 -0
  9. data/exe/pod_builder_y +406 -0
  10. data/lib/core_ext/string.rb +5 -0
  11. data/lib/pod_builder/analyze.rb +59 -0
  12. data/lib/pod_builder/analyzer.rb +16 -0
  13. data/lib/pod_builder/command.rb +14 -0
  14. data/lib/pod_builder/command/build.rb +228 -0
  15. data/lib/pod_builder/command/build_all.rb +15 -0
  16. data/lib/pod_builder/command/clean.rb +75 -0
  17. data/lib/pod_builder/command/deintegrate.rb +101 -0
  18. data/lib/pod_builder/command/generate_lldbinit.rb +128 -0
  19. data/lib/pod_builder/command/generate_podspec.rb +22 -0
  20. data/lib/pod_builder/command/info.rb +18 -0
  21. data/lib/pod_builder/command/init.rb +148 -0
  22. data/lib/pod_builder/command/install_sources.rb +79 -0
  23. data/lib/pod_builder/command/none.rb +16 -0
  24. data/lib/pod_builder/command/restore_all.rb +33 -0
  25. data/lib/pod_builder/command/switch.rb +224 -0
  26. data/lib/pod_builder/command/sync_podfile.rb +34 -0
  27. data/lib/pod_builder/command/update.rb +43 -0
  28. data/lib/pod_builder/configuration.rb +300 -0
  29. data/lib/pod_builder/core.rb +222 -0
  30. data/lib/pod_builder/info.rb +90 -0
  31. data/lib/pod_builder/install.rb +505 -0
  32. data/lib/pod_builder/licenses.rb +73 -0
  33. data/lib/pod_builder/podfile.rb +700 -0
  34. data/lib/pod_builder/podfile/pre_actions_swizzles.rb +84 -0
  35. data/lib/pod_builder/podfile_cp.rb +99 -0
  36. data/lib/pod_builder/podfile_item.rb +530 -0
  37. data/lib/pod_builder/podspec.rb +269 -0
  38. data/lib/pod_builder/rome/post_install.rb +446 -0
  39. data/lib/pod_builder/rome/pre_install.rb +6 -0
  40. data/lib/pod_builder/templates/build_podfile.template +70 -0
  41. data/lib/pod_builder/templates/build_podspec.template +19 -0
  42. data/lib/pod_builder/version.rb +4 -0
  43. data/pod-builder.gemspec +37 -0
  44. 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