cocoapods-core 1.0.0.beta.6 → 1.0.0.beta.7
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 +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
|