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
  | 
require 'download_strategy'
require 'checksum'
require 'version'
# Resource is the fundamental representation of an external resource. The
# primary formula download, along with other declared resources, are instances
# of this class.
class Resource
  include FileUtils
  attr_reader :checksum, :mirrors, :specs, :using
  attr_writer :url, :checksum, :version, :download_strategy
  # Formula name must be set after the DSL, as we have no access to the
  # formula name before initialization of the formula
  attr_accessor :name, :owner
  def initialize name=nil, &block
    @name = name
    @url = nil
    @version = nil
    @mirrors = []
    @specs = {}
    @checksum = nil
    @using = nil
    instance_eval(&block) if block_given?
  end
  def downloader
    @downloader ||= download_strategy.new(download_name, self)
  end
  def download_name
    name.nil? ? owner.name : "#{owner.name}--#{name}"
  end
  def download_strategy
    @download_strategy ||= DownloadStrategyDetector.detect(url, using)
  end
  def cached_download
    downloader.cached_location
  end
  def clear_cache
    downloader.clear_cache
  end
  # Fetch, verify, and unpack the resource
  def stage(target=nil, &block)
    verify_download_integrity(fetch)
    unpack(target, &block)
  end
  # If a target is given, unpack there; else unpack to a temp folder
  # If block is given, yield to that block
  # A target or a block must be given, but not both
  def unpack(target=nil)
    mktemp(download_name) do
      downloader.stage
      if block_given?
        yield self
      elsif target
        target = Pathname.new(target) unless target.is_a? Pathname
        target.install Dir['*']
      end
    end
  end
  Partial = Struct.new(:resource, :files)
  def files(*files)
    Partial.new(self, files)
  end
  # For brew-fetch and others.
  def fetch
    # Ensure the cache exists
    HOMEBREW_CACHE.mkpath
    downloader.fetch
  rescue ErrorDuringExecution, CurlDownloadStrategyError => e
    raise DownloadError.new(self, e)
  else
    cached_download
  end
  def verify_download_integrity fn
    if fn.respond_to?(:file?) && fn.file?
      ohai "Verifying #{fn.basename} checksum" if ARGV.verbose?
      fn.verify_checksum(checksum)
    end
  rescue ChecksumMissingError
    opoo "Cannot verify integrity of #{fn.basename}"
    puts "A checksum was not provided for this resource"
    puts "For your reference the SHA1 is: #{fn.sha1}"
  end
  Checksum::TYPES.each do |type|
    define_method(type) { |val| @checksum = Checksum.new(type, val) }
  end
  def url val=nil, specs={}
    return @url if val.nil?
    @url = val
    @specs.merge!(specs)
    @using = @specs.delete(:using)
  end
  def version val=nil
    @version ||= detect_version(val)
  end
  def mirror val
    mirrors << val
  end
  private
  def detect_version(val)
    case val
    when nil     then Version.detect(url, specs)
    when String  then Version.new(val)
    when Version then val
    else
      raise TypeError, "version '#{val.inspect}' should be a string"
    end
  end
end
  |