Commit e61e6c8c authored by Kyle Fuller's avatar Kyle Fuller

Merge pull request #2725 from CocoaPods/lockfile-checkout-options

[Analyzer] Use specific checkout options from lockfile
parents 9ca5e278 0c218e81
...@@ -14,6 +14,13 @@ To install release candidates run `[sudo] gem install cocoapods --pre` ...@@ -14,6 +14,13 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
[Samuel Giddins](https://github.com/segiddins) [Samuel Giddins](https://github.com/segiddins)
[#532](https://github.com/CocoaPods/CocoaPods/issues/532) [#532](https://github.com/CocoaPods/CocoaPods/issues/532)
* From now on, pods installed directly from their repositories will be recorded
in the `Podfile.lock` file and will be guaranteed to be checked-out using the
same revision on subsequent installations. Examples of this are when using
the `:git`, `:svn`, or `:hg` options in your `Podfile`.
[Samuel Giddins](https://github.com/segiddins)
[#1058](https://github.com/CocoaPods/CocoaPods/issues/1058)
##### Bug Fixes ##### Bug Fixes
* Fix an output formatting issue with various commands like `pod search` * Fix an output formatting issue with various commands like `pod search`
......
GIT GIT
remote: https://github.com/CocoaPods/Core.git remote: https://github.com/CocoaPods/Core.git
revision: 60a486e8141323390dc5ff7f6676cf893a251a48 revision: 320bb0cf36d38f038a6478ad87618dd8bf148ffc
branch: master branch: master
specs: specs:
cocoapods-core (0.35.0.rc2) cocoapods-core (0.35.0.rc2)
...@@ -69,7 +69,7 @@ PATH ...@@ -69,7 +69,7 @@ PATH
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
activesupport (4.1.7) activesupport (4.1.8)
i18n (~> 0.6, >= 0.6.9) i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
......
...@@ -13,9 +13,11 @@ module Pod ...@@ -13,9 +13,11 @@ module Pod
# hash. # hash.
# #
def self.from_dependency(dependency, podfile_path) def self.from_dependency(dependency, podfile_path)
name = dependency.root_name from_params(dependency.external_source, dependency, podfile_path)
params = dependency.external_source end
def self.from_params(params, dependency, podfile_path)
name = dependency.root_name
if klass = concrete_class_from_params(params) if klass = concrete_class_from_params(params)
klass.new(name, params, podfile_path) klass.new(name, params, podfile_path)
else else
......
...@@ -465,8 +465,9 @@ module Pod ...@@ -465,8 +465,9 @@ module Pod
# @return [void] # @return [void]
# #
def write_lockfiles def write_lockfiles
# checkout_options = sandbox.checkout_options external_source_pods = podfile.dependencies.select(&:external_source).map(&:root_name).uniq
@lockfile = Lockfile.generate(podfile, analysis_result.specifications) checkout_options = sandbox.checkout_sources.select { |root_name, _| external_source_pods.include? root_name }
@lockfile = Lockfile.generate(podfile, analysis_result.specifications, checkout_options)
UI.message "- Writing Lockfile in #{UI.path config.lockfile_path}" do UI.message "- Writing Lockfile in #{UI.path config.lockfile_path}" do
@lockfile.write_to_disk(config.lockfile_path) @lockfile.write_to_disk(config.lockfile_path)
......
...@@ -53,6 +53,7 @@ module Pod ...@@ -53,6 +53,7 @@ module Pod
@result.podfile_state = generate_podfile_state @result.podfile_state = generate_podfile_state
@locked_dependencies = generate_version_locking_dependencies @locked_dependencies = generate_version_locking_dependencies
store_existing_checkout_options
fetch_external_sources if allow_fetches fetch_external_sources if allow_fetches
@result.specs_by_target = resolve_dependencies @result.specs_by_target = resolve_dependencies
@result.specifications = generate_specifications @result.specifications = generate_specifications
...@@ -264,33 +265,81 @@ module Pod ...@@ -264,33 +265,81 @@ module Pod
# #
def fetch_external_sources def fetch_external_sources
return unless allow_pre_downloads? return unless allow_pre_downloads?
deps_to_fetch = []
deps_to_fetch_if_needed = []
deps_with_external_source = podfile.dependencies.select(&:external_source)
deps_with_different_sources = podfile.dependencies.group_by(&:root_name).select { |_root_name, dependencies| dependencies.map(&:external_source).uniq.count > 1 } verify_no_pods_with_different_sources!
unless dependencies_to_fetch.empty?
UI.section 'Fetching external sources' do
dependencies_to_fetch.sort.each do |dependency|
fetch_external_source(dependency, !pods_to_fetch.include?(dependency.name))
end
end
end
end
def verify_no_pods_with_different_sources!
deps_with_different_sources = podfile.dependencies.group_by(&:root_name).
select { |_root_name, dependencies| dependencies.map(&:external_source).uniq.count > 1 }
deps_with_different_sources.each do |root_name, dependencies| deps_with_different_sources.each do |root_name, dependencies|
raise Informative, "There are multiple dependencies with different sources for `#{root_name}` in #{UI.path podfile.defined_in_file}:\n\n- #{dependencies.map(&:to_s).join("\n- ")}" raise Informative, "There are multiple dependencies with different " \
"sources for `#{root_name}` in #{UI.path podfile.defined_in_file}:" \
"\n\n- #{dependencies.map(&:to_s).join("\n- ")}"
end end
end
if update_mode == :all def fetch_external_source(dependency, use_lockfile_options)
deps_to_fetch = deps_with_external_source checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name) if lockfile
if checkout_options && use_lockfile_options
source = ExternalSources.from_params(checkout_options, dependency, podfile.defined_in_file)
else else
source = ExternalSources.from_dependency(dependency, podfile.defined_in_file)
end
source.fetch(sandbox)
end
def dependencies_to_fetch
@deps_to_fetch ||= begin
deps_to_fetch = []
deps_to_fetch_if_needed = []
deps_with_external_source = podfile.dependencies.select(&:external_source)
if update_mode == :all
deps_to_fetch = deps_with_external_source
else
deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.name) }
deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.name) }
deps_to_fetch += deps_to_fetch_if_needed.select do |dep|
sandbox.specification(dep.name).nil? ||
!dep.external_source[:local].nil? ||
!dep.external_source[:path].nil? ||
!sandbox.pod_dir(dep.root_name).directory? ||
checkout_requires_update?(dep)
end
end
deps_to_fetch.uniq(&:root_name)
end
end
def checkout_requires_update?(dependency)
return true unless lockfile && sandbox.manifest
locked_checkout_options = lockfile.checkout_options_for_pod_named(dependency.root_name)
sandbox_checkout_options = sandbox.manifest.checkout_options_for_pod_named(dependency.root_name)
locked_checkout_options != sandbox_checkout_options
end
def pods_to_fetch
@pods_to_fetch ||= begin
pods_to_fetch = result.podfile_state.added + result.podfile_state.changed pods_to_fetch = result.podfile_state.added + result.podfile_state.changed
if update_mode == :selected if update_mode == :selected
pods_to_fetch += update[:pods] pods_to_fetch += update[:pods]
end end
deps_to_fetch = deps_with_external_source.select { |dep| pods_to_fetch.include?(dep.name) } pods_to_fetch
deps_to_fetch_if_needed = deps_with_external_source.select { |dep| result.podfile_state.unchanged.include?(dep.name) }
deps_to_fetch += deps_to_fetch_if_needed.select { |dep| sandbox.specification(dep.name).nil? || !dep.external_source[:local].nil? || !dep.external_source[:path].nil? || !sandbox.pod_dir(dep.name).directory? }
end end
end
unless deps_to_fetch.empty? def store_existing_checkout_options
UI.section 'Fetching external sources' do podfile.dependencies.select(&:external_source).each do |dep|
deps_to_fetch.uniq(&:root_name).sort.each do |dependency| if checkout_options = lockfile && lockfile.checkout_options_for_pod_named(dep.root_name)
source = ExternalSources.from_dependency(dependency, podfile.defined_in_file) sandbox.store_checkout_source(dep.root_name, checkout_options)
source.fetch(sandbox)
end
end end
end end
end end
......
...@@ -64,8 +64,12 @@ module Pod ...@@ -64,8 +64,12 @@ module Pod
# @return [Lockfile] the manifest which contains the information about the # @return [Lockfile] the manifest which contains the information about the
# installed pods. # installed pods.
# #
attr_accessor :manifest
def manifest def manifest
Lockfile.from_file(manifest_path) if manifest_path.exist? @manifest ||= begin
Lockfile.from_file(manifest_path) if manifest_path.exist?
end
end end
# @return [Project] the Pods project. # @return [Project] the Pods project.
......
Subproject commit 365a0c8d93de662c019d6dbf3131f65842952357 Subproject commit 1b603f3f9a53c497734ffaf9926416c0582064c2
...@@ -63,7 +63,8 @@ module Pod ...@@ -63,7 +63,8 @@ module Pod
s.version = '1.0' s.version = '1.0'
end, end,
] ]
Lockfile.generate(podfile, specs).write_to_disk(temporary_directory + 'Podfile.lock') external_sources = {}
Lockfile.generate(podfile, specs, external_sources).write_to_disk(temporary_directory + 'Podfile.lock')
end end
it 'for a single missing Pod' do it 'for a single missing Pod' do
......
...@@ -176,6 +176,11 @@ describe_cli 'pod' do ...@@ -176,6 +176,11 @@ describe_cli 'pod' do
behaves_like cli_spec 'install_podfile_callbacks', behaves_like cli_spec 'install_podfile_callbacks',
'install --no-repo-update' 'install --no-repo-update'
end end
describe 'Uses Lockfile checkout options' do
behaves_like cli_spec 'install_using_checkout_options',
'install --no-repo-update'
end
end end
#--------------------------------------# #--------------------------------------#
......
...@@ -26,7 +26,9 @@ require 'bacon' ...@@ -26,7 +26,9 @@ require 'bacon'
require 'mocha-on-bacon' require 'mocha-on-bacon'
require 'pretty_bacon' require 'pretty_bacon'
require 'pathname' require 'pathname'
require 'active_support/core_ext/string/strip' require 'active_support/core_ext/string/strip'
require 'active_support/core_ext/object/deep_dup'
ROOT = Pathname.new(File.expand_path('../../', __FILE__)) ROOT = Pathname.new(File.expand_path('../../', __FILE__))
$:.unshift((ROOT + 'lib').to_s) $:.unshift((ROOT + 'lib').to_s)
......
require File.expand_path('../../../spec_helper', __FILE__) require File.expand_path('../../../spec_helper', __FILE__)
# @return [Analyzer] the sample analyzer.
#
def create_analyzer
@podfile = Pod::Podfile.new do
platform :ios, '6.0'
xcodeproj 'SampleProject/SampleProject'
pod 'JSONKit', '1.5pre'
pod 'AFNetworking', '1.0.1'
pod 'SVPullToRefresh', '0.4'
pod 'libextobjc/EXTKeyPathCoding', '0.2.3'
end
hash = {}
hash['PODS'] = ['JSONKit (1.5pre)', 'NUI (0.2.0)', 'SVPullToRefresh (0.4)']
hash['DEPENDENCIES'] = %w(JSONKit NUI SVPullToRefresh)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
lockfile = Pod::Lockfile.new(hash)
SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, lockfile)
end
#-----------------------------------------------------------------------------# #-----------------------------------------------------------------------------#
module Pod module Pod
describe Installer::Analyzer do describe Installer::Analyzer do
before do
@analyzer = create_analyzer
end
describe 'Analysis' do describe 'Analysis' do
before do
@podfile = Pod::Podfile.new do
platform :ios, '6.0'
xcodeproj 'SampleProject/SampleProject'
pod 'JSONKit', '1.5pre'
pod 'AFNetworking', '1.0.1'
pod 'SVPullToRefresh', '0.4'
pod 'libextobjc/EXTKeyPathCoding', '0.2.3'
end
hash = {}
hash['PODS'] = ['JSONKit (1.5pre)', 'NUI (0.2.0)', 'SVPullToRefresh (0.4)']
hash['DEPENDENCIES'] = %w(JSONKit NUI SVPullToRefresh)
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
@lockfile = Pod::Lockfile.new(hash)
SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, @lockfile)
end
it 'returns whether an installation should be performed' do it 'returns whether an installation should be performed' do
@analyzer.needs_install?.should.be.true @analyzer.needs_install?.should.be.true
...@@ -185,57 +178,6 @@ module Pod ...@@ -185,57 +178,6 @@ module Pod
@analyzer.send(:fetch_external_sources) @analyzer.send(:fetch_external_sources)
end end
it 'raises when dependencies with the same name have different ' \
'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'SEGModules', :git => 'https://github.com/segiddins/SEGModules.git'
pod 'SEGModules', :git => 'https://github.com/segiddins/Modules.git'
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `SEGModules`/
e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/SEGModules.git`\)}
e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/Modules.git`\)}
end
it 'raises when dependencies with the same root name have different ' \
'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'RestKit/Core', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', :git => 'https://github.com/segiddins/RestKit.git'
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `RestKit`/
e.message.should.match %r{RestKit/Core \(from `https://github.com/RestKit/RestKit.git`\)}
e.message.should.match %r{RestKit \(from `https://github.com/segiddins/RestKit.git`\)}
end
it 'raises when dependencies with the same name have different ' \
'external sources with one being nil' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'RestKit', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', '~> 0.23.0'
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
e.message.should.match /different sources for `RestKit`/
e.message.should.match %r{RestKit \(from `https://github.com/RestKit/RestKit.git`\)}
e.message.should.match %r{RestKit \(~> 0.23.0\)}
end
xit 'it fetches the specification from either the sandbox or from the remote be default' do xit 'it fetches the specification from either the sandbox or from the remote be default' do
dependency = Dependency.new('Name', :git => 'www.example.com') dependency = Dependency.new('Name', :git => 'www.example.com')
ExternalSources::DownloaderSource.any_instance.expects(:specification_from_external).returns(Specification.new).once ExternalSources::DownloaderSource.any_instance.expects(:specification_from_external).returns(Specification.new).once
...@@ -311,301 +253,440 @@ module Pod ...@@ -311,301 +253,440 @@ module Pod
state.added.sort.should == %w(AFNetworking JSONKit SVPullToRefresh libextobjc) state.added.sort.should == %w(AFNetworking JSONKit SVPullToRefresh libextobjc)
end end
end #-------------------------------------------------------------------------#
#-------------------------------------------------------------------------# describe 'Private helpers' do
describe 'Private helpers' do describe '#compute_user_project_targets' do
it 'uses the path specified in the target definition while computing the path of the user project' do
target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.user_project_path = 'SampleProject/SampleProject'
describe '#compute_user_project_targets' do path = @analyzer.send(:compute_user_project_path, target_definition)
it 'uses the path specified in the target definition while computing the path of the user project' do path.to_s.should.include 'SampleProject/SampleProject.xcodeproj'
target_definition = Podfile::TargetDefinition.new(:default, nil) end
target_definition.user_project_path = 'SampleProject/SampleProject'
path = @analyzer.send(:compute_user_project_path, target_definition) it 'raises if the user project of the target definition does not exists while computing the path of the user project' do
path.to_s.should.include 'SampleProject/SampleProject.xcodeproj' target_definition = Podfile::TargetDefinition.new(:default, nil)
end target_definition.user_project_path = 'Test'
it 'raises if the user project of the target definition does not exists while computing the path of the user project' do e = lambda { @analyzer.send(:compute_user_project_path, target_definition) }.should.raise Informative
target_definition = Podfile::TargetDefinition.new(:default, nil) e.message.should.match /Unable to find/
target_definition.user_project_path = 'Test' end
e = lambda { @analyzer.send(:compute_user_project_path, target_definition) }.should.raise Informative it 'if not specified in the target definition if looks if there is only one project' do
e.message.should.match /Unable to find/ target_definition = Podfile::TargetDefinition.new(:default, nil)
end config.installation_root = config.installation_root + 'SampleProject'
it 'if not specified in the target definition if looks if there is only one project' do path = @analyzer.send(:compute_user_project_path, target_definition)
target_definition = Podfile::TargetDefinition.new(:default, nil) path.to_s.should.include 'SampleProject/SampleProject.xcodeproj'
config.installation_root = config.installation_root + 'SampleProject' end
path = @analyzer.send(:compute_user_project_path, target_definition) it 'if not specified in the target definition if looks if there is only one project' do
path.to_s.should.include 'SampleProject/SampleProject.xcodeproj' target_definition = Podfile::TargetDefinition.new(:default, nil)
end
e = lambda { @analyzer.send(:compute_user_project_path, target_definition) }.should.raise Informative
e.message.should.match /Could not.*select.*project/
end
it 'if not specified in the target definition if looks if there is only one project' do it 'does not take aggregate targets into consideration' do
target_definition = Podfile::TargetDefinition.new(:default, nil) aggregate_class = Xcodeproj::Project::Object::PBXAggregateTarget
sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
sample_project = Xcodeproj::Project.open(sample_project_path)
sample_project.targets.map(&:class).should.include(aggregate_class)
e = lambda { @analyzer.send(:compute_user_project_path, target_definition) }.should.raise Informative native_targets = @analyzer.send(:native_targets, sample_project).map(&:class)
e.message.should.match /Could not.*select.*project/ native_targets.should.not.include(aggregate_class)
end
end end
it 'does not take aggregate targets into consideration' do #--------------------------------------#
aggregate_class = Xcodeproj::Project::Object::PBXAggregateTarget
sample_project_path = SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
sample_project = Xcodeproj::Project.open(sample_project_path)
sample_project.targets.map(&:class).should.include(aggregate_class)
native_targets = @analyzer.send(:native_targets, sample_project).map(&:class) describe '#compute_user_project_targets' do
native_targets.should.not.include(aggregate_class)
end
end
#--------------------------------------# it 'returns the targets specified in the target definition' do
target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.link_with = ['UserTarget']
user_project = Xcodeproj::Project.new('path')
user_project.new_target(:application, 'FirstTarget', :ios)
user_project.new_target(:application, 'UserTarget', :ios)
describe '#compute_user_project_targets' do targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project)
targets.map(&:name).should == ['UserTarget']
end
it 'returns the targets specified in the target definition' do it 'raises if it is unable to find the targets specified by the target definition' do
target_definition = Podfile::TargetDefinition.new(:default, nil) target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.link_with = ['UserTarget'] target_definition.link_with = ['UserTarget']
user_project = Xcodeproj::Project.new('path') user_project = Xcodeproj::Project.new('path')
user_project.new_target(:application, 'FirstTarget', :ios)
user_project.new_target(:application, 'UserTarget', :ios)
targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project) e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative
targets.map(&:name).should == ['UserTarget'] e.message.should.match /Unable to find the targets/
end end
it 'raises if it is unable to find the targets specified by the target definition' do it 'returns the target with the same name of the target definition' do
target_definition = Podfile::TargetDefinition.new(:default, nil) target_definition = Podfile::TargetDefinition.new('UserTarget', nil)
target_definition.link_with = ['UserTarget'] user_project = Xcodeproj::Project.new('path')
user_project = Xcodeproj::Project.new('path') user_project.new_target(:application, 'FirstTarget', :ios)
user_project.new_target(:application, 'UserTarget', :ios)
e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project)
e.message.should.match /Unable to find the targets/ targets.map(&:name).should == ['UserTarget']
end end
it 'returns the target with the same name of the target definition' do it 'raises if the name of the target definition does not match any file' do
target_definition = Podfile::TargetDefinition.new('UserTarget', nil) target_definition = Podfile::TargetDefinition.new('UserTarget', nil)
user_project = Xcodeproj::Project.new('path') user_project = Xcodeproj::Project.new('path')
user_project.new_target(:application, 'FirstTarget', :ios) e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative
user_project.new_target(:application, 'UserTarget', :ios) e.message.should.match /Unable to find a target named/
end
targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project) it 'returns the first target of the project if the target definition is named default' do
targets.map(&:name).should == ['UserTarget'] target_definition = Podfile::TargetDefinition.new('Pods', nil)
end target_definition.link_with_first_target = true
user_project = Xcodeproj::Project.new('path')
user_project.new_target(:application, 'FirstTarget', :ios)
user_project.new_target(:application, 'UserTarget', :ios)
it 'raises if the name of the target definition does not match any file' do targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project)
target_definition = Podfile::TargetDefinition.new('UserTarget', nil) targets.map(&:name).should == ['FirstTarget']
user_project = Xcodeproj::Project.new('path') end
e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative
e.message.should.match /Unable to find a target named/
end
it 'returns the first target of the project if the target definition is named default' do it 'raises if the default target definition cannot be linked because there are no user targets' do
target_definition = Podfile::TargetDefinition.new('Pods', nil) target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.link_with_first_target = true user_project = Xcodeproj::Project.new('path')
user_project = Xcodeproj::Project.new('path') e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative
user_project.new_target(:application, 'FirstTarget', :ios) e.message.should.match /Unable to find a target/
user_project.new_target(:application, 'UserTarget', :ios) end
targets = @analyzer.send(:compute_user_project_targets, target_definition, user_project)
targets.map(&:name).should == ['FirstTarget']
end end
it 'raises if the default target definition cannot be linked because there are no user targets' do #--------------------------------------#
target_definition = Podfile::TargetDefinition.new(:default, nil)
user_project = Xcodeproj::Project.new('path')
e = lambda { @analyzer.send(:compute_user_project_targets, target_definition, user_project) }.should.raise Informative
e.message.should.match /Unable to find a target/
end
end describe '#compute_user_build_configurations' do
#--------------------------------------# it 'returns the user build configurations of the user targets' do
user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios)
configuration = user_project.new(Xcodeproj::Project::Object::XCBuildConfiguration)
configuration.name = 'AppStore'
target.build_configuration_list.build_configurations << configuration
target_definition = Podfile::TargetDefinition.new(:default, nil)
user_targets = [target]
describe '#compute_user_build_configurations' do configurations = @analyzer.send(:compute_user_build_configurations, target_definition, user_targets)
configurations.should == {
'Debug' => :debug,
'Release' => :release,
'AppStore' => :release,
}
end
it 'returns the user build configurations of the user targets' do it 'returns the user build configurations specified in the target definition' do
user_project = Xcodeproj::Project.new('path') target_definition = Podfile::TargetDefinition.new(:default, nil)
target = user_project.new_target(:application, 'Target', :ios) target_definition.build_configurations = { 'AppStore' => :release }
configuration = user_project.new(Xcodeproj::Project::Object::XCBuildConfiguration) user_targets = []
configuration.name = 'AppStore'
target.build_configuration_list.build_configurations << configuration
target_definition = Podfile::TargetDefinition.new(:default, nil) configurations = @analyzer.send(:compute_user_build_configurations, target_definition, user_targets)
user_targets = [target] configurations.should == { 'AppStore' => :release }
end
configurations = @analyzer.send(:compute_user_build_configurations, target_definition, user_targets)
configurations.should == {
'Debug' => :debug,
'Release' => :release,
'AppStore' => :release,
}
end end
it 'returns the user build configurations specified in the target definition' do #--------------------------------------#
target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.build_configurations = { 'AppStore' => :release }
user_targets = []
configurations = @analyzer.send(:compute_user_build_configurations, target_definition, user_targets) describe '#compute_archs_for_target_definition' do
configurations.should == { 'AppStore' => :release }
end
end it 'handles a single ARCH defined in a single user target' do
user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios)
target.build_configuration_list.set_setting('ARCHS', 'armv7')
#--------------------------------------# target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.set_platform(:ios, '4.0')
user_targets = [target]
describe '#compute_archs_for_target_definition' do archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets)
archs.should == 'armv7'
end
it 'handles a single ARCH defined in a single user target' do it 'handles a single ARCH defined in multiple user targets' do
user_project = Xcodeproj::Project.new('path') user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios) targeta = user_project.new_target(:application, 'Target', :ios)
target.build_configuration_list.set_setting('ARCHS', 'armv7') targeta.build_configuration_list.set_setting('ARCHS', 'armv7')
targetb = user_project.new_target(:application, 'Target', :ios)
targetb.build_configuration_list.set_setting('ARCHS', 'armv7')
target_definition = Podfile::TargetDefinition.new(:default, nil) target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.set_platform(:ios, '4.0') target_definition.set_platform(:ios, '4.0')
user_targets = [target] user_targets = [targeta, targetb]
archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets) archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets)
archs.should == 'armv7' archs.should == 'armv7'
end end
it 'handles an Array of ARCHs defined in a single user target' do
user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios)
target.build_configuration_list.set_setting('ARCHS', %w(armv7 i386))
target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.set_platform(:ios, '4.0')
user_targets = [target]
archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets)
%w(armv7 i386).each { |a| archs.should.include a }
end
it 'handles a single ARCH defined in multiple user targets' do it 'handles an Array of ARCHs defined multiple user targets' do
user_project = Xcodeproj::Project.new('path') user_project = Xcodeproj::Project.new('path')
targeta = user_project.new_target(:application, 'Target', :ios) targeta = user_project.new_target(:application, 'Target', :ios)
targeta.build_configuration_list.set_setting('ARCHS', 'armv7') targeta.build_configuration_list.set_setting('ARCHS', %w(armv7 armv7s))
targetb = user_project.new_target(:application, 'Target', :ios) targetb = user_project.new_target(:application, 'Target', :ios)
targetb.build_configuration_list.set_setting('ARCHS', 'armv7') targetb.build_configuration_list.set_setting('ARCHS', %w(armv7 i386))
target_definition = Podfile::TargetDefinition.new(:default, nil) target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.set_platform(:ios, '4.0') target_definition.set_platform(:ios, '4.0')
user_targets = [targeta, targetb] user_targets = [targeta, targetb]
archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets) archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets)
archs.should == 'armv7' %w(armv7 armv7s i386).each { |a| archs.should.include a }
end
end end
it 'handles an Array of ARCHs defined in a single user target' do #--------------------------------------#
user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios) describe '#compute_platform_for_target_definition' do
target.build_configuration_list.set_setting('ARCHS', %w(armv7 i386))
it 'returns the platform specified in the target definition' do
target_definition = Podfile::TargetDefinition.new(:default, nil)
target_definition.set_platform(:ios, '4.0')
user_targets = []
configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets)
configurations.should == Platform.new(:ios, '4.0')
end
it 'infers the platform from the user targets' do
user_project = Xcodeproj::Project.new('path')
target = user_project.new_target(:application, 'Target', :ios)
target.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
target_definition = Podfile::TargetDefinition.new(:default, nil)
user_targets = [target]
configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets)
configurations.should == Platform.new(:ios, '4.0')
end
it 'uses the lowest deployment target of the user targets if inferring the platform' do
user_project = Xcodeproj::Project.new('path')
target1 = user_project.new_target(:application, 'Target', :ios)
configuration1 = target1.build_configuration_list.build_configurations.first
target1.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target1.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
target2 = user_project.new_target(:application, 'Target', :ios)
target2.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target2.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '6.0')
target_definition = Podfile::TargetDefinition.new(:default, nil)
user_targets = [target1, target2]
target_definition = Podfile::TargetDefinition.new(:default, nil) configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets)
target_definition.set_platform(:ios, '4.0') configurations.should == Platform.new(:ios, '4.0')
user_targets = [target] end
it 'raises if the user targets have a different platform' do
user_project = Xcodeproj::Project.new('path')
target1 = user_project.new_target(:application, 'Target', :ios)
target1.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target1.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets) target2 = user_project.new_target(:application, 'Target', :ios)
%w(armv7 i386).each { |a| archs.should.include a } target2.build_configuration_list.set_setting('SDKROOT', 'macosx')
target2.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '10.6')
target_definition = Podfile::TargetDefinition.new(:default, nil)
user_targets = [target1, target2]
e = lambda { @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets) }.should.raise Informative
e.message.should.match /Targets with different platforms/
end
end end
it 'handles an Array of ARCHs defined multiple user targets' do #--------------------------------------#
user_project = Xcodeproj::Project.new('path')
targeta = user_project.new_target(:application, 'Target', :ios)
targeta.build_configuration_list.set_setting('ARCHS', %w(armv7 armv7s))
targetb = user_project.new_target(:application, 'Target', :ios)
targetb.build_configuration_list.set_setting('ARCHS', %w(armv7 i386))
target_definition = Podfile::TargetDefinition.new(:default, nil) describe '#sources' do
target_definition.set_platform(:ios, '4.0') describe 'when there are no explicit sources' do
user_targets = [targeta, targetb] it 'defaults to all sources' do
@analyzer.send(:sources).map(&:url).should ==
SourcesManager.all.map(&:url)
end
end
archs = @analyzer.send(:compute_archs_for_target_definition, target_definition, user_targets) describe 'when there are explicit sources' do
%w(armv7 armv7s i386).each { |a| archs.should.include a } it 'raises if no specs repo with that URL could be added' do
podfile = Podfile.new do
source 'not-a-git-repo'
end
@analyzer.instance_variable_set(:@podfile, podfile)
should.raise Informative do
@analyzer.send(:sources)
end.message.should.match /Unable to add/
end
it 'fetches a specs repo that is specified by the podfile' do
podfile = Podfile.new do
source 'https://github.com/artsy/Specs.git'
end
@analyzer.instance_variable_set(:@podfile, podfile)
SourcesManager.expects(:find_or_create_source_with_url).once
@analyzer.send(:sources)
end
end
end end
end end
end
#--------------------------------------# describe 'Analysis, concerning naming' do
before do
SpecHelper.create_sample_app_copy_from_fixture('SampleProject')
end
describe '#compute_platform_for_target_definition' do it 'raises when dependencies with the same name have different ' \
'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'SEGModules', :git => 'https://github.com/segiddins/SEGModules.git'
pod 'SEGModules', :git => 'https://github.com/segiddins/Modules.git'
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
it 'returns the platform specified in the target definition' do e.message.should.match /different sources for `SEGModules`/
target_definition = Podfile::TargetDefinition.new(:default, nil) e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/SEGModules.git`\)}
target_definition.set_platform(:ios, '4.0') e.message.should.match %r{SEGModules \(from `https://github.com/segiddins/Modules.git`\)}
user_targets = [] end
configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets) it 'raises when dependencies with the same root name have different ' \
configurations.should == Platform.new(:ios, '4.0') 'external sources' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'RestKit/Core', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', :git => 'https://github.com/segiddins/RestKit.git'
end end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
it 'infers the platform from the user targets' do e.message.should.match /different sources for `RestKit`/
user_project = Xcodeproj::Project.new('path') e.message.should.match %r{RestKit/Core \(from `https://github.com/RestKit/RestKit.git`\)}
target = user_project.new_target(:application, 'Target', :ios) e.message.should.match %r{RestKit \(from `https://github.com/segiddins/RestKit.git`\)}
target.build_configuration_list.set_setting('SDKROOT', 'iphoneos') end
target.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
it 'raises when dependencies with the same name have different ' \
'external sources with one being nil' do
podfile = Podfile.new do
source 'https://github.com/CocoaPods/Specs.git'
xcodeproj 'SampleProject/SampleProject'
platform :ios
pod 'RestKit', :git => 'https://github.com/RestKit/RestKit.git'
pod 'RestKit', '~> 0.23.0'
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile, nil)
e = should.raise(Informative) { analyzer.analyze }
target_definition = Podfile::TargetDefinition.new(:default, nil) e.message.should.match /different sources for `RestKit`/
user_targets = [target] e.message.should.match %r{RestKit \(from `https://github.com/RestKit/RestKit.git`\)}
e.message.should.match %r{RestKit \(~> 0.23.0\)}
end
end
configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets) describe 'using lockfile checkout options' do
configurations.should == Platform.new(:ios, '4.0') before do
@podfile = Pod::Podfile.new do
pod 'BananaLib', :git => 'example.com'
end end
@dependency = @podfile.dependencies.first
@lockfile_checkout_options = { :git => 'example.com', :commit => 'commit' }
hash = {}
hash['PODS'] = ['BananaLib (1.0.0)']
hash['CHECKOUT OPTIONS'] = { 'BananaLib' => @lockfile_checkout_options }
hash['SPEC CHECKSUMS'] = {}
hash['COCOAPODS'] = Pod::VERSION
@lockfile = Pod::Lockfile.new(hash)
@analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile, @lockfile)
end
it 'uses the lowest deployment target of the user targets if inferring the platform' do it 'returns that an update is required when there is no sandbox manifest' do
user_project = Xcodeproj::Project.new('path') @analyzer.sandbox.stubs(:manifest).returns(nil)
target1 = user_project.new_target(:application, 'Target', :ios) @analyzer.should.send(:checkout_requires_update?, @dependency)
configuration1 = target1.build_configuration_list.build_configurations.first end
target1.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target1.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
target2 = user_project.new_target(:application, 'Target', :ios) before do
target2.build_configuration_list.set_setting('SDKROOT', 'iphoneos') @sandbox_manifest = Pod::Lockfile.new(@lockfile.internal_data.deep_dup)
target2.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '6.0') @analyzer.sandbox.manifest = @sandbox_manifest
@analyzer.sandbox.stubs(:specification).with('BananaLib').returns(stub)
pod_dir = stub
pod_dir.stubs(:directory?).returns(true)
@analyzer.sandbox.stubs(:pod_dir).with('BananaLib').returns(pod_dir)
end
target_definition = Podfile::TargetDefinition.new(:default, nil) it 'returns whether or not an update is required' do
user_targets = [target1, target2] @analyzer.send(:checkout_requires_update?, @dependency).should == false
@sandbox_manifest.send(:checkout_options_data).delete('BananaLib')
@analyzer.send(:checkout_requires_update?, @dependency).should == true
end
configurations = @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets) before do
configurations.should == Platform.new(:ios, '4.0') @analyzer.result = Installer::Analyzer::AnalysisResult.new
end @analyzer.result.podfile_state = Installer::Analyzer::SpecsState.new
end
it 'raises if the user targets have a different platform' do it 'uses lockfile checkout options when no source exists in the sandbox' do
user_project = Xcodeproj::Project.new('path') @analyzer.result.podfile_state.unchanged << 'BananaLib'
target1 = user_project.new_target(:application, 'Target', :ios) @sandbox_manifest.send(:checkout_options_data).delete('BananaLib')
target1.build_configuration_list.set_setting('SDKROOT', 'iphoneos')
target1.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '4.0')
target2 = user_project.new_target(:application, 'Target', :ios) downloader = stub('DownloaderSource')
target2.build_configuration_list.set_setting('SDKROOT', 'macosx') ExternalSources.stubs(:from_params).with(@lockfile_checkout_options, @dependency, @podfile.defined_in_file).returns(downloader)
target2.build_configuration_list.set_setting('IPHONEOS_DEPLOYMENT_TARGET', '10.6')
target_definition = Podfile::TargetDefinition.new(:default, nil) downloader.expects(:fetch)
user_targets = [target1, target2] @analyzer.send(:fetch_external_sources)
e = lambda { @analyzer.send(:compute_platform_for_target_definition, target_definition, user_targets) }.should.raise Informative
e.message.should.match /Targets with different platforms/
end
end end
#--------------------------------------# it 'uses lockfile checkout options when a different checkout exists in the sandbox' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@sandbox_manifest.send(:checkout_options_data)['BananaLib'] = @lockfile_checkout_options.merge(:commit => 'other commit')
describe '#sources' do downloader = stub('DownloaderSource')
describe 'when there are no explicit sources' do ExternalSources.stubs(:from_params).with(@lockfile_checkout_options, @dependency, @podfile.defined_in_file).returns(downloader)
it 'defaults to all sources' do
@analyzer.send(:sources).map(&:url).should ==
SourcesManager.all.map(&:url)
end
end
describe 'when there are explicit sources' do downloader.expects(:fetch)
it 'raises if no specs repo with that URL could be added' do @analyzer.send(:fetch_external_sources)
podfile = Podfile.new do end
source 'not-a-git-repo'
end
@analyzer.instance_variable_set(:@podfile, podfile)
should.raise Informative do
@analyzer.send(:sources)
end.message.should.match /Unable to add/
end
it 'fetches a specs repo that is specified by the podfile' do it 'ignores lockfile checkout options when the podfile state has changed' do
podfile = Podfile.new do @analyzer.result.podfile_state.changed << 'BananaLib'
source 'https://github.com/artsy/Specs.git'
end downloader = stub('DownloaderSource')
@analyzer.instance_variable_set(:@podfile, podfile) ExternalSources.stubs(:from_params).with(@dependency.external_source, @dependency, @podfile.defined_in_file).returns(downloader)
SourcesManager.expects(:find_or_create_source_with_url).once
@analyzer.send(:sources) downloader.expects(:fetch)
end @analyzer.send(:fetch_external_sources)
end end
it 'does not re-fetch the external source when the sandbox has the correct revision of the source' do
@analyzer.result.podfile_state.unchanged << 'BananaLib'
@analyzer.expects(:fetch_external_source).never
@analyzer.send(:fetch_external_sources)
end end
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