Commit 04d92a55 authored by Danielle Tomlinson's avatar Danielle Tomlinson Committed by GitHub

Merge pull request #5607 from DanToml/dan_target_validation

Move Installer target verification into the Xcode namespace
parents 5ae791f2 1e9a9e35
......@@ -185,6 +185,10 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
[Danielle Tomlinson](https://github.com/dantoml)
[#5937](https://github.com/CocoaPods/CocoaPods/issues/5937)
* Move Installer target verification into the Xcode namespace
[Danielle Tomlinson](https://github.com/DanToml)
[#5607](https://github.com/CocoaPods/CocoaPods/pull/5607)
##### Bug Fixes
* None.
......
......@@ -109,9 +109,7 @@ module Pod
prepare
resolve_dependencies
download_dependencies
verify_no_duplicate_framework_and_library_names
verify_no_static_framework_transitive_dependencies
verify_framework_usage
validate_targets
generate_pods_project
if installation_options.integrate_targets?
integrate_user_project
......@@ -286,19 +284,11 @@ module Pod
end
end
# TODO: the file accessor should be initialized by the sandbox as they
# created by the Pod source installer as well.
# @return [void] In this step we create the file accessors for the pod
# targets.
#
def create_file_accessors
pod_targets.each do |pod_target|
pod_root = sandbox.pod_dir(pod_target.root_spec.name)
path_list = Sandbox::PathList.new(pod_root)
file_accessors = pod_target.specs.map do |spec|
Sandbox::FileAccessor.new(path_list, spec.consumer(pod_target.platform))
end
pod_target.file_accessors ||= []
pod_target.file_accessors.concat(file_accessors)
end
sandbox.create_file_accessors(pod_targets)
end
# Downloads, installs the documentation and cleans the sources of the Pods
......@@ -393,67 +383,9 @@ module Pod
end
end
def verify_no_duplicate_framework_and_library_names
aggregate_targets.each do |aggregate_target|
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
file_accessors = pod_targets.flat_map(&:file_accessors)
frameworks = file_accessors.flat_map(&:vendored_frameworks).uniq.map(&:basename)
frameworks += pod_targets.select { |pt| pt.should_build? && pt.requires_frameworks? }.map(&:product_module_name)
verify_no_duplicate_names(frameworks, aggregate_target.label, 'frameworks')
libraries = file_accessors.flat_map(&:vendored_libraries).uniq.map(&:basename)
libraries += pod_targets.select { |pt| pt.should_build? && !pt.requires_frameworks? }.map(&:product_name)
verify_no_duplicate_names(libraries, aggregate_target.label, 'libraries')
end
end
end
def verify_no_duplicate_names(names, label, type)
duplicates = names.map { |n| n.to_s.downcase }.group_by { |f| f }.select { |_, v| v.size > 1 }.keys
unless duplicates.empty?
raise Informative, "The '#{label}' target has " \
"#{type} with conflicting names: #{duplicates.to_sentence}."
end
end
def verify_no_static_framework_transitive_dependencies
aggregate_targets.each do |aggregate_target|
next unless aggregate_target.requires_frameworks?
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
dependencies = pod_targets.select(&:should_build?).flat_map(&:dependencies)
dependended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
static_libs = dependended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
unless static_libs.empty?
raise Informative, "The '#{aggregate_target.label}' target has " \
"transitive dependencies that include static binaries: (#{static_libs.to_sentence})"
end
end
end
end
def verify_framework_usage
aggregate_targets.each do |aggregate_target|
next if aggregate_target.requires_frameworks?
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
swift_pods = pod_targets.select(&:uses_swift?)
unless swift_pods.empty?
raise Informative, 'Pods written in Swift can only be integrated as frameworks; ' \
'add `use_frameworks!` to your Podfile or target to opt into using it. ' \
"The Swift #{swift_pods.size == 1 ? 'Pod being used is' : 'Pods being used are'}: " +
swift_pods.map(&:name).to_sentence
end
end
end
def validate_targets
validator = Xcode::TargetValidator.new(aggregate_targets, pod_targets)
validator.validate!
end
# Runs the registered callbacks for the plugins pre install hooks.
......
......@@ -2,6 +2,7 @@ module Pod
class Installer
class Xcode
autoload :PodsProjectGenerator, 'cocoapods/installer/xcode/pods_project_generator'
autoload :TargetValidator, 'cocoapods/installer/xcode/target_validator'
end
end
end
module Pod
class Installer
class Xcode
# The {Xcode::TargetValidator} ensures that the pod and aggregate target
# configuration is valid for installation.
#
class TargetValidator
# @return [Array<AggregateTarget>] The aggregate targets that should be
# validated.
#
attr_reader :aggregate_targets
# @return [Array<PodTarget>] The pod targets that should be validated.
#
attr_reader :pod_targets
# Create a new TargetValidator with aggregate and pod targets to
# validate.
#
# @param [Array<AggregateTarget>] aggregate_targets
# The aggregate targets to validate.
#
# @param [Array<PodTarget>] pod_targets
# The pod targets to validate.
#
def initialize(aggregate_targets, pod_targets)
@aggregate_targets = aggregate_targets
@pod_targets = pod_targets
end
# Perform the validation steps for the provided aggregate and pod
# targets.
#
def validate!
verify_no_duplicate_framework_and_library_names
verify_no_static_framework_transitive_dependencies
verify_framework_usage
end
private
def verify_no_duplicate_framework_and_library_names
aggregate_targets.each do |aggregate_target|
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
file_accessors = pod_targets.flat_map(&:file_accessors)
frameworks = file_accessors.flat_map(&:vendored_frameworks).uniq.map(&:basename)
frameworks += pod_targets.select { |pt| pt.should_build? && pt.requires_frameworks? }.map(&:product_module_name)
verify_no_duplicate_names(frameworks, aggregate_target.label, 'frameworks')
libraries = file_accessors.flat_map(&:vendored_libraries).uniq.map(&:basename)
libraries += pod_targets.select { |pt| pt.should_build? && !pt.requires_frameworks? }.map(&:product_name)
verify_no_duplicate_names(libraries, aggregate_target.label, 'libraries')
end
end
end
def verify_no_duplicate_names(names, label, type)
duplicates = names.map { |n| n.to_s.downcase }.group_by { |f| f }.select { |_, v| v.size > 1 }.keys
unless duplicates.empty?
raise Informative, "The '#{label}' target has " \
"#{type} with conflicting names: #{duplicates.to_sentence}."
end
end
def verify_no_static_framework_transitive_dependencies
aggregate_targets.each do |aggregate_target|
next unless aggregate_target.requires_frameworks?
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
dependencies = pod_targets.select(&:should_build?).flat_map(&:dependencies)
dependended_upon_targets = pod_targets.select { |t| dependencies.include?(t.pod_name) && !t.should_build? }
static_libs = dependended_upon_targets.flat_map(&:file_accessors).flat_map(&:vendored_static_artifacts)
unless static_libs.empty?
raise Informative, "The '#{aggregate_target.label}' target has " \
"transitive dependencies that include static binaries: (#{static_libs.to_sentence})"
end
end
end
end
def verify_framework_usage
aggregate_targets.each do |aggregate_target|
next if aggregate_target.requires_frameworks?
aggregate_target.user_build_configurations.keys.each do |config|
pod_targets = aggregate_target.pod_targets_for_build_configuration(config)
swift_pods = pod_targets.select(&:uses_swift?)
unless swift_pods.empty?
raise Informative, 'Pods written in Swift can only be integrated as frameworks; ' \
'add `use_frameworks!` to your Podfile or target to opt into using it. ' \
"The Swift #{swift_pods.size == 1 ? 'Pod being used is' : 'Pods being used are'}: " +
swift_pods.map(&:name).to_sentence
end
end
end
end
end
end
end
end
......@@ -386,5 +386,28 @@ module Pod
end
#-------------------------------------------------------------------------#
public
# @!group Pods Helpers
# Creates the file accessors for the given Pod Targets.
#
# @param [Array<PodTarget>] pod_targets
# The Pod Targets to create file accessors for.
#
def create_file_accessors(pod_targets)
pod_targets.each do |pod_target|
pod_root = pod_dir(pod_target.root_spec.name)
path_list = PathList.new(pod_root)
file_accessors = pod_target.specs.map do |spec|
FileAccessor.new(path_list, spec.consumer(pod_target.platform))
end
pod_target.file_accessors ||= []
pod_target.file_accessors.concat(file_accessors)
end
end
#-------------------------------------------------------------------------#
end
end
......@@ -480,9 +480,7 @@ module Pod
# for all available platforms with xcodebuild.
#
def install_pod
%i(verify_no_duplicate_framework_and_library_names
verify_no_static_framework_transitive_dependencies
verify_framework_usage generate_pods_project integrate_user_project
%i(validate_targets generate_pods_project integrate_user_project
perform_post_install_actions).each { |m| @installer.send(m) }
deployment_target = spec.subspec_by_name(subspec_name).deployment_target(consumer.platform_name)
......
require File.expand_path('../../../../spec_helper', __FILE__)
module Pod
class Installer
class Xcode
describe TargetValidator do
# @return [Lockfile]
#
def generate_lockfile(lockfile_version: Pod::VERSION)
hash = {}
hash['PODS'] = []
hash['DEPENDENCIES'] = []
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = lockfile_version
Pod::Lockfile.new(hash)
end
# @return [AnalysisResult]
#
def create_validator(sandbox, podfile, lockfile)
installation_options = Installer::InstallationOptions.new.tap do |options|
options.integrate_targets = false
end
@analyzer = Analyzer.new(config.sandbox, podfile, lockfile).tap do |analyzer|
analyzer.installation_options = installation_options
end
result = @analyzer.analyze
aggregate_targets = result.targets
pod_targets = aggregate_targets.map(&:pod_targets).flatten.uniq
sandbox.create_file_accessors(pod_targets)
TargetValidator.new(aggregate_targets, pod_targets)
end
describe '#verify_no_duplicate_framework_and_library_names' do
it 'detects duplicate library names' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([Pathname('a/libBananalib.a')])
Pod::Specification.any_instance.stubs(:dependencies).returns([])
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@validator = create_validator(config.sandbox, podfile, lockfile)
should.raise(Informative) { @validator.validate! }.message.should.match /conflict.*bananalib/
end
it 'detects duplicate framework names' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).
returns([Pathname('a/monkey.framework')]).then.
returns([Pathname('b/monkey.framework')]).then.
returns([Pathname('c/monkey.framework')])
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@validator = create_validator(config.sandbox, podfile, lockfile)
should.raise(Informative) { @validator.validate! }.message.should.match /conflict.*monkey/
end
it 'allows duplicate references to the same expanded framework path' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).returns([fixture('monkey/dynamic-monkey.framework')])
Sandbox::FileAccessor.any_instance.stubs(:dynamic_binary?).returns(true)
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
use_frameworks!
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@validator = create_validator(config.sandbox, podfile, lockfile)
should.not.raise(Informative) { @validator.validate! }
end
end
#-------------------------------------------------------------------------#
describe '#verify_no_static_framework_transitive_dependencies' do
before do
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
@podfile = Pod::Podfile.new do
install! 'cocoapods', 'integrate_targets' => false
platform :ios, '8.0'
project 'SampleProject/SampleProject'
use_frameworks!
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
@lockfile = generate_lockfile
@file = Pathname('/yolo.m')
@file.stubs(:realpath).returns(@file)
@lib_thing = Pathname('/libThing.a')
@lib_thing.stubs(:realpath).returns(@lib_thing)
end
it 'detects transitive static dependencies which are linked directly to the user target' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@validator = create_validator(config.sandbox, @podfile, @lockfile)
should.raise(Informative) { @validator.validate! }.message.should.match /transitive.*libThing/
end
it 'allows transitive static dependencies which contain other source code' do
Sandbox::FileAccessor.any_instance.stubs(:source_files).returns([@file])
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@validator = create_validator(config.sandbox, @podfile, @lockfile)
should.not.raise(Informative) { @validator.validate! }
end
it 'allows transitive static dependencies when both dependencies are linked against the user target' do
PodTarget.any_instance.stubs(:should_build? => false)
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@validator = create_validator(config.sandbox, @podfile, @lockfile)
should.not.raise(Informative) { @validator.validate! }
end
end
#-------------------------------------------------------------------------#
describe '#verify_framework_usage' do
it 'raises when Swift pods are used without explicit `use_frameworks!`' do
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@validator = create_validator(config.sandbox, podfile, lockfile)
should.raise(Informative) { @validator.validate! }.message.should.match /use_frameworks/
end
end
end
end
end
end
......@@ -61,9 +61,7 @@ module Pod
before do
@installer.stubs(:resolve_dependencies)
@installer.stubs(:download_dependencies)
@installer.stubs(:verify_no_duplicate_framework_and_library_names)
@installer.stubs(:verify_no_static_framework_transitive_dependencies)
@installer.stubs(:verify_framework_usage)
@installer.stubs(:validate_targets)
@installer.stubs(:generate_pods_project)
@installer.stubs(:integrate_user_project)
@installer.stubs(:run_plugins_post_install_hooks)
......@@ -259,139 +257,6 @@ module Pod
#-------------------------------------------------------------------------#
describe '#verify_no_duplicate_framework_and_library_names' do
it 'detects duplicate library names' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([Pathname('a/libBananalib.a')])
Pod::Specification.any_instance.stubs(:dependencies).returns([])
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@installer = Installer.new(config.sandbox, podfile, lockfile)
@installer.installation_options.integrate_targets = false
should.raise(Informative) { @installer.install! }.message.should.match /conflict.*bananalib/
end
it 'detects duplicate framework names' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).
returns([Pathname('a/monkey.framework')]).then.
returns([Pathname('b/monkey.framework')]).then.
returns([Pathname('c/monkey.framework')])
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@installer = Installer.new(config.sandbox, podfile, lockfile)
@installer.installation_options.integrate_targets = false
should.raise(Informative) { @installer.install! }.message.should.match /conflict.*monkey/
end
it 'allows duplicate references to the same expanded framework path' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_frameworks).returns([fixture('monkey/dynamic-monkey.framework')])
Sandbox::FileAccessor.any_instance.stubs(:dynamic_binary?).returns(true)
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
use_frameworks!
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@installer = Installer.new(config.sandbox, podfile, lockfile)
@installer.installation_options.integrate_targets = false
should.not.raise(Informative) { @installer.install! }
end
end
#-------------------------------------------------------------------------#
describe '#verify_no_static_framework_transitive_dependencies' do
before do
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
@podfile = Pod::Podfile.new do
install! 'cocoapods', 'integrate_targets' => false
platform :ios, '8.0'
project 'SampleProject/SampleProject'
use_frameworks!
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
target 'SampleProject'
end
@lockfile = generate_lockfile
@file = Pathname('/yolo.m')
@file.stubs(:realpath).returns(@file)
@lib_thing = Pathname('/libThing.a')
@lib_thing.stubs(:realpath).returns(@lib_thing)
end
it 'detects transitive static dependencies which are linked directly to the user target' do
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@installer = Installer.new(config.sandbox, @podfile, @lockfile)
should.raise(Informative) { @installer.install! }.message.should.match /transitive.*libThing/
end
it 'allows transitive static dependencies which contain other source code' do
Sandbox::FileAccessor.any_instance.stubs(:source_files).returns([@file])
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@installer = Installer.new(config.sandbox, @podfile, @lockfile)
should.not.raise(Informative) { @installer.install! }
end
it 'allows transitive static dependencies when both dependencies are linked against the user target' do
PodTarget.any_instance.stubs(:should_build? => false)
Sandbox::FileAccessor.any_instance.stubs(:vendored_libraries).returns([@lib_thing])
@installer = Installer.new(config.sandbox, @podfile, @lockfile)
should.not.raise(Informative) { @installer.install! }
end
end
#-------------------------------------------------------------------------#
describe '#verify_framework_usage' do
it 'raises when Swift pods are used without explicit `use_frameworks!`' do
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
project 'SampleProject/SampleProject'
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'matryoshka', :path => (fixture_path + 'matryoshka').to_s
target 'SampleProject'
end
lockfile = generate_lockfile
@installer = Installer.new(config.sandbox, podfile, lockfile)
@installer.installation_options.integrate_targets = false
should.raise(Informative) { @installer.install! }.message.should.match /use_frameworks/
end
end
#-------------------------------------------------------------------------#
describe 'Dependencies Resolution' do
describe 'updating spec repos' do
it 'does not updates the repositories by default' 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