cocoapods 1.0.0 → 1.1.0

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +329 -0
  3. data/lib/cocoapods/command/init.rb +6 -6
  4. data/lib/cocoapods/command/ipc/list.rb +40 -0
  5. data/lib/cocoapods/command/ipc/podfile.rb +31 -0
  6. data/lib/cocoapods/command/ipc/repl.rb +51 -0
  7. data/lib/cocoapods/command/ipc/spec.rb +29 -0
  8. data/lib/cocoapods/command/ipc/update_search_index.rb +24 -0
  9. data/lib/cocoapods/command/ipc.rb +18 -0
  10. data/lib/cocoapods/command/lib/create.rb +105 -0
  11. data/lib/cocoapods/command/lib/lint.rb +111 -0
  12. data/lib/cocoapods/command/lib.rb +3 -207
  13. data/lib/cocoapods/command/repo/push.rb +44 -20
  14. data/lib/cocoapods/command/setup.rb +2 -1
  15. data/lib/cocoapods/command/spec/lint.rb +4 -0
  16. data/lib/cocoapods/command.rb +2 -1
  17. data/lib/cocoapods/config.rb +4 -1
  18. data/lib/cocoapods/downloader/cache.rb +1 -0
  19. data/lib/cocoapods/downloader.rb +20 -0
  20. data/lib/cocoapods/executable.rb +1 -1
  21. data/lib/cocoapods/gem_version.rb +1 -1
  22. data/lib/cocoapods/generator/acknowledgements/plist.rb +4 -1
  23. data/lib/cocoapods/generator/copy_resources_script.rb +4 -10
  24. data/lib/cocoapods/generator/header.rb +2 -1
  25. data/lib/cocoapods/generator/prefix_header.rb +0 -12
  26. data/lib/cocoapods/generator/xcconfig/aggregate_xcconfig.rb +38 -5
  27. data/lib/cocoapods/generator/xcconfig/xcconfig_helper.rb +5 -4
  28. data/lib/cocoapods/installer/analyzer/pod_variant_set.rb +5 -1
  29. data/lib/cocoapods/installer/analyzer/target_inspector.rb +24 -1
  30. data/lib/cocoapods/installer/analyzer.rb +161 -1
  31. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +29 -9
  32. data/lib/cocoapods/installer/xcode/pods_project_generator/aggregate_target_installer.rb +204 -0
  33. data/lib/cocoapods/installer/xcode/pods_project_generator/file_references_installer.rb +314 -0
  34. data/lib/cocoapods/installer/xcode/pods_project_generator/pod_target_installer.rb +401 -0
  35. data/lib/cocoapods/installer/xcode/pods_project_generator/target_installer.rb +214 -0
  36. data/lib/cocoapods/installer/xcode/pods_project_generator.rb +265 -0
  37. data/lib/cocoapods/installer/xcode.rb +7 -0
  38. data/lib/cocoapods/installer.rb +50 -214
  39. data/lib/cocoapods/resolver.rb +15 -9
  40. data/lib/cocoapods/sandbox/headers_store.rb +4 -10
  41. data/lib/cocoapods/sandbox/path_list.rb +20 -9
  42. data/lib/cocoapods/sources_manager.rb +7 -10
  43. data/lib/cocoapods/target/aggregate_target.rb +20 -0
  44. data/lib/cocoapods/target/pod_target.rb +37 -7
  45. data/lib/cocoapods/user_interface/error_report.rb +7 -0
  46. data/lib/cocoapods/user_interface/inspector_reporter.rb +109 -0
  47. data/lib/cocoapods/user_interface.rb +7 -5
  48. data/lib/cocoapods/validator.rb +59 -11
  49. metadata +112 -83
  50. data/lib/cocoapods/command/inter_process_communication.rb +0 -177
  51. data/lib/cocoapods/installer/file_references_installer.rb +0 -310
  52. data/lib/cocoapods/installer/migrator.rb +0 -86
  53. data/lib/cocoapods/installer/target_installer/aggregate_target_installer.rb +0 -191
  54. data/lib/cocoapods/installer/target_installer/pod_target_installer.rb +0 -368
  55. data/lib/cocoapods/installer/target_installer.rb +0 -210
@@ -73,9 +73,10 @@ module Pod
73
73
  def specs_by_target
74
74
  @specs_by_target ||= {}.tap do |specs_by_target|
75
75
  podfile.target_definition_list.each do |target|
76
+ dependencies = {}
76
77
  specs = target.dependencies.map(&:name).flat_map do |name|
77
78
  node = @activated.vertex_named(name)
78
- valid_dependencies_for_target_from_node(target, node) << node
79
+ valid_dependencies_for_target_from_node(target, dependencies, node) << node
79
80
  end
80
81
 
81
82
  specs_by_target[target] = specs.
@@ -389,7 +390,6 @@ module Pod
389
390
  error.conflicts.each do |name, conflict|
390
391
  local_pod_parent = conflict.requirement_trees.flatten.reverse.find(&:local?)
391
392
  lockfile_reqs = conflict.requirements[name_for_locking_dependency_source]
392
-
393
393
  if lockfile_reqs && lockfile_reqs.last && lockfile_reqs.last.prerelease? && !conflict.existing
394
394
  message = 'Due to the previous naïve CocoaPods resolver, ' \
395
395
  "you were using a pre-release version of `#{name}`, " \
@@ -398,7 +398,7 @@ module Pod
398
398
  'version requirement to your Podfile ' \
399
399
  "(e.g. `pod '#{name}', '#{lockfile_reqs.map(&:requirement).join("', '")}'`) " \
400
400
  "or revert to a stable version by running `pod update #{name}`."
401
- elsif local_pod_parent && !specifications_for_dependency(conflict.requirement).empty?
401
+ elsif local_pod_parent && !specifications_for_dependency(conflict.requirement).empty? && !conflict.possibility
402
402
  # Conflict was caused by a requirement from a local dependency.
403
403
  # Tell user to use `pod update`.
404
404
  message << "\n\nIt seems like you've changed the constraints of dependency `#{name}` " \
@@ -472,13 +472,19 @@ module Pod
472
472
  # An array of target-appropriate nodes whose `payload`s are
473
473
  # dependencies for `target`.
474
474
  #
475
- def valid_dependencies_for_target_from_node(target, node)
476
- validate_platform(node.payload, target)
477
- dependency_nodes = node.outgoing_edges.select do |edge|
478
- edge_is_valid_for_target?(edge, target)
479
- end.map(&:destination)
475
+ def valid_dependencies_for_target_from_node(target, dependencies, node)
476
+ dependencies[node.name] ||= begin
477
+ validate_platform(node.payload, target)
478
+ dependency_nodes = node.outgoing_edges.select do |edge|
479
+ edge_is_valid_for_target?(edge, target)
480
+ end.map(&:destination)
481
+
482
+ dependency_nodes + dependency_nodes.flat_map do |item|
483
+ node_result = valid_dependencies_for_target_from_node(target, dependencies, item)
480
484
 
481
- dependency_nodes + dependency_nodes.flat_map { |n| valid_dependencies_for_target_from_node(target, n) }
485
+ node_result
486
+ end
487
+ end
482
488
  end
483
489
 
484
490
  # Whether the given `edge` should be followed to find dependencies for the
@@ -72,11 +72,11 @@ module Pod
72
72
  #
73
73
  def add_files(namespace, relative_header_paths)
74
74
  relative_header_paths.map do |relative_header_path|
75
- add_file(namespace, relative_header_path, relative_header_path.basename)
75
+ add_file(namespace, relative_header_path)
76
76
  end
77
77
  end
78
78
 
79
- # Adds a header to the directory under different name.
79
+ # Adds a header to the directory.
80
80
  #
81
81
  # @param [Pathname] namespace
82
82
  # the path where the header file should be stored relative to the
@@ -86,23 +86,17 @@ module Pod
86
86
  # the path of the header file relative to the Pods project
87
87
  # (`PODS_ROOT` variable of the xcconfigs).
88
88
  #
89
- # @param [String] final_name
90
- # the name under which the file should be available in the
91
- # headers directory.
92
- #
93
89
  # @note This method does _not_ add the file to the search paths.
94
90
  #
95
91
  # @return [Pathname]
96
92
  #
97
- def add_file(namespace, relative_header_path, final_name)
93
+ def add_file(namespace, relative_header_path)
98
94
  namespaced_path = root + namespace
99
95
  namespaced_path.mkpath unless File.exist?(namespaced_path)
100
96
 
101
97
  absolute_source = (sandbox.root + relative_header_path)
102
98
  source = absolute_source.relative_path_from(namespaced_path)
103
- Dir.chdir(namespaced_path) do
104
- FileUtils.ln_sf(source, final_name)
105
- end
99
+ FileUtils.ln_sf(source, namespaced_path)
106
100
  namespaced_path + relative_header_path.basename
107
101
  end
108
102
 
@@ -1,3 +1,5 @@
1
+ require 'active_support/multibyte/unicode'
2
+
1
3
  module Pod
2
4
  class Sandbox
3
5
  # The PathList class is designed to perform multiple glob matches against
@@ -20,7 +22,8 @@ module Pod
20
22
  # @param [Pathname] root The root of the PathList.
21
23
  #
22
24
  def initialize(root)
23
- @root = root
25
+ root_dir = ActiveSupport::Multibyte::Unicode.normalize(root.to_s)
26
+ @root = Pathname.new(root_dir)
24
27
  @glob_cache = {}
25
28
  end
26
29
 
@@ -47,15 +50,23 @@ module Pod
47
50
  unless root.exist?
48
51
  raise Informative, "Attempt to read non existent folder `#{root}`."
49
52
  end
50
- root_length = root.to_s.length + 1
51
53
  escaped_root = escape_path_for_glob(root)
52
- paths = Dir.glob(escaped_root + '**/*', File::FNM_DOTMATCH).sort_by(&:upcase)
53
- absolute_dirs = paths.select { |path| File.directory?(path) }
54
- relative_dirs = absolute_dirs.map { |p| p[root_length..-1] }
55
- absolute_paths = paths.reject { |p| p == "#{root}/." || p == "#{root}/.." }
56
- relative_paths = absolute_paths.map { |p| p[root_length..-1] }
57
- @files = relative_paths - relative_dirs
58
- @dirs = relative_dirs.map { |d| d.gsub(/\/\.\.?$/, '') }.reject { |d| d == '.' || d == '..' } .uniq
54
+
55
+ absolute_paths = Pathname.glob(escaped_root + '**/*', File::FNM_DOTMATCH).lazy
56
+ dirs_and_files = absolute_paths.reject { |path| path.basename.to_s =~ /^\.\.?$/ }
57
+ dirs, files = dirs_and_files.partition { |path| File.directory?(path) }
58
+
59
+ root_length = root.cleanpath.to_s.length + File::SEPARATOR.length
60
+ sorted_relative_paths_from_full_paths = lambda do |paths|
61
+ relative_paths = paths.lazy.map do |path|
62
+ path_string = path.to_s
63
+ path_string.slice(root_length, path_string.length - root_length)
64
+ end
65
+ relative_paths.sort_by(&:upcase)
66
+ end
67
+
68
+ @dirs = sorted_relative_paths_from_full_paths.call(dirs)
69
+ @files = sorted_relative_paths_from_full_paths.call(files)
59
70
  @glob_cache = {}
60
71
  end
61
72
 
@@ -1,4 +1,5 @@
1
1
  require 'cocoapods-core/source'
2
+ require 'set'
2
3
 
3
4
  module Pod
4
5
  class Source
@@ -102,24 +103,22 @@ module Pod
102
103
  end
103
104
 
104
105
  def update_git_repo(show_output = false)
105
- ensure_in_repo!
106
106
  Config.instance.with_changes(:verbose => show_output) do
107
- git!(%w(pull --ff-only))
107
+ git!(['-C', repo, 'pull', '--ff-only'])
108
108
  end
109
109
  rescue
110
- UI.warn 'CocoaPods was not able to update the ' \
111
- "`#{name}` repo. If this is an unexpected issue " \
112
- 'and persists you can inspect it running ' \
113
- '`pod repo update --verbose`'
110
+ raise Informative, 'CocoaPods was not able to update the ' \
111
+ "`#{name}` repo. If this is an unexpected issue " \
112
+ 'and persists you can inspect it running ' \
113
+ '`pod repo update --verbose`'
114
114
  end
115
115
  end
116
116
 
117
117
  class MasterSource
118
118
  def update_git_repo(show_output = false)
119
- ensure_in_repo!
120
119
  if repo.join('.git', 'shallow').file?
121
120
  UI.info "Performing a deep fetch of the `#{name}` specs repo to improve future performance" do
122
- git!(%w(fetch --unshallow))
121
+ git!(['-C', repo, 'fetch', '--unshallow'])
123
122
  end
124
123
  end
125
124
  super
@@ -141,8 +140,6 @@ module Pod
141
140
  "CocoaPods #{latest_cocoapods_version} is available.".green,
142
141
  "To update use: `#{install_message}`".green,
143
142
  ("[!] This is a test version we'd love you to try.".yellow if rc),
144
- ("Until we reach version 1.0 the features of CocoaPods can and will change.\n" \
145
- 'We strongly recommend that you use the latest version at all times.'.yellow unless rc),
146
143
  '',
147
144
  'For more information, see https://blog.cocoapods.org ' \
148
145
  'and the CHANGELOG for this version at ' \
@@ -7,6 +7,10 @@ module Pod
7
7
  # generated this target.
8
8
  attr_reader :target_definition
9
9
 
10
+ # Product types where the product's frameworks must be embedded in a host target
11
+ #
12
+ EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES = [:app_extension, :framework, :messages_extension, :watch_extension, :xpc_service].freeze
13
+
10
14
  # Initialize a new instance
11
15
  #
12
16
  # @param [TargetDefinition] target_definition @see target_definition
@@ -23,6 +27,22 @@ module Pod
23
27
  @xcconfigs = {}
24
28
  end
25
29
 
30
+ # @return [Boolean] True if the user_target's pods are
31
+ # for an extension and must be embedded in a host,
32
+ # target, otherwise false.
33
+ #
34
+ def requires_host_target?
35
+ # If we don't have a user_project, then we can't
36
+ # glean any info about how this target is going to
37
+ # be integrated, so return false since we can't know
38
+ # for sure that this target refers to an extension
39
+ # target that would require a host target
40
+ return false if user_project.nil?
41
+ symbol_types = user_targets.map(&:symbol_type).uniq
42
+ raise ArgumentError, "Expected single kind of user_target for #{name}. Found #{symbol_types.join(', ')}." unless symbol_types.count == 1
43
+ EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES.include?(symbol_types[0])
44
+ end
45
+
26
46
  # @return [String] the label for the target.
27
47
  #
28
48
  def label
@@ -47,6 +47,7 @@ module Pod
47
47
  @file_accessors = []
48
48
  @resource_bundle_targets = []
49
49
  @dependent_targets = []
50
+ @build_config_cache = {}
50
51
  end
51
52
 
52
53
  # @param [Hash{Array => PodTarget}] cache
@@ -65,6 +66,7 @@ module Pod
65
66
  target.native_target = native_target
66
67
  target.archs = archs
67
68
  target.dependent_targets = dependent_targets.flat_map { |pt| pt.scoped(cache) }.select { |pt| pt.target_definitions == [target_definition] }
69
+ target.host_requires_frameworks = host_requires_frameworks
68
70
  cache[cache_key] = target
69
71
  end
70
72
  end
@@ -80,6 +82,12 @@ module Pod
80
82
  end
81
83
  end
82
84
 
85
+ # @return [String] the Swift version for the target.
86
+ #
87
+ def swift_version
88
+ target_definitions.map(&:swift_version).compact.uniq.first
89
+ end
90
+
83
91
  # @note The deployment target for the pod target is the maximum of all
84
92
  # the deployment targets for the current platform of the target
85
93
  # (or the minimum required to support the current installation
@@ -134,9 +142,12 @@ module Pod
134
142
  # A target should not be build if it has no source files.
135
143
  #
136
144
  def should_build?
137
- source_files = file_accessors.flat_map(&:source_files)
138
- source_files -= file_accessors.flat_map(&:headers)
139
- !source_files.empty?
145
+ return @should_build if defined? @should_build
146
+ @should_build = begin
147
+ source_files = file_accessors.flat_map(&:source_files)
148
+ source_files -= file_accessors.flat_map(&:headers)
149
+ !source_files.empty?
150
+ end
140
151
  end
141
152
 
142
153
  # @return [Array<Specification::Consumer>] the specification consumers for
@@ -149,8 +160,11 @@ module Pod
149
160
  # @return [Boolean] Whether the target uses Swift code
150
161
  #
151
162
  def uses_swift?
152
- file_accessors.any? do |file_accessor|
153
- file_accessor.source_files.any? { |sf| sf.extname == '.swift' }
163
+ return @uses_swift if defined? @uses_swift
164
+ @uses_swift = begin
165
+ file_accessors.any? do |file_accessor|
166
+ file_accessor.source_files.any? { |sf| sf.extname == '.swift' }
167
+ end
154
168
  end
155
169
  end
156
170
 
@@ -188,8 +202,14 @@ module Pod
188
202
  # dependency upon.
189
203
  #
190
204
  def recursive_dependent_targets
191
- targets = dependent_targets + dependent_targets.flat_map(&:recursive_dependent_targets)
192
- targets.uniq!
205
+ targets = dependent_targets.clone
206
+
207
+ targets.each do |target|
208
+ target.dependent_targets.each do |t|
209
+ targets.push(t) unless t == self || targets.include?(t)
210
+ end
211
+ end
212
+
193
213
  targets
194
214
  end
195
215
 
@@ -203,13 +223,20 @@ module Pod
203
223
  # The name of the build configuration.
204
224
  #
205
225
  def include_in_build_config?(target_definition, configuration_name)
226
+ key = [target_definition.label, configuration_name]
227
+ if @build_config_cache.key?(key)
228
+ return @build_config_cache[key]
229
+ end
230
+
206
231
  whitelists = target_definition_dependencies(target_definition).map do |dependency|
207
232
  target_definition.pod_whitelisted_for_configuration?(dependency.name, configuration_name)
208
233
  end.uniq
209
234
 
210
235
  if whitelists.empty?
236
+ @build_config_cache[key] = true
211
237
  return true
212
238
  elsif whitelists.count == 1
239
+ @build_config_cache[key] = whitelists.first
213
240
  whitelists.first
214
241
  else
215
242
  raise Informative, "The subspecs of `#{pod_name}` are linked to " \
@@ -224,13 +251,16 @@ module Pod
224
251
  # @return [Bool]
225
252
  #
226
253
  def inhibit_warnings?
254
+ return @inhibit_warnings if defined? @inhibit_warnings
227
255
  whitelists = target_definitions.map do |target_definition|
228
256
  target_definition.inhibits_warnings_for_pod?(root_spec.name)
229
257
  end.uniq
230
258
 
231
259
  if whitelists.empty?
260
+ @inhibit_warnings = false
232
261
  return false
233
262
  elsif whitelists.count == 1
263
+ @inhibit_warnings = whitelists.first
234
264
  whitelists.first
235
265
  else
236
266
  UI.warn "The pod `#{pod_name}` is linked to different targets " \
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'rbconfig'
4
4
  require 'cgi'
5
+ require 'gh_inspector'
5
6
 
6
7
  module Pod
7
8
  module UserInterface
@@ -112,6 +113,12 @@ EOS
112
113
  EOS
113
114
  end
114
115
 
116
+ def search_for_exceptions(exception)
117
+ inspector = GhInspector::Inspector.new 'cocoapods', 'cocoapods'
118
+ message_delegate = UserInterface::InspectorReporter.new
119
+ inspector.search_exception exception, message_delegate
120
+ end
121
+
115
122
  private
116
123
 
117
124
  def `(other)
@@ -0,0 +1,109 @@
1
+ require 'uri'
2
+
3
+ module Pod
4
+ module UserInterface
5
+ # Redirects GH-issues delegate callbacks to CocoaPods UI methods.
6
+ #
7
+ class InspectorReporter
8
+ # Called just as the investigation has begun.
9
+ # Lets the user know that it's looking for an issue.
10
+ #
11
+ # @param [query] String unused
12
+ #
13
+ # @param [GhInspector::Inspector] inspector
14
+ # The current inspector
15
+ #
16
+ # @return [void]
17
+ #
18
+ def inspector_started_query(_, inspector)
19
+ UI.puts "Looking for related issues on #{inspector.repo_owner}/#{inspector.repo_name}..."
20
+ end
21
+
22
+ # Called once the inspector has recieved a report with more than one issue,
23
+ # showing the top 3 issues, and offering a link to see more.
24
+ #
25
+ # @param [GhInspector::InspectionReport] report
26
+ # Report a list of the issues
27
+ #
28
+ # @param [GhInspector::Inspector] inspector
29
+ # The current inspector
30
+ #
31
+ # @return [void]
32
+ #
33
+ def inspector_successfully_recieved_report(report, _)
34
+ report.issues[0..2].each { |issue| print_issue_full(issue) }
35
+
36
+ if report.issues.count > 3
37
+ UI.puts "and #{report.total_results - 3} more at:"
38
+ UI.puts report.url
39
+ end
40
+ end
41
+
42
+ # Called once the report has been recieved, but when there are no issues found.
43
+ #
44
+ # @param [GhInspector::InspectionReport] report
45
+ # An empty report
46
+ #
47
+ # @param [GhInspector::Inspector] inspector
48
+ # The current inspector
49
+ #
50
+ # @return [void]
51
+ #
52
+ def inspector_recieved_empty_report(_, inspector)
53
+ UI.puts 'Found no similar issues. To create a new issue, please visit:'
54
+ UI.puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/issues/new"
55
+ end
56
+
57
+ # Called when there have been networking issues in creating the report.
58
+ #
59
+ # @param [Error] error
60
+ # The error returned during networking
61
+ #
62
+ # @param [String] query
63
+ # The original search query
64
+ #
65
+ # @param [GhInspector::Inspector] inspector
66
+ # The current inspector
67
+ #
68
+ # @return [void]
69
+ #
70
+ def inspector_could_not_create_report(error, query, inspector)
71
+ safe_query = URI.escape query
72
+ UI.puts 'Could not access the GitHub API, you may have better luck via the website.'
73
+ UI.puts "https://github.com/#{inspector.repo_owner}/#{inspector.repo_name}/search?q=#{safe_query}&type=Issues&utf8=✓"
74
+ UI.puts "Error: #{error.name}"
75
+ end
76
+
77
+ private
78
+
79
+ def print_issue_full(issue)
80
+ safe_url = URI.escape issue.html_url
81
+ UI.puts " - #{issue.title}"
82
+ UI.puts " #{safe_url} [#{issue.state}] [#{issue.comments} comment#{issue.comments == 1 ? '' : 's'}]"
83
+ UI.puts " #{pretty_date(issue.updated_at)}"
84
+ UI.puts ''
85
+ end
86
+
87
+ # Taken from http://stackoverflow.com/questions/195740/how-do-you-do-relative-time-in-rails
88
+ def pretty_date(date_string)
89
+ date = Time.parse(date_string)
90
+ a = (Time.now - date).to_i
91
+
92
+ case a
93
+ when 0 then 'just now'
94
+ when 1 then 'a second ago'
95
+ when 2..59 then a.to_s + ' seconds ago'
96
+ when 60..119 then 'a minute ago' # 120 = 2 minutes
97
+ when 120..3540 then (a / 60).to_i.to_s + ' minutes ago'
98
+ when 3541..7100 then 'an hour ago' # 3600 = 1 hour
99
+ when 7101..82_800 then ((a + 99) / 3600).to_i.to_s + ' hours ago'
100
+ when 82_801..172_000 then 'a day ago' # 86400 = 1 day
101
+ when 172_001..518_400 then ((a + 800) / (60 * 60 * 24)).to_i.to_s + ' days ago'
102
+ when 518_400..1_036_800 then 'a week ago'
103
+ when 1_036_801..4_147_204 then ((a + 180_000) / (60 * 60 * 24 * 7)).to_i.to_s + ' weeks ago'
104
+ else date.strftime('%d %b %Y')
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,4 +1,5 @@
1
1
  require 'cocoapods/user_interface/error_report'
2
+ require 'cocoapods/user_interface/inspector_reporter'
2
3
 
3
4
  module Pod
4
5
  # Provides support for UI output. It provides support for nested sections of
@@ -7,7 +8,7 @@ module Pod
7
8
  module UserInterface
8
9
  require 'colored'
9
10
 
10
- @title_colors = %w( yellow green )
11
+ @title_colors = %w( yellow green )
11
12
  @title_level = 0
12
13
  @indentation_level = 2
13
14
  @treat_titles_as_messages = false
@@ -117,9 +118,6 @@ module Pod
117
118
  self.title_level -= 1
118
119
  end
119
120
 
120
- # def title(title, verbose_prefix = '', relative_indentation = 2)
121
- # end
122
-
123
121
  # Prints a verbose message taking an optional verbose prefix and
124
122
  # a relative indentation valid for the UI action in the passed
125
123
  # block.
@@ -187,7 +185,11 @@ module Pod
187
185
  if pathname
188
186
  from_path = config.podfile_path.dirname if config.podfile_path
189
187
  from_path ||= Pathname.pwd
190
- path = Pathname(pathname).relative_path_from(from_path)
188
+ path = begin
189
+ Pathname(pathname).relative_path_from(from_path)
190
+ rescue
191
+ pathname
192
+ end
191
193
  "`#{path}`"
192
194
  else
193
195
  ''
@@ -1,8 +1,6 @@
1
1
  require 'active_support/core_ext/array'
2
2
  require 'active_support/core_ext/string/inflections'
3
3
 
4
- autoload :Fourflusher, 'fourflusher'
5
-
6
4
  module Pod
7
5
  # Validates a Specification.
8
6
  #
@@ -144,7 +142,15 @@ module Pod
144
142
  reasons << 'all results apply only to public specs, but you can use ' \
145
143
  '`--private` to ignore them if linting the specification for a private pod'
146
144
  end
147
- reasons.to_sentence
145
+ if dot_swift_version.nil?
146
+ reasons.to_sentence + ".\n[!] The validator for Swift projects uses " \
147
+ 'Swift 3.0 by default, if you are using a different version of ' \
148
+ 'swift you can use a `.swift-version` file to set the version for ' \
149
+ "your Pod. For example to use Swift 2.3, run: \n" \
150
+ ' `echo "2.3" > .swift-version`'
151
+ else
152
+ reasons.to_sentence
153
+ end
148
154
  end
149
155
 
150
156
  #-------------------------------------------------------------------------#
@@ -239,6 +245,30 @@ module Pod
239
245
  Pathname(Dir.tmpdir) + 'CocoaPods/Lint'
240
246
  end
241
247
 
248
+ # @return [String] the SWIFT_VERSION to use for validation.
249
+ #
250
+ def swift_version
251
+ @swift_version ||= dot_swift_version || '3.0'
252
+ end
253
+
254
+ # Set the SWIFT_VERSION that should be used to validate the pod.
255
+ #
256
+ attr_writer :swift_version
257
+
258
+ # @return [String] the SWIFT_VERSION in the .swift-version file or nil.
259
+ #
260
+ def dot_swift_version
261
+ swift_version_path = file.dirname + '.swift-version'
262
+ swift_version_path.read if swift_version_path.exist?
263
+ end
264
+
265
+ # @return [String] A string representing the Swift version used during linting
266
+ # or nil, if Swift was not used.
267
+ #
268
+ def used_swift_version
269
+ swift_version if @installer.pod_targets.any?(&:uses_swift?)
270
+ end
271
+
242
272
  #-------------------------------------------------------------------------#
243
273
 
244
274
  private
@@ -388,10 +418,26 @@ module Pod
388
418
 
389
419
  source_file = write_app_import_source_file(pod_target)
390
420
  source_file_ref = app_project.new_group('App', 'App').new_file(source_file)
391
- app_project.targets.first.add_file_references([source_file_ref])
421
+ app_target = app_project.targets.first
422
+ app_target.add_file_references([source_file_ref])
423
+ add_swift_version(app_target)
424
+ add_xctest(app_target) if @installer.pod_targets.any? { |pt| pt.spec_consumers.any? { |c| c.frameworks.include?('XCTest') } }
392
425
  app_project.save
393
426
  end
394
427
 
428
+ def add_swift_version(app_target)
429
+ app_target.build_configurations.each do |configuration|
430
+ configuration.build_settings['SWIFT_VERSION'] = swift_version
431
+ end
432
+ end
433
+
434
+ def add_xctest(app_target)
435
+ app_target.build_configurations.each do |configuration|
436
+ search_paths = configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= '$(inherited)'
437
+ search_paths << ' "$(PLATFORM_DIR)/Developer/Library/Frameworks"'
438
+ end
439
+ end
440
+
395
441
  def write_app_import_source_file(pod_target)
396
442
  language = pod_target.uses_swift? ? :swift : :objc
397
443
 
@@ -427,7 +473,7 @@ module Pod
427
473
  # for all available platforms with xcodebuild.
428
474
  #
429
475
  def install_pod
430
- %i(verify_no_duplicate_framework_names
476
+ %i(verify_no_duplicate_framework_and_library_names
431
477
  verify_no_static_framework_transitive_dependencies
432
478
  verify_framework_usage generate_pods_project integrate_user_project
433
479
  perform_post_install_actions).each { |m| @installer.send(m) }
@@ -438,6 +484,7 @@ module Pod
438
484
  next unless native_target = pod_target.native_target
439
485
  native_target.build_configuration_list.build_configurations.each do |build_configuration|
440
486
  (build_configuration.build_settings['OTHER_CFLAGS'] ||= '$(inherited)') << ' -Wincomplete-umbrella'
487
+ build_configuration.build_settings['SWIFT_VERSION'] = swift_version if pod_target.uses_swift?
441
488
  end
442
489
  end
443
490
  if target.pod_targets.any?(&:uses_swift?) && consumer.platform_name == :ios &&
@@ -575,7 +622,7 @@ module Pod
575
622
  end
576
623
  non_source_files = header_files - file_accessor.source_files
577
624
  unless non_source_files.empty?
578
- error(attr_name, 'The pattern includes header files that are not listed' \
625
+ error(attr_name, 'The pattern includes header files that are not listed ' \
579
626
  "in source_files (#{non_source_files.join(', ')}).")
580
627
  end
581
628
  end
@@ -710,20 +757,21 @@ module Pod
710
757
  # returns its output (both STDOUT and STDERR).
711
758
  #
712
759
  def xcodebuild
713
- command = %w(clean build -workspace App.xcworkspace -scheme App -configuration Release)
760
+ require 'fourflusher'
761
+ command = ['clean', 'build', '-workspace', File.join(validation_dir, 'App.xcworkspace'), '-scheme', 'App', '-configuration', 'Release']
714
762
  case consumer.platform_name
715
763
  when :ios
716
764
  command += %w(CODE_SIGN_IDENTITY=- -sdk iphonesimulator)
717
- command += Fourflusher::SimControl.new.destination('iPhone 4s', deployment_target)
765
+ command += Fourflusher::SimControl.new.destination(:oldest, 'iOS', deployment_target)
718
766
  when :watchos
719
767
  command += %w(CODE_SIGN_IDENTITY=- -sdk watchsimulator)
720
- command += Fourflusher::SimControl.new.destination('Apple Watch - 38mm', deployment_target)
768
+ command += Fourflusher::SimControl.new.destination(:oldest, 'watchOS', deployment_target)
721
769
  when :tvos
722
770
  command += %w(CODE_SIGN_IDENTITY=- -sdk appletvsimulator)
723
- command += Fourflusher::SimControl.new.destination('Apple TV 1080p', deployment_target)
771
+ command += Fourflusher::SimControl.new.destination(:oldest, 'tvOS', deployment_target)
724
772
  end
725
773
 
726
- output, status = Dir.chdir(validation_dir) { _xcodebuild(command) }
774
+ output, status = _xcodebuild(command)
727
775
 
728
776
  unless status.success?
729
777
  message = 'Returned an unsuccessful exit code.'