Commit 0911c67e authored by Marius Rackwitz's avatar Marius Rackwitz

Deduplicate pod targets in non-trivial cases

e.g. if a pod is used on multiple platforms but on some more than once,
also if different targets depend on different subspecs sets of the same
pod and in cases where both combined is necessary.
parent c4471a0d
......@@ -94,15 +94,11 @@ module Pod
#
def settings_to_import_pod_targets
if target.requires_frameworks?
framework_header_search_paths = pod_targets.select(&:should_build?).map do |target|
if target.scoped?
"$PODS_FRAMEWORK_BUILD_PATH/#{target.product_name}/Headers"
else
"$CONFIGURATION_BUILD_DIR/#{target.product_name}/Headers"
end
build_pod_targets = pod_targets.select(&:should_build?)
framework_header_search_paths = build_pod_targets.map do |target|
"#{target.relative_configuration_build_dir}/#{target.product_name}/Headers"
end
build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => XCConfigHelper.quote([target.scoped_configuration_build_dir]),
# Make framework headers discoverable by `import "…"`
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(framework_header_search_paths, '-iquote'),
}
......@@ -112,8 +108,10 @@ module Pod
build_settings['HEADER_SEARCH_PATHS'] = '$(inherited) ' + XCConfigHelper.quote(library_header_search_paths)
build_settings['OTHER_CFLAGS'] += ' ' + XCConfigHelper.quote(library_header_search_paths, '-isystem')
end
if pod_targets.any? { |t| t.should_build? && t.scoped? }
build_settings['FRAMEWORK_SEARCH_PATHS'] = '$PODS_FRAMEWORK_BUILD_PATH'
scoped_pod_targets = build_pod_targets.select(&:scoped?)
unless scoped_pod_targets.empty?
framework_search_paths = scoped_pod_targets.map(&:relative_configuration_build_dir).uniq
build_settings['FRAMEWORK_SEARCH_PATHS'] = XCConfigHelper.quote(framework_search_paths)
end
build_settings
else
......
......@@ -59,10 +59,13 @@ module Pod
if target.requires_frameworks? && target.scoped?
build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => XCConfigHelper.quote([target.configuration_build_dir]),
'FRAMEWORK_SEARCH_PATHS' => '$PODS_FRAMEWORK_BUILD_PATH',
'CONFIGURATION_BUILD_DIR' => '$PODS_FRAMEWORK_BUILD_PATH',
'CONFIGURATION_BUILD_DIR' => target.configuration_build_dir,
}
scoped_dependent_targets = target.dependent_targets.select { |t| t.should_build? && t.scoped? }
unless scoped_dependent_targets.empty?
framework_search_paths = scoped_dependent_targets.map(&:relative_configuration_build_dir).uniq
build_settings['FRAMEWORK_SEARCH_PATHS'] = XCConfigHelper.quote(framework_search_paths)
end
@xcconfig.merge!(build_settings)
end
......
......@@ -303,9 +303,10 @@ module Pod
pod_targets = distinct_targets.flat_map do |_, targets_by_distinctors|
if targets_by_distinctors.count > 1
# There are different sets of subspecs or the spec is used across different platforms
suffixes = scope_suffix_for_distinctor(targets_by_distinctors)
targets_by_distinctors.flat_map do |distinctor, target_definitions|
specs, = *distinctor
generate_pod_target(target_definitions, specs).scoped(dedupe_cache)
generate_pod_target(target_definitions, specs, :scope_suffix => suffixes[distinctor])
end
else
(specs, _), target_definitions = targets_by_distinctors.first
......@@ -337,6 +338,48 @@ module Pod
end
end
# Describes what makes pod targets configurations distinct among other.
#
# @param [(Array<Specification>, Platform) => TargetDefinition] targets_by_distinctors
#
# @return [(Array<Specification>, Platform) => String]
#
def scope_suffix_for_distinctor(targets_by_distinctors)
result = nil
all_spec_variants = targets_by_distinctors.keys.map { |k| k[0] }
all_platform_variants = targets_by_distinctors.keys.map { |k| k[1] }
if all_platform_variants.uniq.count == all_platform_variants.count
all_platform_name_variants = all_platform_variants.map(&:name)
if all_platform_name_variants.uniq.count == all_platform_name_variants.count
# => Platform name
result = targets_by_distinctors.map { |d, _| [d, d[1].name.to_s] }
else
# => Platform name + SDK version
result = targets_by_distinctors.map { |d, _| [d, d[1].to_s] }
end
elsif all_spec_variants.uniq.count == all_spec_variants.count
common_specs = all_spec_variants.reduce(all_spec_variants.first, &:&)
result = targets_by_distinctors.map do |distinctor, _|
specs, = *distinctor
specs -= common_specs
subspec_names = specs.map { |spec| spec.name.split('/')[1..-1].join('_') }
# => Subspecs names without common subspecs
[distinctor, subspec_names.empty? ? nil : subspec_names.join('-')]
end
else
result = targets_by_distinctors.map do |distinctor, target_definitions|
names = target_definitions.map do |target_definition|
target_definition.root? ? 'Pods' : target_definition.name
end
# => *All* target definition names
[distinctor, names.join('-')]
end
end
Hash[result.map { |d, name| [d, name && name.tr('/', '_')] }]
end
# Finds the names of the Pods upon which the given target _transitively_
# depends.
#
......@@ -375,10 +418,13 @@ module Pod
# @param [Array<Specification>] specs
# the specifications of an equal root.
#
# @param [String] scope_suffix
# @see PodTarget#scope_suffix
#
# @return [PodTarget]
#
def generate_pod_target(target_definitions, pod_specs)
pod_target = PodTarget.new(pod_specs, target_definitions, sandbox)
def generate_pod_target(target_definitions, pod_specs, scope_suffix: nil)
pod_target = PodTarget.new(pod_specs, target_definitions, sandbox, scope_suffix)
if installation_options.integrate_targets?
target_inspections = result.target_inspections.select { |t, _| target_definitions.include?(t) }.values
......
......@@ -30,8 +30,14 @@ module Pod
# dependency, but they require different sets of subspecs or they
# are on different platforms.
#
attr_reader :scoped
alias_method :scoped?, :scoped
def scoped?
!scope_suffix.nil?
end
# @return [String] used for the label and the directory name, which is used to
# scope the build product in the default configuration build dir.
#
attr_reader :scope_suffix
# @return [Array<PodTarget>] the targets that this target has a dependency
# upon.
......@@ -41,16 +47,17 @@ module Pod
# @param [Array<Specification>] @spec #see spec
# @param [Array<TargetDefinition>] target_definitions @see target_definitions
# @param [Sandbox] sandbox @see sandbox
# @param [Bool] scoped @see scoped
# @param [String] scope_suffix @see scope_suffix
#
def initialize(specs, target_definitions, sandbox, scoped = false)
def initialize(specs, target_definitions, sandbox, scope_suffix = nil)
raise "Can't initialize a PodTarget without specs!" if specs.nil? || specs.empty?
raise "Can't initialize a PodTarget without TargetDefinition!" if target_definitions.nil? || target_definitions.empty?
raise "Can't initialize a PodTarget with an empty string scope suffix!" if scope_suffix == ''
super()
@specs = specs
@target_definitions = target_definitions
@sandbox = sandbox
@scoped = scoped
@scope_suffix = scope_suffix
@build_headers = Sandbox::HeadersStore.new(sandbox, 'Private')
@file_accessors = []
@resource_bundle_targets = []
......@@ -67,7 +74,7 @@ module Pod
if cache[cache_key]
cache[cache_key]
else
target = PodTarget.new(specs, [target_definition], sandbox, true)
target = PodTarget.new(specs, [target_definition], sandbox, target_definition.label)
target.file_accessors = file_accessors
target.user_build_configurations = user_build_configurations
target.native_target = native_target
......@@ -82,7 +89,7 @@ module Pod
#
def label
if scoped?
"#{target_definitions.first.label}-#{root_spec.name}"
"#{root_spec.name}-#{scope_suffix}"
else
root_spec.name
end
......@@ -246,12 +253,24 @@ module Pod
#
def configuration_build_dir
if scoped?
"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/#{target_definitions.first.label}"
"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/#{scope_suffix}"
else
'$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
end
end
# @return [String] The configuration build dir, relative to the default
# configuration build dir, which is:
# '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
#
def relative_configuration_build_dir
if scoped?
"$CONFIGURATION_BUILD_DIR/#{scope_suffix}"
else
'$CONFIGURATION_BUILD_DIR'
end
end
private
# @param [TargetDefinition] target_definition
......
......@@ -19,7 +19,6 @@ module Pod
@specs.first.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11' }
@pod_targets = @specs.map { |spec| pod_target(spec, @target_definition) }
@target = fixture_aggregate_target(@pod_targets, @target_definition)
@target.target_definition.should == @pod_targets.first.target_definitions.first
@target.target_definition.whitelist_pod_for_configuration(@specs.first.name, 'Release')
@generator = AggregateXCConfig.new(@target, 'Release')
end
......@@ -121,7 +120,7 @@ module Pod
end
it 'links the pod targets with the aggregate target' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"Pods-BananaLib"'
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"BananaLib-Pods"'
end
end
......@@ -189,21 +188,28 @@ module Pod
end
end
it 'sets the PODS_FRAMEWORK_BUILD_PATH build variable' do
@xcconfig.to_hash['PODS_FRAMEWORK_BUILD_PATH'].should == '"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods"'
end
describe 'with a scoped pod target' do
def specs
[
fixture_spec('banana-lib/BananaLib.podspec'),
fixture_spec('orange-framework/OrangeFramework.podspec'),
]
end
def pod_target(spec, target_definition)
fixture_pod_target(spec, [target_definition]).scoped.first
target_definition = fixture_target_definition(spec.name)
target_definition.stubs(:parent).returns(@target_definition.podfile)
fixture_pod_target(spec, [target_definition, @target_definition].uniq).tap do |pod_target|
pod_target.stubs(:scope_suffix).returns(target_definition.label)
end
end
it 'adds the framework build path to the xcconfig, with quotes, as framework search paths' do
@xcconfig.to_hash['FRAMEWORK_SEARCH_PATHS'].should == '$(inherited) $PODS_FRAMEWORK_BUILD_PATH'
@xcconfig.to_hash['FRAMEWORK_SEARCH_PATHS'].should == '$(inherited) "$CONFIGURATION_BUILD_DIR/Pods-BananaLib" "$CONFIGURATION_BUILD_DIR/Pods-OrangeFramework"'
end
it 'adds the framework header paths to the xcconfig, with quotes, as local headers' do
expected = '$(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/OrangeFramework.framework/Headers"'
expected = '$(inherited) -iquote "$CONFIGURATION_BUILD_DIR/Pods-BananaLib/BananaLib.framework/Headers" -iquote "$CONFIGURATION_BUILD_DIR/Pods-OrangeFramework/OrangeFramework.framework/Headers"'
@xcconfig.to_hash['OTHER_CFLAGS'].should == expected
end
end
......
......@@ -132,12 +132,12 @@ module Pod
it 'generates the model to represent the target definitions' do
target = @analyzer.analyze.targets.first
target.pod_targets.map(&:name).sort.should == [
'JSONKit',
'AFNetworking',
'SVPullToRefresh',
'Pods-SampleProject-libextobjc',
].sort
target.pod_targets.map(&:name).sort.should == %w(
JSONKit
AFNetworking
libextobjc
SVPullToRefresh
).sort
target.support_files_dir.should == config.sandbox.target_support_files_dir('Pods-SampleProject')
target.pod_targets.map(&:archs).uniq.should == [[]]
......@@ -237,11 +237,11 @@ module Pod
analyzer.analyze
analyzer.analyze.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w(
Pods-CLITool/Pods-CLITool-monkey
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-BananaLib
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-monkey
Pods-SampleProject/Pods-SampleProject-BananaLib
Pods-SampleProject/Pods-SampleProject-monkey
Pods-CLITool/monkey-osx
Pods-SampleProject-TestRunner/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/monkey-ios
Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/monkey-ios
).sort
end
......@@ -265,10 +265,10 @@ module Pod
analyzer.analyze
analyzer.analyze.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w(
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-BananaLib
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-monkey
Pods-SampleProject/Pods-SampleProject-BananaLib
Pods-SampleProject/Pods-SampleProject-monkey
Pods-SampleProject-TestRunner/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/monkey-Pods-SampleProject-TestRunner
Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/monkey-Pods-SampleProject
).sort
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