cocoapods-core 1.0.0.beta.6 → 1.0.0.beta.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cocoapods-core/core_ui.rb +4 -0
- data/lib/cocoapods-core/gem_version.rb +1 -1
- data/lib/cocoapods-core/podfile/dsl.rb +16 -14
- data/lib/cocoapods-core/source/aggregate.rb +2 -1
- data/lib/cocoapods-core/source/manager.rb +412 -0
- data/lib/cocoapods-core/source/metadata.rb +79 -0
- data/lib/cocoapods-core/source.rb +99 -33
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c385f0112647c0300a73d55e8c6d160f98fec9d
|
4
|
+
data.tar.gz: 1d87fa015bcf769c785e181c394aa1fdc7584ead
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51e7cb1f3134fdd9d08f15427489033a43b76616429d7158d2534ca6fe461fde55da4c459ef98030d9f1e4bd53259b6d64c171edebc4ba4428725f1073259e6d
|
7
|
+
data.tar.gz: 7c0aaacccb38fbd1f89fdb950731eb72c58ecf3462258468a9b6703746fe2d0fb9d1ad5e98b652a317ab1bae6643c114f19748c097da1342052c8b56fa81512f
|
@@ -123,21 +123,22 @@ module Pod
|
|
123
123
|
# your Podfile to use future versions of CocoaPods. Anyway a clear and
|
124
124
|
# simple upgrade path will be provided.
|
125
125
|
#
|
126
|
-
# By default dependencies are installed
|
126
|
+
# By default dependencies are installed in all the build configurations
|
127
127
|
# of the target. For debug purposes or for other reasons, they can be
|
128
|
-
# enabled
|
128
|
+
# only enabled on a list of build configurations.
|
129
129
|
#
|
130
|
-
# pod 'PonyDebugger', :configurations => ['
|
130
|
+
# pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
|
131
131
|
#
|
132
|
-
# Alternatively you
|
132
|
+
# Alternatively, you specify to have it included on a single build
|
133
|
+
# configuration.
|
133
134
|
#
|
134
|
-
# pod 'PonyDebugger', :configuration =>
|
135
|
+
# pod 'PonyDebugger', :configuration => 'Debug'
|
135
136
|
#
|
136
137
|
# ------
|
137
138
|
#
|
138
139
|
# ### Subspecs
|
139
140
|
#
|
140
|
-
# When installing a Pod via
|
141
|
+
# When installing a Pod via its name, it will install all of the
|
141
142
|
# default subspecs defined in the podspec.
|
142
143
|
#
|
143
144
|
# You may install a specific subspec using the following:
|
@@ -295,12 +296,12 @@ module Pod
|
|
295
296
|
# target 'ShowsApp' do
|
296
297
|
# pod 'ShowsKit'
|
297
298
|
#
|
298
|
-
# # Has
|
299
|
+
# # Has its own copy of ShowsKit + ShowTVAuth
|
299
300
|
# target 'ShowsTV' do
|
300
301
|
# pod 'ShowTVAuth'
|
301
302
|
# end
|
302
303
|
#
|
303
|
-
# # Has
|
304
|
+
# # Has its own copy of Specta + Expecta
|
304
305
|
# # and has access to ShowsKit via the app
|
305
306
|
# # that the test target is bundled into
|
306
307
|
#
|
@@ -344,23 +345,24 @@ module Pod
|
|
344
345
|
#
|
345
346
|
# @example Defining an abstract_target wrapping Pods to multiple targets
|
346
347
|
#
|
347
|
-
# # There are no targets called "Shows" in any Xcode projects
|
348
|
+
# # Note: There are no targets called "Shows" in any of this workspace's Xcode projects
|
348
349
|
# abstract_target 'Shows' do
|
349
350
|
# pod 'ShowsKit'
|
350
351
|
#
|
351
|
-
# #
|
352
|
+
# # The target ShowsiOS has its own copy of ShowsKit (inherited) + ShowWebAuth (added here)
|
352
353
|
# target 'ShowsiOS' do
|
353
354
|
# pod 'ShowWebAuth'
|
354
355
|
# end
|
355
356
|
#
|
356
|
-
# #
|
357
|
+
# # The target ShowsTV has its own copy of ShowsKit (inherited) + ShowTVAuth (added here)
|
357
358
|
# target 'ShowsTV' do
|
358
359
|
# pod 'ShowTVAuth'
|
359
360
|
# end
|
360
361
|
#
|
361
|
-
# #
|
362
|
-
# # and has access
|
363
|
-
# #
|
362
|
+
# # Our tests target has its own copy of
|
363
|
+
# # our testing frameworks, and has access
|
364
|
+
# # to ShowsKit as well because it is
|
365
|
+
# # a child of the abstract target 'Shows'
|
364
366
|
#
|
365
367
|
# target 'ShowsTests' do
|
366
368
|
# inherit! :search_paths
|
@@ -10,6 +10,7 @@ module Pod
|
|
10
10
|
# @param [Array<Source>] repos_dirs @see Sources
|
11
11
|
#
|
12
12
|
def initialize(sources)
|
13
|
+
raise "Cannot initialize an aggregate with a nil source: (#{sources})" if sources.include?(nil)
|
13
14
|
@sources = sources
|
14
15
|
end
|
15
16
|
|
@@ -164,7 +165,7 @@ module Pod
|
|
164
165
|
result = {}
|
165
166
|
sets.each do |set|
|
166
167
|
word_list_from_set(set).each do |w|
|
167
|
-
(result[w] ||= []).push(set.name
|
168
|
+
(result[w] ||= []).push(set.name)
|
168
169
|
end
|
169
170
|
end
|
170
171
|
result
|
@@ -0,0 +1,412 @@
|
|
1
|
+
module Pod
|
2
|
+
class Source
|
3
|
+
class Manager
|
4
|
+
# @return [Pathname] The directory that contains the source repo
|
5
|
+
# directories.
|
6
|
+
#
|
7
|
+
attr_reader :repos_dir
|
8
|
+
|
9
|
+
def initialize(repos_dir)
|
10
|
+
@repos_dir = Pathname(repos_dir).expand_path
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Array<Pathname>] The source repo directories.
|
14
|
+
#
|
15
|
+
def source_repos
|
16
|
+
return [] unless repos_dir.exist?
|
17
|
+
repos_dir.children.select(&:directory?).sort_by { |d| d.basename.to_s.downcase }
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Source::Aggregate] The aggregate of all the sources with the
|
21
|
+
# known Pods.
|
22
|
+
#
|
23
|
+
def aggregate
|
24
|
+
aggregate_with_repos(source_repos)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [Source::Aggregate] The aggregate of the sources from repos.
|
28
|
+
#
|
29
|
+
# @param [Dependency] dependency
|
30
|
+
# The dependency for which to find or build the appropriate.
|
31
|
+
# aggregate. If the dependency specifies a source podspec repo
|
32
|
+
# then only that source will be used, otherwise all sources
|
33
|
+
# will be used.
|
34
|
+
#
|
35
|
+
def aggregate_for_dependency(dependency)
|
36
|
+
if dependency.podspec_repo
|
37
|
+
source = source_with_url(dependency.podspec_repo)
|
38
|
+
raise StandardError, '[Bug] Failed to find known source with the URL ' \
|
39
|
+
"#{dependency.podspec_repo.inspect}" if source.nil?
|
40
|
+
|
41
|
+
aggregate_with_repos([source.repo])
|
42
|
+
else
|
43
|
+
aggregate
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Source>] The list of the sources with the given names.
|
48
|
+
#
|
49
|
+
# @param [Array<#to_s>] names
|
50
|
+
# The names of the sources.
|
51
|
+
#
|
52
|
+
def sources(names)
|
53
|
+
dirs = names.map { |name| source_dir(name) }
|
54
|
+
dirs.map { |repo| source_from_path(repo) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [Array<Source>] The list of all the sources known to this
|
58
|
+
# installation of CocoaPods.
|
59
|
+
#
|
60
|
+
def all
|
61
|
+
aggregate.sources
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Array<Source>] The CocoaPods Master Repo source.
|
65
|
+
#
|
66
|
+
def master
|
67
|
+
sources(['master']).select { |s| s.repo.directory? }
|
68
|
+
end
|
69
|
+
|
70
|
+
# @!group Master repo
|
71
|
+
|
72
|
+
# @return [Pathname] The path of the master repo.
|
73
|
+
#
|
74
|
+
def master_repo_dir
|
75
|
+
source_dir('master')
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Bool] Checks if the master repo is usable.
|
79
|
+
#
|
80
|
+
# @note Note this is used to automatically setup the master repo if
|
81
|
+
# needed.
|
82
|
+
#
|
83
|
+
def master_repo_functional?
|
84
|
+
return false unless master_repo = master.first
|
85
|
+
master_repo.metadata.compatible?(CORE_VERSION)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Search the appropriate sources to match the set for the given dependency.
|
89
|
+
#
|
90
|
+
# @return [Set, nil] a set for a given dependency including all the
|
91
|
+
# {Source} that contain the Pod. If no sources containing the
|
92
|
+
# Pod where found it returns nil.
|
93
|
+
#
|
94
|
+
# @raise If no source can be found that includes the dependency.
|
95
|
+
#
|
96
|
+
def search(dependency)
|
97
|
+
aggregate_for_dependency(dependency).search(dependency)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Search all the sources with the given search term.
|
101
|
+
#
|
102
|
+
# @param [String] query
|
103
|
+
# The search term.
|
104
|
+
#
|
105
|
+
# @param [Bool] full_text_search
|
106
|
+
# Whether the search should be limited to the name of the Pod or
|
107
|
+
# should include also the author, the summary, and the
|
108
|
+
# description.
|
109
|
+
#
|
110
|
+
# @raise If no source including the set can be found.
|
111
|
+
#
|
112
|
+
# @return [Array<Set>] The sets that contain the search term.
|
113
|
+
#
|
114
|
+
def search_by_name(query, full_text_search = false)
|
115
|
+
query_word_regexps = query.split.map { |word| /#{word}/i }
|
116
|
+
if full_text_search
|
117
|
+
query_word_results_hash = {}
|
118
|
+
updated_search_index.each_value do |word_spec_hash|
|
119
|
+
word_spec_hash.each_pair do |word, spec_names|
|
120
|
+
query_word_regexps.each do |query_word_regexp|
|
121
|
+
set = (query_word_results_hash[query_word_regexp] ||= Set.new)
|
122
|
+
set.merge(spec_names) if word =~ query_word_regexp
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
found_set_names = query_word_results_hash.values.reduce(:&)
|
127
|
+
found_set_names ||= []
|
128
|
+
sets = found_set_names.map do |name|
|
129
|
+
aggregate.representative_set(name)
|
130
|
+
end
|
131
|
+
# Remove nil values because representative_set return nil if no pod is found in any of the sources.
|
132
|
+
sets.compact!
|
133
|
+
else
|
134
|
+
sets = aggregate.search_by_name(query, false)
|
135
|
+
end
|
136
|
+
if sets.empty?
|
137
|
+
extra = ', author, summary, or description' if full_text_search
|
138
|
+
raise Informative, "Unable to find a pod with name#{extra}" \
|
139
|
+
"matching `#{query}`"
|
140
|
+
end
|
141
|
+
sorted_sets(sets, query_word_regexps)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns given set array by sorting it in-place.
|
145
|
+
#
|
146
|
+
# @param [Array<Set>] sets
|
147
|
+
# Array of sets to be sorted.
|
148
|
+
#
|
149
|
+
# @param [Array<Regexp>] query_word_regexps
|
150
|
+
# Array of regexp objects for user query.
|
151
|
+
#
|
152
|
+
# @return [Array<Set>] Given sets parameter itself after sorting it in-place.
|
153
|
+
#
|
154
|
+
def sorted_sets(sets, query_word_regexps)
|
155
|
+
sets.sort_by! do |set|
|
156
|
+
pre_match_length = nil
|
157
|
+
found_query_index = nil
|
158
|
+
found_query_count = 0
|
159
|
+
query_word_regexps.each_with_index do |q, idx|
|
160
|
+
if (m = set.name.match(/#{q}/i))
|
161
|
+
pre_match_length ||= m.pre_match.length
|
162
|
+
found_query_index ||= idx
|
163
|
+
found_query_count += 1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
pre_match_length ||= 1000
|
167
|
+
found_query_index ||= 1000
|
168
|
+
[-found_query_count, pre_match_length, found_query_index, set.name.downcase]
|
169
|
+
end
|
170
|
+
sets
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns the search data. If a saved search data exists, retrieves it from file and returns it.
|
174
|
+
# Else, creates the search data from scratch, saves it to file system, and returns it.
|
175
|
+
# Search data is grouped by source repos. For each source, it contains a hash where keys are words
|
176
|
+
# and values are the pod names containing corresponding word.
|
177
|
+
#
|
178
|
+
# For each source, list of unique words are generated from the following spec information.
|
179
|
+
# - version
|
180
|
+
# - summary
|
181
|
+
# - description
|
182
|
+
# - authors
|
183
|
+
#
|
184
|
+
# @return [Hash{String => Hash{String => Array<String>}}] The up to date search data.
|
185
|
+
#
|
186
|
+
def updated_search_index
|
187
|
+
index = stored_search_index || {}
|
188
|
+
all.each do |source|
|
189
|
+
source_name = source.name
|
190
|
+
unless index[source_name]
|
191
|
+
CoreUI.print "Creating search index for spec repo '#{source_name}'.."
|
192
|
+
index[source_name] = aggregate.generate_search_index_for_source(source)
|
193
|
+
CoreUI.puts ' Done!'
|
194
|
+
end
|
195
|
+
end
|
196
|
+
save_search_index(index)
|
197
|
+
index
|
198
|
+
end
|
199
|
+
|
200
|
+
# Updates the stored search index if there are changes in spec repos while updating them.
|
201
|
+
# Update is performed incrementally. Only the changed pods' search data is re-generated and updated.
|
202
|
+
# @param [Hash{Source => Array<String>}] changed_spec_paths
|
203
|
+
# A hash containing changed specification paths for each source.
|
204
|
+
#
|
205
|
+
def update_search_index_if_needed(changed_spec_paths)
|
206
|
+
search_index = stored_search_index
|
207
|
+
return unless search_index
|
208
|
+
changed_spec_paths.each_pair do |source, spec_paths|
|
209
|
+
index_for_source = search_index[source.name]
|
210
|
+
next unless index_for_source && !spec_paths.empty?
|
211
|
+
updated_pods = source.pods_for_specification_paths(spec_paths)
|
212
|
+
|
213
|
+
new_index = aggregate.generate_search_index_for_changes_in_source(source, spec_paths)
|
214
|
+
# First traverse search_index and update existing words
|
215
|
+
# Remove traversed words from new_index after adding to search_index,
|
216
|
+
# so that only non existing words will remain in new_index after enumeration completes.
|
217
|
+
index_for_source.each_pair do |word, _|
|
218
|
+
if new_index[word]
|
219
|
+
index_for_source[word] |= new_index[word]
|
220
|
+
new_index.delete(word)
|
221
|
+
else
|
222
|
+
index_for_source[word] -= updated_pods
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Now add non existing words remained in new_index to search_index
|
227
|
+
index_for_source.merge!(new_index)
|
228
|
+
end
|
229
|
+
save_search_index(search_index)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Updates search index for changed pods in background
|
233
|
+
# @param [Hash{Source => Array<String>}] changed_spec_paths
|
234
|
+
# A hash containing changed specification paths for each source.
|
235
|
+
#
|
236
|
+
def update_search_index_if_needed_in_background(changed_spec_paths)
|
237
|
+
Process.fork do
|
238
|
+
Process.daemon
|
239
|
+
update_search_index_if_needed(changed_spec_paths)
|
240
|
+
exit
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Returns the search data stored in the file system.
|
245
|
+
# If existing data in the file system is not valid, returns nil.
|
246
|
+
#
|
247
|
+
def stored_search_index
|
248
|
+
@updated_search_index ||= begin
|
249
|
+
if search_index_path.exist?
|
250
|
+
require 'json'
|
251
|
+
index = JSON.parse(search_index_path.read)
|
252
|
+
index if index.is_a?(Hash) # TODO: should we also check if hash has correct hierarchy?
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Stores given search data in the file system.
|
258
|
+
# @param [Hash] index
|
259
|
+
# Index to be saved in file system
|
260
|
+
#
|
261
|
+
def save_search_index(index)
|
262
|
+
require 'json'
|
263
|
+
@updated_search_index = index
|
264
|
+
search_index_path.open('w') do |io|
|
265
|
+
io.write(@updated_search_index.to_json)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# Allows to clear the search index.
|
270
|
+
#
|
271
|
+
attr_writer :updated_search_index
|
272
|
+
|
273
|
+
# @return [Pathname] The path where the search index should be stored.
|
274
|
+
#
|
275
|
+
attr_accessor :search_index_path
|
276
|
+
|
277
|
+
private
|
278
|
+
|
279
|
+
# @return [Source] The Source at a given path.
|
280
|
+
#
|
281
|
+
# @param [Pathname] path
|
282
|
+
# The local file path to one podspec repo.
|
283
|
+
#
|
284
|
+
def source_from_path(path)
|
285
|
+
@sources_by_path ||= Hash.new do |hash, key|
|
286
|
+
hash[key] = if key.basename.to_s == 'master'
|
287
|
+
MasterSource.new(key)
|
288
|
+
else
|
289
|
+
Source.new(key)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
@sources_by_path[path]
|
293
|
+
end
|
294
|
+
|
295
|
+
# @return [Source::Aggregate] The aggregate of the sources from repos.
|
296
|
+
#
|
297
|
+
# @param [Array<Pathname>] repos
|
298
|
+
# The local file paths to one or more podspec repo caches.
|
299
|
+
#
|
300
|
+
def aggregate_with_repos(repos)
|
301
|
+
sources = repos.map { |path| source_from_path(path) }
|
302
|
+
@aggregates_by_repos ||= {}
|
303
|
+
@aggregates_by_repos[repos] ||= Source::Aggregate.new(sources)
|
304
|
+
end
|
305
|
+
|
306
|
+
# @return [Source] The git source with the given name. If no git source
|
307
|
+
# with given name is found it raises.
|
308
|
+
#
|
309
|
+
# @param [String] name
|
310
|
+
# The name of the source.
|
311
|
+
#
|
312
|
+
def git_source_named(name)
|
313
|
+
specified_source = sources([name]).first
|
314
|
+
unless specified_source
|
315
|
+
raise Informative, "Unable to find the `#{name}` repo."
|
316
|
+
end
|
317
|
+
unless specified_source.git?
|
318
|
+
raise Informative, "The `#{name}` repo is not a git repo."
|
319
|
+
end
|
320
|
+
specified_source
|
321
|
+
end
|
322
|
+
|
323
|
+
# @return [Source] The list of the git sources.
|
324
|
+
#
|
325
|
+
def git_sources
|
326
|
+
all.select(&:git?)
|
327
|
+
end
|
328
|
+
|
329
|
+
# @return [Pathname] The path of the source with the given name.
|
330
|
+
#
|
331
|
+
# @param [String] name
|
332
|
+
# The name of the source.
|
333
|
+
#
|
334
|
+
def source_dir(name)
|
335
|
+
repos_dir + name
|
336
|
+
end
|
337
|
+
|
338
|
+
# @return [Source] The source whose {Source#url} is equal to `url`.
|
339
|
+
#
|
340
|
+
# @param [String] url
|
341
|
+
# The URL of the source.
|
342
|
+
#
|
343
|
+
def source_with_url(url)
|
344
|
+
url = url.downcase.gsub(/.git$/, '')
|
345
|
+
url = 'https://github.com/cocoapods/specs' if url =~ %r{github.com[:/]+cocoapods/specs}
|
346
|
+
all.find do |source|
|
347
|
+
source.url && source.url.downcase.gsub(/.git$/, '') == url
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Returns a suitable repository name for `url`.
|
352
|
+
#
|
353
|
+
# @example A GitHub.com URL
|
354
|
+
#
|
355
|
+
# name_for_url('https://github.com/Artsy/Specs.git')
|
356
|
+
# # "artsy"
|
357
|
+
# name_for_url('https://github.com/Artsy/Specs.git')
|
358
|
+
# # "artsy-1"
|
359
|
+
#
|
360
|
+
# @example A non-Github.com URL
|
361
|
+
#
|
362
|
+
# name_for_url('https://sourceforge.org/Artsy/Specs.git')
|
363
|
+
# # sourceforge-artsy-specs
|
364
|
+
#
|
365
|
+
# @example A file URL
|
366
|
+
#
|
367
|
+
# name_for_url('file:///Artsy/Specs.git')
|
368
|
+
# # artsy-specs
|
369
|
+
#
|
370
|
+
# @param [#to_s] url
|
371
|
+
# The URL of the source.
|
372
|
+
#
|
373
|
+
# @return [String] A suitable repository name for `url`.
|
374
|
+
#
|
375
|
+
def name_for_url(url)
|
376
|
+
base_from_host_and_path = lambda do |host, path|
|
377
|
+
if host
|
378
|
+
base = host.split('.')[-2] || host
|
379
|
+
base += '-'
|
380
|
+
else
|
381
|
+
base = ''
|
382
|
+
end
|
383
|
+
|
384
|
+
base + path.gsub(/.git$/, '').gsub(%r{^/}, '').split('/').join('-')
|
385
|
+
end
|
386
|
+
|
387
|
+
case url.to_s.downcase
|
388
|
+
when %r{github.com[:/]+cocoapods/specs}
|
389
|
+
base = 'master'
|
390
|
+
when %r{github.com[:/]+(.+)/(.+)}
|
391
|
+
base = Regexp.last_match[1]
|
392
|
+
when %r{^\S+@(\S+)[:/]+(.+)$}
|
393
|
+
host, path = Regexp.last_match.captures
|
394
|
+
base = base_from_host_and_path[host, path]
|
395
|
+
when URI.regexp
|
396
|
+
url = URI(url.downcase)
|
397
|
+
base = base_from_host_and_path[url.host, url.path]
|
398
|
+
else
|
399
|
+
base = url.to_s.downcase
|
400
|
+
end
|
401
|
+
|
402
|
+
name = base
|
403
|
+
infinity = 1.0 / 0
|
404
|
+
(1..infinity).each do |i|
|
405
|
+
break unless source_dir(name).exist?
|
406
|
+
name = "#{base}-#{i}"
|
407
|
+
end
|
408
|
+
name
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
autoload :Digest, 'digest/md5'
|
2
|
+
require 'active_support/hash_with_indifferent_access'
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
|
5
|
+
module Pod
|
6
|
+
class Source
|
7
|
+
class Metadata
|
8
|
+
attr_reader :minimum_cocoapods_version
|
9
|
+
attr_reader :maximum_cocoapods_version
|
10
|
+
attr_reader :latest_cocoapods_version
|
11
|
+
attr_reader :prefix_lengths
|
12
|
+
attr_reader :last_compatible_versions
|
13
|
+
|
14
|
+
def initialize(hash = {})
|
15
|
+
hash = hash.with_indifferent_access
|
16
|
+
@minimum_cocoapods_version = hash['min']
|
17
|
+
@minimum_cocoapods_version &&= Pod::Version.new(@minimum_cocoapods_version)
|
18
|
+
@maximum_cocoapods_version = hash['max']
|
19
|
+
@maximum_cocoapods_version &&= Pod::Version.new(@maximum_cocoapods_version)
|
20
|
+
@latest_cocoapods_version = hash['last']
|
21
|
+
@latest_cocoapods_version &&= Pod::Version.new(@latest_cocoapods_version)
|
22
|
+
@prefix_lengths = Array(hash['prefix_lengths']).map!(&:to_i)
|
23
|
+
@last_compatible_versions = Array(hash['last_compatible_versions']).map(&Pod::Version.method(:new)).sort
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.from_file(file)
|
27
|
+
hash = file.file? ? YAMLHelper.load_file(file) : {}
|
28
|
+
new(hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash
|
32
|
+
hash = ActiveSupport::HashWithIndifferentAccess.new
|
33
|
+
hash['min'] = @minimum_cocoapods_version.to_s if @minimum_cocoapods_version
|
34
|
+
hash['max'] = @maximum_cocoapods_version.to_s if @maximum_cocoapods_version
|
35
|
+
hash['last'] = @latest_cocoapods_version.to_s if @latest_cocoapods_version
|
36
|
+
hash['prefix_lengths'] = @prefix_lengths unless @prefix_lengths.empty?
|
37
|
+
hash['last_compatible_versions'] = @last_compatible_versions.map(&:to_s) unless @last_compatible_versions.empty?
|
38
|
+
hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def path_fragment(pod_name, version = nil)
|
42
|
+
prefixes = if prefix_lengths.empty?
|
43
|
+
[]
|
44
|
+
else
|
45
|
+
hashed = Digest::MD5.hexdigest(pod_name)
|
46
|
+
prefix_lengths.map do |length|
|
47
|
+
hashed.slice!(0, length)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
prefixes.concat([pod_name, version]).compact.join(File::SEPARATOR)
|
51
|
+
end
|
52
|
+
|
53
|
+
def last_compatible_version(target_version)
|
54
|
+
return unless minimum_cocoapods_version
|
55
|
+
return if minimum_cocoapods_version <= target_version
|
56
|
+
@last_compatible_versions.reverse_each.bsearch { |v| v <= target_version }.tap do |version|
|
57
|
+
raise Informative, 'Unable to find compatible version' unless version
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns whether a source is compatible with the current version of
|
62
|
+
# CocoaPods.
|
63
|
+
#
|
64
|
+
# @param [Pathname] dir
|
65
|
+
# The directory where the source is stored.
|
66
|
+
#
|
67
|
+
# @return [Bool] whether the source is compatible.
|
68
|
+
#
|
69
|
+
def compatible?(version)
|
70
|
+
bin_version = Gem::Version.new(version)
|
71
|
+
supports_min = !minimum_cocoapods_version ||
|
72
|
+
(bin_version >= Gem::Version.new(minimum_cocoapods_version))
|
73
|
+
supports_max = !maximum_cocoapods_version ||
|
74
|
+
(bin_version <= Gem::Version.new(maximum_cocoapods_version))
|
75
|
+
supports_min && supports_max
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'cocoapods-core/source/acceptor'
|
2
2
|
require 'cocoapods-core/source/aggregate'
|
3
3
|
require 'cocoapods-core/source/health_reporter'
|
4
|
+
require 'cocoapods-core/source/manager'
|
5
|
+
require 'cocoapods-core/source/metadata'
|
4
6
|
|
5
7
|
module Pod
|
6
8
|
# The Source class is responsible to manage a collection of podspecs.
|
@@ -14,14 +16,15 @@ module Pod
|
|
14
16
|
# "#{SPEC_NAME}/#{VERSION}/#{SPEC_NAME}.podspec"
|
15
17
|
#
|
16
18
|
class Source
|
17
|
-
# @return [
|
19
|
+
# @return [Pod::Source::Metadata] The metadata for this source.
|
18
20
|
#
|
19
|
-
attr_reader :
|
21
|
+
attr_reader :metadata
|
20
22
|
|
21
23
|
# @param [Pathname, String] repo @see #repo.
|
22
24
|
#
|
23
25
|
def initialize(repo)
|
24
26
|
@repo = Pathname(repo).expand_path
|
27
|
+
refresh_metadata
|
25
28
|
end
|
26
29
|
|
27
30
|
# @return [String] The name of the source.
|
@@ -74,9 +77,49 @@ module Pod
|
|
74
77
|
"#<#{self.class} name:#{name} type:#{type}>"
|
75
78
|
end
|
76
79
|
|
80
|
+
# @!group Paths
|
81
|
+
#-------------------------------------------------------------------------#
|
82
|
+
|
83
|
+
# @return [Pathname] The path where the source is stored.
|
84
|
+
#
|
85
|
+
attr_reader :repo
|
86
|
+
|
87
|
+
# @return [Pathname] The directory where the specs are stored.
|
88
|
+
#
|
89
|
+
# @note In previous versions of CocoaPods they used to be stored in
|
90
|
+
# the root of the repo. This lead to issues, especially with
|
91
|
+
# the GitHub interface and now they are stored in a dedicated
|
92
|
+
# folder.
|
93
|
+
#
|
94
|
+
def specs_dir
|
95
|
+
@specs_dir ||= begin
|
96
|
+
specs_sub_dir = repo + 'Specs'
|
97
|
+
if specs_sub_dir.exist?
|
98
|
+
specs_sub_dir
|
99
|
+
elsif repo.exist?
|
100
|
+
repo
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [String] name The name of the pod.
|
106
|
+
#
|
107
|
+
# @return [Pathname] The path at which the specs for the given pod are
|
108
|
+
# stored.
|
109
|
+
#
|
110
|
+
def pod_path(name)
|
111
|
+
specs_dir.join(*metadata.path_fragment(name))
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [Pathname] The path at which source metadata is stored.
|
115
|
+
#
|
116
|
+
def metadata_path
|
117
|
+
repo + 'CocoaPods-version.yml'
|
118
|
+
end
|
119
|
+
|
77
120
|
public
|
78
121
|
|
79
|
-
# @!group
|
122
|
+
# @!group Querying the source
|
80
123
|
#-------------------------------------------------------------------------#
|
81
124
|
|
82
125
|
# @return [Array<String>] the list of the name of all the Pods.
|
@@ -86,10 +129,10 @@ module Pod
|
|
86
129
|
unless specs_dir
|
87
130
|
raise Informative, "Unable to find a source named: `#{name}`"
|
88
131
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
132
|
+
glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*')
|
133
|
+
Pathname.glob(glob).reduce([]) do |pods, entry|
|
134
|
+
pods << entry.basename.to_s if entry.directory?
|
135
|
+
pods
|
93
136
|
end.sort
|
94
137
|
end
|
95
138
|
|
@@ -118,7 +161,7 @@ module Pod
|
|
118
161
|
def versions(name)
|
119
162
|
return nil unless specs_dir
|
120
163
|
raise ArgumentError, 'No name' unless name
|
121
|
-
pod_dir =
|
164
|
+
pod_dir = pod_path(name)
|
122
165
|
return unless pod_dir.exist?
|
123
166
|
pod_dir.children.map do |v|
|
124
167
|
basename = v.basename.to_s
|
@@ -153,7 +196,7 @@ module Pod
|
|
153
196
|
def specification_path(name, version)
|
154
197
|
raise ArgumentError, 'No name' unless name
|
155
198
|
raise ArgumentError, 'No version' unless version
|
156
|
-
path =
|
199
|
+
path = pod_path(name) + version.to_s
|
157
200
|
specification_path = path + "#{name}.podspec.json"
|
158
201
|
unless specification_path.exist?
|
159
202
|
specification_path = path + "#{name}.podspec"
|
@@ -169,15 +212,17 @@ module Pod
|
|
169
212
|
# source.
|
170
213
|
#
|
171
214
|
def all_specs
|
172
|
-
|
215
|
+
glob = specs_dir.join('*/' * metadata.prefix_lengths.size, '*', '*', '*.podspec{.json,}')
|
216
|
+
specs = Pathname.glob(glob).map do |path|
|
173
217
|
begin
|
174
|
-
|
218
|
+
Specification.from_file(path)
|
175
219
|
rescue
|
176
|
-
CoreUI.warn "Skipping `#{
|
220
|
+
CoreUI.warn "Skipping `#{path.relative_path_from(repo)}` because the " \
|
221
|
+
'podspec contains errors.'
|
177
222
|
next
|
178
223
|
end
|
179
224
|
end
|
180
|
-
specs.
|
225
|
+
specs.compact
|
181
226
|
end
|
182
227
|
|
183
228
|
# Returns the set for the Pod with the given name.
|
@@ -217,8 +262,10 @@ module Pod
|
|
217
262
|
if query.is_a?(Dependency)
|
218
263
|
query = query.root_name
|
219
264
|
end
|
220
|
-
|
221
|
-
|
265
|
+
found = Pathname.glob(pod_path(query)).map { |path| path.basename.to_s }
|
266
|
+
if [query] == found
|
267
|
+
set = set(query)
|
268
|
+
set if set.specification.name == query
|
222
269
|
end
|
223
270
|
end
|
224
271
|
|
@@ -290,11 +337,37 @@ module Pod
|
|
290
337
|
Dir.chdir(repo) do
|
291
338
|
prev_commit_hash = git_commit_hash
|
292
339
|
update_git_repo(show_output)
|
340
|
+
refresh_metadata
|
341
|
+
if version = metadata.last_compatible_version(Version.new(CORE_VERSION))
|
342
|
+
tag = "v#{version}"
|
343
|
+
CoreUI.warn "Using the `#{tag}` tag of the `#{name}` source because " \
|
344
|
+
"it is the last version compatible with CocoaPods #{CORE_VERSION}."
|
345
|
+
git(['checkout', tag])
|
346
|
+
end
|
293
347
|
changed_spec_paths = diff_until_commit_hash(prev_commit_hash)
|
294
348
|
end
|
295
349
|
changed_spec_paths
|
296
350
|
end
|
297
351
|
|
352
|
+
def git?
|
353
|
+
Dir.chdir(repo) do
|
354
|
+
!git(%w(rev-parse HEAD)).empty?
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def verify_compatibility!
|
359
|
+
return if metadata.compatible?(CORE_VERSION)
|
360
|
+
|
361
|
+
version_msg = if metadata.minimum_cocoapods_version == metadata.maximum_cocoapods_version
|
362
|
+
metadata.minimum_cocoapods_version
|
363
|
+
else
|
364
|
+
"#{metadata.minimum_cocoapods_version} - #{metadata.maximum_cocoapods_version}"
|
365
|
+
end
|
366
|
+
raise Informative, "The `#{name}` repo requires " \
|
367
|
+
"CocoaPods #{version_msg} (currently using #{CORE_VERSION})\n" \
|
368
|
+
'Update CocoaPods, or checkout the appropriate tag in the repo.'
|
369
|
+
end
|
370
|
+
|
298
371
|
public
|
299
372
|
|
300
373
|
# @!group Representations
|
@@ -326,8 +399,9 @@ module Pod
|
|
326
399
|
#-------------------------------------------------------------------------#
|
327
400
|
|
328
401
|
def ensure_in_repo!
|
329
|
-
return if Pathname.pwd == repo
|
330
|
-
raise
|
402
|
+
return if Pathname.pwd.realpath == repo.realpath
|
403
|
+
raise StandardError, "Must be in the root of the repo (#{repo}), " \
|
404
|
+
"instead in #{Pathname.pwd}."
|
331
405
|
end
|
332
406
|
|
333
407
|
# Loads the specification for the given Pod gracefully.
|
@@ -349,22 +423,8 @@ module Pod
|
|
349
423
|
nil
|
350
424
|
end
|
351
425
|
|
352
|
-
|
353
|
-
|
354
|
-
# @note In previous versions of CocoaPods they used to be stored in
|
355
|
-
# the root of the repo. This lead to issues, especially with
|
356
|
-
# the GitHub interface and now they are stored in a dedicated
|
357
|
-
# folder.
|
358
|
-
#
|
359
|
-
def specs_dir
|
360
|
-
@specs_dir ||= begin
|
361
|
-
specs_sub_dir = repo + 'Specs'
|
362
|
-
if specs_sub_dir.exist?
|
363
|
-
specs_sub_dir
|
364
|
-
elsif repo.exist?
|
365
|
-
repo
|
366
|
-
end
|
367
|
-
end
|
426
|
+
def refresh_metadata
|
427
|
+
@metadata = Metadata.from_file(metadata_path)
|
368
428
|
end
|
369
429
|
|
370
430
|
def git_commit_hash
|
@@ -374,10 +434,16 @@ module Pod
|
|
374
434
|
|
375
435
|
def update_git_repo(show_output = false)
|
376
436
|
ensure_in_repo!
|
437
|
+
git(['checkout', git_tracking_branch])
|
377
438
|
output = git(%w(pull --ff-only), :include_error => true)
|
378
439
|
CoreUI.puts output if show_output
|
379
440
|
end
|
380
441
|
|
442
|
+
def git_tracking_branch
|
443
|
+
path = repo.join('.git', 'cocoapods_branch')
|
444
|
+
path.file? ? path.read.strip : 'master'
|
445
|
+
end
|
446
|
+
|
381
447
|
def diff_until_commit_hash(commit_hash)
|
382
448
|
ensure_in_repo!
|
383
449
|
git(%W(diff --name-only #{commit_hash}..HEAD)).split("\n")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocoapods-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.beta.
|
4
|
+
version: 1.0.0.beta.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eloy Duran
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-04-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -98,6 +98,8 @@ files:
|
|
98
98
|
- lib/cocoapods-core/source/acceptor.rb
|
99
99
|
- lib/cocoapods-core/source/aggregate.rb
|
100
100
|
- lib/cocoapods-core/source/health_reporter.rb
|
101
|
+
- lib/cocoapods-core/source/manager.rb
|
102
|
+
- lib/cocoapods-core/source/metadata.rb
|
101
103
|
- lib/cocoapods-core/specification.rb
|
102
104
|
- lib/cocoapods-core/specification/consumer.rb
|
103
105
|
- lib/cocoapods-core/specification/dsl.rb
|
@@ -138,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
140
|
version: '0'
|
139
141
|
requirements: []
|
140
142
|
rubyforge_project:
|
141
|
-
rubygems_version: 2.6.
|
143
|
+
rubygems_version: 2.6.3
|
142
144
|
signing_key:
|
143
145
|
specification_version: 3
|
144
146
|
summary: The models of CocoaPods
|