mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00

The "apply" DSL method can be called from patch-do blocks to specify the paths within an archive of the desired patch files, which will be applied in the order in which they were supplied to the "apply" calls. If "apply" isn't used, raise an error whenever the extracted directory doesn't contain exactly one file. The "apply" method can be called zero or more times within a patch-do block with the following syntaxes supported: apply "single_apply" apply "multiple_apply_1", "multiple_apply_2" apply [array_of_apply] If apply must be used, a single call using the second syntax above is usually best practice. Each apply leaf should be the relative path to a specific patch file in the extracted directory. For example, if extracting this-v123-patches.tar.gz gives you this-123 this-123/.DS_Store this-123/LICENSE.txt this-123/patches this-123/patches/A.diff this-123/patches/B.diff this-123/patches/C.diff this-123/README.txt and you want to apply only B.diff and C.diff, then you need to use "patches/B.diff" and "patches/C.diff" for the lowest-level apply leaves. The code was provided by Xu Cheng. Any mistakes are mine.
204 lines
3.7 KiB
Ruby
204 lines
3.7 KiB
Ruby
require "resource"
|
|
require "erb"
|
|
|
|
module Patch
|
|
def self.create(strip, src, &block)
|
|
case strip
|
|
when :DATA
|
|
DATAPatch.new(:p1)
|
|
when String
|
|
StringPatch.new(:p1, strip)
|
|
when Symbol
|
|
case src
|
|
when :DATA
|
|
DATAPatch.new(strip)
|
|
when String
|
|
StringPatch.new(strip, src)
|
|
else
|
|
ExternalPatch.new(strip, &block)
|
|
end
|
|
else
|
|
raise ArgumentError, "unexpected value #{strip.inspect} for strip"
|
|
end
|
|
end
|
|
|
|
def self.normalize_legacy_patches(list)
|
|
patches = []
|
|
|
|
case list
|
|
when Hash
|
|
list
|
|
when Array, String, :DATA
|
|
{ :p1 => list }
|
|
else
|
|
{}
|
|
end.each_pair do |strip, urls|
|
|
Array(urls).each do |url|
|
|
case url
|
|
when :DATA
|
|
patch = DATAPatch.new(strip)
|
|
else
|
|
patch = LegacyPatch.new(strip, url)
|
|
end
|
|
patches << patch
|
|
end
|
|
end
|
|
|
|
patches
|
|
end
|
|
end
|
|
|
|
class EmbeddedPatch
|
|
attr_writer :owner
|
|
attr_reader :strip
|
|
|
|
def initialize(strip)
|
|
@strip = strip
|
|
end
|
|
|
|
def external?
|
|
false
|
|
end
|
|
|
|
def contents
|
|
end
|
|
|
|
def apply
|
|
data = contents.gsub("HOMEBREW_PREFIX", HOMEBREW_PREFIX)
|
|
cmd = "/usr/bin/patch"
|
|
args = %W[-g 0 -f -#{strip}]
|
|
IO.popen("#{cmd} #{args.join(" ")}", "w") { |p| p.write(data) }
|
|
raise ErrorDuringExecution.new(cmd, args) unless $?.success?
|
|
end
|
|
|
|
def inspect
|
|
"#<#{self.class.name}: #{strip.inspect}>"
|
|
end
|
|
end
|
|
|
|
class DATAPatch < EmbeddedPatch
|
|
attr_accessor :path
|
|
|
|
def initialize(strip)
|
|
super
|
|
@path = nil
|
|
end
|
|
|
|
def contents
|
|
data = ""
|
|
path.open("rb") do |f|
|
|
begin
|
|
line = f.gets
|
|
end until line.nil? || /^__END__$/ === line
|
|
data << line while line = f.gets
|
|
end
|
|
data
|
|
end
|
|
end
|
|
|
|
class StringPatch < EmbeddedPatch
|
|
def initialize(strip, str)
|
|
super(strip)
|
|
@str = str
|
|
end
|
|
|
|
def contents
|
|
@str
|
|
end
|
|
end
|
|
|
|
class ExternalPatch
|
|
attr_reader :resource, :strip
|
|
|
|
def initialize(strip, &block)
|
|
@strip = strip
|
|
@resource = Resource::Patch.new(&block)
|
|
end
|
|
|
|
def external?
|
|
true
|
|
end
|
|
|
|
def owner=(owner)
|
|
resource.owner = owner
|
|
resource.version = resource.checksum || ERB::Util.url_encode(resource.url)
|
|
end
|
|
|
|
def apply
|
|
dir = Pathname.pwd
|
|
resource.unpack do
|
|
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
|
|
|
|
def url
|
|
resource.url
|
|
end
|
|
|
|
def fetch
|
|
resource.fetch
|
|
end
|
|
|
|
def patch_files
|
|
resource.patch_files
|
|
end
|
|
|
|
def verify_download_integrity(fn)
|
|
resource.verify_download_integrity(fn)
|
|
end
|
|
|
|
def cached_download
|
|
resource.cached_download
|
|
end
|
|
|
|
def clear_cache
|
|
resource.clear_cache
|
|
end
|
|
|
|
def inspect
|
|
"#<#{self.class.name}: #{strip.inspect} #{url.inspect}>"
|
|
end
|
|
end
|
|
|
|
# Legacy patches have no checksum and are not cached
|
|
class LegacyPatch < ExternalPatch
|
|
def initialize(strip, url)
|
|
super(strip)
|
|
resource.url(url)
|
|
resource.download_strategy = CurlDownloadStrategy
|
|
end
|
|
|
|
def fetch
|
|
clear_cache
|
|
super
|
|
end
|
|
|
|
def verify_download_integrity(_fn)
|
|
# no-op
|
|
end
|
|
|
|
def apply
|
|
super
|
|
ensure
|
|
clear_cache
|
|
end
|
|
end
|