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,345 @@
1
+ require 'cocoapods-core/podfile/dsl'
2
+ require 'cocoapods-core/podfile/target_definition'
3
+
4
+ module Pod
5
+
6
+ # The Podfile is a specification that describes the dependencies of the
7
+ # targets of an Xcode project.
8
+ #
9
+ # It supports its own DSL and generally is stored in files named
10
+ # `CocoaPods.podfile` or `Podfile`.
11
+ #
12
+ # The Podfile creates a hierarchy of target definitions that that store the
13
+ # information of necessary to generate the CocoaPods libraries.
14
+ #
15
+ class Podfile
16
+
17
+ # @!group DSL support
18
+
19
+ include Pod::Podfile::DSL
20
+
21
+ #-------------------------------------------------------------------------#
22
+
23
+ class Pod::Podfile::StandardError < StandardError; end
24
+
25
+ #-------------------------------------------------------------------------#
26
+
27
+ # @return [Pathname] the path where the podfile was loaded from. It is nil
28
+ # if the podfile was generated programmatically.
29
+ #
30
+ attr_accessor :defined_in_file
31
+
32
+ # @param [Pathname] defined_in_file
33
+ # the path of the podfile.
34
+ #
35
+ # @param [Proc] block
36
+ # an optional block that configures the podfile through the DSL.
37
+ #
38
+ # @example Creating a Podfile.
39
+ #
40
+ # platform :ios, "6.0"
41
+ # target :my_app do
42
+ # pod "AFNetworking", "~> 1.0"
43
+ # end
44
+ #
45
+ def initialize(defined_in_file = nil, internal_hash = {}, &block)
46
+ self.defined_in_file = defined_in_file
47
+ @internal_hash = internal_hash
48
+ if block
49
+ default_target_def = TargetDefinition.new(:default, self)
50
+ @root_target_definitions = [default_target_def]
51
+ @current_target_definition = default_target_def
52
+ instance_eval(&block)
53
+ else
54
+ @root_target_definitions = []
55
+ end
56
+ end
57
+
58
+ # @return [String] a string useful to represent the Podfile in a message
59
+ # presented to the user.
60
+ #
61
+ def to_s
62
+ "Podfile"
63
+ end
64
+
65
+ #-------------------------------------------------------------------------#
66
+
67
+ public
68
+
69
+ # @!group Working with a podfile
70
+
71
+ # @return [Hash{Symbol,String => TargetDefinition}] the target definitions
72
+ # of the podfile stored by their name.
73
+ #
74
+ def target_definitions
75
+ Hash[target_definition_list.map { |td| [td.name, td] }]
76
+ end
77
+
78
+ def target_definition_list
79
+ root_target_definitions.map { |td| [td, td.recursive_children] }.flatten
80
+ end
81
+
82
+ # @return [Array<TargetDefinition>] The root target definition.
83
+ #
84
+ attr_accessor :root_target_definitions
85
+
86
+ # @return [Array<Dependency>] the dependencies of the all the target
87
+ # definitions.
88
+ #
89
+ def dependencies
90
+ target_definition_list.map(&:dependencies).flatten.uniq
91
+ end
92
+
93
+ #-------------------------------------------------------------------------#
94
+
95
+ public
96
+
97
+ # @!group Attributes
98
+
99
+ # @return [String] the path of the workspace if specified by the user.
100
+ #
101
+ def workspace_path
102
+ path = get_hash_value('workspace')
103
+ if path
104
+ if File.extname(path) == '.xcworkspace'
105
+ path
106
+ else
107
+ "#{path}.xcworkspace"
108
+ end
109
+ end
110
+ end
111
+
112
+ # @return [Bool] whether the podfile should generate a BridgeSupport
113
+ # metadata document.
114
+ #
115
+ def generate_bridge_support?
116
+ get_hash_value('generate_bridge_support')
117
+ end
118
+
119
+ # @return [Bool] whether the -fobjc-arc flag should be added to the
120
+ # OTHER_LD_FLAGS.
121
+ #
122
+ def set_arc_compatibility_flag?
123
+ get_hash_value('set_arc_compatibility_flag')
124
+ end
125
+
126
+ #-------------------------------------------------------------------------#
127
+
128
+ public
129
+
130
+ # @!group Hooks
131
+
132
+ # Calls the pre install callback if defined.
133
+ #
134
+ # @param [Pod::Installer] installer
135
+ # the installer that is performing the installation.
136
+ #
137
+ # @return [Bool] whether a pre install callback was specified and it was
138
+ # called.
139
+ #
140
+ def pre_install!(installer)
141
+ if @pre_install_callback
142
+ @pre_install_callback.call(installer)
143
+ true
144
+ else
145
+ false
146
+ end
147
+ end
148
+
149
+ # Calls the post install callback if defined.
150
+ #
151
+ # @param [Pod::Installer] installer
152
+ # the installer that is performing the installation.
153
+ #
154
+ # @return [Bool] whether a post install callback was specified and it was
155
+ # called.
156
+ #
157
+ def post_install!(installer)
158
+ if @post_install_callback
159
+ @post_install_callback.call(installer)
160
+ true
161
+ else
162
+ false
163
+ end
164
+ end
165
+
166
+ #-------------------------------------------------------------------------#
167
+
168
+ public
169
+
170
+ # @!group Representations
171
+
172
+ # @return [Array] The keys used by the hash representation of the Podfile.
173
+ #
174
+ HASH_KEYS = [
175
+ 'target_definitions',
176
+ 'workspace',
177
+ 'generate_bridge_support',
178
+ 'set_arc_compatibility_flag',
179
+ ].freeze
180
+
181
+ # @return [Hash] The hash representation of the Podfile.
182
+ #
183
+ def to_hash
184
+ hash = {}
185
+ hash['target_definitions'] = Hash[root_target_definitions.map { |child| [child.name, child.to_hash] }]
186
+ hash.merge!(internal_hash)
187
+ hash
188
+ end
189
+
190
+ # @return [String] The YAML representation of the Podfile.
191
+ #
192
+ def to_yaml
193
+ to_hash.to_yaml
194
+ end
195
+
196
+ #-------------------------------------------------------------------------#
197
+
198
+ public
199
+
200
+ # @!group Class methods
201
+
202
+ # Initializes a podfile from the file with the given path.
203
+ #
204
+ # @param [Pathname] path
205
+ # the path from where the podfile should be loaded.
206
+ #
207
+ # @return [Podfile] the generated podfile.
208
+ #
209
+ def self.from_file(path)
210
+ path = Pathname.new(path)
211
+ unless path.exist?
212
+ raise StandardError, "No Podfile exists at path `#{path}`."
213
+ end
214
+ string = File.open(path, 'r:utf-8') { |f| f.read }
215
+ # Work around for Rubinius incomplete encoding in 1.9 mode
216
+ if string.respond_to?(:encoding) && string.encoding.name != "UTF-8"
217
+ string.encode!('UTF-8')
218
+ end
219
+
220
+ case path.extname
221
+ when ''
222
+ Podfile.from_ruby(string, path)
223
+ when '.yaml', '.cocoapods'
224
+ Podfile.from_yaml(string, path)
225
+ else
226
+ raise StandardError, "Unsupported Podfile format `#{path}`."
227
+ end
228
+ end
229
+
230
+ # Configures a new Podfile from the given ruby string.
231
+ #
232
+ # @param [String] string
233
+ # The ruby string which will configure the podfile with the DSL.
234
+ #
235
+ # @param [Pathname] path
236
+ # The path from which the Podfile is loaded.
237
+ #
238
+ # @return [Podfile] the new Podfile
239
+ #
240
+ def self.from_ruby(string, path = nil)
241
+ podfile = Podfile.new(path) do
242
+ begin
243
+ eval(string, nil, path.to_s)
244
+ rescue Exception => e
245
+ raise DSLError.new("Invalid `#{path.basename}` file: #{e.message}", path, e.backtrace)
246
+ end
247
+ end
248
+ podfile
249
+ end
250
+
251
+ # Configures a new Podfile from the given hash.
252
+ #
253
+ # @param [Hash] hash
254
+ # The hash which contains the information of the Podfile.
255
+ #
256
+ # @param [Pathname] path
257
+ # The path from which the Podfile is loaded.
258
+ #
259
+ # @return [Podfile] the new Podfile
260
+ #
261
+ def self.from_hash(hash, path = nil)
262
+ internal_hash = hash.dup
263
+ target_definitions = internal_hash.delete('target_definitions') || []
264
+ podfile = Podfile.new(path,internal_hash)
265
+ target_definitions.each do |name, definition_hash|
266
+ definition = TargetDefinition.from_hash(name, definition_hash, podfile)
267
+ podfile.root_target_definitions << definition
268
+ end
269
+ podfile
270
+ end
271
+
272
+ # Configures a new Podfile from the given YAML representation.
273
+ #
274
+ # @param [String] yaml
275
+ # The YAML encoded hash which contains the information of the
276
+ # Podfile.
277
+ #
278
+ # @param [Pathname] path
279
+ # The path from which the Podfile is loaded.
280
+ #
281
+ # @return [Podfile] the new Podfile
282
+ #
283
+ def self.from_yaml(yaml, path = nil)
284
+ hash = YAML.load(yaml)
285
+ from_hash(hash)
286
+ end
287
+
288
+ #-------------------------------------------------------------------------#
289
+
290
+ private
291
+
292
+ # @!group Private helpers
293
+
294
+ # @return [Hash] The hash which store the attributes of the Podfile.
295
+ #
296
+ attr_accessor :internal_hash
297
+
298
+ # Set a value in the internal hash of the Podfile for the given key.
299
+ #
300
+ # @param [String] key
301
+ # The key for which to store the value.
302
+ #
303
+ # @param [Object] value
304
+ # The value to store.
305
+ #
306
+ # @raise If the key is not recognized.
307
+ #
308
+ # @return [void]
309
+ #
310
+ def set_hash_value(key, value)
311
+ raise StandardError, "Unsupported hash key `#{key}`" unless HASH_KEYS.include?(key)
312
+ internal_hash[key] = value
313
+ end
314
+
315
+ # Returns the value for the given key in the internal hash of the Podfile.
316
+ #
317
+ # @param [String] key
318
+ # The key for which the value is needed.
319
+ #
320
+ # @raise If the key is not recognized.
321
+ #
322
+ # @return [Object] The value for the key.
323
+ #
324
+ def get_hash_value(key)
325
+ raise StandardError, "Unsupported hash key `#{key}`" unless HASH_KEYS.include?(key)
326
+ internal_hash[key]
327
+ end
328
+
329
+ # @return [TargetDefinition] The current target definition to which the DSL
330
+ # commands apply.
331
+ #
332
+ attr_accessor :current_target_definition
333
+
334
+ #-------------------------------------------------------------------------#
335
+
336
+ # @deprecated Deprecated in favour of the more succinct {#pod}. Remove for
337
+ # CocoaPods 1.0.
338
+ #
339
+ def dependency(name = nil, *requirements, &block)
340
+ warn "[DEPRECATED] `dependency' is deprecated (use `pod')"
341
+ pod(name, *requirements, &block)
342
+ end
343
+
344
+ end
345
+ end
@@ -0,0 +1,15 @@
1
+ module Pod
2
+
3
+ # A Requirement is a set of one or more version restrictions of a
4
+ # {Dependency}.
5
+ #
6
+ # It is based on the RubyGems class adapted to support CocoaPods specific
7
+ # information.
8
+ #
9
+ # @todo Move support about external sources and head information here from
10
+ # the Dependency class.
11
+ #
12
+ class Requirement < Pod::Vendor::Gem::Requirement
13
+ end
14
+
15
+ end
@@ -0,0 +1,183 @@
1
+ module Pod
2
+ class Source
3
+
4
+ # TODO: THIS CLASS IS A STUB
5
+
6
+ # Checks whether a podspec can be accepted by a source.
7
+ #
8
+ # This class can work on Travis but some checks are lost as the repo is
9
+ # already merged during the test.
10
+ #
11
+ class Validator
12
+
13
+ # @return [Source] the source where the podspec should be added.
14
+ #
15
+ attr_reader :source
16
+
17
+ # @param [Pathname] repo @see Source#repo.
18
+ #
19
+ def initialize(repo)
20
+ @source = Source.new(repo)
21
+ @errors = {}
22
+ @linter_results = {}
23
+ end
24
+
25
+ # @param [Array<Pathname>] spec_paths
26
+ # a list of path that should be checked for being accepted in
27
+ # specs repo.
28
+ #
29
+ # @return [Bool] whether to podspec can be accepted by the source.
30
+ #
31
+ def check(spec_paths)
32
+ spec_paths = [ spec_paths ] unless spec_paths.is_a?(Array)
33
+ @errors = {}
34
+ spec_paths.each do |path|
35
+ @spec_path = Pathname.new(path)
36
+ lint
37
+ next unless spec
38
+ # check_spec_path
39
+ check_spec_source_change
40
+ check_if_untagged_version_is_acceptable
41
+ check_commit_change_for_untagged_version
42
+ check_dependencies
43
+ end
44
+ errors.values.empty?
45
+ end
46
+
47
+ #-----------------------------------------------------------------------#
48
+
49
+ # @!group Validation results
50
+
51
+ public
52
+
53
+
54
+ # @return [Hash{Pathname=>Array<String>}] the errors generated by the
55
+ # validation. If there is any error the podspec cannot be
56
+ # accepted.
57
+ #
58
+ attr_reader :errors
59
+
60
+ # @return [Hash{Pathname=>Array<Specification::Linter::Result>}] the
61
+ # result generated by the linter for not approved specifications.
62
+ #
63
+ attr_reader :linter_results
64
+
65
+ #-----------------------------------------------------------------------#
66
+
67
+ # @!group Validation helpers
68
+
69
+ private
70
+
71
+ # @return [Pathname] The path of the current specification that should be
72
+ # checked.
73
+ #
74
+ attr_reader :spec_path
75
+
76
+ # @return [Specification] The current specification that should be
77
+ # checked.
78
+ #
79
+ # @note The specification is generated by the linter that catches any
80
+ # exception.
81
+ #
82
+ attr_reader :spec
83
+
84
+
85
+ #
86
+ #
87
+ def error(message)
88
+ @errors[spec_path] ||= []
89
+ @errors[spec_path] << message
90
+ end
91
+
92
+ #-----------------------------------------------------------------------#
93
+
94
+ # @!group Check steps
95
+
96
+ private
97
+
98
+ def lint
99
+ linter = Specification::Linter.new(spec_path)
100
+ if linter.lint
101
+ @spec = linter.spec
102
+ else
103
+ error 'Linter failed validation.'
104
+ end
105
+ end
106
+
107
+ # TODO: this check cannot be performed before a merge.
108
+ def check_spec_path
109
+ expected = "#{spec.name}/#{spec.version}/#{spec.name}.podspec"
110
+ relative_path = spec_path.relative_path_from(source.repo).to_s
111
+ unless relative_path == expected
112
+ error "Incorrect path, the path is `#{relative_path}` and should be `#{expected}`."
113
+ end
114
+ end
115
+
116
+ def check_spec_source_change
117
+ return unless spec
118
+ return unless reference_spec
119
+ keys = Spec::DSL::SOURCE_KEYS.keys
120
+ source = spec.source.values_at(*keys).compact.first
121
+ old_source = reference_spec.source.values_at(*keys).compact.first
122
+ unless source == old_source
123
+ error "Attempt to change the source of the specification. " \
124
+ "Source: `#{source}`. Previous: `#{old_source}`.\n " \
125
+ "Contact specs repos maintainers if the library changed location."
126
+ end
127
+ end
128
+
129
+ def check_if_untagged_version_is_acceptable
130
+ return unless spec
131
+ return if !spec.source[:git] || spec.source[:tag]
132
+
133
+ # Allow to fix a 0.0.1 podspec
134
+ if !related_specifications.any? { |s| s.version == '0.0.1' }
135
+ error "There is already versioned specifications so " \
136
+ "untagged versions cannot be added."
137
+ elsif spec.version != Version.new('0.0.1')
138
+ error "Untagged Git repositories should be versioned as 0.0.1"
139
+ end
140
+ end
141
+
142
+ # TODO: this cannot be tested on Travis with the current setup
143
+ def check_commit_change_for_untagged_version
144
+ return unless spec
145
+ return unless spec.version == Version.new('0.0.1')
146
+ ref_spec = related_specifications.find { |s| s.version != '0.0.1' }
147
+ return unless ref_spec
148
+ unless ref_spec.source[:commit] == spec.source[:commit]
149
+ error "Attempt to rewrite the commit of 0.0.1 version."
150
+ end
151
+ end
152
+
153
+ def check_dependencies
154
+ return unless spec
155
+ spec.external_dependencies(true).each do |dep|
156
+ set = source.search(dep)
157
+ unless set && set.specification
158
+ error "Unable to find a specification for the `#{dep}` dependency."
159
+ end
160
+ end
161
+ end
162
+
163
+ #-----------------------------------------------------------------------#
164
+
165
+ # @!group Source helpers
166
+
167
+ private
168
+
169
+ def related_specifications
170
+ versions = source.versions(spec.name)
171
+ return unless versions
172
+ specs = versions.sort.map { |v| source.specification(spec.name, v) }
173
+ specs.reject { |s| s.defined_in_file == spec_path }
174
+ end
175
+
176
+ def reference_spec
177
+ specs = related_specifications
178
+ specs.last if specs
179
+ end
180
+
181
+ end
182
+ end
183
+ end