plasmo_xcodeproj 1.21.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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +95 -0
  4. data/bin/xcodeproj +10 -0
  5. data/lib/xcodeproj/command/config_dump.rb +91 -0
  6. data/lib/xcodeproj/command/project_diff.rb +56 -0
  7. data/lib/xcodeproj/command/show.rb +60 -0
  8. data/lib/xcodeproj/command/sort.rb +44 -0
  9. data/lib/xcodeproj/command/target_diff.rb +43 -0
  10. data/lib/xcodeproj/command.rb +63 -0
  11. data/lib/xcodeproj/config/other_linker_flags_parser.rb +73 -0
  12. data/lib/xcodeproj/config.rb +386 -0
  13. data/lib/xcodeproj/constants.rb +465 -0
  14. data/lib/xcodeproj/differ.rb +239 -0
  15. data/lib/xcodeproj/gem_version.rb +5 -0
  16. data/lib/xcodeproj/helper.rb +30 -0
  17. data/lib/xcodeproj/plist.rb +94 -0
  18. data/lib/xcodeproj/project/case_converter.rb +90 -0
  19. data/lib/xcodeproj/project/object/build_configuration.rb +255 -0
  20. data/lib/xcodeproj/project/object/build_file.rb +84 -0
  21. data/lib/xcodeproj/project/object/build_phase.rb +369 -0
  22. data/lib/xcodeproj/project/object/build_rule.rb +109 -0
  23. data/lib/xcodeproj/project/object/configuration_list.rb +117 -0
  24. data/lib/xcodeproj/project/object/container_item_proxy.rb +116 -0
  25. data/lib/xcodeproj/project/object/file_reference.rb +338 -0
  26. data/lib/xcodeproj/project/object/group.rb +506 -0
  27. data/lib/xcodeproj/project/object/helpers/build_settings_array_settings_by_object_version.rb +72 -0
  28. data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +245 -0
  29. data/lib/xcodeproj/project/object/helpers/groupable_helper.rb +260 -0
  30. data/lib/xcodeproj/project/object/native_target.rb +751 -0
  31. data/lib/xcodeproj/project/object/reference_proxy.rb +86 -0
  32. data/lib/xcodeproj/project/object/root_object.rb +100 -0
  33. data/lib/xcodeproj/project/object/swift_package_product_dependency.rb +29 -0
  34. data/lib/xcodeproj/project/object/swift_package_remote_reference.rb +33 -0
  35. data/lib/xcodeproj/project/object/target_dependency.rb +94 -0
  36. data/lib/xcodeproj/project/object.rb +534 -0
  37. data/lib/xcodeproj/project/object_attributes.rb +522 -0
  38. data/lib/xcodeproj/project/object_dictionary.rb +210 -0
  39. data/lib/xcodeproj/project/object_list.rb +223 -0
  40. data/lib/xcodeproj/project/project_helper.rb +341 -0
  41. data/lib/xcodeproj/project/uuid_generator.rb +132 -0
  42. data/lib/xcodeproj/project.rb +874 -0
  43. data/lib/xcodeproj/scheme/abstract_scheme_action.rb +100 -0
  44. data/lib/xcodeproj/scheme/analyze_action.rb +19 -0
  45. data/lib/xcodeproj/scheme/archive_action.rb +59 -0
  46. data/lib/xcodeproj/scheme/build_action.rb +298 -0
  47. data/lib/xcodeproj/scheme/buildable_product_runnable.rb +55 -0
  48. data/lib/xcodeproj/scheme/buildable_reference.rb +129 -0
  49. data/lib/xcodeproj/scheme/command_line_arguments.rb +162 -0
  50. data/lib/xcodeproj/scheme/environment_variables.rb +170 -0
  51. data/lib/xcodeproj/scheme/execution_action.rb +86 -0
  52. data/lib/xcodeproj/scheme/launch_action.rb +179 -0
  53. data/lib/xcodeproj/scheme/location_scenario_reference.rb +49 -0
  54. data/lib/xcodeproj/scheme/macro_expansion.rb +34 -0
  55. data/lib/xcodeproj/scheme/profile_action.rb +57 -0
  56. data/lib/xcodeproj/scheme/remote_runnable.rb +92 -0
  57. data/lib/xcodeproj/scheme/send_email_action_content.rb +84 -0
  58. data/lib/xcodeproj/scheme/shell_script_action_content.rb +77 -0
  59. data/lib/xcodeproj/scheme/test_action.rb +394 -0
  60. data/lib/xcodeproj/scheme/xml_element_wrapper.rb +82 -0
  61. data/lib/xcodeproj/scheme.rb +375 -0
  62. data/lib/xcodeproj/user_interface.rb +22 -0
  63. data/lib/xcodeproj/workspace/file_reference.rb +79 -0
  64. data/lib/xcodeproj/workspace/group_reference.rb +67 -0
  65. data/lib/xcodeproj/workspace/reference.rb +40 -0
  66. data/lib/xcodeproj/workspace.rb +277 -0
  67. data/lib/xcodeproj/xcodebuild_helper.rb +108 -0
  68. data/lib/xcodeproj.rb +29 -0
  69. metadata +208 -0
@@ -0,0 +1,751 @@
1
+ module Xcodeproj
2
+ class Project
3
+ module Object
4
+ class AbstractTarget < AbstractObject
5
+ # @!group Attributes
6
+
7
+ # @return [String] The name of the Target.
8
+ #
9
+ attribute :name, String
10
+
11
+ # @return [String] the name of the build product.
12
+ #
13
+ attribute :product_name, String
14
+
15
+ # @return [String] Comments associated with this target.
16
+ #
17
+ # This is apparently no longer used by Xcode.
18
+ #
19
+ attribute :comments, String
20
+
21
+ # @return [XCConfigurationList] the list of the build configurations of
22
+ # the target. This list commonly include two configurations
23
+ # `Debug` and `Release`.
24
+ #
25
+ has_one :build_configuration_list, XCConfigurationList
26
+
27
+ # @return [ObjectList<PBXTargetDependency>] the targets necessary to
28
+ # build this target.
29
+ #
30
+ has_many :dependencies, PBXTargetDependency
31
+
32
+ public
33
+
34
+ # @!group Helpers
35
+ #--------------------------------------#
36
+
37
+ # Gets the value for the given build setting in all the build
38
+ # configurations or the value inheriting the value from the project
39
+ # ones if needed.
40
+ #
41
+ # @param [String] key
42
+ # the key of the build setting.
43
+ #
44
+ # @param [Bool] resolve_against_xcconfig
45
+ # whether the resolved setting should take in consideration any
46
+ # configuration file present.
47
+ #
48
+ # @return [Hash{String => String}] The value of the build setting
49
+ # grouped by the name of the build configuration.
50
+ #
51
+ # TODO: Full support for this would require to take into account
52
+ # the default values for the platform.
53
+ #
54
+ def resolved_build_setting(key, resolve_against_xcconfig = false)
55
+ target_settings = build_configuration_list.get_setting(key, resolve_against_xcconfig, self)
56
+ project_settings = project.build_configuration_list.get_setting(key, resolve_against_xcconfig)
57
+ target_settings.merge(project_settings) do |_key, target_val, proj_val|
58
+ target_includes_inherited = Constants::INHERITED_KEYWORDS.any? { |keyword| target_val.include?(keyword) } if target_val
59
+ if target_includes_inherited && proj_val
60
+ if target_val.is_a? String
61
+ target_val.gsub(Regexp.union(Constants::INHERITED_KEYWORDS), proj_val)
62
+ else
63
+ target_val.flat_map { |value| Constants::INHERITED_KEYWORDS.include?(value) ? proj_val : value }
64
+ end
65
+ else
66
+ target_val || proj_val
67
+ end
68
+ end
69
+ end
70
+
71
+ # Gets the value for the given build setting, properly inherited if
72
+ # need, if shared across the build configurations.
73
+ #
74
+ # @param [String] key
75
+ # the key of the build setting.
76
+ #
77
+ # @param [Boolean] resolve_against_xcconfig
78
+ # whether the resolved setting should take in consideration any
79
+ # configuration file present.
80
+ #
81
+ # @raise If the build setting has multiple values.
82
+ #
83
+ # @note As it is common not to have a setting with no value for
84
+ # custom build configurations nil keys are not considered to
85
+ # determine if the setting is unique. This is an heuristic
86
+ # which might not closely match Xcode behaviour.
87
+ #
88
+ # @return [String] The value of the build setting.
89
+ #
90
+ def common_resolved_build_setting(key, resolve_against_xcconfig: false)
91
+ values = resolved_build_setting(key, resolve_against_xcconfig).values.compact.uniq
92
+ if values.count <= 1
93
+ values.first
94
+ else
95
+ raise "[Xcodeproj] Consistency issue: build setting `#{key}` has multiple values: `#{resolved_build_setting(key)}`"
96
+ end
97
+ end
98
+
99
+ # @return [String] the SDK that the target should use.
100
+ #
101
+ def sdk
102
+ common_resolved_build_setting('SDKROOT')
103
+ end
104
+
105
+ # @return [Symbol] the name of the platform of the target.
106
+ #
107
+ def platform_name
108
+ return unless sdk
109
+ if sdk.include? 'iphoneos'
110
+ :ios
111
+ elsif sdk.include? 'macosx'
112
+ :osx
113
+ elsif sdk.include? 'appletvos'
114
+ :tvos
115
+ elsif sdk.include? 'watchos'
116
+ :watchos
117
+ end
118
+ end
119
+
120
+ # @return [String] the version of the SDK.
121
+ #
122
+ def sdk_version
123
+ return unless sdk
124
+ sdk.scan(/[0-9.]+/).first
125
+ end
126
+
127
+ # @visibility private
128
+ #
129
+ # @return [Hash<Symbol, String>]
130
+ # The name of the setting for the deployment target by platform
131
+ # name.
132
+ #
133
+ DEPLOYMENT_TARGET_SETTING_BY_PLATFORM_NAME = {
134
+ :ios => 'IPHONEOS_DEPLOYMENT_TARGET',
135
+ :osx => 'MACOSX_DEPLOYMENT_TARGET',
136
+ :tvos => 'TVOS_DEPLOYMENT_TARGET',
137
+ :watchos => 'WATCHOS_DEPLOYMENT_TARGET',
138
+ }.freeze
139
+
140
+ # @return [String] the deployment target of the target according to its
141
+ # platform.
142
+ #
143
+ def deployment_target
144
+ return unless setting = DEPLOYMENT_TARGET_SETTING_BY_PLATFORM_NAME[platform_name]
145
+ common_resolved_build_setting(setting)
146
+ end
147
+
148
+ # @param [String] deployment_target the deployment target to set for
149
+ # the target according to its platform.
150
+ #
151
+ def deployment_target=(deployment_target)
152
+ return unless setting = DEPLOYMENT_TARGET_SETTING_BY_PLATFORM_NAME[platform_name]
153
+ build_configurations.each do |config|
154
+ config.build_settings[setting] = deployment_target
155
+ end
156
+ end
157
+
158
+ # @return [ObjectList<XCBuildConfiguration>] the build
159
+ # configurations of the target.
160
+ #
161
+ def build_configurations
162
+ build_configuration_list.build_configurations
163
+ end
164
+
165
+ # Adds a new build configuration to the target and populates its with
166
+ # default settings according to the provided type if one doesn't
167
+ # exists.
168
+ #
169
+ # @note If a build configuration with the given name is already
170
+ # present no new build configuration is added.
171
+ #
172
+ # @param [String] name
173
+ # The name of the build configuration.
174
+ #
175
+ # @param [Symbol] type
176
+ # The type of the build configuration used to populate the build
177
+ # settings, must be :debug or :release.
178
+ #
179
+ # @return [XCBuildConfiguration] the created build configuration or the
180
+ # existing one with the same name.
181
+ #
182
+ def add_build_configuration(name, type)
183
+ if existing = build_configuration_list[name]
184
+ existing
185
+ else
186
+ build_configuration = project.new(XCBuildConfiguration)
187
+ build_configuration.name = name
188
+ product_type = self.product_type if respond_to?(:product_type)
189
+ build_configuration.build_settings = ProjectHelper.common_build_settings(type, platform_name, deployment_target, product_type)
190
+ build_configuration_list.build_configurations << build_configuration
191
+ build_configuration
192
+ end
193
+ end
194
+
195
+ # @param [String] build_configuration_name
196
+ # the name of a build configuration.
197
+ #
198
+ # @return [Hash] the build settings of the build configuration with the
199
+ # given name.
200
+ #
201
+ #
202
+ def build_settings(build_configuration_name)
203
+ build_configuration_list.build_settings(build_configuration_name)
204
+ end
205
+
206
+ # @!group Build Phases Helpers
207
+
208
+ # @return [PBXFrameworksBuildPhase]
209
+ # the frameworks build phases of the target.
210
+ #
211
+ def frameworks_build_phases
212
+ build_phases.find { |bp| bp.class == PBXFrameworksBuildPhase }
213
+ end
214
+
215
+ # @return [Array<PBXCopyFilesBuildPhase>]
216
+ # the copy files build phases of the target.
217
+ #
218
+ def copy_files_build_phases
219
+ build_phases.grep(PBXCopyFilesBuildPhase)
220
+ end
221
+
222
+ # @return [Array<PBXShellScriptBuildPhase>]
223
+ # the shell script build phases of the target.
224
+ #
225
+ def shell_script_build_phases
226
+ build_phases.grep(PBXShellScriptBuildPhase)
227
+ end
228
+
229
+ # Adds a dependency on the given target.
230
+ #
231
+ # @param [AbstractTarget] target
232
+ # the target which should be added to the dependencies list of
233
+ # the receiver. The target may be a target of this target's
234
+ # project or of a subproject of this project. Note that the
235
+ # subproject must already be added to this target's project.
236
+ #
237
+ # @return [void]
238
+ #
239
+ def add_dependency(target)
240
+ unless dependency_for_target(target)
241
+ container_proxy = project.new(Xcodeproj::Project::PBXContainerItemProxy)
242
+ if target.project == project
243
+ container_proxy.container_portal = project.root_object.uuid
244
+ else
245
+ subproject_reference = project.reference_for_path(target.project.path)
246
+ raise ArgumentError, 'add_dependency received target that belongs to a project that is not this project and is not a subproject of this project' unless subproject_reference
247
+ container_proxy.container_portal = subproject_reference.uuid
248
+ end
249
+ container_proxy.proxy_type = Constants::PROXY_TYPES[:native_target]
250
+ container_proxy.remote_global_id_string = target.uuid
251
+ container_proxy.remote_info = target.name
252
+
253
+ dependency = project.new(Xcodeproj::Project::PBXTargetDependency)
254
+ dependency.name = target.name
255
+ dependency.target = target if target.project == project
256
+ dependency.target_proxy = container_proxy
257
+
258
+ dependencies << dependency
259
+ end
260
+ end
261
+
262
+ # Checks whether this target has a dependency on the given target.
263
+ #
264
+ # @param [AbstractTarget] target
265
+ # the target to search for.
266
+ #
267
+ # @return [PBXTargetDependency]
268
+ #
269
+ def dependency_for_target(target)
270
+ dependencies.find do |dep|
271
+ if dep.target_proxy.remote?
272
+ subproject_reference = project.reference_for_path(target.project.path)
273
+ uuid = subproject_reference.uuid if subproject_reference
274
+ dep.target_proxy.remote_global_id_string == target.uuid && dep.target_proxy.container_portal == uuid
275
+ else
276
+ dep.target.uuid == target.uuid
277
+ end
278
+ end
279
+ end
280
+
281
+ # Creates a new copy files build phase.
282
+ #
283
+ # @param [String] name
284
+ # an optional name for the phase.
285
+ #
286
+ # @return [PBXCopyFilesBuildPhase] the new phase.
287
+ #
288
+ def new_copy_files_build_phase(name = nil)
289
+ phase = project.new(PBXCopyFilesBuildPhase)
290
+ phase.name = name
291
+ build_phases << phase
292
+ phase
293
+ end
294
+
295
+ # Creates a new shell script build phase.
296
+ #
297
+ # @param (see #new_copy_files_build_phase)
298
+ #
299
+ # @return [PBXShellScriptBuildPhase] the new phase.
300
+ #
301
+ def new_shell_script_build_phase(name = nil)
302
+ phase = project.new(PBXShellScriptBuildPhase)
303
+ phase.name = name
304
+ build_phases << phase
305
+ phase
306
+ end
307
+
308
+ public
309
+
310
+ # @!group System frameworks
311
+ #--------------------------------------#
312
+
313
+ # Adds a file reference for one or more system framework to the project
314
+ # if needed and adds them to the Frameworks build phases.
315
+ #
316
+ # @param [Array<String>, String] names
317
+ # The name or the list of the names of the framework.
318
+ #
319
+ # @note Xcode behaviour is following: if the target has the same SDK
320
+ # of the project it adds the reference relative to the SDK root
321
+ # otherwise the reference is added relative to the Developer
322
+ # directory. This can create confusion or duplication of the
323
+ # references of frameworks linked by iOS and OS X targets. For
324
+ # this reason the new Xcodeproj behaviour is to add the
325
+ # frameworks in a subgroup according to the platform.
326
+ #
327
+ # @return [Array<PBXFileReference>] An array of the newly created file
328
+ # references.
329
+ #
330
+ def add_system_framework(names)
331
+ Array(names).map do |name|
332
+ case platform_name
333
+ when :ios
334
+ group = project.frameworks_group['iOS'] || project.frameworks_group.new_group('iOS')
335
+ path_sdk_name = 'iPhoneOS'
336
+ path_sdk_version = sdk_version || Constants::LAST_KNOWN_IOS_SDK
337
+ when :osx
338
+ group = project.frameworks_group['OS X'] || project.frameworks_group.new_group('OS X')
339
+ path_sdk_name = 'MacOSX'
340
+ path_sdk_version = sdk_version || Constants::LAST_KNOWN_OSX_SDK
341
+ when :tvos
342
+ group = project.frameworks_group['tvOS'] || project.frameworks_group.new_group('tvOS')
343
+ path_sdk_name = 'AppleTVOS'
344
+ path_sdk_version = sdk_version || Constants::LAST_KNOWN_TVOS_SDK
345
+ when :watchos
346
+ group = project.frameworks_group['watchOS'] || project.frameworks_group.new_group('watchOS')
347
+ path_sdk_name = 'WatchOS'
348
+ path_sdk_version = sdk_version || Constants::LAST_KNOWN_WATCHOS_SDK
349
+ else
350
+ raise 'Unknown platform for target'
351
+ end
352
+
353
+ path = "Platforms/#{path_sdk_name}.platform/Developer/SDKs/#{path_sdk_name}#{path_sdk_version}.sdk/System/Library/Frameworks/#{name}.framework"
354
+ unless ref = group.find_file_by_path(path)
355
+ ref = group.new_file(path, :developer_dir)
356
+ end
357
+ frameworks_build_phase.add_file_reference(ref, true)
358
+ ref
359
+ end
360
+ end
361
+ alias_method :add_system_frameworks, :add_system_framework
362
+
363
+ # Adds a file reference for one or more system dylib libraries to the project
364
+ # if needed and adds them to the Frameworks build phases.
365
+ #
366
+ # @param [Array<String>, String] names
367
+ # The name or the list of the names of the libraries.
368
+ #
369
+ # @return [void]
370
+ #
371
+ def add_system_library(names)
372
+ add_system_library_extension(names, 'dylib')
373
+ end
374
+ alias_method :add_system_libraries, :add_system_library
375
+
376
+ def add_system_library_extension(names, extension)
377
+ Array(names).each do |name|
378
+ path = "usr/lib/lib#{name}.#{extension}"
379
+ files = project.frameworks_group.files
380
+ unless reference = files.find { |ref| ref.path == path }
381
+ reference = project.frameworks_group.new_file(path, :sdk_root)
382
+ end
383
+ frameworks_build_phase.add_file_reference(reference, true)
384
+ reference
385
+ end
386
+ end
387
+ private :add_system_library_extension
388
+
389
+ # Adds a file reference for one or more system tbd libraries to the project
390
+ # if needed and adds them to the Frameworks build phases.
391
+ #
392
+ # @param [Array<String>, String] names
393
+ # The name or the list of the names of the libraries.
394
+ #
395
+ # @return [void]
396
+ #
397
+ def add_system_library_tbd(names)
398
+ add_system_library_extension(names, 'tbd')
399
+ end
400
+ alias_method :add_system_libraries_tbd, :add_system_library_tbd
401
+
402
+ public
403
+
404
+ # @!group AbstractObject Hooks
405
+ #--------------------------------------#
406
+
407
+ # @return [Hash{String => Hash}] A hash suitable to display the object
408
+ # to the user.
409
+ #
410
+ def pretty_print
411
+ {
412
+ display_name => {
413
+ 'Build Phases' => build_phases.map(&:pretty_print),
414
+ 'Build Configurations' => build_configurations.map(&:pretty_print),
415
+ },
416
+ }
417
+ end
418
+ end
419
+
420
+ #-----------------------------------------------------------------------#
421
+
422
+ # Represents a target handled by Xcode.
423
+ #
424
+ class PBXNativeTarget < AbstractTarget
425
+ # @!group Attributes
426
+
427
+ # @return [PBXBuildRule] the build rules of this target.
428
+ #
429
+ has_many :build_rules, PBXBuildRule
430
+
431
+ # @return [String] the build product type identifier.
432
+ #
433
+ attribute :product_type, String
434
+
435
+ # @return [PBXFileReference] the reference to the product file.
436
+ #
437
+ has_one :product_reference, PBXFileReference
438
+
439
+ # @return [ObjectList<XCSwiftPackageProductDependency>] the Swift package products necessary to
440
+ # build this target.
441
+ #
442
+ has_many :package_product_dependencies, XCSwiftPackageProductDependency
443
+
444
+ # @return [String] the install path of the product.
445
+ #
446
+ attribute :product_install_path, String
447
+
448
+ # @return [ObjectList<AbstractBuildPhase>] the build phases of the
449
+ # target.
450
+ #
451
+ # @note Apparently only PBXCopyFilesBuildPhase and
452
+ # PBXShellScriptBuildPhase can appear multiple times in a
453
+ # target.
454
+ #
455
+ has_many :build_phases, AbstractBuildPhase
456
+
457
+ public
458
+
459
+ # @!group Helpers
460
+ #--------------------------------------#
461
+
462
+ # @return [Symbol] The type of the target expressed as a symbol.
463
+ #
464
+ def symbol_type
465
+ Constants::PRODUCT_TYPE_UTI.key(product_type)
466
+ end
467
+
468
+ # @return [Boolean] Whether the target is a test target.
469
+ #
470
+ def test_target_type?
471
+ case symbol_type
472
+ when :octest_bundle, :unit_test_bundle, :ui_test_bundle
473
+ true
474
+ else
475
+ false
476
+ end
477
+ end
478
+
479
+ # @return [Boolean] Whether the target is an extension.
480
+ #
481
+ def extension_target_type?
482
+ case symbol_type
483
+ when :app_extension, :watch_extension, :watch2_extension, :tv_extension, :messages_extension
484
+ true
485
+ else
486
+ false
487
+ end
488
+ end
489
+
490
+ # @return [Boolean] Whether the target is launchable.
491
+ #
492
+ def launchable_target_type?
493
+ case symbol_type
494
+ when :application, :command_line_tool
495
+ true
496
+ else
497
+ false
498
+ end
499
+ end
500
+
501
+ # Adds source files to the target.
502
+ #
503
+ # @param [Array<PBXFileReference>] file_references
504
+ # the files references of the source files that should be added
505
+ # to the target.
506
+ #
507
+ # @param [String] compiler_flags
508
+ # the compiler flags for the source files.
509
+ #
510
+ # @yield_param [PBXBuildFile] each created build file.
511
+ #
512
+ # @return [Array<PBXBuildFile>] the created build files.
513
+ #
514
+ def add_file_references(file_references, compiler_flags = {})
515
+ file_references.map do |file|
516
+ extension = File.extname(file.path).downcase
517
+ header_extensions = Constants::HEADER_FILES_EXTENSIONS
518
+ is_header_phase = header_extensions.include?(extension)
519
+ phase = is_header_phase ? headers_build_phase : source_build_phase
520
+
521
+ unless build_file = phase.build_file(file)
522
+ build_file = project.new(PBXBuildFile)
523
+ build_file.file_ref = file
524
+ phase.files << build_file
525
+ end
526
+
527
+ if compiler_flags && !compiler_flags.empty? && !is_header_phase
528
+ (build_file.settings ||= {}).merge!('COMPILER_FLAGS' => compiler_flags) do |_, old, new|
529
+ [old, new].compact.join(' ')
530
+ end
531
+ end
532
+
533
+ yield build_file if block_given?
534
+
535
+ build_file
536
+ end
537
+ end
538
+
539
+ # Adds resource files to the resources build phase of the target.
540
+ #
541
+ # @param [Array<PBXFileReference>] resource_file_references
542
+ # the files references of the resources to the target.
543
+ #
544
+ # @return [void]
545
+ #
546
+ def add_resources(resource_file_references)
547
+ resource_file_references.each do |file|
548
+ next if resources_build_phase.include?(file)
549
+ build_file = project.new(PBXBuildFile)
550
+ build_file.file_ref = file
551
+ resources_build_phase.files << build_file
552
+ end
553
+ end
554
+
555
+ # Adds on demand resources to the resources build phase of the target.
556
+ #
557
+ # @param {String => [Array<PBXFileReference>]} on_demand_resource_tag_files
558
+ # the files references of the on demand resources to add to the target keyed by the tag.
559
+ #
560
+ # @return [void]
561
+ #
562
+ def add_on_demand_resources(on_demand_resource_tag_files)
563
+ on_demand_resource_tag_files.each do |tag, file_refs|
564
+ file_refs.each do |file_ref|
565
+ if resources_build_phase.include?(file_ref)
566
+ existing_build_file = resources_build_phase.build_file(file_ref)
567
+ existing_build_file.settings ||= {}
568
+ existing_build_file.settings['ASSET_TAGS'] ||= []
569
+ existing_build_file.settings['ASSET_TAGS'] << tag
570
+ existing_build_file.settings['ASSET_TAGS'].uniq!
571
+ next
572
+ end
573
+ build_file = resources_build_phase.add_file_reference(file_ref, true)
574
+ build_file.settings = (build_file.settings ||= {}).merge('ASSET_TAGS' => [tag])
575
+ end
576
+ end
577
+ end
578
+
579
+ # Remove on demand resources from the resources build phase of the target.
580
+ #
581
+ # @param {String => [Array<PBXFileReference>]} on_demand_resource_tag_files
582
+ # the files references of the on demand resources to add to the target keyed by the tag.
583
+ #
584
+ # @return [void]
585
+ #
586
+ def remove_on_demand_resources(on_demand_resource_tag_files)
587
+ on_demand_resource_tag_files.each do |tag, file_refs|
588
+ file_refs.each do |file_ref|
589
+ build_file = resources_build_phase.build_file(file_ref)
590
+ next if build_file.nil?
591
+ asset_tags = build_file.settings['ASSET_TAGS']
592
+ asset_tags.delete(tag)
593
+ resources_build_phase.remove_file_reference(file_ref) if asset_tags.empty?
594
+ end
595
+ end
596
+ end
597
+
598
+ # Finds or creates the headers build phase of the target.
599
+ #
600
+ # @note A target should have only one headers build phase.
601
+ #
602
+ # @return [PBXHeadersBuildPhase] the headers build phase.
603
+ #
604
+ def headers_build_phase
605
+ find_or_create_build_phase_by_class(PBXHeadersBuildPhase)
606
+ end
607
+
608
+ # Finds or creates the source build phase of the target.
609
+ #
610
+ # @note A target should have only one source build phase.
611
+ #
612
+ # @return [PBXSourcesBuildPhase] the source build phase.
613
+ #
614
+ def source_build_phase
615
+ find_or_create_build_phase_by_class(PBXSourcesBuildPhase)
616
+ end
617
+
618
+ # Finds or creates the frameworks build phase of the target.
619
+ #
620
+ # @note A target should have only one frameworks build phase.
621
+ #
622
+ # @return [PBXFrameworksBuildPhase] the frameworks build phase.
623
+ #
624
+ def frameworks_build_phase
625
+ find_or_create_build_phase_by_class(PBXFrameworksBuildPhase)
626
+ end
627
+
628
+ # Finds or creates the resources build phase of the target.
629
+ #
630
+ # @note A target should have only one resources build phase.
631
+ #
632
+ # @return [PBXResourcesBuildPhase] the resources build phase.
633
+ #
634
+ def resources_build_phase
635
+ find_or_create_build_phase_by_class(PBXResourcesBuildPhase)
636
+ end
637
+
638
+ private
639
+
640
+ # @!group Internal Helpers
641
+ #--------------------------------------#
642
+
643
+ # Find or create a build phase by a given class
644
+ #
645
+ # @param [Class] phase_class the class of the build phase to find or create.
646
+ #
647
+ # @return [AbstractBuildPhase] the build phase whose class match the given phase_class.
648
+ #
649
+ def find_or_create_build_phase_by_class(phase_class)
650
+ @phases ||= {}
651
+ unless phase_class < AbstractBuildPhase
652
+ raise ArgumentError, "#{phase_class} must be a subclass of #{AbstractBuildPhase.class}"
653
+ end
654
+ @phases[phase_class] ||= build_phases.find { |bp| bp.class == phase_class } ||
655
+ project.new(phase_class).tap { |bp| build_phases << bp }
656
+ end
657
+
658
+ public
659
+
660
+ # @!group AbstractObject Hooks
661
+ #--------------------------------------#
662
+
663
+ # Sorts the to many attributes of the object according to the display
664
+ # name.
665
+ #
666
+ # Build phases are not sorted as they order is relevant.
667
+ #
668
+ def sort(_options = nil)
669
+ attributes_to_sort = to_many_attributes.reject { |attr| attr.name == :build_phases }
670
+ attributes_to_sort.each do |attrb|
671
+ list = attrb.get_value(self)
672
+ list.sort! do |x, y|
673
+ x.display_name <=> y.display_name
674
+ end
675
+ end
676
+ end
677
+
678
+ def to_hash_as(method = :to_hash)
679
+ hash_as = super
680
+ if !hash_as['packageProductDependencies'].nil? && hash_as['packageProductDependencies'].empty?
681
+ hash_as.delete('packageProductDependencies')
682
+ end
683
+ hash_as
684
+ end
685
+
686
+ def to_ascii_plist
687
+ plist = super
688
+ if !plist.value['packageProductDependencies'].nil? && plist.value['packageProductDependencies'].empty?
689
+ plist.value.delete('packageProductDependencies')
690
+ end
691
+ plist
692
+ end
693
+ end
694
+
695
+ #-----------------------------------------------------------------------#
696
+
697
+ # Represents a target that only consists in a aggregate of targets.
698
+ #
699
+ # @todo Apparently it can't have build rules.
700
+ #
701
+ class PBXAggregateTarget < AbstractTarget
702
+ # @!group Attributes
703
+
704
+ # @return [PBXBuildRule] the build phases of the target.
705
+ #
706
+ # @note Apparently only PBXCopyFilesBuildPhase and
707
+ # PBXShellScriptBuildPhase can appear multiple times in a
708
+ # target.
709
+ #
710
+ has_many :build_phases, [PBXCopyFilesBuildPhase, PBXShellScriptBuildPhase]
711
+ end
712
+
713
+ #-----------------------------------------------------------------------#
714
+
715
+ # Represents a legacy target which uses an external build tool.
716
+ #
717
+ # Apparently it can't have any build phase but the attribute can be
718
+ # present.
719
+ #
720
+ class PBXLegacyTarget < AbstractTarget
721
+ # @!group Attributes
722
+
723
+ # @return [String] e.g "Dir"
724
+ #
725
+ attribute :build_working_directory, String
726
+
727
+ # @return [String] e.g "$(ACTION)"
728
+ #
729
+ attribute :build_arguments_string, String
730
+
731
+ # @return [String] e.g "1"
732
+ #
733
+ attribute :pass_build_settings_in_environment, String
734
+
735
+ # @return [String] e.g "/usr/bin/make"
736
+ #
737
+ attribute :build_tool_path, String
738
+
739
+ # @return [PBXBuildRule] the build phases of the target.
740
+ #
741
+ # @note Apparently only PBXCopyFilesBuildPhase and
742
+ # PBXShellScriptBuildPhase can appear multiple times in a
743
+ # target.
744
+ #
745
+ has_many :build_phases, AbstractBuildPhase
746
+ end
747
+
748
+ #-----------------------------------------------------------------------#
749
+ end
750
+ end
751
+ end