Commit 4bb14e58 authored by Fabio Pelosin's avatar Fabio Pelosin

[WIP][Installer] Adapt for LocalPod refactor

parent d2b90c07
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
- Subspecs now do not inherit the files patterns from the parent spec. - Subspecs now do not inherit the files patterns from the parent spec.
- The workspace is written only if needed greatly reducing the occasions in - The workspace is written only if needed greatly reducing the occasions in
which Xcode asks to revert. which Xcode asks to revert.
- Specification hooks are only called when the specification is installed.
###### Specification DSL ###### Specification DSL
......
...@@ -28,9 +28,11 @@ module Pod ...@@ -28,9 +28,11 @@ module Pod
# #
class Installer class Installer
autoload :Analyzer, 'cocoapods/installer/analyzer' autoload :Analyzer, 'cocoapods/installer/analyzer'
autoload :TargetInstaller, 'cocoapods/installer/target_installer' autoload :FileReferencesInstaller, 'cocoapods/installer/file_references_installer'
autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator' autoload :PodSourceInstaller, 'cocoapods/installer/pod_source_installer'
autoload :TargetInstaller, 'cocoapods/installer/target_installer'
autoload :UserProjectIntegrator, 'cocoapods/installer/user_project_integrator'
include Config::Mixin include Config::Mixin
...@@ -53,9 +55,9 @@ module Pod ...@@ -53,9 +55,9 @@ module Pod
# @param [Lockfile] lockfile @see lockfile # @param [Lockfile] lockfile @see lockfile
# #
def initialize(sandbox, podfile, lockfile = nil) def initialize(sandbox, podfile, lockfile = nil)
@sandbox = sandbox @sandbox = sandbox
@podfile = podfile @podfile = podfile
@lockfile = lockfile @lockfile = lockfile
end end
# @return [Bool] Whether the installer is in update mode. In update mode # @return [Bool] Whether the installer is in update mode. In update mode
...@@ -80,34 +82,37 @@ module Pod ...@@ -80,34 +82,37 @@ module Pod
# #
def install! def install!
analyze analyze
generate_local_pods detect_pods_to_install
generate_names_of_pods_to_install
prepare_for_legacy_compatibility prepare_for_legacy_compatibility
prepare_sandbox prepare_sandbox
install_dependencies
UI.section "Downloading dependencies" do
create_file_accessors
install_pod_sources
end
UI.section "Generating Pods Project" do UI.section "Generating Pods Project" do
prepare_pods_project prepare_pods_project
generate_target_installers
add_file_references_to_pods_project
run_pre_install_hooks run_pre_install_hooks
generate_target_support_files install_file_references
install_targets
run_post_install_hooks run_post_install_hooks
write_pod_project write_pod_project
write_lockfiles write_lockfiles
end end
integrate_user_project if config.integrate_targets?
UI.section "Integrating client projects" do
integrate_user_project
end
end
end end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
public public
# @!group API # @!group Installation results
#
# This is the tentative API for the podfile and the specification hooks.
# @return [Analyzer] the analyzer which provides the information about what # @return [Analyzer] the analyzer which provides the information about what
# needs to be installed. # needs to be installed.
...@@ -118,20 +123,6 @@ module Pod ...@@ -118,20 +123,6 @@ module Pod
# #
attr_reader :pods_project attr_reader :pods_project
# @return [Array<TargetInstaller>]
#
attr_reader :target_installers
# @return [Hash{TargetDefinition => Array<LocalPod>}] The local pod
# instances grouped by target.
#
attr_reader :local_pods_by_target
# @return [Array<LocalPod>] The list of LocalPod instances for each
# dependency sorted by name.
#
attr_reader :local_pods
# @return [Array<String>] The Pods that should be installed. # @return [Array<String>] The Pods that should be installed.
# #
attr_reader :names_of_pods_to_install attr_reader :names_of_pods_to_install
...@@ -141,19 +132,21 @@ module Pod ...@@ -141,19 +132,21 @@ module Pod
# #
attr_reader :libraries attr_reader :libraries
#--------------------------------------# # @return [Array<Specification>] The specifications that where installed.
#
attr_accessor :installed_specs
# @!group Hooks compatibility #-------------------------------------------------------------------------#
alias :project :pods_project private
alias :pods :local_pods
#-------------------------------------------------------------------------# # TODO: This is recreating the file accessors
# TODO: the file accessor should be initializable without a path list
# @!group Installation steps # @!group Installation steps
private # @return [void]
#
def analyze def analyze
@analyzer = Analyzer.new(sandbox, podfile, lockfile) @analyzer = Analyzer.new(sandbox, podfile, lockfile)
@analyzer.update_mode = update_mode @analyzer.update_mode = update_mode
...@@ -161,46 +154,6 @@ module Pod ...@@ -161,46 +154,6 @@ module Pod
@libraries = analyzer.libraries @libraries = analyzer.libraries
end end
# Converts the specifications produced by the Resolver in local pods.
#
# The LocalPod class is responsible to handle the concrete representation
# of a specification in the {Sandbox}.
#
# @return [void]
#
# @todo [#535] LocalPods should resolve the path of the specifications
# passing the library as arguments.
#
# @todo Why the local pods are generated by the sandbox? I guess because
# some where pre-downloaded? However the sandbox should just store
# the name of those Pods.
#
def generate_local_pods
@local_pods_by_target = {}
analyzer.specs_by_target.each do |target_definition, specs|
libray = libraries.find {|l| l.target_definition == target_definition }
# TODO the sandbox cached the local pods by target definition
# take into account local? in specification#==
locally_sourced_pods = {}
local_pods = {}
libray.local_pods = specs.map do |spec|
if spec.local?
local_pod = (locally_sourced_pods[spec.root.name] ||= LocalPod::LocalSourcedPod.new(spec.root, sandbox, target_definition.platform))
local_pod.add_specification(spec)
else
local_pod = (local_pods[spec.root.name] ||= LocalPod.new(spec.root, sandbox, target_definition.platform))
local_pod.add_specification(spec)
end
local_pod
end.uniq.compact
end
@local_pods = libraries.map(&:local_pods).flatten.uniq.sort_by { |pod| pod.name.downcase }
end
# Computes the list of the Pods that should be installed or reinstalled in # Computes the list of the Pods that should be installed or reinstalled in
# the {Sandbox}. # the {Sandbox}.
# #
...@@ -217,17 +170,27 @@ module Pod ...@@ -217,17 +170,27 @@ module Pod
# @todo There could be issues with the current implementation regarding # @todo There could be issues with the current implementation regarding
# external specs. # external specs.
# #
def generate_names_of_pods_to_install def detect_pods_to_install
changed_pods_names = [] names = []
if update_mode
changed_pods_names += pods.select do |pod| # TODO
pod.top_specification.version.head? || # specs_by_root_name.each do |root_name, specs|
resolver.pods_from_external_sources.include?(pod.name) # if update_mode
end # if specs.any? { |spec| spec.version.head? } #|| resolver.pods_from_external_sources.include?(root_name)
end # @names_of_pods_to_install << root_name
changed_pods_names += analyzer.sandbox_state.added + analyzer.sandbox_state.changed # end
not_existing_pods = local_pods.reject { |pod| pod.exists? } # end
@names_of_pods_to_install = (changed_pods_names + not_existing_pods.map(&:name)).uniq
# unless pod_installation_exists?(root_name)
# @names_of_pods_to_install << root_name
# end
# end
# TODO user root name.
names += analyzer.sandbox_state.added + analyzer.sandbox_state.changed
names = names.map { |name| Specification.root_name(name) }
names = names.flatten.uniq
@names_of_pods_to_install = names
end end
# Prepares the Pods folder in order to be compatible with the most recent # Prepares the Pods folder in order to be compatible with the most recent
...@@ -258,6 +221,7 @@ module Pod ...@@ -258,6 +221,7 @@ module Pod
sandbox.build_headers.implode! sandbox.build_headers.implode!
sandbox.public_headers.implode! sandbox.public_headers.implode!
# TODO local option
unless analyzer.sandbox_state.deleted.empty? unless analyzer.sandbox_state.deleted.empty?
UI.section "Removing deleted dependencies" do UI.section "Removing deleted dependencies" do
analyzer.sandbox_state.deleted.each do |pod_name| analyzer.sandbox_state.deleted.each do |pod_name|
...@@ -270,25 +234,6 @@ module Pod ...@@ -270,25 +234,6 @@ module Pod
end end
end end
# @return [void] Install the Pods. If the resolver indicated that a Pod
# should be installed and it exits, it is removed an then
# reinstalled. In any case if the Pod doesn't exits it is
# installed.
#
def install_dependencies
UI.section "Downloading dependencies" do
local_pods.each do |pod|
if names_of_pods_to_install.include?(pod.name)
UI.section("Installing #{pod}".green, "-> ".green) do
install_local_pod(pod)
end
else
UI.section("Using #{pod}", "-> ".green)
end
end
end
end
# Creates the Pods project from scratch if it doesn't exists. # Creates the Pods project from scratch if it doesn't exists.
# #
# @return [void] # @return [void]
...@@ -297,70 +242,108 @@ module Pod ...@@ -297,70 +242,108 @@ module Pod
# #
def prepare_pods_project def prepare_pods_project
UI.message "- Creating Pods project" do UI.message "- Creating Pods project" do
@pods_project = Pod::Project.new(nil) @pods_project = Pod::Project.new(sandbox.project_path)
if config.podfile_path.exist? if config.podfile_path.exist?
podfile_relative_path = config.podfile_path.relative_path_from(sandbox.project_path.dirname) @pods_project.add_podfile(config.podfile_path)
@pods_project.add_podfile(podfile_relative_path)
end end
sandbox.project = @pods_project sandbox.project = @pods_project
end end
end end
# Creates a target installer for each definition not empty.
# #
# @return [void]
# #
def generate_target_installers def create_file_accessors
@target_installers = libraries.map do |library| libraries.each do |library|
unless library.target_definition.empty? library.specs.each do |spec|
TargetInstaller.new(sandbox, library) if spec.local?
pod_root = Pathname.new(spec.source[:local]).expand_path
else
pod_root = sandbox.pod_dir(spec.root.name)
end
path_list = Sandbox::PathList.new(pod_root)
file_accessor = Sandbox::FileAccessor.new(path_list, spec.consumer(library.platform))
library.file_accessors ||= []
library.file_accessors << file_accessor
end end
end.compact end
end end
# Adds the source files of the Pods to the Pods project. # Downloads, installs the documentation and cleans the sources of the Pods
# # which need to be installed.
# The source files are grouped by Pod and in turn by subspec
# (recursively). Pods are generally added to the `Pods` group, however, if
# they have a local source they are added to the `Local Pods` group.
# #
# @return [void] # @return [void]
# #
# @todo Clean the groups of the deleted Pods and add only the Pods that def install_pod_sources
# should be installed. @installed_specs = []
# root_specs = analyzer.specifications.map { |spec| spec.root }
def add_file_references_to_pods_project root_specs.each do |spec|
UI.message "- Adding Pods files to Pods project" do if names_of_pods_to_install.include?(spec.name)
local_pods.each do |pod| UI.section("Installing #{spec}".green, "-> ".green) do
pod.add_file_references_to_project(pods_project) install_source_of_pod(spec.name)
pod.link_headers
unless pod.resources.empty?
resources_group = pods_project.new_group(pod.name, "Resources")
pod.resources.each do |resource|
resources_group.new_file(resource.relative_path_from(sandbox.root))
end end
else
UI.section("Using #{spec}", "-> ".green)
end end
end end
end
end end
# Runs the pre install hooks of the installed specs and of the Podfile. # Install the Pods. If the resolver indicated that a Pod should be
# installed and it exits, it is removed an then reinstalled. In any case if
# the Pod doesn't exits it is installed.
# #
# @return [void] # @return [void]
# #
# @todo Run the hooks only for the installed pods. def install_source_of_pod(pod_name)
specs_by_platform = {}
libraries.each do |library|
specs = library.specs.select { |spec| spec.root.name == pod_name }
unless specs.empty?
specs_by_platform[library.platform] ||= []
specs_by_platform[library.platform].concat(specs)
end
end
pod_installer = PodSourceInstaller.new(sandbox, specs_by_platform)
root_spec = specs_by_platform.values.flatten.first.root
local_path = root_spec.source[:local]
pod_installer.local_path = Pathname.new(local_path).expand_path if local_path# TODO
pod_installer.clean = config.clean?
pod_installer.aggressive_cache = config.aggressive_cache?
pod_installer.generate_docs = config.generate_docs?
pod_installer.install_docs = config.install_docs?
pod_installer.install!
@installed_specs.concat(specs_by_platform.values.flatten)
end
# Runs the pre install hooks of the installed specs and of the Podfile.
# #
# @todo Print a message with the names of the specs. # @return [void]
# #
def run_pre_install_hooks def run_pre_install_hooks
UI.message "- Running pre install hooks" do UI.message "- Running pre install hooks" do
local_pods_by_target.each do |target_definition, pods| installed_specs.each do |spec|
pods.each do |pod| pod_data = Hooks::PodData.new
pod.top_specification.pre_install!(pod, target_definition) pod_data.root = sandbox.pod_dir(spec.root.name)
end library_data = Hooks::LibraryData.new
executed = spec.pre_install!(pod_data, library_data)
UI.message "- #{spec.name}" if executed
end end
@podfile.pre_install!(self)
installer_data = Hooks::InstallerData.new
installer_data.project = pods_project
installer_data.sandbox = sandbox
installer_data.libraries = libraries
installer_data.pods = []
root_specs = analyzer.specifications.map { |spec| spec.root }.uniq
root_specs.each do |spec|
pod_data = Hooks::PodData.new
pod_data.root_spec = spec
pod_data.root = nil #TODO
installer_data.pods = [] << pod_data
end
executed = @podfile.pre_install!(installer_data)
UI.message "- Podfile" if executed
end end
end end
...@@ -377,21 +360,40 @@ module Pod ...@@ -377,21 +360,40 @@ module Pod
# #
def run_post_install_hooks def run_post_install_hooks
UI.message "- Running post install hooks" do UI.message "- Running post install hooks" do
target_installers.each do |target_installer| installed_specs.each do |spec|
target_installer.library.specs.each do |spec| target_installer_data = Hooks::TargetInstallerData.new
spec.post_install!(target_installer) target_installer_data.sandbox = sandbox
end target_installer_data.library = libraries.first #TODO
executed = spec.post_install!(target_installer_data)
UI.message "- #{spec.name}" if executed
end end
@podfile.post_install!(self) installer_data = Hooks::InstallerData.new
installer_data.project = pods_project
executed = @podfile.post_install!(installer_data)
UI.message "- Podfile" if executed
end end
end end
# Installs the file references in the Pods project. This is done once per
# Pod as the same file reference might be shared by multiple targets.
#
# @return [void]
#
def install_file_references
installer = FileReferencesInstaller.new(sandbox, libraries, pods_project)
installer.install!
end
# Installs the targets of the Pods projects and generates their support # Installs the targets of the Pods projects and generates their support
# files. # files.
# #
def generate_target_support_files # @return [void]
#
def install_targets
UI.message"- Installing targets" do UI.message"- Installing targets" do
target_installers.each do |target_installer| libraries.each do |library|
next if library.target_definition.empty?
target_installer = TargetInstaller.new(sandbox, library)
target_installer.install! target_installer.install!
end end
end end
...@@ -409,7 +411,7 @@ module Pod ...@@ -409,7 +411,7 @@ module Pod
end end
end end
# Writes the Podfile and the {Sandbox} lock files. # Writes the Podfile and the lock files.
# #
# @return [void] # @return [void]
# #
...@@ -425,12 +427,10 @@ module Pod ...@@ -425,12 +427,10 @@ module Pod
end end
end end
# Integrates the user project. # Integrates the user projects adding the dependencies on the CocoaPods
# # libraries, setting them up to use the xcconfigs and performing other
# The following actions are performed: # actions. This step is also reponsible of creating the workspace if
# - libraries are added. # needed.
# - the build script are added.
# - the xcconfig files are set.
# #
# @return [void] # @return [void]
# #
...@@ -439,77 +439,10 @@ module Pod ...@@ -439,77 +439,10 @@ module Pod
# In any case it appears to be a good idea store target definition # In any case it appears to be a good idea store target definition
# information in the lockfile. # information in the lockfile.
# #
# @todo [#588] The resources should be added through a build phase
# instead of using a script.
#
def integrate_user_project def integrate_user_project
return unless config.integrate_targets? installation_root = config.installation_root
UserProjectIntegrator.new(podfile, sandbox, config.project_root, analyzer.libraries).integrate! libraries = analyzer.libraries
end UserProjectIntegrator.new(podfile, sandbox, installation_root, libraries).integrate!
#-------------------------------------------------------------------------#
private
# @!group Helpers
# Downloads, clean and generates the documentation of a pod.
#
# @note The docs need to be generated before cleaning because the
# documentation is created for all the subspecs.
#
# @note In this step we clean also the Pods that have been pre-downloaded
# in AbstractExternalSource#specification_from_sandbox.
#
# @return [void]
#
def install_local_pod(pod)
unless sandbox.predownloaded_pods.include?(pod.name)
pod.implode
download_pod(pod)
end
generate_docs_if_needed(pod)
pod.clean! if config.clean?
end
# Downloads a Pod forcing the `bleeding edge' version if requested.
#
# @return [void]
#
# @todo Store the source of non specific downloads in the lockfile.
#
def download_pod(pod)
downloader = Downloader.for_target(pod.root, pod.top_specification.source.dup)
downloader.cache_root = "~/Library/Caches/CocoaPods"
downloader.max_cache_size = 500
downloader.agressive_cache = config.agressive_cache?
if pod.top_specification.version.head?
downloader.download_head
specific_source = downloader.checkout_options
else
downloader.download
specific_source = downloader.checkout_options if downloader.specific_options?
end
pod.downloaded = true
if specific_source
# store the specific source
end
end
# Generates the documentation of a Pod unless it exists for a given
# version.
#
# @return [void]
#
def generate_docs_if_needed(pod)
doc_generator = Generator::Documentation.new(pod)
if ( config.generate_docs? && !doc_generator.already_installed? )
UI.section " > Installing documentation"
doc_generator.generate(config.doc_install?)
else
UI.section " > Using existing documentation"
end
end end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
......
require File.expand_path('../../spec_helper', __FILE__) require File.expand_path('../../spec_helper', __FILE__)
# TODO add tests for multiple targets!
# @return [Lockfile] # @return [Lockfile]
# #
def generate_lockfile def generate_lockfile
...@@ -26,7 +28,7 @@ module Pod ...@@ -26,7 +28,7 @@ module Pod
# before do # before do
# @sandbox = temporary_sandbox # @sandbox = temporary_sandbox
# config.repos_dir = fixture('spec-repos') # config.repos_dir = fixture('spec-repos')
# config.project_pods_root = @sandbox.root # config.sandbox_root = @sandbox.root
# FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit') # FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
# end # end
# #
...@@ -38,7 +40,7 @@ module Pod ...@@ -38,7 +40,7 @@ module Pod
describe "Concerning pre-installation computations" do describe "Concerning pre-installation computations" do
# @sandbox = temporary_sandbox # @sandbox = temporary_sandbox
# config.project_pods_root = temporary_sandbox.root # config.sandbox_root = temporary_sandbox.root
# FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit') # FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
# resolver = Resolver.new(podfile, nil, @sandbox) # resolver = Resolver.new(podfile, nil, @sandbox)
...@@ -80,7 +82,7 @@ module Pod ...@@ -80,7 +82,7 @@ module Pod
# end # end
# @sandbox = temporary_sandbox # @sandbox = temporary_sandbox
# config.project_pods_root = temporary_sandbox.root # config.sandbox_root = temporary_sandbox.root
# FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit') # FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
# @installer = Installer.new(@sandbox, podfile) # @installer = Installer.new(@sandbox, podfile)
# target_installer = @installer.target_installers.first # target_installer = @installer.target_installers.first
...@@ -118,7 +120,7 @@ module Pod ...@@ -118,7 +120,7 @@ module Pod
# before do # before do
# sandbox = temporary_sandbox # sandbox = temporary_sandbox
# Config.instance.project_pods_root = sandbox.root # Config.instance.sandbox_root = sandbox.root
# Config.instance.integrate_targets = false # Config.instance.integrate_targets = false
# podspec_path = fixture('integration/Reachability/Reachability.podspec') # podspec_path = fixture('integration/Reachability/Reachability.podspec')
# podfile = Podfile.new do # podfile = Podfile.new do
...@@ -166,7 +168,7 @@ module Pod ...@@ -166,7 +168,7 @@ module Pod
# before do # before do
# sandbox = temporary_sandbox # sandbox = temporary_sandbox
# Config.instance.project_pods_root = sandbox.root # Config.instance.sandbox_root = sandbox.root
# Config.instance.integrate_targets = false # Config.instance.integrate_targets = false
# podspec_path = fixture('chameleon') # podspec_path = fixture('chameleon')
# podfile = Podfile.new do # podfile = Podfile.new do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment