aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/patch.rb
blob: dbb2da8432df17222604ee78b601e79e43e13120 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
require 'resource'
require 'stringio'
require 'erb'

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

  def self.normalize_legacy_patches(list)
    patches = []

    case list
    when Hash
      list
    when Array, String, IO, StringIO
      { :p1 => list }
    else
      {}
    end.each_pair do |strip, urls|
      urls = [urls] unless Array === urls
      urls.each do |url|
        case url
        when IO, StringIO
          patch = IOPatch.new(url, strip)
        else
          patch = LegacyPatch.new(strip, url)
        end
        patches << patch
      end
    end

    patches
  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

  def inspect
    "#<#{self.class}: #{strip.inspect}>"
  end
end

class ExternalPatch < Patch
  attr_reader :resource, :strip

  def initialize(strip, &block)
    @strip    = strip
    @resource = Resource.new("patch", &block)
    @whence   = :resource
  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
      # 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

  def url
    resource.url
  end

  def fetch
    resource.fetch
  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}: #{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
  end

  def owner= owner
    super
    resource.version = ERB::Util.url_encode(resource.url)
  end

  def fetch
    clear_cache
    super
  end

  def verify_download_integrity(fn)
    # no-op
  end

  def apply
    super
  ensure
    clear_cache
  end
end