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.
- 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
|