Class: Pod::Lockfile
- Inherits:
-
Object
- Object
- Pod::Lockfile
- Defined in:
- lib/cocoapods-core/lockfile.rb
Overview
The Lockfile stores information about the pods that were installed by CocoaPods.
It is used in combination with the Podfile to resolve the exact version of
the Pods that should be installed (i.e. to prevent pod install
from
upgrading dependencies).
Moreover it is used as a manifest of an installation to detect which Pods need to be installed or removed.
Constant Summary collapse
- HASH_KEY_ORDER =
Returns The order in which the hash keys should appear in a serialized Lockfile.
[ 'PODS', 'DEPENDENCIES', 'SPEC REPOS', 'EXTERNAL SOURCES', 'CHECKOUT OPTIONS', 'SPEC CHECKSUMS', 'PODFILE CHECKSUM', 'COCOAPODS', ].map(&:freeze).freeze
Instance Attribute Summary collapse
-
#defined_in_file ⇒ String
The file where the Lockfile is serialized.
-
#internal_data ⇒ String
readonly
The hash used to initialize the Lockfile.
Class Method Summary collapse
-
.from_file(path) ⇒ Lockfile
Loads a lockfile form the given path.
-
.generate(podfile, specs, checkout_options, spec_repos = {}) ⇒ Lockfile
Generates a hash representation of the Lockfile generated from a given Podfile and the list of resolved Specifications.
-
.generate_checksums(specs) ⇒ Hash
private
Generates the relative to the checksum of the specifications.
-
.generate_dependencies_data(podfile) ⇒ Array
private
Generates the list of the dependencies of the Podfile.
-
.generate_external_sources_data(podfile) ⇒ Hash
private
Generates the information of the external sources.
-
.generate_pods_data(specs) ⇒ Array<Hash,String>
private
Generates the list of the installed Pods and their dependencies.
-
.generate_spec_repos(spec_repos) ⇒ Object
private
Generates the hash of spec repo sources used in the Podfile.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
Whether the Podfiles are equal.
-
#checkout_options_data ⇒ Hash{String => Hash}
private
A hash where the name of the pods are the keys and the values are a hash of specific checkout options.
-
#checkout_options_for_pod_named(name) ⇒ Hash
Returns the specific checkout options for the external source of the pod with the given name.
-
#checksum(name) ⇒ String, Nil
Returns the checksum for the given Pod.
-
#checksum_data ⇒ Hash{String => Version}
private
A Hash containing the checksums of the specification by the name of their root.
-
#cocoapods_version ⇒ Version
The version of CocoaPods which generated this lockfile.
-
#dependencies ⇒ Array<Dependency>
The dependencies of the Podfile used for the last installation.
-
#dependencies_to_lock_pod_named(name) ⇒ Array<Dependency>
Generates a dependency that requires the exact version of the Pod with the given name.
- #detect_changes_with_podfile(podfile) ⇒ Hash{Symbol=>Array[Strings]}
-
#external_sources_data ⇒ Hash{String => Hash}
private
A hash where the name of the pods are the keys and the values are the external source hash the dependency that required the pod.
-
#generate_pod_names_and_versions ⇒ Array<String, Hash{String => Array[String]}>
private
The pods installed and their dependencies.
-
#initialize(hash) ⇒ Lockfile
constructor
A new instance of Lockfile.
-
#inspect ⇒ String
A string representation suitable for debugging.
-
#pod_names ⇒ Array<String>
The names of the installed Pods.
-
#pod_versions ⇒ Hash{String => Version}
private
A Hash containing the name of the root specification of the installed Pods as the keys and their corresponding Version as the values.
-
#pods_by_spec_repo ⇒ Hash<String, Array<String>>
Returns pod names grouped by the spec repo they were sourced from.
-
#spec_repo(pod_name) ⇒ String, Nil
Returns the source of the given Pod.
-
#spec_repos_by_pod ⇒ Hash{String => String}
private
A hash containing the spec repo used for the specification by the name of the root spec.
-
#to_hash ⇒ Hash{String=>Array,Hash,String}
A hash representation of the Lockfile.
-
#to_yaml ⇒ String
The YAML representation of the Lockfile, used for serialization.
-
#version(pod_name) ⇒ Version, Nil
Returns the version of the given Pod.
-
#write_to_disk(path) ⇒ void
Writes the Lockfile to the given path.
Constructor Details
#initialize(hash) ⇒ Lockfile
Returns a new instance of Lockfile.
23 24 25 |
# File 'lib/cocoapods-core/lockfile.rb', line 23 def initialize(hash) @internal_data = hash end |
Instance Attribute Details
#defined_in_file ⇒ String
Returns the file where the Lockfile is serialized.
51 52 53 |
# File 'lib/cocoapods-core/lockfile.rb', line 51 def defined_in_file @defined_in_file end |
#internal_data ⇒ String (readonly)
Returns the hash used to initialize the Lockfile.
18 19 20 |
# File 'lib/cocoapods-core/lockfile.rb', line 18 def internal_data @internal_data end |
Class Method Details
.from_file(path) ⇒ Lockfile
This method returns nil if the given path doesn't exists.
Loads a lockfile form the given path.
38 39 40 41 42 43 44 45 46 47 |
# File 'lib/cocoapods-core/lockfile.rb', line 38 def self.from_file(path) return nil unless path.exist? hash = YAMLHelper.load_file(path) unless hash && hash.is_a?(Hash) raise Informative, "Invalid Lockfile in `#{path}`" end lockfile = Lockfile.new(hash) lockfile.defined_in_file = path lockfile end |
.generate(podfile, specs, checkout_options, spec_repos = {}) ⇒ Lockfile
Generates a hash representation of the Lockfile generated from a given Podfile and the list of resolved Specifications. This representation is suitable for serialization.
421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/cocoapods-core/lockfile.rb', line 421 def generate(podfile, specs, , spec_repos = {}) hash = { 'PODS' => generate_pods_data(specs), 'DEPENDENCIES' => generate_dependencies_data(podfile), 'SPEC REPOS' => generate_spec_repos(spec_repos), 'EXTERNAL SOURCES' => generate_external_sources_data(podfile), 'CHECKOUT OPTIONS' => , 'SPEC CHECKSUMS' => generate_checksums(specs), 'PODFILE CHECKSUM' => podfile.checksum, 'COCOAPODS' => CORE_VERSION, } Lockfile.new(hash) end |
.generate_checksums(specs) ⇒ Hash (private)
Generates the relative to the checksum of the specifications.
530 531 532 533 534 535 536 |
# File 'lib/cocoapods-core/lockfile.rb', line 530 def generate_checksums(specs) checksums = {} specs.select(&:defined_in_file).each do |spec| checksums[spec.root.name] = spec.checksum end checksums end |
.generate_dependencies_data(podfile) ⇒ Array (private)
Generates the list of the dependencies of the Podfile.
478 479 480 |
# File 'lib/cocoapods-core/lockfile.rb', line 478 def generate_dependencies_data(podfile) YAMLHelper.sorted_array(podfile.dependencies.map(&:to_s)) end |
.generate_external_sources_data(podfile) ⇒ Hash (private)
Generates the information of the external sources.
510 511 512 513 514 515 516 |
# File 'lib/cocoapods-core/lockfile.rb', line 510 def generate_external_sources_data(podfile) deps = podfile.dependencies.select(&:external?) deps = deps.sort { |d, other| d.name <=> other.name } sources = {} deps.each { |d| sources[d.root_name] = d.external_source } sources end |
.generate_pods_data(specs) ⇒ Array<Hash,String> (private)
Specifications should be stored per platform, otherwise they list dependencies which actually might not be used.
The dependencies of iOS and OS X version of the same pod are merged.
Generates the list of the installed Pods and their dependencies.
456 457 458 459 460 461 462 463 464 465 466 467 468 |
# File 'lib/cocoapods-core/lockfile.rb', line 456 def generate_pods_data(specs) pods_and_deps_merged = specs.reduce({}) do |result, spec| name = spec.to_s result[name] ||= [] result[name].concat(spec.all_dependencies.map(&:to_s)) result end pod_and_deps = pods_and_deps_merged.map do |name, deps| deps.empty? ? name : { name => YAMLHelper.sorted_array(deps.uniq) } end YAMLHelper.sorted_array(pod_and_deps) end |
.generate_spec_repos(spec_repos) ⇒ Object (private)
Generates the hash of spec repo sources used in the Podfile.
487 488 489 490 491 492 493 494 495 496 497 498 499 |
# File 'lib/cocoapods-core/lockfile.rb', line 487 def generate_spec_repos(spec_repos) Hash[spec_repos.map do |source, specs| next unless source next if specs.empty? key = source.url || source.name # save `trunk` as 'trunk' so that the URL itself can be changed without lockfile churn key = Pod::TrunkSource::TRUNK_REPO_NAME if source.name == Pod::TrunkSource::TRUNK_REPO_NAME value = specs.map { |s| s.root.name }.uniq [key, YAMLHelper.sorted_array(value)] end.compact] end |
Instance Method Details
#==(other) ⇒ Boolean
Returns Whether the Podfiles are equal.
55 56 57 |
# File 'lib/cocoapods-core/lockfile.rb', line 55 def ==(other) other && to_hash == other.to_hash end |
#checkout_options_data ⇒ Hash{String => Hash} (private)
Returns a hash where the name of the pods are the keys and the values are a hash of specific checkout options.
231 232 233 |
# File 'lib/cocoapods-core/lockfile.rb', line 231 def @checkout_options_data ||= internal_data['CHECKOUT OPTIONS'] || {} end |
#checkout_options_for_pod_named(name) ⇒ Hash
Returns the specific checkout options for the external source of the pod with the given name.
188 189 190 |
# File 'lib/cocoapods-core/lockfile.rb', line 188 def (name) [name] end |
#checksum(name) ⇒ String, Nil
Returns the checksum for the given Pod.
115 116 117 |
# File 'lib/cocoapods-core/lockfile.rb', line 115 def checksum(name) checksum_data[name] end |
#checksum_data ⇒ Hash{String => Version} (private)
Returns A Hash containing the checksums of the specification by the name of their root.
247 248 249 |
# File 'lib/cocoapods-core/lockfile.rb', line 247 def checksum_data internal_data['SPEC CHECKSUMS'] || {} end |
#cocoapods_version ⇒ Version
Returns The version of CocoaPods which generated this lockfile.
194 195 196 |
# File 'lib/cocoapods-core/lockfile.rb', line 194 def cocoapods_version Version.new(internal_data['COCOAPODS']) end |
#dependencies ⇒ Array<Dependency>
It includes only the dependencies explicitly required in the podfile and not those triggered by the Resolver.
Returns the dependencies of the Podfile used for the last installation.
124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/cocoapods-core/lockfile.rb', line 124 def dependencies unless @dependencies data = internal_data['DEPENDENCIES'] || [] @dependencies = data.map do |string| dep = Dependency.from_string(string) dep.external_source = external_sources_data[dep.root_name] dep end end @dependencies end |
#dependencies_to_lock_pod_named(name) ⇒ Array<Dependency>
The generated dependencies used are by the Resolver from upgrading a Pod during an installation.
Generates a dependency that requires the exact version of the Pod with the given name.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/cocoapods-core/lockfile.rb', line 160 def dependencies_to_lock_pod_named(name) deps = dependencies.select { |d| d.root_name == name } if deps.empty? raise StandardError, "Attempt to lock the `#{name}` Pod without a " \ 'known dependency.' end deps.map do |dep| version = version(dep.name) locked_dependency = dep.dup locked_dependency.specific_version = version locked_dependency end end |
#detect_changes_with_podfile(podfile) ⇒ Hash{Symbol=>Array[Strings]}
Why do we look for compatibility instead of just comparing if the two dependencies are equal?
Analyzes the Pod::Lockfile and detects any changes applied to the Podfile since the last installation.
For each Pod, it detects one state among the following:
- added: Pods that weren't present in the Podfile.
- changed: Pods that were present in the Podfile but changed:
- Pods whose version is not compatible anymore with Podfile,
- Pods that changed their external options.
- removed: Pods that were removed form the Podfile.
- unchanged: Pods that are still compatible with Podfile.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/cocoapods-core/lockfile.rb', line 289 def detect_changes_with_podfile(podfile) result = {} [:added, :changed, :removed, :unchanged].each { |k| result[k] = [] } installed_deps = {} dependencies.each do |dep| name = dep.root_name installed_deps[name] ||= dependencies_to_lock_pod_named(name) end installed_deps = installed_deps.values.flatten(1).group_by(&:name) podfile_dependencies = podfile.dependencies podfile_dependencies_by_name = podfile_dependencies.group_by(&:name) all_dep_names = (dependencies + podfile_dependencies).map(&:name).uniq all_dep_names.each do |name| installed_dep = installed_deps[name] installed_dep &&= installed_dep.first podfile_dep = podfile_dependencies_by_name[name] podfile_dep &&= podfile_dep.first if installed_dep.nil? then key = :added elsif podfile_dep.nil? then key = :removed elsif podfile_dep.compatible?(installed_dep) then key = :unchanged else key = :changed end result[key] << name end result end |
#external_sources_data ⇒ Hash{String => Hash} (private)
Returns a hash where the name of the pods are the keys and the values are the external source hash the dependency that required the pod.
224 225 226 |
# File 'lib/cocoapods-core/lockfile.rb', line 224 def external_sources_data @external_sources_data ||= internal_data['EXTERNAL SOURCES'] || {} end |
#generate_pod_names_and_versions ⇒ Array<String, Hash{String => Array[String]}> (private)
Returns the pods installed and their dependencies.
207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/cocoapods-core/lockfile.rb', line 207 def generate_pod_names_and_versions @pod_names = [] @pod_versions = {} return unless pods = internal_data['PODS'] pods.each do |pod| pod = pod.keys.first unless pod.is_a?(String) name, version = Spec.name_and_version_from_string(pod) @pod_names << name @pod_versions[name] = version end end |
#inspect ⇒ String
Returns a string representation suitable for debugging.
61 62 63 |
# File 'lib/cocoapods-core/lockfile.rb', line 61 def inspect "#<#{self.class}>" end |
#pod_names ⇒ Array<String>
Returns the names of the installed Pods.
73 74 75 76 |
# File 'lib/cocoapods-core/lockfile.rb', line 73 def pod_names generate_pod_names_and_versions unless @pod_names @pod_names end |
#pod_versions ⇒ Hash{String => Version} (private)
Returns a Hash containing the name of the root specification of the installed Pods as the keys and their corresponding Version as the values.
239 240 241 242 |
# File 'lib/cocoapods-core/lockfile.rb', line 239 def pod_versions generate_pod_names_and_versions unless @pod_versions @pod_versions end |
#pods_by_spec_repo ⇒ Hash<String, Array<String>>
It does not include pods that come from "external sources".
Returns pod names grouped by the spec repo they were sourced from.
143 144 145 |
# File 'lib/cocoapods-core/lockfile.rb', line 143 def pods_by_spec_repo @pods_by_spec_repo ||= internal_data['SPEC REPOS'] || {} end |
#spec_repo(pod_name) ⇒ String, Nil
Returns the source of the given Pod.
103 104 105 |
# File 'lib/cocoapods-core/lockfile.rb', line 103 def spec_repo(pod_name) spec_repos_by_pod[pod_name] end |
#spec_repos_by_pod ⇒ Hash{String => String} (private)
Returns A hash containing the spec repo used for the specification by the name of the root spec.
254 255 256 257 258 259 260 |
# File 'lib/cocoapods-core/lockfile.rb', line 254 def spec_repos_by_pod @spec_repos_by_pod ||= pods_by_spec_repo.each_with_object({}) do |(spec_repo, pods), spec_repos_by_pod| pods.each do |pod| spec_repos_by_pod[pod] = spec_repo end end end |
#to_hash ⇒ Hash{String=>Array,Hash,String}
Returns a hash representation of the Lockfile.
368 369 370 371 372 373 374 |
# File 'lib/cocoapods-core/lockfile.rb', line 368 def to_hash hash = {} internal_data.each do |key, value| hash[key] = value unless value.nil? || value.empty? end hash end |
#to_yaml ⇒ String
Empty root keys are discarded.
The YAML string is prettified.
Returns the YAML representation of the Lockfile, used for serialization.
397 398 399 |
# File 'lib/cocoapods-core/lockfile.rb', line 397 def to_yaml YAMLHelper.convert_hash(to_hash, HASH_KEY_ORDER, "\n\n") end |
#version(pod_name) ⇒ Version, Nil
Returns the version of the given Pod.
86 87 88 89 90 91 92 93 |
# File 'lib/cocoapods-core/lockfile.rb', line 86 def version(pod_name) version = pod_versions[pod_name] return version if version root_name = pod_versions.keys.find do |name| Specification.root_name(name) == pod_name end pod_versions[root_name] end |
#write_to_disk(path) ⇒ void
This method returns an undefined value.
Writes the Lockfile to the given path.
334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/cocoapods-core/lockfile.rb', line 334 def write_to_disk(path) path.dirname.mkpath unless path.dirname.exist? self.defined_in_file = path # rubocop:disable Lint/RescueException # rubocop:disable Lint/HandleExceptions begin existing = Lockfile.from_file(path) return if existing == self rescue Exception end path.open('w') { |f| f.write(to_yaml) } # rubocop:enable Lint/HandleExceptions # rubocop:enable Lint/RescueException end |