Commit cc958fde authored by Fabio Pelosin's avatar Fabio Pelosin

[UserProjectIntegrator] Clean up.

parent 2421ef03
...@@ -6,69 +6,101 @@ require 'active_support/core_ext/array/conversions' ...@@ -6,69 +6,101 @@ require 'active_support/core_ext/array/conversions'
module Pod module Pod
class Installer class Installer
# The {UserProjectIntegrator} integrates the libraries generated by
# TargetDefinitions of the {Podfile} with their correspondent user project.
#
class UserProjectIntegrator class UserProjectIntegrator
include Pod::Config::Mixin include Pod::Config::Mixin
# @return [Podfile] the podfile that should be integrated with the user
# projects.
#
attr_reader :podfile
# @param [Podfile] podfile @see #podfile.
#
def initialize(podfile) def initialize(podfile)
@podfile = podfile @podfile = podfile
end end
# Integrates the user projects associated with the {TargetDefinitions}
# with the Pods project and its products.
#
# @return [void]
#
def integrate! def integrate!
create_workspace! create_workspace!
# Only need to write out the user's project if any of the target
# integrators actually did some work.
target_integrators.map(&:integrate!) target_integrators.map(&:integrate!)
end end
# creates the workspace containing the Pods project and the user projects
# should be saved.
#
# @return [void]
#
def create_workspace!
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
[pods_project_path, *user_project_paths].each do |project_path|
project_path = project_path.relative_path_from(config.project_root).to_s
workspace << project_path unless workspace.include?(project_path)
end
workspace.save_as(workspace_path)
UI.notice "From now on use `#{workspace_path.basename}'." unless workspace_path.exist?
end
# @return [Pathname] the path where the workspace containing the Pods
# project and the user projects should be saved.
#
def workspace_path def workspace_path
@podfile.workspace || raise(Informative, "Could not automatically select an Xcode workspace. " \ podfile.workspace || raise(Informative, "Could not automatically select an Xcode workspace. " \
"Specify one in your Podfile.") "Specify one in your Podfile.")
end end
# @return [Pathname] the path of the Pods project.
#
def pods_project_path def pods_project_path
config.project_root + "Pods/Pods.xcodeproj" config.project_root + "Pods/Pods.xcodeproj"
end end
# @return [Array<TargetIntegrator>] the target integrators for the non
# empty target definitions.
#
def target_integrators def target_integrators
@target_integrators ||= @podfile.target_definitions.values.map do |definition| @target_integrators ||= @podfile.target_definitions.values.map do |definition|
TargetIntegrator.new(definition) unless definition.empty? TargetIntegrator.new(definition) unless definition.empty?
end.compact end.compact
end end
# @return [Array<Pathname>] the paths of all the user projects referenced
# by the target definitons.
#
def user_project_paths def user_project_paths
@podfile.target_definitions.values.map do |td| @podfile.target_definitions.values.map do |td|
next if td.empty? next if td.empty?
td.user_project.path #|| raise(Informative, "Could not resolve the Xcode project in which the " \ td.user_project.path
# "`#{td.name}' target should be integrated.") end.compact.uniq
end.compact
end end
def create_workspace! #-------------------------------------------------------------------------#
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
[pods_project_path, *user_project_paths].each do |project_path|
project_path = project_path.relative_path_from(config.project_root).to_s
workspace << project_path unless workspace.include?(project_path)
end
unless workspace_path.exist? || config.silent?
UI.notice "From now on use `#{workspace_path.basename}'."
end
workspace.save_as(workspace_path)
end
# This class is responsible for integrating the library generated by a
# {TargetDefinition} with its destination project.
#
class TargetIntegrator class TargetIntegrator
include Pod::Config::Mixin include Pod::Config::Mixin
# @return [TargetDefinition]
# the target definition whose library should be integrated.
#
attr_reader :target_definition attr_reader :target_definition
# @param [TargetDefinition] target_definition @see #target_definition
#
def initialize(target_definition) def initialize(target_definition)
@target_definition = target_definition @target_definition = target_definition
end end
def inspect
"#<#{self.class} for target `#{@target_definition.label}'>"
end
# Integrates the user project targets. Only the targets that do **not** # Integrates the user project targets. Only the targets that do **not**
# already have the Pods library in their frameworks build phase are # already have the Pods library in their frameworks build phase are
# processed. # processed.
...@@ -77,112 +109,162 @@ module Pod ...@@ -77,112 +109,162 @@ module Pod
# #
def integrate! def integrate!
return if targets.empty? return if targets.empty?
message = "Integrating `#{target_definition.lib_name}' into " \
UI.section("Integrating `#{@target_definition.lib_name}' into #{'target'.pluralize(targets.size)} " \ "#{'target'.pluralize(targets.size)} `#{targets.map(&:name).to_sentence}' " \
"`#{targets.map(&:name).to_sentence}' of Xcode project #{UI.path user_project_path}.") do "of project #{UI.path user_project_path}."
UI.section(message) do
add_xcconfig_base_configuration add_xcconfig_base_configuration
add_pods_library add_pods_library
add_copy_resources_script_phase add_copy_resources_script_phase
user_project.save_as(@target_definition.user_project.path) user_project.save_as(target_definition.user_project.path)
end end
end end
# @return [Pathname] the path of the user project. # @return [Xcodeproj::Project]
# the project that will be integrated.
# #
# @raises If the path doesn't exits. 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 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 def user_project_path
if path = @target_definition.user_project.path path = target_definition.user_project.path
unless path
raise Informative, "Could not automatically select an Xcode project.\n" \
"Specify one in your Podfile like so:\n\n xcodeproj 'path/to/NAME.xcodeproj'"
end
unless path.exist? unless path.exist?
raise Informative, "The Xcode project `#{path}' does not exist." raise Informative, "The Xcode project `#{path}' does not exist."
end end
path path
else
raise Informative, "Could not automatically select an Xcode project.\n" \
"Specify one in your Podfile like so:\n\n" \
" xcodeproj 'path/to/XcodeProject'"
end
end end
# @return [Xcodeproj::Project] Returns the project of the user. # Returns a list of the targets from the project of {TargetDefinition}
# that needs to be integrated.
# #
def user_project # The method first looks if there is a target specified with the
@user_project ||= Xcodeproj::Project.new(user_project_path) # `link_with` option of the {TargetDefinition}. Otherwise it looks for
end # the target that has the same name of the target definition.
# Finally if no target was found the first encountered target is
# This returns a list of the targets from the user’s project to which # returned (it is assumed to be the one to integrate in simple
# this Pods static library should be linked. If no explicit target was # projects).
# specified, then the first encountered target is assumed.
# #
# In addition this will only return targets that do **not** already # In addition this will only return targets that do **not** already
# have the Pods library in their frameworks build phase. # have the Pods library in their frameworks build phase.
# #
# @return [Array<PBXNativeTarget>] Returns the list of targets that # @return [Array<PBXNativeTarget>]
# the Pods lib should be linked with. # the list of targets that the Pods lib should be linked with.
#
def targets def targets
@targets ||= begin unless @targets
if link_with = @target_definition.link_with if link_with = target_definition.link_with
# Find explicitly linked targets. targets = user_project.targets.select { |t| link_with.include? t.name }
user_project.targets.select do |target| raise Informative, "Unable to find a target named `#{link_with.to_sentence}` to link with target definition `#{target_definition.name}`" if targets.empty?
link_with.include? target.name elsif target_definition.name != :default
end target = user_project.targets.find { |t| t.name == target_definition.name.to_s }
elsif @target_definition.name != :default targets = [target]
# Find the target with the matching name. raise Informative, "Unable to find a target named `#{target_definition.name.to_s}`" unless target
target = user_project.targets.find { |target| target.name == @target_definition.name.to_s }
raise Informative, "Unable to find a target named `#{@target_definition.name.to_s}'" unless target
[target]
else else
# Default to the first, which in a simple project is probably an app target. targets = [user_project.targets.first]
[user_project.targets.first] raise Informative, "Unable to find a target" if targets.empty?
end.reject do |target| end
# Reject any target that already has this Pods library in one of its frameworks build phases @targets = targets.reject do |target|
target.frameworks_build_phase.files.any? { |build_file| build_file.file_ref.name == @target_definition.lib_name } target.frameworks_build_phase.files.any? { |bf| bf.file_ref.name == target_definition.lib_name }
end end
end end
@targets
end end
#@!group Integration
# Adds the `xcconfig` configurations files generated for the current
# {TargetDefinition} to the build configurations of the targets that
# should be integrated.
#
# It also checks if any build setting of the build configurations
# overrides the `xcconfig` file and warns the user.
#
# TODO: If the xcconfig is already set don't override it and inform the
# user.
#
# @return [void]
#
def add_xcconfig_base_configuration def add_xcconfig_base_configuration
xcconfig = user_project.new_file(@target_definition.xcconfig_relative_path) xcconfig_ref = user_project.new_file(target_definition.xcconfig_relative_path)
targets.each do |target| targets.each do |target|
config_build_names_by_overriden_key = {} check_overridden_build_settings(target_definition.xcconfig, target)
target.build_configurations.each do |config| target.build_configurations.each do |config|
config_name = config.name config.base_configuration_reference = xcconfig_ref
if @target_definition.xcconfig
@target_definition.xcconfig.attributes.each do |key, value|
target_value = config.build_settings[key]
if target_value && !target_value.include?('$(inherited)')
config_build_names_by_overriden_key[key] ||= []
config_build_names_by_overriden_key[key] << config_name
end end
end end
end end
config.base_configuration_reference = xcconfig # Informs the user about any build setting of the target which might
# override the given xcconfig file.
#
# @return [void]
#
def check_overridden_build_settings(xcconfig, target)
return unless xcconfig
configs_by_overridden_key = {}
target.build_configurations.each do |config|
xcconfig.attributes.keys.each do |key|
configs_by_overridden_key[key] ||= []
target_value = config.build_settings[key]
if target_value && !target_value.include?('$(inherited)')
configs_by_overridden_key[key] << config.name
end
end end
config_build_names_by_overriden_key.each do |key, config_build_names| configs_by_overridden_key.each do |key, config_names|
name = "#{target.name} [#{config_build_names.join(' - ')}]" name = "#{target.name} [#{config_names.join(' - ')}]"
actions = [ "Use the `$(inherited)' flag, or", "Remove the build settings from the target." ] actions = [ "Use the `$(inherited)' flag, or", "Remove the build settings from the target." ]
UI.warn("The target `#{name}' overrides the `#{key}' build setting defined in `#{@target_definition.xcconfig_relative_path}'.", actions) UI.warn("The target `#{name}' overrides the `#{key}' build setting defined in `#{target_definition.xcconfig_relative_path}'.", actions)
end end
end end
end end
# Adds a file reference to the library of the {TargetDefinition} and
# adds it to the frameworks build phase of the targets.
#
# @return [void]
#
def add_pods_library def add_pods_library
frameworks = user_project.frameworks_group frameworks = user_project.frameworks_group
pods_library = frameworks.new_static_library(@target_definition.label) pods_library = frameworks.new_static_library(target_definition.label)
targets.each do |target| targets.each do |target|
target.frameworks_build_phase.add_file_reference(pods_library) target.frameworks_build_phase.add_file_reference(pods_library)
end end
end end
# Adds a shell script build phase responsible to copy the resources
# generated by the TargetDefinition to the bundle of the product of the
# targets.
#
# @return [void]
#
def add_copy_resources_script_phase def add_copy_resources_script_phase
targets.each do |target| targets.each do |target|
phase = target.new_shell_script_build_phase('Copy Pods Resources') phase = target.new_shell_script_build_phase('Copy Pods Resources')
phase.shell_script = %{"#{@target_definition.copy_resources_script_relative_path}"\n} phase.shell_script = %{"#{target_definition.copy_resources_script_relative_path}"\n}
end
end end
def inspect
"#<#{self.class} for target `#{target_definition.label}'>"
end end
end end
end end
......
...@@ -3,24 +3,13 @@ require File.expand_path('../../spec_helper', __FILE__) ...@@ -3,24 +3,13 @@ require File.expand_path('../../spec_helper', __FILE__)
describe Pod::Installer::UserProjectIntegrator do describe Pod::Installer::UserProjectIntegrator do
extend SpecHelper::TemporaryDirectory extend SpecHelper::TemporaryDirectory
def integrate!
@integrator = Pod::Installer::UserProjectIntegrator.new(@podfile)
@integrator.integrate!
@sample_project = Xcodeproj::Project.new(@sample_project_path)
end
before do before do
config.silent = true sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject') config.project_root = sample_project_path.dirname
config.project_root = @sample_project_path.dirname
sample_project_path = @sample_project_path
@podfile = Pod::Podfile.new do @podfile = Pod::Podfile.new do
platform :ios platform :ios
xcodeproj sample_project_path, 'Test' => :debug xcodeproj sample_project_path, 'Test' => :debug
link_with 'SampleProject' # this is an app target! link_with 'SampleProject' # this is an app target!
pod 'JSONKit' pod 'JSONKit'
target :test_runner, :exclusive => true do target :test_runner, :exclusive => true do
...@@ -28,12 +17,10 @@ describe Pod::Installer::UserProjectIntegrator do ...@@ -28,12 +17,10 @@ describe Pod::Installer::UserProjectIntegrator do
pod 'Kiwi' pod 'Kiwi'
end end
end end
@sample_project_path = sample_project_path
@sample_project = Xcodeproj::Project.new(@sample_project_path) @integrator = Pod::Installer::UserProjectIntegrator.new(@podfile)
end @integrator.integrate!
@sample_project = Xcodeproj::Project.new(sample_project_path)
before do
integrate!
end end
it 'adds references to the Pods static libraries to the Frameworks group' do it 'adds references to the Pods static libraries to the Frameworks group' do
......
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