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, "..."
This commit is contained in:
parent
f36e676bc9
commit
bc6e4a1894
@ -213,26 +213,36 @@ class ExampleFormula < Formula
|
|||||||
|
|
||||||
## Patches
|
## Patches
|
||||||
|
|
||||||
# Optionally define a `patches` method returning `DATA` and/or a string with
|
# External patches can be declared using resource-style blocks.
|
||||||
# the url to a patch or a list thereof.
|
patch do
|
||||||
def patches
|
url "https://example.com/example_patch.diff"
|
||||||
# DATA is the embedded diff that comes after __END__ in this file!
|
sha1 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
# In this example we only need the patch for older clang than 425:
|
|
||||||
DATA unless MacOS.clang_build_version >= 425
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# More than the one embedded patch? Use a dict with keys :p0, :p1 and/or
|
# A strip level of -p1 is assumed. It can be overridden using a symbol
|
||||||
# p2: as you need, for example:
|
# argument:
|
||||||
def patches
|
patch :p0 do
|
||||||
{:p0 => [
|
url "https://example.com/example_patch.diff"
|
||||||
'https://trac.macports.org/export/yeah/we/often/steal/a/patch.diff',
|
sha1 "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
|
||||||
'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']
|
|
||||||
}
|
|
||||||
end
|
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.
|
## The install method.
|
||||||
|
|
||||||
|
@ -743,6 +743,10 @@ class Formula
|
|||||||
specs.each { |spec| spec.option(name, description) }
|
specs.each { |spec| spec.option(name, description) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def patch strip=:p1, io=nil, &block
|
||||||
|
specs.each { |spec| spec.patch(strip, io, &block) }
|
||||||
|
end
|
||||||
|
|
||||||
def plist_options options
|
def plist_options options
|
||||||
@plist_startup = options[:startup]
|
@plist_startup = options[:startup]
|
||||||
@plist_manual = options[:manual]
|
@plist_manual = options[:manual]
|
||||||
|
82
Library/Homebrew/patch.rb
Normal file
82
Library/Homebrew/patch.rb
Normal file
@ -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
|
@ -8,13 +8,12 @@ require 'version'
|
|||||||
class Resource
|
class Resource
|
||||||
include FileUtils
|
include FileUtils
|
||||||
|
|
||||||
attr_reader :name
|
|
||||||
attr_reader :checksum, :mirrors, :specs, :using
|
attr_reader :checksum, :mirrors, :specs, :using
|
||||||
attr_writer :url, :checksum, :version
|
attr_writer :url, :checksum, :version
|
||||||
|
|
||||||
# Formula name must be set after the DSL, as we have no access to the
|
# Formula name must be set after the DSL, as we have no access to the
|
||||||
# formula name before initialization of the formula
|
# formula name before initialization of the formula
|
||||||
attr_accessor :owner
|
attr_accessor :name, :owner
|
||||||
|
|
||||||
def initialize name=nil, &block
|
def initialize name=nil, &block
|
||||||
@name = name
|
@name = name
|
||||||
|
@ -5,12 +5,13 @@ require 'version'
|
|||||||
require 'build_options'
|
require 'build_options'
|
||||||
require 'dependency_collector'
|
require 'dependency_collector'
|
||||||
require 'bottles'
|
require 'bottles'
|
||||||
|
require 'patch'
|
||||||
|
|
||||||
class SoftwareSpec
|
class SoftwareSpec
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|
||||||
attr_reader :name
|
attr_reader :name, :owner
|
||||||
attr_reader :build, :resources, :owner
|
attr_reader :build, :resources, :patches
|
||||||
attr_reader :dependency_collector
|
attr_reader :dependency_collector
|
||||||
attr_reader :bottle_specification
|
attr_reader :bottle_specification
|
||||||
|
|
||||||
@ -25,6 +26,7 @@ class SoftwareSpec
|
|||||||
@build = BuildOptions.new(ARGV.options_only)
|
@build = BuildOptions.new(ARGV.options_only)
|
||||||
@dependency_collector = DependencyCollector.new
|
@dependency_collector = DependencyCollector.new
|
||||||
@bottle_specification = BottleSpecification.new
|
@bottle_specification = BottleSpecification.new
|
||||||
|
@patches = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def owner= owner
|
def owner= owner
|
||||||
@ -35,6 +37,7 @@ class SoftwareSpec
|
|||||||
r.owner = self
|
r.owner = self
|
||||||
r.version ||= version
|
r.version ||= version
|
||||||
end
|
end
|
||||||
|
patches.each { |p| p.owner = self }
|
||||||
end
|
end
|
||||||
|
|
||||||
def url val=nil, specs={}
|
def url val=nil, specs={}
|
||||||
@ -84,6 +87,10 @@ class SoftwareSpec
|
|||||||
def requirements
|
def requirements
|
||||||
dependency_collector.requirements
|
dependency_collector.requirements
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def patch strip=:p1, io=nil, &block
|
||||||
|
patches << Patch.create(strip, io, &block)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class HeadSoftwareSpec < SoftwareSpec
|
class HeadSoftwareSpec < SoftwareSpec
|
||||||
|
52
Library/Homebrew/test/test_patch.rb
Normal file
52
Library/Homebrew/test/test_patch.rb
Normal file
@ -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
|
@ -80,6 +80,12 @@ class SoftwareSpecTests < Test::Unit::TestCase
|
|||||||
@spec.depends_on('foo' => :optional)
|
@spec.depends_on('foo' => :optional)
|
||||||
assert_equal 'blah', @spec.build.first.description
|
assert_equal 'blah', @spec.build.first.description
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_patch
|
||||||
|
@spec.patch :p1, :DATA
|
||||||
|
assert_equal 1, @spec.patches.length
|
||||||
|
assert_equal :p1, @spec.patches.first.strip
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class HeadSoftwareSpecTests < Test::Unit::TestCase
|
class HeadSoftwareSpecTests < Test::Unit::TestCase
|
||||||
|
Loading…
x
Reference in New Issue
Block a user