Commit 21af2670 authored by Fabio Pelosin's avatar Fabio Pelosin

[Core Extraction] Adapted Target Installer (with clean up).

parent 39f80935
...@@ -26,7 +26,7 @@ EOS ...@@ -26,7 +26,7 @@ EOS
attr_reader :resources attr_reader :resources
# A list of files relative to the project pods root. # A list of files relative to the project pods root.
def initialize(resources) def initialize(resources = [])
@resources = resources @resources = resources
end end
......
module Pod module Pod
class Installer class Installer
# This class is reponsible of creating and configuring the static library # This class is responsible of creating and configuring the static library
# target in Pods project. Every target is generated from a target # target in Pods project. Every target is generated from a target
# definition of the Podfile. # definition of the Podfile.
# #
class TargetInstaller class TargetInstaller
include Config::Mixin
# @return [Podfile]
#
# TODO: is really needed to pass the podfile?
#
attr_reader :podfile
# @return [Project] The Pods project. # @return [Project] The Pods project.
# #
attr_reader :project attr_reader :project
# @return [TargetDefinition] The target definition whoose target needs to # @return [TargetDefinition] The target definition whose target needs to
# be generated. # be generated.
# #
attr_reader :target_definition attr_reader :target_definition
def initialize(podfile, project, target_definition) # @param [Array<LocalPod>] pods the pods are required by the target
@podfile = podfile # definition of this installer.
@project = project #
attr_reader :pods
# @param [Project] project @see project
# @param [TargetDefinition] target_definition @see target_definition
# @param [Array<LocalPod>] pods @see pods
#
def initialize(project, target_definition, pods)
@project = project
@target_definition = target_definition @target_definition = target_definition
@pods = pods
end end
# @return [void] Creates the target in the Pods project and its support # Creates the target in the Pods project and its support files.
# files.
#
# @param [Array<LocalPod>] pods The pods are required by the target
# definition of this installer.
# #
# @param [Sandbox] sandbox The sanbox where the support files # @return [void]
# should be generated.
# #
def install!(pods, sandbox) def install
self.requires_arc = pods.any? { |pod| pod.requires_arc? } add_target
add_build_files_to_target
@target = @project.add_pod_target(@target_definition.label, @target_definition.platform) add_file_reference_for_support_files
configure_target
source_file_descriptions = []
pods.each { |p| p.add_build_files_to_target(@target) }
support_files_group = @project.support_files_group.new_group(@target_definition.label) create_xcconfig_file
target_support_files.each { |path| support_files_group.new_file(path) } create_prefix_header
create_bridge_support_file
xcconfig_file = support_files_group.files.find { |f| f.path == @target_definition.xcconfig_name } create_copy_resources_script
configure_build_configurations(xcconfig_file, sandbox)
create_files(pods, sandbox)
end end
# @return [PBXNativeTarget] The target generated by the installation # TODO This has to be removed, but this means the specs have to be
# process. # updated if they need a reference to the prefix header.
# #
attr_reader :target def prefix_header_filename
library.prefix_header_name
end
#-----------------------------------------------------------------------#
# @return [Boold] Wether the any of the pods requires arc. # @!group Installation steps
private
# Adds the library for the {#target_definition} to the Pods
# project.
# #
# TODO: This should not be an attribute reader. # @return [void]
# #
attr_accessor :requires_arc def add_target
@library = @project.add_pod_library(target_definition)
attr_reader :xcconfig @target = @library.target
end
# In a workspace this is where the static library headers should be found. # Adds the build files of the pods to the target.
#
# @return [void]
# #
def generate_xcconfig(pods, sandbox) def add_build_files_to_target
xcconfig = Xcodeproj::Config.new({ pods.each { |p| p.add_build_files_to_target(target) }
'ALWAYS_SEARCH_USER_PATHS' => 'YES', # needed to make EmbedReader build
'OTHER_LDFLAGS' => default_ld_flags,
'HEADER_SEARCH_PATHS' => '${PODS_HEADERS_SEARCH_PATHS}',
# CocoaPods global keys
'PODS_ROOT' => @target_definition.relative_pods_root,
'PODS_BUILD_HEADERS_SEARCH_PATHS' => quoted(sandbox.build_headers.search_paths).join(" "),
'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quoted(sandbox.public_headers.search_paths).join(" "),
# Pods project specific keys
'PODS_HEADERS_SEARCH_PATHS' => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}'
})
pods.each { |pod| xcconfig.merge!(pod.xcconfig) }
@xcconfig = xcconfig
end end
# Adds the file references for the support files that are generated by
# the installation process to the Pods project.
# #
# @return [void]
# #
def copy_resources_script_for(pods) def add_file_reference_for_support_files
@copy_resources_script ||= Generator::CopyResourcesScript.new(pods.map { |p| p.relative_resource_files }.flatten) g = project.support_files_group.new_group(target_definition.label)
g.new_file(library.copy_resources_script_name)
g.new_file(library.prefix_header_name)
g.new_file(library.xcconfig_name).tap { |f| @xcconfig_file_ref = f }
end end
def bridge_support_generator_for(pods, sandbox) # Configures the build settings of the target.
Generator::BridgeSupport.new(pods.map do |pod| #
pod.relative_header_files.map { |header| sandbox.root + header } # @note The `PODS_HEADERS_SEARCH_PATHS` overrides the xcconfig.
end.flatten) #
# @return [void]
#
def configure_target
set = {}
set['OTHER_LDFLAGS'] = ''
set['GCC_PREFIX_HEADER'] = library.prefix_header_name
set['PODS_ROOT'] = '${SRCROOT}'
set['PODS_HEADERS_SEARCH_PATHS'] = '${PODS_BUILD_HEADERS_SEARCH_PATHS}'
set['GCC_WARN_INHIBIT_ALL_WARNINGS'] =
@target_definition.inhibit_all_warnings? ? 'YES' : 'NO'
@target.build_configurations.each do |c|
c.base_configuration_reference = xcconfig_file_ref
c.build_settings.merge!(set)
end
end 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. # Generates the contents of the xcconfig file and saves it to disk.
def prefix_header_filename #
@target_definition.prefix_header_name # @note The `ALWAYS_SEARCH_USER_PATHS` flag is enabled to support
# libraries like `EmbedReader`.
#
# @return [void]
#
def create_xcconfig_file
UI.message "- Generating xcconfig file at #{UI.path(library.xcconfig_path)}" do
xcconfig = Xcodeproj::Config.new({
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
'OTHER_LDFLAGS' => default_ld_flags,
'HEADER_SEARCH_PATHS' => '${PODS_HEADERS_SEARCH_PATHS}',
'PODS_ROOT' => library.relative_pods_root,
'PODS_BUILD_HEADERS_SEARCH_PATHS' => quote(sandbox.build_headers.search_paths),
'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quote(sandbox.public_headers.search_paths),
'PODS_HEADERS_SEARCH_PATHS' => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}'
})
pods.each { |pod| xcconfig.merge!(pod.xcconfig) }
xcconfig.save_as(library.xcconfig_path)
library.xcconfig = xcconfig
end
end end
# TODO move out to Generator::PrefixHeader # Creates a prefix header file which imports `UIKit` or `Cocoa`. This
def save_prefix_header_as(pathname, pods) # file also include any prefix header content reported by the
pathname.open('w') do |header| # specification of the pods.
header.puts "#ifdef __OBJC__" #
header.puts "#import #{@target_definition.platform == :ios ? '<UIKit/UIKit.h>' : '<Cocoa/Cocoa.h>'}" # @return [void]
header.puts "#endif" #
pods.each do |pod| def create_prefix_header
if prefix_header_contents = pod.top_specification.prefix_header_contents UI.message "- Generating prefix header at #{UI.path(library.prefix_header_path)}" do
header.puts gen = Generator::PrefixHeader.new(target_definition.platform, pods)
header.puts prefix_header_contents gen.save_as(library.prefix_header_path)
elsif prefix_header = pod.prefix_header_file
header.puts
header.puts prefix_header.read
end
end
end end
end end
def target_support_files # Generates the bridge support metadata if requested by the {Podfile}.
[:copy_resources_script_name, :prefix_header_name, :xcconfig_name].map { |file| @target_definition.send(file) } #
# @return [void]
#
# TODO: Why is this added to the copy resources script?
#
def create_bridge_support_file
if target_definition.podfile.generate_bridge_support?
UI.message "- Generating BridgeSupport metadata at #{UI.path library.bridge_support_path}" do
generator = Generator::BridgeSupport.new(pods.map do |pod|
pod.relative_header_files.map { |header| sandbox.root + header }
end.flatten)
generator.save_as(library.bridge_support_path)
copy_resources_script.resources << library.bridge_support_name
end
end
end end
def configure_build_configurations(xcconfig_file, sandbox) # Creates a script that copies the resources to the bundle of the client
@target.build_configurations.each do |config| # target.
config.base_configuration_reference = xcconfig_file #
config.build_settings['OTHER_LDFLAGS'] = '' # TODO: This should be replaced by an Xcode copy resources build phase.
config.build_settings['GCC_PREFIX_HEADER'] = @target_definition.prefix_header_name #
config.build_settings['PODS_ROOT'] = '${SRCROOT}' # @return [void]
config.build_settings['PODS_HEADERS_SEARCH_PATHS'] = '${PODS_BUILD_HEADERS_SEARCH_PATHS}' #
config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'] = @target_definition.inhibit_all_warnings? ? 'YES' : 'NO' def create_copy_resources_script
UI.message "- Generating copy resources script at #{UI.path(library.copy_resources_script_path)}" do
copy_resources_script.resources << pods.map { |p| p.relative_resource_files }.flatten
copy_resources_script.save_as(library.copy_resources_script_path)
end end
end end
#-----------------------------------------------------------------------#
# @!group Installation products.
public
# @return [Library] the library for the target definition.
# #
# @note Generated by the {#add_target} step.
# #
def create_files(pods, sandbox) attr_reader :library
bridge_support_metadata_path = sandbox.root + @target_definition.bridge_support_name
UI.message "- Generating BridgeSupport metadata file at #{UI.path bridge_support_metadata_path}" do
bridge_support_generator_for(pods, sandbox).save_as(bridge_support_metadata_path)
copy_resources_script_for(pods).resources << @target_definition.bridge_support_name
end if @podfile.generate_bridge_support?
UI.message "- Generating xcconfig file at #{UI.path(sandbox.root + @target_definition.xcconfig_name)}" do # @return [PBXNativeTarget] the target generated by the installation
generate_xcconfig(pods, sandbox) # process.
xcconfig.save_as(sandbox.root + @target_definition.xcconfig_name) #
@target_definition.xcconfig = xcconfig # @note Generated by the {#add_target} step.
end #
attr_reader :target
UI.message "- Generating prefix header at #{UI.path(sandbox.root + @target_definition.prefix_header_name)}" do # @return [PBXFileReference] the file reference to the xcconfig file of
save_prefix_header_as(sandbox.root + @target_definition.prefix_header_name, pods) # the target.
end #
# @note Generated by the {#add_file_reference_for_support_files} step.
#
attr_reader :xcconfig_file_ref
UI.message "- Generating copy resources script at #{UI.path(sandbox.root + @target_definition.copy_resources_script_name)}" do #-----------------------------------------------------------------------#
copy_resources_script_for(pods).save_as(sandbox.root + @target_definition.copy_resources_script_name)
end # @!group Private helpers.
end
private private
def quoted(strings) # @return [Sandbox] sandbox the sandbox where the support files should
strings.map { |s| "\"#{s}\"" } # be generated.
#
def sandbox
project.sandbox
end
# Creates and caches the copy resource script.
#
# @return [CopyResourcesScript]
#
# TODO: This should be replaced by an Xcode copy resources build phase.
#
def copy_resources_script
@copy_resources_script ||= Generator::CopyResourcesScript.new
end
# Converts an array of strings to a single string where the each string
# is surrounded by double quotes and separated by a space. Used to
# represent strings in a xcconfig file.
#
# @param [Array<String>] strings
# a list of strings.
#
# @return [String] the resulting string.
#
def quote(strings)
strings.map { |s| %W|"#{s}"| }.join(" ")
end end
# @return [String] the default linker flags. `-ObjC` is always included
# while `-fobjc-arc` is included only if requested in the
# Podfile.
#
def default_ld_flags def default_ld_flags
flags = %w{-ObjC} flags = %w[ -ObjC ]
flags << '-fobjc-arc' if @podfile.set_arc_compatibility_flag? && self.requires_arc requires_arc = pods.any? { |pod| pod.requires_arc? }
if requires_arc && target_definition.podfile.set_arc_compatibility_flag?
flags << '-fobjc-arc'
end
flags.join(" ") flags.join(" ")
end end
end end
......
...@@ -44,7 +44,7 @@ config = Pod::Config.instance ...@@ -44,7 +44,7 @@ config = Pod::Config.instance
config.silent = true config.silent = true
config.repos_dir = SpecHelper.tmp_repos_path config.repos_dir = SpecHelper.tmp_repos_path
config.project_root = SpecHelper.temporary_directory config.project_root = SpecHelper.temporary_directory
Pod::Specification::Statistics.instance.cache_file = nil Pod::Specification::Set::Statistics.instance.cache_file = nil
require 'tmpdir' require 'tmpdir'
...@@ -77,3 +77,4 @@ VCR.configure do |c| ...@@ -77,3 +77,4 @@ VCR.configure do |c|
c.allow_http_connections_when_no_cassette = true c.allow_http_connections_when_no_cassette = true
end end
require "active_support/core_ext/string/strip"
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
TMP_POD_ROOT = ROOT + "tmp" + "podroot" unless defined? TMP_POD_ROOT describe TargetInstaller = Pod::Installer::TargetInstaller do
describe "In general" do
before do
@podfile = Pod::Podfile.new do
platform :ios
end
@target_definition = @podfile.target_definitions[:default]
@project = Pod::Project.new(config.sandbox)
@specification = fixture_spec('banana-lib/BananaLib.podspec')
@pods = [Pod::LocalPod.new(@specification, config.sandbox, Pod::Platform.ios)]
@installer = TargetInstaller.new(@project, @target_definition, @pods,)
end
describe Pod::Installer::TargetInstaller do it "returns the project" do
extend SpecHelper::TemporaryDirectory @installer.project.should == @project
end
before do it "returns the target_definition" do
@podfile = Pod::Podfile.new do @installer.target_definition.should == @target_definition
platform :ios
xcodeproj 'dummy'
end end
@target_definition = @podfile.target_definitions[:default]
@project = Pod::Project.new it "returns the pods of the target definition" do
@project.new_group('Targets Support Files') @installer.pods.should == @pods
end
end
@installer = Pod::Installer::TargetInstaller.new(@podfile, @project, @target_definition) describe "Installation" do
extend SpecHelper::TemporaryDirectory
before do
@podfile = Pod::Podfile.new do
platform :ios
xcodeproj 'dummy'
end
@target_definition = @podfile.target_definitions[:default]
@project = Pod::Project.new(config.sandbox)
specification = fixture_spec('banana-lib/BananaLib.podspec')
@pod = Pod::LocalPod.new(specification, config.sandbox, @target_definition.platform)
@installer = TargetInstaller.new(@project, @target_definition, [@pod])
specification.prefix_header_contents = '#import "BlocksKit.h"'
@pod.stubs(:root).returns(Pathname.new(fixture('banana-lib')))
end
@sandbox = Pod::Sandbox.new(TMP_POD_ROOT) def do_install!
FileUtils.cp_r(fixture('banana-lib'), TMP_POD_ROOT + 'BananaLib') # Prevent raise for missing dummy project.
@specification = fixture_spec('banana-lib/BananaLib.podspec') Pathname.any_instance.stubs(:exist?).returns(true)
@pods = [Pod::LocalPod.new(@specification, @sandbox, Pod::Platform.ios)] @pod.add_file_references_to_project(@project)
end @installer.install
end
def do_install! it 'adds a new static library target to the project' do
@pods.each { |pod| pod.add_file_references_to_project(@project) } do_install!
@installer.install!(@pods, @sandbox) @project.targets.count.should == 1
end @project.targets.first.name.should == @target_definition.label
end
it 'adds a new static library target to the project' do it 'adds the source files of each pod to the target of the Pod library' do
do_install! do_install!
@project.targets.count.should == 1 names = @installer.target.source_build_phase.files.map { |bf| bf.file_ref.name }
@project.targets.first.name.should == @target_definition.label names.should == [ "Banana.m" ]
end end
it "adds the user's build configurations to the target" do it "adds file references for the support files of the target" do
@project.user_build_configurations = { 'Debug' => :debug, 'Release' => :release, 'AppStore' => :release, 'Test' => :debug } do_install!
do_install! group = @project.support_files_group['Pods']
@project.targets.first.build_configurations.map(&:name).sort.should == %w{ AppStore Debug Release Test } group.children.map(&:display_name).sort.should == [
end "Pods-prefix.pch", "Pods-resources.sh", "Pods.xcconfig"
]
end
it 'adds each pod to the static library target' do #--------------------------------------#
@pods[0].expects(:add_build_files_to_target)
do_install! it "adds the user's build configurations to the target" do
end @project.user_build_configurations = { 'Debug' => :debug, 'Release' => :release, 'AppStore' => :release, 'Test' => :debug }
do_install!
@project.targets.first.build_configurations.map(&:name).sort.should == %w{ AppStore Debug Release Test }
end
# TODO: move to project it 'adds the sandbox header search paths to the xcconfig, with quotes' do
# it 'tells each pod to link its headers' do do_install!
# @pods[0].expects(:link_headers) @installer.library.xcconfig.to_hash['PODS_BUILD_HEADERS_SEARCH_PATHS'].should.include("\"#{config.sandbox.build_headers.search_paths.join('" "')}\"")
# do_install! end
# end
it 'adds the sandbox header search paths to the xcconfig, with quotes' do it 'does not add the -fobjc-arc to OTHER_LDFLAGS by default as Xcode 4.3.2 does not support it' do
do_install! do_install!
@installer.xcconfig.to_hash['PODS_BUILD_HEADERS_SEARCH_PATHS'].should.include("\"#{@sandbox.build_headers.search_paths.join('" "')}\"") @installer.library.xcconfig.to_hash['OTHER_LDFLAGS'].split(" ").should.not.include("-fobjc-arc")
end end
it 'does not add the -fobjc-arc to OTHER_LDFLAGS by default as Xcode 4.3.2 does not support it' do it 'adds the -fobjc-arc to OTHER_LDFLAGS if any pods require arc (to support non-ARC projects on iOS 4.0)' do
do_install! Pod::Podfile.any_instance.stubs(:set_arc_compatibility_flag? => true)
@installer.xcconfig.to_hash['OTHER_LDFLAGS'].split(" ").should.not.include("-fobjc-arc") @pod.top_specification.stubs(:requires_arc).returns(true)
end do_install!
@installer.library.xcconfig.to_hash['OTHER_LDFLAGS'].split(" ").should.include("-fobjc-arc")
end
it 'adds the -fobjc-arc to OTHER_LDFLAGS if any pods require arc (to support non-ARC projects on iOS 4.0)' do it "does not enable the GCC_WARN_INHIBIT_ALL_WARNINGS flag by default" do
@podfile.stubs(:set_arc_compatibility_flag? => true) do_install!
@specification.stubs(:requires_arc).returns(true) @installer.target.build_configurations.each do |config|
do_install! config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'].should == 'NO'
@installer.xcconfig.to_hash['OTHER_LDFLAGS'].split(" ").should.include("-fobjc-arc") end
end end
it "does not enable the GCC_WARN_INHIBIT_ALL_WARNINGS flag by default" do it "enables the GCC_WARN_INHIBIT_ALL_WARNINGS flag" do
do_install! @podfile.inhibit_all_warnings!
@installer.target.build_configurations.each do |config| do_install!
config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'].should == 'NO' @installer.target.build_configurations.each do |config|
config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'].should == 'YES'
end
end end
end
it "enables the GCC_WARN_INHIBIT_ALL_WARNINGS flag" do it "creates and xcconfig file" do
@podfile.inhibit_all_warnings! do_install!
do_install! xcconfig = config.sandbox.root + 'Pods.xcconfig'
@installer.target.build_configurations.each do |config| xcconfig.read.should == <<-EOS.strip_heredoc.gsub(/\n$/, '')
config.build_settings['GCC_WARN_INHIBIT_ALL_WARNINGS'].should == 'YES' ALWAYS_SEARCH_USER_PATHS = YES
OTHER_LDFLAGS = -ObjC -framework SystemConfiguration
HEADER_SEARCH_PATHS = ${PODS_HEADERS_SEARCH_PATHS}
PODS_ROOT = ${SRCROOT}/Pods
PODS_BUILD_HEADERS_SEARCH_PATHS = "${PODS_ROOT}/BuildHeaders"
PODS_PUBLIC_HEADERS_SEARCH_PATHS = "${PODS_ROOT}/Headers"
PODS_HEADERS_SEARCH_PATHS = ${PODS_PUBLIC_HEADERS_SEARCH_PATHS}
EOS
end end
end
it "creates a prefix header, including the contents of the specification's prefix header file" do it "creates a prefix header, including the contents of the specification's prefix header" do
do_install! @pod.top_specification.prefix_header_contents = '#import "BlocksKit.h"'
prefix_header = @sandbox.root + 'Pods.pch' do_install!
@installer.save_prefix_header_as(prefix_header, @pods) prefix_header = config.sandbox.root + 'Pods-prefix.pch'
prefix_header.read.should == <<-EOS prefix_header.read.should == <<-EOS.strip_heredoc
#ifdef __OBJC__ #ifdef __OBJC__
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#endif #endif
#import <BananaTree/BananaTree.h> #import "BlocksKit.h"
EOS EOS
end end
it "creates a prefix header, including the contents of the specification's prefix header" do it "creates a bridge support file" do
do_install! Pod::Podfile.any_instance.stubs(:generate_bridge_support? => true)
prefix_header = @sandbox.root + 'Pods.pch' Pod::Generator::BridgeSupport.any_instance.expects(:save_as).once
@specification.prefix_header_contents = '#import "BlocksKit.h"' do_install!
@installer.save_prefix_header_as(prefix_header, @pods) end
prefix_header.read.should == <<-EOS
#ifdef __OBJC__ it "creates a create copy resources script" do
#import <UIKit/UIKit.h> do_install!
#endif script = config.sandbox.root + 'Pods-resources.sh'
script.read.should.include?('logo-sidebar.png')
#import "BlocksKit.h" end
EOS
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