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

class FormulaInstaller
  @@attempted = Set.new

  def initialize
    @install_deps = true
  end

  attr_writer :install_deps

  def 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
Unsatisfied dependency, #{dep}
Homebrew does not provide formula for Python dependencies, pip does:

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

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

    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
  end

  def check_formula_deps f
    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
        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