Commit 0e0523fc authored by Fabio Pelosin's avatar Fabio Pelosin

[Installer] Complete new hook architecture

parent 356799ef
......@@ -49,10 +49,9 @@ module Pod
end
module Hooks
autoload :InstallerData, 'cocoapods/hooks/installer_data'
autoload :LibraryData, 'cocoapods/hooks/library_data'
autoload :PodData, 'cocoapods/hooks/pod_data'
autoload :TargetInstallerData, 'cocoapods/hooks/target_installer_data'
autoload :InstallerRepresentation, 'cocoapods/hooks/installer_representation'
autoload :LibraryRepresentation, 'cocoapods/hooks/library_representation'
autoload :PodRepresentation, 'cocoapods/hooks/pod_representation'
end
end
......
......@@ -8,14 +8,11 @@ module Pod
end
end
# The public API should return dumb data types so it is easier to satisfy its
# implicit contract.
#
module Hooks
# Stores the information of the Installer for the hooks
# The installer representation to pass to the hooks.
#
class InstallerData
class InstallerRepresentation
public
......@@ -33,56 +30,53 @@ module Pod
installer.pods_project
end
# @return [Array<PodData>] The list of LocalPod instances for each
# dependency sorted by name.
# @return [Array<PodRepresentation>] The representation of the Pods.
#
def pods
installer.pods_data
installer.pod_reps
end
# @return [Array<TargetInstallerData>]
# @return [Array<LibraryRepresentation>] The representation of the
# libraries.
#
def target_installers
installer.target_installers_data
def libraries
installer.library_reps
end
# @return [Hash{TargetDefinition => Array<Specification>}] The
# @return [Hash{LibraryRepresentation => Array<Specification>}] The
# specifications grouped by target definition.
#
# @todo Consider grouping by TargetInstallerData.
#
def specs_by_target
def specs_by_lib
result = {}
libraries.each do |lib|
result[lib.target_definition] = lib.specs
installer.libraries.each do |lib|
result[installer.library_rep(lib)] = lib.specs
end
result
end
# @return [Hash{TargetDefinition => Array<LocalPod>}] The local pod
# instances grouped by target.
# @return [Hash{LibraryRepresentation => Array<PodRepresentation>}] The
# local pod instances grouped by target.
#
def pods_by_target
def pods_by_lib
result = {}
libraries.each do |lib|
root_specs = lib.specs.map { |spec| spec.root }.uniq
pods_data = pods.select { |pod_data| root_specs.include?(pod_data.root_spec) }
result[lib.target_definition] = pods_data
installer.libraries.each do |lib|
pod_names = lib.specs.map { |spec| spec.root.name }.uniq
pod_reps = pods.select { |rep| pod_names.include?(rep.name) }
result[lib.target_definition] = pod_reps
end
result
end
# @see pods_by_target
#
# @todo Fix the warning.
#
def local_pods_by_target
# UI.warn "Podfile#config is deprecated. The config is accessible from " \
# "the parameter passed to the hooks".
pods_by_target
end
#-----------------------------------------------------------------------#
public
# @!group Compatibility
#
# The following aliases provides compatibility with CP < 0.17
alias :target_installers :libraries
alias :specs_by_target :specs_by_lib
alias :local_pods_by_target :pods_by_lib
#-----------------------------------------------------------------------#
......@@ -106,6 +100,10 @@ module Pod
Config.instance
end
# @return [Installer] The installer described by this instance.
#
attr_reader :installer
#-----------------------------------------------------------------------#
# @!group Private implementation
......@@ -116,18 +114,6 @@ module Pod
@installer = installer
end
private
# @return [Installer] The installer described by this instance.
#
attr_reader :installer
# @return [Library] The library whose target needs to be generated.
#
def libraries
installer.libraries
end
#-----------------------------------------------------------------------#
end
......
module Pod
module Hooks
# Stores the information of
#
# Was target definition
#
class LibraryData
def dependencies
library.target_definition.dependencies
end
def initialize(library)
@library = library
end
private
attr_reader :library
end
end
end
module Pod
module Hooks
class TargetInstallerData
class LibraryRepresentation
# Stores the information of the target installer
# @return [Sandbox] sandbox the sandbox where the support files should
# be generated.
#
attr_accessor :sandbox
#-----------------------------------------------------------------------#
# @return [Library] The library whose target needs to be generated.
# @return [String] The name of the Pods library.
#
attr_accessor :library
def name
library.name
end
#-----------------------------------------------------------------------#
# @return [Array<Dependency>] The dependencies of this library.
#
def dependencies
target_definition.dependencies
end
# @todo This has to be removed, but this means the specs have to be
# updated if they need a reference to the prefix header.
# @return [Pathname] The path of the prefix_header
#
def prefix_header_filename
library.prefix_header_path.relative_path_from(sandbox.root)
library.prefix_header_path
end
# @return [Project] the Pods project of the sandbox.
# @return [Project] The Pods project of the sandbox.
#
def project
sandbox.project
end
# @return [TargetDefinition] the target definition of the library.
# @return [TargetDefinition] The target definition of the library.
#
def target_definition
library.target_definition
end
# @return [PBXNativeTarget] the target generated by the installation
# @return [PBXNativeTarget] The target generated by the installation
# process.
#
def target
library.target
end
#-----------------------------------------------------------------------#
public
# @!group Unsafe Hooks API
# @return [Sandbox] sandbox the sandbox where the support files should
# be generated.
#
attr_reader :sandbox
# @return [Library] The library whose target needs to be generated.
#
attr_reader :library
#-----------------------------------------------------------------------#
# @!group Private implementation
# @param [Sandbox] sandbox @see sandbox
# @param [Library] library @see library
#
def initialize(sandbox, library)
@sandbox = sandbox
@library = library
end
#-----------------------------------------------------------------------#
end
end
end
......
......@@ -12,18 +12,16 @@ module Pod
# Stores the information of the Installer for the hooks
#
class PodData
class PodRepresentation
# @return [String]
#
def name
root_spec.name
end
attr_accessor :name
# @return [Version]
#
def version
root_spec.name
root_spec.version
end
# @return [Specification]
......@@ -35,7 +33,7 @@ module Pod
# @return [Array<Specification>]
#
def specs
file_accessors.map(&:spec)
file_accessors.map(&:spec).uniq
end
# @return [Pathname]
......@@ -56,7 +54,8 @@ module Pod
# @param [Installer] installer @see installer
#
def initialize(file_accessors)
def initialize(name, file_accessors)
@name = name
@file_accessors = file_accessors
end
......
......@@ -377,11 +377,14 @@ module Pod
def run_pre_install_hooks
UI.message "- Running pre install hooks" do
installed_specs.each do |spec|
executed = spec.pre_install!(pod_data(spec), library_data(libraries.first)) #TODO
executed = false
libraries_using_spec(spec).each do |lib|
executed ||= spec.pre_install!(pod_rep(spec.root.name), library_rep(lib))
end
UI.message "- #{spec.name}" if executed
end
executed = @podfile.pre_install!(installer_data)
executed = @podfile.pre_install!(installer_rep)
UI.message "- Podfile" if executed
end
end
......@@ -395,13 +398,14 @@ module Pod
#
def run_post_install_hooks
UI.message "- Running post install hooks" do
installed_specs.each do |spec|
target_installer_data = target_installers_data.first #TODO
executed = spec.post_install!(target_installer_data)
executed = false
libraries_using_spec(spec).each do |lib|
executed ||= spec.post_install!(library_rep(lib))
end
UI.message "- #{spec.name}" if executed
end
executed = @podfile.post_install!(installer_data)
executed = @podfile.post_install!(installer_rep)
UI.message "- Podfile" if executed
end
end
......@@ -412,33 +416,52 @@ module Pod
# @!group Hooks Data
def installer_data
Hooks::InstallerData.new(self)
# @return [InstallerRepresentation]
#
def installer_rep
Hooks::InstallerRepresentation.new(self)
end
def target_installers_data
@target_installers_data ||= libraries.map do |lib|
data = Hooks::TargetInstallerData.new
data.sandbox = sandbox
data.library = lib
data
end
# @return [PodRepresentation] The hook representation of a Pod.
#
# @param [String] pod
# The name of the pod.
#
# @return [PodRepresentation] The pod representation.
#
def pod_rep(pod)
all_file_accessors = libraries.map(&:file_accessors).flatten.compact
file_accessors = all_file_accessors.select { |fa| fa.spec.root.name == pod }
Hooks::PodRepresentation.new(pod, file_accessors)
end
def pods_data
root_specs.map do |spec|
pod_data(spec)
end
# @return [LibraryRepresentation]
#
def library_rep(library)
Hooks::LibraryRepresentation.new(sandbox, library)
end
def pod_data(spec)
all_file_accessors = libraries.map(&:file_accessors).flatten.compact
file_accessors = all_file_accessors.select { |fa| fa.spec.root == spec.root }
Hooks::PodData.new(file_accessors)
# @return [Array<LibraryRepresentation>]
#
def library_reps
@library_reps ||= libraries.map { |lib| library_rep(lib) }
end
def library_data(library)
Hooks::LibraryData.new(library)
# @return [Array<PodRepresentation>]
#
def pod_reps
root_specs.sort_by { |spec| spec.name }.map { |spec| pod_rep(spec.name) }
end
# Returns the libraries which use the given specification.
#
# @param [Specification] spec
# The specification for which the client libraries are needed.
#
# @return [Array<Library>] The library.
#
def libraries_using_spec(spec)
libraries.select { |lib| lib.specs.include?(spec) }
end
#-------------------------------------------------------------------------#
......
PODS:Reachability (3.1.0) TARGETS:default
PODS:Reachability (3.1.0) TARGETS:Pods
......@@ -135,7 +135,7 @@ module Pod
pod 'SSZipArchive', '0.1.0'
pre_install do |installer|
memo = "PODS:#{installer.pods * ', '} TARGETS:#{installer.project.targets.to_a * ', '}"
memo = "PODS:#{installer.pods * ', '} TARGETS:#{installer.project.targets * ', '}"
File.open(installer.config.sandbox_root + 'memo.txt', 'w') {|f| f.puts memo}
end
end
......
require File.expand_path('../../../spec_helper', __FILE__)
def safe_stub(object, method, return_value = nil)
object.should.respond_to(method)
object.expects(method).returns(return_value)
end
module Pod
describe Hooks::InstallerRepresentation do
before do
podfile = Pod::Podfile.new do
platform :ios
pod 'JSONKit'
end
config.integrate_targets = false
@installer = Installer.new(config.sandbox, podfile)
@installer.send(:analyze)
@specs = @installer.libraries.map(&:specs).flatten
@installer.stubs(:installed_specs).returns(@specs)
@rep = Hooks::InstallerRepresentation.new(@installer)
end
#-------------------------------------------------------------------------#
describe "Public Hooks API" do
it "returns the sandbox root" do
@rep.sandbox_root.should == config.sandbox.root
end
it "returns the pods project" do
project = stub()
safe_stub(@installer, :pods_project, project)
@rep.project.should == project
end
it "the hook representation of the pods" do
@rep.pods.map(&:name).should == ['JSONKit']
end
it "the hook representation of the libraries" do
@rep.libraries.map(&:name).should == ['Pods']
end
it "returns the specs by library representation" do
specs_by_lib = @rep.specs_by_lib
lib_rep = specs_by_lib.keys.first
lib_rep.name.should == 'Pods'
specs_by_lib.should == { lib_rep => @specs }
end
it "returns the pods representation by library representation" do
pods_by_lib = @rep.pods_by_lib
target_definition = @installer.libraries.first.target_definition
pods_by_lib[target_definition].map(&:name).should == ['JSONKit']
end
end
#-------------------------------------------------------------------------#
describe "Unsafe Hooks API" do
it "returns the sandbox" do
@rep.sandbox.should == config.sandbox
end
it "returns the config" do
@rep.config.should == config
end
it "returns the installer" do
@rep.installer.should == @installer
end
end
#-------------------------------------------------------------------------#
end
end
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Hooks::LibraryRepresentation do
before do
@target_definition = Podfile::TargetDefinition.new('MyApp', nil)
@lib = Library.new(@target_definition)
@rep = Hooks::LibraryRepresentation.new(config.sandbox, @lib)
end
#-------------------------------------------------------------------------#
describe "Public Hooks API" do
it "returns the name" do
@rep.name.should == 'Pods-MyApp'
end
it "returns the dependencies" do
@target_definition.store_pod('AFNetworking')
@rep.dependencies.should == [Dependency.new('AFNetworking')]
end
it "returns the path of the prefix header" do
@lib.support_files_root = temporary_directory
@rep.prefix_header_filename.should == temporary_directory + 'Pods-MyApp-prefix.pch'
end
it "returns the pods project" do
project = stub()
config.sandbox.project = project
@rep.project.should == project
end
it "returns the target definition" do
@rep.target_definition.should == @target_definition
end
end
#-------------------------------------------------------------------------#
describe "Unsafe Hooks API" do
it "returns the sandbox" do
@rep.sandbox.should == config.sandbox
end
it "returns the library" do
@rep.library.should == @lib
end
it "returns the native target" do
target = stub()
@lib.target = target
@rep.target.should == target
end
end
#-------------------------------------------------------------------------#
end
end
require File.expand_path('../../../spec_helper', __FILE__)
# Stubs an object ensuring that it responds to the given method
#
def safe_stub(object, method, return_value)
object.should.respond_to?(method)
object.stubs(method).returns(return_value)
end
module Pod
describe Hooks::PodData do
before do
end
#-------------------------------------------------------------------------#
describe "Public Hooks API" do
end
#-------------------------------------------------------------------------#
describe "Unsafe Hooks API" do
it "provides the config to the specification" do
spec = Spec.new(nil, 'Name')
spec.config.should == config
end
end
#-------------------------------------------------------------------------#
end
end
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Hooks::PodRepresentation do
before do
@spec = fixture_spec('banana-lib/BananaLib.podspec')
@root = fixture('banana-lib')
@file_accessor = Sandbox::FileAccessor.new(Sandbox::PathList.new(@root), @spec.consumer(:ios))
@rep = Hooks::PodRepresentation.new('BananaLib', [@file_accessor])
end
#-------------------------------------------------------------------------#
describe "Public Hooks API" do
it "returns the name" do
@rep.name.should == 'BananaLib'
end
it "returns the version" do
@rep.version.should == Version.new('1.0')
end
it "returns the root specification" do
@rep.root_spec.should == @spec
end
it "returns all the activated specifications" do
@rep.specs.should == [@spec]
end
it "returns the directory where the pod is stored" do
@rep.root.should == @root
end
it "returns the source files" do
source_files = @rep.source_files.map{ |sf| sf.relative_path_from(@root).to_s }.sort
source_files.should == [
"Classes/Banana.h",
"Classes/Banana.m",
"Classes/BananaPrivate.h"
]
end
end
#-------------------------------------------------------------------------#
end
end
......@@ -344,167 +344,73 @@ module Pod
describe "Hooks" do
xit "runs the pre install hooks" do
before do
@installer.send(:analyze)
@specs = @installer.libraries.map(&:specs).flatten
@spec = @specs.find { |spec| spec.name == 'JSONKit' }
@installer.stubs(:installed_specs).returns(@specs)
@lib = @installer.libraries.first
end
xit "run_post_install_hooks" do
it "runs the pre install hooks" do
installer_rep = stub()
pod_rep = stub()
library_rep = stub()
@installer.expects(:installer_rep).returns(installer_rep)
@installer.expects(:pod_rep).with('JSONKit').returns(pod_rep)
@installer.expects(:library_rep).with(@lib).returns(library_rep)
@spec.expects(:pre_install!)
@installer.podfile.expects(:pre_install!).with(installer_rep)
@installer.send(:run_pre_install_hooks)
end
xit "creates the installer data hook argument" do
it "run_post_install_hooks" do
installer_rep = stub()
target_installer_data = stub()
@installer.expects(:installer_rep).returns(installer_rep)
@installer.expects(:library_rep).with(@lib).returns(target_installer_data)
@spec.expects(:post_install!)
@installer.podfile.expects(:post_install!).with(installer_rep)
@installer.send(:run_post_install_hooks)
end
xit "creates the target installers data hook argument" do
it "returns the hook representation of the installer" do
rep = @installer.send(:installer_rep)
rep.sandbox_root.should == @installer.sandbox.root
end
xit "creates the pods data hook argument" do
it "returns the hook representation of a pod" do
file_accessor = stub(:spec => @spec)
@lib.stubs(:file_accessors).returns([file_accessor])
rep = @installer.send(:pod_rep, 'JSONKit')
rep.name.should == 'JSONKit'
rep.root_spec.should == @spec
end
xit "creates the pod data hook argument" do
it "returns the hook representation of a library" do
rep = @installer.send(:library_rep, @lib)
rep.send(:library).name.should == 'Pods'
end
it "returns the hook representation of all the pods" do
reps = @installer.send(:pod_reps)
reps.map(&:name).should == ['JSONKit']
end
xit "creates the library data hook argument" do
it "returns the hook representation of all the target installers" do
reps = @installer.send(:library_reps)
reps.map(&:name).should == ['Pods']
end
it "returns the libraries which use a given Pod" do
libs = @installer.send(:libraries_using_spec, @spec)
libs.map(&:name).should == ['Pods']
end
end
#-------------------------------------------------------------------------#
# before do
# @sandbox = temporary_sandbox
# config.repos_dir = fixture('spec-repos')
# config.sandbox_root = @sandbox.root
# FileUtils.cp_r(fixture('integration/JSONKit'), @sandbox.root + 'JSONKit')
# SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
# end
#
# describe "by default" do
# before do
# podfile = Podfile.new do
# platform :ios
# xcodeproj 'MyProject'
# pod 'JSONKit'
# end
# @sandbox = temporary_sandbox
# config.sandbox_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 "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 "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 multiple pods originating form the same spec" do
# extend SpecHelper::Fixture
# before do
# sandbox = temporary_sandbox
# Config.instance.sandbox_root = sandbox.root
# 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
# Config.instance.sandbox_root = sandbox.root
# 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
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