cocoapods 0.39.0 → 1.0.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +261 -12
  3. data/lib/cocoapods.rb +1 -0
  4. data/lib/cocoapods/command.rb +1 -0
  5. data/lib/cocoapods/command/env.rb +66 -0
  6. data/lib/cocoapods/command/init.rb +1 -1
  7. data/lib/cocoapods/command/lib.rb +1 -1
  8. data/lib/cocoapods/command/project.rb +0 -4
  9. data/lib/cocoapods/command/repo/lint.rb +7 -6
  10. data/lib/cocoapods/command/repo/push.rb +22 -1
  11. data/lib/cocoapods/command/setup.rb +0 -24
  12. data/lib/cocoapods/command/spec/create.rb +3 -1
  13. data/lib/cocoapods/command/spec/edit.rb +14 -21
  14. data/lib/cocoapods/command/spec/env_spec.rb +53 -0
  15. data/lib/cocoapods/command/spec/lint.rb +1 -1
  16. data/lib/cocoapods/config.rb +1 -34
  17. data/lib/cocoapods/downloader.rb +9 -4
  18. data/lib/cocoapods/external_sources.rb +0 -4
  19. data/lib/cocoapods/external_sources/abstract_external_source.rb +38 -11
  20. data/lib/cocoapods/external_sources/path_source.rb +2 -2
  21. data/lib/cocoapods/gem_version.rb +2 -2
  22. data/lib/cocoapods/generator/acknowledgements.rb +1 -1
  23. data/lib/cocoapods/generator/acknowledgements/plist.rb +1 -1
  24. data/lib/cocoapods/generator/copy_resources_script.rb +28 -21
  25. data/lib/cocoapods/generator/info_plist_file.rb +34 -8
  26. data/lib/cocoapods/generator/module_map.rb +3 -18
  27. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +22 -10
  28. data/lib/cocoapods/generator/xcconfig/pod_xcconfig.rb +2 -1
  29. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +2 -1
  30. data/lib/cocoapods/hooks_manager.rb +3 -11
  31. data/lib/cocoapods/installer.rb +45 -25
  32. data/lib/cocoapods/installer/analyzer.rb +53 -25
  33. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +2 -13
  34. data/lib/cocoapods/installer/analyzer/target_inspection_result.rb +4 -0
  35. data/lib/cocoapods/installer/analyzer/target_inspector.rb +22 -19
  36. data/lib/cocoapods/installer/file_references_installer.rb +53 -6
  37. data/lib/cocoapods/installer/installation_options.rb +156 -0
  38. data/lib/cocoapods/installer/migrator.rb +1 -56
  39. data/lib/cocoapods/installer/pod_source_installer.rb +10 -8
  40. data/lib/cocoapods/installer/podfile_validator.rb +42 -1
  41. data/lib/cocoapods/installer/post_install_hooks_context.rb +19 -2
  42. data/lib/cocoapods/installer/target_installer.rb +6 -2
  43. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +6 -5
  44. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +82 -14
  45. data/lib/cocoapods/installer/user_project_integrator.rb +37 -16
  46. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +14 -136
  47. data/lib/cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator.rb +15 -22
  48. data/lib/cocoapods/project.rb +109 -19
  49. data/lib/cocoapods/resolver.rb +17 -15
  50. data/lib/cocoapods/resolver/lazy_specification.rb +4 -0
  51. data/lib/cocoapods/sandbox.rb +0 -32
  52. data/lib/cocoapods/sandbox/headers_store.rb +2 -2
  53. data/lib/cocoapods/sandbox/podspec_finder.rb +1 -1
  54. data/lib/cocoapods/sources_manager.rb +181 -50
  55. data/lib/cocoapods/target/aggregate_target.rb +17 -11
  56. data/lib/cocoapods/target/pod_target.rb +31 -4
  57. data/lib/cocoapods/user_interface.rb +32 -3
  58. data/lib/cocoapods/user_interface/error_report.rb +46 -36
  59. data/lib/cocoapods/validator.rb +132 -43
  60. metadata +164 -79
@@ -61,8 +61,8 @@ module Pod
61
61
  def integrate!
62
62
  create_workspace
63
63
  integrate_user_targets
64
- warn_about_empty_podfile
65
64
  warn_about_xcconfig_overrides
65
+ save_projects
66
66
  end
67
67
 
68
68
  #-----------------------------------------------------------------------#
@@ -91,7 +91,7 @@ module Pod
91
91
  workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
92
92
  new_file_references = file_references - workspace.file_references
93
93
  unless new_file_references.empty?
94
- workspace.file_references.concat(new_file_references)
94
+ new_file_references.each { |fr| workspace << fr }
95
95
  workspace.save_as(workspace_path)
96
96
  end
97
97
 
@@ -112,25 +112,42 @@ module Pod
112
112
  # @return [void]
113
113
  #
114
114
  def integrate_user_targets
115
- targets_to_integrate.sort_by(&:name).each do |target|
116
- TargetIntegrator.new(target).integrate!
115
+ target_integrators = targets_to_integrate.sort_by(&:name).map do |target|
116
+ TargetIntegrator.new(target)
117
117
  end
118
+
119
+ Config.instance.with_changes(:silent => true) do
120
+ deintegrator = Deintegrator.new
121
+ all_project_targets = user_projects.flat_map(&:native_targets).uniq
122
+ all_native_targets = targets_to_integrate.flat_map(&:user_targets).uniq
123
+ targets_to_deintegrate = all_project_targets - all_native_targets
124
+ targets_to_deintegrate.each do |target|
125
+ deintegrator.deintegrate_target(target)
126
+ end
127
+ end
128
+
129
+ target_integrators.each(&:integrate!)
118
130
  end
119
131
 
120
- # Warns the user if the podfile is empty.
121
- #
122
- # @note The workspace is created in any case and all the user projects
123
- # are added to it, however the projects are not integrated as
124
- # there is no way to discern between target definitions which are
125
- # empty and target definitions which just serve the purpose to
126
- # wrap other ones. This is not an issue because empty target
127
- # definitions generate empty libraries.
132
+ # Save all user projects.
128
133
  #
129
134
  # @return [void]
130
135
  #
131
- def warn_about_empty_podfile
132
- if podfile.target_definitions.values.all?(&:empty?)
133
- UI.warn '[!] The Podfile does not contain any dependencies.'
136
+ def save_projects
137
+ user_projects.each do |project|
138
+ if project.dirty?
139
+ project.save
140
+ else
141
+ # There is a bug in Xcode where the process of deleting and
142
+ # re-creating the xcconfig files used in the build
143
+ # configuration cause building the user project to fail until
144
+ # Xcode is relaunched.
145
+ #
146
+ # Touching/saving the project causes Xcode to reload these.
147
+ #
148
+ # https://github.com/CocoaPods/CocoaPods/issues/2665
149
+ FileUtils.touch(project.path + 'project.pbxproj')
150
+ end
134
151
  end
135
152
  end
136
153
 
@@ -194,8 +211,12 @@ module Pod
194
211
  targets.map(&:user_project_path).compact.uniq
195
212
  end
196
213
 
214
+ def user_projects
215
+ targets.map(&:user_project).compact.uniq
216
+ end
217
+
197
218
  def targets_to_integrate
198
- targets.reject { |target| target.target_definition.empty? }
219
+ targets.reject { |target| target.target_definition.abstract? }
199
220
  end
200
221
 
201
222
  # Prints a warning informing the user that a build configuration of
@@ -38,34 +38,12 @@ module Pod
38
38
  #
39
39
  def integrate!
40
40
  UI.section(integration_message) do
41
- # TODO: refactor into Xcodeproj https://github.com/CocoaPods/Xcodeproj/issues/202
42
- project_is_dirty = [
43
- XCConfigIntegrator.integrate(target, native_targets),
44
- update_to_cocoapods_0_34,
45
- update_to_cocoapods_0_37_1,
46
- update_to_cocoapods_0_39,
47
- unless native_targets_to_integrate.empty?
48
- add_pods_library
49
- add_embed_frameworks_script_phase
50
- add_copy_resources_script_phase
51
- add_check_manifest_lock_script_phase
52
- true
53
- end,
54
- ].any?
41
+ XCConfigIntegrator.integrate(target, native_targets)
55
42
 
56
- if project_is_dirty
57
- user_project.save
58
- else
59
- # There is a bug in Xcode where the process of deleting and
60
- # re-creating the xcconfig files used in the build
61
- # configuration cause building the user project to fail until
62
- # Xcode is relaunched.
63
- #
64
- # Touching/saving the project causes Xcode to reload these.
65
- #
66
- # https://github.com/CocoaPods/CocoaPods/issues/2665
67
- FileUtils.touch(user_project.path + 'project.pbxproj')
68
- end
43
+ add_pods_library
44
+ add_embed_frameworks_script_phase
45
+ add_copy_resources_script_phase
46
+ add_check_manifest_lock_script_phase
69
47
  end
70
48
  end
71
49
 
@@ -80,85 +58,6 @@ module Pod
80
58
  # @!group Integration steps
81
59
  #---------------------------------------------------------------------#
82
60
 
83
- # Fixes the paths of the copy resource scripts.
84
- #
85
- # @return [Bool] whether any changes to the project were made.
86
- #
87
- # @todo This can be removed for CocoaPods 1.0
88
- #
89
- def update_to_cocoapods_0_34
90
- phases = native_targets.map do |target|
91
- target.shell_script_build_phases.select do |bp|
92
- bp.name == 'Copy Pods Resources'
93
- end
94
- end.flatten
95
-
96
- script_path = target.copy_resources_script_relative_path
97
- shell_script = %("#{script_path}"\n)
98
- changes = false
99
- phases.each do |phase|
100
- unless phase.shell_script == shell_script
101
- phase.shell_script = shell_script
102
- changes = true
103
- end
104
- end
105
- changes
106
- end
107
-
108
- # Removes the embed frameworks phase for target types.
109
- #
110
- # @return [Bool] whether any changes to the project were made.
111
- #
112
- # @todo This can be removed for CocoaPods 1.0
113
- #
114
- def update_to_cocoapods_0_37_1
115
- targets_to_embed = native_targets.select do |target|
116
- EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
117
- end
118
- (native_targets - targets_to_embed).any? do |native_target|
119
- remove_embed_frameworks_script_phase(native_target)
120
- end
121
- end
122
-
123
- # Adds the embed frameworks script when integrating as a static library.
124
- #
125
- # @return [Bool] whether any changes to the project were made.
126
- #
127
- # @todo This can be removed for CocoaPods 1.0
128
- #
129
- def update_to_cocoapods_0_39
130
- targets_to_embed = native_targets.select do |target|
131
- EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
132
- end
133
- requires_update = targets_to_embed.any? do |target|
134
- !target.shell_script_build_phases.find { |bp| bp.name == EMBED_FRAMEWORK_PHASE_NAME }
135
- end
136
- if requires_update
137
- targets_to_embed.each do |native_target|
138
- add_embed_frameworks_script_phase_to_target(native_target)
139
- end
140
- end
141
-
142
- frameworks = user_project.frameworks_group
143
- native_targets.each do |native_target|
144
- build_phase = native_target.frameworks_build_phase
145
-
146
- product_ref = frameworks.files.find { |f| f.path == target.product_name }
147
- if product_ref
148
- build_file = build_phase.build_file(product_ref)
149
- if build_file &&
150
- build_file.settings.is_a?(Hash) &&
151
- build_file.settings['ATTRIBUTES'].is_a?(Array) &&
152
- build_file.settings['ATTRIBUTES'].include?('Weak')
153
- build_file.settings = nil
154
- requires_update = true
155
- end
156
- end
157
- end
158
-
159
- requires_update
160
- end
161
-
162
61
  # Adds spec product reference to the frameworks build phase of the
163
62
  # {TargetDefinition} integration libraries. Adds a file reference to
164
63
  # the frameworks group of the project and adds it to the frameworks
@@ -168,7 +67,7 @@ module Pod
168
67
  #
169
68
  def add_pods_library
170
69
  frameworks = user_project.frameworks_group
171
- native_targets_to_integrate.each do |native_target|
70
+ native_targets.each do |native_target|
172
71
  build_phase = native_target.frameworks_build_phase
173
72
 
174
73
  # Find and delete possible reference for the other product type
@@ -209,13 +108,10 @@ module Pod
209
108
  #
210
109
  # @param [PBXNativeTarget] native_target
211
110
  #
212
- # @return [Bool] whether any changes to the project were made.
213
- #
214
111
  def remove_embed_frameworks_script_phase(native_target)
215
112
  embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == EMBED_FRAMEWORK_PHASE_NAME }
216
- return false unless embed_build_phase.present?
113
+ return unless embed_build_phase.present?
217
114
  native_target.build_phases.delete(embed_build_phase)
218
- true
219
115
  end
220
116
 
221
117
  # Adds a shell script build phase responsible to copy the resources
@@ -226,7 +122,7 @@ module Pod
226
122
  #
227
123
  def add_copy_resources_script_phase
228
124
  phase_name = 'Copy Pods Resources'
229
- native_targets_to_integrate.each do |native_target|
125
+ native_targets.each do |native_target|
230
126
  phase = create_or_update_build_phase(native_target, phase_name)
231
127
  script_path = target.copy_resources_script_relative_path
232
128
  phase.shell_script = %("#{script_path}"\n)
@@ -244,9 +140,9 @@ module Pod
244
140
  #
245
141
  def add_check_manifest_lock_script_phase
246
142
  phase_name = 'Check Pods Manifest.lock'
247
- native_targets_to_integrate.each do |native_target|
143
+ native_targets.each do |native_target|
248
144
  phase = create_or_update_build_phase(native_target, phase_name)
249
- native_target.build_phases.unshift(phase).uniq!
145
+ native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase
250
146
  phase.shell_script = <<-SH.strip_heredoc
251
147
  diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
252
148
  if [[ $? != 0 ]] ; then
@@ -261,14 +157,14 @@ module Pod
261
157
 
262
158
  private
263
159
 
264
- # @!group Private helpers
160
+ # @!group Private Helpers
265
161
  #---------------------------------------------------------------------#
266
162
 
267
163
  # @return [Array<PBXNativeTarget>] The list of all the targets that
268
164
  # match the given target.
269
165
  #
270
166
  def native_targets
271
- @native_targets ||= target.user_targets(user_project)
167
+ @native_targets ||= target.user_targets
272
168
  end
273
169
 
274
170
  # @return [Array<PBXNativeTarget>] The list of all the targets that
@@ -276,36 +172,18 @@ module Pod
276
172
  # directory / product bundle.
277
173
  #
278
174
  def native_targets_to_embed_in
279
- native_targets_to_integrate.select do |target|
175
+ native_targets.select do |target|
280
176
  EMBED_FRAMEWORK_TARGET_TYPES.include?(target.symbol_type)
281
177
  end
282
178
  end
283
179
 
284
- # @return [Array<PBXNativeTarget>] The list of the targets
285
- # that have not been integrated by past installations
286
- # of
287
- #
288
- def native_targets_to_integrate
289
- unless @native_targets_to_integrate
290
- @native_targets_to_integrate = native_targets.reject do |native_target|
291
- native_target.frameworks_build_phase.files.any? do |build_file|
292
- file_ref = build_file.file_ref
293
- file_ref &&
294
- file_ref.isa == 'PBXFileReference' &&
295
- file_ref.display_name == target.product_name
296
- end
297
- end
298
- end
299
- @native_targets_to_integrate
300
- end
301
-
302
180
  # Read the project from the disk to ensure that it is up to date as
303
181
  # other TargetIntegrators might have modified it.
304
182
  #
305
183
  # @return [Project]
306
184
  #
307
185
  def user_project
308
- @user_project ||= Xcodeproj::Project.open(target.user_project_path)
186
+ target.user_project
309
187
  end
310
188
 
311
189
  # @return [Specification::Consumer] the consumer for the specifications.
@@ -15,17 +15,13 @@ module Pod
15
15
  # The native targets associated which should be integrated
16
16
  # with the Pod bundle.
17
17
  #
18
- # @return [Bool] whether any changes to the project were made.
19
- #
20
18
  def self.integrate(pod_bundle, targets)
21
- changes = false
22
19
  targets.each do |target|
23
20
  target.build_configurations.each do |config|
24
- changes = true if update_to_cocoapods_0_34(pod_bundle, targets)
25
- changes = true if set_target_xcconfig(pod_bundle, target, config)
21
+ update_to_cocoapods_0_34(pod_bundle, targets)
22
+ set_target_xcconfig(pod_bundle, target, config)
26
23
  end
27
24
  end
28
- changes
29
25
  end
30
26
 
31
27
  private
@@ -42,13 +38,10 @@ module Pod
42
38
  # @param [Array<XcodeProj::PBXNativeTarget>] targets
43
39
  # The native targets.
44
40
  #
45
- # @return [Bool] whether any changes to the project were made.
46
- #
47
41
  # @todo This can be removed for CocoaPods 1.0
48
42
  #
49
43
  def self.update_to_cocoapods_0_34(pod_bundle, targets)
50
44
  sandbox = pod_bundle.sandbox
51
- changes = false
52
45
  targets.map(&:project).uniq.each do |project|
53
46
  file_refs = project.files.select do |file_ref|
54
47
  path = file_ref.path.to_s
@@ -64,10 +57,7 @@ module Pod
64
57
  file_ref.remove_from_project
65
58
  end
66
59
  end
67
-
68
- changes = true unless file_refs.empty?
69
60
  end
70
- changes
71
61
  end
72
62
 
73
63
  # Creates a file reference to the xcconfig generated by
@@ -83,28 +73,31 @@ module Pod
83
73
  # @param [Xcodeproj::XCBuildConfiguration] config
84
74
  # The build configuration.
85
75
  #
86
- # @return [Boolean] Indicates whether or not any changes were made.
87
- #
88
76
  def self.set_target_xcconfig(pod_bundle, target, config)
89
77
  path = pod_bundle.xcconfig_relative_path(config.name)
90
78
  group = config.project['Pods'] || config.project.new_group('Pods')
91
79
  file_ref = group.files.find { |f| f.path == path }
92
- if config.base_configuration_reference &&
93
- config.base_configuration_reference != file_ref
94
- unless xcconfig_includes_target_xcconfig?(config.base_configuration_reference, path)
80
+ existing = config.base_configuration_reference
81
+
82
+ set_base_configuration_reference = ->() do
83
+ file_ref ||= group.new_file(path)
84
+ config.base_configuration_reference = file_ref
85
+ end
86
+
87
+ if existing && existing != file_ref
88
+ if existing.real_path.to_path.start_with?(pod_bundle.sandbox.root.to_path << '/')
89
+ set_base_configuration_reference.call
90
+ elsif !xcconfig_includes_target_xcconfig?(config.base_configuration_reference, path)
95
91
  UI.warn 'CocoaPods did not set the base configuration of your ' \
96
92
  'project because your project already has a custom ' \
97
93
  'config set. In order for CocoaPods integration to work at ' \
98
94
  'all, please either set the base configurations of the target ' \
99
95
  "`#{target.name}` to `#{path}` or include the `#{path}` in your " \
100
- 'build configuration.'
96
+ "build configuration (#{UI.path(existing.real_path)})."
101
97
  end
102
98
  elsif config.base_configuration_reference.nil? || file_ref.nil?
103
- file_ref ||= group.new_file(path)
104
- config.base_configuration_reference = file_ref
105
- return true
99
+ set_base_configuration_reference.call
106
100
  end
107
- false
108
101
  end
109
102
 
110
103
  private
@@ -1,4 +1,5 @@
1
1
  require 'xcodeproj'
2
+ require 'active_support/core_ext/string/inflections'
2
3
 
3
4
  module Pod
4
5
  # The Pods project.
@@ -20,6 +21,7 @@ module Pod
20
21
  super(path, skip_initialization, object_version)
21
22
  @support_files_group = new_group('Targets Support Files')
22
23
  @refs_by_absolute_path = {}
24
+ @variant_groups_by_path_and_name = {}
23
25
  @pods = new_group('Pods')
24
26
  @development_pods = new_group('Development Pods')
25
27
  self.symroot = LEGACY_BUILD_ROOT
@@ -178,24 +180,13 @@ module Pod
178
180
  # @return [PBXFileReference] The new file reference.
179
181
  #
180
182
  def add_file_reference(absolute_path, group, reflect_file_system_structure = false)
181
- file_path_name = Pathname.new(absolute_path)
182
- unless file_path_name.absolute?
183
- raise ArgumentError, "Paths must be absolute #{absolute_path}"
184
- end
183
+ file_path_name = absolute_path.is_a?(Pathname) ? absolute_path : Pathname.new(absolute_path)
184
+ group = group_for_path_in_group(file_path_name, group, reflect_file_system_structure)
185
185
 
186
- if reflect_file_system_structure
187
- relative_path = file_path_name.relative_path_from(group.real_path)
188
- relative_dir = relative_path.dirname
189
- relative_dir.each_filename do|name|
190
- next if name == '.'
191
- group = group[name] || group.new_group(name, name)
192
- end
193
- end
194
-
195
- if ref = reference_for_path(absolute_path)
186
+ if ref = reference_for_path(file_path_name.realpath)
196
187
  ref
197
188
  else
198
- ref = group.new_file(absolute_path)
189
+ ref = group.new_file(file_path_name.realpath)
199
190
  @refs_by_absolute_path[absolute_path.to_s] = ref
200
191
  end
201
192
  end
@@ -250,18 +241,29 @@ module Pod
250
241
  #
251
242
  def add_build_configuration(name, type)
252
243
  build_configuration = super
253
- values = ["#{name.gsub(/[^a-zA-Z0-9_]/, '_').sub(/(^[0-9])/, '_\1').upcase}=1"]
254
244
  settings = build_configuration.build_settings
255
- definitions = Array(settings['GCC_PREPROCESSOR_DEFINITIONS'])
256
- values.each do |value|
245
+ definitions = settings['GCC_PREPROCESSOR_DEFINITIONS'] || ['$(inherited)']
246
+ defines = [defininition_for_build_configuration(name)]
247
+ defines << 'DEBUG' if type == :debug
248
+ defines.each do |define|
249
+ value = "#{define}=1"
257
250
  unless definitions.include?(value)
258
- definitions << value
251
+ definitions.unshift(value)
259
252
  end
260
253
  end
261
254
  settings['GCC_PREPROCESSOR_DEFINITIONS'] = definitions
262
255
  build_configuration
263
256
  end
264
257
 
258
+ # @param [String] name
259
+ # The name of the build configuration.
260
+ #
261
+ # @return [String] The preprocessor definition to set for the configuration.
262
+ #
263
+ def defininition_for_build_configuration(name)
264
+ "POD_CONFIGURATION_#{name.underscore}".gsub(/[^a-zA-Z0-9_]/, '_').upcase
265
+ end
266
+
265
267
  private
266
268
 
267
269
  # @!group Private helpers
@@ -272,6 +274,94 @@ module Pod
272
274
  #
273
275
  attr_reader :refs_by_absolute_path
274
276
 
277
+ # @return [Hash{[Pathname, String] => PBXVariantGroup}] The variant groups
278
+ # grouped by absolute path of parent dir and name.
279
+ #
280
+ attr_reader :variant_groups_by_path_and_name
281
+
282
+ # Returns the group for an absolute file path in another group.
283
+ # Creates subgroups to reflect the file system structure if
284
+ # reflect_file_system_structure is set to true.
285
+ # Makes a variant group if the path points to a localized file inside a
286
+ # *.lproj directory. To support Apple Base Internationalization, the same
287
+ # variant group is returned for interface files and strings files with
288
+ # the same name.
289
+ #
290
+ # @param [Pathname] absolute_pathname
291
+ # The pathname of the file to get the group for.
292
+ #
293
+ # @param [PBXGroup] group
294
+ # The parent group used as the base of the relative path.
295
+ #
296
+ # @param [Bool] reflect_file_system_structure
297
+ # Whether group structure should reflect the file system structure.
298
+ # If yes, where needed, intermediate groups are created, similar to
299
+ # how mkdir -p operates.
300
+ #
301
+ # @return [PBXGroup] The appropriate group for the filepath.
302
+ # Can be PBXVariantGroup, if the file is localized.
303
+ #
304
+ def group_for_path_in_group(absolute_pathname, group, reflect_file_system_structure)
305
+ unless absolute_pathname.absolute?
306
+ raise ArgumentError, "Paths must be absolute #{absolute_pathname}"
307
+ end
308
+
309
+ relative_pathname = absolute_pathname.relative_path_from(group.real_path)
310
+ relative_dir = relative_pathname.dirname
311
+ lproj_regex = /\.lproj/i
312
+
313
+ # Add subgroups for directories, but treat .lproj as a file
314
+ if reflect_file_system_structure
315
+ relative_dir.each_filename do|name|
316
+ break if name.to_s =~ lproj_regex
317
+ next if name == '.'
318
+ group = group[name] || group.new_group(name, name)
319
+ end
320
+ end
321
+
322
+ # Turn files inside .lproj directories into a variant group
323
+ if relative_dir.basename.to_s =~ lproj_regex
324
+ group_name = variant_group_name(absolute_pathname)
325
+ lproj_parent_dir = absolute_pathname.dirname.dirname
326
+ group = @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||
327
+ group.new_variant_group(group_name, lproj_parent_dir)
328
+ @variant_groups_by_path_and_name[[lproj_parent_dir, group_name]] ||= group
329
+ end
330
+
331
+ group
332
+ end
333
+
334
+ # Returns the name to be used for a the variant group for a file at a given path.
335
+ # The path must be localized (within an *.lproj directory).
336
+ #
337
+ # @param [Pathname] The localized path to get a variant group name for.
338
+ #
339
+ # @return [String] The variant group name.
340
+ #
341
+ def variant_group_name(path)
342
+ unless path.to_s.downcase.include?('.lproj/')
343
+ raise ArgumentError, 'Only localized resources can be added to variant groups.'
344
+ end
345
+
346
+ # When using Base Internationalization for XIBs and Storyboards a strings
347
+ # file is generated with the same name as the XIB/Storyboard in each .lproj
348
+ # directory:
349
+ # Base.lproj/MyViewController.xib
350
+ # fr.lproj/MyViewController.strings
351
+ #
352
+ # In this scenario we want the variant group to be the same as the XIB or Storyboard.
353
+ #
354
+ # Base Internationalization: https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPInternational/InternationalizingYourUserInterface/InternationalizingYourUserInterface.html
355
+ if path.extname.downcase == '.strings'
356
+ %w(.xib .storyboard).each do |extension|
357
+ possible_interface_file = path.dirname.dirname + 'Base.lproj' + path.basename.sub_ext(extension)
358
+ return possible_interface_file.basename.to_s if possible_interface_file.exist?
359
+ end
360
+ end
361
+
362
+ path.basename.to_s
363
+ end
364
+
275
365
  #-------------------------------------------------------------------------#
276
366
  end
277
367
  end