plasmo_xcodeproj 1.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +95 -0
- data/bin/xcodeproj +10 -0
- data/lib/xcodeproj/command/config_dump.rb +91 -0
- data/lib/xcodeproj/command/project_diff.rb +56 -0
- data/lib/xcodeproj/command/show.rb +60 -0
- data/lib/xcodeproj/command/sort.rb +44 -0
- data/lib/xcodeproj/command/target_diff.rb +43 -0
- data/lib/xcodeproj/command.rb +63 -0
- data/lib/xcodeproj/config/other_linker_flags_parser.rb +73 -0
- data/lib/xcodeproj/config.rb +386 -0
- data/lib/xcodeproj/constants.rb +465 -0
- data/lib/xcodeproj/differ.rb +239 -0
- data/lib/xcodeproj/gem_version.rb +5 -0
- data/lib/xcodeproj/helper.rb +30 -0
- data/lib/xcodeproj/plist.rb +94 -0
- data/lib/xcodeproj/project/case_converter.rb +90 -0
- data/lib/xcodeproj/project/object/build_configuration.rb +255 -0
- data/lib/xcodeproj/project/object/build_file.rb +84 -0
- data/lib/xcodeproj/project/object/build_phase.rb +369 -0
- data/lib/xcodeproj/project/object/build_rule.rb +109 -0
- data/lib/xcodeproj/project/object/configuration_list.rb +117 -0
- data/lib/xcodeproj/project/object/container_item_proxy.rb +116 -0
- data/lib/xcodeproj/project/object/file_reference.rb +338 -0
- data/lib/xcodeproj/project/object/group.rb +506 -0
- data/lib/xcodeproj/project/object/helpers/build_settings_array_settings_by_object_version.rb +72 -0
- data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +245 -0
- data/lib/xcodeproj/project/object/helpers/groupable_helper.rb +260 -0
- data/lib/xcodeproj/project/object/native_target.rb +751 -0
- data/lib/xcodeproj/project/object/reference_proxy.rb +86 -0
- data/lib/xcodeproj/project/object/root_object.rb +100 -0
- data/lib/xcodeproj/project/object/swift_package_product_dependency.rb +29 -0
- data/lib/xcodeproj/project/object/swift_package_remote_reference.rb +33 -0
- data/lib/xcodeproj/project/object/target_dependency.rb +94 -0
- data/lib/xcodeproj/project/object.rb +534 -0
- data/lib/xcodeproj/project/object_attributes.rb +522 -0
- data/lib/xcodeproj/project/object_dictionary.rb +210 -0
- data/lib/xcodeproj/project/object_list.rb +223 -0
- data/lib/xcodeproj/project/project_helper.rb +341 -0
- data/lib/xcodeproj/project/uuid_generator.rb +132 -0
- data/lib/xcodeproj/project.rb +874 -0
- data/lib/xcodeproj/scheme/abstract_scheme_action.rb +100 -0
- data/lib/xcodeproj/scheme/analyze_action.rb +19 -0
- data/lib/xcodeproj/scheme/archive_action.rb +59 -0
- data/lib/xcodeproj/scheme/build_action.rb +298 -0
- data/lib/xcodeproj/scheme/buildable_product_runnable.rb +55 -0
- data/lib/xcodeproj/scheme/buildable_reference.rb +129 -0
- data/lib/xcodeproj/scheme/command_line_arguments.rb +162 -0
- data/lib/xcodeproj/scheme/environment_variables.rb +170 -0
- data/lib/xcodeproj/scheme/execution_action.rb +86 -0
- data/lib/xcodeproj/scheme/launch_action.rb +179 -0
- data/lib/xcodeproj/scheme/location_scenario_reference.rb +49 -0
- data/lib/xcodeproj/scheme/macro_expansion.rb +34 -0
- data/lib/xcodeproj/scheme/profile_action.rb +57 -0
- data/lib/xcodeproj/scheme/remote_runnable.rb +92 -0
- data/lib/xcodeproj/scheme/send_email_action_content.rb +84 -0
- data/lib/xcodeproj/scheme/shell_script_action_content.rb +77 -0
- data/lib/xcodeproj/scheme/test_action.rb +394 -0
- data/lib/xcodeproj/scheme/xml_element_wrapper.rb +82 -0
- data/lib/xcodeproj/scheme.rb +375 -0
- data/lib/xcodeproj/user_interface.rb +22 -0
- data/lib/xcodeproj/workspace/file_reference.rb +79 -0
- data/lib/xcodeproj/workspace/group_reference.rb +67 -0
- data/lib/xcodeproj/workspace/reference.rb +40 -0
- data/lib/xcodeproj/workspace.rb +277 -0
- data/lib/xcodeproj/xcodebuild_helper.rb +108 -0
- data/lib/xcodeproj.rb +29 -0
- 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
|