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

[Installer] Complete new hook architecture

parent 356799ef
...@@ -49,10 +49,9 @@ module Pod ...@@ -49,10 +49,9 @@ module Pod
end end
module Hooks module Hooks
autoload :InstallerData, 'cocoapods/hooks/installer_data' autoload :InstallerRepresentation, 'cocoapods/hooks/installer_representation'
autoload :LibraryData, 'cocoapods/hooks/library_data' autoload :LibraryRepresentation, 'cocoapods/hooks/library_representation'
autoload :PodData, 'cocoapods/hooks/pod_data' autoload :PodRepresentation, 'cocoapods/hooks/pod_representation'
autoload :TargetInstallerData, 'cocoapods/hooks/target_installer_data'
end end
end end
......
...@@ -8,14 +8,11 @@ module Pod ...@@ -8,14 +8,11 @@ module Pod
end end
end end
# The public API should return dumb data types so it is easier to satisfy its
# implicit contract.
#
module Hooks module Hooks
# Stores the information of the Installer for the hooks # The installer representation to pass to the hooks.
# #
class InstallerData class InstallerRepresentation
public public
...@@ -33,56 +30,53 @@ module Pod ...@@ -33,56 +30,53 @@ module Pod
installer.pods_project installer.pods_project
end end
# @return [Array<PodData>] The list of LocalPod instances for each # @return [Array<PodRepresentation>] The representation of the Pods.
# dependency sorted by name.
# #
def pods def pods
installer.pods_data installer.pod_reps
end end
# @return [Array<TargetInstallerData>] # @return [Array<LibraryRepresentation>] The representation of the
# libraries.
# #
def target_installers def libraries
installer.target_installers_data installer.library_reps
end end
# @return [Hash{TargetDefinition => Array<Specification>}] The # @return [Hash{LibraryRepresentation => Array<Specification>}] The
# specifications grouped by target definition. # specifications grouped by target definition.
# #
# @todo Consider grouping by TargetInstallerData. def specs_by_lib
#
def specs_by_target
result = {} result = {}
libraries.each do |lib| installer.libraries.each do |lib|
result[lib.target_definition] = lib.specs result[installer.library_rep(lib)] = lib.specs
end end
result result
end end
# @return [Hash{TargetDefinition => Array<LocalPod>}] The local pod # @return [Hash{LibraryRepresentation => Array<PodRepresentation>}] The
# instances grouped by target. # local pod instances grouped by target.
# #
def pods_by_target def pods_by_lib
result = {} result = {}
libraries.each do |lib| installer.libraries.each do |lib|
root_specs = lib.specs.map { |spec| spec.root }.uniq pod_names = lib.specs.map { |spec| spec.root.name }.uniq
pods_data = pods.select { |pod_data| root_specs.include?(pod_data.root_spec) } pod_reps = pods.select { |rep| pod_names.include?(rep.name) }
result[lib.target_definition] = pods_data result[lib.target_definition] = pod_reps
end end
result result
end end
# @see pods_by_target #-----------------------------------------------------------------------#
# public
# @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
# @!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 ...@@ -106,6 +100,10 @@ module Pod
Config.instance Config.instance
end end
# @return [Installer] The installer described by this instance.
#
attr_reader :installer
#-----------------------------------------------------------------------# #-----------------------------------------------------------------------#
# @!group Private implementation # @!group Private implementation
...@@ -116,18 +114,6 @@ module Pod ...@@ -116,18 +114,6 @@ module Pod
@installer = installer @installer = installer
end 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 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 Pod
module Hooks module Hooks
class TargetInstallerData class LibraryRepresentation
# Stores the information of the target installer # 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 # @return [Pathname] The path of the prefix_header
# updated if they need a reference to the prefix header.
# #
def prefix_header_filename def prefix_header_filename
library.prefix_header_path.relative_path_from(sandbox.root) library.prefix_header_path
end end
# @return [Project] the Pods project of the sandbox. # @return [Project] The Pods project of the sandbox.
# #
def project def project
sandbox.project sandbox.project
end end
# @return [TargetDefinition] the target definition of the library. # @return [TargetDefinition] The target definition of the library.
# #
def target_definition def target_definition
library.target_definition library.target_definition
end end
# @return [PBXNativeTarget] the target generated by the installation # @return [PBXNativeTarget] The target generated by the installation
# process. # process.
# #
def target def target
library.target library.target
end 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 end
end end
......
...@@ -12,18 +12,16 @@ module Pod ...@@ -12,18 +12,16 @@ module Pod
# Stores the information of the Installer for the hooks # Stores the information of the Installer for the hooks
# #
class PodData class PodRepresentation
# @return [String] # @return [String]
# #
def name attr_accessor :name
root_spec.name
end
# @return [Version] # @return [Version]
# #
def version def version
root_spec.name root_spec.version
end end
# @return [Specification] # @return [Specification]
...@@ -35,7 +33,7 @@ module Pod ...@@ -35,7 +33,7 @@ module Pod
# @return [Array<Specification>] # @return [Array<Specification>]
# #
def specs def specs
file_accessors.map(&:spec) file_accessors.map(&:spec).uniq
end end
# @return [Pathname] # @return [Pathname]
...@@ -56,7 +54,8 @@ module Pod ...@@ -56,7 +54,8 @@ module Pod
# @param [Installer] installer @see installer # @param [Installer] installer @see installer
# #
def initialize(file_accessors) def initialize(name, file_accessors)
@name = name
@file_accessors = file_accessors @file_accessors = file_accessors
end end
......
...@@ -377,11 +377,14 @@ module Pod ...@@ -377,11 +377,14 @@ module Pod
def run_pre_install_hooks def run_pre_install_hooks
UI.message "- Running pre install hooks" do UI.message "- Running pre install hooks" do
installed_specs.each do |spec| 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 UI.message "- #{spec.name}" if executed
end end
executed = @podfile.pre_install!(installer_data) executed = @podfile.pre_install!(installer_rep)
UI.message "- Podfile" if executed UI.message "- Podfile" if executed
end end
end end
...@@ -395,13 +398,14 @@ module Pod ...@@ -395,13 +398,14 @@ module Pod
# #
def run_post_install_hooks def run_post_install_hooks
UI.message "- Running post install hooks" do UI.message "- Running post install hooks" do
installed_specs.each do |spec| installed_specs.each do |spec|
target_installer_data = target_installers_data.first #TODO executed = false
executed = spec.post_install!(target_installer_data) libraries_using_spec(spec).each do |lib|
executed ||= spec.post_install!(library_rep(lib))
end
UI.message "- #{spec.name}" if executed UI.message "- #{spec.name}" if executed
end end
executed = @podfile.post_install!(installer_data) executed = @podfile.post_install!(installer_rep)
UI.message "- Podfile" if executed UI.message "- Podfile" if executed
end end
end end
...@@ -412,33 +416,52 @@ module Pod ...@@ -412,33 +416,52 @@ module Pod
# @!group Hooks Data # @!group Hooks Data
def installer_data # @return [InstallerRepresentation]
Hooks::InstallerData.new(self) #
def installer_rep
Hooks::InstallerRepresentation.new(self)
end end
def target_installers_data # @return [PodRepresentation] The hook representation of a Pod.
@target_installers_data ||= libraries.map do |lib| #
data = Hooks::TargetInstallerData.new # @param [String] pod
data.sandbox = sandbox # The name of the pod.
data.library = lib #
data # @return [PodRepresentation] The pod representation.
end #
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 end
def pods_data # @return [LibraryRepresentation]
root_specs.map do |spec| #
pod_data(spec) def library_rep(library)
end Hooks::LibraryRepresentation.new(sandbox, library)
end end
def pod_data(spec) # @return [Array<LibraryRepresentation>]
all_file_accessors = libraries.map(&:file_accessors).flatten.compact #
file_accessors = all_file_accessors.select { |fa| fa.spec.root == spec.root } def library_reps
Hooks::PodData.new(file_accessors) @library_reps ||= libraries.map { |lib| library_rep(lib) }
end end
def library_data(library) # @return [Array<PodRepresentation>]
Hooks::LibraryData.new(library) #
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 end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
......
PODS:Reachability (3.1.0) TARGETS:default PODS:Reachability (3.1.0) TARGETS:Pods
...@@ -135,7 +135,7 @@ module Pod ...@@ -135,7 +135,7 @@ module Pod
pod 'SSZipArchive', '0.1.0' pod 'SSZipArchive', '0.1.0'
pre_install do |installer| 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} File.open(installer.config.sandbox_root + 'memo.txt', 'w') {|f| f.puts memo}
end end
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 ...@@ -344,167 +344,73 @@ module Pod
describe "Hooks" do 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 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 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 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 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 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 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
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
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