plasmo_xcodeproj 1.21.1

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