From 0c160f612929cecf47d83cf33d450fb3be7f86d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Thu, 15 Jun 2023 15:42:48 +0200 Subject: [PATCH] SPM: allow specs to declare Swift Package Manager depenencies with spm_dependency --- lib/cocoapods-core/specification.rb | 1 + lib/cocoapods-core/specification/consumer.rb | 9 ++++ lib/cocoapods-core/specification/dsl.rb | 28 ++++++++++ lib/cocoapods-core/specification/linter.rb | 22 ++++++++ .../specification/spm_requirement.rb | 51 +++++++++++++++++++ spec/specification/consumer_spec.rb | 24 +++++++++ spec/specification/dsl_spec.rb | 11 ++++ spec/specification/json_spec.rb | 12 +++++ spec/specification/linter_spec.rb | 7 +++ 9 files changed, 165 insertions(+) create mode 100644 lib/cocoapods-core/specification/spm_requirement.rb diff --git a/lib/cocoapods-core/specification.rb b/lib/cocoapods-core/specification.rb index fa9c2674..60d941e8 100644 --- a/lib/cocoapods-core/specification.rb +++ b/lib/cocoapods-core/specification.rb @@ -6,6 +6,7 @@ require 'cocoapods-core/specification/root_attribute_accessors' require 'cocoapods-core/specification/set' require 'cocoapods-core/specification/json' +require 'cocoapods-core/specification/spm_requirement' module Pod # The Specification provides a DSL to describe a Pod. A pod is defined as a diff --git a/lib/cocoapods-core/specification/consumer.rb b/lib/cocoapods-core/specification/consumer.rb index e469504b..e4d015d0 100644 --- a/lib/cocoapods-core/specification/consumer.rb +++ b/lib/cocoapods-core/specification/consumer.rb @@ -1,4 +1,5 @@ require 'cocoapods-core/specification/root_attribute_accessors' +require 'cocoapods-core/specification/spm_requirement' module Pod class Specification @@ -234,6 +235,14 @@ def dependencies end end + #-----------------------------------------------------------------------# + def spm_dependencies + value = value_for_attribute(:spm_dependencies) + value.map do |spm_dependency| + {:url => spm_dependency[:url], :requirement => Pod::Specification::SpmRequirement.new(spm_dependency[:requirement]), :products => spm_dependency[:products]} + end + end + # Raw values need to be prepared as soon as they are read so they can be # safely merged to support multi platform attributes and inheritance #-----------------------------------------------------------------------# diff --git a/lib/cocoapods-core/specification/dsl.rb b/lib/cocoapods-core/specification/dsl.rb index e3364b9e..b80753ea 100644 --- a/lib/cocoapods-core/specification/dsl.rb +++ b/lib/cocoapods-core/specification/dsl.rb @@ -755,6 +755,34 @@ def dependency=(args) #------------------# + attribute :spm_dependencies, + :container => Array, + :inherited => true + + # Dependency on Swift Manager Packages. + # + # --- + # + # Dependencies should specify versions requirements. + # + # @example + # spec.spm_dependency( + # :url => 'https://github.com/apple/swift-atomics.git', + # :requirement' => {:kind => 'upToNextMajor', :minimumVersion => '1.1.0'}, + # :products' => ['Atomics'] + # ) + + def spm_dependency(url: , requirement:, products:) + attributes_hash['spm_dependencies'] ||= [] + attributes_hash['spm_dependencies'] << { + :url => url, + :requirement => requirement, + :products => products, + } + end + + #------------------# + # @!method info_plist=(info_plist) # # Key-Value pairs to add to the generated `Info.plist`. diff --git a/lib/cocoapods-core/specification/linter.rb b/lib/cocoapods-core/specification/linter.rb index 91e7bf1c..f32d410c 100644 --- a/lib/cocoapods-core/specification/linter.rb +++ b/lib/cocoapods-core/specification/linter.rb @@ -471,6 +471,28 @@ def _validate_scheme(s) end end + + # Performs validations related to the `spm_dependencies` attribute. + # + def _validate_spm_dependencies(s) + unless s.empty? + s.each do |spm_dependency| + if spm_dependency[:url].nil? + results.add_error('spm_dependencies', 'SPM dependencies should specify a url.') + end + if spm_dependency[:requirement].nil? + results.add_error('spm_dependencies', 'SPM dependencies should specify a requirement.') + end + if requirement_errors = SpmRequirement.validate(spm_dependency[:requirement]) + results.add_error('spm_dependencies', requirement_errors) + end + if spm_dependency[:products].nil? + results.add_error('spm_dependencies', 'SPM dependencies should specify products.') + end + end + end + end + # Performs validations related to github sources. # def perform_github_source_checks(s) diff --git a/lib/cocoapods-core/specification/spm_requirement.rb b/lib/cocoapods-core/specification/spm_requirement.rb new file mode 100644 index 000000000..b8326cd1 --- /dev/null +++ b/lib/cocoapods-core/specification/spm_requirement.rb @@ -0,0 +1,51 @@ +module Pod + class Specification + # This class validates and represents swift package manager dependecy requirements + class SpmRequirement + REQUIRED_KEYS = { + :upToNextMajorVersion => [:minimumVersion], + :upToNextMinorVersion => [:minimumVersion], + :exactVersion => [:version], + :range => %i[minimumVersion maximumVersion], + }.freeze + + def initialize(requirement_hash) + @requirement = requirement_hash + validate + end + + def kind + @requirement[:kind] + end + + def ==(other) + @requirement == other.instance_variable_get(:@requirement) + end + + def validate + raise 'Kind should be string' unless kind.is_a?(String) + raise "Unknown requirement kind #{kind}" if REQUIRED_KEYS[kind.to_sym].nil? + + REQUIRED_KEYS[kind.to_sym].each do |key| + raise "Missing key #{key} for requirement #{@requirement}" unless @requirement[key] + end + end + + def to_h + @requirement.map { |k,v| [k.to_s, v.to_s] }.to_h + end + + def self.validate(requirement_hash) + if (requirement_hash.is_a? SpmRequirement) + requirement_hash.validate + nil + else + new(requirement_hash) + nil + end + rescue => e + e.message + end + end + end +end \ No newline at end of file diff --git a/spec/specification/consumer_spec.rb b/spec/specification/consumer_spec.rb index 71b93077..d88e1dc9 100644 --- a/spec/specification/consumer_spec.rb +++ b/spec/specification/consumer_spec.rb @@ -602,6 +602,30 @@ module Pod #-------------------------------------------------------------------------# + describe 'SPM Dependencies' do + before do + @spec = Spec.new do |s| + s.name = 'Pod' + s.spm_dependency( + :url => "https://github.com/apple/example-package-fisheryates.git", + :requirement => { :kind => "upToNextMajorVersion", :minimumVersion => "1.0.0" }, + :products => ["FisherYates"], + ) + end + @consumer = Specification::Consumer.new(@spec, :ios) + end + + it 'returns the spm_dependencies with SpmRequirement' do + @consumer.spm_dependencies.should == [{ + :url => "https://github.com/apple/example-package-fisheryates.git", + :requirement => Specification::SpmRequirement.new({ :kind => "upToNextMajorVersion", :minimumVersion => "1.0.0" }), + :products => ["FisherYates"], + }] + end + end + + #-------------------------------------------------------------------------# + describe 'Private helpers' do before do @spec = Spec.new do |s| diff --git a/spec/specification/dsl_spec.rb b/spec/specification/dsl_spec.rb index d0d794c1..80a902a6 100644 --- a/spec/specification/dsl_spec.rb +++ b/spec/specification/dsl_spec.rb @@ -302,6 +302,17 @@ module Pod #------------------# + describe 'spm_dependency' do + it 'allows to specify a single spm_dependency' do + @spec.spm_dependency(:url => "foo", :requirement => {:kind => "upToNextMajorVersion", :minimumVersion => "1.0.0"}, :products => ["Foo"]) + @spec.attributes_hash['spm_dependencies'].should == [ + { :url => 'foo', :requirement => {:kind => "upToNextMajorVersion", :minimumVersion => "1.0.0"}, :products => ["Foo"] }, + ] + end + end + + #------------------# + it 'allows to specify whether the specification requires ARC' do @spec.requires_arc = false @spec.attributes_hash['requires_arc'].should == false diff --git a/spec/specification/json_spec.rb b/spec/specification/json_spec.rb index 4589aeff..821b43c1 100644 --- a/spec/specification/json_spec.rb +++ b/spec/specification/json_spec.rb @@ -570,6 +570,18 @@ module Pod result.swift_versions.map(&:to_s).should == %w(3.2 4.0 4.1) result.swift_version.to_s.should == '4.1' end + + describe 'Swift Package Manger dependencies' do + it 'writes spm dependency' do + @spec.spm_dependency( + :url => 'http://github.com/foo/foo', + :requirement => {:kind => 'upToNextMajor', :minimumVersion => '1.0.0'}, + :products => ['Foo'], + ) + hash = @spec.to_hash + hash['spm_dependencies'].should == [{:url => 'http://github.com/foo/foo', :requirement => {:kind => 'upToNextMajor', :minimumVersion => '1.0.0'}, :products => ['Foo']}] + end + end end end end diff --git a/spec/specification/linter_spec.rb b/spec/specification/linter_spec.rb index fd920174..20152376 100644 --- a/spec/specification/linter_spec.rb +++ b/spec/specification/linter_spec.rb @@ -693,6 +693,13 @@ def result_should_include(*values) @spec.compiler_flags = '-some_flag', '-another -Wno_flags' result_should_include('warnings', 'disabled', 'compiler_flags') end + + #------------------# + + it 'check if spm dependency requirement upToNextMinorVersion has a minimumVersion key' do + @spec.spm_dependency(:url => 'https://github.com/foo/foo', :requirement => { :kind => 'upToNextMinorVersion' }, :products => ['Foo']) + result_should_include('missing key minimumVersion') + end end #--------------------------------------#