diff --git a/.gitignore b/.gitignore index 569684e9a..f6515aee5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ spec/fixtures/vcr coverage/ .coveralls.yml .idea/ +documentation/ diff --git a/lib/cocoapods-core/lockfile.rb b/lib/cocoapods-core/lockfile.rb index 1aa843d96..d75ff7342 100644 --- a/lib/cocoapods-core/lockfile.rb +++ b/lib/cocoapods-core/lockfile.rb @@ -135,18 +135,21 @@ def dependencies # # @return [Dependency] the generated dependency. # - def dependency_to_lock_pod_named(name) - dep = dependencies.find { |d| d.name == name || d.root_name == name } - version = version(name) - - unless dep && version + def dependencies_to_lock_pod_named(name) + result = [] + deps = dependencies.select { |d| d.root_name == name } + if deps.empty? raise StandardError, "Attempt to lock the `#{name}` Pod without an " \ 'known dependency.' end - locked_dependency = dep.dup - locked_dependency.specific_version = version - locked_dependency + deps.each do |dep| + version = version(dep.name) + locked_dependency = dep.dup + locked_dependency.specific_version = version + result << locked_dependency + end + result end # @return [Version] The version of CocoaPods which generated this lockfile. @@ -233,8 +236,8 @@ def detect_changes_with_podfile(podfile) [:added, :changed, :removed, :unchanged].each { |k| result[k] = [] } installed_deps = dependencies.map do |dep| - dependency_to_lock_pod_named(dep.name) - end + dependencies_to_lock_pod_named(dep.name) + end.flatten all_dep_names = (dependencies + podfile.dependencies).map(&:name).uniq all_dep_names.each do |name| installed_dep = installed_deps.find { |d| d.name == name } diff --git a/lib/cocoapods-core/podfile.rb b/lib/cocoapods-core/podfile.rb index 7e6f4989d..086ec722d 100644 --- a/lib/cocoapods-core/podfile.rb +++ b/lib/cocoapods-core/podfile.rb @@ -95,6 +95,12 @@ def dependencies # @!group Attributes + # @return [Array] The name of the sources. + # + def sources + get_hash_value('sources') || [] + end + # @return [String] the path of the workspace if specified by the user. # def workspace_path @@ -173,6 +179,7 @@ def post_install!(installer) HASH_KEYS = %w( target_definitions workspace + sources generate_bridge_support set_arc_compatibility_flag ).freeze diff --git a/lib/cocoapods-core/podfile/dsl.rb b/lib/cocoapods-core/podfile/dsl.rb index 8758a8adf..090f0c3e1 100644 --- a/lib/cocoapods-core/podfile/dsl.rb +++ b/lib/cocoapods-core/podfile/dsl.rb @@ -468,6 +468,44 @@ def set_arc_compatibility_flag! #-----------------------------------------------------------------------# + # @!group Sources + # + # The Podfile retrieves specs from a given list of sources (repos). + # + # Sources are __global__ and they are not stored per target definition. + + #-----------------------------------------------------------------------# + + # Specifies the location of specs + # + # ----- + # + # By default, the github Cocoapods/specs repository is used. Use this + # method to specify (an) other(s) source(s). The order of the sources is + # relevant. CocoaPods will use the highest version of a Pod of the first + # source which includes the Pod (regardless whether other sources have a + # higher version). + # + # @param [String] source + # The name of a specs repo. Previously specified by user + # via pod repo add command + # + # @example Specifying to use first `my_private_repo` and then the + # CocoaPods Master Repo + # + # source 'my_private_repo' + # source 'master' + # + # @return [void] + # + def source(source) + hash_sources = get_hash_value('sources') || [] + hash_sources << source + set_hash_value('sources', hash_sources.uniq) + end + + #-----------------------------------------------------------------------# + # @!group Hooks # The Podfile provides hooks that will be called during the # installation process. diff --git a/lib/cocoapods-core/source/aggregate.rb b/lib/cocoapods-core/source/aggregate.rb index b0e78d442..e5ca8d1ed 100644 --- a/lib/cocoapods-core/source/aggregate.rb +++ b/lib/cocoapods-core/source/aggregate.rb @@ -3,38 +3,38 @@ class Source # The Aggregate manages a directory of sources repositories. # class Aggregate - # @return [Pathname] the directory were the repositories are stored. + # @return [Array] The ordered list of source directories. # - attr_reader :repos_dir + attr_reader :directories - # @param [Pathname] repos_dir @see repos_dir. + # @param [Array] repos_dirs @see directories # - def initialize(repos_dir) - @repos_dir = repos_dir + def initialize(repos_dirs) + @directories = Array(repos_dirs) end - # @return [Array] all the sources. + # @return [Array] The ordered list of the sources. # - def all - @sources ||= dirs.map { |repo| Source.new(repo) }.sort_by(&:name) + def sources + @sources ||= directories.map { |repo| Source.new(repo) } end # @return [Array] the names of all the pods available. # def all_pods - all.map(&:pods).flatten.uniq + sources.map(&:pods).flatten.uniq end - # @return [Array] the sets for all the pods available. + # @return [Array] The sets for all the pods available. # # @note Implementation detail: The sources don't cache their values # because they might change in response to an update. Therefore - # this method to prevent slowness caches the values before + # this method to preserve performance caches the values before # processing them. # def all_sets pods_by_source = {} - all.each do |source| + sources.each do |source| pods_by_source[source] = source.pods end sources = pods_by_source.keys @@ -47,20 +47,6 @@ def all_sets end end - # @return [Array] the directories where the sources are stored. - # - # @note If the repos dir doesn't exits this will return an empty array. - # - # @raise If the repos dir doesn't exits. - # - def dirs - if repos_dir.exist? - repos_dir.children.select(&:directory?) - else - [] - end - end - # Returns a set configured with the source which contains the highest # version in the aggregate. # @@ -73,7 +59,7 @@ def dirs def representative_set(name) representative_source = nil highest_version = nil - all.each do |source| + sources.each do |source| source_versions = source.versions(name) if source_versions source_version = source_versions.first @@ -100,9 +86,9 @@ def representative_set(name) # @see Source#search # def search(dependency) - sources = all.select { |s| s.search(dependency) } - unless sources.empty? - Specification::Set.new(dependency.root_name, sources) + found_sources = sources.select { |s| s.search(dependency) } + unless found_sources.empty? + Specification::Set.new(dependency.root_name, found_sources) end end @@ -117,7 +103,7 @@ def search(dependency) def search_by_name(query, full_text_search = false) pods_by_source = {} result = [] - all.each do |s| + sources.each do |s| source_pods = s.search_by_name(query, full_text_search) pods_by_source[s] = source_pods.map(&:name) end diff --git a/spec/fixtures/Podfile b/spec/fixtures/Podfile index bbd843b59..265ed45b4 100644 --- a/spec/fixtures/Podfile +++ b/spec/fixtures/Podfile @@ -1,3 +1,5 @@ +source 'myrepo1' +source 'myrepo2' platform :ios pod 'SSZipArchive', '>= 1' pod 'ASIHTTPRequest', '~> 1.8.0' diff --git a/spec/fixtures/Podfile.yaml b/spec/fixtures/Podfile.yaml index bbadedba2..38f8ec492 100644 --- a/spec/fixtures/Podfile.yaml +++ b/spec/fixtures/Podfile.yaml @@ -1,8 +1,12 @@ --- +sources: + - 'myrepo1' + - 'myrepo2' target_definitions: - name: Pods link_with_first_target: true platform: ios + dependencies: - SSZipArchive: - '>= 1' diff --git a/spec/lockfile_spec.rb b/spec/lockfile_spec.rb index 863556067..bb5e52694 100644 --- a/spec/lockfile_spec.rb +++ b/spec/lockfile_spec.rb @@ -143,12 +143,13 @@ def self.specs it 'returns the dependency that locks the pod with the given name to the installed version' do json_dep = Dependency.new('JSONKit', '1.4') json_dep.external_source = { :podspec => 'path/JSONKit.podspec' } - @lockfile.dependency_to_lock_pod_named('JSONKit').should == json_dep + result = @lockfile.dependencies_to_lock_pod_named('JSONKit') + result.should == [json_dep] end it 'raises if there is a request for a locking dependency for a not stored Pod' do should.raise StandardError do - @lockfile.dependency_to_lock_pod_named('Missing') + @lockfile.dependencies_to_lock_pod_named('Missing') end.message.should.match /without an known dependency/ end diff --git a/spec/podfile_spec.rb b/spec/podfile_spec.rb index 982fba4f3..5ce977d7f 100644 --- a/spec/podfile_spec.rb +++ b/spec/podfile_spec.rb @@ -117,6 +117,21 @@ module Pod Podfile.new { set_arc_compatibility_flag! }.should.set_arc_compatibility_flag end + describe 'source' do + it 'can have multiple sources' do + Podfile.new do + source 'new_repo_1' + source 'new_repo_2' + end.sources.size.should == 2 + + Podfile.new do + source 'new_repo_1' + source 'new_repo_2' + source 'master' + end.sources.size.should == 3 + end + + end end #-------------------------------------------------------------------------# @@ -234,6 +249,22 @@ module Pod } end + it 'includes the specified sources in the hash representation' do + podfile = Podfile.new do + source 'new_ASIHTTPRequest_source' + pod 'ASIHTTPRequest' + end + podfile.to_hash.should == { + 'sources' => %w(new_ASIHTTPRequest_source), + 'target_definitions' => [ + { + 'name' => 'Pods', + 'link_with_first_target' => true, + 'dependencies' => %w(ASIHTTPRequest), + }, + ], + } + end end #-------------------------------------------------------------------------# diff --git a/spec/source/aggregate_spec.rb b/spec/source/aggregate_spec.rb index 7ff6f6968..619ef953d 100644 --- a/spec/source/aggregate_spec.rb +++ b/spec/source/aggregate_spec.rb @@ -7,15 +7,16 @@ module Pod # JSONKit is in test repo has version 1.4 (duplicated) and the 999.999.999. # before do - @subject = Source::Aggregate.new(fixture('spec-repos')) + repos = [fixture('spec-repos/test_repo'), fixture('spec-repos/master')] + @subject = Source::Aggregate.new(repos) end #-------------------------------------------------------------------------# describe 'In general' do - it 'returns all the sources' do - @subject.all.map(&:name).should == %w(master test_repo) + it 'returns the sources' do + @subject.sources.map(&:name).sort.should == %w(master test_repo) end it 'returns the name of all the available pods' do @@ -55,15 +56,6 @@ module Pod set.should.nil? end - it 'returns the directories where the repos are defined' do - @subject.dirs.map { |d| d.basename.to_s }.sort.should == %w(master test_repo) - end - - it "returns an empty list for the directories if the repos dir doesn't exists" do - aggregate = Source::Aggregate.new(Pathname.new('missing-dir')) - aggregate.dirs.should == [] - end - it 'returns a set configured to use only the source which contains the highest version' do set = @subject.representative_set('JSONKit') set.versions.map(&:to_s).should == ['999.999.999', '1.13', '1.4'] @@ -92,14 +84,14 @@ module Pod end it 'performs a full text search' do - @subject.stubs(:dirs).returns([fixture('spec-repos/test_repo')]) + @subject.stubs(:directories).returns([fixture('spec-repos/test_repo')]) sets = @subject.search_by_name('Banana Corp', true) sets.count.should == 1 sets.first.name.should == 'BananaLib' end it 'raises an informative if unable to find a Pod with the given name' do - @subject.stubs(:dirs).returns([fixture('spec-repos/test_repo')]) + @subject.stubs(:directories).returns([fixture('spec-repos/test_repo')]) should.raise Informative do @subject.search_by_name('Some-funky-name', true) end.message.should.match /Unable to find/ @@ -113,7 +105,7 @@ module Pod before do test_source = Source.new(fixture('spec-repos/test_repo')) - @subject.stubs(:all).returns([test_source]) + @subject.stubs(:sources).returns([test_source]) end it 'generates the search index from scratch' do diff --git a/spec/specification/set/presenter_spec.rb b/spec/specification/set/presenter_spec.rb index a4de82d98..7e0962699 100644 --- a/spec/specification/set/presenter_spec.rb +++ b/spec/specification/set/presenter_spec.rb @@ -5,7 +5,7 @@ module Pod describe 'Set Information' do before do - set = Source::Aggregate.new(fixture('spec-repos')).search_by_name('JSONKit').first + set = Source::Aggregate.new([fixture('spec-repos/master'), fixture('spec-repos/test_repo')]).search_by_name('JSONKit').first @presenter = Spec::Set::Presenter.new(set) end diff --git a/spec/specification/set_spec.rb b/spec/specification/set_spec.rb index 4aecebd30..31dc14394 100644 --- a/spec/specification/set_spec.rb +++ b/spec/specification/set_spec.rb @@ -109,7 +109,8 @@ module Pod before do # JSONKit is in test repo has version 1.4 (duplicated) and the 999.999.999. - @set = Source::Aggregate.new(fixture('spec-repos')).search_by_name('JSONKit').first + repos_dirs = [fixture('spec-repos/master'), fixture('spec-repos/test_repo')] + @set = Source::Aggregate.new(repos_dirs).search_by_name('JSONKit').first end it 'returns the sources where a podspec is available' do