xcodeproj 1.7.0 → 1.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xcodeproj/command/config_dump.rb +5 -1
  3. data/lib/xcodeproj/config.rb +3 -1
  4. data/lib/xcodeproj/config/other_linker_flags_parser.rb +2 -2
  5. data/lib/xcodeproj/constants.rb +70 -44
  6. data/lib/xcodeproj/gem_version.rb +1 -1
  7. data/lib/xcodeproj/project.rb +23 -2
  8. data/lib/xcodeproj/project/object.rb +3 -1
  9. data/lib/xcodeproj/project/object/build_configuration.rb +96 -35
  10. data/lib/xcodeproj/project/object/build_file.rb +9 -1
  11. data/lib/xcodeproj/project/object/build_phase.rb +45 -4
  12. data/lib/xcodeproj/project/object/build_rule.rb +12 -0
  13. data/lib/xcodeproj/project/object/helpers/file_references_factory.rb +11 -4
  14. data/lib/xcodeproj/project/object/helpers/groupable_helper.rb +13 -6
  15. data/lib/xcodeproj/project/object/native_target.rb +52 -9
  16. data/lib/xcodeproj/project/object/root_object.rb +15 -2
  17. data/lib/xcodeproj/project/object/swift_package_product_dependency.rb +19 -0
  18. data/lib/xcodeproj/project/object/swift_package_remote_reference.rb +19 -0
  19. data/lib/xcodeproj/project/object/target_dependency.rb +9 -0
  20. data/lib/xcodeproj/project/project_helper.rb +2 -1
  21. data/lib/xcodeproj/project/uuid_generator.rb +41 -19
  22. data/lib/xcodeproj/scheme.rb +6 -4
  23. data/lib/xcodeproj/scheme/build_action.rb +21 -0
  24. data/lib/xcodeproj/scheme/buildable_reference.rb +3 -1
  25. data/lib/xcodeproj/scheme/launch_action.rb +50 -0
  26. data/lib/xcodeproj/scheme/test_action.rb +36 -1
  27. data/lib/xcodeproj/workspace.rb +16 -7
  28. data/lib/xcodeproj/workspace/file_reference.rb +7 -12
  29. data/lib/xcodeproj/workspace/group_reference.rb +18 -15
  30. data/lib/xcodeproj/workspace/reference.rb +40 -0
  31. metadata +11 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a926103d6bbeeea7900326a53221df41a158228484f36dce922c54eadfa741b1
4
- data.tar.gz: d15b7d1db2f2ad119b4660a5cad26476930866dd1bee818e770ce8d2c1b8ee4e
3
+ metadata.gz: 72f6eb6645b557dea09ae4be0fd400e48b0d4f1e42c31b7b61af87fd3c31a9cd
4
+ data.tar.gz: 0c0bcfa7abc28721f61f48e295198da53576e90d62161c50a6cc7333c1e94455
5
5
  SHA512:
6
- metadata.gz: 4886ecf3b87f33ab29072b8ae56b22bfc7810cd0feb9a460449c612d9c947befb7a101e56ffd3dadc03bda4268057f4a48d46cb2ad2dade6a645aa0a65ab5dff
7
- data.tar.gz: 85e155a16497eab7dafc72b1c4732cf32ed1ecf90cb10d08d144eea810dd79b99193782c5031609cb5be17e2f961526b4b087eb1196ae971e067083e2f81ab94
6
+ metadata.gz: 4f856d8cbee5b9b7b6bcbe7acce4034e8277fd05ac7caf0d2c317f75d622bb5c518dd2ddee67dac1c6a7fb2773aebb5a5de2cf2232af3fffc2353323fd08623d
7
+ data.tar.gz: 9cf088a34dea380b56716f005c35fc601d9b193a0a4908556d4f8ccc80408ee328755ccb767dc0ed50adc0bc3e822cc9bcdcdeac405c97fc514f6dd429f746ac
@@ -76,7 +76,11 @@ module Xcodeproj
76
76
 
77
77
  def dump_config_to_file(settings, file_path, includes = [])
78
78
  dir = @output_path + file_path + '..'
79
- dir.mkdir unless dir.exist?
79
+ dir.mkpath
80
+
81
+ settings = Hash[settings.map do |k, v|
82
+ [k, Array(v).join(' ')]
83
+ end]
80
84
 
81
85
  config = Config.new(settings)
82
86
  config.includes = includes
@@ -157,6 +157,8 @@ module Xcodeproj
157
157
  end
158
158
  end
159
159
 
160
+ alias_method :to_h, :to_hash
161
+
160
162
  # @return [Set<String>] The list of the frameworks required by this
161
163
  # settings file.
162
164
  #
@@ -275,7 +277,7 @@ module Xcodeproj
275
277
  string.split("\n").each do |line|
276
278
  uncommented_line = strip_comment(line)
277
279
  if include = extract_include(uncommented_line)
278
- @includes.push include
280
+ @includes.push normalized_xcconfig_path(include)
279
281
  else
280
282
  key, value = extract_key_value(uncommented_line)
281
283
  next unless key
@@ -55,13 +55,13 @@ module Xcodeproj
55
55
  # The other linker flags value.
56
56
  #
57
57
  def self.split(flags)
58
- flags.strip.shellsplit.map do |string|
58
+ flags.strip.shellsplit.flat_map do |string|
59
59
  if string =~ /\A-l.+/
60
60
  ['-l', string[2..-1]]
61
61
  else
62
62
  string
63
63
  end
64
- end.flatten
64
+ end
65
65
  end
66
66
  end
67
67
  end
@@ -4,17 +4,19 @@ module Xcodeproj
4
4
  module Constants
5
5
  # @return [String] The last known iOS SDK (stable).
6
6
  #
7
- LAST_KNOWN_IOS_SDK = '12.0'
7
+ LAST_KNOWN_IOS_SDK = '14.0'
8
8
 
9
9
  # @return [String] The last known OS X SDK (stable).
10
10
  #
11
- LAST_KNOWN_OSX_SDK = '10.14'
11
+ LAST_KNOWN_OSX_SDK = '10.15'
12
12
 
13
13
  # @return [String] The last known tvOS SDK (stable).
14
- LAST_KNOWN_TVOS_SDK = '12.0'
14
+ #
15
+ LAST_KNOWN_TVOS_SDK = '14.0'
15
16
 
16
17
  # @return [String] The last known watchOS SDK (stable).
17
- LAST_KNOWN_WATCHOS_SDK = '5.0'
18
+ #
19
+ LAST_KNOWN_WATCHOS_SDK = '7.0'
18
20
 
19
21
  # @return [String] The last known archive version to Xcodeproj.
20
22
  #
@@ -22,22 +24,23 @@ module Xcodeproj
22
24
 
23
25
  # @return [String] The last known Swift version (stable).
24
26
  #
25
- LAST_KNOWN_SWIFT_VERSION = '4.0'
27
+ LAST_KNOWN_SWIFT_VERSION = '5.0'
26
28
 
27
29
  # @return [String] The default object version for Xcodeproj.
30
+ #
28
31
  DEFAULT_OBJECT_VERSION = 46
29
32
 
30
33
  # @return [String] The last known object version to Xcodeproj.
31
34
  #
32
- LAST_KNOWN_OBJECT_VERSION = 51
35
+ LAST_KNOWN_OBJECT_VERSION = 54
33
36
 
34
37
  # @return [String] The last known object version to Xcodeproj.
35
38
  #
36
- LAST_UPGRADE_CHECK = '0930'
39
+ LAST_UPGRADE_CHECK = '1100'
37
40
 
38
41
  # @return [String] The last known object version to Xcodeproj.
39
42
  #
40
- LAST_SWIFT_UPGRADE_CHECK = '0930'
43
+ LAST_SWIFT_UPGRADE_CHECK = '1100'
41
44
 
42
45
  # @return [String] The version of `.xcscheme` files supported by Xcodeproj
43
46
  #
@@ -90,12 +93,14 @@ module Xcodeproj
90
93
  'app' => 'wrapper.application',
91
94
  'appex' => 'wrapper.app-extension',
92
95
  'bundle' => 'wrapper.plug-in',
96
+ 'cpp' => 'sourcecode.cpp.cpp',
93
97
  'dylib' => 'compiled.mach-o.dylib',
94
98
  'entitlements' => 'text.plist.entitlements',
95
99
  'framework' => 'wrapper.framework',
96
100
  'gif' => 'image.gif',
97
101
  'gpx' => 'text.xml',
98
102
  'h' => 'sourcecode.c.h',
103
+ 'hpp' => 'sourcecode.cpp.h',
99
104
  'm' => 'sourcecode.c.objc',
100
105
  'markdown' => 'text',
101
106
  'mdimporter' => 'wrapper.cfbundle',
@@ -120,44 +125,65 @@ module Xcodeproj
120
125
  'zip' => 'archive.zip',
121
126
  }.freeze
122
127
 
128
+ # @return [Hash] The compatibility version string for different object versions.
129
+ #
130
+ COMPATIBILITY_VERSION_BY_OBJECT_VERSION = {
131
+ 54 => 'Xcode 12.0',
132
+ 53 => 'Xcode 11.4',
133
+ 52 => 'Xcode 11.0',
134
+ 51 => 'Xcode 10.0',
135
+ 50 => 'Xcode 9.3',
136
+ 48 => 'Xcode 8.0',
137
+ 47 => 'Xcode 6.3',
138
+ 46 => 'Xcode 3.2',
139
+ 45 => 'Xcode 3.1',
140
+ }.freeze
141
+
123
142
  # @return [Hash] The uniform type identifier of various product types.
124
143
  #
125
144
  PRODUCT_TYPE_UTI = {
126
- :application => 'com.apple.product-type.application',
127
- :framework => 'com.apple.product-type.framework',
128
- :dynamic_library => 'com.apple.product-type.library.dynamic',
129
- :static_library => 'com.apple.product-type.library.static',
130
- :bundle => 'com.apple.product-type.bundle',
131
- :octest_bundle => 'com.apple.product-type.bundle',
132
- :unit_test_bundle => 'com.apple.product-type.bundle.unit-test',
133
- :ui_test_bundle => 'com.apple.product-type.bundle.ui-testing',
134
- :app_extension => 'com.apple.product-type.app-extension',
135
- :command_line_tool => 'com.apple.product-type.tool',
136
- :watch_app => 'com.apple.product-type.application.watchapp',
137
- :watch2_app => 'com.apple.product-type.application.watchapp2',
138
- :watch_extension => 'com.apple.product-type.watchkit-extension',
139
- :watch2_extension => 'com.apple.product-type.watchkit2-extension',
140
- :tv_extension => 'com.apple.product-type.tv-app-extension',
141
- :messages_application => 'com.apple.product-type.application.messages',
142
- :messages_extension => 'com.apple.product-type.app-extension.messages',
143
- :sticker_pack => 'com.apple.product-type.app-extension.messages-sticker-pack',
144
- :xpc_service => 'com.apple.product-type.xpc-service',
145
+ :application => 'com.apple.product-type.application',
146
+ :application_on_demand_install_capable => 'com.apple.product-type.application.on-demand-install-capable',
147
+ :framework => 'com.apple.product-type.framework',
148
+ :dynamic_library => 'com.apple.product-type.library.dynamic',
149
+ :static_library => 'com.apple.product-type.library.static',
150
+ :bundle => 'com.apple.product-type.bundle',
151
+ :octest_bundle => 'com.apple.product-type.bundle',
152
+ :unit_test_bundle => 'com.apple.product-type.bundle.unit-test',
153
+ :ui_test_bundle => 'com.apple.product-type.bundle.ui-testing',
154
+ :app_extension => 'com.apple.product-type.app-extension',
155
+ :command_line_tool => 'com.apple.product-type.tool',
156
+ :watch_app => 'com.apple.product-type.application.watchapp',
157
+ :watch2_app => 'com.apple.product-type.application.watchapp2',
158
+ :watch2_app_container => 'com.apple.product-type.application.watchapp2-container',
159
+ :watch_extension => 'com.apple.product-type.watchkit-extension',
160
+ :watch2_extension => 'com.apple.product-type.watchkit2-extension',
161
+ :tv_extension => 'com.apple.product-type.tv-app-extension',
162
+ :messages_application => 'com.apple.product-type.application.messages',
163
+ :messages_extension => 'com.apple.product-type.app-extension.messages',
164
+ :sticker_pack => 'com.apple.product-type.app-extension.messages-sticker-pack',
165
+ :xpc_service => 'com.apple.product-type.xpc-service',
145
166
  }.freeze
146
167
 
147
168
  # @return [Hash] The extensions or the various product UTIs.
148
169
  #
149
170
  PRODUCT_UTI_EXTENSIONS = {
150
- :application => 'app',
151
- :framework => 'framework',
152
- :dynamic_library => 'dylib',
153
- :static_library => 'a',
154
- :bundle => 'bundle',
155
- :octest_bundle => 'octest',
156
- :unit_test_bundle => 'xctest',
157
- :ui_test_bundle => 'xctest',
158
- :app_extension => 'appex',
159
- :watch2_extension => 'appex',
160
- :watch2_app => 'app',
171
+ :application => 'app',
172
+ :application_on_demand_install_capable => 'app',
173
+ :framework => 'framework',
174
+ :dynamic_library => 'dylib',
175
+ :static_library => 'a',
176
+ :bundle => 'bundle',
177
+ :octest_bundle => 'octest',
178
+ :unit_test_bundle => 'xctest',
179
+ :ui_test_bundle => 'xctest',
180
+ :app_extension => 'appex',
181
+ :messages_application => 'app',
182
+ :messages_extension => 'appex',
183
+ :sticker_pack => 'appex',
184
+ :watch2_extension => 'appex',
185
+ :watch2_app => 'app',
186
+ :watch2_app_container => 'app',
161
187
  }.freeze
162
188
 
163
189
  # @return [Hash] The common build settings grouped by platform, and build
@@ -178,11 +204,9 @@ module Xcodeproj
178
204
  }.freeze,
179
205
  [:ios] => {
180
206
  'SDKROOT' => 'iphoneos',
181
- 'CODE_SIGN_IDENTITY' => 'iPhone Developer',
182
207
  }.freeze,
183
208
  [:osx] => {
184
209
  'SDKROOT' => 'macosx',
185
- 'CODE_SIGN_IDENTITY' => '-',
186
210
  }.freeze,
187
211
  [:tvos] => {
188
212
  'SDKROOT' => 'appletvos',
@@ -231,7 +255,6 @@ module Xcodeproj
231
255
  [:debug, :static_library, :swift] => {
232
256
  }.freeze,
233
257
  [:framework] => {
234
- 'CODE_SIGN_IDENTITY' => '',
235
258
  'CURRENT_PROJECT_VERSION' => '1',
236
259
  'DEFINES_MODULE' => 'YES',
237
260
  'DYLIB_COMPATIBILITY_VERSION' => '1',
@@ -249,7 +272,6 @@ module Xcodeproj
249
272
  }.freeze,
250
273
  [:osx, :framework] => {
251
274
  'COMBINE_HIDPI_IMAGES' => 'YES',
252
- 'FRAMEWORK_VERSION' => 'A',
253
275
  'LD_RUNPATH_SEARCH_PATHS' => '$(inherited) @executable_path/../Frameworks @loader_path/Frameworks',
254
276
  }.freeze,
255
277
  [:watchos, :framework] => {
@@ -291,6 +313,7 @@ module Xcodeproj
291
313
  }.freeze,
292
314
  [:application] => {
293
315
  'ASSETCATALOG_COMPILER_APPICON_NAME' => 'AppIcon',
316
+ 'ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME' => 'AccentColor',
294
317
  }.freeze,
295
318
  [:ios, :application] => {
296
319
  'LD_RUNPATH_SEARCH_PATHS' => '$(inherited) @executable_path/Frameworks',
@@ -306,10 +329,12 @@ module Xcodeproj
306
329
  }.freeze,
307
330
  [:tvos, :application] => {
308
331
  'ASSETCATALOG_COMPILER_APPICON_NAME' => 'App Icon & Top Shelf Image',
309
- 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME' => 'LaunchImage',
310
332
  'LD_RUNPATH_SEARCH_PATHS' => '$(inherited) @executable_path/Frameworks',
311
333
  'TARGETED_DEVICE_FAMILY' => '3',
312
334
  }.freeze,
335
+ [:tvos, :application, :swift] => {
336
+ 'ENABLE_PREVIEWS' => 'YES',
337
+ }.freeze,
313
338
  [:watchos, :application, :swift] => {
314
339
  'ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES' => 'YES',
315
340
  }.freeze,
@@ -355,6 +380,7 @@ module Xcodeproj
355
380
  'CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF' => 'YES',
356
381
  'CLANG_WARN_OBJC_LITERAL_CONVERSION' => 'YES',
357
382
  'CLANG_WARN_OBJC_ROOT_CLASS' => 'YES_ERROR',
383
+ 'CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER' => 'YES',
358
384
  'CLANG_WARN_RANGE_LOOP_ANALYSIS' => 'YES',
359
385
  'CLANG_WARN_STRICT_PROTOTYPES' => 'YES',
360
386
  'CLANG_WARN_SUSPICIOUS_MOVE' => 'YES',
@@ -372,7 +398,7 @@ module Xcodeproj
372
398
  'GCC_WARN_UNUSED_VARIABLE' => 'YES',
373
399
  'MTL_FAST_MATH' => 'YES',
374
400
  'PRODUCT_NAME' => '$(TARGET_NAME)',
375
- 'SWIFT_VERSION' => '4.2',
401
+ 'SWIFT_VERSION' => '5.0',
376
402
  },
377
403
  :release => {
378
404
  'DEBUG_INFORMATION_FORMAT' => 'dwarf-with-dsym',
@@ -1,5 +1,5 @@
1
1
  module Xcodeproj
2
2
  # The version of the xcodeproj gem.
3
3
  #
4
- VERSION = '1.7.0'.freeze unless defined? Xcodeproj::VERSION
4
+ VERSION = '1.19.0'.freeze unless defined? Xcodeproj::VERSION
5
5
  end
@@ -80,6 +80,10 @@ module Xcodeproj
80
80
  unless skip_initialization
81
81
  initialize_from_scratch
82
82
  @object_version = object_version.to_s
83
+ unless Constants::COMPATIBILITY_VERSION_BY_OBJECT_VERSION.key?(object_version)
84
+ raise ArgumentError, "[Xcodeproj] Unable to find compatibility version string for object version `#{object_version}`."
85
+ end
86
+ root_object.compatibility_version = Constants::COMPATIBILITY_VERSION_BY_OBJECT_VERSION[object_version]
83
87
  end
84
88
  end
85
89
 
@@ -388,7 +392,24 @@ module Xcodeproj
388
392
  # @return [void]
389
393
  #
390
394
  def predictabilize_uuids
391
- UUIDGenerator.new(self).generate!
395
+ UUIDGenerator.new([self]).generate!
396
+ end
397
+
398
+ # Replaces all the UUIDs in the list of provided projects with deterministic MD5 checksums.
399
+ #
400
+ # @param [Array<Project>] projects
401
+ #
402
+ # @note The current sorting of the project is taken into account when
403
+ # generating the new UUIDs.
404
+ #
405
+ # @note This method should only be used for entirely machine-generated
406
+ # projects, as true UUIDs are useful for tracking changes in the
407
+ # project.
408
+ #
409
+ # @return [void]
410
+ #
411
+ def self.predictabilize_uuids(projects)
412
+ UUIDGenerator.new(projects).generate!
392
413
  end
393
414
 
394
415
  public
@@ -568,7 +589,7 @@ module Xcodeproj
568
589
  #
569
590
  def embedded_targets_in_native_target(native_target)
570
591
  native_targets.select do |target|
571
- host_targets_for_embedded_target(target).map(&:uuid).include? native_target.uuid
592
+ host_targets_for_embedded_target(target).any? { |host| host.uuid == native_target.uuid }
572
593
  end
573
594
  end
574
595
 
@@ -350,7 +350,7 @@ module Xcodeproj
350
350
  unless object = project.objects_by_uuid[uuid] || project.new_from_plist(uuid, objects_by_uuid_plist)
351
351
  UI.warn "`#{inspect}` attempted to initialize an object with " \
352
352
  "an unknown UUID. `#{uuid}` for attribute: " \
353
- "`#{attribute.name}`. This can be the result of a merge and " \
353
+ "`#{attribute.name}`. This can be the result of a merge and " \
354
354
  'the unknown UUID is being discarded.'
355
355
  end
356
356
  object
@@ -518,6 +518,8 @@ Xcodeproj::Constants::KNOWN_ISAS.each do |superclass_name, isas|
518
518
  end
519
519
 
520
520
  # Now load the concrete subclasses.
521
+ require 'xcodeproj/project/object/swift_package_remote_reference'
522
+ require 'xcodeproj/project/object/swift_package_product_dependency'
521
523
  require 'xcodeproj/project/object/build_configuration'
522
524
  require 'xcodeproj/project/object/build_file'
523
525
  require 'xcodeproj/project/object/build_phase'
@@ -6,6 +6,10 @@ module Xcodeproj
6
6
  # {PBXProject} or a {PBXNativeTarget}.
7
7
  #
8
8
  class XCBuildConfiguration < AbstractObject
9
+ MUTUAL_RECURSION_SENTINEL = 'xcodeproj.mutual_recursion_sentinel'.freeze
10
+
11
+ private_constant :MUTUAL_RECURSION_SENTINEL
12
+
9
13
  # @!group Attributes
10
14
 
11
15
  # @return [String] the name of the Target.
@@ -79,21 +83,36 @@ module Xcodeproj
79
83
  # the key of the build setting.
80
84
  #
81
85
  # @param [PBXNativeTarget] root_target
82
- # use this to resolve complete recursion between project and targets
86
+ # use this to resolve complete recursion between project and targets.
87
+ #
88
+ # @param [String] previous_key
89
+ # use this to resolve complete recursion between different build settings.
83
90
  #
84
91
  # @return [String] The value of the build setting
85
92
  #
86
- def resolve_build_setting(key, root_target = nil)
93
+ def resolve_build_setting(key, root_target = nil, previous_key = nil)
87
94
  setting = build_settings[key]
88
- setting = resolve_variable_substitution(key, setting, root_target) if setting.is_a?(String)
89
- config_setting = base_configuration_reference && config[key]
90
- config_setting = resolve_variable_substitution(key, config_setting, root_target) if config_setting.is_a?(String)
95
+ setting = resolve_variable_substitution(key, setting, root_target, previous_key)
96
+
97
+ config_setting = config[key]
98
+ config_setting = resolve_variable_substitution(key, config_setting, root_target, previous_key)
91
99
 
92
100
  project_setting = project.build_configuration_list[name]
93
- project_setting = nil if project_setting == self
101
+ project_setting = nil if equal?(project_setting)
94
102
  project_setting &&= project_setting.resolve_build_setting(key, root_target)
95
103
 
96
- [project_setting, config_setting, setting, ENV[key]].compact.reduce do |inherited, value|
104
+ defaults = {
105
+ 'CONFIGURATION' => name,
106
+ 'SRCROOT' => project.project_dir.to_s,
107
+ }
108
+
109
+ # if previous_key is nil, it means that we're back at the first call, so we can replace our sentinel string
110
+ # used to prevent recursion with nil
111
+ if previous_key.nil? && setting == MUTUAL_RECURSION_SENTINEL
112
+ setting = nil
113
+ end
114
+
115
+ [defaults[key], project_setting, config_setting, setting, ENV[key]].compact.reduce(nil) do |inherited, value|
97
116
  expand_build_setting(value, inherited)
98
117
  end
99
118
  end
@@ -102,15 +121,25 @@ module Xcodeproj
102
121
 
103
122
  private
104
123
 
124
+ VARIABLE_NAME_PATTERN =
125
+ '( # capture block
126
+ [_a-zA-Z0-9]+? # non-greedy lookup for everything contained in this list
127
+ )'.freeze
128
+ private_constant :VARIABLE_NAME_PATTERN
129
+
105
130
  CAPTURE_VARIABLE_IN_BUILD_CONFIG = /
106
131
  \$ # matches dollar sign literally
107
- [{(] # matches a single caracter on this list
108
- ( # capture block
109
- [^inherited] # ignore if match characters in this list
110
- [$(){}_a-zA-Z0-9]*? # non-greedy lookup for everything that contains this list
111
- )
112
- [})] # matches a single caracter on this list
132
+ (?: # non-capturing group
133
+ [{] # matches a single character on this list
134
+ #{VARIABLE_NAME_PATTERN}
135
+ [}] # matches a single character on this list
136
+ | # or
137
+ [(] # matches a single character on this list
138
+ #{VARIABLE_NAME_PATTERN}
139
+ [)] # matches a single character on this list
140
+ )
113
141
  /x
142
+ private_constant :CAPTURE_VARIABLE_IN_BUILD_CONFIG
114
143
 
115
144
  def expand_build_setting(build_setting_value, config_value)
116
145
  if build_setting_value.is_a?(Array) && config_value.is_a?(String)
@@ -123,27 +152,52 @@ module Xcodeproj
123
152
  inherited = config_value || default
124
153
 
125
154
  return build_setting_value.gsub(Regexp.union(Constants::INHERITED_KEYWORDS), inherited) if build_setting_value.is_a? String
126
- build_setting_value.map { |value| Constants::INHERITED_KEYWORDS.include?(value) ? inherited : value }.flatten
155
+ build_setting_value.flat_map { |value| Constants::INHERITED_KEYWORDS.include?(value) ? inherited : value }
127
156
  end
128
157
 
129
- def resolve_variable_substitution(key, value, root_target)
130
- variable = match_variable(value)
131
- return nil if key.eql?(variable)
132
- if variable.nil?
133
- return name if value.eql?('CONFIGURATION')
134
- if root_target
135
- return root_target.build_configuration_list[name].resolve_build_setting(value, root_target) || value
136
- else
137
- return resolve_build_setting(value, root_target) || value
138
- end
158
+ def resolve_variable_substitution(key, value, root_target, previous_key = nil)
159
+ case value
160
+ when Array
161
+ return value.map { |v| resolve_variable_substitution(key, v, root_target) }
162
+ when nil
163
+ return
164
+ when String
165
+ # we know how to resolve strings!
166
+ nil
167
+ else
168
+ raise ArgumentError, "Settings values can only be nil, string, or array, got #{value.inspect} for #{key}"
139
169
  end
140
- resolve_variable_substitution(key, value.sub(CAPTURE_VARIABLE_IN_BUILD_CONFIG, resolve_variable_substitution(key, variable, root_target)), root_target)
141
- end
142
170
 
143
- def match_variable(config_setting)
144
- match_data = config_setting.match(CAPTURE_VARIABLE_IN_BUILD_CONFIG)
145
- return match_data.captures.first unless match_data.nil?
146
- match_data
171
+ unless variable_match_data = value.match(CAPTURE_VARIABLE_IN_BUILD_CONFIG)
172
+ # no variables left, return the value unchanged
173
+ return value
174
+ end
175
+ variable_reference, variable = *variable_match_data.values_at(0, 1, 2).compact
176
+
177
+ case variable
178
+ when 'inherited'
179
+ # this is handled separately, after resolving all other variable references
180
+ value
181
+ when key
182
+ # to prevent infinite recursion
183
+ nil
184
+ when previous_key
185
+ # to prevent infinite recursion; we don't return nil as for the self recursion because it needs to be
186
+ # distinguished outside this method too
187
+ MUTUAL_RECURSION_SENTINEL
188
+ else
189
+ configuration_to_resolve_against = root_target ? root_target.build_configuration_list[name] : self
190
+ resolved_value_for_variable = configuration_to_resolve_against.resolve_build_setting(variable, root_target, key) || ''
191
+
192
+ # we use this sentinel string instead of nil, because, otherwise, it would be swallowed by the default empty
193
+ # string from the preceding line, and we want to distinguish between mutual recursion and other cases
194
+ if resolved_value_for_variable == MUTUAL_RECURSION_SENTINEL
195
+ return MUTUAL_RECURSION_SENTINEL
196
+ end
197
+
198
+ value = value.gsub(variable_reference, resolved_value_for_variable)
199
+ resolve_variable_substitution(key, value, root_target)
200
+ end
147
201
  end
148
202
 
149
203
  def sorted_build_settings
@@ -161,14 +215,15 @@ module Xcodeproj
161
215
 
162
216
  settings.keys.each do |key|
163
217
  next unless value = settings[key]
218
+ stripped_key = key.sub(/\[[^\]]+\]$/, '')
164
219
  case value
165
220
  when String
166
- next unless array_settings.include?(key)
221
+ next unless array_settings.include?(stripped_key)
167
222
  array_value = split_build_setting_array_to_string(value)
168
223
  next unless array_value.size > 1
169
224
  settings[key] = array_value
170
225
  when Array
171
- next if value.size > 1 && array_settings.include?(key)
226
+ next if value.size > 1 && array_settings.include?(stripped_key)
172
227
  settings[key] = value.join(' ')
173
228
  end
174
229
  end
@@ -180,9 +235,15 @@ module Xcodeproj
180
235
  end
181
236
 
182
237
  def config
183
- @config ||= Xcodeproj::Config.new(base_configuration_reference.real_path).to_hash.tap do |hash|
184
- normalize_array_settings(hash)
185
- end
238
+ return {} unless base_configuration_reference
239
+ @config ||=
240
+ if base_configuration_reference.real_path.exist?
241
+ Xcodeproj::Config.new(base_configuration_reference.real_path).to_hash.tap do |hash|
242
+ normalize_array_settings(hash)
243
+ end
244
+ else
245
+ {}
246
+ end
186
247
  end
187
248
 
188
249
  #---------------------------------------------------------------------#