Commit 3ad07c83 authored by Fabio Pelosin's avatar Fabio Pelosin

[Core Extraction] Adapted Project (with clean up) - Added Project::Library class.

parent 29d6551e
......@@ -74,6 +74,12 @@ module Pod
end
end
# @return [Sandbox]
#
def sandbox
@sandbox ||= Sandbox.new(project_pods_root)
end
module Mixin
def config
Config.instance
......
require 'xcodeproj'
# Xcodeproj::Project::Object::PBXCopyFilesBuildPhase.instance_eval do
# def self.new_pod_dir(project, pod_name, path)
# new(project, nil, {
# "dstPath" => "Pods/#{path}",
# "name" => "Copy #{pod_name} Public Headers",
# })
# end
# end
module Pod
# Provides support for generating the Pods project
#
class Project < Xcodeproj::Project
include Config::Mixin
attr_reader :support_files_group
# @return [Sandbox] the sandbox that contains the project.
#
attr_reader :sandbox
def initialize(*)
super
podfile_path = config.project_podfile.relative_path_from(config.project_pods_root).to_s
podfile_ref = new_file(podfile_path)
podfile_ref.xc_language_specification_identifier = 'xcode.lang.ruby'
new_group('Pods')
# @param [Sandbox] sandbox @see #sandbox
#
def initialize(sandbox)
super(nil)
@sandbox = sandbox
@support_files_group = new_group('Targets Support Files')
@user_build_configurations = []
@libraries = []
end
def user_build_configurations=(user_build_configurations)
@user_build_configurations = user_build_configurations
# The configurations at the top level only need to exist, they don't hold
# any build settings themselves, that's left to `add_pod_target`.
user_build_configurations.each do |name, _|
unless build_configurations.map(&:name).include?(name)
bc = new(XCBuildConfiguration)
bc.name = name
build_configurations << bc
end
# @return [Pathname] the path of the Pods project.
#
def path
sandbox.project_path
end
# @return [String] a string representation suited for debugging.
#
def inspect
"#<#{self.class}>"
end
# Shortcut access to the `Pods' PBXGroup.
#--------------------------------------#
#@!group Working with groups
# @return [PBXGroup] the group where the support files for the Pod
# libraries should be added.
#
attr_reader :support_files_group
# Returns the `Pods` group, creating it if needed.
#
# @return [PBXGroup] the group.
#
def pods
@pods ||= self['Pods'] || new_group('Pods')
@pods ||= new_group('Pods')
end
# Shortcut access to the `Local Pods' PBXGroup.
# Returns the `Local Pods` group, creating it if needed. This group is used
# to contain locally sourced pods.
#
# @return [PBXGroup] the group.
#
def local_pods
@local_pods ||= self['Local Pods'] || new_group('Local Pods')
@local_pods ||= new_group('Local Pods')
end
# Adds a group as child to the `Pods' group namespacing subspecs.
# Adds a group as child to the `Pods` group namespacing subspecs.
#
# TODO: Pass the specification directly and don't expose the pods groups.
#
def add_spec_group(name, parent_group)
current_group = parent_group
group = nil
......@@ -59,19 +74,69 @@ module Pod
group
end
def add_pod_target(name, platform)
target = new_target(:static_library, name, platform.name)
#--------------------------------------#
settings = {}
if platform.requires_legacy_ios_archs?
settings['ARCHS'] = "armv6 armv7"
#@!group Manipulating the project
# @return [Array<Library>] the libraries generated from the target
# definitions of the Podfile.
#
attr_reader :libraries
# Adds a file reference to the podfile.
#
# @param [#to_s] path
# the path of the podfile
#
# @return [PBXFileReference]
#
def add_podfile(podfile_path)
podfile_path = Pathname.new(podfile_path.to_s)
podfile_ref = new_file(podfile_path.relative_path_from(path.dirname))
podfile_ref.xc_language_specification_identifier = 'xcode.lang.ruby'
podfile_ref
end
# Creates the user build configurations for the Pods project.
#
# @note The configurations at the top level only need to exist, they
# don't hold any build settings themselves, that's left to
# `add_pod_library`.
#
# @return [void]
#
# TODO: why is this needed?
#
def user_build_configurations=(user_build_configurations)
@user_build_configurations = user_build_configurations
user_build_configurations.each do |name, _|
unless build_configurations.map(&:name).include?(name)
bc = new(XCBuildConfiguration)
bc.name = name
build_configurations << bc
end
end
end
if platform == :ios && platform.deployment_target
# TODO: add for osx as well
settings['IPHONEOS_DEPLOYMENT_TARGET'] = platform.deployment_target.to_s
# Creates a static library target for the given target_definition.
#
# @param [TargetDefinition] target_definition
# the target definition of the library.
#
# @raise If the target definition doesn't specifies a platform.
#
# @return [Library] the library for the created target.
#
def add_pod_library(target_definition)
name = target_definition.label
platform = target_definition.platform
unless platform
raise Informative, "Missing platform for #{target_definition}"
end
settings = settings_for_platform(platform)
target = new_target(:static_library, name, platform.name)
target.build_settings('Debug').merge!(settings)
target.build_settings('Release').merge!(settings)
......@@ -85,7 +150,265 @@ module Pod
end
end
target
lib = Library.new(target_definition, target)
libraries << lib
lib
end
#--------------------------------------#
#@!group Helpers
private
# Returns the Xcode build settings for a target with the given platform.
#
# @param [Platform] platform
# the platform for which the build settings are needed.
#
# @return [Hash] the build settings.
#
def settings_for_platform(platform)
settings = {}
settings['ARCHS'] = "armv6 armv7" if platform.requires_legacy_ios_archs?
if dt = platform.deployment_target
if platform == :ios
settings['IPHONEOS_DEPLOYMENT_TARGET'] = dt.to_s
else
# TODO: add MACOSX_DEPLOYMENT_TARGET
end
end
settings
end
#-------------------------------------------------------------------------#
# Describes a library generated for the Pods project.
#
class Library
include Config::Mixin
# @return [PBXNativeTarget] the target definition of the Podfile that
# generated this library.
#
attr_reader :target_definition
# @return [PBXNativeTarget] the target generated in the Pods project for
# this library.
#
attr_reader :target
# @param [TargetDefinition] target_definition @see target_definition
# @param [PBXNativeTarget] target @see target
# @param [Project] project @see project
#
def initialize(target_definition, target)
@target_definition = target_definition
@target = target
end
def label
target_definition.label
end
#-----------------------------------------------------------------------#
# @!group User project
# @return [Xcodeproj::Project]
# the project that will be integrated.
#
def user_project
@user_project ||= Xcodeproj::Project.new(user_project_path)
end
# Returns the path of the user project that the {TargetDefinition}
# should integrate.
#
# @raises If the project is implicit and there are multiple projects.
#
# @raises If the path doesn't exits.
#
# @return [Pathname] the path of the user project.
#
def user_project_path
unless @user_project_path
if target_definition.user_project_path
@user_project_path = Pathname.new(config.project_root + target_definition.user_project_path)
unless @user_project_path.exist?
raise Informative, "Unable to find the Xcode project `#{@user_project_path}` for the target `#{label}`."
end
else
xcodeprojs = Pathname.glob(config.project_root + '*.xcodeproj')
if xcodeprojs.size == 1
@user_project_path = xcodeprojs.first
else
raise Informative, "Could not automatically select an Xcode project. " \
"Specify one in your Podfile like so:\n\n" \
" xcodeproj 'path/to/Project.xcodeproj'\n"
end
end
end
@user_project_path
end
# Returns a list of the targets from the project of {TargetDefinition}
# that needs to be integrated.
#
# @note The method first looks if there is a target specified with
# the `link_with` option of the {TargetDefinition}. Otherwise
# it looks for the target that has the same name of the target
# definition. Finally if no target was found the first
# encountered target is returned (it is assumed to be the one
# to integrate in simple projects).
#
# @note This will only return targets that do **not** already have
# the Pods library in their frameworks build phase.
#
# @return [Array<PBXNativeTarget>] the list of targets that the Pods
# lib should be linked with.
#
def user_targets
unless @targets
if link_with = target_definition.link_with
@targets = user_project.targets.select { |t| link_with.include? t.name }
raise Informative, "Unable to find a target named `#{link_with.to_sentence}` to link with target definition `#{target_definition.name}`" if @targets.empty?
elsif target_definition.name != :default
target = user_project.targets.find { |t| t.name == target_definition.name.to_s }
@targets = [ target ].compact
raise Informative, "Unable to find a target named `#{target_definition.name.to_s}`" if @targets.empty?
else
@targets = [ user_project.targets.first ].compact
raise Informative, "Unable to find a target" if @targets.empty?
end
end
@targets
end
#-----------------------------------------------------------------------#
# @!group TargetInstaller & UserProjectIntegrator helpers
# @return [String] the name of the library.
#
def name
"lib#{target_definition.label}.a"
end
# @return [Project] the Pods project.
#
def project
target.project
end
# Computes the relative path of a sandboxed file from the `$(SRCROOT)` of
# the user's project.
#
# @param [Pathname] path
#
# @return [String] the computed path.
#
def relative_to_srcroot(path = nil)
base_path = path ? config.project_pods_root + path : config.project_pods_root
(base_path).relative_path_from(user_project_path.dirname).to_s
end
def relative_pods_root
"${SRCROOT}/#{relative_to_srcroot}"
end
# @return [Pathname] the folder where to store the support files of this
# library.
#
# TODO: each library should have a folder for its support files
#
def support_files_root
project.sandbox.root
end
#---------------------------------------#
# @return [Xcodeproj::Config] the configuration file of the library
#
# @note The configuration is generated by the {TargetInstaller} and
# used by {UserProjectIntegrator} to check for any overridden
# values.
#
attr_accessor :xcconfig
# @return [String] the name of the xcconfig file relative to this target.
#
def xcconfig_name
"#{label}.xcconfig"
end
# @return [Pathname] the absolute path of the xcconfig file.
#
def xcconfig_path
support_files_root + xcconfig_name
end
# @return [String] the path of the xcconfig file relative to the root of
# the user project.
#
def xcconfig_relative_path
relative_to_srcroot("#{xcconfig_name}").to_s
end
#---------------------------------------#
# @return [String] the name of the copy resources script relative to this
# target.
#
def copy_resources_script_name
"#{label}-resources.sh"
end
# @return [Pathname] the absolute path of the copy resources script.
#
def copy_resources_script_path
support_files_root + copy_resources_script_name
end
# @return [String] the path of the copy resources script relative to the
# root of the user project.
#
def copy_resources_script_relative_path
"${SRCROOT}/#{relative_to_srcroot("#{copy_resources_script_name}")}"
end
#---------------------------------------#
# @return [String] the name of the prefix header file relative to this
# target.
#
def prefix_header_name
"#{label}-prefix.pch"
end
# @return [Pathname] the absolute path of the prefix header file.
#
def prefix_header_path
support_files_root + prefix_header_name
end
#---------------------------------------#
# @return [String] the name of the bridge support file relative to this
# target.
#
def bridge_support_name
"#{label}.bridgesupport"
end
# @return [Pathname] the absolute path of the bridge support file.
#
def bridge_support_path
support_files_root + bridge_support_name
end
end
end
end
require File.expand_path('../../spec_helper', __FILE__)
describe 'Pod::Project' do
describe Pod::Project do
describe "In general" do
before do
@project = Pod::Project.new
@project = Pod::Project.new(config.sandbox)
end
it "adds the Podfile configured as a Ruby file" do
f = @project['Podfile']
f.name.should == 'Podfile'
f.source_tree.should == 'SOURCE_ROOT'
f.xc_language_specification_identifier.should == 'xcode.lang.ruby'
f.path.should == '../Podfile'
it "returns the sandbox used for the project" do
@project.sandbox.should == config.sandbox
end
it "creates the support file group on initialization" do
@project.support_files_group.name.should == 'Targets Support Files'
end
it "returns its path" do
@project.path.should == config.sandbox.project_path
end
it "returns the `Pods` group" do
@project.pods.name.should == 'Pods'
end
it "returns the `Local Pods` group" do
@project.local_pods.name.should == 'Local Pods'
end
it "adds a group to the `Pods' group" do
it "adds a group for a specification" do
group = @project.add_spec_group('JSONKit', @project.pods)
@project.pods.children.should.include?(group)
g = @project['Pods/JSONKit']
......@@ -29,73 +42,237 @@ describe 'Pod::Project' do
g.children.should.be.empty?
end
# it "creates a copy build header phase which will copy headers to a specified path" do
# @project.targets.new
# phase = @project.targets.first.copy_files_build_phases.new_pod_dir("SomePod", "Path/To/Source")
# find_object({
# 'isa' => 'PBXCopyFilesBuildPhase',
# 'dstPath' => 'Pods/Path/To/Source',
# 'name' => 'Copy SomePod Public Headers'
# }).should.not == nil
# @project.targets.first.build_phases.should.include phase
# end
it "adds the Podfile configured as a Ruby file" do
@project.add_podfile(config.project_podfile)
f = @project['Podfile']
f.name.should == 'Podfile'
f.source_tree.should == 'SOURCE_ROOT'
f.xc_language_specification_identifier.should == 'xcode.lang.ruby'
f.path.should == '../Podfile'
end
it "adds build configurations named after every configuration across all of the user's projects" do
@project.user_build_configurations = { 'Debug' => :debug, 'Release' => :release, 'Test' => :debug, 'AppStore' => :release }
@project.build_configurations.map(&:name).sort.should == %w{ AppStore Debug Release Test }
end
end
describe "Libraries" do
before do
@project = Pod::Project.new(config.sandbox)
podfile = Pod::Podfile.new do
platform :ios, '4.3'
pod 'JSONKit'
end
@target_definition = podfile.target_definitions.values.first
end
it "adds build configurations named after every configuration across all of the user's projects to a target" do
@project.user_build_configurations = { 'Debug' => :debug, 'Release' => :release, 'Test' => :debug, 'AppStore' => :release }
target = @project.add_pod_target('SomeTarget', Pod::Platform.ios)
library = @project.add_pod_library(@target_definition)
target = library.target
target.build_settings('Test')["VALIDATE_PRODUCT"].should == nil
target.build_settings('AppStore')["VALIDATE_PRODUCT"].should == "YES"
end
describe "concerning its :ios targets" do
it "sets VALIDATE_PRODUCT to YES for the Release configuration" do
target = Pod::Project.new.add_pod_target('Pods', Pod::Platform.ios)
target.build_settings('Release')["VALIDATE_PRODUCT"].should == "YES"
it "sets ARCHS to 'armv6 armv7' for both configurations if the deployment target is less than 4.3 for iOS targets" do
@target_definition.platform = Pod::Platform.new(:ios, '4.2')
library = @project.add_pod_library(@target_definition)
target = library.target
target.build_settings('Debug')["ARCHS"].should == "armv6 armv7"
target.build_settings('Release')["ARCHS"].should == "armv6 armv7"
end
before do
@lib = @project.add_pod_library(@target_definition)
@target = @lib.target
end
it "uses standard ARCHs if deployment target is 4.3 or above" do
@target.build_settings('Debug')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
@target.build_settings('Release')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
end
it "sets VALIDATE_PRODUCT to YES for the Release configuration for iOS targets" do
@lib.target.build_settings('Release')["VALIDATE_PRODUCT"].should == "YES"
end
it "sets IPHONEOS_DEPLOYMENT_TARGET for iOS targets" do
@target.build_settings('Debug')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.3"
@target.build_settings('Release')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.3"
end
describe "concerning its :ios targets with a deployment target" do
it "returns the added libraries" do
@project.libraries.should == [ @lib ]
end
end
end
#-----------------------------------------------------------------------------#
describe Pod::Project::Library do
describe "In general" do
before do
@project = Pod::Project.new
project = Pod::Project.new(config.sandbox)
podfile = Pod::Podfile.new do
platform :ios
pod 'JSONKit'
end
@target_definition = podfile.target_definitions.values.first
@lib = project.add_pod_library(@target_definition)
end
it "sets ARCHS to 'armv6 armv7' for both configurations if the deployment target is less than 4.3" do
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.0"))
target.build_settings('Debug')["ARCHS"].should == "armv6 armv7"
target.build_settings('Release')["ARCHS"].should == "armv6 armv7"
it "returns the target_definition that generated it" do
@lib.target_definition.should == @target_definition
end
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.1"))
target.build_settings('Debug')["ARCHS"].should == "armv6 armv7"
target.build_settings('Release')["ARCHS"].should == "armv6 armv7"
it "returns it target in the Pods project" do
@lib.target.name.should == 'Pods'
end
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.2"))
target.build_settings('Debug')["ARCHS"].should == "armv6 armv7"
target.build_settings('Release')["ARCHS"].should == "armv6 armv7"
it "returns the label of the target definition" do
@lib.label.should == 'Pods'
end
end
it "uses standard ARCHs if deployment target is 4.3 or above" do
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.3"))
target.build_settings('Debug')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
target.build_settings('Release')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
#---------------------------------------#
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.4"))
target.build_settings('Debug')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
target.build_settings('Release')["ARCHS"].should == "$(ARCHS_STANDARD_32_BIT)"
describe "User project" do
before do
user_project_path = fixture('SampleProject/SampleProject.xcodeproj')
project = Pod::Project.new(config.sandbox)
podfile = Pod::Podfile.new do
platform :ios
xcodeproj user_project_path
pod 'JSONKit'
end
@target_definition = podfile.target_definitions.values.first
@lib = project.add_pod_library(@target_definition)
end
it "returns the user project path" do
path = fixture('SampleProject/SampleProject.xcodeproj')
@lib.user_project_path.should == path
end
it "raises if no project could be selected" do
@target_definition.stubs(:user_project_path).returns(nil)
Pathname.any_instance.stubs(:exist?).returns(true)
lambda { @lib.user_project_path }.should.raise Pod::Informative
end
it "raises if the project path doesn't exist" do
Pathname.any_instance.stubs(:exist?).returns(false)
lambda { @lib.user_project_path }.should.raise Pod::Informative
end
it "returns the user project" do
@lib.user_project.class.should == Xcodeproj::Project
end
it "returns the user targets associated with the target definition" do
@lib.user_targets.all? { |t| t.isa == 'PBXNativeTarget' }.should.be.true
@lib.user_targets.map(&:name).should == [ 'SampleProject' ]
end
it "uses the targets specified to link with by the target definition" do
@target_definition.stubs(:link_with).returns(['TestRunner'])
@target_definition.stubs(:name).returns('NON-EXISTING')
@lib.user_targets.first.name.should == 'TestRunner'
end
it "it raises if it can't find any target specified to link with by the target definition" do
@target_definition.stubs(:link_with).returns(['NON-EXISTING'])
lambda { @lib.user_targets }.should.raise Pod::Informative
end
it "uses the target with the same name if the target definition name is different from `:default'" do
@target_definition.stubs(:name).returns('TestRunner')
@lib.user_targets.first.name.should == 'TestRunner'
end
it "it raises if it can't find a target with the same name of the target definition" do
@target_definition.stubs(:name).returns('NON-EXISTING')
lambda { @lib.user_targets }.should.raise Pod::Informative
end
it "uses the first target in the user's project if no explicit target is specified for the default target definition" do
project = Xcodeproj::Project.new(@lib.user_project_path)
@lib.user_targets.should == [ project.targets.first ]
end
end
#---------------------------------------#
describe "TargetInstaller & UserProjectIntegrator" do
before do
user_project_path = fixture('SampleProject/SampleProject.xcodeproj')
project = Pod::Project.new(config.sandbox)
podfile = Pod::Podfile.new do
platform :ios
xcodeproj user_project_path
pod 'JSONKit'
end
@target_definition = podfile.target_definitions.values.first
@lib = project.add_pod_library(@target_definition)
end
it "sets IPHONEOS_DEPLOYMENT_TARGET for both configurations" do
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios))
target.build_settings('Debug')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.3"
target.build_settings('Release')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.3"
#---------------------------------------#
target = @project.add_pod_target('Pods', Pod::Platform.new(:ios, :deployment_target => "4.0"))
target.build_settings('Debug')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.0"
target.build_settings('Release')["IPHONEOS_DEPLOYMENT_TARGET"].should == "4.0"
it "returns the name of its product" do
@lib.name.should == 'libPods.a'
end
it "returns it Pods project" do
@lib.project.path.should == config.sandbox.project_path
end
#---------------------------------------#
it "stores the xcconfig" do
@lib.xcconfig = Xcodeproj::Config.new({'PODS_ROOT' => '${SRCROOT}'})
@lib.xcconfig.to_hash['PODS_ROOT'].should == '${SRCROOT}'
end
it "returns the xcconfig name" do
@lib.xcconfig_name.should == 'Pods.xcconfig'
end
it "returns the absolute path of the xcconfig file" do
@lib.xcconfig_path.to_s.should.include?('Pods/Pods.xcconfig')
end
it "returns the path of the xcconfig file relative to the user project" do
@lib.xcconfig_relative_path.should == '../../../tmp/Pods/Pods.xcconfig'
end
it "returns the resources script name" do
@lib.copy_resources_script_name.should == 'Pods-resources.sh'
end
it "returns the absolute path of the resources script" do
@lib.copy_resources_script_path.to_s.should.include?('Pods/Pods-resources.sh')
end
it "returns the path of the resources script relative to the user project" do
@lib.copy_resources_script_relative_path.should == '${SRCROOT}/../../../tmp/Pods/Pods-resources.sh'
end
it "returns the prefix header file name" do
@lib.prefix_header_name.should == 'Pods-prefix.pch'
end
it "returns the absolute path of the prefix header file" do
@lib.prefix_header_path.to_s.should.include?('Pods/Pods-prefix.pch')
end
it "returns the bridge support file name" do
@lib.bridge_support_name.should == 'Pods.bridgesupport'
end
it "returns the absolute path of the bridge support file" do
@lib.bridge_support_path.to_s.should.include?('Pods/Pods.bridgesupport')
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