cocoapods 0.16.4 → 0.17.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +108 -0
  3. data/README.md +3 -3
  4. data/bin/pod +1 -1
  5. data/lib/cocoapods.rb +31 -31
  6. data/lib/cocoapods/command.rb +62 -107
  7. data/lib/cocoapods/command/inter_process_communication.rb +103 -0
  8. data/lib/cocoapods/command/list.rb +45 -44
  9. data/lib/cocoapods/command/outdated.rb +28 -25
  10. data/lib/cocoapods/command/project.rb +90 -0
  11. data/lib/cocoapods/command/push.rb +50 -32
  12. data/lib/cocoapods/command/repo.rb +125 -155
  13. data/lib/cocoapods/command/search.rb +23 -12
  14. data/lib/cocoapods/command/setup.rb +103 -64
  15. data/lib/cocoapods/command/spec.rb +329 -90
  16. data/lib/cocoapods/config.rb +197 -44
  17. data/lib/cocoapods/downloader.rb +47 -34
  18. data/lib/cocoapods/executable.rb +98 -41
  19. data/lib/cocoapods/external_sources.rb +325 -0
  20. data/lib/cocoapods/file_list.rb +8 -1
  21. data/lib/cocoapods/gem_version.rb +7 -0
  22. data/lib/cocoapods/generator/acknowledgements.rb +71 -7
  23. data/lib/cocoapods/generator/acknowledgements/markdown.rb +10 -9
  24. data/lib/cocoapods/generator/acknowledgements/plist.rb +9 -8
  25. data/lib/cocoapods/generator/copy_resources_script.rb +2 -2
  26. data/lib/cocoapods/generator/documentation.rb +153 -37
  27. data/lib/cocoapods/generator/prefix_header.rb +82 -0
  28. data/lib/cocoapods/generator/target_header.rb +58 -0
  29. data/lib/cocoapods/generator/xcconfig.rb +130 -0
  30. data/lib/cocoapods/hooks/installer_representation.rb +123 -0
  31. data/lib/cocoapods/hooks/library_representation.rb +79 -0
  32. data/lib/cocoapods/hooks/pod_representation.rb +74 -0
  33. data/lib/cocoapods/installer.rb +398 -147
  34. data/lib/cocoapods/installer/analyzer.rb +556 -0
  35. data/lib/cocoapods/installer/analyzer/sandbox_analyzer.rb +253 -0
  36. data/lib/cocoapods/installer/file_references_installer.rb +179 -0
  37. data/lib/cocoapods/installer/pod_source_installer.rb +289 -0
  38. data/lib/cocoapods/installer/target_installer.rb +307 -112
  39. data/lib/cocoapods/installer/user_project_integrator.rb +140 -176
  40. data/lib/cocoapods/installer/user_project_integrator/target_integrator.rb +193 -0
  41. data/lib/cocoapods/library.rb +195 -0
  42. data/lib/cocoapods/open_uri.rb +16 -14
  43. data/lib/cocoapods/project.rb +175 -52
  44. data/lib/cocoapods/resolver.rb +151 -164
  45. data/lib/cocoapods/sandbox.rb +276 -54
  46. data/lib/cocoapods/sandbox/file_accessor.rb +210 -0
  47. data/lib/cocoapods/sandbox/headers_store.rb +96 -0
  48. data/lib/cocoapods/sandbox/path_list.rb +178 -0
  49. data/lib/cocoapods/sources_manager.rb +218 -0
  50. data/lib/cocoapods/user_interface.rb +82 -18
  51. data/lib/cocoapods/{command → user_interface}/error_report.rb +5 -5
  52. data/lib/cocoapods/validator.rb +379 -0
  53. metadata +74 -55
  54. data/lib/cocoapods/command/install.rb +0 -55
  55. data/lib/cocoapods/command/linter.rb +0 -317
  56. data/lib/cocoapods/command/update.rb +0 -25
  57. data/lib/cocoapods/dependency.rb +0 -285
  58. data/lib/cocoapods/downloader/git.rb +0 -276
  59. data/lib/cocoapods/downloader/http.rb +0 -99
  60. data/lib/cocoapods/downloader/mercurial.rb +0 -26
  61. data/lib/cocoapods/downloader/subversion.rb +0 -42
  62. data/lib/cocoapods/local_pod.rb +0 -620
  63. data/lib/cocoapods/lockfile.rb +0 -274
  64. data/lib/cocoapods/platform.rb +0 -127
  65. data/lib/cocoapods/podfile.rb +0 -551
  66. data/lib/cocoapods/source.rb +0 -223
  67. data/lib/cocoapods/specification.rb +0 -579
  68. data/lib/cocoapods/specification/set.rb +0 -175
  69. data/lib/cocoapods/specification/statistics.rb +0 -112
  70. data/lib/cocoapods/user_interface/ui_pod.rb +0 -130
  71. data/lib/cocoapods/version.rb +0 -26
@@ -1,221 +1,208 @@
1
- require 'colored'
2
-
3
1
  module Pod
4
- class Resolver
5
- include Config::Mixin
6
-
7
- # @return [Bool] Whether the resolver should find the pods to install or
8
- # the pods to update.
9
- #
10
- attr_accessor :update_mode
11
-
12
- # @return [Bool] Whether the resolver should update the external specs
13
- # in the resolution process.
14
- #
15
- attr_accessor :update_external_specs
16
-
17
- # @return [Podfile] The Podfile used by the resolver.
18
- #
19
- attr_reader :podfile
20
2
 
21
- # @return [Lockfile] The Lockfile used by the resolver.
22
- #
23
- attr_reader :lockfile
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
24
18
 
25
- # @return [Sandbox] The Sandbox used by the resolver to find external
26
- # dependencies.
19
+ # @return [Sandbox] the Sandbox used by the resolver to find external
20
+ # dependencies.
27
21
  #
28
22
  attr_reader :sandbox
29
23
 
30
- # @return [Array<Strings>] The name of the pods that have an
31
- # external source.
24
+ # @return [Podfile] the Podfile used by the resolver.
32
25
  #
33
- attr_reader :pods_from_external_sources
26
+ attr_reader :podfile
34
27
 
35
- # @return [Array<Set>] A cache of the sets used to resolve the dependencies.
28
+ # @return [Array<Dependency>] the list of dependencies locked to a specific
29
+ # version.
36
30
  #
37
- attr_reader :cached_sets
31
+ attr_reader :locked_dependencies
38
32
 
39
- # @return [Source::Aggregate] A cache of the sources needed to find the
40
- # podspecs.
33
+ # @param [Sandbox] sandbox @see sandbox
34
+ # @param [Podfile] podfile @see podfile
35
+ # @param [Array<Dependency>] locked_dependencies @see locked_dependencies
41
36
  #
42
- attr_reader :cached_sources
37
+ def initialize(sandbox, podfile, locked_dependencies = [])
38
+ @sandbox = sandbox
39
+ @podfile = podfile
40
+ @locked_dependencies = locked_dependencies
41
+ end
43
42
 
44
- # @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
45
- # Returns the resolved specifications grouped by target.
46
- #
47
- attr_reader :specs_by_target
43
+ #-------------------------------------------------------------------------#
48
44
 
49
- def initialize(podfile, lockfile, sandbox)
50
- @podfile = podfile
51
- @lockfile = lockfile
52
- @sandbox = sandbox
53
- @update_external_specs = true
45
+ public
54
46
 
55
- @cached_sets = {}
56
- @cached_sources = Source::Aggregate.new
57
- end
47
+ # @!group Resolution
58
48
 
59
- # Identifies the specifications that should be installed according whether
60
- # the resolver is in update mode or not.
49
+ # Identifies the specifications that should be installed.
61
50
  #
62
- # @return [Hash{Podfile::TargetDefinition => Array<Specification>}] specs_by_target
51
+ # @return [Hash{TargetDefinition => Array<Specification>}] specs_by_target
52
+ # the specifications that need to be installed grouped by target
53
+ # definition.
63
54
  #
64
55
  def resolve
65
- @cached_specs = {}
56
+ @cached_sources = SourcesManager.aggregate
57
+ @cached_sets = {}
58
+ @cached_specs = {}
66
59
  @specs_by_target = {}
67
- @pods_from_external_sources = []
68
- @pods_to_lock = []
69
-
70
- if @lockfile
71
- @pods_by_state = @lockfile.detect_changes_with_podfile(podfile)
72
- UI.section "Finding added, modified or removed dependencies:" do
73
- marks = {:added => "A".green, :changed => "M".yellow, :removed => "R".red, :unchanged => "-" }
74
- @pods_by_state.each do |symbol, pod_names|
75
- pod_names.each do |pod_name|
76
- UI.message("#{marks[symbol]} #{pod_name}", '',2)
77
- end
78
- end
79
- end if config.verbose?
80
- @pods_to_lock = (lockfile.pods_names - @pods_by_state[:added] - @pods_by_state[:changed] - @pods_by_state[:removed]).uniq
81
- end
82
-
83
- unless config.skip_repo_update?
84
- UI.section 'Updating spec repositories' do
85
- Command::Repo.new(Command::ARGV.new(["update"])).run
86
- end if !@lockfile || !(@pods_by_state[:added] + @pods_by_state[:changed]).empty? || update_mode
87
- end
88
60
 
89
- @podfile.target_definitions.values.each do |target_definition|
90
- UI.section "Resolving dependencies for target `#{target_definition.name}' (#{target_definition.platform})" do
61
+ target_definitions = podfile.target_definitions.values.sort_by{ |td| td.name.to_s }
62
+ target_definitions.each do |target|
63
+ UI.section "Resolving dependencies for target `#{target.name}' (#{target.platform})" do
91
64
  @loaded_specs = []
92
- find_dependency_specs(@podfile, target_definition.dependencies, target_definition)
93
- @specs_by_target[target_definition] = @cached_specs.values_at(*@loaded_specs).sort_by(&:name)
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
94
68
  end
95
69
  end
96
70
 
97
- @cached_specs.values.sort_by(&:name)
98
- @specs_by_target
71
+ cached_specs.values.sort_by(&:name)
72
+ specs_by_target
99
73
  end
100
74
 
101
- # @return [Array<Specification>] The specifications loaded by the resolver.
75
+ # @return [Hash{Podfile::TargetDefinition => Array<Specification>}]
76
+ # returns the resolved specifications grouped by target.
102
77
  #
103
- def specs
104
- @cached_specs.values.uniq
105
- end
78
+ # @note The returned specifications can be subspecs.
79
+ #
80
+ attr_reader :specs_by_target
106
81
 
107
- # @return [Bool] Whether a pod should be installed/reinstalled.
82
+ #-------------------------------------------------------------------------#
83
+
84
+ private
85
+
86
+ # !@ Resolution context
87
+
88
+ # @return [Source::Aggregate] A cache of the sources needed to find the
89
+ # podspecs.
108
90
  #
109
- def should_install?(name)
110
- pods_to_install.include? name
111
- end
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
112
95
 
113
- # @return [Array<Strings>] The name of the pods that should be
114
- # installed/reinstalled.
115
- #
116
- def pods_to_install
117
- unless @pods_to_install
118
- if lockfile
119
- @pods_to_install = specs.select do |spec|
120
- spec.version != lockfile.pods_versions[spec.pod_name]
121
- end.map(&:name)
122
- if update_mode
123
- @pods_to_install += specs.select do |spec|
124
- spec.version.head? || pods_from_external_sources.include?(spec.pod_name)
125
- end.map(&:name)
126
- end
127
- @pods_to_install += @pods_by_state[:added] + @pods_by_state[:changed]
128
- else
129
- @pods_to_install = specs.map(&:name)
130
- end
131
- end
132
- @pods_to_install
133
- end
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
134
106
 
135
- # @return [Array<Strings>] The name of the pods that were installed
136
- # but don't have any dependency anymore. The name of the Pods are
137
- # stripped from subspecs.
107
+ # @return [Hash<String => Specification>] The loaded specifications grouped
108
+ # by name.
138
109
  #
139
- def removed_pods
140
- return [] unless lockfile
141
- unless @removed_pods
142
- previously_installed = lockfile.pods_names.map { |pod_name| pod_name.split('/').first }
143
- installed = specs.map { |spec| spec.name.split('/').first }
144
- @removed_pods = previously_installed - installed
145
- end
146
- @removed_pods
147
- end
110
+ attr_accessor :cached_specs
111
+
112
+ #-------------------------------------------------------------------------#
148
113
 
149
114
  private
150
115
 
151
- # @return [Set] The cached set for a given dependency.
152
- #
153
- def find_cached_set(dependency, platform)
154
- set_name = dependency.name.split('/').first
155
- @cached_sets[set_name] ||= begin
156
- if dependency.specification
157
- Specification::Set::External.new(dependency.specification)
158
- elsif external_source = dependency.external_source
159
- if update_mode && update_external_specs
160
- # Always update external sources in update mode.
161
- specification = external_source.specification_from_external(@sandbox, platform)
162
- else
163
- # Don't update external sources in install mode if not needed.
164
- specification = external_source.specification_from_sandbox(@sandbox, platform)
165
- end
166
- set = Specification::Set::External.new(specification)
167
- if dependency.subspec_dependency?
168
- @cached_sets[dependency.top_level_spec_name] ||= set
169
- end
170
- set
171
- else
172
- @cached_sources.search(dependency)
173
- end
174
- end
175
- end
116
+ # @!group Private helpers
176
117
 
177
- # Resolves the dependencies of a specification and stores them in @cached_specs
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.
178
127
  #
179
- # @param [Specification] dependent_specification
180
- # @param [Array<Dependency>] dependencies
181
- # @param [TargetDefinition] target_definition
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.
182
144
  #
183
145
  # @return [void]
184
146
  #
185
- def find_dependency_specs(dependent_specification, dependencies, target_definition)
147
+ def find_dependency_specs(dependent_spec, dependencies, target_definition)
186
148
  dependencies.each do |dependency|
187
- # Replace the dependency with a more specific one if the pod is already installed.
188
- if !update_mode && @pods_to_lock.include?(dependency.name)
189
- dependency = lockfile.dependency_for_installed_pod_named(dependency.name)
190
- end
149
+ locked_dep = locked_dependencies.find { |ld| ld.name == dependency.name }
150
+ dependency = locked_dep if locked_dep
151
+
191
152
  UI.message("- #{dependency}", '', 2) do
192
- set = find_cached_set(dependency, target_definition.platform)
193
- set.required_by(dependency, dependent_specification.to_s)
153
+ set = find_cached_set(dependency)
154
+ set.required_by(dependency, dependent_spec.to_s)
194
155
 
195
- # Ensure we don't resolve the same spec twice for one target
196
156
  unless @loaded_specs.include?(dependency.name)
197
- spec = set.specification_by_name(dependency.name)
198
- @pods_from_external_sources << spec.pod_name if dependency.external?
157
+ spec = set.specification.subspec_by_name(dependency.name)
199
158
  @loaded_specs << spec.name
200
- @cached_specs[spec.name] = spec
201
- # Configure the specification
202
- spec.activate_platform(target_definition.platform)
159
+ cached_specs[spec.name] = spec
160
+ validate_platform(spec, target_definition)
203
161
  spec.version.head = dependency.head?
204
- # And recursively load the dependencies of the spec.
205
- find_dependency_specs(spec, spec.dependencies, target_definition) if spec.dependencies
162
+
163
+ spec_dependencies = spec.all_dependencies(target_definition.platform)
164
+ find_dependency_specs(spec, spec_dependencies, target_definition)
206
165
  end
207
- validate_platform(spec || @cached_specs[dependency.name], target_definition)
208
166
  end
209
167
  end
210
168
  end
211
169
 
212
- # Ensures that a spec is compatible with the platform of a target.
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.
213
175
  #
214
- # @raises If the spec is not supported by the target.
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
+ end
192
+ cached_sets[name]
193
+ end
194
+
195
+ # Ensures that a specification is compatible with the platform of a target.
196
+ #
197
+ # @raise If the specification is not supported by the target.
198
+ #
199
+ # @return [void]
215
200
  #
216
201
  def validate_platform(spec, target)
217
- unless spec.available_platforms.any? { |platform| target.platform.supports?(platform) }
218
- raise Informative, "[!] The platform of the target `#{target.name}' (#{target.platform}) is not compatible with `#{spec}' which has a minimun requirement of #{spec.available_platforms.join(' - ')}.".red
202
+ unless spec.available_platforms.any? { |p| target.platform.supports?(p) }
203
+ raise Informative, "The platform of the target `#{target.name}` " \
204
+ "(#{target.platform}) is not compatible with `#{spec}` which has " \
205
+ "a minimum requirement of #{spec.available_platforms.join(' - ')}."
219
206
  end
220
207
  end
221
208
  end
@@ -1,105 +1,327 @@
1
1
  require 'fileutils'
2
2
 
3
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
+ # +-- Headers
16
+ # | +-- Private
17
+ # | | +-- [Pod Name]
18
+ # | +-- Public
19
+ # | +-- [Pod Name]
20
+ # |
21
+ # +-- Sources
22
+ # | +-- [Pod Name]
23
+ # |
24
+ # +-- Specifications
25
+ # | +-- External Sources
26
+ # | +-- Normal Sources
27
+ # |
28
+ # +-- Target Support Files
29
+ # | +-- [Target Name]
30
+ # | +-- Acknowledgements.markdown
31
+ # | +-- Acknowledgements.plist
32
+ # | +-- Pods.xcconfig
33
+ # | +-- Pods-prefix.pch
34
+ # | +-- Pods-dummy_Pods.m
35
+ # |
36
+ # +-- Manifest.lock
37
+ # |
38
+ # +-- Pods.xcodeproj
39
+ #
4
40
  class Sandbox
41
+
42
+ autoload :FileAccessor, 'cocoapods/sandbox/file_accessor'
43
+ autoload :HeadersStore, 'cocoapods/sandbox/headers_store'
44
+ autoload :PathList, 'cocoapods/sandbox/path_list'
45
+
46
+ # @return [Pathname] the root of the sandbox.
47
+ #
5
48
  attr_reader :root
49
+
50
+ # @return [HeadersStore] the header directory for the Pods libraries.
51
+ #
6
52
  attr_reader :build_headers
7
- attr_reader :public_headers
8
53
 
9
- BUILD_HEADERS_DIR = "BuildHeaders"
10
- PUBLIC_HEADERS_DIR = "Headers"
54
+ # @return [HeadersStore] the header directory for the user targets.
55
+ #
56
+ attr_reader :public_headers
11
57
 
12
- def initialize(path)
13
- @root = Pathname.new(path)
14
- @build_headers = HeadersDirectory.new(self, BUILD_HEADERS_DIR)
15
- @public_headers = HeadersDirectory.new(self, PUBLIC_HEADERS_DIR)
16
- @cached_local_pods = {}
17
- @cached_locally_sourced_pods = {}
58
+ # @param [String, Pathname] root @see root
59
+ #
60
+ def initialize(root)
61
+ @root = Pathname.new(root)
62
+ @build_headers = HeadersStore.new(self, "BuildHeaders")
63
+ @public_headers = HeadersStore.new(self, "Headers")
64
+ @predownloaded_pods = []
65
+ @checkout_sources = {}
66
+ @local_pods = {}
18
67
  FileUtils.mkdir_p(@root)
19
68
  end
20
69
 
70
+ # @return [Lockfile] the manifest which contains the information about the
71
+ # installed pods.
72
+ #
73
+ def manifest
74
+ Lockfile.from_file(manifest_path) if manifest_path.exist?
75
+ end
76
+
77
+ # @return [Project] the Pods project.
78
+ #
79
+ attr_accessor :project
80
+
81
+ # Removes the sandbox.
82
+ #
83
+ # @return [void]
84
+ #
21
85
  def implode
22
86
  root.rmtree
23
87
  end
24
88
 
89
+ #
90
+ #
91
+ #
92
+ def clean_pod(name)
93
+ root_name = Specification.root_name(name)
94
+ unless local?(root_name)
95
+ path = pod_dir(name)
96
+ path.rmtree if path.exist?
97
+ end
98
+ podspe_path = specification_path(name)
99
+ podspe_path.rmtree if podspe_path
100
+ end
101
+
102
+ # @return [String] a string representation suitable for debugging.
103
+ #
104
+ def inspect
105
+ "#<#{self.class}> with root #{root}"
106
+ end
107
+
108
+ #-------------------------------------------------------------------------#
109
+
110
+ public
111
+
112
+ # @!group Paths
113
+
114
+ # @return [Pathname] the path of the manifest.
115
+ #
116
+ def manifest_path
117
+ root + "Manifest.lock"
118
+ end
119
+
120
+ # @return [Pathname] the path of the Pods project.
121
+ #
25
122
  def project_path
26
123
  root + "Pods.xcodeproj"
27
124
  end
28
125
 
29
- def prepare_for_install
30
- build_headers.prepare_for_install
31
- public_headers.prepare_for_install
126
+ # Returns the path for the directory where to store the support files of
127
+ # a target.
128
+ #
129
+ # @param [String] name
130
+ # The name of the target.
131
+ #
132
+ # @return [Pathname] the path of the support files.
133
+ #
134
+ def library_support_files_dir(name)
135
+ # root + "Target Support Files/#{name}"
136
+ root
32
137
  end
33
138
 
34
- def local_pod_for_spec(spec, platform)
35
- key = [spec.top_level_parent.name, platform.to_sym]
36
- (@cached_local_pods[key] ||= LocalPod.new(spec.top_level_parent, self, platform)).tap do |pod|
37
- pod.add_specification(spec)
139
+ # Returns the path where the Pod with the given name is stored, taking into
140
+ # account whether the Pod is locally sourced.
141
+ #
142
+ # @param [String] name
143
+ # The name of the Pod.
144
+ #
145
+ # @return [Pathname] the path of the Pod.
146
+ #
147
+ def pod_dir(name)
148
+ root_name = Specification.root_name(name)
149
+ if local?(root_name)
150
+ Pathname.new(local_pods[root_name])
151
+ else
152
+ # root + "Sources/#{name}"
153
+ root + root_name
38
154
  end
39
155
  end
40
156
 
41
- # TODO: refactor the pods from a local source should not be chached by the sandbox
157
+ #-------------------------------------------------------------------------#
158
+
159
+ public
160
+
161
+ # @!group Specification store
162
+
163
+ # Returns the specification for the Pod with the given name.
164
+ #
165
+ # @param [String] name
166
+ # the name of the Pod for which the specification is requested.
42
167
  #
43
- def locally_sourced_pod_for_spec(spec, platform)
44
- key = [spec.top_level_parent.name, platform.to_sym]
45
- (@cached_locally_sourced_pods[key] ||= LocalPod::LocalSourcedPod.new(spec.top_level_parent, self, platform)).tap do |pod|
46
- pod.add_specification(spec)
168
+ # @return [Specification] the specification if the file is found.
169
+ #
170
+ def specification(name)
171
+ if file = specification_path(name)
172
+ Specification.from_file(file)
47
173
  end
48
174
  end
49
175
 
50
- def installed_pod_named(name, platform)
51
- if spec_path = podspec_for_name(name)
52
- key = [name, platform.to_sym]
53
- @cached_local_pods[key] ||= LocalPod.from_podspec(spec_path, self, platform)
54
- end
176
+ # @return [Pathname] the path for the directory where to store the
177
+ # specifications.
178
+ #
179
+ # @todo Migrate old installations and store the for all the pods.
180
+ # Two folders should be created `External Sources` and `Podspecs`.
181
+ #
182
+ def specifications_dir(external_source = false)
183
+ # root + "Specifications"
184
+ root + "Local Podspecs"
55
185
  end
56
186
 
57
- def podspec_for_name(name)
58
- path = root + "Local Podspecs/#{name}.podspec"
187
+ # Returns the path of the specification for the Pod with the
188
+ # given name, if one is stored.
189
+ #
190
+ # @param [String] name
191
+ # the name of the Pod for which the podspec file is requested.
192
+ #
193
+ # @return [Pathname] the path or nil.
194
+ # @return [Nil] if the podspec is not stored.
195
+ #
196
+ def specification_path(name)
197
+ path = specifications_dir + "#{name}.podspec"
59
198
  path.exist? ? path : nil
60
199
  end
61
- end
62
200
 
63
- class HeadersDirectory
64
- def initialize(sandbox, base_dir)
65
- @sandbox = sandbox
66
- @base_dir = base_dir
67
- @search_paths = [base_dir]
201
+ # Stores a specification in the `Local Podspecs` folder.
202
+ #
203
+ # @param [Sandbox] sandbox
204
+ # the sandbox where the podspec should be stored.
205
+ #
206
+ # @param [String, Pathname] podspec
207
+ # The contents of the specification (String) or the path to a
208
+ # podspec file (Pathname).
209
+ #
210
+ # @todo Store all the specifications (including those not originating
211
+ # from external sources) so users can check them.
212
+ #
213
+ def store_podspec(name, podspec, external_source = false)
214
+ output_path = specifications_dir(external_source) + "#{name}.podspec"
215
+ output_path.dirname.mkpath
216
+ if podspec.is_a?(String)
217
+ output_path.open('w') { |f| f.puts(podspec) }
218
+ else
219
+ unless podspec.exist?
220
+ raise Informative, "No podspec found for `#{name}` in #{podspec}"
221
+ end
222
+ FileUtils.copy(podspec, output_path)
223
+ end
224
+ spec = Specification.from_file(output_path)
225
+ unless spec.name == name
226
+ raise Informative, "The name of the given podspec `#{spec.name}` doesn't match the expected one `#{name}`"
227
+ end
68
228
  end
69
229
 
70
- def root
71
- @sandbox.root + @base_dir
72
- end
230
+ #-------------------------------------------------------------------------#
231
+
232
+ public
233
+
234
+ # @!group Pods information
73
235
 
74
- def add_file(namespace_path, relative_header_path)
75
- namespaced_header_path = root + namespace_path
76
- namespaced_header_path.mkpath unless File.exist?(namespaced_header_path)
77
- source = (@sandbox.root + relative_header_path).relative_path_from(namespaced_header_path)
78
- Dir.chdir(namespaced_header_path) { FileUtils.ln_sf(source, relative_header_path.basename)}
79
- @search_paths << namespaced_header_path.relative_path_from(@sandbox.root)
80
- namespaced_header_path + relative_header_path.basename
236
+ # Marks a Pod as pre-downloaded
237
+ #
238
+ # @param [String] name
239
+ # The name of the Pod.
240
+ #
241
+ # @return [void]
242
+ #
243
+ def store_pre_downloaded_pod(name)
244
+ root_name = Specification.root_name(name)
245
+ predownloaded_pods << root_name
81
246
  end
82
247
 
83
- def add_files(namespace_path, relative_header_paths)
84
- relative_header_paths.map { |path| add_file(namespace_path, path) }
248
+ # @return [Array<String>] The names of the pods that have been
249
+ # pre-downloaded from an external source.
250
+ #
251
+ attr_reader :predownloaded_pods
252
+
253
+ # Checks if a Pod has been pre-downloaded by the resolver in order to fetch
254
+ # the podspec.
255
+ #
256
+ # @param [String] name
257
+ # The name of the Pod.
258
+ #
259
+ # @return [Bool] Whether the Pod has been pre-downloaded.
260
+ #
261
+ def predownloaded?(name)
262
+ root_name = Specification.root_name(name)
263
+ predownloaded_pods.include?(root_name)
85
264
  end
86
265
 
87
- def search_paths
88
- @search_paths.uniq.map { |path| "${PODS_ROOT}/#{path}" }
266
+ #--------------------------------------#
267
+
268
+ # Stores the local path of a Pod.
269
+ #
270
+ # @param [String] name
271
+ # The name of the Pod.
272
+ #
273
+ # @param [Hash] source
274
+ # The hash which contains the options as returned by the
275
+ # downloader.
276
+ #
277
+ # @return [void]
278
+ #
279
+ def store_checkout_source(name, source)
280
+ root_name = Specification.root_name(name)
281
+ checkout_sources[root_name] = source
89
282
  end
90
283
 
91
- # Adds an header search path to the sandbox.
284
+ # @return [Hash{String=>Hash}] The options necessary to recreate the exact
285
+ # checkout of a given Pod grouped by its name.
286
+ #
287
+ attr_reader :checkout_sources
288
+
289
+ #--------------------------------------#
290
+
291
+ # Stores the local path of a Pod.
292
+ #
293
+ # @param [String] name
294
+ # The name of the Pod.
92
295
  #
93
- # @param path [Pathname] The path tho add.
296
+ # @param [#to_s] path
297
+ # The local path where the Pod is stored.
94
298
  #
95
299
  # @return [void]
96
300
  #
97
- def add_search_path(path)
98
- @search_paths << Pathname.new(@base_dir) + path
301
+ def store_local_path(name, path)
302
+ root_name = Specification.root_name(name)
303
+ local_pods[root_name] = path.to_s
99
304
  end
100
305
 
101
- def prepare_for_install
102
- root.rmtree if root.exist?
306
+ # @return [Hash{String=>String}] The path of the Pods with a local source
307
+ # grouped by their name.
308
+ #
309
+ attr_reader :local_pods
310
+
311
+ # Checks if a Pod is locally sourced?
312
+ #
313
+ # @param [String] name
314
+ # The name of the Pod.
315
+ #
316
+ # @return [Bool] Whether the Pod is locally sourced.
317
+ #
318
+ def local?(name)
319
+ root_name = Specification.root_name(name)
320
+ !local_pods[root_name].nil?
103
321
  end
322
+
323
+ #-------------------------------------------------------------------------#
324
+
104
325
  end
105
326
  end
327
+