cocoapods 0.16.4 → 0.17.0.rc1

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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +108 -0
  3. data/README.md +3 -3
  4. data/bin/pod +1 -1
  5. data/lib/cocoapods.rb +31 -31
  6. data/lib/cocoapods/command.rb +62 -107
  7. data/lib/cocoapods/command/inter_process_communication.rb +103 -0
  8. data/lib/cocoapods/command/list.rb +45 -44
  9. data/lib/cocoapods/command/outdated.rb +28 -25
  10. data/lib/cocoapods/command/project.rb +90 -0
  11. data/lib/cocoapods/command/push.rb +50 -32
  12. data/lib/cocoapods/command/repo.rb +125 -155
  13. data/lib/cocoapods/command/search.rb +23 -12
  14. data/lib/cocoapods/command/setup.rb +103 -64
  15. data/lib/cocoapods/command/spec.rb +329 -90
  16. data/lib/cocoapods/config.rb +197 -44
  17. data/lib/cocoapods/downloader.rb +47 -34
  18. data/lib/cocoapods/executable.rb +98 -41
  19. data/lib/cocoapods/external_sources.rb +325 -0
  20. data/lib/cocoapods/file_list.rb +8 -1
  21. data/lib/cocoapods/gem_version.rb +7 -0
  22. data/lib/cocoapods/generator/acknowledgements.rb +71 -7
  23. data/lib/cocoapods/generator/acknowledgements/markdown.rb +10 -9
  24. data/lib/cocoapods/generator/acknowledgements/plist.rb +9 -8
  25. data/lib/cocoapods/generator/copy_resources_script.rb +2 -2
  26. data/lib/cocoapods/generator/documentation.rb +153 -37
  27. data/lib/cocoapods/generator/prefix_header.rb +82 -0
  28. data/lib/cocoapods/generator/target_header.rb +58 -0
  29. data/lib/cocoapods/generator/xcconfig.rb +130 -0
  30. data/lib/cocoapods/hooks/installer_representation.rb +123 -0
  31. data/lib/cocoapods/hooks/library_representation.rb +79 -0
  32. data/lib/cocoapods/hooks/pod_representation.rb +74 -0
  33. data/lib/cocoapods/installer.rb +398 -147
  34. data/lib/cocoapods/installer/analyzer.rb +556 -0
  35. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
  36. data/lib/cocoapods/installer/file_references_installer.rb +179 -0
  37. data/lib/cocoapods/installer/pod_source_installer.rb +289 -0
  38. data/lib/cocoapods/installer/target_installer.rb +307 -112
  39. data/lib/cocoapods/installer/user_project_integrator.rb +140 -176
  40. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +193 -0
  41. data/lib/cocoapods/library.rb +195 -0
  42. data/lib/cocoapods/open_uri.rb +16 -14
  43. data/lib/cocoapods/project.rb +175 -52
  44. data/lib/cocoapods/resolver.rb +151 -164
  45. data/lib/cocoapods/sandbox.rb +276 -54
  46. data/lib/cocoapods/sandbox/file_accessor.rb +210 -0
  47. data/lib/cocoapods/sandbox/headers_store.rb +96 -0
  48. data/lib/cocoapods/sandbox/path_list.rb +178 -0
  49. data/lib/cocoapods/sources_manager.rb +218 -0
  50. data/lib/cocoapods/user_interface.rb +82 -18
  51. data/lib/cocoapods/{command → user_interface}/error_report.rb +5 -5
  52. data/lib/cocoapods/validator.rb +379 -0
  53. metadata +74 -55
  54. data/lib/cocoapods/command/install.rb +0 -55
  55. data/lib/cocoapods/command/linter.rb +0 -317
  56. data/lib/cocoapods/command/update.rb +0 -25
  57. data/lib/cocoapods/dependency.rb +0 -285
  58. data/lib/cocoapods/downloader/git.rb +0 -276
  59. data/lib/cocoapods/downloader/http.rb +0 -99
  60. data/lib/cocoapods/downloader/mercurial.rb +0 -26
  61. data/lib/cocoapods/downloader/subversion.rb +0 -42
  62. data/lib/cocoapods/local_pod.rb +0 -620
  63. data/lib/cocoapods/lockfile.rb +0 -274
  64. data/lib/cocoapods/platform.rb +0 -127
  65. data/lib/cocoapods/podfile.rb +0 -551
  66. data/lib/cocoapods/source.rb +0 -223
  67. data/lib/cocoapods/specification.rb +0 -579
  68. data/lib/cocoapods/specification/set.rb +0 -175
  69. data/lib/cocoapods/specification/statistics.rb +0 -112
  70. data/lib/cocoapods/user_interface/ui_pod.rb +0 -130
  71. data/lib/cocoapods/version.rb +0 -26
@@ -0,0 +1,218 @@
1
+ module Pod
2
+
3
+ # Manages all the sources known to the running CocoaPods Instance.
4
+ #
5
+ class SourcesManager
6
+
7
+ class << self
8
+
9
+ include Config::Mixin
10
+
11
+ # @return [Source::Aggregate] the aggregate of all the sources known to
12
+ # this installation of CocoaPods.
13
+ #
14
+ def aggregate
15
+ Source::Aggregate.new(config.repos_dir)
16
+ end
17
+
18
+ # @return [Array<Source>] the list of all the sources known to this
19
+ # installation of CocoaPods.
20
+ #
21
+ def all
22
+ aggregate.all
23
+ end
24
+
25
+ # @return [Array<Specification::Set>] the list of all the specification
26
+ # sets know to this installation of CocoaPods.
27
+ #
28
+ def all_sets
29
+ aggregate.all_sets
30
+ end
31
+
32
+ # Search all the sources to match the set for the given dependency.
33
+ #
34
+ # @return [Set, nil] a set for a given dependency including all the
35
+ # {Source} that contain the Pod. If no sources containing the
36
+ # Pod where found it returns nil.
37
+ #
38
+ # @raise If no source including the set can be found.
39
+ #
40
+ def search(dependency)
41
+ set = aggregate.search(dependency)
42
+ unless set
43
+ raise Informative, "Unable to find a pod named `#{dependency.name}`"
44
+ end
45
+ set
46
+ end
47
+
48
+ # Search all the sources with the given search term.
49
+ #
50
+ # @param [String] query
51
+ # The search term.
52
+ #
53
+ # @param [Bool] full_text_search
54
+ # Whether the search should be limited to the name of the Pod or
55
+ # should include also the author, the summary, and the
56
+ # description.
57
+ #
58
+ # @raise If no source including the set can be found.
59
+ #
60
+ # @note Full text search requires to load the specification for each
61
+ # pod, hence is considerably slower.
62
+ #
63
+ # @return [Array<Set>] The sets that contain the search term.
64
+ #
65
+ def search_by_name(query, full_text_search = false)
66
+ result = aggregate.search_by_name(query, full_text_search)
67
+ if result.empty?
68
+ extra = ", author, summary, or description" if full_text_search
69
+ raise Informative "Unable to find a pod with name#{extra} matching `#{query}`"
70
+ end
71
+ result
72
+ end
73
+
74
+ #-----------------------------------------------------------------------#
75
+
76
+ # @!group Updating Sources
77
+
78
+ extend Executable
79
+ executable :git
80
+
81
+ # Updates the local clone of the spec-repo with the given name or of all
82
+ # the git repos if the name is omitted.
83
+ #
84
+ # @param [String] name
85
+ #
86
+ # @return [void]
87
+ #
88
+ def update(source_name = nil, show_output = false)
89
+ if source_name
90
+ source = aggregate.all.find { |s| s.name == source_name }
91
+ raise Informative, "Unable to find the `#{source_name}` repo." unless source
92
+ raise Informative, "The `#{source_name}` repo is not a git repo." unless git_repo?(source.repo)
93
+ sources = [source]
94
+ else
95
+ sources = aggregate.all.select { |source| git_repo?(source.repo) }
96
+ end
97
+
98
+ sources.each do |source|
99
+ UI.section "Updating spec repo `#{source.name}`" do
100
+ Dir.chdir(source.repo) do
101
+ output = git!("pull")
102
+ UI.puts output if show_output && !config.verbose?
103
+ end
104
+ check_version_information(source.repo)
105
+ end
106
+ end
107
+ end
108
+
109
+ # Returns whether a source is a GIT repo.
110
+ #
111
+ # @param [Pathname] dir
112
+ # The directory where the source is stored.
113
+ #
114
+ # @return [Bool] Wether the given source is a GIT repo.
115
+ #
116
+ def git_repo?(dir)
117
+ Dir.chdir(dir) { `git rev-parse >/dev/null 2>&1` }
118
+ $?.exitstatus.zero?
119
+ end
120
+
121
+ # Checks the version information of the source with the given directory.
122
+ # It raises if the source is not compatible and if there is CocoaPods
123
+ # update it informs the user.
124
+ #
125
+ # @param [Pathname] dir
126
+ # The directory where the source is stored.
127
+ #
128
+ # @raise If the source is not compatible.
129
+ #
130
+ # @return [void]
131
+ #
132
+ def check_version_information(dir)
133
+ versions = version_information(dir)
134
+ unless repo_compatible?(dir)
135
+ min, max = versions['min'], versions['max']
136
+ version_msg = ( min == max ) ? min : "#{min} - #{max}"
137
+ raise Informative, "The `#{dir.basename}` repo requires " \
138
+ "CocoaPods #{version_msg}\n".red +
139
+ "Update Cocoapods, or checkout the appropriate tag in the repo."
140
+ end
141
+
142
+ if config.new_version_message? && cocoapods_update?(versions)
143
+ UI.puts "\nCocoapods #{versions['last']} is available.\n".green
144
+ end
145
+ end
146
+
147
+ # Returns whether a source is compatible with the current version of
148
+ # CocoaPods.
149
+ #
150
+ # @param [Pathname] dir
151
+ # The directory where the source is stored.
152
+ #
153
+ # @return [Bool] whether the source is compatible.
154
+ #
155
+ def repo_compatible?(dir)
156
+ versions = version_information(dir)
157
+
158
+ min, max = versions['min'], versions['max']
159
+ bin_version = Gem::Version.new(Pod::VERSION)
160
+ supports_min = !min || bin_version >= Gem::Version.new(min)
161
+ supports_max = !max || bin_version <= Gem::Version.new(max)
162
+ supports_min && supports_max
163
+ end
164
+
165
+ # Checks whether there is a CocoaPods given the version information of a
166
+ # repo.
167
+ #
168
+ # @param [Hash] version_information
169
+ # The version information of a repository.
170
+ #
171
+ # @return [Bool] whether there is an update.
172
+ #
173
+ def cocoapods_update?(version_information)
174
+ version = version_information['last']
175
+ version && Gem::Version.new(version) > Gem::Version.new(Pod::VERSION)
176
+ end
177
+
178
+ # Returns the contents of the `CocoaPods-version.yml` file, which stores
179
+ # information about CocoaPods versions.
180
+ #
181
+ # This file is a hash with the following keys:
182
+ #
183
+ # - last: the last version of CocoaPods known to the source.
184
+ # - min: the minimum version of CocoaPods supported by the source.
185
+ # - max: the maximum version of CocoaPods supported by the source.
186
+ #
187
+ # @param [Pathname] dir
188
+ # The directory where the source is stored.
189
+ #
190
+ # @return [Hash] the versions information from the repo.
191
+ #
192
+ def version_information(dir)
193
+ require 'yaml'
194
+ yaml_file = dir + 'CocoaPods-version.yml'
195
+ yaml_file.exist? ? YAML.load_file(yaml_file) : {}
196
+ end
197
+
198
+ #-----------------------------------------------------------------------#
199
+
200
+ # @!group Master repo
201
+
202
+ # @return [Pathname] The path of the master repo.
203
+ #
204
+ def master_repo_dir
205
+ config.repos_dir + 'master'
206
+ end
207
+
208
+ # @return [Bool] Checks if the master repo is usable.
209
+ #
210
+ # @note Note this is used to automatically setup the master repo if
211
+ # needed.
212
+ #
213
+ def master_repo_functional?
214
+ master_repo_dir.exist? && repo_compatible?(master_repo_dir)
215
+ end
216
+ end
217
+ end
218
+ end
@@ -1,18 +1,27 @@
1
+ require 'cocoapods/user_interface/error_report'
2
+
1
3
  module Pod
2
- require 'colored'
4
+
5
+ # Provides support for UI output. It provides support for nested sections of
6
+ # information and for a verbose mode.
7
+ #
3
8
  module UserInterface
4
9
 
5
- autoload :UIPod, 'cocoapods/user_interface/ui_pod'
10
+ require 'colored'
6
11
 
7
12
  @title_colors = %w|yellow green|
8
13
  @title_level = 0
9
14
  @indentation_level = 2
10
15
  @treat_titles_as_messages = false
16
+ @warnings = []
11
17
 
12
18
  class << self
19
+
13
20
  include Config::Mixin
14
21
 
15
- attr_accessor :indentation_level, :title_level
22
+ attr_accessor :indentation_level
23
+ attr_accessor :title_level
24
+ attr_accessor :warnings
16
25
 
17
26
  # Prints a title taking an optional verbose prefix and
18
27
  # a relative indentation valid for the UI action in the passed
@@ -22,13 +31,34 @@ module Pod
22
31
  # to their level. In normal mode titles are printed only if
23
32
  # they have nesting level smaller than 2.
24
33
  #
25
- # TODO: refactor to title (for always visible titles like search)
26
- # and sections (titles that reppresent collapsible sections).
34
+ # @todo Refactor to title (for always visible titles like search)
35
+ # and sections (titles that represent collapsible sections).
27
36
  #
28
37
  def section(title, verbose_prefix = '', relative_indentation = 0)
29
38
  if config.verbose?
30
39
  title(title, verbose_prefix, relative_indentation)
31
- elsif title_level < 2
40
+ elsif title_level < 1
41
+ puts title
42
+ end
43
+
44
+ self.indentation_level += relative_indentation
45
+ self.title_level += 1
46
+ yield if block_given?
47
+ self.indentation_level -= relative_indentation
48
+ self.title_level -= 1
49
+ end
50
+
51
+ # In verbose mode it shows the sections and the contents.
52
+ # In normal mode it just prints the title.
53
+ #
54
+ # @return [void]
55
+ #
56
+ def titled_section(title, options = {})
57
+ relative_indentation = options[:relative_indentation] || 0
58
+ verbose_prefix = options[:verbose_prefix] || ''
59
+ if config.verbose?
60
+ title(title, verbose_prefix, relative_indentation)
61
+ else
32
62
  puts title
33
63
  end
34
64
 
@@ -39,7 +69,7 @@ module Pod
39
69
  self.title_level -= 1
40
70
  end
41
71
 
42
- # A title oposed to a section is always visible
72
+ # A title opposed to a section is always visible
43
73
  #
44
74
  def title(title, verbose_prefix = '', relative_indentation = 2)
45
75
  if(@treat_titles_as_messages)
@@ -67,7 +97,7 @@ module Pod
67
97
  # a relative indentation valid for the UI action in the passed
68
98
  # block.
69
99
  #
70
- # TODO: clean interface.
100
+ # @todo Clean interface.
71
101
  #
72
102
  def message(message, verbose_prefix = '', relative_indentation = 2)
73
103
  message = verbose_prefix + message if config.verbose?
@@ -106,19 +136,32 @@ module Pod
106
136
  puts("\n[!] #{message}".green)
107
137
  end
108
138
 
109
- # Prints an important warning to the user optionally followed by actions
110
- # that the user should take.
139
+ # Stores important warning to the user optionally followed by actions
140
+ # that the user should take. To print them use #{print_warnings}
111
141
  #
112
142
  # @param [String] message The message to print.
113
143
  # @param [Array] actions The actions that the user should take.
114
144
  #
115
145
  # return [void]
116
146
  #
117
- def warn(message, actions)
118
- puts("\n[!] #{message}".yellow)
119
- actions.each do |action|
120
- indented = wrap_string(action, " - ")
121
- puts(indented)
147
+ def warn(message, actions = [], verbose_only = false)
148
+ warnings << { :message => message, :actions => actions }
149
+ end
150
+
151
+ # Prints the stored warnings. This method is intended to be called at the
152
+ # end of the execution of the binary.
153
+ #
154
+ # @return [void]
155
+ #
156
+ def print_warnings
157
+ return if config.silent? && verbose_only
158
+ STDOUT.flush
159
+ warnings.each do |warning|
160
+ STDERR.puts("\n[!] #{warning[:message]}".yellow)
161
+ warning[:actions].each do |action|
162
+ indented = wrap_string(action, " - ")
163
+ puts(indented)
164
+ end
122
165
  end
123
166
  end
124
167
 
@@ -128,19 +171,20 @@ module Pod
128
171
  #
129
172
  def path(pathname)
130
173
  if pathname
131
- "`./#{pathname.relative_path_from(config.project_podfile.dirname || Pathname.pwd)}'"
174
+ path = pathname.relative_path_from(config.podfile_path.dirname || Pathname.pwd)
175
+ "`#{path}`"
132
176
  else
133
177
  ''
134
178
  end
135
179
  end
136
180
 
137
- # Prints the textual repprensentation of a given set.
181
+ # Prints the textual representation of a given set.
138
182
  #
139
183
  def pod(set, mode = :normal)
140
184
  if mode == :name
141
185
  puts_indented set.name
142
186
  else
143
- pod = UIPod.new(set)
187
+ pod = Specification::Set::Presenter.new(set)
144
188
  title("\n-> #{pod.name} (#{pod.version})".green, '', 1) do
145
189
  puts_indented pod.summary
146
190
  labeled('Homepage', pod.homepage)
@@ -217,4 +261,24 @@ module Pod
217
261
  end
218
262
  end
219
263
  UI = UserInterface
264
+
265
+ # Redirects cocoapods-core UI.
266
+ #
267
+ module CoreUI
268
+
269
+ class << self
270
+
271
+ # @todo enable in CocoaPods 0.17.0 release
272
+ #
273
+ def puts(message)
274
+ # UI.puts message
275
+ end
276
+
277
+ # @todo enable in CocoaPods 0.17.0 release
278
+ #
279
+ def warn(message)
280
+ # UI.warn message
281
+ end
282
+ end
283
+ end
220
284
  end
@@ -4,13 +4,13 @@ require 'rbconfig'
4
4
  require 'cgi'
5
5
 
6
6
  module Pod
7
- class Command
7
+ module UserInterface
8
8
  module ErrorReport
9
9
  class << self
10
10
  def report(error)
11
11
  return <<-EOS
12
12
 
13
- #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
13
+ #{'――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――'.reversed}
14
14
 
15
15
  ### Report
16
16
 
@@ -58,13 +58,13 @@ EOS
58
58
  private
59
59
 
60
60
  def markdown_podfile
61
- return '' unless Config.instance.project_podfile && Config.instance.project_podfile.exist?
61
+ return '' unless Config.instance.podfile_path && Config.instance.podfile_path.exist?
62
62
  <<-EOS
63
63
 
64
64
  ### Podfile
65
65
 
66
66
  ```ruby
67
- #{Config.instance.project_podfile.read.strip}
67
+ #{Config.instance.podfile_path.read.strip}
68
68
  ```
69
69
  EOS
70
70
  end
@@ -86,7 +86,7 @@ EOS
86
86
  end
87
87
 
88
88
  def repo_information
89
- Pod::Source.all.map do |source|
89
+ SourcesManager.all.map do |source|
90
90
  repo = source.repo
91
91
  Dir.chdir(repo) do
92
92
  url = `git config --get remote.origin.url 2>&1`.strip
@@ -0,0 +1,379 @@
1
+ module Pod
2
+
3
+ # Validates a Specification.
4
+ #
5
+ # Extends the Linter from the Core to add additional which require the
6
+ # LocalPod and the Installer.
7
+ #
8
+ # In detail it checks that the file patterns defined by the user match
9
+ # actually do match at least a file and that the Pod builds, by installing
10
+ # it without integration and building the project with xcodebuild.
11
+ #
12
+ class Validator
13
+
14
+ include Config::Mixin
15
+
16
+ # @return [Specification::Linter] the linter instance from CocoaPods
17
+ # Core.
18
+ #
19
+ attr_reader :linter
20
+
21
+ # @param [Specification, Pathname, String] spec_or_path
22
+ # the Specification or the path of the `podspec` file to lint.
23
+ #
24
+ def initialize(spec_or_path)
25
+ @linter = Specification::Linter.new(spec_or_path)
26
+ end
27
+
28
+ #-------------------------------------------------------------------------#
29
+
30
+ # @return [Specification] the specification to lint.
31
+ #
32
+ def spec
33
+ @linter.spec
34
+ end
35
+
36
+ # @return [Pathname] the path of the `podspec` file where {#spec} is
37
+ # defined.
38
+ #
39
+ def file
40
+ @linter.file
41
+ end
42
+
43
+ # @return [Sandbox::FileAccessor] the file accessor for the spec.
44
+ #
45
+ attr_accessor :file_accessor
46
+
47
+ #-------------------------------------------------------------------------#
48
+
49
+ # Lints the specification adding a {Specification::Linter::Result} for any
50
+ # failed check to the {#results} list.
51
+ #
52
+ # @note This method shows immediately which pod is being processed and
53
+ # overrides the printed line once the result is known.
54
+ #
55
+ # @return [Bool] whether the specification passed validation.
56
+ #
57
+ def validate
58
+ @results = []
59
+ unless disable_ui_output
60
+ print " -> #{spec ? spec.name : file.basename}\r" unless config.silent?
61
+ $stdout.flush
62
+ end
63
+
64
+ perform_linting
65
+
66
+ # begin
67
+ if spec
68
+ check_repo_path if repo_path
69
+ perform_extensive_analysis unless quick
70
+ end
71
+ # rescue Exception => e
72
+ # error "The specification is malformed and crashed the linter."
73
+ # end
74
+
75
+ unless disable_ui_output
76
+ UI.puts " -> ".send(result_color) << (spec ? spec.name : file.basename.to_s)
77
+ print_results
78
+ end
79
+ validated?
80
+ end
81
+
82
+ def print_results
83
+ results.each do |result|
84
+ if result.platforms == [:ios]
85
+ platform_message = "[iOS] "
86
+ elsif result.platforms == [:osx]
87
+ platform_message = "[OSX] "
88
+ end
89
+
90
+ case result.type
91
+ when :error then type = "ERROR"
92
+ when :warning then type = "WARN"
93
+ when :note then type = "NOTE"
94
+ else raise "#{result.type}" end
95
+ UI.puts " - #{type.ljust(5)} | #{platform_message}#{result.message}"
96
+ end
97
+ UI.puts
98
+ end
99
+
100
+ #-------------------------------------------------------------------------#
101
+
102
+ # @!group Configuration
103
+
104
+ # @return [Bool] Whether the validator should print the results of the
105
+ # validation. This is useful for clients which want to customize
106
+ # output.
107
+ #
108
+ attr_accessor :disable_ui_output
109
+
110
+ # @return [Pathname] whether the validation should be performed against a repo.
111
+ #
112
+ attr_accessor :repo_path
113
+
114
+ # @return [Bool] whether the validation should skip the checks that
115
+ # requires the download of the library.
116
+ #
117
+ attr_accessor :quick
118
+
119
+ # @return [Bool] whether the linter should not clean up temporary files
120
+ # for inspection.
121
+ #
122
+ attr_accessor :no_clean
123
+
124
+ # @return [Bool] whether the validation should be performed against the root of
125
+ # the podspec instead to its original source.
126
+ #
127
+ # @note Uses the `:local` option of the Podfile.
128
+ #
129
+ attr_writer :local
130
+ def local?; @local; end
131
+
132
+ # @return [Bool] Whether the validator should fail only on errors or also
133
+ # on warnings.
134
+ #
135
+ attr_accessor :only_errors
136
+
137
+ #-------------------------------------------------------------------------#
138
+
139
+ # !@group Lint results
140
+
141
+ #
142
+ #
143
+ attr_reader :results
144
+
145
+ # @return [Boolean]
146
+ #
147
+ def validated?
148
+ result_type != :error && (result_type != :warning || only_errors)
149
+ end
150
+
151
+ # @return [Symbol]
152
+ #
153
+ def result_type
154
+ types = results.map(&:type).uniq
155
+ if types.include?(:error) then :error
156
+ elsif types.include?(:warning) then :warning
157
+ else :note end
158
+ end
159
+
160
+ # @return [Symbol]
161
+ #
162
+ def result_color
163
+ case result_type
164
+ when :error then :red
165
+ when :warning then :yellow
166
+ else :green end
167
+ end
168
+
169
+ # @return [Pathname] the temporary directory used by the linter.
170
+ #
171
+ def validation_dir
172
+ Pathname.new('/tmp/CocoaPods/Lint')
173
+ end
174
+
175
+ #-------------------------------------------------------------------------#
176
+
177
+ private
178
+
179
+ # !@group Lint steps
180
+
181
+ #
182
+ #
183
+ def perform_linting
184
+ linter.lint
185
+ @results.concat(linter.results)
186
+ end
187
+
188
+ #
189
+ #
190
+ def check_repo_path
191
+ expected_path = "#{spec.name}/#{spec.version}/#{spec.name}.podspec"
192
+ path = file.relative_path_from(repo_path).to_s
193
+ unless path == expected_path
194
+ error "Incorrect path, the path is `#{file}` and should be `#{expected_path}`"
195
+ end
196
+ end
197
+
198
+ #
199
+ #
200
+ def perform_extensive_analysis
201
+ spec.available_platforms.each do |platform|
202
+ UI.message "\n\n#{spec} - Analyzing on #{platform} platform.".green.reversed
203
+ @consumer = spec.consumer(platform)
204
+ setup_validation_environment
205
+ install_pod
206
+ build_pod
207
+ check_file_patterns
208
+ tear_down_validation_environment
209
+ end
210
+ end
211
+
212
+ attr_accessor :consumer
213
+
214
+ def setup_validation_environment
215
+ validation_dir.rmtree if validation_dir.exist?
216
+ validation_dir.mkpath
217
+ @original_config = Config.instance.clone
218
+ config.installation_root = validation_dir
219
+ config.sandbox_root = validation_dir + 'Pods'
220
+ config.silent = !config.verbose
221
+ config.integrate_targets = false
222
+ config.generate_docs = false
223
+ config.skip_repo_update = true
224
+ end
225
+
226
+ def tear_down_validation_environment
227
+ validation_dir.rmtree unless no_clean
228
+ Config.instance = @original_config
229
+ end
230
+
231
+ # It creates a podfile in memory and builds a library containing the pod
232
+ # for all available platforms with xcodebuild.
233
+ #
234
+ def install_pod
235
+ podfile = podfile_from_spec(consumer.platform_name, spec.deployment_target(consumer.platform_name))
236
+ sandbox = Sandbox.new(config.sandbox_root)
237
+ installer = Installer.new(sandbox, podfile)
238
+ installer.install!
239
+
240
+ file_accessors = installer.libraries.first.file_accessors
241
+ @file_accessor = file_accessors.find { |accessor| accessor.spec == spec }
242
+ config.silent
243
+ end
244
+
245
+ # Performs platform specific analysis. It requires to download the source
246
+ # at each iteration
247
+ #
248
+ # @note Xcode warnings are treaded as notes because the spec maintainer
249
+ # might not be the author of the library
250
+ #
251
+ # @return [void]
252
+ #
253
+ def build_pod
254
+ if `which xcodebuild`.strip.empty?
255
+ UI.warn "Skipping compilation with `xcodebuild' because it can't be found.\n".yellow
256
+ else
257
+ UI.message "\nBuilding with xcodebuild.\n".yellow do
258
+ output = Dir.chdir(config.sandbox_root) { xcodebuild }
259
+ UI.puts output
260
+ parsed_output = parse_xcodebuild_output(output)
261
+ parsed_output.each do |message|
262
+ if message.include?('error: ')
263
+ error "[xcodebuild] #{message}"
264
+ else
265
+ note "[xcodebuild] #{message}"
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
271
+
272
+ # It checks that every file pattern specified in a spec yields
273
+ # at least one file. It requires the pods to be already present
274
+ # in the current working directory under Pods/spec.name.
275
+ #
276
+ # @return [void]
277
+ #
278
+ def check_file_patterns
279
+ [:source_files, :resources, :preserve_paths].each do |attr_name|
280
+ # file_attr = Specification::DSL.attributes.values.find{|attr| attr.name == attr_name }
281
+ if !file_accessor.spec_consumer.send(attr_name).empty? && file_accessor.send(attr_name).empty?
282
+ error "The `#{attr_name}` pattern did not match any file."
283
+ end
284
+ end
285
+
286
+ unless file_accessor.license || spec.license && ( spec.license[:type] == 'Public Domain' || spec.license[:text] )
287
+ warning "Unable to find a license file"
288
+ end
289
+ end
290
+
291
+ #-------------------------------------------------------------------------#
292
+
293
+ private
294
+
295
+ # !@group Result Helpers
296
+
297
+ def error(message)
298
+ add_result(:error, message)
299
+ end
300
+
301
+ def warning(message)
302
+ add_result(:warning, message)
303
+ end
304
+
305
+ def note(message)
306
+ add_result(:note, message)
307
+ end
308
+
309
+ def add_result(type, message)
310
+ result = results.find { |r| r.type == type && r.message == message }
311
+ unless result
312
+ result = Specification::Linter::Result.new(type, message)
313
+ results << result
314
+ end
315
+ result.platforms << consumer.platform_name if consumer
316
+ end
317
+
318
+ #-------------------------------------------------------------------------#
319
+
320
+ private
321
+
322
+ # !@group Helpers
323
+
324
+ # @return [Podfile] a podfile that requires the specification on the
325
+ # current platform.
326
+ #
327
+ # @note The generated podfile takes into account whether the linter is
328
+ # in local mode.
329
+ #
330
+ def podfile_from_spec(platform_name, deployment_target)
331
+ name = spec.name
332
+ podspec = file.realpath
333
+ local = local?
334
+ podfile = Pod::Podfile.new do
335
+ platform(platform_name, deployment_target)
336
+ if (local)
337
+ pod name, :local => podspec.dirname.to_s
338
+ else
339
+ pod name, :podspec => podspec.to_s
340
+ end
341
+ end
342
+ podfile
343
+ end
344
+
345
+ # Parse the xcode build output to identify the lines which are relevant
346
+ # to the linter.
347
+ #
348
+ # @param [String] output the output generated by the xcodebuild tool.
349
+ #
350
+ # @note The indentation and the temporary path is stripped form the
351
+ # lines.
352
+ #
353
+ # @return [Array<String>] the lines that are relevant to the linter.
354
+ #
355
+ def parse_xcodebuild_output(output)
356
+ lines = output.split("\n")
357
+ selected_lines = lines.select do |l|
358
+ l.include?('error: ') &&
359
+ (l !~ /errors? generated\./) && (l !~ /error: \(null\)/) ||
360
+ l.include?('warning: ') && (l !~ /warnings? generated\./) ||
361
+ l.include?('note: ') && (l !~ /expanded from macro/)
362
+ end
363
+ selected_lines.map do |l|
364
+ new = l.gsub(/\/tmp\/CocoaPods\/Lint\/Pods\//,'')
365
+ new.gsub!(/^ */,' ')
366
+ end
367
+ end
368
+
369
+ # @return [String] Executes xcodebuild in the current working directory and
370
+ # returns its output (bot STDOUT and STDERR).
371
+ #
372
+ def xcodebuild
373
+ `xcodebuild clean build 2>&1`
374
+ end
375
+
376
+ #-------------------------------------------------------------------------#
377
+
378
+ end
379
+ end