diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 1578174fba499..04591ff87fc8d 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -151,7 +151,7 @@ def self.load_formula_from_api(name, flags:) klass = Class.new(::Formula) do desc json_formula["desc"] homepage json_formula["homepage"] - license json_formula["license"] + license SPDX.string_to_license_expression(json_formula["license"]) revision json_formula["revision"] version_scheme json_formula["version_scheme"] diff --git a/Library/Homebrew/test/utils/spdx_spec.rb b/Library/Homebrew/test/utils/spdx_spec.rb index d02de416eff35..8d84bb03e1640 100644 --- a/Library/Homebrew/test/utils/spdx_spec.rb +++ b/Library/Homebrew/test/utils/spdx_spec.rb @@ -217,6 +217,31 @@ end end + describe ".string_to_license_expression" do + it "returns the correct result for 'and', 'or' and 'with'" do + expr_string = "Apache-2.0 and (Apache-2.0 with LLVM-exception) and (MIT or NCSA)" + expect(described_class.string_to_license_expression(expr_string)).to eq({ + all_of: [ + "Apache-2.0", + { "Apache-2.0" => { with: "LLVM-exception" } }, + { any_of: ["MIT", "NCSA"] }, + ], + }) + end + + it "handles nested brackets" do + expect(described_class.string_to_license_expression("A and (B or (C and D))")).to eq({ + all_of: [ + "A", + any_of: [ + "B", + all_of: ["C", "D"], + ], + ], + }) + end + end + describe ".license_version_info" do it "returns license without version" do expect(described_class.license_version_info("MIT")).to eq ["MIT"] diff --git a/Library/Homebrew/utils/spdx.rb b/Library/Homebrew/utils/spdx.rb index 38e0183e92dac..873cc8310e86d 100644 --- a/Library/Homebrew/utils/spdx.rb +++ b/Library/Homebrew/utils/spdx.rb @@ -129,6 +129,40 @@ def license_expression_to_string(license_expression, bracket: false, hash_type: end end + def string_to_license_expression(string) + return if string.blank? + + result = string + result_type = nil + + and_parts = string.split(/ and (?![^(]*\))/) + if and_parts.length > 1 + result = and_parts + result_type = :all_of + else + or_parts = string.split(/ or (?![^(]*\))/) + if or_parts.length > 1 + result = or_parts + result_type = :any_of + end + end + + if result_type + result.map! do |part| + part = part[1..-2] if part[0] == "(" && part[-1] == ")" + string_to_license_expression(part) + end + { result_type => result } + else + with_parts = string.split(" with ", 2) + if with_parts.length > 1 + { with_parts.first => { with: with_parts.second } } + else + result + end + end + end + def license_version_info(license) return [license] if ALLOWED_LICENSE_SYMBOLS.include? license