aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/dependency_collector.rb
blob: 9ad93c268e427f96513d6044ccca4a8f993e3e80 (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
require "dependency"
require "dependencies"
require "requirement"
require "requirements"
require "set"
require "extend/cachable"

## A dependency is a formula that another formula needs to install.
## A requirement is something other than a formula that another formula
## needs to be present. This includes external language modules,
## command-line tools in the path, or any arbitrary predicate.
##
## The `depends_on` method in the formula DSL is used to declare
## dependencies and requirements.

# This class is used by `depends_on` in the formula DSL to turn dependency
# specifications into the proper kinds of dependencies and requirements.
class DependencyCollector
  extend Cachable

  attr_reader :deps, :requirements

  def initialize
    @deps = Dependencies.new
    @requirements = Requirements.new
  end

  def add(spec)
    case dep = fetch(spec)
    when Dependency
      @deps << dep
    when Requirement
      @requirements << dep
    end
    dep
  end

  def fetch(spec)
    self.class.cache.fetch(cache_key(spec)) { |key| self.class.cache[key] = build(spec) }
  end

  def cache_key(spec)
    if spec.is_a?(Resource) && spec.download_strategy == CurlDownloadStrategy
      File.extname(spec.url)
    else
      spec
    end
  end

  def build(spec)
    spec, tags = spec.is_a?(Hash) ? spec.first : spec
    parse_spec(spec, Array(tags))
  end

  def cvs_dep_if_needed(tags)
    Dependency.new("cvs", tags)
  end

  def xz_dep_if_needed(tags)
    Dependency.new("xz", tags)
  end

  def ld64_dep_if_needed(*); end

  def self.tar_needs_xz_dependency?
    !new.xz_dep_if_needed([]).nil?
  end

  private

  def parse_spec(spec, tags)
    case spec
    when String
      parse_string_spec(spec, tags)
    when Resource
      resource_dep(spec, tags)
    when Symbol
      parse_symbol_spec(spec, tags)
    when Requirement, Dependency
      spec
    when Class
      parse_class_spec(spec, tags)
    else
      raise TypeError, "Unsupported type #{spec.class.name} for #{spec.inspect}"
    end
  end

  def parse_string_spec(spec, tags)
    if spec =~ HOMEBREW_TAP_FORMULA_REGEX
      TapDependency.new(spec, tags)
    elsif tags.empty?
      Dependency.new(spec, tags)
    else
      Dependency.new(spec, tags)
    end
  end

  def parse_symbol_spec(spec, tags)
    case spec
    when :x11        then X11Requirement.new(spec.to_s, tags)
    when :xcode      then XcodeRequirement.new(tags)
    when :linux      then LinuxRequirement.new(tags)
    when :macos      then MacOSRequirement.new(tags)
    when :arch       then ArchRequirement.new(tags)
    when :java       then JavaRequirement.new(tags)
    when :osxfuse    then OsxfuseRequirement.new(tags)
    when :tuntap     then TuntapRequirement.new(tags)
    when :ld64       then ld64_dep_if_needed(tags)
    else
      raise ArgumentError, "Unsupported special dependency #{spec.inspect}"
    end
  end

  def parse_class_spec(spec, tags)
    unless spec < Requirement
      raise TypeError, "#{spec.inspect} is not a Requirement subclass"
    end

    spec.new(tags)
  end

  def resource_dep(spec, tags)
    tags << :build
    strategy = spec.download_strategy

    if strategy <= CurlDownloadStrategy
      parse_url_spec(spec.url, tags)
    elsif strategy <= GitDownloadStrategy
      GitRequirement.new(tags)
    elsif strategy <= SubversionDownloadStrategy
      SubversionRequirement.new(tags)
    elsif strategy <= MercurialDownloadStrategy
      Dependency.new("mercurial", tags)
    elsif strategy <= FossilDownloadStrategy
      Dependency.new("fossil", tags)
    elsif strategy <= BazaarDownloadStrategy
      Dependency.new("bazaar", tags)
    elsif strategy <= CVSDownloadStrategy
      cvs_dep_if_needed(tags)
    elsif strategy < AbstractDownloadStrategy
      # allow unknown strategies to pass through
    else
      raise TypeError,
        "#{strategy.inspect} is not an AbstractDownloadStrategy subclass"
    end
  end

  def parse_url_spec(url, tags)
    case File.extname(url)
    when ".xz"          then xz_dep_if_needed(tags)
    when ".lha", ".lzh" then Dependency.new("lha", tags)
    when ".lz"          then Dependency.new("lzip", tags)
    when ".rar"         then Dependency.new("unrar", tags)
    when ".7z"          then Dependency.new("p7zip", tags)
    end
  end
end

require "extend/os/dependency_collector"