cocoapods-mapfile 0.2.5 → 0.2.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,45 +11,48 @@ module HMap
11
11
  def project_buckets
12
12
  h_name = File.basename(path)
13
13
  h_dir = File.dirname(path)
14
- [BucketStr.new(h_name, "#{h_dir}/", h_name)]
14
+ { h_name => ["#{h_dir}/", h_name] }
15
15
  end
16
16
 
17
17
  def module_buckets(moudle_name)
18
18
  h_name = File.basename(path)
19
19
  module_p = "#{moudle_name}/"
20
- [BucketStr.new(h_name, module_p, h_name)]
20
+ { h_name => [module_p, h_name] }
21
21
  end
22
22
 
23
23
  def full_module_buckets(moudle_name)
24
24
  h_name = File.basename(path)
25
25
  h_dir = File.dirname(path)
26
26
  module_k = "#{moudle_name}/#{h_name}"
27
- [BucketStr.new(module_k, "#{h_dir}/", h_name)]
27
+ { module_k => ["#{h_dir}/", h_name] }
28
28
  end
29
29
 
30
30
  def project_buckets_extra
31
31
  h_name = File.basename(path)
32
32
  h_dir = File.dirname(path)
33
- buckets = []
34
- buckets << BucketStr.new(h_name, "#{h_dir}/", h_name) unless extra_keys.include?(h_name)
35
- buckets + extra_keys.map { |key| BucketStr.new(key, "#{h_dir}/", h_name) }
33
+ buckets = {}
34
+ buckets[h_name] = ["#{h_dir}/", h_name] unless extra_keys.include?(h_name)
35
+ extra_keys.each { |key| buckets[key] = ["#{h_dir}/", h_name] }
36
+ buckets
36
37
  end
37
38
 
38
39
  def module_buckets_extra(moudle_name)
39
40
  h_name = File.basename(path)
40
41
  module_p = "#{moudle_name}/"
41
- buckets = []
42
- buckets << BucketStr.new(h_name, module_p, h_name) unless extra_keys.include?(h_name)
43
- buckets + extra_keys.map { |key| BucketStr.new(key, module_p, h_name) }
42
+ buckets = {}
43
+ buckets[h_name] = [module_p, h_name] unless extra_keys.include?(h_name)
44
+ extra_keys.each { |key| buckets[key] = [module_p, h_name] }
45
+ buckets
44
46
  end
45
47
 
46
48
  def full_module_buckets_extra(moudle_name)
47
49
  h_name = File.basename(path)
48
50
  h_dir = File.dirname(path)
49
51
  module_k = "#{moudle_name}/#{h_name}"
50
- buckets = []
51
- buckets << BucketStr.new(module_k, "#{h_dir}/", h_name) unless extra_keys.include?(module_k)
52
- buckets + extra_keys.map { |key| BucketStr.new(key, "#{h_dir}/", h_name) }
52
+ buckets = {}
53
+ buckets[module_k] = ["#{h_dir}/", h_name] unless extra_keys.include?(module_k)
54
+ extra_keys.each { |key| buckets[key] = ["#{h_dir}/", h_name] }
55
+ buckets
53
56
  end
54
57
  end
55
58
  end
@@ -1,15 +1,19 @@
1
1
  module HMap
2
2
  module PBXHelper
3
+ PBX_GROUP = '<group>'.freeze
4
+ PBX_SOURCE_ROOT = 'SOURCE_ROOT'.freeze
5
+ private_constant :PBX_GROUP, :PBX_SOURCE_ROOT
3
6
  def self.get_groups(xct)
4
7
  groups = xct.referrers.select { |e| e.is_a?(Constants::PBXGroup) } || []
5
8
  groups += groups.flat_map { |g| get_groups(g) }
6
- groups.compact.reverse
9
+ groups.compact
7
10
  end
8
11
 
9
12
  def self.group_paths(xct)
10
- ps = get_groups(xct).map do |g|
13
+ gs = get_groups(xct).reverse
14
+ ps = gs.map do |g|
11
15
  s_hash = g.instance_variable_get('@simple_attributes_hash')
12
- s_hash['path'] unless s_hash.nil?
16
+ s_hash['path'] unless s_hash.nil? && s_hash['sourceTree'] == PBX_GROUP
13
17
  end.compact
14
18
  File.join(*ps)
15
19
  end
@@ -46,8 +50,10 @@ module HMap
46
50
  p_path = File.dirname(project.path)
47
51
  project.root_object.project_references.map do |sp|
48
52
  ff = sp[:project_ref]
49
- path = ff.instance_variable_get('@simple_attributes_hash')['path'] || ''
50
- full_path = File.join(p_path, path)
53
+ f_hash = ff.instance_variable_get('@simple_attributes_hash')
54
+ g_path = PBXHelper.group_paths(ff) if f_hash['sourceTree'] == PBX_GROUP
55
+ path = f_hash['path'] || ''
56
+ full_path = File.join(p_path, g_path || '', path)
51
57
  Xcodeproj::Project.open(full_path)
52
58
  end << project
53
59
  end
@@ -39,7 +39,7 @@ module HMap
39
39
  end
40
40
 
41
41
  def project_build_settings(project_path)
42
- targets = xcodebuild(project_path) || []
42
+ targets = xcodebuild_project(project_path) || []
43
43
  targets.first['buildSettings']
44
44
  end
45
45
 
@@ -121,7 +121,7 @@ module HMap
121
121
  command += %w[-json -showBuildSettings]
122
122
  results = Executable.execute_command('xcodebuild', command, false)
123
123
  index = results.index(/"action"/m)
124
- reg = results[index..-1]
124
+ reg = results[index..]
125
125
  reg = "[\n{\n#{reg}"
126
126
  JSON.parse(reg) unless results.nil?
127
127
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'hmap/xc/target/target_vfs'
3
4
 
4
5
  module HMap
@@ -63,13 +64,7 @@ module HMap
63
64
  end
64
65
 
65
66
  def symlink_to(path, need_platform)
66
- return unless path.exist?
67
-
68
- filepath = Pathname.new(hmap_filepath(need_platform))
69
- filepath.dirname.mkpath unless filepath.exist?
70
- return if File.identical?(filepath, path)
71
-
72
- filepath.make_symlink(path)
67
+ Utils.file_symlink_to(path, Pathname.new(hmap_filepath(need_platform)))
73
68
  end
74
69
 
75
70
  def write(data, need_platform)
@@ -42,35 +42,38 @@ module HMap
42
42
  def xcconfig_paths
43
43
  return @xcconfig_paths if defined?(@xcconfig_paths)
44
44
 
45
- @xcconfig_paths = target.build_configuration_list.build_configurations.flat_map do |configuration|
45
+ @xcconfig_paths = target.build_configurations.flat_map do |configuration|
46
46
  if configuration.is_a?(Constants::XCBuildConfiguration)
47
47
  bcr = configuration.base_configuration_reference
48
48
  unless bcr.nil?
49
49
  s_path = PBXHelper.group_paths(bcr)
50
50
  x = bcr.instance_variable_get('@simple_attributes_hash')['path'] || ''
51
- File.expand_path File.join(project.project_dir, s_path, x)
51
+ path = File.expand_path(File.join(project.project_dir, s_path, x))
52
+ xc = XCConfig.new(path)
53
+ inc = xc.includes_paths
54
+ path if inc.empty? || project.workspace.xcconfig_paths.none? { |pa| inc.include?(pa) }
52
55
  end
53
56
  end
54
57
  end.compact
55
- # @xcconfig_paths = TargetConfiguration.new_from_xc(target, project.project_dir).map do |c|
56
- # c.xcconfig_path unless c.xcconfig_path.nil?
57
- # end.compact
58
- # @xcconfig_paths = bc.map(&:xcconfig_path).compact
59
- # xc_type = 'XCConfigurationList'
60
- # xcs = target.build_configuration_list.build_configurations
61
- # @xcconfig_paths = xcs.map do |xc|
62
- # if xc.isa == xc_type
63
- # xc_path = xc.instance_variable_get('@simple_attributes_hash')['path'] || ''
64
- # { xc => File.join(project.project_dir, xc_path) }
65
- # else
66
- # # ab_path = Pathname(project.project_dir + "hmap-#{name}.#{xc.name}.xcconfig")
67
- # # File.new(ab_path, 'w') unless ab_path.exist?
68
- # # xc_ref = target.project.new_file(ab_path)
69
- # # xc.base_configuration_reference = xc_ref
70
- # # target.project.save
71
- # # ab_path
58
+
59
+ # @xcconfig_paths = target.build_configuration_list.build_configurations.flat_map do |configuration|
60
+ # if configuration.is_a?(Constants::XCBuildConfiguration)
61
+ # bcr = configuration.base_configuration_reference
62
+ # # if bcr.nil?
63
+ # # ab_path = Pathname(project.project_dir + "hmap-#{target_name}.#{configuration.name}.xcconfig")
64
+ # # File.new(ab_path, 'w') unless ab_path.exist?
65
+ # # xc_ref = target.project.new_file(ab_path)
66
+ # # configuration.base_configuration_reference = xc_ref
67
+ # # target.project.save
68
+ # # ab_path
69
+ # # else
70
+ # unless bcr.nil?
71
+ # s_path = PBXHelper.group_paths(bcr)
72
+ # x = bcr.instance_variable_get('@simple_attributes_hash')['path'] || ''
73
+ # File.expand_path File.join(project.project_dir, s_path, x)
74
+ # end
72
75
  # end
73
- # end
76
+ # end.compact
74
77
  end
75
78
  end
76
79
  end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HMap
4
- class Target
4
+ class Target
5
5
  class TargetContext < Context
6
6
  attr_reader :product_name
7
+
7
8
  def initialize(build_root, temp_dir, hmap_root, temp_name, build_dir, product_name, full_product_name, defines_modules, build_as_framework_swift)
8
9
  super(build_root, temp_dir, hmap_root, temp_name, build_dir)
9
10
  @full_product_name = full_product_name
@@ -11,15 +12,15 @@ module HMap
11
12
  @defines_modules = defines_modules
12
13
  @build_as_framework_swift = build_as_framework_swift
13
14
  end
14
-
15
+
15
16
  def build_as_framework_swift?
16
17
  @build_as_framework_swift
17
18
  end
18
-
19
+
19
20
  def defines_modules?
20
21
  @defines_modules
21
22
  end
22
-
23
+
23
24
  def build_path(platform)
24
25
  path = super(platform)
25
26
  File.join(path, @full_product_name)
@@ -16,29 +16,50 @@ module HMap
16
16
  define_method(:all_non_framework_target_headers) do
17
17
  return if build_as_framework?
18
18
 
19
+ return @all_non_framework_target_headers if defined? @all_non_framework_target_headers
20
+
19
21
  p_h = public_entrys + private_entrys
20
- p_h.flat_map { |entry| entry.full_module_buckets(product_name) }
22
+ @all_non_framework_target_headers = p_h.inject({}) do |sum, entry|
23
+ sum.merge!(entry.full_module_buckets(product_name)) { |_, v1, _| v1 }
24
+ end
21
25
  end
22
26
 
23
27
  # all_targets include header full module path
24
28
  define_method(:all_target_headers) do
29
+ return @all_target_headers if defined? @all_target_headers
30
+
25
31
  p_h = public_entrys + private_entrys
26
- p_h.flat_map do |entry|
27
- entry.full_module_buckets_extra(product_name) + entry.module_buckets(product_name)
32
+ @all_target_headers = p_h.inject({}) do |sum, entry|
33
+ sum.merge!(entry.module_buckets(product_name)) { |_, v1, _| v1 }
34
+ sum.merge!(entry.full_module_buckets(product_name)) { |_, v1, _| v1 }
28
35
  end
29
36
  end
30
37
 
31
38
  define_method(:project_headers) do
32
- all_target_headers + project_entrys.flat_map(&:project_buckets_extra)
39
+ return @project_headers if defined? @project_headers
40
+
41
+ p_h = public_entrys + private_entrys
42
+ hs = p_h.inject({}) do |sum, entry|
43
+ sum.merge!(entry.module_buckets(product_name)) { |_, v1, _| v1 }
44
+ end
45
+ @project_headers = project_entrys.inject(hs) do |sum, entry|
46
+ sum.merge!(entry.project_buckets_extra) { |_, v1, _| v1 }
47
+ end
33
48
  end
34
49
 
35
50
  define_method(:own_target_headers) do
51
+ return @own_target_headers if defined? @own_target_headers
52
+
36
53
  headers = public_entrys + private_entrys
37
- headers = headers.flat_map { |entry| entry.module_buckets(product_name) }
38
- headers + project_entrys.flat_map do |entry|
39
- entry.project_buckets + entry.full_module_buckets(product_name)
54
+ hs = headers.inject({}) do |sum, entry|
55
+ sum.merge!(entry.module_buckets(product_name)) { |_, v1, _| v1 }
56
+ end
57
+ @own_target_headers = project_entrys.inject(hs) do |sum, entry|
58
+ sum.merge!(entry.project_buckets) { |_, v1, _| v1 }
59
+ sum.merge!(entry.full_module_buckets(product_name)) { |_, v1, _| v1 }
40
60
  end
41
61
  end
62
+
42
63
  def build_root
43
64
  project.build_root
44
65
  end
@@ -52,7 +73,10 @@ module HMap
52
73
  end
53
74
 
54
75
  def product_name
55
- target.product_name.gsub(/[-]/, '_')
76
+ product_name = target.build_settings(target.build_configurations.first.name)['PRODUCT_NAME']
77
+ return target_name.gsub(/-/, '_') if product_name.nil? || product_name.include?('TARGET_NAME')
78
+
79
+ product_name
56
80
  end
57
81
 
58
82
  def full_product_name
@@ -70,13 +94,14 @@ module HMap
70
94
  def build_dir
71
95
  return @build_dir if defined?(@build_dir)
72
96
 
73
- b_d = xcconfig_paths.any? do |path|
74
- xc = Xcodeproj::Config.new(path)
75
- !xc.attributes[Constants::CONFIGURATION_BUILD_DIR].nil?
76
- end
97
+ b_d = xcconfig_paths.none? { |path| XCConfig.new(path).attributes[Constants::CONFIGURATION_BUILD_DIR].nil? }
77
98
  @build_dir = target_name if b_d
78
99
  end
79
100
 
101
+ def app_target?
102
+ Xcodeproj::Constants::PRODUCT_UTI_EXTENSIONS[target.symbol_type] == 'app'
103
+ end
104
+
80
105
  def build_as_framework?
81
106
  PBXHelper.build_as_framework?(target)
82
107
  end
@@ -59,7 +59,6 @@ module HMap
59
59
  end
60
60
  end
61
61
 
62
-
63
62
  # A collection of Each TargetVFSEntry
64
63
  class TargetVFS
65
64
  def initialize(public_headers, private_headers, platforms, setting)
@@ -0,0 +1,295 @@
1
+ module HMap
2
+ # This class holds the data for a Xcode build settings file (xcconfig) and
3
+ # provides support for serialization.
4
+ #
5
+ class XCConfig
6
+ require 'set'
7
+
8
+ KEY_VALUE_PATTERN = /
9
+ (
10
+ [^=\[]+ # Any char, but not an assignment operator
11
+ # or subscript (non-greedy)
12
+ (?: # One or multiple conditional subscripts
13
+ \[
14
+ [^\]]* # The subscript key
15
+ (?:
16
+ = # The subscript comparison operator
17
+ [^\]]* # The subscript value
18
+ )?
19
+ \]
20
+ )*
21
+ )
22
+ \s* # Whitespaces after the key (needed because subscripts
23
+ # always end with ']')
24
+ = # The assignment operator
25
+ (.*) # The value
26
+ /x
27
+ private_constant :KEY_VALUE_PATTERN
28
+
29
+ INHERITED = %w($(inherited) ${inherited}).freeze
30
+ private_constant :INHERITED
31
+
32
+ INHERITED_REGEXP = Regexp.union(INHERITED)
33
+ private_constant :INHERITED_REGEXP
34
+
35
+ # @return [Hash{String => String}] The attributes of the settings file
36
+ # excluding frameworks, weak_framework and libraries.
37
+ #
38
+ attr_accessor :attributes
39
+
40
+ # @return [Array] The list of the configuration files included by this
41
+ # configuration file (`#include "SomeConfig"`).
42
+ #
43
+ attr_accessor :includes_paths
44
+
45
+ # @return [Hash{Symbol => Set<String>}] The other linker flags by key.
46
+ # Xcodeproj handles them in a dedicated way to prevent duplication
47
+ # of the libraries and of the frameworks.
48
+ #
49
+ def initialize(xcconfig_hash_or_file = {})
50
+ @attributes = {}
51
+ @includes = []
52
+ @include_attributes = {}
53
+ merge!(extract_hash(xcconfig_hash_or_file))
54
+ @includes_paths = @includes.map { |i| File.expand_path(i, @filepath.dirname) }
55
+ end
56
+
57
+ def inspect
58
+ to_hash.inspect
59
+ end
60
+
61
+ def ==(other)
62
+ other.attributes == attributes && other.includes_paths = @includes_paths
63
+ end
64
+
65
+ # @!group Serialization
66
+ #-------------------------------------------------------------------------#
67
+
68
+ # Sorts the internal data by setting name and serializes it in the xcconfig
69
+ # format.
70
+ #
71
+ # @example
72
+ #
73
+ # config = Config.new('PODS_ROOT' => '"$(SRCROOT)/Pods"', 'OTHER_LDFLAGS' => '-lxml2')
74
+ # config.to_s # => "OTHER_LDFLAGS = -lxml2\nPODS_ROOT = \"$(SRCROOT)/Pods\""
75
+ #
76
+ # @return [String] The serialized internal data.
77
+ #
78
+ def to_s(prefix = nil)
79
+ include_lines = @includes.map { |path| "#include \"#{normalized_xcconfig_path(path)}\"" }
80
+ settings = to_hash(prefix).sort_by(&:first).map { |k, v| "#{k} = #{v}".strip }
81
+ (include_lines + settings).join("\n") << "\n"
82
+ end
83
+
84
+ # Writes the serialized representation of the internal data to the given
85
+ # path.
86
+ #
87
+ # @param [Pathname] pathname
88
+ # The file where the data should be written to.
89
+ #
90
+ # @return [void]
91
+ #
92
+ def save_as(pathname, prefix = nil)
93
+ return if File.exist?(pathname) && (XCConfig.new(pathname) == self)
94
+
95
+ pathname.open('w') { |file| file << to_s(prefix) }
96
+ end
97
+
98
+ # The hash representation of the xcconfig. The hash includes the
99
+ # frameworks, the weak frameworks, the libraries and the simple other
100
+ # linker flags in the `Other Linker Flags` (`OTHER_LDFLAGS`).
101
+ #
102
+ # @note All the values are sorted to have a consistent output in Ruby
103
+ # 1.8.7.
104
+ #
105
+ # @return [Hash] The hash representation
106
+ #
107
+ def to_hash(prefix = nil)
108
+ result = attributes.dup
109
+ result.reject! { |_, v| INHERITED.any? { |i| i == v.to_s.strip } }
110
+ if prefix
111
+ Hash[result.map { |k, v| [prefix + k, v] }]
112
+ else
113
+ result
114
+ end
115
+ end
116
+
117
+ alias to_h to_hash
118
+
119
+ # @!group Merging
120
+ #-------------------------------------------------------------------------#
121
+
122
+ # Merges the given xcconfig representation in the receiver.
123
+ # @note If a key in the given hash already exists in the internal data
124
+ # then its value is appended.
125
+ #
126
+ # @param [Hash, Config] config
127
+ # The xcconfig representation to merge.
128
+ #
129
+ # @todo The logic to normalize an hash should be extracted and the
130
+ # initializer should not call this method.
131
+ #
132
+ # @return [void]
133
+ #
134
+ def merge!(xcconfig)
135
+ if xcconfig.is_a? XCConfig
136
+ merge_attributes!(xcconfig.attributes)
137
+ else
138
+ merge_attributes!(xcconfig.to_hash)
139
+ end
140
+ end
141
+ alias << merge!
142
+
143
+ # Creates a new #{Config} with the data of the receiver merged with the
144
+ # given xcconfig representation.
145
+ #
146
+ # @param [Hash, Config] config
147
+ # The xcconfig representation to merge.
148
+ #
149
+ # @return [Config] the new xcconfig.
150
+ #
151
+ def merge(config)
152
+ dup.tap { |x| x.merge!(config) }
153
+ end
154
+
155
+ # @return [Config] A copy of the receiver.
156
+ #
157
+ def dup
158
+ HMap::XCConfig.new(to_hash.dup)
159
+ end
160
+
161
+ #-------------------------------------------------------------------------#
162
+
163
+ private
164
+
165
+ # @!group Private Helpers
166
+
167
+ # Returns a hash from the given argument reading it from disk if necessary.
168
+ #
169
+ # @param [String, Pathname, Hash] argument
170
+ # The source from where the hash should be extracted.
171
+ #
172
+ # @return [Hash]
173
+ #
174
+ def extract_hash(argument)
175
+ return argument if argument.is_a?(Hash)
176
+
177
+ if argument.respond_to? :read
178
+ @filepath = Pathname.new(argument.to_path)
179
+ hash_from_file_content(argument.read)
180
+ elsif File.readable?(argument.to_s)
181
+ @filepath = Pathname.new(argument.to_s)
182
+ hash_from_file_content(File.read(argument))
183
+ else
184
+ argument
185
+ end
186
+ end
187
+
188
+ # Returns a hash from the string representation of an Xcconfig file.
189
+ #
190
+ # @param [String] string
191
+ # The string representation of an xcconfig file.
192
+ #
193
+ # @return [Hash] the hash containing the xcconfig data.
194
+ #
195
+ def hash_from_file_content(string)
196
+ hash = {}
197
+ string.split("\n").each do |line|
198
+ uncommented_line = strip_comment(line)
199
+ e = extract_include(uncommented_line)
200
+ if e.nil?
201
+ key, value = extract_key_value(uncommented_line)
202
+ next unless key
203
+ value.gsub!(INHERITED_REGEXP) { |m| hash.fetch(key, m) }
204
+ hash[key] = value
205
+ else
206
+ @includes.push normalized_xcconfig_path(e)
207
+ end
208
+ end
209
+ hash
210
+ end
211
+
212
+ # Merges the given attributes hash while ensuring values are not duplicated.
213
+ #
214
+ # @param [Hash] attributes
215
+ # The attributes hash to merge into @attributes.
216
+ #
217
+ # @return [void]
218
+ #
219
+ def merge_attributes!(attributes)
220
+ @attributes.merge!(attributes) do |_, v1, v2|
221
+ v1 = v1.strip
222
+ v2 = v2.strip
223
+ v1_split = v1.shellsplit
224
+ v2_split = v2.shellsplit
225
+ if (v2_split - v1_split).empty? || v1_split.first(v2_split.size) == v2_split
226
+ v1
227
+ elsif v2_split.first(v1_split.size) == v1_split
228
+ v2
229
+ else
230
+ "#{v1} #{v2}"
231
+ end
232
+ end
233
+ end
234
+
235
+ # Strips the comments from a line of an xcconfig string.
236
+ #
237
+ # @param [String] line
238
+ # the line to process.
239
+ #
240
+ # @return [String] the uncommented line.
241
+ #
242
+ def strip_comment(line)
243
+ line.partition('//').first
244
+ end
245
+
246
+ # Returns the file included by a line of an xcconfig string if present.
247
+ #
248
+ # @param [String] line
249
+ # the line to process.
250
+ #
251
+ # @return [String] the included file.
252
+ # @return [Nil] if no include was found in the line.
253
+ #
254
+ def extract_include(line)
255
+ regexp = /#include\s*"(.+)"/
256
+ match = line.match(regexp)
257
+ match[1] if match
258
+ end
259
+
260
+ # Returns the key and the value described by the given line of an xcconfig.
261
+ #
262
+ # @param [String] line
263
+ # the line to process.
264
+ #
265
+ # @return [Array] A tuple where the first entry is the key and the second
266
+ # entry is the value.
267
+ #
268
+ def extract_key_value(line)
269
+ match = line.match(KEY_VALUE_PATTERN)
270
+ if match
271
+ key = match[1]
272
+ value = match[2]
273
+ [key.strip, value.strip]
274
+ else
275
+ []
276
+ end
277
+ end
278
+
279
+ # Normalizes the given path to an xcconfing file to be used in includes,
280
+ # appending the extension if necessary.
281
+ #
282
+ # @param [String] path
283
+ # The path of the file which will be included in the xcconfig.
284
+ #
285
+ # @return [String] The normalized path.
286
+ #
287
+ def normalized_xcconfig_path(path)
288
+ if File.extname(path) == '.xcconfig'
289
+ path
290
+ else
291
+ "#{path}.xcconfig"
292
+ end
293
+ end
294
+ end
295
+ end