aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/formula_installer.rb
blob: 3fceb01b8156a2ec4efe1a649149e0ac706cf423 (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
require 'formula'
require 'set'

class FormulaInstaller
  @@attempted = Set.new

  def initialize
    @install_deps = true
  end

  attr_writer :install_deps

  def self.expand_deps f
    deps = []
    f.deps.collect do |dep|
      dep = Formula.factory dep
      deps += expand_deps dep
      deps << dep
    end
    deps
  end

  def pyerr dep
    brew_pip = ' brew install pip &&' unless Formula.factory('pip').installed?
    <<-EOS.undent
    Unsatisfied dependency, #{dep}
    Homebrew does not provide Python dependencies, pip does:

        #{brew_pip} pip install #{dep}
    EOS
  end
  def plerr dep; <<-EOS.undent
    Unsatisfied dependency, #{dep}
    Homebrew does not provide Perl dependencies, cpan does:

        cpan -i #{dep}
    EOS
  end
  def rberr dep; <<-EOS.undent
    Unsatisfied dependency "#{dep}"
    Homebrew does not provide Ruby dependencies, rubygems does:

        gem install #{dep}
    EOS
  end
  def jrberr dep; <<-EOS.undent
    Unsatisfied dependency "#{dep}"
    Homebrew does not provide JRuby dependencies, rubygems does:

        jruby -S gem install #{dep}
    EOS
  end

  def check_external_deps f
    return unless f.external_deps

    f.external_deps[:python].each do |dep|
      raise pyerr(dep) unless quiet_system "/usr/bin/env", "python", "-c", "import #{dep}"
    end
    f.external_deps[:perl].each do |dep|
      raise plerr(dep) unless quiet_system "/usr/bin/env", "perl", "-e", "use #{dep}"
    end
    f.external_deps[:ruby].each do |dep|
      raise rberr(dep) unless quiet_system "/usr/bin/env", "ruby", "-rubygems", "-e", "require '#{dep}'"
    end
    f.external_deps[:jruby].each do |dep|
      raise jrberr(dep) unless quiet_system "/usr/bin/env", "jruby", "-rubygems", "-e", "require '#{dep}'"
    end
  end

  def check_formula_deps f
    FormulaInstaller.expand_deps(f).each do |dep|
      begin
        install_private dep unless dep.installed?
      rescue
        #TODO continue if this is an optional dep
        raise
      end
    end
  end

  def install f
    if @install_deps
      check_external_deps f
      check_formula_deps f
    end
    install_private f
  end

  private

  def install_private f
    return if @@attempted.include? f.name
    @@attempted << f.name

    # 1. formulae can modify ENV, so we must ensure that each
    #    installation has a pristine ENV when it starts, forking now is 
    #    the easiest way to do this
    # 2. formulae have access to __END__ the only way to allow this is
    #    to make the formula script the executed script
    read, write = IO.pipe
    # I'm guessing this is not a good way to do this, but I'm no UNIX guru
    ENV['HOMEBREW_ERROR_PIPE'] = write.to_i.to_s

     begin #watch_out_for_spill do #disabled temporarily, see Issue #124
      fork do
        begin
          read.close
          exec '/usr/bin/nice', '/usr/bin/ruby', '-I', File.dirname(__FILE__), '-rinstall', f.path, '--', *ARGV.options_only
        rescue => e
          Marshal.dump(e, write)
          write.close
          exit! 1
        end
      end
      ignore_interrupts do # because child proc will get it and marshall it back
        write.close
        Process.wait
        data = read.read
        raise Marshal.load(data) unless data.nil? or data.empty?
        raise "Suspicious installation failure" unless $?.success?
      end
    end
  end
end