diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 759109546e..b4fc8b8496 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -12,10 +12,56 @@ #: recent version that can be found will be used. require "utils/git" -require "formula_versions" require "formulary" require "tap" +class BottleSpecification + def method_missing(m, *_args, &_block) + if [:sha1, :md5].include?(m) + opoo "Formula has unknown or deprecated stanza: #{m}" if ARGV.debug? + else + super + end + end +end + +class Module + def method_missing(m, *_args, &_block) + if [:sha1, :md5].include?(m) + opoo "Formula has unknown or deprecated stanza: #{m}" if ARGV.debug? + else + super + end + end +end + +class DependencyCollector + def parse_symbol_spec(spec, tags) + case spec + when :x11 then X11Requirement.new(spec.to_s, tags) + when :xcode then XcodeRequirement.new(tags) + when :linux then LinuxRequirement.new(tags) + when :macos then MacOSRequirement.new(tags) + when :arch then ArchRequirement.new(tags) + when :java then JavaRequirement.new(tags) + when :osxfuse then OsxfuseRequirement.new(tags) + when :tuntap then TuntapRequirement.new(tags) + when :ld64 then ld64_dep_if_needed(tags) + else + opoo "Unsupported special dependency #{spec.inspect}" if ARGV.debug? + end + end + + module Compat + def parse_string_spec(spec, tags) + opoo "'depends_on ... => :run' is disabled. There is no replacement." if tags.include?(:run) && ARGV.debug? + super + end + end + + prepend Compat +end + module Homebrew module_function @@ -35,56 +81,73 @@ module Homebrew odie "The tap to which the formula is extracted must be specified!" if args.tap.nil? - formula = Formulary.factory(ARGV.named.first) - if args.version.nil? - version = formula.version - else - version = args.version + begin + formula = Formulary.factory(ARGV.named.first) + name = formula.name + repo = formula.path.parent.parent + file = formula.path + rescue FormulaUnavailableError => e + opoo "'#{ARGV.named.first}' does not currently exist in the core tap" if ARGV.debug? + core = Tap.fetch("homebrew/core") + name = ARGV.named.first.downcase + repo = core.path + file = core.path.join("Formula", "#{name}.rb") end + destination_tap = Tap.fetch(args.tap) destination_tap.install unless destination_tap.installed? odie "Cannot extract formula to homebrew/core!" if destination_tap.name == "homebrew/core" - path = Pathname.new("#{destination_tap.path}/Formula/#{formula}@#{version}.rb") + if args.version.nil? + rev = Git.last_revision_commit_of_file(repo, file) + version = formula_at_revision(repo, name, file, rev).version + odie "Could not find #{name}! The formula or version may not have existed." if rev.empty? + result = Git.last_revision_of_file(repo, file) + else + version = args.version + rev = "HEAD" + test_formula = nil + loop do + loop do + rev = Git.last_revision_commit_of_file(repo, file, before_commit: "#{rev}~1") + break if rev.empty? + break unless Git.last_revision_of_file(repo, file, before_commit: rev).empty? + ohai "Skipping revision #{rev} - file is empty at this revision" if ARGV.debug? + end + test_formula = formula_at_revision(repo, name, file, rev) + break if test_formula.nil? || test_formula.version == version + ohai "Trying #{test_formula.version} from revision #{rev} against desired #{version}" if ARGV.debug? + end + odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil? + result = Git.last_revision_of_file(repo, file, before_commit: rev) + end + + # The class name has to be renamed to match the new filename, e.g. Foo version 1.2.3 becomes FooAT123 and resides in Foo@1.2.3.rb. + class_name = name.capitalize + versioned_name = Formulary.class_s("#{class_name}@#{version}") + result.gsub!("class #{class_name} < Formula", "class #{versioned_name} < Formula") + + path = destination_tap.path.join("Formula", "#{name}@#{version}.rb") if path.exist? unless ARGV.force? odie <<~EOS Destination formula already exists: #{path} To overwrite it and continue anyways, run: - `brew extract #{formula} --version=#{version} --tap=#{destination_tap.name} --force` + `brew extract #{name} --version=#{version} --tap=#{destination_tap.name} --force` EOS end ohai "Overwriting existing formula at #{path}" if ARGV.debug? path.delete end - - if args.version.nil? - rev = Git.last_revision_commit_of_file(formula.path.parent.parent, formula.path) - odie "Could not find #{formula} #{version}!" if rev.empty? - version_resolver = FormulaVersions.new(formula) - else - rev = "HEAD" - version_resolver = FormulaVersions.new(formula) - until version_resolver.formula_at_revision(rev) { |f| version_matches?(f, version, rev) || rev.empty? } do - rev = Git.last_revision_commit_of_file(formula.path.parent.parent, formula.path, before_commit: "#{rev}~1") - end - odie "Could not find #{formula} #{version}!" if rev.empty? - end - - result = version_resolver.file_contents_at_revision(rev) - - # The class name has to be renamed to match the new filename, e.g. Foo version 1.2.3 becomes FooAT123 and resides in Foo@1.2.3.rb. - name = formula.name.capitalize - versioned_name = Formulary.class_s("#{name}@#{version}") - result.gsub!("class #{name} < Formula", "class #{versioned_name} < Formula") - ohai "Writing formula for #{formula} from #{rev} to #{path}" + ohai "Writing formula for #{name} from #{rev} to #{path}" path.write result end # @private - def version_matches?(formula, version, rev) - ohai "Trying #{formula.version} from revision #{rev} against desired #{version}" if ARGV.debug? - formula.version == version + def formula_at_revision(repo, name, file, rev) + return nil if rev.empty? + contents = Git.last_revision_of_file(repo, file, before_commit: rev) + Formulary.from_contents(name, file, contents) end end