Commit 697f8bd1 authored by Fabio Pelosin's avatar Fabio Pelosin

WIP

parent 6ca21338
...@@ -6,9 +6,6 @@ process do |files| ...@@ -6,9 +6,6 @@ process do |files|
specs = files.take_and_map do |file| specs = files.take_and_map do |file|
if file =~ %r{lib/cocoapods/(.+?)\.rb$} if file =~ %r{lib/cocoapods/(.+?)\.rb$}
s = Dir.glob("spec/**/#{File.basename(file, '.rb')}_spec.rb") s = Dir.glob("spec/**/#{File.basename(file, '.rb')}_spec.rb")
if file =~ %r{lib/cocoapods/installer.*\.rb$}
s.concat(['spec/unit/installer_spec.rb', 'spec/unit/installer/target_installer_spec.rb'])
end
s.uniq unless s.empty? s.uniq unless s.empty?
end end
end end
......
...@@ -113,10 +113,9 @@ module Pod ...@@ -113,10 +113,9 @@ module Pod
config.verbose config.verbose
config.skip_repo_update = true config.skip_repo_update = true
sandbox = Sandbox.new(config.project_pods_root) sandbox = Sandbox.new(config.project_pods_root)
resolver = Resolver.new(podfile, nil, sandbox) installer = Installer.new(sandbox, podfile)
installer = Installer.new(resolver)
installer.install! installer.install!
@pod = installer.pods.find { |pod| pod.top_specification == spec } @pod = installer.local_pods.find { |pod| pod.top_specification == spec }
config.silent config.silent
end end
......
module Pod module Pod
# The installer is the core of CocoaPods. This class is responsible of taking # The {Installer} is the core of CocoaPods. This class is responsible of
# a Podfile and transform it in the Pods libraries. This class also # taking a Podfile and transform it in the Pods libraries. This class also
# integrates the user project so the Pods libraries can be used out of the # integrates the user project so the Pods libraries can be used out of the
# box. # box.
# #
# The installer is capable of doing incremental updates to an existing Pod # The Installer is capable of doing incremental updates to an existing Pod
# installation. # installation.
# #
# The installer gets the information that it needs mainly from 3 files: # The Installer gets the information that it needs mainly from 3 files:
# #
# - Podfile: The specification written by the user that contains # - Podfile: The specification written by the user that contains
# information about targets and Pods. # information about targets and Pods.
...@@ -16,11 +16,11 @@ module Pod ...@@ -16,11 +16,11 @@ module Pod
# installed and in concert with the Podfile provides information about # installed and in concert with the Podfile provides information about
# which specific version of a Pod should be installed. This file is # which specific version of a Pod should be installed. This file is
# ignored in update mode. # ignored in update mode.
# - Pods.lock: A file contained in the Pods folder that keeps track # - Manifest.lock: A file contained in the Pods folder that keeps track of
# of the pods installed in the local machine. This files is used once # the pods installed in the local machine. This files is used once the
# the exact versions of the Pods has been computed to detect if that # exact versions of the Pods has been computed to detect if that version
# version is already installed. This file is not intended to be kept # is already installed. This file is not intended to be kept under source
# under source control and is a copy of the Podfile.lock. # control and is a copy of the Podfile.lock.
# #
# Once completed the installer should produce the following file structure: # Once completed the installer should produce the following file structure:
# #
...@@ -45,7 +45,7 @@ module Pod ...@@ -45,7 +45,7 @@ module Pod
# | +-- Pods-prefix.pch # | +-- Pods-prefix.pch
# | +-- PodsDummy_Pods.m # | +-- PodsDummy_Pods.m
# | # |
# +-- Pods.lock # +-- Manifest.lock
# | # |
# +-- Pods.xcodeproj # +-- Pods.xcodeproj
# #
...@@ -55,23 +55,27 @@ module Pod ...@@ -55,23 +55,27 @@ module Pod
include Config::Mixin include Config::Mixin
# @return [Sandbox] The sandbox where to install the Pods. # @return [Sandbox]
# the sandbox where the Pods should be installed.
# #
attr_reader :sandbox attr_reader :sandbox
# @return [Podfile] The Podfile specification that contains the # @return [Podfile]
# information of the Pods that should be installed. # the Podfile specification that contains the information of the Pods
# that should be installed.
# #
attr_reader :podfile attr_reader :podfile
# @return [Lockfile] The Lockfile that stores the information about the # @return [Lockfile]
# installed Pods. # the Lockfile that stores the information about the Pods previously
# installed on any machine.
# #
attr_reader :lockfile attr_reader :lockfile
# @return [Bool] Whether the installer is in update mode. In update # @return [Bool]
# mode the contents of the Lockfile are not taken into # whether the installer is in update mode. In update mode the contents of
# account for deciding what Pods to install. # the Lockfile are not taken into account for deciding what Pods to
# install.
# #
attr_reader :update_mode attr_reader :update_mode
...@@ -87,8 +91,10 @@ module Pod ...@@ -87,8 +91,10 @@ module Pod
@update_mode = update_mode @update_mode = update_mode
end end
# @return [void] The installation process of is mostly linear with few # Installs the Pods.
# minor complications to keep in mind: #
# The installation process of is mostly linear with few minor complications
# to keep in mind:
# #
# - The stored podspecs need to be cleaned before the resolution step # - The stored podspecs need to be cleaned before the resolution step
# otherwise the sandbox might return an old podspec and not download # otherwise the sandbox might return an old podspec and not download
...@@ -100,141 +106,159 @@ module Pod ...@@ -100,141 +106,159 @@ module Pod
# @note The order of the steps is very important and should be changed # @note The order of the steps is very important and should be changed
# carefully. # carefully.
# #
# TODO: # @return [void]
# #
def install! def install!
# TODO: prepare_for_legacy_compatibility analyze
compare_podfile_and_lockfile prepare_for_legacy_compatibility
clean_global_support_files clean_global_support_files
clean_removed_pods clean_removed_pods
clean_pods_to_install clean_pods_to_install
install_dependencies
install_targets
write_lockfiles
integrate_user_project
end
# Performs only the computation parts of an installation.
#
# It is used by the `outdated` subcommand.
#
# @return [void]
#
def analyze
generate_pods_by_podfile_state
update_repositories_if_needed update_repositories_if_needed
generate_locked_dependencies generate_locked_dependencies
resolve_dependencies resolve_dependencies
generate_local_pods
generate_pods_that_should_be_installed
end
# TODO: detect_installed_versions #---------------------------------------------------------------------------#
create_local_pods
detect_pods_to_install
install_dependencies
generate_support_files # @!group Analysis products
write_lockfile
# TODO: write_sandbox_lockfile
integrate_user_project public
end
# @return [void] the # @return [Array<String>]
# the names of the pods that were added to Podfile since the last
# installation on any machine.
# #
def dry_run attr_reader :pods_added_from_the_lockfile
end
# @return [Array<String>]
# the names of the pods whose version requirements in the Podfile are
# incompatible with the version stored in the lockfile.
#
attr_reader :pods_changed_from_the_lockfile
# @!group Prepare for legacy compatibility # @return [Array<String>]
# the names of the pods that were deleted from Podfile since the last
# installation on any machine.
#
attr_reader :pods_deleted_from_the_lockfile
# @return [void] In this step we prepare the Pods folder in order to be # @return [Array<String>]
# compatible with the most recent version of CocoaPods. # the names of the pods that didn't change since the last installation on
# any machine.
# #
# @note This step should be removed by version 1.0. attr_reader :pods_unchanged_from_the_lockfile
# @return [Array<Dependency>]
# the dependencies generate by the lockfile that prevent the resolver to
# update a Pod.
# #
def prepare_for_legacy_compatibility attr_reader :locked_dependencies
# move_target_support_files_if_needed
# copy_lock_file_to_Pods_lock_if_needed
# move_Local_Podspecs_to_Podspecs_if_needed
# move_pods_to_sources_folder_if_needed
end
# @return [Hash{TargetDefinition => Array<Spec>}]
# the specifications grouped by target as identified in the
# resolve_dependencies step.
#
attr_reader :specs_by_target
# @return [Array<Specification>]
# the specifications of the resolved version of Pods that should be
# installed.
#
attr_reader :specifications
# @!group Detect Podfile changes step # @return [Hash{TargetDefinition => Array<LocalPod>}]
# the local pod instances grouped by target.
#
attr_reader :local_pods_by_target
# @return [Hash{Symbol => Array<Spec>}] The name of the pods directly # @return [Array<LocalPod>]
# specified in the Podfile grouped by a symbol representing their state # the list of LocalPod instances for each dependency sorted by name.
# (added, changed, removed, unchanged) as identified by the {Lockfile}.
# #
attr_reader :pods_by_state attr_reader :local_pods
# @return [void] In this step the podfile is compared with the lockfile in # @return [Array<String>]
# order to detect which dependencies should be locked. # the Pods that should be installed.
# #
# #TODO: If there is not lockfile all the Pods should be marked as added. attr_reader :pods_to_install
# #TODO: This should use the Pods.lock file because they are used by the
# to detect what needs to be installed. #---------------------------------------------------------------------------#
# @!group Installation products
public
# @return [Pod::Project]
# the `Pods/Pods.xcodeproj` project.
# #
def compare_podfile_and_lockfile attr_reader :pods_project
if lockfile
UI.section "Finding added, modified or removed dependencies:" do
@pods_by_state = lockfile.detect_changes_with_podfile(podfile)
display_dependencies_state
end
else
@pods_by_state = {}
end
end
# @return [void] Displays the state of each dependency. # @return [Array<TargetInstaller>]
# #
def display_dependencies_state attr_reader :target_installers
return unless config.verbose?
marks = { :added => "A".green,
:changed => "M".yellow,
:removed => "R".red,
:unchanged => "-" }
pods_by_state.each do |symbol, pod_names|
pod_names.each do |pod_name|
UI.message("#{marks[symbol]} #{pod_name}", '',2)
end
end
end
#---------------------------------------------------------------------------#
# @!group Pre-installation computations
# @!group Cleaning steps private
# @return [void] In this step we clean all the folders that will be # Compares the {Podfile} with the {Lockfile} in order to detect which
# regenerated from scratch and any file which might not be overwritten. # dependencies should be locked.
# #
# @TODO: Clean the podspecs of all the pods that aren't unchanged so the # @return [void]
# resolution process doesn't get confused by them.
# #
def clean_global_support_files # TODO: If there is not Lockfile all the Pods should be marked as added.
sandbox.prepare_for_install
end
# @return [void] In this step we clean all the files related to the removed
# Pods.
# #
# @TODO: Use the local pod implode. # TODO: Once the manifest.lock is implemented only the unchanged pods
# @TODO: [#534] Clean all the Pods folder that are not unchanged? # should be tracked.
# #
def clean_removed_pods def generate_pods_by_podfile_state
UI.section "Removing deleted dependencies" do if lockfile
pods_by_state[:removed].each do |pod_name| UI.section "Finding added, modified or removed dependencies:" do
UI.section("Removing #{pod_name}", "-> ".red) do pods_by_state = lockfile.detect_changes_with_podfile(podfile)
path = sandbox.root + pod_name @pods_added_from_the_lockfile = pods_by_state[:added] || []
path.rmtree if path.exist? @pods_deleted_from_the_lockfile = pods_by_state[:removed] || []
end @pods_changed_from_the_lockfile = pods_by_state[:changed] || []
@pods_unchanged_from_the_lockfile = pods_by_state[:unchanged] || []
display_pods_by_lockfile_state
end end
end unless pods_by_state[:removed].empty? else
@pods_added_from_the_lockfile = []
@pods_deleted_from_the_lockfile = []
@pods_changed_from_the_lockfile = []
@pods_unchanged_from_the_lockfile = []
end
end end
# @return [void] In this step we clean the files of the Pods that will be # Displays the state of each dependency.
# installed. We clean the files that might affect the resolution process
# and the files that might not be overwritten.
# #
# @TODO: [#247] Clean the headers of only the pods to install. # @return [void]
# #
def clean_pods_to_install def display_pods_by_lockfile_state
return unless config.verbose?
pods_added_from_the_lockfile .each { |pod| UI.message("A".green + "#{pod}", '', 2) }
pods_deleted_from_the_lockfile .each { |pod| UI.message("R".red + "#{pod}", '', 2) }
pods_changed_from_the_lockfile .each { |pod| UI.message("M".yellow + "#{pod}", '', 2) }
pods_unchanged_from_the_lockfile .each { |pod| UI.message("-" + "#{pod}", '', 2) }
end end
# @!group Generate locked dependencies step
# @return [void] Lazily updates the source repositories. The update is # @return [void] Lazily updates the source repositories. The update is
# triggered if: # triggered if:
# - There are pods that changed in the Podfile. # - There are pods that changed in the Podfile.
...@@ -244,56 +268,52 @@ module Pod ...@@ -244,56 +268,52 @@ module Pod
# TODO: Remove the lockfile condition once compare_podfile_and_lockfile # TODO: Remove the lockfile condition once compare_podfile_and_lockfile
# is updated. # is updated.
# #
# TODO: Lazy resolution can't be done if we want to fully support detection
# of changes in specifications checksum.
#
def update_repositories_if_needed def update_repositories_if_needed
return if config.skip_repo_update? return if config.skip_repo_update?
changed_pods = (pods_by_state[:added] + pods_by_state[:changed]) changed_pods = (pods_changed_from_the_lockfile + pods_deleted_from_the_lockfile)
UI.section 'Updating spec repositories' do should_update = !lockfile || !changed_pods.empty? || update_mode
Command::Repo.new(Command::ARGV.new(["update"])).run if should_update
end if !lockfile || !changed_pods.empty? || update_mode UI.section 'Updating spec repositories' do
Command::Repo.new(Command::ARGV.new(["update"])).run
end
end
end end
# @!group Generate locked dependencies step # Generates dependencies that require the specific version of the Pods that
# haven't changed in the {Lockfile}.
# @return [Array<Specification>] All dependencies that have been resolved.
# #
attr_reader :locked_dependencies # These dependencies are passed to the {Resolver}, unless the installer is
# in update mode, to prevent it from upgrading the Pods that weren't
# @return [void] In this step we generate the dependencies of necessary to # changed in the {Podfile}.
# prevent the resolver from updating the pods which are in unchanged #
# state. The Podfile is compared to the Podfile.lock to detect what # @return [void]
# version of a dependency should be locked.
# #
def generate_locked_dependencies def generate_locked_dependencies
if update_mode @locked_dependencies = pods_unchanged_from_the_lockfile.map do |pod|
@locked_dependencies = [] lockfile.dependency_for_installed_pod_named(pod)
else
@locked_dependencies = pods_by_state[:unchanged].map do |pod_name|
lockfile.dependency_for_installed_pod_named(pod_name)
end
end end
end end
# Converts the Podfile in a list of specifications grouped by target.
# @!group Resolution steps
# @return [Hash{Podfile::TargetDefinition => Array<Spec>}]
# The specifications grouped by target as identified in
# the resolve_dependencies step.
# #
attr_reader :specs_by_target # @note As some dependencies might have external sources the resolver is
# aware of the {Sandbox} and interacts with it to download the
# @return [Array<Specification>] All dependencies that have been resolved. # podspecs of the external sources. This is necessary because the
# resolver needs the specifications to analyze their dependencies
# (which might be from external sources).
# #
attr_reader :specifications # @note In update mode the resolver is set to always update the specs from
# external sources.
# @return [void] Converts the Podfile in a list of specifications grouped
# by target.
# #
# In update mode the specs from external sources are always downloaded. # @return [void]
# #
def resolve_dependencies def resolve_dependencies
UI.section "Resolving dependencies of #{UI.path podfile.defined_in_file}" do UI.section "Resolving dependencies of #{UI.path podfile.defined_in_file}" do
resolver = Resolver.new(sandbox, podfile, locked_dependencies) locked_deps = update_mode ? [] : locked_dependencies
resolver = Resolver.new(sandbox, podfile, locked_deps)
resolver.update_external_specs = update_mode resolver.update_external_specs = update_mode
@specs_by_target = resolver.resolve @specs_by_target = resolver.resolve
@specifications = specs_by_target.values.flatten @specifications = specs_by_target.values.flatten
...@@ -301,27 +321,25 @@ module Pod ...@@ -301,27 +321,25 @@ module Pod
end end
# Computes the list of the Pods that should be installed or reinstalled in
# @!group Detect Pods to install step # the {Sandbox}.
# @return [Array<String>] The names of the Pods that should be installed.
# #
attr_reader :pods_to_install # The pods to install are identified as the Pods that don't exist in the
# sandbox or the Pods whose version differs from the one of the lockfile.
# @return [<void>] In this step the pods to install are detected. #
# The pods to install are identified as the Pods that don't exist in the # In update mode specs originating from external dependencies and or from
# sandbox or the Pods whose version differs from the one of the lockfile. # head sources are always reinstalled.
# #
# In update mode specs originating from external dependencies and or from # @return [void]
# head sources are always reinstalled.
# #
# TODO: Decide a how the Lockfile should report versions. # TODO: Use {Sandbox} manifest.
# TODO: [#534] Detect if the folder of a Pod is empty.
# #
def detect_pods_to_install # TODO: [#534] Detect if the folder of a Pod is empty.
#
def generate_pods_that_should_be_installed
changed_pods_names = [] changed_pods_names = []
if lockfile if lockfile
changed_pods = pods.select do |pod| changed_pods = local_pods.select do |pod|
pod.top_specification.version != lockfile.pods_versions[pod.name] pod.top_specification.version != lockfile.pods_versions[pod.name]
end end
if update_mode if update_mode
...@@ -330,42 +348,33 @@ module Pod ...@@ -330,42 +348,33 @@ module Pod
resolver.pods_from_external_sources.include?(pod.name) resolver.pods_from_external_sources.include?(pod.name)
end end
end end
changed_pods_names += @pods_by_state[:added] + @pods_by_state[:changed] changed_pods_names += pods_added_from_the_lockfile + pods_changed_from_the_lockfile
else else
changed_pods = pods changed_pods = local_pods
end end
not_existing_pods = pods.reject { |pod| pod.exists? } not_existing_pods = local_pods.reject { |pod| pod.exists? }
@pods_to_install = (changed_pods + not_existing_pods).uniq @pods_to_install = (changed_pods + not_existing_pods).uniq
end end
# Converts the specifications produced by the Resolver in local pods.
# @!group Install step
# @return [Hash{Podfile::TargetDefinition => Array<LocalPod>}]
# #
attr_reader :pods_by_target # The LocalPod class is responsible to handle the concrete representation
# of a specification in the {Sandbox}.
# @return [Array<LocalPod>] A list of LocalPod instances for each #
# dependency sorted by name. # @return [void]
# (that is not a download-only one?)
attr_reader :pods
# @return [void] In this step the specifications obtained by the resolver
# are converted in local pods. The LocalPod class is responsible to
# handle the concrete representation of a specification a sandbox.
# #
# @TODO: [#535] Pods should be accumulated per Target, also in the Local # TODO: [#535] Pods should be accumulated per Target, also in the Local
# Pod class. The Local Pod class should have a method to add itself # Pod class. The Local Pod class should have a method to add itself
# to a given project so it can use the sources of all the activated # to a given project so it can use the sources of all the activated
# podspecs across all targets. Also cleaning should take into # podspecs across all targets. Also cleaning should take into account
# account that. # that.
# #
def create_local_pods def generate_local_pods
@pods_by_target = {} @local_pods_by_target = {}
specs_by_target.each do |target_definition, specs| specs_by_target.each do |target_definition, specs|
@pods_by_target[target_definition] = specs.map do |spec| @local_pods_by_target[target_definition] = specs.map do |spec|
if spec.local? if spec.local?
sandbox.locally_sourced_pod_for_spec(spec, target_definition.platform) sandbox.locally_sourced_pod_for_spec(spec, target_definition.platform)
else else
...@@ -374,12 +383,63 @@ module Pod ...@@ -374,12 +383,63 @@ module Pod
end.uniq.compact end.uniq.compact
end end
@pods = pods_by_target.values.flatten.uniq.sort_by { |pod| pod.name.downcase } @local_pods = local_pods_by_target.values.flatten.uniq.sort_by { |pod| pod.name.downcase }
end end
#---------------------------------------------------------------------------#
# @!group Installation
private
# Prepares the Pods folder in order to be compatible with the most recent
# version of CocoaPods.
#
# @return [void]
#
def prepare_for_legacy_compatibility
# move_target_support_files_if_needed
# copy_lock_file_to_Pods_lock_if_needed
# move_Local_Podspecs_to_Podspecs_if_needed
# move_pods_to_sources_folder_if_needed
end
# @return [void] In this step we clean all the folders that will be
# regenerated from scratch and any file which might not be overwritten.
#
# @TODO: Clean the podspecs of all the pods that aren't unchanged so the
# resolution process doesn't get confused by them.
#
def clean_global_support_files
sandbox.prepare_for_install
end
# @!group Install step # @return [void] In this step we clean all the files related to the removed
# Pods.
#
# @TODO: Use the local pod implode.
# @TODO: [#534] Clean all the Pods folder that are not unchanged?
#
def clean_removed_pods
UI.section "Removing deleted dependencies" do
pods_deleted_from_the_lockfile.each do |pod_name|
UI.section("Removing #{pod_name}", "-> ".red) do
path = sandbox.root + pod_name
path.rmtree if path.exist?
end
end
end unless pods_deleted_from_the_lockfile.empty?
end
# @return [void] In this step we clean the files of the Pods that will be
# installed. We clean the files that might affect the resolution process
# and the files that might not be overwritten.
#
# @TODO: [#247] Clean the headers of only the pods to install.
#
def clean_pods_to_install
end
# @return [void] Install the Pods. If the resolver indicated that a Pod # @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 # should be installed and it exits, it is removed an then reinstalled. In
...@@ -387,7 +447,7 @@ module Pod ...@@ -387,7 +447,7 @@ module Pod
# #
def install_dependencies def install_dependencies
UI.section "Downloading dependencies" do UI.section "Downloading dependencies" do
pods.each do |pod| local_pods.each do |pod|
if pods_to_install.include?(pod) if pods_to_install.include?(pod)
UI.section("Installing #{pod}".green, "-> ".green) do UI.section("Installing #{pod}".green, "-> ".green) do
install_local_pod(pod) install_local_pod(pod)
...@@ -454,18 +514,17 @@ module Pod ...@@ -454,18 +514,17 @@ module Pod
end end
end end
# Creates and populates the targets of the pods project.
# @!group Generate Pods project and support files step
# @return [void] Creates and populates the targets of the pods project.
# #
# @note Post install hooks run _before_ saving of project, so that they can # @note Post install hooks run _before_ saving of project, so that they can
# alter it before saving. # alter it before saving.
# #
def generate_support_files # @return [void]
#
def install_targets
UI.section "Generating support files" do UI.section "Generating support files" do
prepare_pods_project prepare_pods_project
generate_target_installers
add_source_files_to_pods_project add_source_files_to_pods_project
run_pre_install_hooks run_pre_install_hooks
generate_target_support_files generate_target_support_files
...@@ -474,13 +533,11 @@ module Pod ...@@ -474,13 +533,11 @@ module Pod
end end
end end
# @return [Project] The Pods project. # Creates the Pods project from scratch if it doesn't exists.
# #
attr_reader :pods_project # TODO clean and modify the project if it exists.
#
# @return [void] In this step we create the Pods project from scratch if it # @return [void]
# doesn't exists. If the Pods project exists instead we clean it and
# prepare it for installation.
# #
def prepare_pods_project def prepare_pods_project
UI.message "- Creating Pods project" do UI.message "- Creating Pods project" do
...@@ -489,31 +546,40 @@ module Pod ...@@ -489,31 +546,40 @@ module Pod
end end
end end
# @return [void] In this step we add the source files of the Pods to the # Creates a target installer for each definition not empty.
# Pods project. The source files are grouped by Pod and in turn by subspec #
# (recursively). Pods are generally added to the Pods group. However, if # @return [void]
# they are local they are added to the Local Pods group. #
def generate_target_installers
@target_installers = podfile.target_definitions.values.map do |definition|
TargetInstaller.new(podfile, pods_project, definition) unless definition.empty?
end.compact
end
# Adds the source files of the Pods to the Pods project.
# #
# @TODO [#143] This step is quite slow and should be made incremental by # The source files are grouped by Pod and in turn by subspec
# modifying only the files of the changed pods. Xcodeproj deletion # (recursively). Pods are generally added to the `Pods` group, however, if
# and sorting of folders is required. # they have a local source they are added to the `Local Pods` group.
#
# @return [void]
#
# TODO Clean the groups of the deleted Pods and add only the Pods that
# should be installed.
#
# TODO [#588] Add file references for the resources of the Pods as well so
# they are visible for the user.
# #
def add_source_files_to_pods_project def add_source_files_to_pods_project
UI.message "- Adding source files to Pods project" do UI.message "- Adding source files to Pods project" do
pods.each { |p| p.add_file_references_to_project(@project) } local_pods.each { |p| p.add_file_references_to_project(pods_project) }
pods.each { |p| p.link_headers } local_pods.each { |p| p.link_headers }
end end
end end
def target_installers
@target_installers ||= podfile.target_definitions.values.map do |definition|
TargetInstaller.new(podfile, pods_project, definition) unless definition.empty?
end.compact
end
def run_pre_install_hooks def run_pre_install_hooks
UI.message "- Running pre install hooks" do UI.message "- Running pre install hooks" do
pods_by_target.each do |target_definition, pods| local_pods_by_target.each do |target_definition, pods|
pods.each do |pod| pods.each do |pod|
pod.top_specification.pre_install(pod, target_definition) pod.top_specification.pre_install(pod, target_definition)
end end
...@@ -538,7 +604,7 @@ module Pod ...@@ -538,7 +604,7 @@ module Pod
def generate_target_support_files def generate_target_support_files
UI.message"- Installing targets" do UI.message"- Installing targets" do
target_installers.each do |target_installer| target_installers.each do |target_installer|
pods_for_target = pods_by_target[target_installer.target_definition] pods_for_target = local_pods_by_target[target_installer.target_definition]
target_installer.install!(pods_for_target, sandbox) target_installer.install!(pods_for_target, sandbox)
acknowledgements_path = target_installer.target_definition.acknowledgements_path acknowledgements_path = target_installer.target_definition.acknowledgements_path
Generator::Acknowledgements.new(target_installer.target_definition, Generator::Acknowledgements.new(target_installer.target_definition,
...@@ -554,41 +620,51 @@ module Pod ...@@ -554,41 +620,51 @@ module Pod
filename = "#{dummy_source.class_name}.m" filename = "#{dummy_source.class_name}.m"
pathname = Pathname.new(sandbox.root + filename) pathname = Pathname.new(sandbox.root + filename)
dummy_source.save_as(pathname) dummy_source.save_as(pathname)
file = project.new_file(filename, "Targets Support Files") file = pods_project.new_file(filename, "Targets Support Files")
target_installer.target.source_build_phase.add_file_reference(file) target_installer.target.source_build_phase.add_file_reference(file)
end end
# Writes the Pods project to the disk.
#
# @return [void]
#
def write_pod_project def write_pod_project
UI.message "- Writing Xcode project file to #{UI.path @sandbox.project_path}" do UI.message "- Writing Xcode project file to #{UI.path @sandbox.project_path}" do
pods_project.save_as(@sandbox.project_path) pods_project.save_as(@sandbox.project_path)
end end
end end
# Writes the Podfile and the {Sandbox} lock files.
#
# @!group Lockfile related steps # @return [void]
#
def write_lockfile # TODO: [#552] Implement
#
def write_lockfiles
@lockfile = Lockfile.generate(podfile, specs_by_target.values.flatten)
UI.message "- Writing Lockfile in #{UI.path config.project_lockfile}" do UI.message "- Writing Lockfile in #{UI.path config.project_lockfile}" do
@lockfile = Lockfile.generate(podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk(config.project_lockfile) @lockfile.write_to_disk(config.project_lockfile)
end end
end
# @TODO: [#552] Implement
#
def write_sandbox_lockfile
# UI.message "- Writing Manifest in #{UI.path sandbox.manifest_path}" do
# @lockfile.write_to_disk(sandbox.manifest_path)
# end
end end
# @!group Integrate user project step # Integrates the user project.
#
# @return [void] In this step the user project is integrated. The Pods # The following actions are performed:
# libraries are added, the build script are added, and the xcconfig files # - libraries are added.
# are set. # - the build script are added.
# - the xcconfig files are set.
#
# @return [void]
#
# TODO: [#397] The libraries should be cleaned and the re-added on every
# installation. Maybe a clean_user_project phase should be added.
# #
# @TODO: [#397] The libraries should be cleaned and the re-added on every # TODO: [#588] The resources should be added through a build phase instead
# install. Maybe a clean_user_project phase should be added. # of using a script.
# #
def integrate_user_project def integrate_user_project
UserProjectIntegrator.new(podfile).integrate! if config.integrate_targets? UserProjectIntegrator.new(podfile).integrate! if config.integrate_targets?
......
...@@ -70,8 +70,7 @@ else ...@@ -70,8 +70,7 @@ else
end end
# Note that we are *not* using the stubbed SpecHelper::Installer subclass. # Note that we are *not* using the stubbed SpecHelper::Installer subclass.
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root)) installer = Pod::Installer.new(Pod::Sandbox.new(config.project_pods_root), podfile)
installer = Pod::Installer.new(resolver)
installer.install! installer.install!
result = installer.lockfile.to_hash result = installer.lockfile.to_hash
result['PODS'].should == ['SSToolkit (0.1.3)'] result['PODS'].should == ['SSToolkit (0.1.3)']
...@@ -88,8 +87,7 @@ else ...@@ -88,8 +87,7 @@ else
pod 'Reachability', :podspec => url pod 'Reachability', :podspec => url
end end
resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root)) installer = SpecHelper::Installer.new(Pod::Sandbox.new(config.project_pods_root), podfile)
installer = SpecHelper::Installer.new(resolver)
installer.install! installer.install!
result = installer.lockfile.to_hash result = installer.lockfile.to_hash
result['PODS'].should == ['Reachability (1.2.3)'] result['PODS'].should == ['Reachability (1.2.3)']
......
...@@ -2,200 +2,233 @@ require File.expand_path('../../spec_helper', __FILE__) ...@@ -2,200 +2,233 @@ require File.expand_path('../../spec_helper', __FILE__)
module Pod module Pod
describe Installer do describe Installer do
before do before do
@sandbox = temporary_sandbox
config.repos_dir = fixture('spec-repos') config.repos_dir = fixture('spec-repos')
config.project_pods_root = @sandbox.root
FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
end end
describe "by default" do describe "Concerning pre-installation computations" do
before do def generate_podfile(pods = ['JSONKit'])
podfile = Podfile.new do
platform :ios
xcodeproj 'MyProject'
pod 'JSONKit'
end
@sandbox = temporary_sandbox
config.project_pods_root = temporary_sandbox.root
FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
@installer = Installer.new(sandbox, podfile)
target_installer = @installer.target_installers.first
target_installer.generate_xcconfig([], @sandbox)
@xcconfig = target_installer.xcconfig.to_hash
end
it "sets the header search paths where installed Pod headers can be found" do
@xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
end
it "configures the project to load all members that implement Objective-c classes or categories from the static library" do
@xcconfig['OTHER_LDFLAGS'].should == '-ObjC'
end
it "sets the PODS_ROOT build variable" do
@xcconfig['PODS_ROOT'].should.not == nil
end
it "generates a BridgeSupport metadata file from all the pod headers" do
podfile = Podfile.new do
platform :osx
pod 'ASIHTTPRequest'
end
FileUtils.cp_r(fixture('integration/ASIHTTPRequest'), @sandbox.root + 'ASIHTTPRequest')
resolver = Resolver.new(podfile, nil, @sandbox)
installer = Installer.new(resolver)
pods = installer.specifications.map do |spec|
LocalPod.new(spec, installer.sandbox, podfile.target_definitions[:default].platform)
end
expected = pods.map { |pod| pod.header_files }.flatten.map { |header| config.project_pods_root + header }
expected.size.should > 0
installer.target_installers.first.bridge_support_generator_for(pods, installer.sandbox).headers.should == expected
end
it "omits empty target definitions" do
podfile = Podfile.new do
platform :ios
target :not_empty do
pod 'JSONKit'
end
end
resolver = Resolver.new(podfile, nil, @sandbox)
installer = Installer.new(resolver)
installer.target_installers.map(&:target_definition).map(&:name).should == [:not_empty]
end
it "adds the user's build configurations" do
path = fixture('SampleProject/SampleProject.xcodeproj')
podfile = Podfile.new do
platform :ios
xcodeproj path, 'App Store' => :release
end
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
installer = Installer.new(resolver)
installer.project.build_configurations.map(&:name).sort.should == ['App Store', 'Debug', 'Release', 'Test']
end
it "forces downloading of the `bleeding edge' version of a pod" do
podfile = Podfile.new do
platform :ios
pod 'JSONKit', :head
end
resolver = Resolver.new(podfile, nil, Sandbox.new(fixture('integration')))
installer = Installer.new(resolver)
pod = installer.pods.first
downloader = stub('Downloader')
Downloader.stubs(:for_pod).returns(downloader)
downloader.expects(:download_head)
installer.download_pod(pod)
end
end
describe "concerning xcconfig files generation" do
before do
podfile = Podfile.new do podfile = Podfile.new do
platform :ios platform :ios
xcodeproj 'MyProject' xcodeproj 'MyProject'
pod 'JSONKit' pods.each { |name| pod name }
end end
sandbox = Sandbox.new(fixture('integration'))
installer = Installer.new(sandbox, podfile)
@xcconfig = installer.target_installers.first.xcconfig.to_hash
end
it "sets the header search paths where installed Pod headers can be found" do
@xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
end
it "configures the project to load all members that implement Objective-c classes or categories from the static library" do
@xcconfig['OTHER_LDFLAGS'].should == '-ObjC'
end end
it "sets the PODS_ROOT build variable" do def generate_lockfile
@xcconfig['PODS_ROOT'].should.not == nil hash = {}
hash['PODS'] = []
hash['DEPENDENCIES'] = []
hash['SPEC CHECKSUMS'] = []
hash['COCOAPODS'] = Pod::VERSION
Pod::Lockfile.new(hash)
end end
end
describe "concerning multiple pods originating form the same spec" do
extend SpecHelper::Fixture
before do before do
sandbox = temporary_sandbox podfile = generate_podfile
Pod::Config.instance.project_pods_root = sandbox.root lockfile = generate_lockfile
Pod::Config.instance.integrate_targets = false @installer = Installer.new(@sandbox, podfile, lockfile)
podspec_path = fixture('integration/Reachability/Reachability.podspec')
podfile = Podfile.new do
platform :osx
pod 'Reachability', :podspec => podspec_path.to_s
target :debug do
pod 'Reachability'
end
end
resolver = Resolver.new(podfile, nil, sandbox)
@installer = Installer.new(resolver)
end
# The double installation leads to a bug when different subspecs are
# activated for the same pod. We need a way to install a pod only
# once while keeping all the files of the actived subspecs.
#
# LocalPodSet?
#
it "installs the pods only once" do
LocalPod.any_instance.stubs(:downloaded?).returns(false)
Downloader::GitHub.any_instance.expects(:download).once
@installer.install! @installer.install!
end end
it "cleans a pod only once" do # describe "#analyze" do
LocalPod.any_instance.expects(:clean!).once # it "doesn't affects creates changes in the file system" do
@installer.install! # end
end # end
it "adds the files of the pod to the Pods project only once" do it "marks all pods as added if there is no lockfile" do
@installer.install! @installer.pods_added_from_the_lockfile.should == ['JSONKit']
group = @installer.project.pods.groups.find { |g| g.name == 'Reachability' }
group.files.map(&:name).should == ["Reachability.h", "Reachability.m"]
end end
it "lists a pod only once" do
reachability_pods = @installer.pods.map(&:to_s).select { |s| s.include?('Reachability') }
reachability_pods.count.should == 1
end
end end
describe "concerning namespacing" do # describe "by default" do
extend SpecHelper::Fixture # before do
# podfile = Podfile.new do
before do # platform :ios
sandbox = temporary_sandbox # xcodeproj 'MyProject'
Pod::Config.instance.project_pods_root = sandbox.root # pod 'JSONKit'
Pod::Config.instance.integrate_targets = false # end
podspec_path = fixture('chameleon')
podfile = Podfile.new do # @sandbox = temporary_sandbox
platform :osx # config.project_pods_root = temporary_sandbox.root
pod 'Chameleon', :local => podspec_path # FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
end # @installer = Installer.new(@sandbox, podfile)
resolver = Resolver.new(podfile, nil, sandbox) # target_installer = @installer.target_installers.first
@installer = Installer.new(resolver) # target_installer.generate_xcconfig([], @sandbox)
end # @xcconfig = target_installer.xcconfig.to_hash
# end
it "namespaces local pods" do
@installer.install! # it "sets the header search paths where installed Pod headers can be found" do
group = @installer.project['Local Pods'] # @xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
group.groups.map(&:name).sort.should == %w| Chameleon | # end
end
# it "configures the project to load all members that implement Objective-c classes or categories from the static library" do
it "namespaces subspecs" do # @xcconfig['OTHER_LDFLAGS'].should == '-ObjC'
@installer.install! # end
group = @installer.project['Local Pods/Chameleon']
group.groups.map(&:name).sort.should == %w| AVFoundation AssetsLibrary MediaPlayer MessageUI StoreKit UIKit | # it "sets the PODS_ROOT build variable" do
end # @xcconfig['PODS_ROOT'].should.not == nil
end # end
# it "generates a BridgeSupport metadata file from all the pod headers" do
# podfile = Podfile.new do
# platform :osx
# pod 'ASIHTTPRequest'
# end
# FileUtils.cp_r(fixture('integration/ASIHTTPRequest'), @sandbox.root + 'ASIHTTPRequest')
# installer = Installer.new(@sandbox, podfile)
# pods = installer.specifications.map do |spec|
# LocalPod.new(spec, installer.sandbox, podfile.target_definitions[:default].platform)
# end
# expected = pods.map { |pod| pod.header_files }.flatten.map { |header| config.project_pods_root + header }
# expected.size.should > 0
# installer.target_installers.first.bridge_support_generator_for(pods, installer.sandbox).headers.should == expected
# end
# it "omits empty target definitions" do
# podfile = Podfile.new do
# platform :ios
# target :not_empty do
# pod 'JSONKit'
# end
# end
# installer = Installer.new(@sandbox, podfile)
# installer.target_installers.map(&:target_definition).map(&:name).should == [:not_empty]
# end
# it "adds the user's build configurations" do
# path = fixture('SampleProject/SampleProject.xcodeproj')
# podfile = Podfile.new do
# platform :ios
# xcodeproj path, 'App Store' => :release
# end
# installer = Installer.new(@sandbox, podfile)
# installer.project.build_configurations.map(&:name).sort.should == ['App Store', 'Debug', 'Release', 'Test']
# end
# it "forces downloading of the `bleeding edge' version of a pod" do
# podfile = Podfile.new do
# platform :ios
# pod 'JSONKit', :head
# end
# installer = Installer.new(@sandbox, podfile)
# pod = installer.pods.first
# downloader = stub('Downloader')
# Downloader.stubs(:for_pod).returns(downloader)
# downloader.expects(:download_head)
# installer.download_pod(pod)
# end
# end
# describe "concerning xcconfig files generation" do
# before do
# podfile = Podfile.new do
# platform :ios
# xcodeproj 'MyProject'
# pod 'JSONKit'
# end
# sandbox = Sandbox.new(fixture('integration'))
# installer = Installer.new(sandbox, podfile)
# @xcconfig = installer.target_installers.first.xcconfig.to_hash
# end
# it "sets the header search paths where installed Pod headers can be found" do
# @xcconfig['ALWAYS_SEARCH_USER_PATHS'].should == 'YES'
# end
# it "configures the project to load all members that implement Objective-c classes or categories from the static library" do
# @xcconfig['OTHER_LDFLAGS'].should == '-ObjC'
# end
# it "sets the PODS_ROOT build variable" do
# @xcconfig['PODS_ROOT'].should.not == nil
# end
# end
# describe "concerning multiple pods originating form the same spec" do
# extend SpecHelper::Fixture
# before do
# sandbox = temporary_sandbox
# Pod::Config.instance.project_pods_root = sandbox.root
# Pod::Config.instance.integrate_targets = false
# podspec_path = fixture('integration/Reachability/Reachability.podspec')
# podfile = Podfile.new do
# platform :osx
# pod 'Reachability', :podspec => podspec_path.to_s
# target :debug do
# pod 'Reachability'
# end
# end
# resolver = Resolver.new(podfile, nil, sandbox)
# @installer = Installer.new(resolver)
# end
# # The double installation leads to a bug when different subspecs are
# # activated for the same pod. We need a way to install a pod only
# # once while keeping all the files of the actived subspecs.
# #
# # LocalPodSet?
# #
# it "installs the pods only once" do
# LocalPod.any_instance.stubs(:downloaded?).returns(false)
# Downloader::GitHub.any_instance.expects(:download).once
# @installer.install!
# end
# it "cleans a pod only once" do
# LocalPod.any_instance.expects(:clean!).once
# @installer.install!
# end
# it "adds the files of the pod to the Pods project only once" do
# @installer.install!
# group = @installer.project.pods.groups.find { |g| g.name == 'Reachability' }
# group.files.map(&:name).should == ["Reachability.h", "Reachability.m"]
# end
# it "lists a pod only once" do
# reachability_pods = @installer.pods.map(&:to_s).select { |s| s.include?('Reachability') }
# reachability_pods.count.should == 1
# end
# end
# describe "concerning namespacing" do
# extend SpecHelper::Fixture
# before do
# sandbox = temporary_sandbox
# Pod::Config.instance.project_pods_root = sandbox.root
# Pod::Config.instance.integrate_targets = false
# podspec_path = fixture('chameleon')
# podfile = Podfile.new do
# platform :osx
# pod 'Chameleon', :local => podspec_path
# end
# resolver = Resolver.new(podfile, nil, sandbox)
# @installer = Installer.new(resolver)
# end
# it "namespaces local pods" do
# @installer.install!
# group = @installer.project['Local Pods']
# group.groups.map(&:name).sort.should == %w| Chameleon |
# end
# it "namespaces subspecs" do
# @installer.install!
# group = @installer.project['Local Pods/Chameleon']
# group.groups.map(&:name).sort.should == %w| AVFoundation AssetsLibrary MediaPlayer MessageUI StoreKit UIKit |
# end
# end
end end
end end
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