From 2952a9bf743b16382dea6760fca6d1b56c691ad5 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sun, 29 Jul 2018 00:27:36 -0400 Subject: [PATCH 01/25] Add new extract command to retrieve old versions of formulae --- Library/Homebrew/cmd/extract.rb | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Library/Homebrew/cmd/extract.rb diff --git a/Library/Homebrew/cmd/extract.rb b/Library/Homebrew/cmd/extract.rb new file mode 100644 index 0000000000..440d77faba --- /dev/null +++ b/Library/Homebrew/cmd/extract.rb @@ -0,0 +1,53 @@ +#: * `extract` : +#: Looks through repository history to find the of and +#: creates a copy in /Formula/@.rb. If the tap is +#: not installed yet, attempts to install/clone the tap before continuing. +#: +#: If the file at /Formula/@.rb already exists, +#: it will not be overwritten unless `--force` is specified. +#: + +require "utils/git" +require "formula_versions" +require "formulary" +require "tap" + +module Homebrew + module_function + + def extract + formula = Formulary.factory(ARGV.named[0]) + version = ARGV.named[1] + dest = Tap.fetch(ARGV.named[2]) + dest.install unless dest.installed? + path = Pathname.new("#{dest.path}/Formula/#{formula}@#{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} #{dest.name} --force`. + EOS + end + ohai "Clobbering existing formula at #{path}" if ARGV.debug? + path.delete + end + + rev = "HEAD" + version_resolver = FormulaVersions.new(formula) + rev = Git.last_revision_commit_of_file(formula.path.parent.parent, formula.path, before_commit: "#{rev}~1") until version_resolver.formula_at_revision(rev) { |f| version_matches?(f, version, rev) || rev.empty? } + + odie "Could not find #{formula} #{version}." if rev.empty? + + result = version_resolver.file_contents_at_revision(rev) + ohai "Writing formula for #{formula} from #{rev} to #{path}" + + # 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. + path.write result.gsub("class #{formula.name.capitalize} < Formula", "class #{formula.name.capitalize}AT#{version.gsub(/[^0-9a-z ]/i, "")} < Formula") + end + + # @private + def version_matches?(formula, version, rev) + ohai "Trying #{formula.version} from revision #{rev} against desired #{version}" if ARGV.debug? + formula.version == version + end +end From 055460884bbcc4bd22a370b032b48d84825a3000 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sun, 29 Jul 2018 20:51:57 -0400 Subject: [PATCH 02/25] Fix up extract command - Move from cmd to dev-cmd - Add --stdout flag - Add sanity checks for args/flags - Minor rewording on error messages --- Library/Homebrew/cmd/extract.rb | 53 -------------------- Library/Homebrew/dev-cmd/extract.rb | 75 +++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 53 deletions(-) delete mode 100644 Library/Homebrew/cmd/extract.rb create mode 100644 Library/Homebrew/dev-cmd/extract.rb diff --git a/Library/Homebrew/cmd/extract.rb b/Library/Homebrew/cmd/extract.rb deleted file mode 100644 index 440d77faba..0000000000 --- a/Library/Homebrew/cmd/extract.rb +++ /dev/null @@ -1,53 +0,0 @@ -#: * `extract` : -#: Looks through repository history to find the of and -#: creates a copy in /Formula/@.rb. If the tap is -#: not installed yet, attempts to install/clone the tap before continuing. -#: -#: If the file at /Formula/@.rb already exists, -#: it will not be overwritten unless `--force` is specified. -#: - -require "utils/git" -require "formula_versions" -require "formulary" -require "tap" - -module Homebrew - module_function - - def extract - formula = Formulary.factory(ARGV.named[0]) - version = ARGV.named[1] - dest = Tap.fetch(ARGV.named[2]) - dest.install unless dest.installed? - path = Pathname.new("#{dest.path}/Formula/#{formula}@#{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} #{dest.name} --force`. - EOS - end - ohai "Clobbering existing formula at #{path}" if ARGV.debug? - path.delete - end - - rev = "HEAD" - version_resolver = FormulaVersions.new(formula) - rev = Git.last_revision_commit_of_file(formula.path.parent.parent, formula.path, before_commit: "#{rev}~1") until version_resolver.formula_at_revision(rev) { |f| version_matches?(f, version, rev) || rev.empty? } - - odie "Could not find #{formula} #{version}." if rev.empty? - - result = version_resolver.file_contents_at_revision(rev) - ohai "Writing formula for #{formula} from #{rev} to #{path}" - - # 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. - path.write result.gsub("class #{formula.name.capitalize} < Formula", "class #{formula.name.capitalize}AT#{version.gsub(/[^0-9a-z ]/i, "")} < Formula") - end - - # @private - def version_matches?(formula, version, rev) - ohai "Trying #{formula.version} from revision #{rev} against desired #{version}" if ARGV.debug? - formula.version == version - end -end diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb new file mode 100644 index 0000000000..f933f0a99d --- /dev/null +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -0,0 +1,75 @@ +#: * `extract` [`--force`] [`--stdout`] []: +#: Looks through repository history to find the of and +#: creates a copy in /Formula/@.rb. If the tap is +#: not installed yet, attempts to install/clone the tap before continuing. +#: +#: If `--force` is passed, the file at the destination will be overwritten +#: if it already exists. Otherwise, existing files will be preserved. +#: +#: If `--stdout` is passed, the file will be written to stdout on the +#: terminal instead of written to a file. A cannot be passed when +#: using `--stdout`. + +require "utils/git" +require "formula_versions" +require "formulary" +require "tap" + +module Homebrew + module_function + + def extract + Homebrew::CLI::Parser.parse do + switch "--stdout", description: "Output to stdout on terminal instead of file" + switch :debug + switch :force + end + + odie "Cannot use a tap and --stdout at the same time!" if ARGV.named.length == 3 && args.stdout? + raise UsageError unless (ARGV.named.length == 3 && !args.stdout?) || (ARGV.named.length == 2 && args.stdout?) + + formula = Formulary.factory(ARGV.named.first) + version = ARGV.named[1] + destination_tap = Tap.fetch(ARGV.named[2]) unless args.stdout? + destination_tap.install unless destination_tap.installed? unless args.stdout? + + unless args.stdout? + path = Pathname.new("#{destination_tap.path}/Formula/#{formula}@#{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} #{destination_tap.name} --force`. + EOS + end + ohai "Overwriting existing formula at #{path}" if ARGV.debug? + path.delete + end + end + + 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? + + 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. + result.gsub!("class #{formula.name.capitalize} < Formula", "class #{formula.name.capitalize}AT#{version.gsub(/[^0-9a-z ]/i, "")} < Formula") + if args.stdout? + puts result if args.stdout? + else + ohai "Writing formula for #{formula} from #{rev} to #{path}" + path.write result + end + end + + # @private + def version_matches?(formula, version, rev) + ohai "Trying #{formula.version} from revision #{rev} against desired #{version}" if ARGV.debug? + formula.version == version + end +end From 1dff2f141ca7398ade175d8a1da85df3fc561350 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sun, 29 Jul 2018 20:53:14 -0400 Subject: [PATCH 03/25] Add manpages/docs for extract command --- docs/Manpage.md | 12 ++++++++++++ manpages/brew.1 | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/Manpage.md b/docs/Manpage.md index d716efd57a..b4268f7096 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -790,6 +790,18 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note * `edit` `formula`: Open `formula` in the editor. + * `extract` [`--force`] [`--stdout`] `formula` `version` [`tap`]: + Looks through repository history to find the `version` of `formula` and + creates a copy in `tap`/Formula/`formula`@`version`.rb. If the tap is + not installed yet, attempts to install/clone the tap before continuing. + + If `--force` is passed, the file at the destination will be overwritten + if it already exists. Otherwise, existing files will be preserved. + + If `--stdout` is passed, the file will be written to stdout on the + terminal instead of written to a file. A `tap` cannot be passed when + using `--stdout`. + * `formula` `formula`: Display the path where `formula` is located. diff --git a/manpages/brew.1 b/manpages/brew.1 index fee4bd3757..3c75e869e1 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -727,6 +727,16 @@ Open all of Homebrew for editing\. Open \fIformula\fR in the editor\. . .TP +\fBextract\fR [\fB\-\-force\fR] [\fB\-\-stdout\fR] \fIformula\fR \fIversion\fR [\fItap\fR] +Looks through repository history to find the \fIversion\fR of \fIformula\fR and creates a copy in \fItap\fR/Formula/\fIformula\fR@\fIversion\fR\.rb\. If the tap is not installed yet, attempts to install/clone the tap before continuing\. +. +.IP +If \fB\-\-force\fR is passed, the file at the destination will be overwritten if it already exists\. Otherwise, existing files will be preserved\. +. +.IP +If \fB\-\-stdout\fR is passed, the file will be written to stdout on the terminal instead of written to a file\. A \fItap\fR cannot be passed when using \fB\-\-stdout\fR\. +. +.TP \fBformula\fR \fIformula\fR Display the path where \fIformula\fR is located\. . From bd2ac70c0fb9442f6fa8195d9b8cf899dba66481 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sun, 29 Jul 2018 21:02:36 -0400 Subject: [PATCH 04/25] Fix style issues in extract command --- Library/Homebrew/dev-cmd/extract.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index f933f0a99d..d412e6a949 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -19,11 +19,11 @@ module Homebrew module_function def extract - Homebrew::CLI::Parser.parse do + Homebrew::CLI::Parser.parse do switch "--stdout", description: "Output to stdout on terminal instead of file" switch :debug switch :force - end + end odie "Cannot use a tap and --stdout at the same time!" if ARGV.named.length == 3 && args.stdout? raise UsageError unless (ARGV.named.length == 3 && !args.stdout?) || (ARGV.named.length == 2 && args.stdout?) @@ -31,7 +31,7 @@ module Homebrew formula = Formulary.factory(ARGV.named.first) version = ARGV.named[1] destination_tap = Tap.fetch(ARGV.named[2]) unless args.stdout? - destination_tap.install unless destination_tap.installed? unless args.stdout? + destination_tap.install unless destination_tap.installed? || args.stdout? unless args.stdout? path = Pathname.new("#{destination_tap.path}/Formula/#{formula}@#{version}.rb") From 1003aa72c9b976a83fa145004001e8638353c763 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Mon, 30 Jul 2018 18:41:45 -0400 Subject: [PATCH 05/25] Additional fixups for extract command - Rework command line options - Make specifying a version optional - Remove stdout option and require a tap to be specified - Do not allow user to extract into homebrew/core - Rework new class name generation to use existing Formulary tools --- Library/Homebrew/dev-cmd/extract.rb | 87 +++++++++++++++++------------ 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index d412e6a949..759109546e 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -1,14 +1,15 @@ -#: * `extract` [`--force`] [`--stdout`] []: +#: * `extract` [`--force`] `--tap=` [`--version=`]: #: Looks through repository history to find the of and #: creates a copy in /Formula/@.rb. If the tap is #: not installed yet, attempts to install/clone the tap before continuing. +#: A tap must be passed through `--tap` in order for `extract` to work. #: #: If `--force` is passed, the file at the destination will be overwritten #: if it already exists. Otherwise, existing files will be preserved. #: -#: If `--stdout` is passed, the file will be written to stdout on the -#: terminal instead of written to a file. A cannot be passed when -#: using `--stdout`. +#: If an argument is passed through `--version`, of +#: will be extracted and placed in the destination tap. Otherwise, the most +#: recent version that can be found will be used. require "utils/git" require "formula_versions" @@ -20,51 +21,65 @@ module Homebrew def extract Homebrew::CLI::Parser.parse do - switch "--stdout", description: "Output to stdout on terminal instead of file" - switch :debug - switch :force + flag "--tap=" + flag "--version=" + switch :debug + switch :force end - odie "Cannot use a tap and --stdout at the same time!" if ARGV.named.length == 3 && args.stdout? - raise UsageError unless (ARGV.named.length == 3 && !args.stdout?) || (ARGV.named.length == 2 && args.stdout?) + # If no formula args are given, ask specifically for a formula to be specified + raise FormulaUnspecifiedError if ARGV.named.empty? + + # If some other number of args are given, provide generic usage information + raise UsageError if ARGV.named.length != 1 + + odie "The tap to which the formula is extracted must be specified!" if args.tap.nil? formula = Formulary.factory(ARGV.named.first) - version = ARGV.named[1] - destination_tap = Tap.fetch(ARGV.named[2]) unless args.stdout? - destination_tap.install unless destination_tap.installed? || args.stdout? + if args.version.nil? + version = formula.version + else + version = args.version + end + destination_tap = Tap.fetch(args.tap) + destination_tap.install unless destination_tap.installed? - unless args.stdout? - path = Pathname.new("#{destination_tap.path}/Formula/#{formula}@#{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} #{destination_tap.name} --force`. - EOS - end - ohai "Overwriting existing formula at #{path}" if ARGV.debug? - path.delete + odie "Cannot extract formula to homebrew/core!" if destination_tap.name == "homebrew/core" + + path = Pathname.new("#{destination_tap.path}/Formula/#{formula}@#{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` + EOS end + ohai "Overwriting existing formula at #{path}" if ARGV.debug? + path.delete end - 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") + 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 - odie "Could not find #{formula} #{version}!" if rev.empty? - 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. - result.gsub!("class #{formula.name.capitalize} < Formula", "class #{formula.name.capitalize}AT#{version.gsub(/[^0-9a-z ]/i, "")} < Formula") - if args.stdout? - puts result if args.stdout? - else - ohai "Writing formula for #{formula} from #{rev} to #{path}" - path.write result - end + 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}" + path.write result end # @private From 8d8d235019220ad939375e231e5e05488c587e77 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Mon, 30 Jul 2018 18:45:24 -0400 Subject: [PATCH 06/25] Bring up manpages and docs to match new changes --- docs/Manpage.md | 9 +++++---- manpages/brew.1 | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/Manpage.md b/docs/Manpage.md index b4268f7096..87f0cc4ef0 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -790,17 +790,18 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note * `edit` `formula`: Open `formula` in the editor. - * `extract` [`--force`] [`--stdout`] `formula` `version` [`tap`]: + * `extract` [`--force`] `formula` `--tap=``tap` [`--version=``version`]: Looks through repository history to find the `version` of `formula` and creates a copy in `tap`/Formula/`formula`@`version`.rb. If the tap is not installed yet, attempts to install/clone the tap before continuing. + A tap must be passed through `--tap` in order for `extract` to work. If `--force` is passed, the file at the destination will be overwritten if it already exists. Otherwise, existing files will be preserved. - If `--stdout` is passed, the file will be written to stdout on the - terminal instead of written to a file. A `tap` cannot be passed when - using `--stdout`. + If an argument is passed through `--version`, `version` of `formula` + will be extracted and placed in the destination tap. Otherwise, the most + recent version that can be found will be used. * `formula` `formula`: Display the path where `formula` is located. diff --git a/manpages/brew.1 b/manpages/brew.1 index 3c75e869e1..d0f4606210 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -727,14 +727,14 @@ Open all of Homebrew for editing\. Open \fIformula\fR in the editor\. . .TP -\fBextract\fR [\fB\-\-force\fR] [\fB\-\-stdout\fR] \fIformula\fR \fIversion\fR [\fItap\fR] -Looks through repository history to find the \fIversion\fR of \fIformula\fR and creates a copy in \fItap\fR/Formula/\fIformula\fR@\fIversion\fR\.rb\. If the tap is not installed yet, attempts to install/clone the tap before continuing\. +\fBextract\fR [\fB\-\-force\fR] \fIformula\fR \fB\-\-tap=\fR\fItap\fR [\fB\-\-version=\fR\fIversion\fR] +Looks through repository history to find the \fIversion\fR of \fIformula\fR and creates a copy in \fItap\fR/Formula/\fIformula\fR@\fIversion\fR\.rb\. If the tap is not installed yet, attempts to install/clone the tap before continuing\. A tap must be passed through \fB\-\-tap\fR in order for \fBextract\fR to work\. . .IP If \fB\-\-force\fR is passed, the file at the destination will be overwritten if it already exists\. Otherwise, existing files will be preserved\. . .IP -If \fB\-\-stdout\fR is passed, the file will be written to stdout on the terminal instead of written to a file\. A \fItap\fR cannot be passed when using \fB\-\-stdout\fR\. +If an argument is passed through \fB\-\-version\fR, \fIversion\fR of \fIformula\fR will be extracted and placed in the destination tap\. Otherwise, the most recent version that can be found will be used\. . .TP \fBformula\fR \fIformula\fR From ff8b5f8c5ab89a2460a0c8b22350a5ae6b20b7de Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Thu, 2 Aug 2018 00:21:09 -0400 Subject: [PATCH 07/25] extract: rework to search through old/deleted formulae - Removed use of FormulaVersions - it's generally too brittle for our uses. We now interface directly with the Git repo via utils/git and monkeypatch the formula loading process where needed. - Really old formulae (that specify fields as instance vars instead of methods) still need more work to be supported; they don't work here quite yet. - Properly handles deleted/removed formulae from Homebrew/homebrew-core. Additional work is still needed to search through Homebrew/legacy-homebrew if this functionality is desired. --- Library/Homebrew/dev-cmd/extract.rb | 127 +++++++++++++++++++++------- 1 file changed, 95 insertions(+), 32 deletions(-) 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 From 95abdf96624029d5319462ac1d54613795c5cce9 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Fri, 10 Aug 2018 14:59:09 -0400 Subject: [PATCH 08/25] Use refinements instead of general monkey-patching --- Library/Homebrew/dev-cmd/extract.rb | 112 +++++++++++----------------- 1 file changed, 44 insertions(+), 68 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index b4fc8b8496..d9dd6a642d 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -15,96 +15,67 @@ require "utils/git" 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? +module ExtractExtensions + refine BottleSpecification do + def method_missing(m, *_args, &_block) + # no-op end end - module Compat + refine Module do + def method_missing(m, *_args, &_block) + # no-op + end + end + + refine DependencyCollector do + 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 + # no-op + end + end + 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 + using ExtractExtensions module_function def extract Homebrew::CLI::Parser.parse do - flag "--tap=" flag "--version=" switch :debug switch :force end - # If no formula args are given, ask specifically for a formula to be specified - raise FormulaUnspecifiedError if ARGV.named.empty? + # Expect exactly two named arguments: formula and tap + raise UsageError if ARGV.named.length != 2 - # If some other number of args are given, provide generic usage information - raise UsageError if ARGV.named.length != 1 - - odie "The tap to which the formula is extracted must be specified!" if args.tap.nil? - - 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 = Tap.fetch(ARGV.named[1]) + odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap? destination_tap.install unless destination_tap.installed? - odie "Cannot extract formula to homebrew/core!" if destination_tap.name == "homebrew/core" + core = CoreTap.instance + name = ARGV.named.first.downcase + repo = core.path + file = core.path/"Formula/#{name}.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 + if args.version version = args.version rev = "HEAD" test_formula = nil @@ -121,6 +92,11 @@ module Homebrew 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) + else + 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) 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. @@ -128,7 +104,7 @@ module Homebrew 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") + path = destination_tap.path/"Formula/#{name}@#{version}.rb" if path.exist? unless ARGV.force? odie <<~EOS @@ -146,7 +122,7 @@ module Homebrew # @private def formula_at_revision(repo, name, file, rev) - return nil if rev.empty? + return if rev.empty? contents = Git.last_revision_of_file(repo, file, before_commit: rev) Formulary.from_contents(name, file, contents) end From 260867627be79384be1ba73eb087d9670f8ace53 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Thu, 16 Aug 2018 12:52:54 -0400 Subject: [PATCH 09/25] Switch back to monkey-patching Also gsub! some particular formula contents to work around older formulae ("brewkit" isn't around anymore, and url is expected to be a method, not an instance variable). --- Library/Homebrew/dev-cmd/extract.rb | 57 +++++++++++++++-------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index d9dd6a642d..4f193c44f6 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -15,45 +15,46 @@ require "utils/git" require "formulary" require "tap" -module ExtractExtensions - refine BottleSpecification do - def method_missing(m, *_args, &_block) +class BottleSpecification + def method_missing(m, *_args, &_block) + # no-op + end +end + +class Module + def method_missing(m, *_args, &_block) + # no-op + 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 # no-op end end - refine Module do - def method_missing(m, *_args, &_block) - # no-op - end - end - - refine DependencyCollector do - 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 - # no-op - 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 - using ExtractExtensions module_function def extract @@ -124,6 +125,8 @@ module Homebrew def formula_at_revision(repo, name, file, rev) return if rev.empty? contents = Git.last_revision_of_file(repo, file, before_commit: rev) + contents.gsub!("@url=", "url ") + contents.gsub!("require 'brewkit'", "require 'formula'") Formulary.from_contents(name, file, contents) end end From 713aedb6ed2eaeb8b8c10f10be4ab8a09a1bafbf Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Mon, 20 Aug 2018 09:20:45 -0400 Subject: [PATCH 10/25] Fix code review requests --- Library/Homebrew/dev-cmd/extract.rb | 31 +++++++++-------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 4f193c44f6..98f1d2e03c 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -16,37 +16,21 @@ require "formulary" require "tap" class BottleSpecification - def method_missing(m, *_args, &_block) - # no-op + def method_missing(*) end end class Module - def method_missing(m, *_args, &_block) - # no-op + def method_missing(*) 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 - # no-op - 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 @@ -71,10 +55,12 @@ module Homebrew odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap? destination_tap.install unless destination_tap.installed? - core = CoreTap.instance name = ARGV.named.first.downcase - repo = core.path - file = core.path/"Formula/#{name}.rb" + repo = CoreTap.instance.path + # Formulae can technically live in "/.rb" or + # "/Formula/.rb", but explicitly use the latter for now + # since that is now core tap is structured. + file = repo/"Formula/#{name}.rb" if args.version version = args.version @@ -100,7 +86,8 @@ module Homebrew result = Git.last_revision_of_file(repo, file) 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. + # 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") From 875885dda69ee2040f28187d7ca5f53e9ffa5c33 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Mon, 20 Aug 2018 09:21:00 -0400 Subject: [PATCH 11/25] Add a starter file for spec --- Library/Homebrew/test/dev-cmd/extract_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Library/Homebrew/test/dev-cmd/extract_spec.rb diff --git a/Library/Homebrew/test/dev-cmd/extract_spec.rb b/Library/Homebrew/test/dev-cmd/extract_spec.rb new file mode 100644 index 0000000000..5fd98eb940 --- /dev/null +++ b/Library/Homebrew/test/dev-cmd/extract_spec.rb @@ -0,0 +1,13 @@ +describe "brew extract", :integration_test do + it "extracts the most recent formula version without version argument" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + formula_file = setup_test_formula "foo" + + expect { brew "extract", "foo", target.name } + .to be_a_success + + expect(path/"Formula/foo@1.0.rb").to exist + end +end From 6018968f91993f285fc1ff00652504e2523c1f5b Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Tue, 21 Aug 2018 09:49:58 -0400 Subject: [PATCH 12/25] Further collapse monkeypatches --- Library/Homebrew/dev-cmd/extract.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 98f1d2e03c..a2ae913281 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -16,18 +16,15 @@ require "formulary" require "tap" class BottleSpecification - def method_missing(*) - end + def method_missing(*); end end class Module - def method_missing(*) - end + def method_missing(*); end end class DependencyCollector - def parse_symbol_spec(spec, tags) - end + def parse_symbol_spec(*); end module Compat def parse_string_spec(spec, tags) From 0552dcff62f23017457ad4d1c76f7c8936d2a40a Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Tue, 21 Aug 2018 10:13:07 -0400 Subject: [PATCH 13/25] extract: accept tap as a non-flagged argument --- Library/Homebrew/dev-cmd/extract.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index a2ae913281..9ebec61354 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -1,8 +1,7 @@ -#: * `extract` [`--force`] `--tap=` [`--version=`]: +#: * `extract` [`--force`] [`--version=`]: #: Looks through repository history to find the of and #: creates a copy in /Formula/@.rb. If the tap is #: not installed yet, attempts to install/clone the tap before continuing. -#: A tap must be passed through `--tap` in order for `extract` to work. #: #: If `--force` is passed, the file at the destination will be overwritten #: if it already exists. Otherwise, existing files will be preserved. From bc551d5ed147cdba249d6d4ef76458dcaf81b4f1 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Tue, 21 Aug 2018 10:14:24 -0400 Subject: [PATCH 14/25] extract: regenerate docs and manpages --- docs/Manpage.md | 3 +-- manpages/brew.1 | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/Manpage.md b/docs/Manpage.md index 87f0cc4ef0..4a79c8457c 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -790,11 +790,10 @@ With `--verbose` or `-v`, many commands print extra debugging information. Note * `edit` `formula`: Open `formula` in the editor. - * `extract` [`--force`] `formula` `--tap=``tap` [`--version=``version`]: + * `extract` [`--force`] `formula` `tap` [`--version=``version`]: Looks through repository history to find the `version` of `formula` and creates a copy in `tap`/Formula/`formula`@`version`.rb. If the tap is not installed yet, attempts to install/clone the tap before continuing. - A tap must be passed through `--tap` in order for `extract` to work. If `--force` is passed, the file at the destination will be overwritten if it already exists. Otherwise, existing files will be preserved. diff --git a/manpages/brew.1 b/manpages/brew.1 index d0f4606210..f6809c133f 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -727,8 +727,8 @@ Open all of Homebrew for editing\. Open \fIformula\fR in the editor\. . .TP -\fBextract\fR [\fB\-\-force\fR] \fIformula\fR \fB\-\-tap=\fR\fItap\fR [\fB\-\-version=\fR\fIversion\fR] -Looks through repository history to find the \fIversion\fR of \fIformula\fR and creates a copy in \fItap\fR/Formula/\fIformula\fR@\fIversion\fR\.rb\. If the tap is not installed yet, attempts to install/clone the tap before continuing\. A tap must be passed through \fB\-\-tap\fR in order for \fBextract\fR to work\. +\fBextract\fR [\fB\-\-force\fR] \fIformula\fR \fItap\fR [\fB\-\-version=\fR\fIversion\fR] +Looks through repository history to find the \fIversion\fR of \fIformula\fR and creates a copy in \fItap\fR/Formula/\fIformula\fR@\fIversion\fR\.rb\. If the tap is not installed yet, attempts to install/clone the tap before continuing\. . .IP If \fB\-\-force\fR is passed, the file at the destination will be overwritten if it already exists\. Otherwise, existing files will be preserved\. From a232e3a791ac97b3ac45ab9d6b34e5c65a49b943 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Fri, 24 Aug 2018 11:38:14 -0400 Subject: [PATCH 15/25] extract: fix corner case If the formula to be retrieved was just version-bumped in the most recent commit (HEAD), we would've ended up grabbing the second-most recent version. Instead, if the file already exists in tree at the current commit, just construct the formula from that to get the version (for naming purposes) and copy the file outright to its new location. --- Library/Homebrew/dev-cmd/extract.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 9ebec61354..9d19a2ffc6 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -16,10 +16,18 @@ require "tap" class BottleSpecification def method_missing(*); end + + def respond_to_missing?(*) + true + end end class Module def method_missing(*); end + + def respond_to_missing?(*) + true + end end class DependencyCollector @@ -75,6 +83,9 @@ module Homebrew 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) + elsif File.exist?(file) + version = Formulary.factory(file).version + result = File.read(file) else rev = Git.last_revision_commit_of_file(repo, file) version = formula_at_revision(repo, name, file, rev).version From 9ab3ff4a4175748ccade7e15706748f0409862e4 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Fri, 24 Aug 2018 11:42:11 -0400 Subject: [PATCH 16/25] extract: add spec/integration test --- Library/Homebrew/test/dev-cmd/extract_spec.rb | 232 +++++++++++++++++- 1 file changed, 227 insertions(+), 5 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/extract_spec.rb b/Library/Homebrew/test/dev-cmd/extract_spec.rb index 5fd98eb940..fe91531dc8 100644 --- a/Library/Homebrew/test/dev-cmd/extract_spec.rb +++ b/Library/Homebrew/test/dev-cmd/extract_spec.rb @@ -1,13 +1,235 @@ describe "brew extract", :integration_test do - it "extracts the most recent formula version without version argument" do + it "retrieves the most recent formula version without version argument" do path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" (path/"Formula").mkpath target = Tap.from_path(path) - formula_file = setup_test_formula "foo" - - expect { brew "extract", "foo", target.name } + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + end + expect { brew "extract", "testball", target.name } .to be_a_success - expect(path/"Formula/foo@1.0.rb").to exist + expect(path/"Formula/testball@0.2.rb").to exist + + expect(Formulary.factory(path/"Formula/testball@0.2.rb").version).to be == "0.2" + end + + it "does not overwrite existing files, except when running with --force" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + end + expect { brew "extract", "testball", target.name } + .to be_a_success + + expect(path/"Formula/testball@0.2.rb").to exist + + expect { brew "extract", "testball", target.name } + .to be_a_failure + + expect { brew "extract", "testball", target.name, "--force" } + .to be_a_success + + expect { brew "extract", "testball", target.name, "--version=0.1" } + .to be_a_success + + expect(path/"Formula/testball@0.2.rb").to exist + + expect { brew "extract", "testball", "--version=0.1", target.name } + .to be_a_failure + + expect { brew "extract", "testball", target.name, "--version=0.1", "--force" } + .to be_a_success + end + + it "retrieves the specified formula version when given argument" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + end + expect { brew "extract", "testball", target.name, "--version=0.1" } + .to be_a_success + + expect { brew "extract", "testball", target.name, "--version=0.1", "--force" } + .to be_a_success + + expect(Formulary.factory(path/"Formula/testball@0.1.rb").version).to be == "0.1" + end + + it "retrieves most recent deleted formula when no argument is given" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + File.delete(formula_file) + system "git", "add", "--all" + system "git", "commit", "-m", "Remove testball" + end + + expect { brew "extract", "testball", target.name } + .to be_a_success + + expect(path/"Formula/testball@0.2.rb").to exist + + expect(Formulary.factory(path/"Formula/testball@0.2.rb").version).to be == "0.2" + end + + it "retrieves old version of deleted formula when argument is given" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + File.delete(formula_file) + system "git", "add", "--all" + system "git", "commit", "-m", "Remove testball" + end + + expect { brew "extract", "testball", target.name, "--version=0.1" } + .to be_a_success + + expect(path/"Formula/testball@0.1.rb").to exist + + expect(Formulary.factory(path/"Formula/testball@0.1.rb").version).to be == "0.1" + end + + it "retrieves old formulae that use outdated/missing blocks" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + contents = <<~EOF + require 'brewkit' + class Testball < Formula + @url="file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" + @md5='80a8aa0c5a8310392abf3b69f0319204' + + def install + prefix.install "bin" + prefix.install "libexec" + Dir.chdir "doc" + end + end + EOF + formula_file = core_tap.path/"Formula/testball.rb" + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + File.delete(formula_file) + system "git", "add", "--all" + system "git", "commit", "-m", "Remove testball" + end + + expect { brew "extract", "testball", target.name, "--version=0.1" } + .to be_a_success + + expect(path/"Formula/testball@0.1.rb").to exist + end + + it "fails when formula does not exist" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + File.delete(formula_file) + system "git", "add", "--all" + system "git", "commit", "-m", "Remove testball" + end + expect { brew "extract", "foo", target.name } + .to be_a_failure + expect(Dir.entries(path/"Formula").size).to be == 2 + end + + it "fails when formula does not have the specified version" do + path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" + (path/"Formula").mkpath + target = Tap.from_path(path) + core_tap = CoreTap.new + core_tap.path.cd do + system "git", "init" + formula_file = setup_test_formula "testball" + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.1" + contents = File.read(formula_file) + contents.gsub!("testball-0.1", "testball-0.2") + File.write(formula_file, contents) + system "git", "add", "--all" + system "git", "commit", "-m", "testball 0.2" + File.delete(formula_file) + system "git", "add", "--all" + system "git", "commit", "-m", "Remove testball" + end + + expect { brew "extract", "testball", target.name, "--version=0.3" } + .to be_a_failure + + expect(path/"Formula/testball@0.3.rb").not_to exist end end From 1e4cd780c34de2eebf7b39fd730b75110e0383ae Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 10:49:06 -0400 Subject: [PATCH 17/25] extract: fix style --- Library/Homebrew/dev-cmd/extract.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 9d19a2ffc6..d4d5cf87a2 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -14,6 +14,7 @@ require "utils/git" require "formulary" require "tap" +# rubocop:disable Style/MethodMissingSuper class BottleSpecification def method_missing(*); end @@ -42,6 +43,7 @@ class DependencyCollector prepend Compat end +# rubocop:enable Style/MethodMissingSuper module Homebrew module_function From 0e080eba97095d0212c605390ec59df2d52d23b0 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 10:51:31 -0400 Subject: [PATCH 18/25] extract: simplify integration tests --- Library/Homebrew/test/dev-cmd/extract_spec.rb | 205 +----------------- 1 file changed, 1 insertion(+), 204 deletions(-) diff --git a/Library/Homebrew/test/dev-cmd/extract_spec.rb b/Library/Homebrew/test/dev-cmd/extract_spec.rb index fe91531dc8..59e7cc1905 100644 --- a/Library/Homebrew/test/dev-cmd/extract_spec.rb +++ b/Library/Homebrew/test/dev-cmd/extract_spec.rb @@ -1,5 +1,5 @@ describe "brew extract", :integration_test do - it "retrieves the most recent formula version without version argument" do + it "retrieves the specified version of formula, defaulting to most recent" do path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" (path/"Formula").mkpath target = Tap.from_path(path) @@ -21,119 +21,6 @@ describe "brew extract", :integration_test do expect(path/"Formula/testball@0.2.rb").to exist expect(Formulary.factory(path/"Formula/testball@0.2.rb").version).to be == "0.2" - end - - it "does not overwrite existing files, except when running with --force" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - end - expect { brew "extract", "testball", target.name } - .to be_a_success - - expect(path/"Formula/testball@0.2.rb").to exist - - expect { brew "extract", "testball", target.name } - .to be_a_failure - - expect { brew "extract", "testball", target.name, "--force" } - .to be_a_success - - expect { brew "extract", "testball", target.name, "--version=0.1" } - .to be_a_success - - expect(path/"Formula/testball@0.2.rb").to exist - - expect { brew "extract", "testball", "--version=0.1", target.name } - .to be_a_failure - - expect { brew "extract", "testball", target.name, "--version=0.1", "--force" } - .to be_a_success - end - - it "retrieves the specified formula version when given argument" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - end - expect { brew "extract", "testball", target.name, "--version=0.1" } - .to be_a_success - - expect { brew "extract", "testball", target.name, "--version=0.1", "--force" } - .to be_a_success - - expect(Formulary.factory(path/"Formula/testball@0.1.rb").version).to be == "0.1" - end - - it "retrieves most recent deleted formula when no argument is given" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - File.delete(formula_file) - system "git", "add", "--all" - system "git", "commit", "-m", "Remove testball" - end - - expect { brew "extract", "testball", target.name } - .to be_a_success - - expect(path/"Formula/testball@0.2.rb").to exist - - expect(Formulary.factory(path/"Formula/testball@0.2.rb").version).to be == "0.2" - end - - it "retrieves old version of deleted formula when argument is given" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - File.delete(formula_file) - system "git", "add", "--all" - system "git", "commit", "-m", "Remove testball" - end expect { brew "extract", "testball", target.name, "--version=0.1" } .to be_a_success @@ -142,94 +29,4 @@ describe "brew extract", :integration_test do expect(Formulary.factory(path/"Formula/testball@0.1.rb").version).to be == "0.1" end - - it "retrieves old formulae that use outdated/missing blocks" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - contents = <<~EOF - require 'brewkit' - class Testball < Formula - @url="file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" - @md5='80a8aa0c5a8310392abf3b69f0319204' - - def install - prefix.install "bin" - prefix.install "libexec" - Dir.chdir "doc" - end - end - EOF - formula_file = core_tap.path/"Formula/testball.rb" - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - File.delete(formula_file) - system "git", "add", "--all" - system "git", "commit", "-m", "Remove testball" - end - - expect { brew "extract", "testball", target.name, "--version=0.1" } - .to be_a_success - - expect(path/"Formula/testball@0.1.rb").to exist - end - - it "fails when formula does not exist" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - File.delete(formula_file) - system "git", "add", "--all" - system "git", "commit", "-m", "Remove testball" - end - expect { brew "extract", "foo", target.name } - .to be_a_failure - expect(Dir.entries(path/"Formula").size).to be == 2 - end - - it "fails when formula does not have the specified version" do - path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo" - (path/"Formula").mkpath - target = Tap.from_path(path) - core_tap = CoreTap.new - core_tap.path.cd do - system "git", "init" - formula_file = setup_test_formula "testball" - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.1" - contents = File.read(formula_file) - contents.gsub!("testball-0.1", "testball-0.2") - File.write(formula_file, contents) - system "git", "add", "--all" - system "git", "commit", "-m", "testball 0.2" - File.delete(formula_file) - system "git", "add", "--all" - system "git", "commit", "-m", "Remove testball" - end - - expect { brew "extract", "testball", target.name, "--version=0.3" } - .to be_a_failure - - expect(path/"Formula/testball@0.3.rb").not_to exist - end end From be0cd21d0ffd9c56efaa35a288b2884c62bd1843 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 10:58:13 -0400 Subject: [PATCH 19/25] extract: explicitly load cli_parser --- Library/Homebrew/dev-cmd/extract.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index d4d5cf87a2..c77d58f7b0 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -10,6 +10,7 @@ #: will be extracted and placed in the destination tap. Otherwise, the most #: recent version that can be found will be used. +require "cli_parser" require "utils/git" require "formulary" require "tap" From d4a2006f0404de6696876e0e252ebc707ce419ee Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 13:33:25 -0400 Subject: [PATCH 20/25] extract: add missing monkey-patch --- Library/Homebrew/dev-cmd/extract.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index c77d58f7b0..fb0066d347 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -32,6 +32,14 @@ class Module end end +class Resource + def method_missing(*); end + + def respond_to_missing?(*) + true + end +end + class DependencyCollector def parse_symbol_spec(*); end From ff2ce2f0936bf5a271a1fbdf834f55b94c08062a Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 13:35:08 -0400 Subject: [PATCH 21/25] extract: better success message Fixes the empty rev string when copying file from HEAD; explicitly set rev to HEAD in that case. --- Library/Homebrew/dev-cmd/extract.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index fb0066d347..8c9239b167 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -95,6 +95,7 @@ module Homebrew 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) elsif File.exist?(file) + rev = "HEAD" version = Formulary.factory(file).version result = File.read(file) else @@ -122,7 +123,7 @@ module Homebrew ohai "Overwriting existing formula at #{path}" if ARGV.debug? path.delete end - ohai "Writing formula for #{name} from #{rev} to #{path}" + ohai "Writing formula for #{name} from revision #{rev} to #{path}" path.write result end From a8563afc9e60abc749bff43177c768c0af69dab9 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 13:40:57 -0400 Subject: [PATCH 22/25] extract: add progress message when searching Git history For the impatient ones out there. --- Library/Homebrew/dev-cmd/extract.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 8c9239b167..5b19fb80da 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -78,6 +78,7 @@ module Homebrew file = repo/"Formula/#{name}.rb" if args.version + ohai "Searching repository history" version = args.version rev = "HEAD" test_formula = nil @@ -99,6 +100,7 @@ module Homebrew version = Formulary.factory(file).version result = File.read(file) else + ohai "Searching repository history" 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? From bd352bcf35fd45f811e5bd2dbe7f918124e591a2 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 14:52:30 -0400 Subject: [PATCH 23/25] extract: use localized monkey-patching Instead of just blanketing over with monkey-patches like before, set up monkey-patches as needed, and make sure to clean up after we're done. --- Library/Homebrew/dev-cmd/extract.rb | 58 +++++++++++++++++++---------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index 5b19fb80da..d45d60d8eb 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -15,28 +15,49 @@ require "utils/git" require "formulary" require "tap" -# rubocop:disable Style/MethodMissingSuper -class BottleSpecification - def method_missing(*); end - - def respond_to_missing?(*) - true +def with_monkey_patch + BottleSpecification.class_eval do + if method_defined?(:method_missing) + alias_method :old_method_missing, :method_missing + end + define_method(:method_missing) { |*| } end -end -class Module - def method_missing(*); end - - def respond_to_missing?(*) - true + Module.class_eval do + if method_defined?(:method_missing) + alias_method :old_method_missing, :method_missing + end + define_method(:method_missing) { |*| } end -end -class Resource - def method_missing(*); end + Resource.class_eval do + if method_defined?(:method_missing) + alias_method :old_method_missing, :method_missing + end + define_method(:method_missing) { |*| } + end - def respond_to_missing?(*) - true + yield +ensure + BottleSpecification.class_eval do + if method_defined?(:old_method_missing) + alias_method :method_missing, :old_method_missing + undef :old_method_missing + end + end + + Module.class_eval do + if method_defined?(:old_method_missing) + alias_method :method_missing, :old_method_missing + undef :old_method_missing + end + end + + Resource.class_eval do + if method_defined?(:old_method_missing) + alias_method :method_missing, :old_method_missing + undef :old_method_missing + end end end @@ -52,7 +73,6 @@ class DependencyCollector prepend Compat end -# rubocop:enable Style/MethodMissingSuper module Homebrew module_function @@ -135,6 +155,6 @@ module Homebrew contents = Git.last_revision_of_file(repo, file, before_commit: rev) contents.gsub!("@url=", "url ") contents.gsub!("require 'brewkit'", "require 'formula'") - Formulary.from_contents(name, file, contents) + with_monkey_patch { Formulary.from_contents(name, file, contents) } end end From ad0f9d603f5b04cb0c4e9d75fab36f4118bff942 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sat, 25 Aug 2018 19:52:22 -0400 Subject: [PATCH 24/25] extract: localize DependencyCollector monkey-patches too --- Library/Homebrew/dev-cmd/extract.rb | 32 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index d45d60d8eb..e6f85cb41b 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -37,6 +37,20 @@ def with_monkey_patch define_method(:method_missing) { |*| } end + DependencyCollector.class_eval do + if method_defined?(:parse_symbol_spec) + alias_method :old_parse_symbol_spec, :parse_symbol_spec + end + define_method(:parse_symbol_spec) { |*| } + end + + DependencyCollector::Compat.class_eval do + if method_defined?(:parse_string_spec) + alias_method :old_parse_string_spec, :parse_string_spec + end + define_method(:parse_string_spec) { |*| } + end + yield ensure BottleSpecification.class_eval do @@ -59,18 +73,20 @@ ensure undef :old_method_missing end end -end -class DependencyCollector - def parse_symbol_spec(*); end - - module Compat - def parse_string_spec(spec, tags) - super + DependencyCollector.class_eval do + if method_defined?(:old_parse_symbol_spec) + alias_method :parse_symbol_spec, :old_parse_symbol_spec + undef :old_parse_symbol_spec end end - prepend Compat + DependencyCollector::Compat.class_eval do + if method_defined?(:old_parse_string_spec) + alias_method :parse_string_spec, :old_parse_string_spec + undef :old_parse_string_spec + end + end end module Homebrew From b1120dd404b796853273459c127178c193fc95b8 Mon Sep 17 00:00:00 2001 From: Caleb Xu Date: Sun, 26 Aug 2018 02:22:09 -0400 Subject: [PATCH 25/25] extract: check if DependencyCollector::Compat is defined before monkey-patch --- Library/Homebrew/dev-cmd/extract.rb | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Library/Homebrew/dev-cmd/extract.rb b/Library/Homebrew/dev-cmd/extract.rb index e6f85cb41b..9a5298aaaa 100644 --- a/Library/Homebrew/dev-cmd/extract.rb +++ b/Library/Homebrew/dev-cmd/extract.rb @@ -44,11 +44,13 @@ def with_monkey_patch define_method(:parse_symbol_spec) { |*| } end - DependencyCollector::Compat.class_eval do - if method_defined?(:parse_string_spec) - alias_method :old_parse_string_spec, :parse_string_spec + if defined?(DependencyCollector::Compat) + DependencyCollector::Compat.class_eval do + if method_defined?(:parse_string_spec) + alias_method :old_parse_string_spec, :parse_string_spec + end + define_method(:parse_string_spec) { |*| } end - define_method(:parse_string_spec) { |*| } end yield @@ -81,10 +83,12 @@ ensure end end - DependencyCollector::Compat.class_eval do - if method_defined?(:old_parse_string_spec) - alias_method :parse_string_spec, :old_parse_string_spec - undef :old_parse_string_spec + if defined?(DependencyCollector::Compat) + DependencyCollector::Compat.class_eval do + if method_defined?(:old_parse_string_spec) + alias_method :parse_string_spec, :old_parse_string_spec + undef :old_parse_string_spec + end end end end