Commit 717dc518 authored by Jeremy Slater's avatar Jeremy Slater

Support xcconfig files per Podfile target

Xcconfig files are generated for every library target and target 
defined in the Podfile.  However, only the Podfile target xcconfig 
files are added to the user project.  This required tracking all the 
libraries as well as the Podfile targets through the installation and 
integration procedures.
parent 9a715213
......@@ -44,7 +44,7 @@ module Pod
autoload :ExternalSources, 'cocoapods/external_sources'
autoload :Installer, 'cocoapods/installer'
autoload :SourcesManager, 'cocoapods/sources_manager'
autoload :Library, 'cocoapods/library'
autoload :Target, 'cocoapods/target'
autoload :Project, 'cocoapods/project'
autoload :Resolver, 'cocoapods/resolver'
autoload :Sandbox, 'cocoapods/sandbox'
......
......@@ -6,7 +6,7 @@ module Pod
#
class XCConfig
# @return [Library] the library represented by this xcconfig.
# @return [Target] the library or target represented by this xcconfig.
#
attr_reader :library
......@@ -24,13 +24,13 @@ module Pod
#
attr_reader :relative_pods_root
# @param [Sandbox] sandbox @see sandbox
# @param [Array<LocalPod>] pods @see pods
# @param [Target] library @see library
# @param [Array<Specification::Consumer>] spec_consumers @see spec_consumers
# @param [String] relative_pods_root @see relative_pods_root
#
def initialize(library, sandbox, spec_consumers, relative_pods_root)
def initialize(library, spec_consumers, relative_pods_root)
@library = library
@sandbox = sandbox
@sandbox = library.sandbox
@spec_consumers = spec_consumers
@relative_pods_root = relative_pods_root
end
......@@ -55,6 +55,9 @@ module Pod
ld_flags << ' -fobjc-arc'
end
public_headers = [library.public_headers.search_paths,
library.libraries.map { |lib| lib.public_headers.search_paths }].flatten
@xcconfig = Xcodeproj::Config.new({
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
'OTHER_LDFLAGS' => ld_flags,
......@@ -62,7 +65,7 @@ module Pod
'PODS_ROOT' => relative_pods_root,
'PODS_HEADERS_SEARCH_PATHS' => '${PODS_PUBLIC_HEADERS_SEARCH_PATHS}',
'PODS_BUILD_HEADERS_SEARCH_PATHS' => quote(library.build_headers.search_paths),
'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quote(sandbox.public_headers.search_paths),
'PODS_PUBLIC_HEADERS_SEARCH_PATHS' => quote(public_headers),
'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1'
})
......
......@@ -60,7 +60,7 @@ module Pod
def specs_by_lib
result = {}
installer.libraries.each do |lib|
result[installer.library_rep(lib)] = lib.specs
result[installer.library_rep(lib)] = [lib.spec]
end
result
end
......@@ -71,7 +71,7 @@ module Pod
def pods_by_lib
result = {}
installer.libraries.each do |lib|
pod_names = lib.specs.map { |spec| spec.root.name }.uniq
pod_names = [lib.spec.root.name]
pod_reps = pods.select { |rep| pod_names.include?(rep.name) }
result[lib.target_definition] = pod_reps
end
......
......@@ -108,7 +108,7 @@ module Pod
UI.section "Generating Pods project" do
prepare_pods_project
install_file_references
install_targets
install_libraries
run_post_install_hooks
write_pod_project
write_lockfiles
......@@ -134,10 +134,10 @@ module Pod
#
attr_reader :names_of_pods_to_install
# @return [Array<Library>] The libraries generated by the installation
# process.
# @return [Array<Target>] The Podfile targets containing library
# dependencies.
#
attr_reader :libraries
attr_reader :targets
# @return [Array<Specification>] The specifications that where installed.
#
......@@ -165,7 +165,7 @@ module Pod
analyzer = Analyzer.new(sandbox, podfile, lockfile)
analyzer.update_mode = update_mode
@analysis_result = analyzer.analyze
@libraries = analyzer.result.libraries
@targets = analyzer.result.targets
end
# Prepares the Pods folder in order to be compatible with the most recent
......@@ -186,8 +186,12 @@ module Pod
# @todo [#247] Clean the headers of only the pods to install.
#
def clean_sandbox
sandbox.build_headers.implode!
sandbox.public_headers.implode!
targets.each do |target|
target.libraries.each do |library|
library.build_headers.implode!
library.public_headers.implode!
end
end
unless sandbox_state.deleted.empty?
title_options = { :verbose_prefix => "-> ".red }
......@@ -203,11 +207,11 @@ module Pod
# created by the Pod source installer as well.
#
def create_file_accessors
libraries.each do |library|
library.specs.each do |spec|
pod_root = sandbox.pod_dir(spec.root.name)
targets.each do |target|
target.libraries.each do |library|
pod_root = sandbox.pod_dir(library.spec.root.name)
path_list = Sandbox::PathList.new(pod_root)
file_accessor = Sandbox::FileAccessor.new(path_list, spec.consumer(library.platform))
file_accessor = Sandbox::FileAccessor.new(path_list, library.spec.consumer(library.platform))
library.file_accessors ||= []
library.file_accessors << file_accessor
end
......@@ -243,11 +247,9 @@ module Pod
def install_source_of_pod(pod_name)
specs_by_platform = {}
libraries.each do |library|
specs = library.specs.select { |spec| spec.root.name == pod_name }
unless specs.empty?
if library.spec.root.name == pod_name
specs_by_platform[library.platform] ||= []
specs_by_platform[library.platform].concat(specs)
specs_by_platform[library.platform] << library.spec
end
end
......@@ -286,7 +288,7 @@ module Pod
@pods_project.add_podfile(config.podfile_path)
end
sandbox.project = @pods_project
platforms = libraries.map(&:platform)
platforms = targets.map(&:platform)
osx_deployment_target = platforms.select { |p| p.name == :osx }.map(&:deployment_target).min
ios_deployment_target = platforms.select { |p| p.name == :ios }.map(&:deployment_target).min
@pods_project.build_configurations.each do |build_configuration|
......@@ -307,15 +309,14 @@ module Pod
installer.install!
end
# Installs the targets of the Pods projects and generates their support
# files.
# Installs the library targets of the Pods projects and generates their
# support files.
#
# @return [void]
#
def install_targets
UI.message"- Installing targets" do
def install_libraries
UI.message"- Installing libraries" do
libraries.sort_by(&:name).each do |library|
next if library.target_definition.empty?
target_installer = TargetInstaller.new(sandbox, library)
target_installer.install!
end
......@@ -368,7 +369,7 @@ module Pod
def integrate_user_project
UI.section "Integrating client #{'project'.pluralize(libraries.map(&:user_project_path).uniq.count) }" do
installation_root = config.installation_root
integrator = UserProjectIntegrator.new(podfile, sandbox, installation_root, libraries)
integrator = UserProjectIntegrator.new(podfile, sandbox, installation_root, targets)
integrator.integrate!
end
end
......@@ -545,7 +546,14 @@ module Pod
# @return [Array<Library>] The library.
#
def libraries_using_spec(spec)
libraries.select { |lib| lib.specs.include?(spec) }
libraries.select { |lib| lib.spec == spec }
end
# @return [Array<Library>] The libraries generated by the installation
# process.
#
def libraries
targets.map(&:libraries).flatten
end
#-------------------------------------------------------------------------#
......
......@@ -55,7 +55,7 @@ module Pod
fetch_external_sources if allow_fetches
@result.specs_by_target = resolve_dependencies
@result.specifications = generate_specifications
@result.libraries = generated_libraries
@result.targets = generate_targets
@result.sandbox_state = generate_sandbox_state
@result
end
......@@ -161,9 +161,13 @@ module Pod
#
# @return [Array<Libraries>] the generated libraries.
#
def generated_libraries
libraries = []
def generate_targets
targets = []
result.specs_by_target.each do |target_definition, specs|
libs_by_name = {}
target = Target.new(target_definition, sandbox)
targets << target
specs.each do |spec|
lib_target = Podfile::TargetDefinition.from_hash(target_definition.to_hash, target_definition.parent)
if target_definition.name == 'Pods'
......@@ -172,29 +176,42 @@ module Pod
lib_target.name = "#{target_definition.name}-#{spec.name.gsub('/', '-')}"
end
lib = Library.new(lib_target, sandbox)
lib = Target.new(lib_target, sandbox)
libs_by_name[spec.name] = lib
lib.support_files_root = sandbox.library_support_files_dir(lib.name)
lib.platform = lib_target.platform
lib.specs = [spec]
lib.platform = target_definition.platform
lib.spec = spec
target.support_files_root = sandbox.library_support_files_dir(target.name)
target.platform = target_definition.platform
if config.integrate_targets?
project_path = compute_user_project_path(target_definition)
user_project = Xcodeproj::Project.new(project_path)
targets = compute_user_project_targets(target_definition, user_project)
native_targets = compute_user_project_targets(target_definition, user_project)
lib.user_project_path = project_path
lib.client_root = project_path.dirname
lib.user_target_uuids = targets.map(&:uuid)
lib.user_build_configurations = compute_user_build_configurations(lib_target, targets)
lib.user_build_configurations = compute_user_build_configurations(target_definition, native_targets)
target.user_project_path = project_path
target.client_root = project_path.dirname
target.user_target_uuids = native_targets.map(&:uuid)
target.user_build_configurations = compute_user_build_configurations(target_definition, native_targets)
else
lib.client_root = config.installation_root
lib.user_target_uuids = []
lib.user_build_configurations = {}
end
libraries << lib
target.libraries << lib
end
target.libraries.each do |library|
library.libraries = library.spec.dependencies(library.platform).map { |dep| libs_by_name[dep.name] }
end
end
libraries
targets
end
# Generates dependencies that require the specific version of the Pods
......@@ -485,10 +502,10 @@ module Pod
#
attr_accessor :sandbox_state
# @return [Array<Library>] the libraries generated by the target
# definitions.
# @return [Array<Target>] The Podfile targets containing library
# dependencies.
#
attr_accessor :libraries
attr_accessor :targets
end
......
......@@ -107,28 +107,18 @@ module Pod
#
def link_headers
UI.message "- Linking headers" do
file_accessors.each do |file_accessor|
headers_sandbox = Pathname.new(file_accessor.spec.root.name)
sandbox.build_headers.add_search_path(headers_sandbox)
sandbox.public_headers.add_search_path(headers_sandbox)
header_mappings(headers_sandbox, file_accessor, file_accessor.headers).each do |namespaced_path, files|
sandbox.build_headers.add_files(namespaced_path, files)
end
header_mappings(headers_sandbox, file_accessor, file_accessor.public_headers).each do |namespaced_path, files|
sandbox.public_headers.add_files(namespaced_path, files)
end
end
libraries.each do |library|
library.file_accessors.each do |file_accessor|
headers_sandbox = Pathname.new(file_accessor.spec.root.name)
library.build_headers.add_search_path(headers_sandbox)
library.public_headers.add_search_path(headers_sandbox)
header_mappings(headers_sandbox, file_accessor, file_accessor.headers).each do |namespaced_path, files|
sandbox.build_headers.add_files(namespaced_path, files)
library.build_headers.add_files(namespaced_path, files)
end
header_mappings(headers_sandbox, file_accessor, file_accessor.public_headers).each do |namespaced_path, files|
library.public_headers.add_files(namespaced_path, files)
end
end
end
......
......@@ -140,7 +140,7 @@ module Pod
def create_xcconfig_file
path = library.xcconfig_path
UI.message "- Generating xcconfig file at #{UI.path(path)}" do
gen = Generator::XCConfig.new(library, sandbox, spec_consumers, library.relative_pods_root)
gen = Generator::XCConfig.new(library, spec_consumers, library.relative_pods_root)
gen.set_arc_compatibility_flag = target_definition.podfile.set_arc_compatibility_flag?
gen.save_as(path)
library.xcconfig = gen.xcconfig
......@@ -161,7 +161,7 @@ module Pod
def create_target_environment_header
path = library.target_environment_header_path
UI.message "- Generating target environment header at #{UI.path(path)}" do
generator = Generator::TargetEnvironmentHeader.new(library.specs)
generator = Generator::TargetEnvironmentHeader.new([library.spec])
generator.save_as(path)
add_file_to_support_group(path)
end
......
......@@ -35,9 +35,9 @@ module Pod
#
attr_reader :installation_root
# @return [Library] the libraries generated by the installer.
# @return [Array<Target>] the targets represented in the Podfile.
#
attr_reader :libraries
attr_reader :targets
# @param [Podfile] podfile @see #podfile
# @param [Sandbox] sandbox @see #sandbox
......@@ -46,11 +46,11 @@ module Pod
#
# @todo Too many initialization arguments
#
def initialize(podfile, sandbox, installation_root, libraries)
def initialize(podfile, sandbox, installation_root, targets)
@podfile = podfile
@sandbox = sandbox
@installation_root = installation_root
@libraries = libraries
@targets = targets
end
# Integrates the user projects associated with the {TargetDefinitions}
......@@ -110,8 +110,8 @@ module Pod
# @return [void]
#
def integrate_user_targets
libraries_to_integrate.sort_by(&:name).each do |lib|
TargetIntegrator.new(lib).integrate!
targets_to_integrate.sort_by(&:name).each do |target|
TargetIntegrator.new(target).integrate!
end
end
......@@ -163,14 +163,14 @@ module Pod
# @note Empty target definitions are ignored.
#
def user_project_paths
libraries.map do |lib|
lib.user_project_path
targets.map do |target|
target.user_project_path
end.compact.uniq
end
def libraries_to_integrate
libraries.reject { |lib| lib.target_definition.empty? }
def targets_to_integrate
targets.reject { |target| target.target_definition.empty? }
end
#-----------------------------------------------------------------------#
......
......@@ -9,14 +9,14 @@ module Pod
#
class TargetIntegrator
# @return [Library] the library that should be integrated.
# @return [Target] the target that should be integrated.
#
attr_reader :library
attr_reader :target
# @param [Library] library @see #target_definition
# @param [Target] target @see #target_definition
#
def initialize(library)
@library = library
def initialize(target)
@target = target
end
# Integrates the user project targets. Only the targets that do **not**
......@@ -26,8 +26,8 @@ module Pod
# @return [void]
#
def integrate!
return if targets.empty?
UI.section(integration_message) do
create_xcconfig_file
add_xcconfig_base_configuration
add_pods_library
add_copy_resources_script_phase
......@@ -36,40 +36,17 @@ module Pod
end
end
# @return [Array<PBXNativeTarget>] the list of targets that the Pods
# lib that need to be integrated.
# @return [Array<PBXNativeTarget>] the user targets for integration.
#
# @note A target is considered integrated if it already references
#
def targets
unless @targets
target_uuids = library.user_target_uuids
targets = target_uuids.map do |uuid|
target = user_project.objects_by_uuid[uuid]
unless target
raise Informative, "[Bug] Unable to find the target with " \
"the `#{uuid}` UUID for the `#{library}` library"
end
target
end
non_integrated = targets.reject do |target|
target.frameworks_build_phase.files.any? do |build_file|
file_ref = build_file.file_ref
file_ref &&
file_ref.isa == 'PBXFileReference' &&
file_ref.display_name == library.product_name
end
end
@targets = non_integrated
end
@targets
def native_targets
@native_targets ||= target.user_target_uuids.map { |uuid| user_project.objects_by_uuid[uuid] }
end
# Read the project from the disk to ensure that it is up to date as
# other TargetIntegrators might have modified it.
#
def user_project
@user_project ||= Xcodeproj::Project.new(library.user_project_path)
@user_project ||= Xcodeproj::Project.new(target.user_project_path)
end
# @return [String] a string representation suitable for debugging.
......@@ -84,6 +61,29 @@ module Pod
private
# @return [Specification::Consumer] the consumer for the specifications.
#
def spec_consumers
@spec_consumers ||= target.libraries.map(&:file_accessors).flatten.map(&:spec_consumer)
end
# Generates the contents of the xcconfig file and saves it to disk.
#
# @note The `ALWAYS_SEARCH_USER_PATHS` flag is enabled to support
# libraries like `EmbedReader`.
#
# @return [void]
#
def create_xcconfig_file
path = target.xcconfig_path
UI.message "- Generating xcconfig file at #{UI.path(path)}" do
gen = Generator::XCConfig.new(target, spec_consumers, target.relative_pods_root)
gen.set_arc_compatibility_flag = target.target_definition.podfile.set_arc_compatibility_flag?
gen.save_as(path)
target.xcconfig = gen.xcconfig
end
end
# Adds the `xcconfig` configurations files generated for the current
# {TargetDefinition} to the build configurations of the targets that
# should be integrated.
......@@ -98,10 +98,11 @@ module Pod
# @return [void]
#
def add_xcconfig_base_configuration
xcconfig = user_project.new_file(library.xcconfig_relative_path)
targets.each do |target|
check_overridden_build_settings(library.xcconfig, target)
target.build_configurations.each do |config|
xcconfig = user_project.files.select { |f| f.path == target.xcconfig_relative_path }.first ||
user_project.new_file(target.xcconfig_relative_path)
native_targets.each do |native_target|
check_overridden_build_settings(target.xcconfig, native_target)
native_target.build_configurations.each do |config|
config.base_configuration_reference = xcconfig
end
end
......@@ -114,9 +115,14 @@ module Pod
#
def add_pods_library
frameworks = user_project.frameworks_group
pods_library = frameworks.new_static_library(library.label)
targets.each do |target|
target.frameworks_build_phase.add_file_reference(pods_library)
native_targets.each do |native_target|
target.libraries.each do |library|
lib = frameworks.files.select { |f| f.path == library.product_name }.first ||
frameworks.new_static_library(library.name)
unless native_target.frameworks_build_phase.files_references.include?(lib)
native_target.frameworks_build_phase.add_file_reference(lib)
end
end
end
end
......@@ -127,12 +133,16 @@ module Pod
# @return [void]
#
def add_copy_resources_script_phase
targets.each do |target|
phase = target.new_shell_script_build_phase("Copy Pods Resources (#{library.name})")
native_targets.each do |native_target|
target.libraries.each do |library|
phase_name = "Copy Pods Resources (#{library.name})"
next if native_target.shell_script_build_phases.select { |bp| bp.name == phase_name }.first
phase = native_target.new_shell_script_build_phase(phase_name)
path = library.copy_resources_script_relative_path
phase.shell_script = %{"#{path}"\n}
end
end
end
# Adds a shell script build phase responsible for checking if the Pods
# locked in the Pods/Manifest.lock file are in sync with the Pods defined
......@@ -145,10 +155,10 @@ module Pod
#
def add_check_manifest_lock_script_phase
phase_name = 'Check Pods Manifest.lock'
targets.each do |target|
next if target.shell_script_build_phases.any? { |phase| phase.name == phase_name }
phase = target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
target.build_phases.unshift(phase)
native_targets.each do |native_target|
next if native_target.shell_script_build_phases.any? { |phase| phase.name == phase_name }
phase = native_target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
native_target.build_phases.unshift(phase)
phase.name = phase_name
phase.shell_script = <<-EOS.strip_heredoc
diff "${PODS_ROOT}/../Podfile.lock" "${PODS_ROOT}/Manifest.lock" > /dev/null
......@@ -167,7 +177,7 @@ module Pod
# @return [void]
#
def save_user_project
user_project.save_as(library.user_project_path)
user_project.save_as(target.user_project_path)
end
#---------------------------------------------------------------------#
......@@ -181,11 +191,11 @@ module Pod
#
# @return [void]
#
def check_overridden_build_settings(xcconfig, target)
def check_overridden_build_settings(xcconfig, native_target)
return unless xcconfig
configs_by_overridden_key = {}
target.build_configurations.each do |config|
native_target.build_configurations.each do |config|
xcconfig.attributes.keys.each do |key|
target_value = config.build_settings[key]
......@@ -196,13 +206,13 @@ module Pod
end
configs_by_overridden_key.each do |key, config_names|
name = "#{target.name} [#{config_names.join(' - ')}]"
name = "#{native_target.name} [#{config_names.join(' - ')}]"
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 `#{library.xcconfig_relative_path}'.",
"setting defined in `#{target.xcconfig_relative_path}'.",
actions)
end
end
......@@ -212,10 +222,10 @@ module Pod
# integration.
#
def integration_message
"Integrating `#{library.product_name}` into " \
"#{'target'.pluralize(targets.size)} " \
"`#{targets.map(&:name).to_sentence}` " \
"of project #{UI.path library.user_project_path}."
"Integrating #{'library'.pluralize(target.libraries.size)} " \
"`#{target.libraries.map(&:name).to_sentence}` " \
"into target #{target.name} " \
"of project #{UI.path target.user_project_path}."
end
#---------------------------------------------------------------------#
......
module Pod
# Model class which describes a Pods library.
# Model class which describes a Pods target.
#
# The Library class stores and provides the information necessary for
# working with a library in the Pods project and in the user projects
# through the installation process.
# The Target class stores and provides the information necessary for
# working with a target in the Podfile and it's dependent libraries.
# This class is used to represent both the targets and their libraries.
#
class Library
class Target
# @return [PBXNativeTarget] the target definition of the Podfile that
# generated this library.
# generated this target.
#
attr_reader :target_definition
# @return [HeadersStore] the header directory for the Pods libraries.
# @return [Sandbox] The sandbox where the Pods should be installed.
#
attr_reader :sandbox
# @return [HeadersStore] the header directory for the library.
#
attr_reader :build_headers
# @return [HeadersStore] the public header directory for the library.
#
attr_reader :public_headers
# @param [TargetDefinition] target_definition @see target_definition
# @param [PBXNativeTarget] target @see target
# @param [Sandbox] sandbox @see sandbox
#
def initialize(target_definition, sandbox)
@target_definition = target_definition
@build_headers = Sandbox::HeadersStore.new(sandbox, "BuildHeaders")
@sandbox = sandbox
@build_headers = Sandbox::HeadersStore.new(sandbox, "BuildHeaders/#{target_definition.name}")
@public_headers = Sandbox::HeadersStore.new(sandbox, "Headers/#{target_definition.name}")
@libraries = []
end
# @return [String] the label for the library.
......@@ -34,13 +45,13 @@ module Pod
# @return [String] the name of the library.
#
def name
target_definition.label.to_s
label
end
# @return [String] the name of the library.
#
def product_name
"lib#{target_definition.label}.a"
"lib#{label}.a"
end
# @return [String] A string suitable for debugging.
......@@ -104,15 +115,19 @@ module Pod
#
attr_accessor :xcconfig
# @return [Array<Specification>] the specifications of this library.
# @return [Specification] the specification for this library.
#
attr_accessor :specs
attr_accessor :spec
# @return [Array<Sandbox::FileAccessor>] the file accessors for the
# specifications of this library.
#
attr_accessor :file_accessors
# @return [Array<Target>] the dependencies for this target (or library).
#
attr_accessor :libraries
#-------------------------------------------------------------------------#
# @!group Support files
......
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