cocoapods-tt 0.0.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.
Files changed (124) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cocoapods-tt/command/native/install.rb +56 -0
  3. data/lib/cocoapods-tt/command/native/update.rb +157 -0
  4. data/lib/cocoapods-tt/command/tt/make.rb +92 -0
  5. data/lib/cocoapods-tt/command/tt.rb +115 -0
  6. data/lib/cocoapods-tt/command.rb +1 -0
  7. data/lib/cocoapods-tt/gem_version.rb +3 -0
  8. data/lib/cocoapods-tt/native/command.rb +185 -0
  9. data/lib/cocoapods-tt/native/config.rb +366 -0
  10. data/lib/cocoapods-tt/native/core_overrides.rb +1 -0
  11. data/lib/cocoapods-tt/native/downloader/cache.rb +322 -0
  12. data/lib/cocoapods-tt/native/downloader/request.rb +86 -0
  13. data/lib/cocoapods-tt/native/downloader/response.rb +16 -0
  14. data/lib/cocoapods-tt/native/downloader.rb +192 -0
  15. data/lib/cocoapods-tt/native/executable.rb +247 -0
  16. data/lib/cocoapods-tt/native/external_sources/abstract_external_source.rb +205 -0
  17. data/lib/cocoapods-tt/native/external_sources/downloader_source.rb +30 -0
  18. data/lib/cocoapods-tt/native/external_sources/path_source.rb +55 -0
  19. data/lib/cocoapods-tt/native/external_sources/podspec_source.rb +54 -0
  20. data/lib/cocoapods-tt/native/external_sources.rb +57 -0
  21. data/lib/cocoapods-tt/native/gem_version.rb +5 -0
  22. data/lib/cocoapods-tt/native/generator/acknowledgements/markdown.rb +44 -0
  23. data/lib/cocoapods-tt/native/generator/acknowledgements/plist.rb +94 -0
  24. data/lib/cocoapods-tt/native/generator/acknowledgements.rb +107 -0
  25. data/lib/cocoapods-tt/native/generator/app_target_helper.rb +363 -0
  26. data/lib/cocoapods-tt/native/generator/bridge_support.rb +22 -0
  27. data/lib/cocoapods-tt/native/generator/constant.rb +19 -0
  28. data/lib/cocoapods-tt/native/generator/copy_dsyms_script.rb +56 -0
  29. data/lib/cocoapods-tt/native/generator/copy_resources_script.rb +223 -0
  30. data/lib/cocoapods-tt/native/generator/copy_xcframework_script.rb +227 -0
  31. data/lib/cocoapods-tt/native/generator/dummy_source.rb +31 -0
  32. data/lib/cocoapods-tt/native/generator/embed_frameworks_script.rb +196 -0
  33. data/lib/cocoapods-tt/native/generator/file_list.rb +39 -0
  34. data/lib/cocoapods-tt/native/generator/header.rb +103 -0
  35. data/lib/cocoapods-tt/native/generator/info_plist_file.rb +128 -0
  36. data/lib/cocoapods-tt/native/generator/module_map.rb +99 -0
  37. data/lib/cocoapods-tt/native/generator/prefix_header.rb +60 -0
  38. data/lib/cocoapods-tt/native/generator/script_phase_constants.rb +100 -0
  39. data/lib/cocoapods-tt/native/generator/umbrella_header.rb +46 -0
  40. data/lib/cocoapods-tt/native/hooks_manager.rb +132 -0
  41. data/lib/cocoapods-tt/native/installer/analyzer/analysis_result.rb +87 -0
  42. data/lib/cocoapods-tt/native/installer/analyzer/locking_dependency_analyzer.rb +103 -0
  43. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant.rb +87 -0
  44. data/lib/cocoapods-tt/native/installer/analyzer/pod_variant_set.rb +175 -0
  45. data/lib/cocoapods-tt/native/installer/analyzer/podfile_dependency_cache.rb +55 -0
  46. data/lib/cocoapods-tt/native/installer/analyzer/sandbox_analyzer.rb +268 -0
  47. data/lib/cocoapods-tt/native/installer/analyzer/specs_state.rb +108 -0
  48. data/lib/cocoapods-tt/native/installer/analyzer/target_inspection_result.rb +58 -0
  49. data/lib/cocoapods-tt/native/installer/analyzer/target_inspector.rb +258 -0
  50. data/lib/cocoapods-tt/native/installer/analyzer.rb +1204 -0
  51. data/lib/cocoapods-tt/native/installer/base_install_hooks_context.rb +135 -0
  52. data/lib/cocoapods-tt/native/installer/installation_options.rb +195 -0
  53. data/lib/cocoapods-tt/native/installer/pod_source_installer.rb +224 -0
  54. data/lib/cocoapods-tt/native/installer/pod_source_preparer.rb +77 -0
  55. data/lib/cocoapods-tt/native/installer/podfile_validator.rb +168 -0
  56. data/lib/cocoapods-tt/native/installer/post_install_hooks_context.rb +9 -0
  57. data/lib/cocoapods-tt/native/installer/post_integrate_hooks_context.rb +9 -0
  58. data/lib/cocoapods-tt/native/installer/pre_install_hooks_context.rb +51 -0
  59. data/lib/cocoapods-tt/native/installer/pre_integrate_hooks_context.rb +9 -0
  60. data/lib/cocoapods-tt/native/installer/project_cache/project_cache.rb +11 -0
  61. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analysis_result.rb +53 -0
  62. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_analyzer.rb +200 -0
  63. data/lib/cocoapods-tt/native/installer/project_cache/project_cache_version.rb +43 -0
  64. data/lib/cocoapods-tt/native/installer/project_cache/project_installation_cache.rb +103 -0
  65. data/lib/cocoapods-tt/native/installer/project_cache/project_metadata_cache.rb +73 -0
  66. data/lib/cocoapods-tt/native/installer/project_cache/target_cache_key.rb +176 -0
  67. data/lib/cocoapods-tt/native/installer/project_cache/target_metadata.rb +74 -0
  68. data/lib/cocoapods-tt/native/installer/sandbox_dir_cleaner.rb +105 -0
  69. data/lib/cocoapods-tt/native/installer/sandbox_header_paths_installer.rb +45 -0
  70. data/lib/cocoapods-tt/native/installer/source_provider_hooks_context.rb +34 -0
  71. data/lib/cocoapods-tt/native/installer/target_uuid_generator.rb +34 -0
  72. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +179 -0
  73. data/lib/cocoapods-tt/native/installer/user_project_integrator/target_integrator.rb +815 -0
  74. data/lib/cocoapods-tt/native/installer/user_project_integrator.rb +280 -0
  75. data/lib/cocoapods-tt/native/installer/xcode/multi_pods_project_generator.rb +82 -0
  76. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_dependency_installer.rb +66 -0
  77. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/aggregate_target_installer.rb +192 -0
  78. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/app_host_installer.rb +154 -0
  79. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/file_references_installer.rb +329 -0
  80. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_dependency_installer.rb +195 -0
  81. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_installer.rb +1239 -0
  82. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pod_target_integrator.rb +312 -0
  83. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/pods_project_writer.rb +90 -0
  84. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/project_generator.rb +120 -0
  85. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installation_result.rb +140 -0
  86. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer.rb +257 -0
  87. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator/target_installer_helper.rb +110 -0
  88. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator.rb +291 -0
  89. data/lib/cocoapods-tt/native/installer/xcode/pods_project_generator_result.rb +54 -0
  90. data/lib/cocoapods-tt/native/installer/xcode/single_pods_project_generator.rb +38 -0
  91. data/lib/cocoapods-tt/native/installer/xcode/target_validator.rb +170 -0
  92. data/lib/cocoapods-tt/native/installer/xcode.rb +11 -0
  93. data/lib/cocoapods-tt/native/installer.rb +1044 -0
  94. data/lib/cocoapods-tt/native/native_target_extension.rb +60 -0
  95. data/lib/cocoapods-tt/native/open-uri.rb +33 -0
  96. data/lib/cocoapods-tt/native/podfile.rb +13 -0
  97. data/lib/cocoapods-tt/native/project.rb +544 -0
  98. data/lib/cocoapods-tt/native/resolver/lazy_specification.rb +88 -0
  99. data/lib/cocoapods-tt/native/resolver/resolver_specification.rb +41 -0
  100. data/lib/cocoapods-tt/native/resolver.rb +600 -0
  101. data/lib/cocoapods-tt/native/sandbox/file_accessor.rb +532 -0
  102. data/lib/cocoapods-tt/native/sandbox/headers_store.rb +163 -0
  103. data/lib/cocoapods-tt/native/sandbox/path_list.rb +242 -0
  104. data/lib/cocoapods-tt/native/sandbox/pod_dir_cleaner.rb +71 -0
  105. data/lib/cocoapods-tt/native/sandbox/podspec_finder.rb +23 -0
  106. data/lib/cocoapods-tt/native/sandbox.rb +470 -0
  107. data/lib/cocoapods-tt/native/sources_manager.rb +221 -0
  108. data/lib/cocoapods-tt/native/target/aggregate_target.rb +558 -0
  109. data/lib/cocoapods-tt/native/target/build_settings.rb +1385 -0
  110. data/lib/cocoapods-tt/native/target/pod_target.rb +1168 -0
  111. data/lib/cocoapods-tt/native/target.rb +378 -0
  112. data/lib/cocoapods-tt/native/user_interface/error_report.rb +204 -0
  113. data/lib/cocoapods-tt/native/user_interface/inspector_reporter.rb +102 -0
  114. data/lib/cocoapods-tt/native/user_interface.rb +463 -0
  115. data/lib/cocoapods-tt/native/validator.rb +1170 -0
  116. data/lib/cocoapods-tt/native/version_metadata.rb +26 -0
  117. data/lib/cocoapods-tt/native/xcode/framework_paths.rb +54 -0
  118. data/lib/cocoapods-tt/native/xcode/linkage_analyzer.rb +22 -0
  119. data/lib/cocoapods-tt/native/xcode/xcframework/xcframework_slice.rb +138 -0
  120. data/lib/cocoapods-tt/native/xcode/xcframework.rb +99 -0
  121. data/lib/cocoapods-tt/native/xcode.rb +7 -0
  122. data/lib/cocoapods-tt.rb +1 -0
  123. data/lib/cocoapods_plugin.rb +17 -0
  124. metadata +193 -0
@@ -0,0 +1,815 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'cocoapods/xcode/framework_paths'
3
+ require 'cocoapods/target/build_settings'
4
+
5
+ module Pod
6
+ class Installer
7
+ class UserProjectIntegrator
8
+ # This class is responsible for integrating the library generated by a
9
+ # {TargetDefinition} with its destination project.
10
+ #
11
+ class TargetIntegrator
12
+ autoload :XCConfigIntegrator, 'cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator'
13
+
14
+ # @return [String] the string to use as prefix for every build phase added to the user project
15
+ #
16
+ BUILD_PHASE_PREFIX = '[CP] '.freeze
17
+
18
+ # @return [String] the string to use as prefix for every build phase declared by the user within a podfile
19
+ # or podspec.
20
+ #
21
+ USER_BUILD_PHASE_PREFIX = '[CP-User] '.freeze
22
+
23
+ # @return [String] the name of the check manifest phase
24
+ #
25
+ CHECK_MANIFEST_PHASE_NAME = 'Check Pods Manifest.lock'.freeze
26
+
27
+ # @return [Array<Symbol>] the symbol types, which require that the pod
28
+ # frameworks are embedded in the output directory / product bundle.
29
+ #
30
+ # @note This does not include :app_extension or :watch_extension because
31
+ # these types must have their frameworks embedded in their host targets.
32
+ # For messages extensions, this only applies if it's embedded in a messages
33
+ # application.
34
+ #
35
+ EMBED_FRAMEWORK_TARGET_TYPES = [:application, :application_on_demand_install_capable, :unit_test_bundle,
36
+ :ui_test_bundle, :watch2_extension, :messages_application].freeze
37
+
38
+ # @return [String] the name of the embed frameworks phase
39
+ #
40
+ EMBED_FRAMEWORK_PHASE_NAME = 'Embed Pods Frameworks'.freeze
41
+
42
+ # @return [String] the name of the copy xcframeworks phase
43
+ #
44
+ COPY_XCFRAMEWORKS_PHASE_NAME = 'Copy XCFrameworks'.freeze
45
+
46
+ # @return [String] the name of the copy resources phase
47
+ #
48
+ COPY_PODS_RESOURCES_PHASE_NAME = 'Copy Pods Resources'.freeze
49
+
50
+ # @return [String] the name of the copy dSYM files phase
51
+ #
52
+ COPY_DSYM_FILES_PHASE_NAME = 'Copy dSYMs'.freeze
53
+
54
+ # @return [Integer] the maximum number of input and output paths to use for a script phase
55
+ #
56
+ MAX_INPUT_OUTPUT_PATHS = 1000
57
+
58
+ # @return [Array<String>] names of script phases that existed in previous versions of CocoaPods
59
+ #
60
+ REMOVED_SCRIPT_PHASE_NAMES = [
61
+ 'Prepare Artifacts'.freeze,
62
+ ].freeze
63
+
64
+ # @return [float] Returns Minimum Xcode Compatibility version for FileLists
65
+ #
66
+ MIN_FILE_LIST_COMPATIBILITY_VERSION = 9.3
67
+
68
+ # @return [String] Returns Minimum Xcode Object version for FileLists
69
+ #
70
+ MIN_FILE_LIST_OBJECT_VERSION = 50
71
+
72
+ # @return [AggregateTarget] the target that should be integrated.
73
+ #
74
+ attr_reader :target
75
+
76
+ # @return [Boolean] whether to use input/output paths for build phase scripts
77
+ #
78
+ attr_reader :use_input_output_paths
79
+ alias use_input_output_paths? use_input_output_paths
80
+
81
+ # Init a new TargetIntegrator
82
+ #
83
+ # @param [AggregateTarget] target @see #target
84
+ # @param [Boolean] use_input_output_paths @see #use_input_output_paths
85
+ #
86
+ def initialize(target, use_input_output_paths: true)
87
+ @target = target
88
+ @use_input_output_paths = use_input_output_paths
89
+ end
90
+
91
+ # @private
92
+ #
93
+ XCFileListConfigKey = Struct.new(:file_list_path, :file_list_relative_path)
94
+
95
+ class << self
96
+ # @param [Xcodeproj::Project::Object::AbstractObject] object
97
+ #
98
+ # @return [Boolean] Whether input & output paths for the given object
99
+ # should be stored in a file list file.
100
+ #
101
+ def input_output_paths_use_filelist?(object)
102
+ unless object.project.root_object.compatibility_version.nil?
103
+ version_match = object.project.root_object.compatibility_version.match(/Xcode ([0-9]*\.[0-9]*)/).to_a
104
+ end
105
+ if version_match&.at(1).nil?
106
+ object.project.object_version.to_i >= MIN_FILE_LIST_OBJECT_VERSION
107
+ else
108
+ Pod::Version.new(version_match[1]) >= Pod::Version.new(MIN_FILE_LIST_COMPATIBILITY_VERSION)
109
+ end
110
+ end
111
+
112
+ # Sets the input & output paths for the given script build phase.
113
+ #
114
+ # @param [Xcodeproj::Project::Object::PBXShellScriptBuildPhase] phase
115
+ # The phase to set input & output paths on.
116
+ #
117
+ # @param [Hash] input_paths_by_config
118
+ #
119
+ # @return [Void]
120
+ def set_input_output_paths(phase, input_paths_by_config, output_paths_by_config)
121
+ if input_output_paths_use_filelist?(phase)
122
+ [input_paths_by_config, output_paths_by_config].each do |hash|
123
+ hash.each do |file_list, files|
124
+ generator = Generator::FileList.new(files)
125
+ Xcode::PodsProjectGenerator::TargetInstallerHelper.update_changed_file(generator, file_list.file_list_path)
126
+ end
127
+ end
128
+
129
+ phase.input_paths = nil
130
+ phase.output_paths = nil
131
+ phase.input_file_list_paths = input_paths_by_config.each_key.map(&:file_list_relative_path).uniq
132
+ phase.output_file_list_paths = output_paths_by_config.each_key.map(&:file_list_relative_path).uniq
133
+ else
134
+ input_paths = input_paths_by_config.values.flatten(1).uniq
135
+ output_paths = output_paths_by_config.values.flatten(1).uniq
136
+ TargetIntegrator.validate_input_output_path_limit(input_paths, output_paths)
137
+
138
+ phase.input_paths = input_paths
139
+ phase.output_paths = output_paths
140
+ phase.input_file_list_paths = nil
141
+ phase.output_file_list_paths = nil
142
+ end
143
+ end
144
+
145
+ # Adds a shell script build phase responsible to copy (embed) the frameworks
146
+ # generated by the TargetDefinition to the bundle of the product of the
147
+ # targets.
148
+ #
149
+ # @param [PBXNativeTarget] native_target
150
+ # The native target to add the script phase into.
151
+ #
152
+ # @param [String] script_path
153
+ # The script path to execute as part of this script phase.
154
+ #
155
+ # @param [Hash<Array, String>] input_paths_by_config
156
+ # The input paths (if any) to include for this script phase.
157
+ #
158
+ # @param [Hash<Array, String>] output_paths_by_config
159
+ # The output paths (if any) to include for this script phase.
160
+ #
161
+ # @return [void]
162
+ #
163
+ def create_or_update_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths_by_config = {}, output_paths_by_config = {})
164
+ phase = TargetIntegrator.create_or_update_shell_script_build_phase(native_target, BUILD_PHASE_PREFIX + EMBED_FRAMEWORK_PHASE_NAME)
165
+ phase.shell_script = %("#{script_path}"\n)
166
+ TargetIntegrator.set_input_output_paths(phase, input_paths_by_config, output_paths_by_config)
167
+ end
168
+
169
+ # Delete a 'Embed Pods Frameworks' Script Build Phase if present
170
+ #
171
+ # @param [PBXNativeTarget] native_target
172
+ # The native target to remove the script phase from.
173
+ #
174
+ def remove_embed_frameworks_script_phase_from_target(native_target)
175
+ remove_script_phase_from_target(native_target, EMBED_FRAMEWORK_PHASE_NAME)
176
+ end
177
+
178
+ # Adds a shell script build phase responsible to copy the xcframework slice
179
+ # to the intermediate build directory.
180
+ #
181
+ # @param [PBXNativeTarget] native_target
182
+ # The native target to add the script phase into.
183
+ #
184
+ # @param [String] script_path
185
+ # The script path to execute as part of this script phase.
186
+ #
187
+ # @param [Hash<Array, String>] input_paths_by_config
188
+ # The input paths (if any) to include for this script phase.
189
+ #
190
+ # @param [Hash<Array, String>] output_paths_by_config
191
+ # The output paths (if any) to include for this script phase.
192
+ #
193
+ # @return [void]
194
+ #
195
+ def create_or_update_copy_xcframeworks_script_phase_to_target(native_target, script_path, input_paths_by_config = {}, output_paths_by_config = {})
196
+ phase = TargetIntegrator.create_or_update_shell_script_build_phase(native_target, BUILD_PHASE_PREFIX + COPY_XCFRAMEWORKS_PHASE_NAME)
197
+ phase.shell_script = %("#{script_path}"\n)
198
+ TargetIntegrator.set_input_output_paths(phase, input_paths_by_config, output_paths_by_config)
199
+ reorder_script_phase(native_target, phase, :before_compile)
200
+ end
201
+
202
+ # Delete a 'Copy XCFrameworks' Script Build Phase if present
203
+ #
204
+ # @param [PBXNativeTarget] native_target
205
+ # The native target to remove the script phase from.
206
+ #
207
+ def remove_copy_xcframeworks_script_phase_from_target(native_target)
208
+ remove_script_phase_from_target(native_target, COPY_XCFRAMEWORKS_PHASE_NAME)
209
+ end
210
+
211
+ # Removes a script phase from a native target by name
212
+ #
213
+ # @param [PBXNativeTarget] native_target
214
+ # The target from which the script phased should be removed
215
+ #
216
+ # @param [String] phase_name
217
+ # The name of the script phase to remove
218
+ #
219
+ def remove_script_phase_from_target(native_target, phase_name)
220
+ build_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(phase_name) }
221
+ return unless build_phase.present?
222
+ native_target.build_phases.delete(build_phase)
223
+ end
224
+
225
+ # Adds a shell script build phase responsible to copy the resources
226
+ # generated by the TargetDefinition to the bundle of the product of the
227
+ # targets.
228
+ #
229
+ # @param [PBXNativeTarget] native_target
230
+ # The native target to add the script phase into.
231
+ #
232
+ # @param [String] script_path
233
+ # The script path to execute as part of this script phase.
234
+ #
235
+ # @param [Hash<Array, String>] input_paths_by_config
236
+ # The input paths (if any) to include for this script phase.
237
+ #
238
+ # @param [Hash<Array, String>] output_paths_by_config
239
+ # The output paths (if any) to include for this script phase.
240
+ #
241
+ # @return [void]
242
+ #
243
+ def create_or_update_copy_resources_script_phase_to_target(native_target, script_path, input_paths_by_config = {}, output_paths_by_config = {})
244
+ phase_name = COPY_PODS_RESOURCES_PHASE_NAME
245
+ phase = TargetIntegrator.create_or_update_shell_script_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
246
+ phase.shell_script = %("#{script_path}"\n)
247
+ TargetIntegrator.set_input_output_paths(phase, input_paths_by_config, output_paths_by_config)
248
+ end
249
+
250
+ # Delete a 'Copy Pods Resources' script phase if present
251
+ #
252
+ # @param [PBXNativeTarget] native_target
253
+ # The native target to remove the script phase from.
254
+ #
255
+ def remove_copy_resources_script_phase_from_target(native_target)
256
+ build_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(COPY_PODS_RESOURCES_PHASE_NAME) }
257
+ return unless build_phase.present?
258
+ native_target.build_phases.delete(build_phase)
259
+ end
260
+
261
+ # Creates or update a shell script build phase for the given target.
262
+ #
263
+ # @param [PBXNativeTarget] native_target
264
+ # The native target to add the script phase into.
265
+ #
266
+ # @param [String] script_phase_name
267
+ # The name of the script phase to use.
268
+ #
269
+ # @param [String] show_env_vars_in_log
270
+ # The value to set for show environment variables in the log during execution of this script phase or
271
+ # `nil` for not setting the value at all.
272
+ #
273
+ # @return [PBXShellScriptBuildPhase] The existing or newly created shell script build phase.
274
+ #
275
+ def create_or_update_shell_script_build_phase(native_target, script_phase_name, show_env_vars_in_log = '0')
276
+ build_phases = native_target.build_phases.grep(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
277
+ build_phases.find { |phase| phase.name && phase.name.end_with?(script_phase_name) }.tap { |p| p.name = script_phase_name if p } ||
278
+ native_target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase).tap do |phase|
279
+ UI.message("Adding Build Phase '#{script_phase_name}' to project.") do
280
+ phase.name = script_phase_name
281
+ unless show_env_vars_in_log.nil?
282
+ phase.show_env_vars_in_log = show_env_vars_in_log
283
+ end
284
+ native_target.build_phases << phase
285
+ end
286
+ end
287
+ end
288
+
289
+ # Updates all target script phases for the current target, including creating or updating, deleting
290
+ # and re-ordering.
291
+ #
292
+ # @return [void]
293
+ #
294
+ def create_or_update_user_script_phases(script_phases, native_target)
295
+ script_phase_names = script_phases.map { |k| k[:name] }
296
+ # Delete script phases no longer present in the target.
297
+ native_target_script_phases = native_target.shell_script_build_phases.select do |bp|
298
+ !bp.name.nil? && bp.name.start_with?(USER_BUILD_PHASE_PREFIX)
299
+ end
300
+ native_target_script_phases.each do |script_phase|
301
+ script_phase_name_without_prefix = script_phase.name.sub(USER_BUILD_PHASE_PREFIX, '')
302
+ unless script_phase_names.include?(script_phase_name_without_prefix)
303
+ native_target.build_phases.delete(script_phase)
304
+ end
305
+ end
306
+ # Create or update the ones that are expected to be.
307
+ script_phases.each do |script_phase|
308
+ name_with_prefix = USER_BUILD_PHASE_PREFIX + script_phase[:name]
309
+ phase = TargetIntegrator.create_or_update_shell_script_build_phase(native_target, name_with_prefix, nil)
310
+ phase.shell_script = script_phase[:script]
311
+ phase.shell_path = script_phase[:shell_path] || '/bin/sh'
312
+ phase.input_paths = script_phase[:input_files]
313
+ phase.output_paths = script_phase[:output_files]
314
+ phase.input_file_list_paths = script_phase[:input_file_lists]
315
+ phase.output_file_list_paths = script_phase[:output_file_lists]
316
+ phase.dependency_file = script_phase[:dependency_file]
317
+ # At least with Xcode 10 `showEnvVarsInLog` is *NOT* set to any value even if it's checked and it only
318
+ # gets set to '0' if the user has explicitly disabled this.
319
+ if (show_env_vars_in_log = script_phase.fetch(:show_env_vars_in_log, '1')) == '0'
320
+ phase.show_env_vars_in_log = show_env_vars_in_log
321
+ end
322
+
323
+ execution_position = script_phase[:execution_position]
324
+ reorder_script_phase(native_target, phase, execution_position)
325
+ end
326
+ end
327
+
328
+ def reorder_script_phase(native_target, script_phase, execution_position)
329
+ return if execution_position == :any || execution_position.to_s.empty?
330
+ target_phase_type = case execution_position
331
+ when :before_compile, :after_compile
332
+ Xcodeproj::Project::Object::PBXSourcesBuildPhase
333
+ when :before_headers, :after_headers
334
+ Xcodeproj::Project::Object::PBXHeadersBuildPhase
335
+ else
336
+ raise ArgumentError, "Unknown execution position `#{execution_position}`"
337
+ end
338
+ order_before = case execution_position
339
+ when :before_compile, :before_headers
340
+ true
341
+ when :after_compile, :after_headers
342
+ false
343
+ else
344
+ raise ArgumentError, "Unknown execution position `#{execution_position}`"
345
+ end
346
+
347
+ target_phase_index = native_target.build_phases.index do |bp|
348
+ bp.is_a?(target_phase_type)
349
+ end
350
+ return if target_phase_index.nil?
351
+ script_phase_index = native_target.build_phases.index do |bp|
352
+ bp.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && !bp.name.nil? && bp.name == script_phase.name
353
+ end
354
+ if (order_before && script_phase_index > target_phase_index) ||
355
+ (!order_before && script_phase_index < target_phase_index)
356
+ native_target.build_phases.move_from(script_phase_index, target_phase_index)
357
+ end
358
+ end
359
+
360
+ # Script phases can have a limited number of input and output paths due to each one being exported to `env`.
361
+ # A large number can cause a build failure because of limitations in `env`. See issue
362
+ # https://github.com/CocoaPods/CocoaPods/issues/7362.
363
+ #
364
+ # @param [Array<String>] input_paths
365
+ # The input paths to trim.
366
+ #
367
+ # @param [Array<String>] output_paths
368
+ # The output paths to trim.
369
+ #
370
+ # @return [void]
371
+ #
372
+ def validate_input_output_path_limit(input_paths, output_paths)
373
+ if (input_paths.count + output_paths.count) > MAX_INPUT_OUTPUT_PATHS
374
+ input_paths.clear
375
+ output_paths.clear
376
+ end
377
+ end
378
+
379
+ # Returns the resource output paths for all given input paths.
380
+ #
381
+ # @param [Array<String>] resource_input_paths
382
+ # The input paths to map to.
383
+ #
384
+ # @return [Array<String>] The resource output paths.
385
+ #
386
+ def resource_output_paths(resource_input_paths)
387
+ resource_input_paths.map do |resource_input_path|
388
+ base_path = '${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}'
389
+ extname = File.extname(resource_input_path)
390
+ basename = extname == '.xcassets' ? 'Assets' : File.basename(resource_input_path)
391
+ output_extension = Target.output_extension_for_resource(extname)
392
+ File.join(base_path, File.basename(basename, extname) + output_extension)
393
+ end.uniq
394
+ end
395
+
396
+ # Returns the framework input paths for the given framework paths
397
+ #
398
+ # @param [Array<Xcode::FrameworkPaths>] framework_paths
399
+ # The target's framework paths to map to input paths.
400
+ #
401
+ # @param [Array<XCFramework>] xcframeworks
402
+ # The target's xcframeworks to map to input paths.
403
+ #
404
+ # @return [Array<String>] The embed frameworks script input paths
405
+ #
406
+ def embed_frameworks_input_paths(framework_paths, xcframeworks)
407
+ input_paths = framework_paths.map(&:source_path)
408
+ # Only include dynamic xcframeworks as the input since we will not be copying static xcframework slices
409
+ xcframeworks.select { |xcf| xcf.build_type.dynamic_framework? }.each do |xcframework|
410
+ name = xcframework.name
411
+ input_paths << "#{Pod::Target::BuildSettings.xcframework_intermediate_dir(xcframework)}/#{name}.framework/#{name}"
412
+ end
413
+ input_paths
414
+ end
415
+
416
+ # Returns the framework output paths for the given framework paths
417
+ #
418
+ # @param [Array<Xcode::FrameworkPaths>] framework_paths
419
+ # The framework input paths to map to output paths.
420
+ #
421
+ # @param [Array<XCFramework>] xcframeworks
422
+ # The installed xcframeworks.
423
+ #
424
+ # @return [Array<String>] The embed framework script output paths
425
+ #
426
+ def embed_frameworks_output_paths(framework_paths, xcframeworks)
427
+ paths = framework_paths.map do |framework_path|
428
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{File.basename(framework_path.source_path)}"
429
+ end.uniq
430
+ # Static xcframeworks are not copied to the build dir
431
+ # so only include dynamic artifacts that will be copied to the build folder
432
+ xcframework_paths = xcframeworks.select { |xcf| xcf.build_type.dynamic_framework? }.map do |xcframework|
433
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/#{xcframework.name}.framework"
434
+ end
435
+ paths + xcframework_paths
436
+ end
437
+
438
+ # Updates a projects native targets to include on demand resources specified by the supplied parameters.
439
+ # Note that currently, only app level targets are allowed to include on demand resources.
440
+ #
441
+ # @param [Sandbox] sandbox
442
+ # The sandbox to use for calculating ODR file references.
443
+ #
444
+ # @param [Xcodeproj::Project] project
445
+ # The project to update known asset tags as well as add the ODR group.
446
+ #
447
+ # @param [Xcodeproj::PBXNativeTarget, Array<Xcodeproj::PBXNativeTarget>] native_targets
448
+ # The native targets to integrate on demand resources into.
449
+ #
450
+ # @param [Sandbox::FileAccessor, Array<Sandbox::FileAccessor>] file_accessors
451
+ # The file accessors that that provide the ODRs to integrate.
452
+ #
453
+ # @param [Xcodeproj::PBXGroup] parent_odr_group
454
+ # The group to use as the parent to add ODR file references into.
455
+ #
456
+ # @param [String] target_odr_group_name
457
+ # The name to use for the group created that contains the ODR file references.
458
+ #
459
+ # @return [void]
460
+ #
461
+ def update_on_demand_resources(sandbox, project, native_targets, file_accessors, parent_odr_group,
462
+ target_odr_group_name)
463
+ category_to_tags = {}
464
+ file_accessors = Array(file_accessors)
465
+ native_targets = Array(native_targets)
466
+
467
+ # Target no longer provides ODR references so remove everything related to this target.
468
+ if file_accessors.all? { |fa| fa.on_demand_resources.empty? }
469
+ old_target_odr_group = parent_odr_group[target_odr_group_name]
470
+ old_odr_file_refs = old_target_odr_group&.recursive_children_groups&.each_with_object({}) do |group, hash|
471
+ hash[group.name] = group.files
472
+ end || {}
473
+ native_targets.each do |native_target|
474
+ native_target.remove_on_demand_resources(old_odr_file_refs)
475
+ update_on_demand_resources_build_settings(native_target, nil => old_odr_file_refs.keys)
476
+ end
477
+ old_target_odr_group&.remove_from_project
478
+ return
479
+ end
480
+
481
+ target_odr_group = parent_odr_group[target_odr_group_name] || parent_odr_group.new_group(target_odr_group_name)
482
+ current_file_refs = target_odr_group.recursive_children_groups.flat_map(&:files)
483
+
484
+ added_file_refs = file_accessors.flat_map do |file_accessor|
485
+ target_odr_files_refs = Hash[file_accessor.on_demand_resources.map do |tag, value|
486
+ tag_group = target_odr_group[tag] || target_odr_group.new_group(tag)
487
+ category_to_tags[value[:category]] ||= []
488
+ category_to_tags[value[:category]] << tag
489
+ resources_file_refs = value[:paths].map do |resource|
490
+ odr_resource_file_ref = Pathname.new(resource).relative_path_from(sandbox.root)
491
+ tag_group.find_file_by_path(odr_resource_file_ref.to_s) || tag_group.new_file(odr_resource_file_ref)
492
+ end
493
+ [tag, resources_file_refs]
494
+ end]
495
+ native_targets.each do |native_target|
496
+ native_target.add_on_demand_resources(target_odr_files_refs)
497
+ end
498
+ target_odr_files_refs.values.flatten
499
+ end
500
+
501
+ # if the target ODR file references were updated, make sure we remove the ones that are no longer present
502
+ # for the target.
503
+ remaining_refs = current_file_refs - added_file_refs
504
+ remaining_refs.each do |ref|
505
+ native_targets.each do |user_target|
506
+ user_target.resources_build_phase.remove_file_reference(ref)
507
+ end
508
+ ref.remove_from_project
509
+ end
510
+ target_odr_group.recursive_children_groups.each { |g| g.remove_from_project if g.empty? }
511
+
512
+ attributes = project.root_object.attributes
513
+ attributes['KnownAssetTags'] = (attributes['KnownAssetTags'] ||= []) | category_to_tags.values.flatten
514
+ project.root_object.attributes = attributes
515
+
516
+ native_targets.each do |native_target|
517
+ update_on_demand_resources_build_settings(native_target, category_to_tags)
518
+ end
519
+ end
520
+
521
+ def update_on_demand_resources_build_settings(native_target, category_to_tags)
522
+ %w[ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGS ON_DEMAND_RESOURCES_PREFETCH_ORDER].each do |category_key|
523
+ native_target.build_configurations.each do |c|
524
+ key = case category_key
525
+ when 'ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGS'
526
+ :initial_install
527
+ when 'ON_DEMAND_RESOURCES_PREFETCH_ORDER'
528
+ :prefetched
529
+ else
530
+ :download_on_demand
531
+ end
532
+ tags_for_category = (c.build_settings[category_key] || '').split
533
+ category_to_tags_dup = category_to_tags.dup
534
+ tags_to_add = category_to_tags_dup.delete(key) || []
535
+ tags_to_delete = category_to_tags_dup.values.flatten
536
+ tags_for_category = (tags_for_category + tags_to_add - tags_to_delete).flatten.compact.uniq
537
+ if tags_for_category.empty?
538
+ val = c.build_settings.delete(category_key)
539
+ native_target.project.mark_dirty! unless val.nil?
540
+ else
541
+ tags = tags_for_category.join(' ')
542
+ unless c.build_settings[category_key] == tags
543
+ c.build_settings[category_key] = tags
544
+ native_target.project.mark_dirty!
545
+ end
546
+ end
547
+ end
548
+ end
549
+ end
550
+ end
551
+
552
+ # Integrates the user project targets. Only the targets that do **not**
553
+ # already have the Pods library in their frameworks build phase are
554
+ # processed.
555
+ #
556
+ # @return [void]
557
+ #
558
+ def integrate!
559
+ UI.section(integration_message) do
560
+ XCConfigIntegrator.integrate(target, native_targets)
561
+
562
+ remove_obsolete_script_phases
563
+ add_pods_library
564
+ add_embed_frameworks_script_phase
565
+ remove_embed_frameworks_script_phase_from_embedded_targets
566
+ add_copy_resources_script_phase
567
+ add_check_manifest_lock_script_phase
568
+ add_user_script_phases
569
+ add_on_demand_resources
570
+ end
571
+ end
572
+
573
+ # @return [String] a string representation suitable for debugging.
574
+ #
575
+ def inspect
576
+ "#<#{self.class} for target `#{target.label}'>"
577
+ end
578
+
579
+ private
580
+
581
+ # @!group Integration steps
582
+ #---------------------------------------------------------------------#
583
+
584
+ # Adds spec product reference to the frameworks build phase of the
585
+ # {TargetDefinition} integration libraries. Adds a file reference to
586
+ # the frameworks group of the project and adds it to the frameworks
587
+ # build phase of the targets.
588
+ #
589
+ # @return [void]
590
+ #
591
+ def add_pods_library
592
+ frameworks = user_project.frameworks_group
593
+ native_targets.each do |native_target|
594
+ build_phase = native_target.frameworks_build_phase
595
+ product_name = target.product_name
596
+
597
+ # Delete previously integrated references.
598
+ product_build_files = build_phase.files.select do |build_file|
599
+ build_file.display_name =~ Pod::Deintegrator::FRAMEWORK_NAMES
600
+ end
601
+
602
+ product_build_files.each do |product_file|
603
+ next unless product_name != product_file.display_name
604
+ UI.message("Removing old product reference `#{product_file.display_name}` from project.")
605
+ frameworks.remove_reference(product_file.file_ref)
606
+ build_phase.remove_build_file(product_file)
607
+ end
608
+
609
+ # Find or create and add a reference for the current product type
610
+ new_product_ref = frameworks.files.find { |f| f.path == product_name } ||
611
+ frameworks.new_product_ref_for_target(target.product_basename, target.product_type)
612
+ build_phase.build_file(new_product_ref) ||
613
+ build_phase.add_file_reference(new_product_ref, true)
614
+ end
615
+ end
616
+
617
+ # Find or create a 'Copy Pods Resources' build phase
618
+ #
619
+ # @return [void]
620
+ #
621
+ def add_copy_resources_script_phase
622
+ unless target.includes_resources?
623
+ native_targets.each do |native_target|
624
+ TargetIntegrator.remove_copy_resources_script_phase_from_target(native_target)
625
+ end
626
+ return
627
+ end
628
+
629
+ script_path = target.copy_resources_script_relative_path
630
+ input_paths_by_config = {}
631
+ output_paths_by_config = {}
632
+ if use_input_output_paths
633
+ target.resource_paths_by_config.each do |config, resource_paths|
634
+ input_paths_key = XCFileListConfigKey.new(target.copy_resources_script_input_files_path(config),
635
+ target.copy_resources_script_input_files_relative_path)
636
+ input_paths_by_config[input_paths_key] = [script_path] + resource_paths
637
+
638
+ output_paths_key = XCFileListConfigKey.new(target.copy_resources_script_output_files_path(config),
639
+ target.copy_resources_script_output_files_relative_path)
640
+ output_paths_by_config[output_paths_key] = TargetIntegrator.resource_output_paths(resource_paths)
641
+ end
642
+ end
643
+
644
+ native_targets.each do |native_target|
645
+ # Static library targets cannot include resources. Skip this phase from being added instead.
646
+ next if native_target.symbol_type == :static_library
647
+ TargetIntegrator.create_or_update_copy_resources_script_phase_to_target(native_target, script_path,
648
+ input_paths_by_config,
649
+ output_paths_by_config)
650
+ end
651
+ end
652
+
653
+ # Removes the embed frameworks build phase from embedded targets
654
+ #
655
+ # @note Older versions of CocoaPods would add this build phase to embedded
656
+ # targets. They should be removed on upgrade because embedded targets
657
+ # will have their frameworks embedded in their host targets.
658
+ #
659
+ def remove_embed_frameworks_script_phase_from_embedded_targets
660
+ return unless target.requires_host_target?
661
+ native_targets.each do |native_target|
662
+ if AggregateTarget::EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES.include? native_target.symbol_type
663
+ TargetIntegrator.remove_embed_frameworks_script_phase_from_target(native_target)
664
+ end
665
+ end
666
+ end
667
+
668
+ # Find or create a 'Embed Pods Frameworks' Copy Files Build Phase
669
+ #
670
+ # @return [void]
671
+ #
672
+ def add_embed_frameworks_script_phase
673
+ unless target.includes_frameworks? || (target.xcframeworks_by_config.values.flatten.any? { |xcf| xcf.build_type.dynamic_framework? })
674
+ native_targets_to_embed_in.each do |native_target|
675
+ TargetIntegrator.remove_embed_frameworks_script_phase_from_target(native_target)
676
+ end
677
+ return
678
+ end
679
+
680
+ script_path = target.embed_frameworks_script_relative_path
681
+ input_paths_by_config = {}
682
+ output_paths_by_config = {}
683
+ if use_input_output_paths?
684
+ configs = Set.new(target.framework_paths_by_config.keys + target.xcframeworks_by_config.keys).sort
685
+ configs.each do |config|
686
+ framework_paths = target.framework_paths_by_config[config] || []
687
+ xcframeworks = target.xcframeworks_by_config[config] || []
688
+
689
+ input_paths_key = XCFileListConfigKey.new(target.embed_frameworks_script_input_files_path(config), target.embed_frameworks_script_input_files_relative_path)
690
+ input_paths_by_config[input_paths_key] = [script_path] + TargetIntegrator.embed_frameworks_input_paths(framework_paths, xcframeworks)
691
+
692
+ output_paths_key = XCFileListConfigKey.new(target.embed_frameworks_script_output_files_path(config), target.embed_frameworks_script_output_files_relative_path)
693
+ output_paths_by_config[output_paths_key] = TargetIntegrator.embed_frameworks_output_paths(framework_paths, xcframeworks)
694
+ end
695
+ end
696
+
697
+ native_targets_to_embed_in.each do |native_target|
698
+ TargetIntegrator.create_or_update_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths_by_config, output_paths_by_config)
699
+ end
700
+ end
701
+
702
+ # Updates all target script phases for the current target, including creating or updating, deleting
703
+ # and re-ordering.
704
+ #
705
+ # @return [void]
706
+ #
707
+ def add_user_script_phases
708
+ native_targets.each do |native_target|
709
+ TargetIntegrator.create_or_update_user_script_phases(target.target_definition.script_phases, native_target)
710
+ end
711
+ end
712
+
713
+ # Adds a shell script build phase responsible for checking if the Pods
714
+ # locked in the Pods/Manifest.lock file are in sync with the Pods defined
715
+ # in the Podfile.lock.
716
+ #
717
+ # @note The build phase is appended to the front because to fail
718
+ # fast.
719
+ #
720
+ # @return [void]
721
+ #
722
+ def add_check_manifest_lock_script_phase
723
+ phase_name = CHECK_MANIFEST_PHASE_NAME
724
+ native_targets.each do |native_target|
725
+ phase = TargetIntegrator.create_or_update_shell_script_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
726
+ native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase
727
+ phase.shell_script = <<-SH.strip_heredoc
728
+ diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
729
+ if [ $? != 0 ] ; then
730
+ # print error to STDERR
731
+ echo "error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation." >&2
732
+ exit 1
733
+ fi
734
+ # This output is used by Xcode 'outputs' to avoid re-running this script phase.
735
+ echo "SUCCESS" > "${SCRIPT_OUTPUT_FILE_0}"
736
+ SH
737
+ phase.input_paths = %w(${PODS_PODFILE_DIR_PATH}/Podfile.lock ${PODS_ROOT}/Manifest.lock)
738
+ phase.output_paths = [target.check_manifest_lock_script_output_file_path]
739
+ end
740
+ end
741
+
742
+ # @param [Array<String>] removed_phase_names
743
+ # The names of the script phases that should be removed
744
+ #
745
+ def remove_obsolete_script_phases(removed_phase_names = REMOVED_SCRIPT_PHASE_NAMES)
746
+ native_targets.each do |native_target|
747
+ removed_phase_names.each do |phase_name|
748
+ TargetIntegrator.remove_script_phase_from_target(native_target, phase_name)
749
+ end
750
+ end
751
+ end
752
+
753
+ def add_on_demand_resources
754
+ target.pod_targets.each do |pod_target|
755
+ # When integrating with the user's project we are only interested in integrating ODRs from library specs
756
+ # and not test specs or app specs.
757
+ library_file_accessors = pod_target.file_accessors.select { |fa| fa.spec.library_specification? }
758
+ target_odr_group_name = "#{pod_target.label}-OnDemandResources"
759
+ # The 'Pods' group would always be there for production code however for tests its sometimes not added.
760
+ # This ensures its always present and makes it easier for existing and new tests.
761
+ parent_odr_group = target.user_project.main_group['Pods'] || target.user_project.new_group('Pods')
762
+ TargetIntegrator.update_on_demand_resources(target.sandbox, target.user_project, target.user_targets,
763
+ library_file_accessors, parent_odr_group, target_odr_group_name)
764
+ end
765
+ end
766
+
767
+ private
768
+
769
+ # @!group Private Helpers
770
+ #---------------------------------------------------------------------#
771
+
772
+ # @return [Array<PBXNativeTarget>] The list of all the targets that
773
+ # match the given target.
774
+ #
775
+ def native_targets
776
+ @native_targets ||= target.user_targets
777
+ end
778
+
779
+ # @return [Array<PBXNativeTarget>] The list of all the targets that
780
+ # require that the pod frameworks are embedded in the output
781
+ # directory / product bundle.
782
+ #
783
+ def native_targets_to_embed_in
784
+ return [] if target.requires_host_target?
785
+ native_targets.select do |target|
786
+ EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
787
+ end
788
+ end
789
+
790
+ # Read the project from the disk to ensure that it is up to date as
791
+ # other TargetIntegrators might have modified it.
792
+ #
793
+ # @return [Project]
794
+ #
795
+ def user_project
796
+ target.user_project
797
+ end
798
+
799
+ # @return [Specification::Consumer] the consumer for the specifications.
800
+ #
801
+ def spec_consumers
802
+ @spec_consumers ||= target.pod_targets.map(&:file_accessors).flatten.map(&:spec_consumer)
803
+ end
804
+
805
+ # @return [String] the message that should be displayed for the target
806
+ # integration.
807
+ #
808
+ def integration_message
809
+ "Integrating target `#{target.name}` " \
810
+ "(#{UI.path target.user_project_path} project)"
811
+ end
812
+ end
813
+ end
814
+ end
815
+ end