diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index 6f2fb849c1..36fd927dbc 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -441,6 +441,9 @@ class DuplicateResourceError < ArgumentError end end +# raised when a single patch file is not found and apply hasn't been specified +class MissingApplyError < RuntimeError ; end + class BottleVersionMismatchError < RuntimeError def initialize(bottle_file, bottle_version, formula, formula_version) super <<-EOS.undent diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb index 3c5bd42eaa..ebfb8e347b 100644 --- a/Library/Homebrew/patch.rb +++ b/Library/Homebrew/patch.rb @@ -112,7 +112,7 @@ class ExternalPatch def initialize(strip, &block) @strip = strip - @resource = Resource.new("patch", &block) + @resource = Resource::Patch.new(&block) end def external? @@ -127,9 +127,25 @@ class ExternalPatch def apply dir = Pathname.pwd resource.unpack do - # Assumption: the only file in the staging directory is the patch - patchfile = Pathname.pwd.children.first - dir.cd { safe_system "/usr/bin/patch", "-g", "0", "-f", "-#{strip}", "-i", patchfile } + patch_dir = Pathname.pwd + if patch_files.empty? + children = patch_dir.children + if (children.count == 1 && children.first.file?) + patch_files << children.first.basename + else + raise MissingApplyError, <<-EOS.undent + There should be exactly one patch file in the staging directory unless + the "apply" method was used one or more times in the patch-do block. + EOS + end + end + dir.cd do + patch_files.each do |patch_file| + ohai "Applying #{patch_file}" + patch_file = patch_dir/patch_file + safe_system "/usr/bin/patch", "-g", "0", "-f", "-#{strip}", "-i", patch_file + end + end end end @@ -141,6 +157,10 @@ class ExternalPatch resource.fetch end + def patch_files + resource.patch_files + end + def verify_download_integrity(fn) resource.verify_download_integrity(fn) end diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index bc5d62aeec..a15788fb3f 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -165,4 +165,19 @@ class Resource super(target/name) end end + + class Patch < Resource + attr_reader :patch_files + + def initialize(&block) + @patch_files = [] + super "patch", &block + end + + def apply(*paths) + paths.flatten! + @patch_files.concat(paths) + @patch_files.uniq! + end + end end diff --git a/Library/Homebrew/test/lib/config.rb b/Library/Homebrew/test/lib/config.rb index 3f72a57642..39c4ae27f1 100644 --- a/Library/Homebrew/test/lib/config.rb +++ b/Library/Homebrew/test/lib/config.rb @@ -21,3 +21,6 @@ HOMEBREW_LOGS = HOMEBREW_PREFIX.parent+"logs" TESTBALL_SHA1 = "be478fd8a80fe7f29196d6400326ac91dad68c37" TESTBALL_SHA256 = "91e3f7930c98d7ccfb288e115ed52d06b0e5bc16fec7dce8bdda86530027067b" +TESTBALL_PATCHES_SHA256 = "799c2d551ac5c3a5759bea7796631a7906a6a24435b52261a317133a0bfb34d9" +PATCH_A_SHA256 = "83404f4936d3257e65f176c4ffb5a5b8d6edd644a21c8d8dcc73e22a6d28fcfa" +PATCH_B_SHA256 = "57958271bb802a59452d0816e0670d16c8b70bdf6530bcf6f78726489ad89b90" diff --git a/Library/Homebrew/test/patches/noop-c.diff b/Library/Homebrew/test/patches/noop-c.diff new file mode 100644 index 0000000000..6d0de5b7ba --- /dev/null +++ b/Library/Homebrew/test/patches/noop-c.diff @@ -0,0 +1,9 @@ +diff --git a/libexec/NOOP b/libexec/NOOP +index e08d8f4..3ebfb9d 100755 +--- a/libexec/NOOP ++++ b/libexec/NOOP +@@ -1,2 +1,2 @@ + #!/bin/bash +-echo ABCD +\ No newline at end of file ++echo 1234 diff --git a/Library/Homebrew/test/tarballs/testball-0.1-patches.tgz b/Library/Homebrew/test/tarballs/testball-0.1-patches.tgz new file mode 100644 index 0000000000..4b43f535e8 Binary files /dev/null and b/Library/Homebrew/test/tarballs/testball-0.1-patches.tgz differ diff --git a/Library/Homebrew/test/test_patch.rb b/Library/Homebrew/test/test_patch.rb index 0d0033ee1f..3c6743ded0 100644 --- a/Library/Homebrew/test/test_patch.rb +++ b/Library/Homebrew/test/test_patch.rb @@ -128,3 +128,28 @@ class ExternalPatchTests < Homebrew::TestCase assert_equal "/tmp/foo.tar.gz", @p.cached_download end end + +class ApplyPatchTests < Homebrew::TestCase + def test_empty_patch_files + patch = Patch.create(:p2, nil) + resource = patch.resource + patch_files = patch.patch_files + assert_kind_of Resource::Patch, resource + assert_equal patch_files, resource.patch_files + assert_equal patch_files, [] + end + + def test_resource_patch_apply_method + patch = Patch.create(:p2, nil) + resource = patch.resource + patch_files = patch.patch_files + resource.apply("patch1.diff") + assert_equal patch_files, ["patch1.diff"] + resource.apply("patch2.diff", "patch3.diff") + assert_equal patch_files, ["patch1.diff", "patch2.diff", "patch3.diff"] + resource.apply(["patch4.diff", "patch5.diff"]) + assert_equal patch_files.count, 5 + resource.apply("patch4.diff", ["patch5.diff", "patch6.diff"], "patch7.diff") + assert_equal patch_files.count, 7 + end +end diff --git a/Library/Homebrew/test/test_patching.rb b/Library/Homebrew/test/test_patching.rb index 3c307a43f2..8a18125efb 100644 --- a/Library/Homebrew/test/test_patching.rb +++ b/Library/Homebrew/test/test_patching.rb @@ -2,15 +2,20 @@ require "testing_env" require "formula" class PatchingTests < Homebrew::TestCase + TESTBALL_URL = "file://#{TEST_DIRECTORY}/tarballs/testball-0.1.tbz" + TESTBALL_PATCHES_URL = "file://#{TEST_DIRECTORY}/tarballs/testball-0.1-patches.tgz" PATCH_URL_A = "file://#{TEST_DIRECTORY}/patches/noop-a.diff" PATCH_URL_B = "file://#{TEST_DIRECTORY}/patches/noop-b.diff" PATCH_A_CONTENTS = File.read "#{TEST_DIRECTORY}/patches/noop-a.diff" PATCH_B_CONTENTS = File.read "#{TEST_DIRECTORY}/patches/noop-b.diff" + APPLY_A = "noop-a.diff" + APPLY_B = "noop-b.diff" + APPLY_C = "noop-c.diff" def formula(*args, &block) super do - url "file://#{TEST_DIRECTORY}/tarballs/testball-0.1.tbz" - sha1 TESTBALL_SHA1 + url TESTBALL_URL + sha256 TESTBALL_SHA256 class_eval(&block) end end @@ -31,6 +36,28 @@ class PatchingTests < Homebrew::TestCase end end + def assert_sequentially_patched(formula) + shutup do + formula.brew do + formula.patch + s = File.read("libexec/NOOP") + refute_includes s, "NOOP", "libexec/NOOP was not patched as expected" + refute_includes s, "ABCD", "libexec/NOOP was not patched as expected" + assert_includes s, "1234", "libexec/NOOP was not patched as expected" + end + end + end + + def assert_missing_apply_fail(formula) + assert_raises(MissingApplyError) do + shutup do + formula.brew do + formula.patch + end + end + end + end + def test_single_patch assert_patched formula { def patches @@ -43,7 +70,27 @@ class PatchingTests < Homebrew::TestCase assert_patched formula { patch do url PATCH_URL_A - sha1 "fa8af2e803892e523fdedc6b758117c45e5749a2" + sha256 PATCH_A_SHA256 + end + } + end + + def test_single_patch_dsl_with_apply + assert_patched formula { + patch do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply APPLY_A + end + } + end + + def test_single_patch_dsl_with_sequential_apply + assert_sequentially_patched formula { + patch do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply APPLY_A, APPLY_C end } end @@ -52,7 +99,17 @@ class PatchingTests < Homebrew::TestCase assert_patched formula { patch :p1 do url PATCH_URL_A - sha1 "fa8af2e803892e523fdedc6b758117c45e5749a2" + sha256 PATCH_A_SHA256 + end + } + end + + def test_single_patch_dsl_with_strip_with_apply + assert_patched formula { + patch :p1 do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply APPLY_A end } end @@ -63,7 +120,21 @@ class PatchingTests < Homebrew::TestCase formula do patch :p0 do url PATCH_URL_A - sha1 "fa8af2e803892e523fdedc6b758117c45e5749a2" + sha256 PATCH_A_SHA256 + end + end.brew(&:patch) + end + end + end + + def test_single_patch_dsl_with_incorrect_strip_with_apply + assert_raises(ErrorDuringExecution) do + shutup do + formula do + patch :p0 do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply APPLY_A end end.brew(&:patch) end @@ -74,11 +145,21 @@ class PatchingTests < Homebrew::TestCase assert_patched formula { patch :p0 do url PATCH_URL_B - sha1 "3b54bd576f998ef6d6623705ee023b55062b9504" + sha256 PATCH_B_SHA256 end } end + def test_patch_p0_dsl_with_apply + assert_patched formula { + patch :p0 do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply APPLY_B + end + } + end + def test_patch_p0 assert_patched formula { def patches @@ -126,6 +207,37 @@ class PatchingTests < Homebrew::TestCase end } end + + def test_single_patch_missing_apply_fail + assert_missing_apply_fail formula { + def patches + TESTBALL_PATCHES_URL + end + } + end + + def test_single_patch_dsl_missing_apply_fail + assert_missing_apply_fail formula { + patch do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + end + } + end + + def test_single_patch_dsl_with_apply_enoent_fail + assert_raises(ErrorDuringExecution) do + shutup do + formula do + patch do + url TESTBALL_PATCHES_URL + sha256 TESTBALL_PATCHES_SHA256 + apply "patches/#{APPLY_A}" + end + end.brew(&:patch) + end + end + end end __END__