Permalink
Browse files

Introduced Pod `update`, `outdated`.

See #131, #191.

- The installer is initialized with a resolver. The resolver is responsible of
  indicating which specs must be installed/reinstalled.
- It was introduced a slight change in the format of the Podfile.lock.
- The specification set was simplified to receive and handle Pod::Dependency
  instead of Pod::Specification. With this change it also appears to be more
  robust.

A this stage it appears to be working. However the support, for external and
head dependencies is weak.
  • Loading branch information...
fabiopelosin committed Aug 8, 2012
1 parent 0223233 commit 34a68534d8b4a9fa3f4d9f9dbb905de10d19bb11
View
@@ -32,6 +32,7 @@ def message
autoload :Executable, 'cocoapods/executable'
autoload :Installer, 'cocoapods/installer'
autoload :LocalPod, 'cocoapods/local_pod'
+ autoload :Lockfile, 'cocoapods/lockfile'
autoload :Platform, 'cocoapods/platform'
autoload :Podfile, 'cocoapods/podfile'
autoload :Project, 'cocoapods/project'
View
@@ -6,12 +6,14 @@ class Command
autoload :Install, 'cocoapods/command/install'
autoload :List, 'cocoapods/command/list'
autoload :Linter, 'cocoapods/command/linter'
+ autoload :Outdated, 'cocoapods/command/outdated'
autoload :Presenter, 'cocoapods/command/presenter'
autoload :Push, 'cocoapods/command/push'
autoload :Repo, 'cocoapods/command/repo'
autoload :Search, 'cocoapods/command/search'
autoload :Setup, 'cocoapods/command/setup'
autoload :Spec, 'cocoapods/command/spec'
+ autoload :Update, 'cocoapods/command/update'
class Help < Informative
def initialize(command_class, argv)
@@ -99,13 +101,15 @@ def self.parse(*argv)
String.send(:define_method, :colorize) { |string , _| string } if argv.option( '--no-color' )
command_class = case argv.shift_argument
- when 'install' then Install
- when 'repo' then Repo
- when 'search' then Search
- when 'list' then List
- when 'setup' then Setup
- when 'spec' then Spec
- when 'push' then Push
+ when 'install' then Install
+ when 'list' then List
+ when 'outdated' then Outdated
+ when 'push' then Push
+ when 'repo' then Repo
+ when 'search' then Search
+ when 'setup' then Setup
+ when 'spec' then Spec
+ when 'update' then Update
end
if show_help || command_class.nil?
@@ -6,7 +6,7 @@ def self.banner
$ pod install
- Downloads all dependencies defined in `Podfile' and creates an Xcode
+ Downloads all dependencies updated in `Podfile' and creates an Xcode
Pods library project in `./Pods'.
The Xcode project file should be specified in your `Podfile` like this:
@@ -50,7 +50,9 @@ def run
Repo.new(ARGV.new(["update"])).run
end
- Installer.new(podfile).install!
+ sandbox = Sandbox.new(config.project_pods_root)
+ resolver = Resolver.new(podfile, config.lockfile, sandbox)
+ Installer.new(resolver).install!
end
end
end
@@ -103,7 +103,9 @@ def peform_extensive_analysis
def install_pod
podfile = podfile_from_spec
config.verbose
- installer = Installer.new(podfile)
+ sandbox = Sandbox.new(config.project_pods_root)
+ resolver = Resolver.new(podfile, nil, sandbox)
+ installer = Installer.new(resolver)
installer.install!
@pod = installer.pods.find { |pod| pod.top_specification == spec }
config.silent
@@ -0,0 +1,52 @@
+module Pod
+ class Command
+ class Outdated < Command
+ def self.banner
+%{Updates dependencies of a project:
+
+ $ pod outdated
+
+ Shows the dependencies that would be installed by `pod update'. }
+ end
+
+ def self.options
+ [
+ ["--no-update", "Skip running `pod repo update` before install"],
+ ].concat(super)
+ end
+
+ def initialize(argv)
+ @update_repo = !argv.option('--no-update')
+ super unless argv.empty?
+ end
+
+ def run
+ unless podfile = config.podfile
+ raise Informative, "No `Podfile' found in the current working directory."
+ end
+ unless lockfile = config.lockfile
+ raise Informative, "No `Podfile.lock' found in the current working directory, run `pod install'."
+ end
+
+ # if @update_repo
+ # print_title 'Updating Spec Repositories', true
+ # Re"o.new(ARGV.new(["update"])).run
+ # end
+
+ sandbox = Sandbox.new(config.project_pods_root)
+ resolver = Resolver.new(podfile, lockfile, sandbox)
+ resolver.update_mode = true
+ resolver.resolve
+ specs_to_install = resolver.specs_to_install
+ if specs_to_install.empty?
+ puts "\nNo updates are available.\n".yellow
+ else
+ puts "\nThe following updates are available:".green
+ puts " - " << specs_to_install.join("\n - ") << "\n\n"
+ end
+ end
+ end
+ end
+end
+
+
@@ -0,0 +1,51 @@
+module Pod
+ class Command
+ class Update < Command
+ def self.banner
+%{Updates dependencies of a project:
+
+ $ pod update
+
+ Updates all dependencies installed by `pod install`. It doesn't
+ install new dependencies. }
+ end
+
+ def self.options
+ [
+ ["--no-clean", "Leave SCM dirs like `.git' and `.svn' intact after downloading"],
+ ["--no-doc", "Skip documentation generation with appledoc"],
+ ["--no-integrate", "Skip integration of the Pods libraries in the Xcode project(s)"],
+ ["--no-update", "Skip running `pod repo update` before install"],
+ ].concat(super)
+ end
+
+ def initialize(argv)
+ config.clean = !argv.option('--no-clean')
+ config.generate_docs = !argv.option('--no-doc')
+ config.integrate_targets = !argv.option('--no-integrate')
+ @update_repo = !argv.option('--no-update')
+ super unless argv.empty?
+ end
+
+ def run
+ unless podfile = config.podfile
+ raise Informative, "No `Podfile' found in the current working directory."
+ end
+ unless lockfile = config.lockfile
+ raise Informative, "No `Podfile.lock' found in the current working directory, run `pod install'."
+ end
+
+ # if @update_repo
+ # print_title 'Updating Spec Repositories', true
+ # Repo.new(ARGV.new(["update"])).run
+ # end
+
+ sandbox = Sandbox.new(config.project_pods_root)
+ resolver = Resolver.new(podfile, lockfile, sandbox)
+ resolver.update_mode = true
+ Installer.new(resolver).install!
+ end
+ end
+ end
+end
+
View
@@ -41,6 +41,10 @@ def project_podfile
@project_podfile ||= project_root + 'Podfile'
end
+ def project_lockfile
+ @project_lockfile ||= project_root + 'Podfile.lock'
+ end
+
def headers_symlink_root
@headers_symlink_root ||= "#{project_pods_root}/Headers"
end
@@ -53,6 +57,12 @@ def podfile
end
attr_writer :podfile
+ def lockfile
+ @lockfile ||= begin
+ Lockfile.from_file(project_lockfile) if project_lockfile.exist?
+ end
+ end
+
module Mixin
def config
Config.instance
View
@@ -7,19 +7,12 @@ class Installer
include Config::Mixin
- attr_reader :sandbox
-
- def initialize(podfile)
- @podfile = podfile
- # FIXME: pass this into the installer as a parameter
- @sandbox = Sandbox.new(config.project_pods_root)
- @resolver = Resolver.new(@podfile, @sandbox)
- # TODO: remove in 0.7 (legacy support for config.ios? and config.osx?)
- config.podfile = podfile
- end
+ attr_reader :resolver, :sandbox, :lockfile
- def lock_file
- config.project_root + 'Podfile.lock'
+ def initialize(resolver)
+ @resolver = resolver
+ @podfile = resolver.podfile
+ @sandbox = resolver.sandbox
end
def project
@@ -46,6 +39,9 @@ def target_installers
def install_dependencies!
pods.each do |pod|
+ name = pod.top_specification.name
+ should_install = @resolver.should_install?(name) || !pod.exists?
+
unless config.silent?
marker = config.verbose ? "\n-> ".green : ''
if subspec_name = pod.top_specification.preferred_dependency
@@ -54,20 +50,22 @@ def install_dependencies!
name = pod.to_s
end
name << " [HEAD]" if pod.top_specification.version.head?
- puts marker << ( pod.exists? ? "Using #{name}" : "Installing #{name}".green )
+ puts marker << ( should_install ? "Installing #{name}".green : "Using #{name}" )
end
- download_pod(pod) unless pod.exists?
-
- # This will not happen if the pod existed before we started the install
- # process.
- if pod.downloaded?
- # The docs need to be generated before cleaning because the
- # documentation is created for all the subspecs.
- generate_docs(pod)
- # Here we clean pod's that just have been downloaded or have been
- # pre-downloaded in AbstractExternalSource#specification_from_sandbox.
- pod.clean! if config.clean?
+ if should_install
+ pod.implode
+ download_pod(pod)
+ # This will not happen if the pod existed before we started the install
+ # process.
+ if pod.downloaded?
+ # The docs need to be generated before cleaning because the
+ # documentation is created for all the subspecs.
+ generate_docs(pod)
+ # Here we clean pod's that just have been downloaded or have been
+ # pre-downloaded in AbstractExternalSource#specification_from_sandbox.
+ pod.clean! if config.clean?
+ end
end
end
end
@@ -117,68 +115,32 @@ def install!
generate_dummy_source(target_installer)
end
- generate_lock_file!(specifications)
-
puts "- Running post install hooks" if config.verbose?
# Post install hooks run _before_ saving of project, so that they can alter it before saving.
run_post_install_hooks
puts "- Writing Xcode project file to `#{@sandbox.project_path}'\n\n" if config.verbose?
project.save_as(@sandbox.project_path)
+ puts "- Writing lockfile in `#{lockfile.defined_in_file}'\n\n" if config.verbose?
+ @lockfile = Lockfile.create(config.project_lockfile, @podfile, specs_by_target.values.flatten)
+ @lockfile.write_to_disk
+
UserProjectIntegrator.new(@podfile).integrate! if config.integrate_targets?
end
def run_post_install_hooks
# we loop over target installers instead of pods, because we yield the target installer
# to the spec post install hook.
target_installers.each do |target_installer|
- specs_by_target[target_installer.target_definition].each do |spec|
+ @specs_by_target[target_installer.target_definition].each do |spec|
spec.post_install(target_installer)
end
end
@podfile.post_install!(self)
end
- def generate_lock_file!(specs)
- lock_file.open('w') do |file|
- file.puts "PODS:"
-
- # Get list of [name, dependencies] pairs.
- pod_and_deps = specs.map do |spec|
- [spec.to_s, spec.dependencies.map(&:to_s).sort]
- end.uniq
-
- # Merge dependencies of ios and osx version of the same pod.
- tmp = {}
- pod_and_deps.each do |name, deps|
- if tmp[name]
- tmp[name].concat(deps).uniq!
- else
- tmp[name] = deps
- end
- end
- pod_and_deps = tmp
-
- # Sort by name and print
- pod_and_deps.sort_by(&:first).each do |name, deps|
- if deps.empty?
- file.puts " - #{name}"
- else
- file.puts " - #{name}:"
- deps.each { |dep| file.puts " - #{dep}" }
- end
- end
-
- file.puts
- file.puts "DEPENDENCIES:"
- @podfile.dependencies.map(&:to_s).sort.each do |dep|
- file.puts " - #{dep}"
- end
- end
- end
-
def generate_dummy_source(target_installer)
class_name_identifier = target_installer.target_definition.label
dummy_source = Generator::DummySource.new(class_name_identifier)
@@ -192,15 +154,6 @@ def generate_dummy_source(target_installer)
target_installer.target.source_build_phases.first << project_file
end
- def specs_by_target
- @specs_by_target ||= @resolver.resolve
- end
-
- # @return [Array<Specification>] All dependencies that have been resolved.
- def specifications
- specs_by_target.values.flatten
- end
-
# @return [Array<LocalPod>] A list of LocalPod instances for each
# dependency that is not a download-only one.
def pods
@@ -219,6 +172,14 @@ def pods_by_target
result
end
+ def specifications
+ specs_by_target.values.flatten
+ end
+
+ def specs_by_target
+ @specs_by_target ||= @resolver.resolve
+ end
+
private
def print_title(title, only_verbose = true)
Oops, something went wrong.

0 comments on commit 34a6853

Please sign in to comment.