cocoapods 0.16.4 → 0.17.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+