cocoapods 0.16.4 → 0.17.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +108 -0
- data/README.md +3 -3
- data/bin/pod +1 -1
- data/lib/cocoapods.rb +31 -31
- data/lib/cocoapods/command.rb +62 -107
- data/lib/cocoapods/command/inter_process_communication.rb +103 -0
- data/lib/cocoapods/command/list.rb +45 -44
- data/lib/cocoapods/command/outdated.rb +28 -25
- data/lib/cocoapods/command/project.rb +90 -0
- data/lib/cocoapods/command/push.rb +50 -32
- data/lib/cocoapods/command/repo.rb +125 -155
- data/lib/cocoapods/command/search.rb +23 -12
- data/lib/cocoapods/command/setup.rb +103 -64
- data/lib/cocoapods/command/spec.rb +329 -90
- data/lib/cocoapods/config.rb +197 -44
- data/lib/cocoapods/downloader.rb +47 -34
- data/lib/cocoapods/executable.rb +98 -41
- data/lib/cocoapods/external_sources.rb +325 -0
- data/lib/cocoapods/file_list.rb +8 -1
- data/lib/cocoapods/gem_version.rb +7 -0
- data/lib/cocoapods/generator/acknowledgements.rb +71 -7
- data/lib/cocoapods/generator/acknowledgements/markdown.rb +10 -9
- data/lib/cocoapods/generator/acknowledgements/plist.rb +9 -8
- data/lib/cocoapods/generator/copy_resources_script.rb +2 -2
- data/lib/cocoapods/generator/documentation.rb +153 -37
- data/lib/cocoapods/generator/prefix_header.rb +82 -0
- data/lib/cocoapods/generator/target_header.rb +58 -0
- data/lib/cocoapods/generator/xcconfig.rb +130 -0
- data/lib/cocoapods/hooks/installer_representation.rb +123 -0
- data/lib/cocoapods/hooks/library_representation.rb +79 -0
- data/lib/cocoapods/hooks/pod_representation.rb +74 -0
- data/lib/cocoapods/installer.rb +398 -147
- data/lib/cocoapods/installer/analyzer.rb +556 -0
- data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
- data/lib/cocoapods/installer/file_references_installer.rb +179 -0
- data/lib/cocoapods/installer/pod_source_installer.rb +289 -0
- data/lib/cocoapods/installer/target_installer.rb +307 -112
- data/lib/cocoapods/installer/user_project_integrator.rb +140 -176
- data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +193 -0
- data/lib/cocoapods/library.rb +195 -0
- data/lib/cocoapods/open_uri.rb +16 -14
- data/lib/cocoapods/project.rb +175 -52
- data/lib/cocoapods/resolver.rb +151 -164
- data/lib/cocoapods/sandbox.rb +276 -54
- data/lib/cocoapods/sandbox/file_accessor.rb +210 -0
- data/lib/cocoapods/sandbox/headers_store.rb +96 -0
- data/lib/cocoapods/sandbox/path_list.rb +178 -0
- data/lib/cocoapods/sources_manager.rb +218 -0
- data/lib/cocoapods/user_interface.rb +82 -18
- data/lib/cocoapods/{command → user_interface}/error_report.rb +5 -5
- data/lib/cocoapods/validator.rb +379 -0
- metadata +74 -55
- data/lib/cocoapods/command/install.rb +0 -55
- data/lib/cocoapods/command/linter.rb +0 -317
- data/lib/cocoapods/command/update.rb +0 -25
- data/lib/cocoapods/dependency.rb +0 -285
- data/lib/cocoapods/downloader/git.rb +0 -276
- data/lib/cocoapods/downloader/http.rb +0 -99
- data/lib/cocoapods/downloader/mercurial.rb +0 -26
- data/lib/cocoapods/downloader/subversion.rb +0 -42
- data/lib/cocoapods/local_pod.rb +0 -620
- data/lib/cocoapods/lockfile.rb +0 -274
- data/lib/cocoapods/platform.rb +0 -127
- data/lib/cocoapods/podfile.rb +0 -551
- data/lib/cocoapods/source.rb +0 -223
- data/lib/cocoapods/specification.rb +0 -579
- data/lib/cocoapods/specification/set.rb +0 -175
- data/lib/cocoapods/specification/statistics.rb +0 -112
- data/lib/cocoapods/user_interface/ui_pod.rb +0 -130
- 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
|
-
|
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
|
-
|
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
|
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
|
-
#
|
26
|
-
#
|
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 <
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
|
7
|
+
module UserInterface
|
8
8
|
module ErrorReport
|
9
9
|
class << self
|
10
10
|
def report(error)
|
11
11
|
return <<-EOS
|
12
12
|
|
13
|
-
|
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.
|
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.
|
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
|
-
|
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
|