cocoapods 1.3.1 → 1.4.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.
@@ -7,6 +7,10 @@ module Pod
7
7
  #
8
8
  attr_accessor :specs
9
9
 
10
+ # @return [Array<Specification>] the test specs for the target
11
+ #
12
+ attr_accessor :test_specs
13
+
10
14
  # @return [Platform] the platform
11
15
  #
12
16
  attr_accessor :platform
@@ -24,16 +28,21 @@ module Pod
24
28
 
25
29
  # Initialize a new instance from its attributes.
26
30
  #
27
- # @param [Array<String>] specs @see #specs
28
- # @param [Platform] platform @see #platform
29
- # @param [Bool] requires_frameworks @see #requires_frameworks?
31
+ # @param [Array<Specification>] specs @see #specs
32
+ # @param [Array<Specification>] test_specs @see #test_specs
33
+ # @param [Platform] platform @see #platform
34
+ # @param [Bool] requires_frameworks @see #requires_frameworks?
30
35
  #
31
- def initialize(specs, platform, requires_frameworks = false)
36
+ def initialize(specs, test_specs, platform, requires_frameworks = false)
32
37
  self.specs = specs
38
+ self.test_specs = test_specs
33
39
  self.platform = platform
34
40
  self.requires_frameworks = requires_frameworks
35
41
  end
36
42
 
43
+ # @note Test specs are intentionally not included as part of the equality for pod variants since a
44
+ # pod variant should not be affected by the number of test specs included.
45
+ #
37
46
  # @return [Bool] whether the {PodVariant} is equal to another taking all
38
47
  # all their attributes into account
39
48
  #
@@ -161,8 +161,8 @@ module Pod
161
161
  "Unable to determine the platform for the `#{target_definition.name}` target."
162
162
  end
163
163
 
164
- UI.warn "Automatically assigning platform #{name} with version #{deployment_target} " \
165
- "on target #{target_definition.name} because no platform was specified. " \
164
+ UI.warn "Automatically assigning platform `#{name}` with version `#{deployment_target}` " \
165
+ "on target `#{target_definition.name}` because no platform was specified. " \
166
166
  "Please specify a platform for this target in your Podfile. See `#{PLATFORM_INFO_URL}`."
167
167
 
168
168
  target_definition.set_platform(name, deployment_target)
@@ -9,10 +9,15 @@ module Pod
9
9
  class TargetIntegrator
10
10
  autoload :XCConfigIntegrator, 'cocoapods/installer/user_project_integrator/target_integrator/xcconfig_integrator'
11
11
 
12
- # @return [String] the PACKAGE emoji to use as prefix for every build phase added to the user project
12
+ # @return [String] the string to use as prefix for every build phase added to the user project
13
13
  #
14
14
  BUILD_PHASE_PREFIX = '[CP] '.freeze
15
15
 
16
+ # @return [String] the string to use as prefix for every build phase declared by the user within a podfile
17
+ # or podspec.
18
+ #
19
+ USER_BUILD_PHASE_PREFIX = '[CP-User] '.freeze
20
+
16
21
  # @return [String] the name of the check manifest phase
17
22
  #
18
23
  CHECK_MANIFEST_PHASE_NAME = 'Check Pods Manifest.lock'.freeze
@@ -67,7 +72,7 @@ module Pod
67
72
  # @return [void]
68
73
  #
69
74
  def add_embed_frameworks_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = [])
70
- phase = TargetIntegrator.create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME)
75
+ phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + EMBED_FRAMEWORK_PHASE_NAME)
71
76
  phase.shell_script = %("#{script_path}"\n)
72
77
  unless input_paths.empty?
73
78
  phase.input_paths = input_paths
@@ -108,7 +113,7 @@ module Pod
108
113
  #
109
114
  def add_copy_resources_script_phase_to_target(native_target, script_path, input_paths = [], output_paths = [])
110
115
  phase_name = COPY_PODS_RESOURCES_PHASE_NAME
111
- phase = TargetIntegrator.create_or_update_build_phase(native_target, phase_name)
116
+ phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
112
117
  phase.shell_script = %("#{script_path}"\n)
113
118
  unless input_paths.empty?
114
119
  phase.input_paths = input_paths
@@ -132,12 +137,11 @@ module Pod
132
137
  # @return [void]
133
138
  #
134
139
  def create_or_update_build_phase(native_target, phase_name, phase_class = Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
135
- prefixed_phase_name = BUILD_PHASE_PREFIX + phase_name
136
140
  build_phases = native_target.build_phases.grep(phase_class)
137
- build_phases.find { |phase| phase.name && phase.name.end_with?(phase_name) }.tap { |p| p.name = prefixed_phase_name if p } ||
141
+ build_phases.find { |phase| phase.name && phase.name.end_with?(phase_name) }.tap { |p| p.name = phase_name if p } ||
138
142
  native_target.project.new(phase_class).tap do |phase|
139
- UI.message("Adding Build Phase '#{prefixed_phase_name}' to project.") do
140
- phase.name = prefixed_phase_name
143
+ UI.message("Adding Build Phase '#{phase_name}' to project.") do
144
+ phase.name = phase_name
141
145
  phase.show_env_vars_in_log = '0'
142
146
  native_target.build_phases << phase
143
147
  end
@@ -160,6 +164,7 @@ module Pod
160
164
  remove_embed_frameworks_script_phase_from_embedded_targets
161
165
  add_copy_resources_script_phase
162
166
  add_check_manifest_lock_script_phase
167
+ update_target_script_phases
163
168
  end
164
169
  end
165
170
 
@@ -255,6 +260,46 @@ module Pod
255
260
  end
256
261
  end
257
262
 
263
+ # Updates all target script phases for the current target, including creating or updating, deleting
264
+ # and re-ordering.
265
+ #
266
+ # @return [void]
267
+ #
268
+ def update_target_script_phases
269
+ target_definition_script_phases = target.target_definition.script_phases
270
+ target_definition_script_phase_names = target_definition_script_phases.map { |k| k[:name] }
271
+ native_targets.each do |native_target|
272
+ # Delete script phases no longer present in the target definition.
273
+ native_target_script_phases = native_target.shell_script_build_phases.select { |bp| !bp.name.nil? && bp.name.start_with?(USER_BUILD_PHASE_PREFIX) }
274
+ native_target_script_phases.each do |script_phase|
275
+ script_phase_name_without_prefix = script_phase.name.sub(USER_BUILD_PHASE_PREFIX, '')
276
+ unless target_definition_script_phase_names.include?(script_phase_name_without_prefix)
277
+ native_target.build_phases.delete(script_phase)
278
+ end
279
+ end
280
+
281
+ # Create or update the ones that are expected to be.
282
+ target_definition_script_phases.each do |td_script_phase|
283
+ phase = TargetIntegrator.create_or_update_build_phase(native_target, USER_BUILD_PHASE_PREFIX + td_script_phase[:name])
284
+ phase.shell_script = td_script_phase[:script]
285
+ phase.shell_path = td_script_phase[:shell_path] if td_script_phase.key?(:shell_path)
286
+ phase.input_paths = td_script_phase[:input_files] if td_script_phase.key?(:input_files)
287
+ phase.output_paths = td_script_phase[:output_files] if td_script_phase.key?(:output_files)
288
+ phase.show_env_vars_in_log = td_script_phase[:show_env_vars_in_log] ? '1' : '0' if td_script_phase.key?(:show_env_vars_in_log)
289
+ end
290
+
291
+ # Move script phases to their correct index if the order has changed.
292
+ offset = native_target.build_phases.count - target_definition_script_phases.count
293
+ target_definition_script_phases.each_with_index do |td_script_phase, index|
294
+ current_index = native_target.build_phases.index do |bp|
295
+ bp.is_a?(Xcodeproj::Project::Object::PBXShellScriptBuildPhase) && bp.name.sub(USER_BUILD_PHASE_PREFIX, '') == td_script_phase[:name]
296
+ end
297
+ expected_index = offset + index
298
+ native_target.build_phases.insert(expected_index, native_target.build_phases.delete_at(current_index)) if current_index != expected_index
299
+ end
300
+ end
301
+ end
302
+
258
303
  # Adds a shell script build phase responsible for checking if the Pods
259
304
  # locked in the Pods/Manifest.lock file are in sync with the Pods defined
260
305
  # in the Podfile.lock.
@@ -267,7 +312,7 @@ module Pod
267
312
  def add_check_manifest_lock_script_phase
268
313
  phase_name = CHECK_MANIFEST_PHASE_NAME
269
314
  native_targets.each do |native_target|
270
- phase = TargetIntegrator.create_or_update_build_phase(native_target, phase_name)
315
+ phase = TargetIntegrator.create_or_update_build_phase(native_target, BUILD_PHASE_PREFIX + phase_name)
271
316
  native_target.build_phases.unshift(phase).uniq! unless native_target.build_phases.first == phase
272
317
  phase.shell_script = <<-SH.strip_heredoc
273
318
  diff "${PODS_PODFILE_DIR_PATH}/Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
@@ -208,12 +208,14 @@ module Pod
208
208
  #
209
209
  def set_target_dependencies
210
210
  frameworks_group = project.frameworks_group
211
+ test_only_pod_targets = pod_targets.dup
211
212
  aggregate_targets.each do |aggregate_target|
212
213
  is_app_extension = !(aggregate_target.user_targets.map(&:symbol_type) &
213
214
  [:app_extension, :watch_extension, :watch2_extension, :tv_extension, :messages_extension]).empty?
214
215
  is_app_extension ||= aggregate_target.user_targets.any? { |ut| ut.common_resolved_build_setting('APPLICATION_EXTENSION_API_ONLY') == 'YES' }
215
216
 
216
217
  aggregate_target.pod_targets.each do |pod_target|
218
+ test_only_pod_targets.delete(pod_target)
217
219
  configure_app_extension_api_only_for_target(aggregate_target) if is_app_extension
218
220
 
219
221
  unless pod_target.should_build?
@@ -225,8 +227,24 @@ module Pod
225
227
  aggregate_target.native_target.add_dependency(pod_target.native_target)
226
228
  configure_app_extension_api_only_for_target(pod_target) if is_app_extension
227
229
 
228
- add_dependent_targets_to_native_target(pod_target.dependent_targets, pod_target.native_target, is_app_extension, pod_target.requires_frameworks?, frameworks_group)
229
-
230
+ unless pod_target.static_framework?
231
+ add_dependent_targets_to_native_target(pod_target.dependent_targets,
232
+ pod_target.native_target, is_app_extension,
233
+ pod_target.requires_frameworks?, frameworks_group)
234
+ add_pod_target_test_dependencies(pod_target, frameworks_group)
235
+ end
236
+ end
237
+ end
238
+ # Wire up remaining pod targets used only by tests and are not used by any aggregate target.
239
+ test_only_pod_targets.each do |pod_target|
240
+ unless pod_target.should_build?
241
+ add_pod_target_test_dependencies(pod_target, frameworks_group)
242
+ next
243
+ end
244
+ unless pod_target.static_framework?
245
+ add_dependent_targets_to_native_target(pod_target.dependent_targets,
246
+ pod_target.native_target, false,
247
+ pod_target.requires_frameworks?, frameworks_group)
230
248
  add_pod_target_test_dependencies(pod_target, frameworks_group)
231
249
  end
232
250
  end
@@ -242,7 +242,7 @@ module Pod
242
242
  end
243
243
 
244
244
  # Returns a Pathname of the nearest parent from which all the given paths descend.
245
- # Converts each Pathname to a string and finds the longest common prefix
245
+ # Converts each Pathname to a list of path components and finds the longest common prefix
246
246
  #
247
247
  # @param [Array<Pathname>] paths
248
248
  # The paths to files or directories on disk. Must be absolute paths
@@ -258,10 +258,12 @@ module Pod
258
258
  path.dirname.to_s
259
259
  end
260
260
  min, max = strs.minmax
261
+ min = min.split('/')
262
+ max = max.split('/')
261
263
  idx = min.size.times { |i| break i if min[i] != max[i] }
262
- result = Pathname.new(min[0...idx])
264
+ result = Pathname.new(min[0...idx].join('/'))
263
265
  # Don't consider "/" a common path
264
- return result unless result.to_s == '/'
266
+ return result unless result.to_s == '' || result.to_s == '/'
265
267
  end
266
268
 
267
269
  # Computes the destination sub-directory in the sandbox
@@ -25,7 +25,9 @@ module Pod
25
25
  create_xcconfig_file
26
26
  create_test_xcconfig_files if target.contains_test_specifications?
27
27
  if target.requires_frameworks?
28
- create_info_plist_file
28
+ unless target.static_framework?
29
+ create_info_plist_file
30
+ end
29
31
  create_module_map
30
32
  create_umbrella_header do |generator|
31
33
  file_accessors = target.file_accessors
@@ -39,6 +41,9 @@ module Pod
39
41
  end
40
42
  end
41
43
  create_build_phase_to_symlink_header_folders
44
+ if target.static_framework?
45
+ create_build_phase_to_move_static_framework_archive
46
+ end
42
47
  end
43
48
  create_prefix_header
44
49
  create_dummy_source
@@ -194,6 +199,8 @@ module Pod
194
199
  # requires frameworks. For tests we always use the test target name as the product name
195
200
  # irrelevant to whether we use frameworks or not.
196
201
  configuration.build_settings['PRODUCT_NAME'] = name
202
+ # We must codesign iOS XCTest bundles that contain binary frameworks to allow them to be launchable in the simulator
203
+ configuration.build_settings['CODE_SIGNING_REQUIRED'] = 'YES' unless target.platform == :osx
197
204
  end
198
205
 
199
206
  # Test native targets also need frameworks and resources to be copied over to their xctest bundle.
@@ -219,7 +226,6 @@ module Pod
219
226
  bundle_target.product_reference.tap do |bundle_product|
220
227
  bundle_file_name = "#{bundle_name}.bundle"
221
228
  bundle_product.name = bundle_file_name
222
- bundle_product.path = bundle_file_name
223
229
  end
224
230
 
225
231
  filter_resource_file_references(paths) do |resource_phase_refs, compile_phase_refs|
@@ -388,6 +394,22 @@ module Pod
388
394
  eos
389
395
  end
390
396
 
397
+ # Creates a build phase to put the static framework archive in the appropriate framework location
398
+ # Since Xcode does not provide template support for static library frameworks, we've built a static library
399
+ # of the form lib{LibraryName}.a. We need to move that to the framework location -
400
+ # {LibraryName}.framework/{LibraryName}.
401
+ #
402
+ # @return [void]
403
+ #
404
+ def create_build_phase_to_move_static_framework_archive
405
+ build_phase = native_target.new_shell_script_build_phase('Setup Static Framework Archive')
406
+ build_phase.shell_script = <<-eos.strip_heredoc
407
+ mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Modules"
408
+ cp "${BUILT_PRODUCTS_DIR}/lib${PRODUCT_NAME}.a" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
409
+ cp "${MODULEMAP_FILE}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Modules/module.modulemap"
410
+ eos
411
+ end
412
+
391
413
  # Creates a prefix header file which imports `UIKit` or `Cocoa` according
392
414
  # to the platform of the target. This file also include any prefix header
393
415
  # content reported by the specification of the pods.
@@ -81,7 +81,12 @@ module Pod
81
81
  end
82
82
 
83
83
  if target.requires_frameworks?
84
- settings['PRODUCT_NAME'] = target.product_module_name
84
+ framework_name = target.product_module_name
85
+ settings['PRODUCT_NAME'] = framework_name
86
+ if target.static_framework?
87
+ settings['PUBLIC_HEADERS_FOLDER_PATH'] = framework_name + '.framework' + '/Headers'
88
+ settings['PRIVATE_HEADERS_FOLDER_PATH'] = framework_name + '.framework' + '/PrivateHeaders'
89
+ end
85
90
  else
86
91
  settings.merge!('OTHER_LDFLAGS' => '', 'OTHER_LIBTOOLFLAGS' => '')
87
92
  end
@@ -73,7 +73,7 @@ module Pod
73
73
  aggregate_target.user_build_configurations.keys.each do |config|
74
74
  pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
75
75
 
76
- dependencies = pod_targets.select(&:should_build?).flat_map(&:dependencies)
76
+ dependencies = pod_targets.select(&:should_build?).reject(&:static_framework?).flat_map(&:dependencies)
77
77
  depended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
78
78
 
79
79
  static_libs = depended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
@@ -12,6 +12,39 @@ module Pod
12
12
  # by target for a given Podfile.
13
13
  #
14
14
  class Resolver
15
+ # A small container that wraps a resolved specification for a given target definition. Additional metadata
16
+ # is included here such as if the specification is only used by tests.
17
+ #
18
+ class ResolverSpecification
19
+ # @return [Specification] the specification that was resolved
20
+ #
21
+ attr_reader :spec
22
+
23
+ # @return [Bool] whether this resolved specification is only used by tests.
24
+ #
25
+ attr_reader :used_by_tests_only
26
+ alias used_by_tests_only? used_by_tests_only
27
+
28
+ def initialize(spec, used_by_tests_only)
29
+ @spec = spec
30
+ @used_by_tests_only = used_by_tests_only
31
+ end
32
+
33
+ def name
34
+ spec.name
35
+ end
36
+
37
+ def root
38
+ spec.root
39
+ end
40
+
41
+ def ==(other)
42
+ self.class == other &&
43
+ spec == other.spec &&
44
+ used_by_tests_only == other.test_only
45
+ end
46
+ end
47
+
15
48
  include Pod::Installer::InstallationOptions::Mixin
16
49
 
17
50
  delegate_installation_options { podfile }
@@ -64,8 +97,8 @@ module Pod
64
97
 
65
98
  # Identifies the specifications that should be installed.
66
99
  #
67
- # @return [Hash{TargetDefinition => Array<Specification>}] specs_by_target
68
- # the specifications that need to be installed grouped by target
100
+ # @return [Hash{TargetDefinition => Array<ResolverSpecification>}] resolver_specs_by_target
101
+ # the resolved specifications that need to be installed grouped by target
69
102
  # definition.
70
103
  #
71
104
  def resolve
@@ -75,28 +108,28 @@ module Pod
75
108
  end
76
109
  end
77
110
  @activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
78
- specs_by_target
111
+ resolver_specs_by_target
79
112
  rescue Molinillo::ResolverError => e
80
113
  handle_resolver_error(e)
81
114
  end
82
115
 
83
- # @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
116
+ # @return [Hash{Podfile::TargetDefinition => Array<ResolverSpecification>}]
84
117
  # returns the resolved specifications grouped by target.
85
118
  #
86
119
  # @note The returned specifications can be subspecs.
87
120
  #
88
- def specs_by_target
89
- @specs_by_target ||= {}.tap do |specs_by_target|
121
+ def resolver_specs_by_target
122
+ @resolver_specs_by_target ||= {}.tap do |resolver_specs_by_target|
90
123
  podfile.target_definition_list.each do |target|
91
124
  dependencies = {}
92
125
  specs = target.dependencies.map(&:name).flat_map do |name|
93
126
  node = @activated.vertex_named(name)
94
- valid_dependencies_for_target_from_node(target, dependencies, node) << node
127
+ (valid_dependencies_for_target_from_node(target, dependencies, node) << node).map { |s| [s, node.payload.test_specification?] }
95
128
  end
96
129
 
97
- specs_by_target[target] = specs.
98
- map(&:payload).
99
- uniq.
130
+ resolver_specs_by_target[target] = specs.
131
+ group_by(&:first).
132
+ map { |vertex, spec_test_only_tuples| ResolverSpecification.new(vertex.payload, spec_test_only_tuples.map { |tuple| tuple[1] }.all?) }.
100
133
  sort_by(&:name)
101
134
  end
102
135
  end
@@ -179,21 +212,33 @@ module Pod
179
212
  # @param [Specification] spec the specification in question.
180
213
  #
181
214
  def requirement_satisfied_by?(requirement, activated, spec)
182
- existing_vertices = activated.vertices.values.select do |v|
183
- Specification.root_name(v.name) == requirement.root_name
184
- end
185
- existing = existing_vertices.map(&:payload).compact.first
186
- requirement_satisfied =
187
- if existing
188
- existing.version == spec.version && requirement.requirement.satisfied_by?(spec.version)
215
+ version = spec.version
216
+ return false unless requirement.requirement.satisfied_by?(version)
217
+ shared_possibility_versions, prerelease_requirement = possibility_versions_for_root_name(requirement, activated)
218
+ return false if !shared_possibility_versions.empty? && !shared_possibility_versions.include?(version)
219
+ return false if version.prerelease? && !prerelease_requirement
220
+ return false unless spec_is_platform_compatible?(activated, requirement, spec)
221
+ true
222
+ end
223
+
224
+ def possibility_versions_for_root_name(requirement, activated)
225
+ prerelease_requirement = requirement.prerelease? || requirement.external_source
226
+ existing = activated.vertices.values.flat_map do |vertex|
227
+ next unless vertex.payload
228
+ next unless Specification.root_name(vertex.name) == requirement.root_name
229
+
230
+ prerelease_requirement ||= vertex.requirements.any? { |r| r.prerelease? || r.external_source }
231
+
232
+ if vertex.payload.respond_to?(:possibilities)
233
+ vertex.payload.possibilities.map(&:version)
189
234
  else
190
- requirement.requirement.satisfied_by? spec.version
235
+ vertex.payload.version
191
236
  end
192
- requirement_satisfied && !(
193
- spec.version.prerelease? &&
194
- existing_vertices.flat_map(&:requirements).none? { |r| r.prerelease? || r.external_source }
195
- ) && spec_is_platform_compatible?(activated, requirement, spec)
237
+ end.compact
238
+
239
+ [existing, prerelease_requirement]
196
240
  end
241
+ private :possibility_versions_for_root_name
197
242
 
198
243
  # Sort dependencies so that the ones that are easiest to resolve are first.
199
244
  # Easiest to resolve is (usually) defined by:
@@ -399,64 +444,69 @@ module Pod
399
444
  # @param [Molinillo::ResolverError] error
400
445
  #
401
446
  def handle_resolver_error(error)
402
- message = error.message.dup
447
+ message = error.message
403
448
  type = Informative
404
449
  case error
405
450
  when Molinillo::VersionConflict
406
- error.conflicts.each do |name, conflict|
407
- local_pod_parent = conflict.requirement_trees.flatten.reverse.find(&:local?)
408
- lockfile_reqs = conflict.requirements[name_for_locking_dependency_source]
409
- if lockfile_reqs && lockfile_reqs.last && lockfile_reqs.last.prerelease? && !conflict.existing
410
- message = 'Due to the previous naïve CocoaPods resolver, ' \
411
- "you were using a pre-release version of `#{name}`, " \
412
- 'without explicitly asking for a pre-release version, which now leads to a conflict. ' \
413
- 'Please decide to either use that pre-release version by adding the ' \
414
- 'version requirement to your Podfile ' \
415
- "(e.g. `pod '#{name}', '#{lockfile_reqs.map(&:requirement).join("', '")}'`) " \
416
- "or revert to a stable version by running `pod update #{name}`."
417
- elsif local_pod_parent && !specifications_for_dependency(conflict.requirement).empty? && !conflict.possibility
418
- # Conflict was caused by a requirement from a local dependency.
419
- # Tell user to use `pod update`.
420
- message << "\n\nIt seems like you've changed the constraints of dependency `#{name}` " \
421
- "inside your development pod `#{local_pod_parent.name}`.\nYou should run `pod update #{name}` to apply " \
422
- "changes you've made."
423
- elsif (conflict.possibility && conflict.possibility.version.prerelease?) &&
424
- (conflict.requirement && !(
425
- conflict.requirement.prerelease? ||
426
- conflict.requirement.external_source)
427
- )
428
- # Conflict was caused by not specifying an explicit version for the requirement #[name],
429
- # and there is no available stable version satisfying constraints for the requirement.
430
- message = "There are only pre-release versions available satisfying the following requirements:\n"
431
- conflict.requirements.values.flatten.each do |r|
432
- unless search_for(r).empty?
433
- message << "\n\t'#{name}', '#{r.requirement}'\n"
451
+ message = error.message_with_trees(
452
+ :solver_name => 'CocoaPods',
453
+ :possibility_type => 'pod',
454
+ :version_for_spec => lambda(&:version),
455
+ :additional_message_for_conflict => lambda do |o, name, conflict|
456
+ local_pod_parent = conflict.requirement_trees.flatten.reverse.find(&:local?)
457
+ lockfile_reqs = conflict.requirements[name_for_locking_dependency_source]
458
+ if lockfile_reqs && lockfile_reqs.last && lockfile_reqs.last.prerelease? && !conflict.existing
459
+ o << "\nDue to the previous naïve CocoaPods resolver, " \
460
+ "you were using a pre-release version of `#{name}`, " \
461
+ 'without explicitly asking for a pre-release version, which now leads to a conflict. ' \
462
+ 'Please decide to either use that pre-release version by adding the ' \
463
+ 'version requirement to your Podfile ' \
464
+ "(e.g. `pod '#{name}', '#{lockfile_reqs.map(&:requirement).join("', '")}'`) " \
465
+ "or revert to a stable version by running `pod update #{name}`."
466
+ elsif local_pod_parent && !specifications_for_dependency(conflict.requirement).empty? && !conflict.possibility && conflict.locked_requirement
467
+ # Conflict was caused by a requirement from a local dependency.
468
+ # Tell user to use `pod update`.
469
+ o << "\nIt seems like you've changed the constraints of dependency `#{name}` " \
470
+ "inside your development pod `#{local_pod_parent.name}`.\nYou should run `pod update #{name}` to apply " \
471
+ "changes you've made."
472
+ elsif (conflict.possibility && conflict.possibility.version.prerelease?) &&
473
+ (conflict.requirement && !(
474
+ conflict.requirement.prerelease? ||
475
+ conflict.requirement.external_source)
476
+ )
477
+ # Conflict was caused by not specifying an explicit version for the requirement #[name],
478
+ # and there is no available stable version satisfying constraints for the requirement.
479
+ o << "\nThere are only pre-release versions available satisfying the following requirements:\n"
480
+ conflict.requirements.values.flatten.each do |r|
481
+ unless search_for(r).empty?
482
+ o << "\n\t'#{name}', '#{r.requirement}'\n"
483
+ end
434
484
  end
435
- end
436
- message << "\nYou should explicitly specify the version in order to install a pre-release version"
437
- elsif !conflict.existing
438
- conflicts = conflict.requirements.values.flatten.uniq
439
- found_conflicted_specs = conflicts.reject { |c| search_for(c).empty? }
440
- if found_conflicted_specs.empty?
441
- # There are no existing specification inside any of the spec repos with given requirements.
442
- type = NoSpecFoundError
443
- dependencies = conflicts.count == 1 ? 'dependency' : 'dependencies'
444
- message << "\n\nNone of your spec sources contain a spec satisfying "\
445
- "the #{dependencies}: `#{conflicts.join(', ')}`." \
446
- "\n\nYou have either:"
447
- unless specs_updated?
448
- message << "\n * out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`."
485
+ o << "\nYou should explicitly specify the version in order to install a pre-release version"
486
+ elsif !conflict.existing
487
+ conflicts = conflict.requirements.values.flatten.uniq
488
+ found_conflicted_specs = conflicts.reject { |c| search_for(c).empty? }
489
+ if found_conflicted_specs.empty?
490
+ # There are no existing specification inside any of the spec repos with given requirements.
491
+ type = NoSpecFoundError
492
+ dependencies = conflicts.count == 1 ? 'dependency' : 'dependencies'
493
+ o << "\nNone of your spec sources contain a spec satisfying "\
494
+ "the #{dependencies}: `#{conflicts.join(', ')}`." \
495
+ "\n\nYou have either:"
496
+ unless specs_updated?
497
+ o << "\n * out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`."
498
+ end
499
+ o << "\n * mistyped the name or version." \
500
+ "\n * not added the source repo that hosts the Podspec to your Podfile." \
501
+ "\n\nNote: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default."
502
+
503
+ else
504
+ o << "\nSpecs satisfying the `#{conflicts.join(', ')}` dependency were found, " \
505
+ 'but they required a higher minimum deployment target.'
449
506
  end
450
- message << "\n * mistyped the name or version." \
451
- "\n * not added the source repo that hosts the Podspec to your Podfile." \
452
- "\n\nNote: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by default."
453
-
454
- else
455
- message << "\n\nSpecs satisfying the `#{conflicts.join(', ')}` dependency were found, " \
456
- 'but they required a higher minimum deployment target.'
457
507
  end
458
- end
459
- end
508
+ end,
509
+ )
460
510
  end
461
511
  raise type, message
462
512
  end
@@ -500,7 +550,6 @@ module Pod
500
550
 
501
551
  dependency_nodes + dependency_nodes.flat_map do |item|
502
552
  node_result = valid_dependencies_for_target_from_node(target, dependencies, item)
503
-
504
553
  node_result
505
554
  end
506
555
  end