cocoapods-square-stable 0.19.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1296 -0
  3. data/LICENSE +20 -0
  4. data/README.md +94 -0
  5. data/bin/pod +16 -0
  6. data/bin/sandbox-pod +120 -0
  7. data/lib/cocoapods.rb +77 -0
  8. data/lib/cocoapods/command.rb +116 -0
  9. data/lib/cocoapods/command/help.rb +23 -0
  10. data/lib/cocoapods/command/inter_process_communication.rb +178 -0
  11. data/lib/cocoapods/command/list.rb +77 -0
  12. data/lib/cocoapods/command/outdated.rb +56 -0
  13. data/lib/cocoapods/command/podfile_info.rb +91 -0
  14. data/lib/cocoapods/command/project.rb +88 -0
  15. data/lib/cocoapods/command/push.rb +172 -0
  16. data/lib/cocoapods/command/repo.rb +145 -0
  17. data/lib/cocoapods/command/search.rb +61 -0
  18. data/lib/cocoapods/command/setup.rb +134 -0
  19. data/lib/cocoapods/command/spec.rb +590 -0
  20. data/lib/cocoapods/config.rb +231 -0
  21. data/lib/cocoapods/downloader.rb +59 -0
  22. data/lib/cocoapods/executable.rb +118 -0
  23. data/lib/cocoapods/external_sources.rb +363 -0
  24. data/lib/cocoapods/file_list.rb +36 -0
  25. data/lib/cocoapods/gem_version.rb +7 -0
  26. data/lib/cocoapods/generator/acknowledgements.rb +107 -0
  27. data/lib/cocoapods/generator/acknowledgements/markdown.rb +40 -0
  28. data/lib/cocoapods/generator/acknowledgements/plist.rb +64 -0
  29. data/lib/cocoapods/generator/bridge_support.rb +22 -0
  30. data/lib/cocoapods/generator/copy_resources_script.rb +54 -0
  31. data/lib/cocoapods/generator/dummy_source.rb +22 -0
  32. data/lib/cocoapods/generator/prefix_header.rb +82 -0
  33. data/lib/cocoapods/generator/target_environment_header.rb +86 -0
  34. data/lib/cocoapods/generator/xcconfig.rb +185 -0
  35. data/lib/cocoapods/hooks/installer_representation.rb +134 -0
  36. data/lib/cocoapods/hooks/library_representation.rb +94 -0
  37. data/lib/cocoapods/hooks/pod_representation.rb +74 -0
  38. data/lib/cocoapods/installer.rb +571 -0
  39. data/lib/cocoapods/installer/analyzer.rb +559 -0
  40. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
  41. data/lib/cocoapods/installer/file_references_installer.rb +179 -0
  42. data/lib/cocoapods/installer/pod_source_installer.rb +248 -0
  43. data/lib/cocoapods/installer/target_installer.rb +379 -0
  44. data/lib/cocoapods/installer/user_project_integrator.rb +180 -0
  45. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +224 -0
  46. data/lib/cocoapods/library.rb +202 -0
  47. data/lib/cocoapods/open_uri.rb +24 -0
  48. data/lib/cocoapods/project.rb +209 -0
  49. data/lib/cocoapods/resolver.rb +212 -0
  50. data/lib/cocoapods/sandbox.rb +343 -0
  51. data/lib/cocoapods/sandbox/file_accessor.rb +217 -0
  52. data/lib/cocoapods/sandbox/headers_store.rb +96 -0
  53. data/lib/cocoapods/sandbox/path_list.rb +208 -0
  54. data/lib/cocoapods/sources_manager.rb +276 -0
  55. data/lib/cocoapods/user_interface.rb +304 -0
  56. data/lib/cocoapods/user_interface/error_report.rb +101 -0
  57. data/lib/cocoapods/validator.rb +350 -0
  58. metadata +238 -0
@@ -0,0 +1,209 @@
1
+ require 'xcodeproj'
2
+
3
+ module Pod
4
+
5
+ # The Pods project.
6
+ #
7
+ # Model class which provides helpers for working with the Pods project
8
+ # through the installation process.
9
+ #
10
+ class Project < Xcodeproj::Project
11
+
12
+ # @return [Pathname] the path of the xcodeproj file which stores the
13
+ # project.
14
+ #
15
+ attr_reader :path
16
+
17
+ # @param [Sandbox] sandbox @see #sandbox
18
+ #
19
+ def initialize(path = nil)
20
+ super(nil) # Recreate the project from scratch for now.
21
+ @path = path
22
+ @support_files_group = new_group('Targets Support Files')
23
+
24
+ @refs_by_absolute_path = {}
25
+ end
26
+
27
+ # @return [Pathname] the path of the xcodeproj file which stores the
28
+ # project.
29
+ #
30
+ attr_reader :path
31
+
32
+ # @return [Pathname] the directory where the project is stored.
33
+ #
34
+ def root
35
+ @root ||= path.dirname
36
+ end
37
+
38
+ # @return [Pathname] Returns the relative path from the project root.
39
+ #
40
+ # @param [Pathname] path
41
+ # The path that needs to be converted to the relative format.
42
+ #
43
+ # @note If the two absolute paths don't share the same root directory an
44
+ # extra `../` is added to the result of
45
+ # {Pathname#relative_path_from}.
46
+ #
47
+ # @example
48
+ #
49
+ # path = Pathname.new('/Users/dir')
50
+ # @sandbox.root #=> Pathname('/tmp/CocoaPods/Lint/Pods')
51
+ #
52
+ # @sandbox.relativize(path) #=> '../../../../Users/dir'
53
+ # @sandbox.relativize(path) #=> '../../../../../Users/dir'
54
+ #
55
+ def relativize(path)
56
+ unless path.absolute?
57
+ raise StandardError, "[Bug] Attempt to add relative path `#{path}` to the Pods project"
58
+ end
59
+
60
+ result = path.relative_path_from(root)
61
+ unless root.to_s.split('/')[1] == path.to_s.split('/')[1]
62
+ result = Pathname.new('../') + result
63
+ end
64
+ result
65
+ end
66
+
67
+ # @return [String] a string representation suited for debugging.
68
+ #
69
+ def inspect
70
+ "#<#{self.class}> path:#{path}"
71
+ end
72
+
73
+ #-------------------------------------------------------------------------#
74
+
75
+ public
76
+
77
+ # @!group Groups
78
+
79
+ # @return [PBXGroup] the group where the support files for the Pod
80
+ # libraries should be added.
81
+ #
82
+ attr_reader :support_files_group
83
+
84
+ # Returns the `Pods` group, creating it if needed.
85
+ #
86
+ # @return [PBXGroup] the group.
87
+ #
88
+ def pods
89
+ @pods ||= new_group('Pods')
90
+ end
91
+
92
+ # Returns the `Local Pods` group, creating it if needed. This group is used
93
+ # to contain locally sourced pods.
94
+ #
95
+ # @return [PBXGroup] the group.
96
+ #
97
+ def local_pods
98
+ @local_pods ||= new_group('Local Pods')
99
+ end
100
+
101
+ # Returns the `Local Pods` group, creating it if needed. This group is used
102
+ # to contain locally sourced pods.
103
+ #
104
+ # @return [PBXGroup] the group.
105
+ #
106
+ def resources
107
+ @resources ||= new_group('Resources')
108
+ end
109
+
110
+ # Adds a group as child to the `Pods` group namespacing subspecs.
111
+ #
112
+ # @param [String] spec_name
113
+ # The full name of the specification.
114
+ #
115
+ # @param [PBXGroup] root_group
116
+ # The group where to add the specification. Either `Pods` or `Local
117
+ # Pods`.
118
+ #
119
+ # @return [PBXGroup] the group for the spec with the given name.
120
+ #
121
+ def add_spec_group(spec_name, root_group)
122
+ current_group = root_group
123
+ group = nil
124
+ spec_name.split('/').each do |name|
125
+ group = current_group[name] || current_group.new_group(name)
126
+ current_group = group
127
+ end
128
+ group
129
+ end
130
+
131
+ #-------------------------------------------------------------------------#
132
+
133
+ public
134
+
135
+ # @!group File references
136
+
137
+ # Adds a file reference for each one of the given files in the specified
138
+ # group, namespaced by specification unless a file reference for the given
139
+ # path already exits.
140
+ #
141
+ # @note With this set-up different subspecs might not reference the same
142
+ # file (i.e. the first will win). Not sure thought if this is a
143
+ # limitation or a feature.
144
+ #
145
+ # @param [Array<Pathname,String>] paths
146
+ # The files for which the file reference is needed.
147
+ #
148
+ # @param [String] spec_name
149
+ # The full name of the specification.
150
+ #
151
+ # @param [PBXGroup] parent_group
152
+ # The group where the file references should be added.
153
+ #
154
+ # @return [void]
155
+ #
156
+ def add_file_references(absolute_path, spec_name, parent_group)
157
+ group = add_spec_group(spec_name, parent_group)
158
+ absolute_path.each do |file|
159
+ existing = file_reference(file)
160
+ unless existing
161
+ file = Pathname.new(file)
162
+ ref = group.new_file(relativize(file))
163
+ @refs_by_absolute_path[file] = ref
164
+ end
165
+ end
166
+ end
167
+
168
+ # Returns the file reference for the given absolute file path.
169
+ #
170
+ # @param [Pathname,String] absolute_path
171
+ # The absolute path of the file whose reference is needed.
172
+ #
173
+ # @return [PBXFileReference] The file reference.
174
+ # @return [Nil] If no file reference could be found.
175
+ #
176
+ def file_reference(absolute_path)
177
+ absolute_path = Pathname.new(absolute_path)
178
+ refs_by_absolute_path[absolute_path]
179
+ end
180
+
181
+ # Adds a file reference to the podfile.
182
+ #
183
+ # @param [Pathname,String] podfile_path
184
+ # the path of the podfile
185
+ #
186
+ # @return [PBXFileReference] the file reference.
187
+ #
188
+ def add_podfile(podfile_path)
189
+ podfile_path = Pathname.new(podfile_path)
190
+ podfile_ref = new_file(relativize(podfile_path))
191
+ podfile_ref.xc_language_specification_identifier = 'xcode.lang.ruby'
192
+ podfile_ref
193
+ end
194
+
195
+ #-------------------------------------------------------------------------#
196
+
197
+ private
198
+
199
+ # @!group Private helpers
200
+
201
+ # @return [Hash{Pathname => PBXFileReference}] The file references grouped
202
+ # by absolute path.
203
+ #
204
+ attr_reader :refs_by_absolute_path
205
+
206
+ #-------------------------------------------------------------------------#
207
+
208
+ end
209
+ end
@@ -0,0 +1,212 @@
1
+ module Pod
2
+
3
+ # The resolver is responsible of generating a list of specifications grouped
4
+ # by target for a given Podfile.
5
+ #
6
+ # @todo Its current implementation is naive, in the sense that it can't do full
7
+ # automatic resolves like Bundler:
8
+ # [how-does-bundler-bundle](http://patshaughnessy.net/2011/9/24/how-does-bundler-bundle)
9
+ #
10
+ # @todo Another limitation is that the order of the dependencies matter. The
11
+ # current implementation could create issues, for example, if a
12
+ # specification is loaded for a target definition and later for another
13
+ # target is set in head mode. The first specification will not be in head
14
+ # mode.
15
+ #
16
+ #
17
+ class Resolver
18
+
19
+ # @return [Sandbox] the Sandbox used by the resolver to find external
20
+ # dependencies.
21
+ #
22
+ attr_reader :sandbox
23
+
24
+ # @return [Podfile] the Podfile used by the resolver.
25
+ #
26
+ attr_reader :podfile
27
+
28
+ # @return [Array<Dependency>] the list of dependencies locked to a specific
29
+ # version.
30
+ #
31
+ attr_reader :locked_dependencies
32
+
33
+ # @param [Sandbox] sandbox @see sandbox
34
+ # @param [Podfile] podfile @see podfile
35
+ # @param [Array<Dependency>] locked_dependencies @see locked_dependencies
36
+ #
37
+ def initialize(sandbox, podfile, locked_dependencies = [])
38
+ @sandbox = sandbox
39
+ @podfile = podfile
40
+ @locked_dependencies = locked_dependencies
41
+ end
42
+
43
+ #-------------------------------------------------------------------------#
44
+
45
+ public
46
+
47
+ # @!group Resolution
48
+
49
+ # Identifies the specifications that should be installed.
50
+ #
51
+ # @return [Hash{TargetDefinition => Array<Specification>}] specs_by_target
52
+ # the specifications that need to be installed grouped by target
53
+ # definition.
54
+ #
55
+ def resolve
56
+ @cached_sources = SourcesManager.aggregate
57
+ @cached_sets = {}
58
+ @cached_specs = {}
59
+ @specs_by_target = {}
60
+
61
+ target_definitions = podfile.target_definition_list
62
+ target_definitions.each do |target|
63
+ UI.section "Resolving dependencies for target `#{target.name}' (#{target.platform})" do
64
+ @loaded_specs = []
65
+ find_dependency_specs(podfile, target.dependencies, target)
66
+ specs = cached_specs.values_at(*@loaded_specs).sort_by(&:name)
67
+ specs_by_target[target] = specs
68
+ end
69
+ end
70
+
71
+ cached_specs.values.sort_by(&:name)
72
+ specs_by_target
73
+ end
74
+
75
+ # @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
76
+ # returns the resolved specifications grouped by target.
77
+ #
78
+ # @note The returned specifications can be subspecs.
79
+ #
80
+ attr_reader :specs_by_target
81
+
82
+ #-------------------------------------------------------------------------#
83
+
84
+ private
85
+
86
+ # !@ Resolution context
87
+
88
+ # @return [Source::Aggregate] A cache of the sources needed to find the
89
+ # podspecs.
90
+ #
91
+ # @note The sources are cached because frequently accessed by the
92
+ # resolver and loading them requires disk activity.
93
+ #
94
+ attr_accessor :cached_sources
95
+
96
+ # @return [Hash<String => Set>] A cache that keeps tracks of the sets
97
+ # loaded by the resolution process.
98
+ #
99
+ # @note Sets store the resolved dependencies and return the highest
100
+ # available specification found in the sources. This is done
101
+ # globally and not per target definition because there can be just
102
+ # one Pod installation, so different version of the same Pods for
103
+ # target definitions are not allowed.
104
+ #
105
+ attr_accessor :cached_sets
106
+
107
+ # @return [Hash<String => Specification>] The loaded specifications grouped
108
+ # by name.
109
+ #
110
+ attr_accessor :cached_specs
111
+
112
+ #-------------------------------------------------------------------------#
113
+
114
+ private
115
+
116
+ # @!group Private helpers
117
+
118
+ # Resolves recursively the dependencies of a specification and stores them
119
+ # in the @cached_specs ivar.
120
+ #
121
+ # @param [Podfile, Specification, #to_s] dependent_spec
122
+ # the specification whose dependencies are being resolved. Used
123
+ # only for UI purposes.
124
+ #
125
+ # @param [Array<Dependency>] dependencies
126
+ # the dependencies of the specification.
127
+ #
128
+ # @param [TargetDefinition] target_definition
129
+ # the target definition that owns the specification.
130
+ #
131
+ # @note If there is a locked dependency with the same name of a
132
+ # given dependency the locked one is used in place of the
133
+ # dependency of the specification. In this way it is possible to
134
+ # prevent the update of the version of installed pods not changed
135
+ # in the Podfile.
136
+ #
137
+ # @note The recursive process checks if a dependency has already been
138
+ # loaded to prevent an infinite loop.
139
+ #
140
+ # @note The set class merges all (of all the target definitions) the
141
+ # dependencies and thus it keeps track of whether it is in head
142
+ # mode or from an external source because {Dependency#merge}
143
+ # preserves this information.
144
+ #
145
+ # @return [void]
146
+ #
147
+ def find_dependency_specs(dependent_spec, dependencies, target_definition)
148
+ dependencies.each do |dependency|
149
+ locked_dep = locked_dependencies.find { |ld| ld.name == dependency.name }
150
+ dependency = locked_dep if locked_dep
151
+
152
+ UI.message("- #{dependency}", '', 2) do
153
+ set = find_cached_set(dependency)
154
+ set.required_by(dependency, dependent_spec.to_s)
155
+
156
+ unless @loaded_specs.include?(dependency.name)
157
+ spec = set.specification.subspec_by_name(dependency.name)
158
+ @loaded_specs << spec.name
159
+ cached_specs[spec.name] = spec
160
+ validate_platform(spec, target_definition)
161
+ spec.version.head = dependency.head?
162
+
163
+ spec_dependencies = spec.all_dependencies(target_definition.platform)
164
+ find_dependency_specs(spec, spec_dependencies, target_definition)
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ # Loads or returns a previously initialized for the Pod of the given
171
+ # dependency.
172
+ #
173
+ # @param [Dependency] dependency
174
+ # the dependency for which the set is needed.
175
+ #
176
+ # @return [Set] the cached set for a given dependency.
177
+ #
178
+ def find_cached_set(dependency)
179
+ name = dependency.root_name
180
+ unless cached_sets[name]
181
+ if dependency.external_source
182
+ spec = sandbox.specification(dependency.root_name)
183
+ unless spec
184
+ raise StandardError, "[Bug] Unable to find the specification for `#{dependency}`."
185
+ end
186
+ set = Specification::Set::External.new(spec)
187
+ else
188
+ set = cached_sources.search(dependency)
189
+ end
190
+ cached_sets[name] = set
191
+ unless set
192
+ raise Informative, "Unable to find a specification for `#{dependency}`."
193
+ end
194
+ end
195
+ cached_sets[name]
196
+ end
197
+
198
+ # Ensures that a specification is compatible with the platform of a target.
199
+ #
200
+ # @raise If the specification is not supported by the target.
201
+ #
202
+ # @return [void]
203
+ #
204
+ def validate_platform(spec, target)
205
+ unless spec.available_platforms.any? { |p| target.platform.supports?(p) }
206
+ raise Informative, "The platform of the target `#{target.name}` " \
207
+ "(#{target.platform}) is not compatible with `#{spec}` which has " \
208
+ "a minimum requirement of #{spec.available_platforms.join(' - ')}."
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,343 @@
1
+ require 'fileutils'
2
+
3
+ module Pod
4
+
5
+ # The sandbox provides support for the directory that CocoaPods uses for an
6
+ # installation. In this directory the Pods projects, the support files and
7
+ # the sources of the Pods are stored.
8
+ #
9
+ # CocoaPods assumes to have control of the sandbox.
10
+ #
11
+ # Once completed the sandbox will have the following file structure:
12
+ #
13
+ # Pods
14
+ # |
15
+ # +-- User
16
+ # | +-- [Target Name]-configuration.h
17
+ # | +-- Specs
18
+ # | +-- Scripts
19
+ # |
20
+ # +-- Generated
21
+ # +-- Headers
22
+ # | +-- Private
23
+ # | | +-- [Pod Name]
24
+ # | +-- Public
25
+ # | +-- [Pod Name]
26
+ # |
27
+ # +-- Sources
28
+ # | +-- [Pod Name]
29
+ # |
30
+ # +-- Specs
31
+ # | +-- External Sources
32
+ # | +-- Normal Sources
33
+ # |
34
+ # +-- Target Support Files
35
+ # | +-- [Target Name]
36
+ # | +-- Pods-acknowledgements.markdown
37
+ # | +-- Pods-acknowledgements.plist
38
+ # | +-- Pods-dummy.m
39
+ # | +-- Pods-prefix.pch
40
+ # | +-- Pods.xcconfig
41
+ # |
42
+ # +-- Manifest.lock
43
+ # |
44
+ # +-- Pods.xcodeproj
45
+ #
46
+ # See #833
47
+ #
48
+ class Sandbox
49
+
50
+ autoload :FileAccessor, 'cocoapods/sandbox/file_accessor'
51
+ autoload :HeadersStore, 'cocoapods/sandbox/headers_store'
52
+ autoload :PathList, 'cocoapods/sandbox/path_list'
53
+
54
+ # @return [Pathname] the root of the sandbox.
55
+ #
56
+ attr_reader :root
57
+
58
+ # @return [HeadersStore] the header directory for the Pods libraries.
59
+ #
60
+ attr_reader :build_headers
61
+
62
+ # @return [HeadersStore] the header directory for the user targets.
63
+ #
64
+ attr_reader :public_headers
65
+
66
+ # @param [String, Pathname] root @see root
67
+ #
68
+ def initialize(root)
69
+ @root = Pathname.new(root)
70
+ @build_headers = HeadersStore.new(self, "BuildHeaders")
71
+ @public_headers = HeadersStore.new(self, "Headers")
72
+ @predownloaded_pods = []
73
+ @checkout_sources = {}
74
+ @local_pods = {}
75
+ FileUtils.mkdir_p(@root)
76
+ end
77
+
78
+ # @return [Lockfile] the manifest which contains the information about the
79
+ # installed pods.
80
+ #
81
+ def manifest
82
+ Lockfile.from_file(manifest_path) if manifest_path.exist?
83
+ end
84
+
85
+ # @return [Project] the Pods project.
86
+ #
87
+ attr_accessor :project
88
+
89
+ # Removes the sandbox.
90
+ #
91
+ # @return [void]
92
+ #
93
+ def implode
94
+ root.rmtree
95
+ end
96
+
97
+ #
98
+ #
99
+ #
100
+ def clean_pod(name)
101
+ root_name = Specification.root_name(name)
102
+ unless local?(root_name)
103
+ path = pod_dir(name)
104
+ path.rmtree if path.exist?
105
+ end
106
+ podspe_path = specification_path(name)
107
+ podspe_path.rmtree if podspe_path
108
+ end
109
+
110
+ # @return [String] a string representation suitable for debugging.
111
+ #
112
+ def inspect
113
+ "#<#{self.class}> with root #{root}"
114
+ end
115
+
116
+ #-------------------------------------------------------------------------#
117
+
118
+ public
119
+
120
+ # @!group Paths
121
+
122
+ # @return [Pathname] the path of the manifest.
123
+ #
124
+ def manifest_path
125
+ root + "Manifest.lock"
126
+ end
127
+
128
+ # @return [Pathname] the path of the Pods project.
129
+ #
130
+ def project_path
131
+ root + "Pods.xcodeproj"
132
+ end
133
+
134
+ # Returns the path for the directory where to store the support files of
135
+ # a target.
136
+ #
137
+ # @param [String] name
138
+ # The name of the target.
139
+ #
140
+ # @return [Pathname] the path of the support files.
141
+ #
142
+ def library_support_files_dir(name)
143
+ # root + "Target Support Files/#{name}"
144
+ root
145
+ end
146
+
147
+ # Returns the path where the Pod with the given name is stored, taking into
148
+ # account whether the Pod is locally sourced.
149
+ #
150
+ # @param [String] name
151
+ # The name of the Pod.
152
+ #
153
+ # @return [Pathname] the path of the Pod.
154
+ #
155
+ def pod_dir(name)
156
+ root_name = Specification.root_name(name)
157
+ if local?(root_name)
158
+ Pathname.new(local_pods[root_name])
159
+ else
160
+ # root + "Sources/#{name}"
161
+ root + root_name
162
+ end
163
+ end
164
+
165
+ # @return [Pathname] the directory where to store the documentation.
166
+ #
167
+ def documentation_dir
168
+ root + 'Documentation'
169
+ end
170
+
171
+ #-------------------------------------------------------------------------#
172
+
173
+ public
174
+
175
+ # @!group Specification store
176
+
177
+ # Returns the specification for the Pod with the given name.
178
+ #
179
+ # @param [String] name
180
+ # the name of the Pod for which the specification is requested.
181
+ #
182
+ # @return [Specification] the specification if the file is found.
183
+ #
184
+ def specification(name)
185
+ if file = specification_path(name)
186
+ Specification.from_file(file)
187
+ end
188
+ end
189
+
190
+ # @return [Pathname] the path for the directory where to store the
191
+ # specifications.
192
+ #
193
+ # @todo Migrate old installations and store the for all the pods.
194
+ # Two folders should be created `External Sources` and `Podspecs`.
195
+ #
196
+ def specifications_dir(external_source = false)
197
+ # root + "Specifications"
198
+ root + "Local Podspecs"
199
+ end
200
+
201
+ # Returns the path of the specification for the Pod with the
202
+ # given name, if one is stored.
203
+ #
204
+ # @param [String] name
205
+ # the name of the Pod for which the podspec file is requested.
206
+ #
207
+ # @return [Pathname] the path or nil.
208
+ # @return [Nil] if the podspec is not stored.
209
+ #
210
+ def specification_path(name)
211
+ path = specifications_dir + "#{name}.podspec"
212
+ path.exist? ? path : nil
213
+ end
214
+
215
+ # Stores a specification in the `Local Podspecs` folder.
216
+ #
217
+ # @param [Sandbox] sandbox
218
+ # the sandbox where the podspec should be stored.
219
+ #
220
+ # @param [String, Pathname] podspec
221
+ # The contents of the specification (String) or the path to a
222
+ # podspec file (Pathname).
223
+ #
224
+ # @todo Store all the specifications (including those not originating
225
+ # from external sources) so users can check them.
226
+ #
227
+ def store_podspec(name, podspec, external_source = false)
228
+ output_path = specifications_dir(external_source) + "#{name}.podspec"
229
+ output_path.dirname.mkpath
230
+ if podspec.is_a?(String)
231
+ output_path.open('w') { |f| f.puts(podspec) }
232
+ else
233
+ unless podspec.exist?
234
+ raise Informative, "No podspec found for `#{name}` in #{podspec}"
235
+ end
236
+ FileUtils.copy(podspec, output_path)
237
+ end
238
+ spec = Specification.from_file(output_path)
239
+ unless spec.name == name
240
+ raise Informative, "The name of the given podspec `#{spec.name}` doesn't match the expected one `#{name}`"
241
+ end
242
+ end
243
+
244
+ #-------------------------------------------------------------------------#
245
+
246
+ public
247
+
248
+ # @!group Pods information
249
+
250
+ # Marks a Pod as pre-downloaded
251
+ #
252
+ # @param [String] name
253
+ # The name of the Pod.
254
+ #
255
+ # @return [void]
256
+ #
257
+ def store_pre_downloaded_pod(name)
258
+ root_name = Specification.root_name(name)
259
+ predownloaded_pods << root_name
260
+ end
261
+
262
+ # @return [Array<String>] The names of the pods that have been
263
+ # pre-downloaded from an external source.
264
+ #
265
+ attr_reader :predownloaded_pods
266
+
267
+ # Checks if a Pod has been pre-downloaded by the resolver in order to fetch
268
+ # the podspec.
269
+ #
270
+ # @param [String] name
271
+ # The name of the Pod.
272
+ #
273
+ # @return [Bool] Whether the Pod has been pre-downloaded.
274
+ #
275
+ def predownloaded?(name)
276
+ root_name = Specification.root_name(name)
277
+ predownloaded_pods.include?(root_name)
278
+ end
279
+
280
+ #--------------------------------------#
281
+
282
+ # Stores the local path of a Pod.
283
+ #
284
+ # @param [String] name
285
+ # The name of the Pod.
286
+ #
287
+ # @param [Hash] source
288
+ # The hash which contains the options as returned by the
289
+ # downloader.
290
+ #
291
+ # @return [void]
292
+ #
293
+ def store_checkout_source(name, source)
294
+ root_name = Specification.root_name(name)
295
+ checkout_sources[root_name] = source
296
+ end
297
+
298
+ # @return [Hash{String=>Hash}] The options necessary to recreate the exact
299
+ # checkout of a given Pod grouped by its name.
300
+ #
301
+ attr_reader :checkout_sources
302
+
303
+ #--------------------------------------#
304
+
305
+ # Stores the local path of a Pod.
306
+ #
307
+ # @param [String] name
308
+ # The name of the Pod.
309
+ #
310
+ # @param [#to_s] path
311
+ # The local path where the Pod is stored.
312
+ #
313
+ # @return [void]
314
+ #
315
+ def store_local_path(name, path)
316
+ root_name = Specification.root_name(name)
317
+ local_pods[root_name] = path.to_s
318
+ end
319
+
320
+ # @return [Hash{String=>String}] The path of the Pods with a local source
321
+ # grouped by their name.
322
+ #
323
+ # @todo Rename (e.g. `pods_with_local_path`)
324
+ #
325
+ attr_reader :local_pods
326
+
327
+ # Checks if a Pod is locally sourced?
328
+ #
329
+ # @param [String] name
330
+ # The name of the Pod.
331
+ #
332
+ # @return [Bool] Whether the Pod is locally sourced.
333
+ #
334
+ def local?(name)
335
+ root_name = Specification.root_name(name)
336
+ !local_pods[root_name].nil?
337
+ end
338
+
339
+ #-------------------------------------------------------------------------#
340
+
341
+ end
342
+ end
343
+