diff options
| author | Jack Nagel | 2014-03-13 19:51:23 -0500 | 
|---|---|---|
| committer | Jack Nagel | 2014-03-13 21:35:41 -0500 | 
| commit | ee354c9e194069d8e7a92df1cd941b857ee01b38 (patch) | |
| tree | c043cf463916d79824b4cb5ffd18195a58662a1c | |
| parent | f9938b5e5022b2b8b9e52c89bd4a5d2789dd3b87 (diff) | |
| download | homebrew-ee354c9e194069d8e7a92df1cd941b857ee01b38.tar.bz2 | |
New patch implementation and DSL
This commit introduces a new patch implementation that supports
checksums and caching.
Patches are declared in blocks:
  patch do
    url ...
    sha1 ...
  end
A strip level of -p1 is assumed. It can be overridden using a symbol
argument:
  patch :p0 do
    url ...
    sha1 ...
  end
Patches can be declared in stable, devel, and head blocks. This form is
preferred over using conditionals.
  stable do
    # ...
    patch do
      url ...
      sha1 ...
    end
  end
Embedded (__END__) patches are declared like so:
  patch :DATA
  patch :p0, :DATA
Patches can also be embedded by passing a string. This makes it possible
to provide multiple embedded patches while making only some of them
conditional.
  patch :p0, "..."
| -rw-r--r-- | Library/Contributions/example-formula.rb | 42 | ||||
| -rw-r--r-- | Library/Homebrew/formula.rb | 4 | ||||
| -rw-r--r-- | Library/Homebrew/patch.rb | 82 | ||||
| -rw-r--r-- | Library/Homebrew/resource.rb | 3 | ||||
| -rw-r--r-- | Library/Homebrew/software_spec.rb | 11 | ||||
| -rw-r--r-- | Library/Homebrew/test/test_patch.rb | 52 | ||||
| -rw-r--r-- | Library/Homebrew/test/test_software_spec.rb | 6 | 
7 files changed, 180 insertions, 20 deletions
| diff --git a/Library/Contributions/example-formula.rb b/Library/Contributions/example-formula.rb index 305ffd187..fb54b2ad1 100644 --- a/Library/Contributions/example-formula.rb +++ b/Library/Contributions/example-formula.rb @@ -213,26 +213,36 @@ class ExampleFormula < Formula    ## Patches -  # Optionally define a `patches` method returning `DATA` and/or a string with -  # the url to a patch or a list thereof. -  def patches -    # DATA is the embedded diff that comes after __END__ in this file! -    # In this example we only need the patch for older clang than 425: -    DATA unless MacOS.clang_build_version >= 425 +  # External patches can be declared using resource-style blocks. +  patch do +    url "https://example.com/example_patch.diff" +    sha1 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"    end -  # More than the one embedded patch? Use a dict with keys :p0, :p1 and/or -  # p2: as you need, for example: -  def patches -    {:p0 => [ -      'https://trac.macports.org/export/yeah/we/often/steal/a/patch.diff', -      'https://github.com/example/foobar/commit/d46a8c6265c4c741aaae2b807a41ea31bc097052.diff', -      DATA ], -     # For gists, please use the link to a specific hash, so nobody can change it unnoticed. -     :p2 => ['https://raw.github.com/gist/4010022/ab0697dc87a40e65011e2192439609c17578c5be/tags.patch'] -    } +  # A strip level of -p1 is assumed. It can be overridden using a symbol +  # argument: +  patch :p0 do +    url "https://example.com/example_patch.diff" +    sha1 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"    end +  # Patches can be declared in stable, devel, and head blocks. This form is +  # preferred over using conditionals. +  stable do +    patch do +      url "https://example.com/example_patch.diff" +      sha1 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" +    end +  end + +  # Embedded (__END__) patches are declared like so: +  patch :DATA +  patch :p0, :DATA + +  # Patches can also be embedded by passing a string. This makes it possible +  # to provide multiple embedded patches while making only some of them +  # conditional. +  patch :p0, "..."    ## The install method. diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 71e2a730c..57eb2ab4c 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -743,6 +743,10 @@ class Formula        specs.each { |spec| spec.option(name, description) }      end +    def patch strip=:p1, io=nil, &block +      specs.each { |spec| spec.patch(strip, io, &block) } +    end +      def plist_options options        @plist_startup = options[:startup]        @plist_manual = options[:manual] diff --git a/Library/Homebrew/patch.rb b/Library/Homebrew/patch.rb new file mode 100644 index 000000000..a4616eee6 --- /dev/null +++ b/Library/Homebrew/patch.rb @@ -0,0 +1,82 @@ +require 'resource' +require 'stringio' + +class Patch +  def self.create(strip, io=nil, &block) +    case strip ||= :p1 +    when :DATA, IO, StringIO +      IOPatch.new(strip, :p1) +    when String +      IOPatch.new(StringIO.new(strip), :p1) +    when Symbol +      case io +      when :DATA, IO, StringIO +        IOPatch.new(io, strip) +      when String +        IOPatch.new(StringIO.new(io), strip) +      else +        ExternalPatch.new(strip, &block) +      end +    else +      raise ArgumentError, "unexpected value #{strip.inspect} for strip" +    end +  end + +  attr_reader :whence + +  def external? +    whence == :resource +  end +end + +class IOPatch < Patch +  attr_writer :owner +  attr_reader :strip + +  def initialize(io, strip) +    @io     = io +    @strip  = strip +    @whence = :io +  end + +  def apply +    @io = DATA if @io == :DATA +    data = @io.read +    data.gsub!("HOMEBREW_PREFIX", HOMEBREW_PREFIX) +    IO.popen("/usr/bin/patch -g 0 -f -#{strip}", "w") { |p| p.write(data) } +    raise ErrorDuringExecution, "Applying DATA patch failed" unless $?.success? +  ensure +    # IO and StringIO cannot be marshaled, so remove the reference +    # in case we are indirectly referenced by an exception later. +    @io = nil +  end +end + +class ExternalPatch < Patch +  attr_reader :resource, :strip + +  def initialize(strip, &block) +    @strip    = strip +    @resource = Resource.new(&block) +    @whence   = :resource +  end + +  def url +    resource.url +  end + +  def owner= owner +    resource.owner   = owner +    resource.name    = "patch-#{resource.checksum}" +    resource.version = owner.version +  end + +  def apply +    dir = Pathname.pwd +    resource.unpack do +      # Assumption: the only file in the staging directory is the patch +      patchfile = Pathname.pwd.children.first +      safe_system "/usr/bin/patch", "-g", "0", "-f", "-d", dir, "-#{strip}", "-i", patchfile +    end +  end +end diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index d1f0b91b4..a9380db88 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -8,13 +8,12 @@ require 'version'  class Resource    include FileUtils -  attr_reader :name    attr_reader :checksum, :mirrors, :specs, :using    attr_writer :url, :checksum, :version    # Formula name must be set after the DSL, as we have no access to the    # formula name before initialization of the formula -  attr_accessor :owner +  attr_accessor :name, :owner    def initialize name=nil, &block      @name = name diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 43969b2fa..0349dd3dd 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -5,12 +5,13 @@ require 'version'  require 'build_options'  require 'dependency_collector'  require 'bottles' +require 'patch'  class SoftwareSpec    extend Forwardable -  attr_reader :name -  attr_reader :build, :resources, :owner +  attr_reader :name, :owner +  attr_reader :build, :resources, :patches    attr_reader :dependency_collector    attr_reader :bottle_specification @@ -25,6 +26,7 @@ class SoftwareSpec      @build = BuildOptions.new(ARGV.options_only)      @dependency_collector = DependencyCollector.new      @bottle_specification = BottleSpecification.new +    @patches = []    end    def owner= owner @@ -35,6 +37,7 @@ class SoftwareSpec        r.owner     = self        r.version ||= version      end +    patches.each { |p| p.owner = self }    end    def url val=nil, specs={} @@ -84,6 +87,10 @@ class SoftwareSpec    def requirements      dependency_collector.requirements    end + +  def patch strip=:p1, io=nil, &block +    patches << Patch.create(strip, io, &block) +  end  end  class HeadSoftwareSpec < SoftwareSpec diff --git a/Library/Homebrew/test/test_patch.rb b/Library/Homebrew/test/test_patch.rb new file mode 100644 index 000000000..1cc1ac7af --- /dev/null +++ b/Library/Homebrew/test/test_patch.rb @@ -0,0 +1,52 @@ +require 'testing_env' +require 'patch' + +class PatchTests < Test::Unit::TestCase +  def test_create_simple +    patch = Patch.create(:p2) +    assert_kind_of ExternalPatch, patch +    assert patch.external? +    assert_equal :p2, patch.strip +  end + +  def test_create_io +    patch = Patch.create(:p0, StringIO.new("foo")) +    assert_kind_of IOPatch, patch +    assert !patch.external? +    assert_equal :p0, patch.strip +  end + +  def test_create_io_without_strip +    patch = Patch.create(StringIO.new("foo")) +    assert_kind_of IOPatch, patch +    assert_equal :p1, patch.strip +  end + +  def test_create_string +    patch = Patch.create(:p0, "foo") +    assert_kind_of IOPatch, patch +    assert_equal :p0, patch.strip +  end + +  def test_create_string_without_strip +    patch = Patch.create("foo") +    assert_kind_of IOPatch, patch +    assert_equal :p1, patch.strip +  end + +  def test_create_DATA +    patch = Patch.create(:p0, :DATA) +    assert_kind_of IOPatch, patch +    assert_equal :p0, patch.strip +  end + +  def test_create_DATA_without_strip +    patch = Patch.create(:DATA) +    assert_kind_of IOPatch, patch +    assert_equal :p1, patch.strip +  end + +  def test_raises_for_unknown_values +    assert_raises(ArgumentError) { Patch.create(Object.new) } +  end +end diff --git a/Library/Homebrew/test/test_software_spec.rb b/Library/Homebrew/test/test_software_spec.rb index c35294320..7c9fb8720 100644 --- a/Library/Homebrew/test/test_software_spec.rb +++ b/Library/Homebrew/test/test_software_spec.rb @@ -80,6 +80,12 @@ class SoftwareSpecTests < Test::Unit::TestCase      @spec.depends_on('foo' => :optional)      assert_equal 'blah', @spec.build.first.description    end + +  def test_patch +    @spec.patch :p1, :DATA +    assert_equal 1, @spec.patches.length +    assert_equal :p1, @spec.patches.first.strip +  end  end  class HeadSoftwareSpecTests < Test::Unit::TestCase | 
