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 ...@@ -94,15 +94,11 @@ module Pod
# #
def settings_to_import_pod_targets def settings_to_import_pod_targets
if target.requires_frameworks? if target.requires_frameworks?
framework_header_search_paths = pod_targets.select(&:should_build?).map do |target| build_pod_targets = pod_targets.select(&:should_build?)
if target.scoped? framework_header_search_paths = build_pod_targets.map do |target|
"$PODS_FRAMEWORK_BUILD_PATH/#{target.product_name}/Headers" "#{target.relative_configuration_build_dir}/#{target.product_name}/Headers"
else
"$CONFIGURATION_BUILD_DIR/#{target.product_name}/Headers"
end
end end
build_settings = { build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => XCConfigHelper.quote([target.scoped_configuration_build_dir]),
# Make framework headers discoverable by `import "…"` # Make framework headers discoverable by `import "…"`
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(framework_header_search_paths, '-iquote'), 'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(framework_header_search_paths, '-iquote'),
} }
...@@ -112,8 +108,10 @@ module Pod ...@@ -112,8 +108,10 @@ module Pod
build_settings['HEADER_SEARCH_PATHS'] = '$(inherited) ' + XCConfigHelper.quote(library_header_search_paths) build_settings['HEADER_SEARCH_PATHS'] = '$(inherited) ' + XCConfigHelper.quote(library_header_search_paths)
build_settings['OTHER_CFLAGS'] += ' ' + XCConfigHelper.quote(library_header_search_paths, '-isystem') build_settings['OTHER_CFLAGS'] += ' ' + XCConfigHelper.quote(library_header_search_paths, '-isystem')
end end
if pod_targets.any? { |t| t.should_build? && t.scoped? } scoped_pod_targets = build_pod_targets.select(&:scoped?)
build_settings['FRAMEWORK_SEARCH_PATHS'] = '$PODS_FRAMEWORK_BUILD_PATH' 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 end
build_settings build_settings
else else
......
...@@ -59,10 +59,13 @@ module Pod ...@@ -59,10 +59,13 @@ module Pod
if target.requires_frameworks? && target.scoped? if target.requires_frameworks? && target.scoped?
build_settings = { build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => XCConfigHelper.quote([target.configuration_build_dir]), 'CONFIGURATION_BUILD_DIR' => target.configuration_build_dir,
'FRAMEWORK_SEARCH_PATHS' => '$PODS_FRAMEWORK_BUILD_PATH',
'CONFIGURATION_BUILD_DIR' => '$PODS_FRAMEWORK_BUILD_PATH',
} }
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) @xcconfig.merge!(build_settings)
end end
......
...@@ -303,9 +303,10 @@ module Pod ...@@ -303,9 +303,10 @@ module Pod
pod_targets = distinct_targets.flat_map do |_, targets_by_distinctors| pod_targets = distinct_targets.flat_map do |_, targets_by_distinctors|
if targets_by_distinctors.count > 1 if targets_by_distinctors.count > 1
# There are different sets of subspecs or the spec is used across different platforms # 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| targets_by_distinctors.flat_map do |distinctor, target_definitions|
specs, = *distinctor specs, = *distinctor
generate_pod_target(target_definitions, specs).scoped(dedupe_cache) generate_pod_target(target_definitions, specs, :scope_suffix => suffixes[distinctor])
end end
else else
(specs, _), target_definitions = targets_by_distinctors.first (specs, _), target_definitions = targets_by_distinctors.first
...@@ -337,6 +338,48 @@ module Pod ...@@ -337,6 +338,48 @@ module Pod
end end
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_ # Finds the names of the Pods upon which the given target _transitively_
# depends. # depends.
# #
...@@ -375,10 +418,13 @@ module Pod ...@@ -375,10 +418,13 @@ module Pod
# @param [Array<Specification>] specs # @param [Array<Specification>] specs
# the specifications of an equal root. # the specifications of an equal root.
# #
# @param [String] scope_suffix
# @see PodTarget#scope_suffix
#
# @return [PodTarget] # @return [PodTarget]
# #
def generate_pod_target(target_definitions, pod_specs) def generate_pod_target(target_definitions, pod_specs, scope_suffix: nil)
pod_target = PodTarget.new(pod_specs, target_definitions, sandbox) pod_target = PodTarget.new(pod_specs, target_definitions, sandbox, scope_suffix)
if installation_options.integrate_targets? if installation_options.integrate_targets?
target_inspections = result.target_inspections.select { |t, _| target_definitions.include?(t) }.values target_inspections = result.target_inspections.select { |t, _| target_definitions.include?(t) }.values
......
...@@ -30,8 +30,14 @@ module Pod ...@@ -30,8 +30,14 @@ module Pod
# dependency, but they require different sets of subspecs or they # dependency, but they require different sets of subspecs or they
# are on different platforms. # are on different platforms.
# #
attr_reader :scoped def scoped?
alias_method :scoped?, :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 # @return [Array<PodTarget>] the targets that this target has a dependency
# upon. # upon.
...@@ -41,16 +47,17 @@ module Pod ...@@ -41,16 +47,17 @@ module Pod
# @param [Array<Specification>] @spec #see spec # @param [Array<Specification>] @spec #see spec
# @param [Array<TargetDefinition>] target_definitions @see target_definitions # @param [Array<TargetDefinition>] target_definitions @see target_definitions
# @param [Sandbox] sandbox @see sandbox # @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 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 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() super()
@specs = specs @specs = specs
@target_definitions = target_definitions @target_definitions = target_definitions
@sandbox = sandbox @sandbox = sandbox
@scoped = scoped @scope_suffix = scope_suffix
@build_headers = Sandbox::HeadersStore.new(sandbox, 'Private') @build_headers = Sandbox::HeadersStore.new(sandbox, 'Private')
@file_accessors = [] @file_accessors = []
@resource_bundle_targets = [] @resource_bundle_targets = []
...@@ -67,7 +74,7 @@ module Pod ...@@ -67,7 +74,7 @@ module Pod
if cache[cache_key] if cache[cache_key]
cache[cache_key] cache[cache_key]
else 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.file_accessors = file_accessors
target.user_build_configurations = user_build_configurations target.user_build_configurations = user_build_configurations
target.native_target = native_target target.native_target = native_target
...@@ -82,7 +89,7 @@ module Pod ...@@ -82,7 +89,7 @@ module Pod
# #
def label def label
if scoped? if scoped?
"#{target_definitions.first.label}-#{root_spec.name}" "#{root_spec.name}-#{scope_suffix}"
else else
root_spec.name root_spec.name
end end
...@@ -246,12 +253,24 @@ module Pod ...@@ -246,12 +253,24 @@ module Pod
# #
def configuration_build_dir def configuration_build_dir
if scoped? if scoped?
"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/#{target_definitions.first.label}" "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/#{scope_suffix}"
else else
'$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)' '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)'
end end
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 private
# @param [TargetDefinition] target_definition # @param [TargetDefinition] target_definition
......
...@@ -19,7 +19,6 @@ module Pod ...@@ -19,7 +19,6 @@ module Pod
@specs.first.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11' } @specs.first.pod_target_xcconfig = { 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++11' }
@pod_targets = @specs.map { |spec| pod_target(spec, @target_definition) } @pod_targets = @specs.map { |spec| pod_target(spec, @target_definition) }
@target = fixture_aggregate_target(@pod_targets, @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') @target.target_definition.whitelist_pod_for_configuration(@specs.first.name, 'Release')
@generator = AggregateXCConfig.new(@target, 'Release') @generator = AggregateXCConfig.new(@target, 'Release')
end end
...@@ -121,7 +120,7 @@ module Pod ...@@ -121,7 +120,7 @@ module Pod
end end
it 'links the pod targets with the aggregate target' do 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
end end
...@@ -189,21 +188,28 @@ module Pod ...@@ -189,21 +188,28 @@ module Pod
end end
end end
it 'sets the PODS_FRAMEWORK_BUILD_PATH build variable' do describe 'with a scoped pod target' do
@xcconfig.to_hash['PODS_FRAMEWORK_BUILD_PATH'].should == '"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods"' def specs
[
fixture_spec('banana-lib/BananaLib.podspec'),
fixture_spec('orange-framework/OrangeFramework.podspec'),
]
end end
describe 'with a scoped pod target' do
def pod_target(spec, target_definition) 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 end
it 'adds the framework build path to the xcconfig, with quotes, as framework search paths' do 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 end
it 'adds the framework header paths to the xcconfig, with quotes, as local headers' do 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 @xcconfig.to_hash['OTHER_CFLAGS'].should == expected
end end
end end
......
...@@ -132,12 +132,12 @@ module Pod ...@@ -132,12 +132,12 @@ module Pod
it 'generates the model to represent the target definitions' do it 'generates the model to represent the target definitions' do
target = @analyzer.analyze.targets.first target = @analyzer.analyze.targets.first
target.pod_targets.map(&:name).sort.should == [ target.pod_targets.map(&:name).sort.should == %w(
'JSONKit', JSONKit
'AFNetworking', AFNetworking
'SVPullToRefresh', libextobjc
'Pods-SampleProject-libextobjc', SVPullToRefresh
].sort ).sort
target.support_files_dir.should == config.sandbox.target_support_files_dir('Pods-SampleProject') target.support_files_dir.should == config.sandbox.target_support_files_dir('Pods-SampleProject')
target.pod_targets.map(&:archs).uniq.should == [[]] target.pod_targets.map(&:archs).uniq.should == [[]]
...@@ -237,11 +237,11 @@ module Pod ...@@ -237,11 +237,11 @@ module Pod
analyzer.analyze analyzer.analyze
analyzer.analyze.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w( analyzer.analyze.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w(
Pods-CLITool/Pods-CLITool-monkey Pods-CLITool/monkey-osx
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-BananaLib Pods-SampleProject-TestRunner/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-monkey Pods-SampleProject-TestRunner/monkey-ios
Pods-SampleProject/Pods-SampleProject-BananaLib Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/Pods-SampleProject-monkey Pods-SampleProject/monkey-ios
).sort ).sort
end end
...@@ -265,10 +265,10 @@ module Pod ...@@ -265,10 +265,10 @@ module Pod
analyzer.analyze analyzer.analyze
analyzer.analyze.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == %w( 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/BananaLib-Pods-SampleProject-TestRunner
Pods-SampleProject-TestRunner/Pods-SampleProject-TestRunner-monkey Pods-SampleProject-TestRunner/monkey-Pods-SampleProject-TestRunner
Pods-SampleProject/Pods-SampleProject-BananaLib Pods-SampleProject/BananaLib-Pods-SampleProject
Pods-SampleProject/Pods-SampleProject-monkey Pods-SampleProject/monkey-Pods-SampleProject
).sort ).sort
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