Permalink
Browse files

WIP

  • Loading branch information...
1 parent 6ca2133 commit 697f8bd164eb0b2ddf8d37565b50da33b982a966 @fabiopelosin fabiopelosin committed Oct 21, 2012
Showing with 523 additions and 420 deletions.
  1. +0 −3 .kick
  2. +2 −3 lib/cocoapods/command/linter.rb
  3. +312 −236 lib/cocoapods/installer.rb
  4. +2 −4 spec/integration_spec.rb
  5. +207 −174 spec/unit/installer_spec.rb
View
3 .kick
@@ -6,9 +6,6 @@ process do |files|
specs = files.take_and_map do |file|
if file =~ %r{lib/cocoapods/(.+?)\.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?
end
end
View
5 lib/cocoapods/command/linter.rb
@@ -113,10 +113,9 @@ def install_pod
config.verbose
config.skip_repo_update = true
sandbox = Sandbox.new(config.project_pods_root)
- resolver = Resolver.new(podfile, nil, sandbox)
- installer = Installer.new(resolver)
+ installer = Installer.new(sandbox, podfile)
installer.install!
- @pod = installer.pods.find { |pod| pod.top_specification == spec }
+ @pod = installer.local_pods.find { |pod| pod.top_specification == spec }
config.silent
end
View
548 lib/cocoapods/installer.rb
@@ -1,26 +1,26 @@
module Pod
- # The installer is the core of CocoaPods. This class is responsible of taking
- # a Podfile and transform it in the Pods libraries. This class also
+ # The {Installer} is the core of CocoaPods. This class is responsible of
+ # 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
# 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.
#
- # 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
# information about targets and Pods.
# - Podfile.lock: Contains information about the pods that were previously
# installed and in concert with the Podfile provides information about
# which specific version of a Pod should be installed. This file is
# ignored in update mode.
- # - Pods.lock: A file contained in the Pods folder that keeps track
- # of the pods installed in the local machine. This files is used once
- # the exact versions of the Pods has been computed to detect if that
- # version is already installed. This file is not intended to be kept
- # under source control and is a copy of the Podfile.lock.
+ # - Manifest.lock: A file contained in the Pods folder that keeps track of
+ # the pods installed in the local machine. This files is used once the
+ # exact versions of the Pods has been computed to detect if that version
+ # is already installed. This file is not intended to be kept under source
+ # control and is a copy of the Podfile.lock.
#
# Once completed the installer should produce the following file structure:
#
@@ -45,7 +45,7 @@ module Pod
# | +-- Pods-prefix.pch
# | +-- PodsDummy_Pods.m
# |
- # +-- Pods.lock
+ # +-- Manifest.lock
# |
# +-- Pods.xcodeproj
#
@@ -55,23 +55,27 @@ class Installer
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
- # @return [Podfile] The Podfile specification that contains the
- # information of the Pods that should be installed.
+ # @return [Podfile]
+ # the Podfile specification that contains the information of the Pods
+ # that should be installed.
#
attr_reader :podfile
- # @return [Lockfile] The Lockfile that stores the information about the
- # installed Pods.
+ # @return [Lockfile]
+ # the Lockfile that stores the information about the Pods previously
+ # installed on any machine.
#
attr_reader :lockfile
- # @return [Bool] Whether the installer is in update mode. In update
- # mode the contents of the Lockfile are not taken into
- # account for deciding what Pods to install.
+ # @return [Bool]
+ # whether the installer is in update mode. In update mode the contents of
+ # the Lockfile are not taken into account for deciding what Pods to
+ # install.
#
attr_reader :update_mode
@@ -87,8 +91,10 @@ def initialize(sandbox, podfile, lockfile = nil, update_mode = false)
@update_mode = update_mode
end
- # @return [void] The installation process of is mostly linear with few
- # minor complications to keep in mind:
+ # Installs the Pods.
+ #
+ # 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
# otherwise the sandbox might return an old podspec and not download
@@ -100,141 +106,159 @@ def initialize(sandbox, podfile, lockfile = nil, update_mode = false)
# @note The order of the steps is very important and should be changed
# carefully.
#
- # TODO:
+ # @return [void]
#
def install!
- # TODO: prepare_for_legacy_compatibility
- compare_podfile_and_lockfile
-
+ analyze
+ prepare_for_legacy_compatibility
clean_global_support_files
clean_removed_pods
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
generate_locked_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
- write_lockfile
- # TODO: write_sandbox_lockfile
+ # @!group Analysis products
- integrate_user_project
- end
+ public
- # @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
-
- end
-
+ attr_reader :pods_added_from_the_lockfile
+ # @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
- # compatible with the most recent version of CocoaPods.
+ # @return [Array<String>]
+ # 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
- # 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
+ attr_reader :locked_dependencies
+ # @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
- # specified in the Podfile grouped by a symbol representing their state
- # (added, changed, removed, unchanged) as identified by the {Lockfile}.
+ # @return [Array<LocalPod>]
+ # the list of LocalPod instances for each dependency sorted by name.
#
- attr_reader :pods_by_state
+ attr_reader :local_pods
- # @return [void] In this step the podfile is compared with the lockfile in
- # order to detect which dependencies should be locked.
+ # @return [Array<String>]
+ # the Pods that should be installed.
#
- # #TODO: If there is not lockfile all the Pods should be marked as added.
- # #TODO: This should use the Pods.lock file because they are used by the
- # to detect what needs to be installed.
+ attr_reader :pods_to_install
+
+ #---------------------------------------------------------------------------#
+
+ # @!group Installation products
+
+ public
+
+ # @return [Pod::Project]
+ # the `Pods/Pods.xcodeproj` project.
#
- def compare_podfile_and_lockfile
- 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
+ attr_reader :pods_project
- # @return [void] Displays the state of each dependency.
+ # @return [Array<TargetInstaller>]
#
- def display_dependencies_state
- 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
+ attr_reader :target_installers
+ #---------------------------------------------------------------------------#
+ # @!group Pre-installation computations
- # @!group Cleaning steps
+ private
- # @return [void] In this step we clean all the folders that will be
- # regenerated from scratch and any file which might not be overwritten.
+ # Compares the {Podfile} with the {Lockfile} in order to detect which
+ # dependencies should be locked.
#
- # @TODO: Clean the podspecs of all the pods that aren't unchanged so the
- # resolution process doesn't get confused by them.
+ # @return [void]
#
- def clean_global_support_files
- sandbox.prepare_for_install
- end
-
- # @return [void] In this step we clean all the files related to the removed
- # Pods.
+ # TODO: If there is not Lockfile all the Pods should be marked as added.
#
- # @TODO: Use the local pod implode.
- # @TODO: [#534] Clean all the Pods folder that are not unchanged?
+ # TODO: Once the manifest.lock is implemented only the unchanged pods
+ # should be tracked.
#
- def clean_removed_pods
- UI.section "Removing deleted dependencies" do
- pods_by_state[:removed].each do |pod_name|
- UI.section("Removing #{pod_name}", "-> ".red) do
- path = sandbox.root + pod_name
- path.rmtree if path.exist?
- end
+ def generate_pods_by_podfile_state
+ if lockfile
+ UI.section "Finding added, modified or removed dependencies:" do
+ pods_by_state = lockfile.detect_changes_with_podfile(podfile)
+ @pods_added_from_the_lockfile = pods_by_state[:added] || []
+ @pods_deleted_from_the_lockfile = pods_by_state[:removed] || []
+ @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 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
- # @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.
+ # Displays the state of each dependency.
#
- # @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
-
-
- # @!group Generate locked dependencies step
-
# @return [void] Lazily updates the source repositories. The update is
# triggered if:
# - There are pods that changed in the Podfile.
@@ -244,84 +268,78 @@ def clean_pods_to_install
# TODO: Remove the lockfile condition once compare_podfile_and_lockfile
# 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
return if config.skip_repo_update?
- changed_pods = (pods_by_state[:added] + pods_by_state[:changed])
- UI.section 'Updating spec repositories' do
- Command::Repo.new(Command::ARGV.new(["update"])).run
- end if !lockfile || !changed_pods.empty? || update_mode
+ changed_pods = (pods_changed_from_the_lockfile + pods_deleted_from_the_lockfile)
+ should_update = !lockfile || !changed_pods.empty? || update_mode
+ if should_update
+ UI.section 'Updating spec repositories' do
+ Command::Repo.new(Command::ARGV.new(["update"])).run
+ end
+ end
end
- # @!group Generate locked dependencies step
-
- # @return [Array<Specification>] All dependencies that have been resolved.
+ # Generates dependencies that require the specific version of the Pods that
+ # haven't changed in the {Lockfile}.
#
- attr_reader :locked_dependencies
-
- # @return [void] In this step we generate the dependencies of necessary to
- # prevent the resolver from updating the pods which are in unchanged
- # state. The Podfile is compared to the Podfile.lock to detect what
- # version of a dependency should be locked.
+ # These dependencies are passed to the {Resolver}, unless the installer is
+ # in update mode, to prevent it from upgrading the Pods that weren't
+ # changed in the {Podfile}.
+ #
+ # @return [void]
#
def generate_locked_dependencies
- if update_mode
- @locked_dependencies = []
- else
- @locked_dependencies = pods_by_state[:unchanged].map do |pod_name|
- lockfile.dependency_for_installed_pod_named(pod_name)
- end
+ @locked_dependencies = pods_unchanged_from_the_lockfile.map do |pod|
+ lockfile.dependency_for_installed_pod_named(pod)
end
end
-
- # @!group Resolution steps
-
- # @return [Hash{Podfile::TargetDefinition => Array<Spec>}]
- # The specifications grouped by target as identified in
- # the resolve_dependencies step.
+ # Converts the Podfile in a list of specifications grouped by target.
#
- attr_reader :specs_by_target
-
- # @return [Array<Specification>] All dependencies that have been resolved.
+ # @note As some dependencies might have external sources the resolver is
+ # aware of the {Sandbox} and interacts with it to download the
+ # 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
-
- # @return [void] Converts the Podfile in a list of specifications grouped
- # by target.
+ # @note In update mode the resolver is set to always update the specs from
+ # external sources.
#
- # In update mode the specs from external sources are always downloaded.
+ # @return [void]
#
def resolve_dependencies
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
@specs_by_target = resolver.resolve
@specifications = specs_by_target.values.flatten
end
end
-
- # @!group Detect Pods to install step
-
- # @return [Array<String>] The names of the Pods that should be installed.
+ # Computes the list of the Pods that should be installed or reinstalled in
+ # the {Sandbox}.
#
- attr_reader :pods_to_install
-
- # @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
- # sandbox or the Pods whose version differs from the one of the lockfile.
+ # 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.
+ #
+ # In update mode specs originating from external dependencies and or from
+ # head sources are always reinstalled.
#
- # In update mode specs originating from external dependencies and or from
- # head sources are always reinstalled.
+ # @return [void]
#
- # TODO: Decide a how the Lockfile should report versions.
- # TODO: [#534] Detect if the folder of a Pod is empty.
+ # TODO: Use {Sandbox} manifest.
#
- 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 = []
if lockfile
- changed_pods = pods.select do |pod|
+ changed_pods = local_pods.select do |pod|
pod.top_specification.version != lockfile.pods_versions[pod.name]
end
if update_mode
@@ -330,42 +348,33 @@ def detect_pods_to_install
resolver.pods_from_external_sources.include?(pod.name)
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
- changed_pods = pods
+ changed_pods = local_pods
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
end
-
- # @!group Install step
-
- # @return [Hash{Podfile::TargetDefinition => Array<LocalPod>}]
+ # Converts the specifications produced by the Resolver in local pods.
#
- attr_reader :pods_by_target
-
- # @return [Array<LocalPod>] A list of LocalPod instances for each
- # dependency sorted by name.
- # (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.
+ # The LocalPod class is responsible to handle the concrete representation
+ # of a specification in the {Sandbox}.
+ #
+ # @return [void]
#
- # @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
- # to a given project so it can use the sources of all the activated
- # podspecs across all targets. Also cleaning should take into
- # account that.
+ # 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
+ # to a given project so it can use the sources of all the activated
+ # podspecs across all targets. Also cleaning should take into account
+ # that.
#
- def create_local_pods
- @pods_by_target = {}
+ def generate_local_pods
+ @local_pods_by_target = {}
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?
sandbox.locally_sourced_pod_for_spec(spec, target_definition.platform)
else
@@ -374,20 +383,71 @@ def create_local_pods
end.uniq.compact
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
+ #---------------------------------------------------------------------------#
+
+ # @!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
# 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
- pods.each do |pod|
+ local_pods.each do |pod|
if pods_to_install.include?(pod)
UI.section("Installing #{pod}".green, "-> ".green) do
install_local_pod(pod)
@@ -454,18 +514,17 @@ def generate_docs_if_needed(pod)
end
end
-
-
- # @!group Generate Pods project and support files step
-
- # @return [void] Creates and populates the targets of the pods project.
+ # Creates and populates the targets of the pods project.
#
# @note Post install hooks run _before_ saving of project, so that they can
# alter it before saving.
#
- def generate_support_files
+ # @return [void]
+ #
+ def install_targets
UI.section "Generating support files" do
prepare_pods_project
+ generate_target_installers
add_source_files_to_pods_project
run_pre_install_hooks
generate_target_support_files
@@ -474,13 +533,11 @@ def generate_support_files
end
end
- # @return [Project] The Pods project.
+ # Creates the Pods project from scratch if it doesn't exists.
#
- attr_reader :pods_project
-
- # @return [void] In this step we create the Pods project from scratch if it
- # doesn't exists. If the Pods project exists instead we clean it and
- # prepare it for installation.
+ # TODO clean and modify the project if it exists.
+ #
+ # @return [void]
#
def prepare_pods_project
UI.message "- Creating Pods project" do
@@ -489,31 +546,40 @@ def prepare_pods_project
end
end
- # @return [void] In this step we add the source files of the Pods to the
- # 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
- # they are local they are added to the Local Pods group.
+ # Creates a target installer for each definition not empty.
+ #
+ # @return [void]
+ #
+ 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
- # modifying only the files of the changed pods. Xcodeproj deletion
- # and sorting of folders is required.
+ # 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]
+ #
+ # 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
UI.message "- Adding source files to Pods project" do
- pods.each { |p| p.add_file_references_to_project(@project) }
- pods.each { |p| p.link_headers }
+ local_pods.each { |p| p.add_file_references_to_project(pods_project) }
+ local_pods.each { |p| p.link_headers }
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
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|
pod.top_specification.pre_install(pod, target_definition)
end
@@ -538,7 +604,7 @@ def run_post_install_hooks
def generate_target_support_files
UI.message"- Installing targets" do
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)
acknowledgements_path = target_installer.target_definition.acknowledgements_path
Generator::Acknowledgements.new(target_installer.target_definition,
@@ -554,41 +620,51 @@ def generate_dummy_source(target_installer)
filename = "#{dummy_source.class_name}.m"
pathname = Pathname.new(sandbox.root + filename)
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)
end
+ # Writes the Pods project to the disk.
+ #
+ # @return [void]
+ #
def write_pod_project
UI.message "- Writing Xcode project file to #{UI.path @sandbox.project_path}" do
pods_project.save_as(@sandbox.project_path)
end
end
-
-
- # @!group Lockfile related steps
-
- def write_lockfile
+ # Writes the Podfile and the {Sandbox} lock files.
+ #
+ # @return [void]
+ #
+ # 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
- @lockfile = Lockfile.generate(podfile, specs_by_target.values.flatten)
@lockfile.write_to_disk(config.project_lockfile)
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
- # @!group Integrate user project step
-
- # @return [void] In this step the user project is integrated. The Pods
- # libraries are added, the build script are added, and the xcconfig files
- # are set.
+ # Integrates the user project.
+ #
+ # The following actions are performed:
+ # - libraries are added.
+ # - 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
- # install. Maybe a clean_user_project phase should be added.
+ # TODO: [#588] The resources should be added through a build phase instead
+ # of using a script.
#
def integrate_user_project
UserProjectIntegrator.new(podfile).integrate! if config.integrate_targets?
View
6 spec/integration_spec.rb
@@ -70,8 +70,7 @@ def should_xcodebuild(target_definition)
end
# 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(resolver)
+ installer = Pod::Installer.new(Pod::Sandbox.new(config.project_pods_root), podfile)
installer.install!
result = installer.lockfile.to_hash
result['PODS'].should == ['SSToolkit (0.1.3)']
@@ -88,8 +87,7 @@ def should_xcodebuild(target_definition)
pod 'Reachability', :podspec => url
end
- resolver = Pod::Resolver.new(podfile, nil, Pod::Sandbox.new(config.project_pods_root))
- installer = SpecHelper::Installer.new(resolver)
+ installer = SpecHelper::Installer.new(Pod::Sandbox.new(config.project_pods_root), podfile)
installer.install!
result = installer.lockfile.to_hash
result['PODS'].should == ['Reachability (1.2.3)']
View
381 spec/unit/installer_spec.rb
@@ -2,200 +2,233 @@
module Pod
describe Installer do
+
before do
+ @sandbox = temporary_sandbox
config.repos_dir = fixture('spec-repos')
+ config.project_pods_root = @sandbox.root
+ FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
end
- describe "by default" do
- before do
- 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
+ describe "Concerning pre-installation computations" do
+ def generate_podfile(pods = ['JSONKit'])
podfile = Podfile.new do
platform :ios
xcodeproj 'MyProject'
- pod 'JSONKit'
+ pods.each { |name| pod name }
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
+ def generate_lockfile
+ hash = {}
+ hash['PODS'] = []
+ hash['DEPENDENCIES'] = []
+ hash['SPEC CHECKSUMS'] = []
+ hash['COCOAPODS'] = Pod::VERSION
+ Pod::Lockfile.new(hash)
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
+ podfile = generate_podfile
+ lockfile = generate_lockfile
+ @installer = Installer.new(@sandbox, podfile, lockfile)
@installer.install!
end
- it "cleans a pod only once" do
- LocalPod.any_instance.expects(:clean!).once
- @installer.install!
- end
+ # describe "#analyze" do
+ # it "doesn't affects creates changes in the file system" do
+ # end
+ # 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"]
+ it "marks all pods as added if there is no lockfile" do
+ @installer.pods_added_from_the_lockfile.should == ['JSONKit']
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
+ # describe "by default" do
+ # before do
+ # 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')
+ # 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

0 comments on commit 697f8bd

Please sign in to comment.