cocoapods-core 0.17.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +36 -0
  4. data/lib/cocoapods-core/core_ui.rb +19 -0
  5. data/lib/cocoapods-core/dependency.rb +295 -0
  6. data/lib/cocoapods-core/gem_version.rb +6 -0
  7. data/lib/cocoapods-core/lockfile.rb +440 -0
  8. data/lib/cocoapods-core/platform.rb +171 -0
  9. data/lib/cocoapods-core/podfile/dsl.rb +459 -0
  10. data/lib/cocoapods-core/podfile/target_definition.rb +503 -0
  11. data/lib/cocoapods-core/podfile.rb +345 -0
  12. data/lib/cocoapods-core/requirement.rb +15 -0
  13. data/lib/cocoapods-core/source/validator.rb +183 -0
  14. data/lib/cocoapods-core/source.rb +284 -0
  15. data/lib/cocoapods-core/specification/consumer.rb +356 -0
  16. data/lib/cocoapods-core/specification/dsl/attribute.rb +245 -0
  17. data/lib/cocoapods-core/specification/dsl/attribute_support.rb +76 -0
  18. data/lib/cocoapods-core/specification/dsl/deprecations.rb +47 -0
  19. data/lib/cocoapods-core/specification/dsl/platform_proxy.rb +67 -0
  20. data/lib/cocoapods-core/specification/dsl.rb +1110 -0
  21. data/lib/cocoapods-core/specification/linter.rb +436 -0
  22. data/lib/cocoapods-core/specification/root_attribute_accessors.rb +152 -0
  23. data/lib/cocoapods-core/specification/set/presenter.rb +229 -0
  24. data/lib/cocoapods-core/specification/set/statistics.rb +277 -0
  25. data/lib/cocoapods-core/specification/set.rb +171 -0
  26. data/lib/cocoapods-core/specification/yaml.rb +60 -0
  27. data/lib/cocoapods-core/specification.rb +592 -0
  28. data/lib/cocoapods-core/standard_error.rb +84 -0
  29. data/lib/cocoapods-core/vendor/dependency.rb +264 -0
  30. data/lib/cocoapods-core/vendor/requirement.rb +208 -0
  31. data/lib/cocoapods-core/vendor/version.rb +333 -0
  32. data/lib/cocoapods-core/vendor.rb +56 -0
  33. data/lib/cocoapods-core/version.rb +99 -0
  34. data/lib/cocoapods-core/yaml_converter.rb +202 -0
  35. data/lib/cocoapods-core.rb +23 -0
  36. metadata +154 -0
@@ -0,0 +1,284 @@
1
+ require 'cocoapods-core/source/validator'
2
+
3
+ module Pod
4
+
5
+ # The Source class is responsible to manage a collection of podspecs.
6
+ #
7
+ # The backing store of the podspecs collection is an implementation detail
8
+ # abstracted from the rest of CocoaPods.
9
+ #
10
+ # The default implementation uses a git repo as a backing store, where the
11
+ # podspecs are namespaced as:
12
+ #
13
+ # "#{SPEC_NAME}/#{VERSION}/#{SPEC_NAME}.podspec"
14
+ #
15
+ class Source
16
+
17
+ # @return [Pathname] the location of the repo of the source.
18
+ #
19
+ attr_reader :repo
20
+
21
+ # @param [Pathname] repo @see #repo.
22
+ #
23
+ def initialize(repo)
24
+ @repo = repo
25
+ end
26
+
27
+ # @return [String] the name of the source.
28
+ #
29
+ def name
30
+ @repo.basename.to_s
31
+ end
32
+
33
+ alias_method :to_s, :name
34
+
35
+
36
+ # @return [Integer] compares a source with another one for sorting
37
+ # purposes.
38
+ #
39
+ # @note Source are compared by the alphabetical order of their name, and
40
+ # this convention should be used in any case where sources need to
41
+ # be disambiguated.
42
+ #
43
+ def <=> (other)
44
+ name <=> other.name
45
+ end
46
+
47
+ #---------------------------------------------------------------------------#
48
+
49
+ # @!group Queering the source
50
+
51
+ # @return [Array<String>] the list of the name of all the Pods.
52
+ #
53
+ def pods
54
+ @repo.children.map do |child|
55
+ child.basename.to_s if child.directory? && child.basename.to_s != '.git'
56
+ end.compact
57
+ end
58
+
59
+ # @return [Array<Sets>] the sets of all the Pods.
60
+ #
61
+ def pod_sets
62
+ pods.map { |pod| Specification::Set.new(pod, self) }
63
+ end
64
+
65
+ # @return [Array<Version>] all the available versions for the Pod, sorted
66
+ # from highest to lowest.
67
+ #
68
+ # @param [String] name
69
+ # the name of the Pod.
70
+ #
71
+ def versions(name)
72
+ pod_dir = repo + name
73
+ return unless pod_dir.exist?
74
+ pod_dir.children.map do |v|
75
+ basename = v.basename.to_s
76
+ Version.new(basename) if v.directory? && basename[0,1] != '.'
77
+ end.compact.sort.reverse
78
+ end
79
+
80
+ # @return [Specification] the specification for a given version of Pod.
81
+ #
82
+ # @param [String] name
83
+ # the name of the Pod.
84
+ #
85
+ # @param [Version,String] version
86
+ # the version for the specification.
87
+ #
88
+ def specification(name, version)
89
+ path = repo + name + version.to_s
90
+ specification_path = path + "#{name}.podspec.yaml"
91
+ unless specification_path.exist?
92
+ specification_path = path + "#{name}.podspec"
93
+ end
94
+ Specification.from_file(specification_path)
95
+ end
96
+
97
+ # @return [Array<Specification>] all the specifications contained by the
98
+ # source.
99
+ #
100
+ def all_specs
101
+ specs = pods.map do |name|
102
+ begin
103
+ versions(name).map { |version| specification(name, version) }
104
+ rescue DSLError => e
105
+ CoreUI.warn "Skipping `#{name}` because the podspec contains errors."
106
+ next
107
+ end
108
+ end
109
+ specs.flatten.compact
110
+ end
111
+
112
+ #---------------------------------------------------------------------------#
113
+
114
+ # @!group Searching the source
115
+
116
+ # @return [Set] a set for a given dependency. The set is identified by the
117
+ # name of the dependency and takes into account subspecs.
118
+ #
119
+ def search(dependency)
120
+ pod_sets.find do |set|
121
+ # First match the (top level) name, which does not yet load the spec from disk
122
+ set.name == dependency.root_name &&
123
+ # Now either check if it's a dependency on the top level spec, or if it's not
124
+ # check if the requested subspec exists in the top level spec.
125
+ set.specification.subspec_by_name(dependency.name)
126
+ end
127
+ end
128
+
129
+ # @return [Array<Set>] The list of the sets that contain the search term.
130
+ #
131
+ # @param [String] query
132
+ # the search term.
133
+ #
134
+ # @param [Bool] full_text_search
135
+ # whether the search should be limited to the name of the Pod or
136
+ # should include also the author, the summary, and the description.
137
+ #
138
+ # @note full text search requires to load the specification for each pod,
139
+ # hence is considerably slower.
140
+ #
141
+ def search_by_name(query, full_text_search = false)
142
+ pod_sets.map do |set|
143
+ if full_text_search
144
+ begin
145
+ s = set.specification
146
+ text = "#{s.name} #{s.authors} #{s.summary} #{s.description}"
147
+ rescue
148
+ CoreUI.warn "Skipping `#{set.name}` because the podspec contains errors."
149
+ end
150
+ else
151
+ text = set.name
152
+ end
153
+ set if text && text.downcase.include?(query.downcase)
154
+ end.compact
155
+ end
156
+
157
+ #---------------------------------------------------------------------------#
158
+
159
+ # @!group Representations
160
+
161
+ # @return [Hash{String=>{String=>Specification}}] the static representation
162
+ # of all the specifications grouped first by name and then by
163
+ # version.
164
+ #
165
+ def to_hash
166
+ hash = {}
167
+ all_specs.each do |spec|
168
+ print '.'
169
+ hash[spec.name] ||= {}
170
+ hash[spec.name][spec.version.version] = spec.to_hash
171
+ end
172
+ hash
173
+ end
174
+
175
+ # @return [String] the YAML encoded {to_hash} representation.
176
+ #
177
+ def to_yaml
178
+ require 'yaml'
179
+ to_hash.to_yaml
180
+ end
181
+
182
+ # @return [String] the JSON encoded {to_hash} representation.
183
+ #
184
+ def to_json
185
+ require 'json'
186
+ to_hash.to_json
187
+ end
188
+
189
+ #-------------------------------------------------------------------------#
190
+
191
+ # The Aggregate manages a directory of sources repositories.
192
+ #
193
+ class Aggregate
194
+
195
+ # @return [Pathname] the directory were the repositories are stored.
196
+ #
197
+ attr_reader :repos_dir
198
+
199
+ # @param [Pathname] repos_dir @see repos_dir.
200
+ #
201
+ def initialize(repos_dir)
202
+ @repos_dir = repos_dir
203
+ end
204
+
205
+ # @return [Array<Source>] all the sources.
206
+ #
207
+ def all
208
+ @sources ||= dirs.map { |repo| Source.new(repo) }.sort_by(&:name)
209
+ end
210
+
211
+ # @return [Array<String>] the names of all the pods available.
212
+ #
213
+ def all_pods
214
+ all.map(&:pods).flatten.uniq
215
+ end
216
+
217
+ # @return [Array<Set>] the sets for all the pods available.
218
+ #
219
+ # @note Implementation detail: The sources don't cache their values
220
+ # because they might change in response to an update. Therefore
221
+ # this method to prevent slowness caches the values before
222
+ # processing them.
223
+ #
224
+ def all_sets
225
+ pods_by_source = {}
226
+ all.each do |source|
227
+ pods_by_source[source] = source.pods
228
+ end
229
+ sources = pods_by_source.keys
230
+ pods = pods_by_source.values.flatten.uniq
231
+
232
+ pods.map do |pod|
233
+ pod_sources = sources.select{ |s| pods_by_source[s].include?(pod) }.compact
234
+ Specification::Set.new(pod, pod_sources)
235
+ end
236
+ end
237
+
238
+ # @return [Set, nil] a set for a given dependency including all the
239
+ # {Source} that contain the Pod. If no sources containing the
240
+ # Pod where found it returns nil.
241
+ #
242
+ # @raise If no source including the set can be found.
243
+ #
244
+ # @see Source#search
245
+ #
246
+ def search(dependency)
247
+ sources = all.select { |s| !s.search(dependency).nil? }
248
+ Specification::Set.new(dependency.root_name, sources) unless sources.empty?
249
+ end
250
+
251
+ # @return [Array<Set>] the sets that contain the search term.
252
+ #
253
+ # @raise If no source including the set can be found.
254
+ #
255
+ # @see Source#search_by_name
256
+ #
257
+ def search_by_name(query, full_text_search = false)
258
+ pods_by_source = {}
259
+ result = []
260
+ all.each { |s| pods_by_source[s] = s.search_by_name(query, full_text_search).map(&:name) }
261
+ root_spec_names = pods_by_source.values.flatten.uniq
262
+ root_spec_names.each do |pod|
263
+ sources = []
264
+ pods_by_source.each{ |source, pods| sources << source if pods.include?(pod) }
265
+ result << Specification::Set.new(pod, sources)
266
+ end
267
+ if result.empty?
268
+ extra = ", author, summary, or description" if full_text_search
269
+ raise(Informative, "Unable to find a pod with name" \
270
+ "#{extra} matching `#{query}'")
271
+ end
272
+ result
273
+ end
274
+
275
+ # @return [Array<Pathname>] the directories where the sources are stored.
276
+ #
277
+ # @raise If the repos dir doesn't exits.
278
+ #
279
+ def dirs
280
+ repos_dir.children.select(&:directory?)
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,356 @@
1
+ require 'active_support/core_ext/string/strip.rb'
2
+
3
+ module Pod
4
+ class Specification
5
+
6
+ # Allows to conveniently access a Specification programmatically.
7
+ #
8
+ # It takes care of:
9
+ #
10
+ # - standardizing the attributes
11
+ # - handling multi-platform values
12
+ # - handle default values
13
+ # - handle inherited values
14
+ #
15
+ # This class allows to store the values of the attributes in the
16
+ # Specification as specified in the DSL. The benefits is reduced reliance
17
+ # on meta programming to access the attributes and the possibility of
18
+ # serializing a specification back exactly as defined in a file.
19
+ #
20
+ class Consumer
21
+
22
+ # @return [Specification] The specification to consume.
23
+ #
24
+ attr_reader :spec
25
+
26
+ # @return [Symbol] The name of the platform for which the specification
27
+ # needs to be consumed.
28
+ #
29
+ attr_reader :platform_name
30
+
31
+ # @param [Specification] spec @see spec
32
+ # @param [Symbol, Platform] platform
33
+ # The platform for which the specification needs to be consumed.
34
+ #
35
+ def initialize(spec, platform)
36
+ @spec = spec
37
+ @platform_name = platform.is_a?(Symbol) ? platform : platform.name
38
+
39
+ unless spec.supported_on_platform?(platform)
40
+ raise StandardError, "#{to_s} is not compatible with #{platform}."
41
+ end
42
+ end
43
+
44
+ # Creates a method to access the contents of the attribute.
45
+ #
46
+ # @param [Symbol] name
47
+ # the name of the attribute.
48
+ #
49
+ # @macro [attach]
50
+ # @!method $1
51
+ #
52
+ def self.spec_attr_accessor(name)
53
+ define_method(name) do
54
+ value_for_attribute(name)
55
+ end
56
+ end
57
+
58
+ #-----------------------------------------------------------------------#
59
+
60
+ # @!group Regular attributes
61
+
62
+ # @return [Bool] Whether the source files of the specification require to
63
+ # be compiled with ARC.
64
+ #
65
+ spec_attr_accessor :requires_arc
66
+ alias_method :requires_arc?, :requires_arc
67
+
68
+ # @return [Array<String>] A list of frameworks that the user’s target
69
+ # needs to link against
70
+ #
71
+ spec_attr_accessor :frameworks
72
+
73
+ # @return [Array<String>] A list of frameworks that the user’s target
74
+ # needs to **weakly** link against
75
+ #
76
+ spec_attr_accessor :weak_frameworks
77
+
78
+ # @return [Array<String>] A list of libraries that the user’s target
79
+ # needs to link against
80
+ #
81
+ spec_attr_accessor :libraries
82
+
83
+ # @return [Array<String>] the list of compiler flags needed by the
84
+ # specification files.
85
+ #
86
+ spec_attr_accessor :compiler_flags
87
+
88
+ # @return [Hash{String => String}] the xcconfig flags for the current
89
+ # specification.
90
+ #
91
+ spec_attr_accessor :xcconfig
92
+
93
+ # @return [String] The contents of the prefix header.
94
+ #
95
+ spec_attr_accessor :prefix_header_contents
96
+
97
+ # @return [String] The path of the prefix header file.
98
+ #
99
+ spec_attr_accessor :prefix_header_file
100
+
101
+ # @return [String] the headers directory.
102
+ #
103
+ spec_attr_accessor :header_dir
104
+
105
+ # @return [String] the directory from where to preserve the headers
106
+ # namespacing.
107
+ #
108
+ spec_attr_accessor :header_mappings_dir
109
+
110
+ #-----------------------------------------------------------------------#
111
+
112
+ # @!group File patterns
113
+
114
+ # @return [Array<String>] the source files of the Pod.
115
+ #
116
+ spec_attr_accessor :source_files
117
+
118
+ # @return [Array<String>] the public headers of the Pod.
119
+ #
120
+ spec_attr_accessor :public_header_files
121
+
122
+ # @return [Array<String>] A hash where the key represents the
123
+ # paths of the resources to copy and the values the paths of
124
+ # the resources that should be copied.
125
+ #
126
+ spec_attr_accessor :resources
127
+
128
+ # @return [Array<String>] The file patterns that the
129
+ # Pod should ignore.
130
+ #
131
+ spec_attr_accessor :exclude_files
132
+
133
+ # @return [Array<String>] The paths that should be not
134
+ # cleaned.
135
+ #
136
+ spec_attr_accessor :preserve_paths
137
+
138
+ #-----------------------------------------------------------------------#
139
+
140
+ # @!group Dependencies
141
+
142
+ # @return [Array<Dependency>] the dependencies on other Pods.
143
+ #
144
+ def dependencies
145
+ value = value_for_attribute(:dependencies)
146
+ value.map do |name, requirements|
147
+ Dependency.new(name, requirements)
148
+ end
149
+ end
150
+
151
+ #-----------------------------------------------------------------------#
152
+
153
+ private
154
+
155
+ # Returns the value for the attribute with the given name for the
156
+ # specification. It takes into account inheritance, multi-platform
157
+ # attributes and default values.
158
+ #
159
+ # @param [Symbol] attr_name
160
+ # The name of the attribute.
161
+ #
162
+ # @return [String, Array, Hash] the value for the attribute.
163
+ #
164
+ def value_for_attribute(attr_name)
165
+ attr = Specification::DSL.attributes[attr_name]
166
+ value = value_with_inheritance(spec, attr)
167
+ value ||= attr.default(platform_name)
168
+ value ||= attr.container.new if attr.container
169
+ value
170
+ end
171
+
172
+ # Returns the value of a given attribute taking into account inheritance.
173
+ #
174
+ # @param [Specification] the_spec
175
+ # the specification for which the value is needed.
176
+ #
177
+ # @param [Specification::DSL::Attribute] attr
178
+ # the attribute for which that value is needed.
179
+ #
180
+ # @return [String, Array, Hash] the value for the attribute.
181
+ #
182
+ def value_with_inheritance(the_spec, attr)
183
+ value = raw_value_for_attribute(the_spec, attr)
184
+ if the_spec.root? || !attr.inherited?
185
+ return value
186
+ end
187
+
188
+ parent_value = value_with_inheritance(the_spec.parent, attr)
189
+ merge_values(attr, parent_value, value)
190
+ end
191
+
192
+ # Returns the value of a given attribute taking into account multi
193
+ # platform values.
194
+ #
195
+ # @param [Specification] the_spec
196
+ # the specification for which the value is needed.
197
+ #
198
+ # @param [Specification::DSL::Attribute] attr
199
+ # the attribute for which that value is needed.
200
+ #
201
+ # @return [String, Array, Hash] The value for an attribute.
202
+ #
203
+ def raw_value_for_attribute(the_spec, attr)
204
+ value = the_spec.attributes_hash[attr.name.to_s]
205
+ value = prepare_value(attr, value)
206
+
207
+ if attr.multi_platform? && the_spec.attributes_hash[platform_name.to_s]
208
+ platform_value = the_spec.attributes_hash[platform_name.to_s][attr.name.to_s]
209
+ platform_value = prepare_value(attr, platform_value)
210
+ value = merge_values(attr, value, platform_value)
211
+ end
212
+ value
213
+ end
214
+
215
+ # Merges the values of an attribute, either because the attribute is
216
+ # multi platform or because it is inherited.
217
+ #
218
+ # @param [Specification::DSL::Attribute] attr
219
+ # the attribute for which that value is needed.
220
+ #
221
+ # @param [String, Array, Hash] existing_value
222
+ # the current value (the value of the parent or non-multiplatform
223
+ # value).
224
+ #
225
+ # @param [String, Array, Hash] new_value
226
+ # the value to append (the value of the spec or the
227
+ # multi-platform value).
228
+ #
229
+ # @return [String, Array, Hash] The merged value.
230
+ #
231
+ def merge_values(attr, existing_value, new_value)
232
+ return existing_value if new_value.nil?
233
+ return new_value if existing_value.nil?
234
+
235
+ if attr.types.include?(TrueClass)
236
+ new_value.nil? ? existing_value : new_value
237
+ elsif attr.container == Array
238
+ r = [*existing_value] + [*new_value]
239
+ r.compact
240
+ elsif attr.container == Hash
241
+ existing_value = existing_value.merge(new_value) do |_, old, new|
242
+ if new.is_a?(Array) || old.is_a?(Array)
243
+ r = [*old] + [*new]
244
+ r.compact
245
+ else
246
+ old + ' ' + new
247
+ end
248
+ end
249
+ else
250
+ value
251
+ end
252
+ end
253
+
254
+ # Wraps a value in an Array if needed and calls the prepare hook to
255
+ # allow further customization of a value before storing it in the
256
+ # instance variable.
257
+ #
258
+ # @note Only array containers are wrapped. To automatically wrap
259
+ # values for attributes with hash containers a prepare hook
260
+ # should be used.
261
+ #
262
+ # @return [Object] the customized value of the original one if no
263
+ # prepare hook was defined.
264
+ #
265
+ def prepare_value(attr, value)
266
+ if attr.container == Array
267
+ if value.class == Rake::FileList
268
+ value = [value]
269
+ else
270
+ value = [*value].compact
271
+ end
272
+ end
273
+
274
+ hook_name = prepare_hook_name(attr)
275
+ if self.respond_to?(hook_name, true)
276
+ value = self.send(hook_name, value)
277
+ else
278
+ value
279
+ end
280
+ end
281
+
282
+ #-----------------------------------------------------------------------#
283
+
284
+ private
285
+
286
+ # Converts the keys of the given hash to a string.
287
+ #
288
+ # @todo Removed if not used by `resources_bundle`
289
+ #
290
+ # @param [Object] value
291
+ # the value that needs to be stripped from the Symbols.
292
+ #
293
+ # @return [Hash] the hash with the strings instead of the keys.
294
+ #
295
+ # def convert_keys_to_symbol(value)
296
+ # return unless value
297
+ # result = {}
298
+ # value.each do |key, subvalue|
299
+ # subvalue = convert_keys_to_symbol(subvalue) if subvalue.is_a?(Hash)
300
+ # result[key.to_sym] = subvalue
301
+ # end
302
+ # result
303
+ # end
304
+
305
+ #-----------------------------------------------------------------------#
306
+
307
+ private
308
+
309
+ # @!group Preparing Values
310
+ #
311
+ # Raw values need to be prepared as soon as they are read so they can be
312
+ # safely merged to support multi platform attributes and inheritance
313
+
314
+ # @return [String] the name of the prepare hook for this attribute.
315
+ #
316
+ # @note The hook is called after the value has been wrapped in an
317
+ # array (if needed according to the container) but before
318
+ # validation.
319
+ #
320
+ def prepare_hook_name(attr)
321
+ "_prepare_#{attr.name}"
322
+ end
323
+
324
+ # Converts the prefix header to a string if specified as an array.
325
+ #
326
+ # @param [String, Array] value.
327
+ # The value of the attribute as specified by the user.
328
+ #
329
+ # @return [String] the prefix header.
330
+ #
331
+ def _prepare_prefix_header_contents(value)
332
+ value.is_a?(Array) ? value * "\n" : value
333
+ end
334
+
335
+ # Converts the resources file patterns to a hash defaulting to the
336
+ # resource key if they are defined as an Array or a String.
337
+ #
338
+ # @param [String, Array, Hash] value.
339
+ # The value of the attribute as specified by the user.
340
+ #
341
+ # @return [Hash] the resources.
342
+ #
343
+ # def _prepare_resources_bundle(value)
344
+ # value = { :resources => value } unless value.is_a?(Hash)
345
+ # result = {}
346
+ # value.each do |key, patterns|
347
+ # result[key] = [*patterns].compact
348
+ # end
349
+ # result
350
+ # end
351
+
352
+ #-----------------------------------------------------------------------#
353
+
354
+ end
355
+ end
356
+ end