diff options
48 files changed, 1284 insertions, 1238 deletions
| diff --git a/Library/Contributions/examples/brew-missing.rb b/Library/Contributions/examples/brew-missing.rb index 68206278c..701bb1ca2 100755 --- a/Library/Contributions/examples/brew-missing.rb +++ b/Library/Contributions/examples/brew-missing.rb @@ -1,21 +1,18 @@  require "formula" -require 'formula_installer' +require "cmd/outdated"  def main    # Names of outdated brews; they count as installed. -  outdated = outdated_brews.collect {|b| b[1]} +  outdated = Homebrew.outdated_brews.collect{ |b| b[1] } -  HOMEBREW_CELLAR.subdirs.each do |keg| -    next unless keg.subdirs -    if ((f = Formula.factory(keg.basename.to_s)).installed? rescue false) -      f_deps = FormulaInstaller.expand_deps(f).collect{|g| g.name}.uniq -      next if f_deps.empty? - -      missing_deps = f_deps.reject do |dep_name| +  HOMEBREW_CELLAR.subdirs.each do |rack| +    f = Formula.factory rack.basename.to_s rescue nil +    if f and f.installed? +      missing_deps = f.recursive_deps.map{ |g| g.name }.uniq.reject do |dep_name|          Formula.factory(dep_name).installed? or outdated.include?(dep_name)        end -      puts "#{f.name}: #{missing_deps.join(', ')}" unless missing_deps.empty? +      puts "#{f.name}: #{missing_deps * ', '}" unless missing_deps.empty?      end    end  end diff --git a/Library/Contributions/examples/brew-server b/Library/Contributions/examples/brew-server index b9919cf7a..5e7f80b58 100755 --- a/Library/Contributions/examples/brew-server +++ b/Library/Contributions/examples/brew-server @@ -82,7 +82,6 @@ get '/' do  end  get '/search' do -  require 'brew.h'    q = params['q']    results = search_brews(q) diff --git a/Library/Contributions/examples/brew-upgrade.rb b/Library/Contributions/examples/brew-upgrade.rb index 6c7831fde..3ffb570ac 100755 --- a/Library/Contributions/examples/brew-upgrade.rb +++ b/Library/Contributions/examples/brew-upgrade.rb @@ -1,11 +1,7 @@  # Updates all outdated brews  # See: http://github.com/mxcl/homebrew/issues/issue/1324 -# patch ARGV to use all of the outdated packages as the names passed in -module HomebrewArgvExtension -  def formulae -    @formulae = outdated_brews.map {|_keg, name, _version| Formula.factory name} -  end -end +require 'cmd/outdated' +require 'cmd/install' -brew_install +Homebrew.install_formulae Homebrew.outdated_brews.map{ |_keg, name, _version| Formula.factory name } diff --git a/Library/Homebrew/beer_events.rb b/Library/Homebrew/beer_events.rb deleted file mode 100644 index 18d0d0a6c..000000000 --- a/Library/Homebrew/beer_events.rb +++ /dev/null @@ -1,199 +0,0 @@ -# Vendored from Rucola: http://github.com/alloy/rucola/tree/master -# -# Copyright (c) 2007, 2008, 2009 Eloy Duran <eloy.de.enige@gmail.com> -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, -# copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following -# conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -begin -  require 'osx/cocoa' -  OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework' -   -  module Rucola -    class FSEvents -      class FSEvent -        attr_reader :fsevents_object -        attr_reader :id -        attr_reader :path -        def initialize(fsevents_object, id, path) -          @fsevents_object, @id, @path = fsevents_object, id, path -        end -         -        # Returns an array of the files/dirs in the path that the event occurred in. -        # The files are sorted by the modification time, the first entry is the last modified file. -        def files -          Dir.glob("#{File.expand_path(path)}/*").sort_by {|f| File.mtime(f) }.reverse -        end -         -        # Returns the last modified file in the path that the event occurred in. -        def last_modified_file -          files.first -        end -      end -       -      class StreamError < StandardError; end -       -      attr_reader :paths -      attr_reader :stream -       -      attr_accessor :allocator -      attr_accessor :context -      attr_accessor :since -      attr_accessor :latency -      attr_accessor :flags -       -      # Initializes a new FSEvents `watchdog` object and starts watching the directories you specify for events. The -      # block is used as a handler for events, which are passed as the block's argument. This method is the easiest -      # way to start watching some directories if you don't care about the details of setting up the event stream. -      # -      #   Rucola::FSEvents.start_watching('/tmp') do |events| -      #     events.each { |event| log.debug("#{event.files.inspect} were changed.") } -      #   end -      #    -      #   Rucola::FSEvents.start_watching('/var/log/system.log', '/var/log/secure.log', :since => last_id, :latency => 5) do -      #     Growl.notify("Something was added to your log files!") -      #   end -      # -      # Note that the method also returns the FSEvents object. This enables you to control the event stream if you want to. -      # -      #   fsevents = Rucola::FSEvents.start_watching('/Volumes') do |events| -      #     events.each { |event| Growl.notify("Volume changes: #{event.files.to_sentence}") } -      #   end -      #   fsevents.stop -      def self.start_watching(*params, &block) -        fsevents = new(*params, &block) -        fsevents.create_stream -        fsevents.start -        fsevents -      end -       -      # Creates a new FSEvents `watchdog` object. You can specify a list of paths to watch and options to control the -      # behaviour of the watchdog. The block you pass serves as a callback when an event is generated on one of the -      # specified paths. -      # -      #   fsevents = FSEvents.new('/etc/passwd') { Mailer.send_mail("Someone touched the password file!") } -      #   fsevents.create_stream -      #   fsevents.start -      # -      #   fsevents = FSEvents.new('/home/upload', :since => UploadWatcher.last_event_id) do |events| -      #     events.each do |event| -      #       UploadWatcher.last_event_id = event.id -      #       event.files.each do |file| -      #         UploadWatcher.logfile.append("#{file} was changed") -      #       end -      #     end -      #   end -      # -      # *:since: The service will report events that have happened after the supplied event ID. Never use 0 because that  -      #   will cause every fsevent since the "beginning of time" to be reported. Use OSX::KFSEventStreamEventIdSinceNow -      #   if you want to receive events that have happened after this call. (Default: OSX::KFSEventStreamEventIdSinceNow). -      #   You can find the ID's passed with :since in the events passed to your block. -      # *:latency: Number of seconds to wait until an FSEvent is reported, this allows the service to bundle events. (Default: 0.0) -      # -      # Please refer to the Cocoa documentation for the rest of the options. -      def initialize(*params, &block) -        raise ArgumentError, 'No callback block was specified.' unless block_given? -         -        options = params.last.kind_of?(Hash) ? params.pop : {} -        @paths = params.flatten -         -        paths.each { |path| raise ArgumentError, "The specified path (#{path}) does not exist." unless File.exist?(path) } -         -        @allocator = options[:allocator] || OSX::KCFAllocatorDefault -        @context   = options[:context]   || nil -        @since     = options[:since]     || OSX::KFSEventStreamEventIdSinceNow -        @latency   = options[:latency]   || 0.0 -        @flags     = options[:flags]     || 0 -        @stream    = options[:stream]    || nil -         -        @user_callback = block -        @callback = Proc.new do |stream, client_callback_info, number_of_events, paths_pointer, event_flags, event_ids| -          paths_pointer.regard_as('*') -          events = [] -          number_of_events.times {|i| events << Rucola::FSEvents::FSEvent.new(self, event_ids[i], paths_pointer[i]) } -          @user_callback.call(events) -        end -      end -       -      # Create the stream. -      # Raises a Rucola::FSEvents::StreamError if the stream could not be created. -      def create_stream -        @stream = OSX.FSEventStreamCreate(@allocator, @callback, @context, @paths, @since, @latency, @flags) -        raise(StreamError, 'Unable to create FSEvents stream.') unless @stream -        OSX.FSEventStreamScheduleWithRunLoop(@stream, OSX.CFRunLoopGetCurrent, OSX::KCFRunLoopDefaultMode) -      end -       -      # Start the stream. -      # Raises a Rucola::FSEvents::StreamError if the stream could not be started. -      def start -        raise(StreamError, 'Unable to start FSEvents stream.') unless OSX.FSEventStreamStart(@stream) -      end -       -      # Stop the stream. -      # You can resume it by calling `start` again. -      def stop -        OSX.FSEventStreamStop(@stream) -      end -    end -  end -   -  # The complete BeerEvents API :) -  HOMEBREW_KEEP_DRY = %w{ /System /usr /etc /sbin /bin /Applications /Library } -   -  def watch_out_for_spill -    # Disable the RubyCocoa thread hook as apparently Laurent did not apply the -    # thread patches to the OS X system Ruby -    ENV['RUBYCOCOA_THREAD_HOOK_DISABLE'] = 'kampai' -     -    Thread.new { OSX.CFRunLoopRun() } -     -    start = Time.now -    dog = Rucola::FSEvents.start_watching(*HOMEBREW_KEEP_DRY) do |events| -      spill = events.map { |e| e.files }.flatten -      spill.reject! { |f| File.mtime(f) < start } -      spill.reject! { |path| path =~ /^#{HOMEBREW_PREFIX}/ } -      # irrelevent files that change a lot -      spill.reject! { |path| path == "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist-lock" } -      spill.reject! { |path| path =~ %r{^(/System)?/Library/Caches/} } -      unless spill.empty? -        opoo "Detected installation of files outside the Homebrew prefix:" -        puts *spill -      end -    end -    yield -  ensure -    dog.stop -  end -rescue LoadError => e -  onoe "RubyCocoa could not be loaded, therefore checking for spill is disabled." -  puts "When using a custom Ruby installation, you'll need to install RubyCocoa." -  puts "If this is not the case, see if the following ticket applies, or create one." -  puts "  http://github.com/mxcl/homebrew/issues#issue/37" -   -  if ARGV.verbose? -    onoe e.message -    puts e.backtrace -  end -   -  def watch_out_for_spill -    yield -  end -end
\ No newline at end of file diff --git a/Library/Homebrew/brew.h.rb b/Library/Homebrew/brew.h.rb deleted file mode 100644 index c4bfd1de1..000000000 --- a/Library/Homebrew/brew.h.rb +++ /dev/null @@ -1,600 +0,0 @@ -FORMULA_META_FILES = %w[README README.md ChangeLog COPYING LICENSE LICENCE COPYRIGHT AUTHORS] -PLEASE_REPORT_BUG = "#{Tty.white}Please follow the instructions to report this bug at: #{Tty.em}\nhttps://github.com/mxcl/homebrew/wiki/new-issue#{Tty.reset}" - -def check_for_blacklisted_formula names -  return if ARGV.force? - -  names.each do |name| -    case name -    when 'tex', 'tex-live', 'texlive' then abort <<-EOS.undent -      Installing TeX from source is weird and gross, requires a lot of patches, -      and only builds 32-bit (and thus can't use Homebrew deps on Snow Leopard.) - -      We recommend using a MacTeX distribution: -        http://www.tug.org/mactex/ -    EOS - -    when 'mercurial', 'hg' then abort <<-EOS.undent -      Mercurial can be install thusly: -        brew install pip && pip install mercurial -    EOS - -    when 'npm' then abort <<-EOS.undent -      npm can be installed thusly by following the instructions at -        http://npmjs.org/ - -      To do it in one line, use this command: -        curl http://npmjs.org/install.sh | sh -    EOS - - -    when 'setuptools' then abort <<-EOS.undent -      When working with a Homebrew-built Python, distribute is preferred -      over setuptools, and can be used as the prerequisite for pip. - -      Install distribute using: -        brew install distribute -    EOS -    end -  end -end - -def __make url, name -  require 'formula' -  require 'digest' -  require 'erb' - -  path = Formula.path(name) -  raise "#{path} already exists" if path.exist? - -  if Formula.aliases.include? name and not ARGV.force? -    realname = Formula.resolve_alias(name) -    raise <<-EOS.undent -          "#{name}" is an alias for formula "#{realname}". -          Please check that you are not creating a duplicate. -          To force creation use --force. -          EOS -  end - -  if ARGV.include? '--cmake' -    mode = :cmake -  elsif ARGV.include? '--autotools' -    mode = :autotools -  else -    mode = nil -  end - -  version = Pathname.new(url).version -  if version.nil? -    opoo "Version cannot be determined from URL." -    puts "You'll need to add an explicit 'version' to the formula." -  else -    puts "Version detected as #{version}." -  end - -  md5 = '' -  if ARGV.include? "--cache" and version != nil -    strategy = detect_download_strategy url -    if strategy == CurlDownloadStrategy -      d = strategy.new url, name, version, nil -      the_tarball = d.fetch -      md5 = the_tarball.md5 -      puts "MD5 is #{md5}" -    else -      puts "--cache requested, but we can only cache formulas that use Curl." -    end -  end - -  formula_template = <<-EOS -require 'formula' - -class #{Formula.class_s name} <Formula -  url '#{url}' -  homepage '' -  md5 '#{md5}' - -<% if mode == :cmake %> -  depends_on 'cmake' -<% elsif mode == nil %> -  # depends_on 'cmake' -<% end %> - -  def install -<% if mode == :cmake %> -    system "cmake . \#{std_cmake_parameters}" -<% elsif mode == :autotools %> -    system "./configure", "--disable-debug", "--disable-dependency-tracking", -                          "--prefix=\#{prefix}" -<% else %> -    system "./configure", "--disable-debug", "--disable-dependency-tracking", -                          "--prefix=\#{prefix}" -    # system "cmake . \#{std_cmake_parameters}" -<% end %> -    system "make install" -  end -end -  EOS - -  path.write(ERB.new(formula_template, nil, '>').result(binding)) -  return path -end - -def make url -  path = Pathname.new url - -  /(.*?)[-_.]?#{path.version}/.match path.basename - -  unless $1.to_s.empty? -    name = $1 -  else -    print "Formula name [#{path.stem}]: " -    gots = $stdin.gets.chomp -    if gots.empty? -      name = path.stem -    else -      name = gots -    end -  end - -  force_text = "If you really want to make this formula use --force." - -  case name.downcase -  when 'vim', 'screen' -    raise <<-EOS -#{name} is blacklisted for creation -Apple distributes this program with OS X. - -#{force_text} -    EOS -  when 'libarchive', 'libpcap' -    raise <<-EOS -#{name} is blacklisted for creation -Apple distributes this library with OS X, you can find it in /usr/lib. - -#{force_text} -    EOS -  when 'libxml', 'libxlst', 'freetype', 'libpng' -    raise <<-EOS -#{name} is blacklisted for creation -Apple distributes this library with OS X, you can find it in /usr/X11/lib. -However not all build scripts look here, so you may need to call ENV.x11 or -ENV.libxml2 in your formula's install function. - -#{force_text} -    EOS -  when 'rubygem' -    raise "Sorry RubyGems comes with OS X so we don't package it.\n\n#{force_text}" -  when 'wxwidgets' -    raise <<-EOS -#{name} is blacklisted for creation -An older version of wxWidgets is provided by Apple with OS X, but -a formula for wxWidgets 2.8.10 is provided: - -    brew install wxmac - -  #{force_text} -    EOS -  end unless ARGV.force? - -  __make url, name -end - -def github_info name -  formula_name = Formula.path(name).basename -  user = 'mxcl' -  branch = 'master' - -  if system "/usr/bin/which -s git" -    gh_user=`git config --global github.user 2>/dev/null`.chomp -    /^\*\s*(.*)/.match(`git --work-tree=#{HOMEBREW_REPOSITORY} branch 2>/dev/null`) -    unless $1.nil? || $1.empty? || gh_user.empty? -      branch = $1.chomp -      user = gh_user -    end -  end - -  return "http://github.com/#{user}/homebrew/commits/#{branch}/Library/Formula/#{formula_name}" -end - -def info f -  exec 'open', github_info(f.name) if ARGV.flag? '--github' - -  puts "#{f.name} #{f.version}" -  puts f.homepage - -  puts "Depends on: #{f.deps.join(', ')}" unless f.deps.empty? - -  if f.prefix.parent.directory? -    kids=f.prefix.parent.children -    kids.each do |keg| -      next if keg.basename.to_s == '.DS_Store' -      print "#{keg} (#{keg.abv})" -      print " *" if f.installed_prefix == keg and kids.length > 1 -      puts -    end -  else -    puts "Not installed" -  end - -  if f.caveats -    puts -    puts f.caveats -    puts -  end - -  history = github_info(f.name) -  puts history if history - -rescue FormulaUnavailableError -  # check for DIY installation -  d=HOMEBREW_PREFIX+name -  if d.directory? -    ohai "DIY Installation" -    d.children.each {|keg| puts "#{keg} (#{keg.abv})"} -  else -    raise "No such formula or keg" -  end -end - -def issues_for_formula name -  # bit basic as depends on the issue at github having the exact name of the -  # formula in it. Which for stuff like objective-caml is unlikely. So we -  # really should search for aliases too. - -  name = f.name if Formula === name - -  require 'open-uri' -  require 'yaml' - -  issues = [] - -  open("http://github.com/api/v2/yaml/issues/search/mxcl/homebrew/open/"+name) do |f| -    YAML::load(f.read)['issues'].each do |issue| -      issues << 'http://github.com/mxcl/homebrew/issues/#issue/%s' % issue['number'] -    end -  end - -  issues -rescue -  [] -end - -def cleanup name -  require 'formula' - -  f = Formula.factory name -  formula_cellar = f.prefix.parent - -  if f.installed? and formula_cellar.directory? -    kids = f.prefix.parent.children -    kids.each do |keg| -      next if f.installed_prefix == keg -      print "Uninstalling #{keg}..." -      FileUtils.rm_rf keg -      puts -    end -  else -    # If the cellar only has one version installed, don't complain -    # that we can't tell which one to keep. -    if formula_cellar.children.length > 1 -      opoo "Skipping #{name}: most recent version #{f.version} not installed" -    end -  end -end - -def clean f -  require 'cleaner' -  Cleaner.new f -  -  # Hunt for empty folders and nuke them unless they are -  # protected by f.skip_clean? -  # We want post-order traversal, so put the dirs in a stack -  # and then pop them off later. -  paths = [] -  f.prefix.find do |path| -    paths << path if path.directory? -  end - -  until paths.empty? do -    d = paths.pop -    if d.children.empty? and not f.skip_clean? d -      puts "rmdir: #{d} (empty)" if ARGV.verbose? -      d.rmdir -    end -  end -end - - -def prune -  $n=0 -  $d=0 - -  dirs=Array.new -  paths=%w[bin sbin etc lib include share].collect {|d| HOMEBREW_PREFIX+d} - -  paths.each do |path| -    path.find do |path| -      path.extend ObserverPathnameExtension -      if path.symlink? -        path.unlink unless path.resolved_path_exists? -      elsif path.directory? -        dirs<<path -      end -    end -  end - -  dirs.sort.reverse_each {|d| d.rmdir_if_possible} - -  if $n == 0 and $d == 0 -    puts "Nothing pruned" if ARGV.verbose? -  else -    # always showing symlinks text is deliberate -    print "Pruned #{$n} symbolic links " -    print "and #{$d} directories " if $d > 0 -    puts  "from #{HOMEBREW_PREFIX}" -  end -end - - -def diy -  path=Pathname.getwd - -  if ARGV.include? '--set-version' -    version=ARGV.next -  else -    version=path.version -    raise "Couldn't determine version, try --set-version" if version.to_s.empty? -  end -   -  if ARGV.include? '--set-name' -    name=ARGV.next -  else -    path.basename.to_s =~ /(.*?)-?#{version}/ -    if $1.nil? or $1.empty? -      name=path.basename -    else -      name=$1 -    end -  end - -  prefix=HOMEBREW_CELLAR+name+version - -  if File.file? 'CMakeLists.txt' -    "-DCMAKE_INSTALL_PREFIX=#{prefix}" -  elsif File.file? 'Makefile.am' -    "--prefix=#{prefix}" -  else -    raise "Couldn't determine build system" -  end -end - -def macports_or_fink_installed? -  # See these issues for some history: -  # http://github.com/mxcl/homebrew/issues/#issue/13 -  # http://github.com/mxcl/homebrew/issues/#issue/41 -  # http://github.com/mxcl/homebrew/issues/#issue/48 - -  %w[port fink].each do |ponk| -    path = `/usr/bin/which -s #{ponk}` -    return ponk unless path.empty? -  end - -  # we do the above check because macports can be relocated and fink may be -  # able to be relocated in the future. This following check is because if -  # fink and macports are not in the PATH but are still installed it can -  # *still* break the build -- because some build scripts hardcode these paths: -  %w[/sw/bin/fink /opt/local/bin/port].each do |ponk| -    return ponk if File.exist? ponk -  end - -  # finally, sometimes people make their MacPorts or Fink read-only so they -  # can quickly test Homebrew out, but still in theory obey the README's  -  # advise to rename the root directory. This doesn't work, many build scripts -  # error out when they try to read from these now unreadable directories. -  %w[/sw /opt/local].each do |path| -    path = Pathname.new(path) -    return path if path.exist? and not path.readable? -  end -   -  false -end - -def versions_of(keg_name) -  `/bin/ls #{HOMEBREW_CELLAR}/#{keg_name}`.collect { |version| version.strip }.reverse -end - - -def outdated_brews -  require 'formula' - -  results = [] -  HOMEBREW_CELLAR.subdirs.each do |keg| -    # Skip kegs with no versions installed -    next unless keg.subdirs - -    # Skip HEAD formulae, consider them "evergreen" -    next if keg.subdirs.collect{|p|p.basename.to_s}.include? "HEAD" - -    name = keg.basename.to_s -    if (not (f = Formula.factory(name)).installed? rescue nil) -      results << [keg, name, f.version] -    end -  end -  return results -end - -def search_brews text -  require "formula" - -  return Formula.names if text.to_s.empty? - -  rx = if text =~ %r{^/(.*)/$} -    Regexp.new($1) -  else -    /.*#{Regexp.escape text}.*/i -  end - -  aliases = Formula.aliases -  results = (Formula.names+aliases).grep rx - -  # Filter out aliases when the full name was also found -  results.reject do |alias_name| -    if aliases.include? alias_name -      results.include? Formula.resolve_alias(alias_name) -    end -  end -end - -def brew_install -  require 'formula_installer' -  require 'hardware' - -  ############################################################ sanity checks -  case Hardware.cpu_type when :ppc, :dunno -    abort "Sorry, Homebrew does not support your computer's CPU architecture.\n"+ -          "For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc" -  end - -  raise "Cannot write to #{HOMEBREW_CELLAR}" if HOMEBREW_CELLAR.exist? and not HOMEBREW_CELLAR.writable? -  raise "Cannot write to #{HOMEBREW_PREFIX}" unless HOMEBREW_PREFIX.writable? - -  ################################################################# warnings -  begin -    if MACOS_VERSION >= 10.6 -      opoo "You should upgrade to Xcode 3.2.3" if llvm_build < RECOMMENDED_LLVM -    else -      opoo "You should upgrade to Xcode 3.1.4" if (gcc_40_build < RECOMMENDED_GCC_40) or (gcc_42_build < RECOMMENDED_GCC_42) -    end -  rescue -    # the reason we don't abort is some formula don't require Xcode -    # TODO allow formula to declare themselves as "not needing Xcode" -    opoo "Xcode is not installed! Builds may fail!" -  end - -  if macports_or_fink_installed? -    opoo "It appears you have MacPorts or Fink installed." -    puts "Software installed with MacPorts and Fink are known to cause problems." -    puts "If you experience issues try uninstalling these tools." -  end - -  ################################################################# install! -  installer = FormulaInstaller.new -  installer.install_deps = !ARGV.include?('--ignore-dependencies') - -  ARGV.formulae.each do |f| -    if not f.installed? or ARGV.force? -      installer.install f -    else -      puts "Formula already installed: #{f.prefix}" -    end -  end -end - -########################################################## class PrettyListing -class PrettyListing -  def initialize path -    Pathname.new(path).children.sort{ |a,b| a.to_s.downcase <=> b.to_s.downcase }.each do |pn| -      case pn.basename.to_s -      when 'bin', 'sbin' -        pn.find { |pnn| puts pnn unless pnn.directory? } -      when 'lib' -        print_dir pn do |pnn| -          # dylibs have multiple symlinks and we don't care about them -          (pnn.extname == '.dylib' or pnn.extname == '.pc') and not pnn.symlink? -        end -      else -        if pn.directory? -          if pn.symlink? -            puts "#{pn} -> #{pn.readlink}" -          else -            print_dir pn -          end -        elsif not (FORMULA_META_FILES.include? pn.basename.to_s or pn.basename.to_s == '.DS_Store') -          puts pn -        end -      end -    end -  end - -private -  def print_dir root -    dirs = [] -    remaining_root_files = [] -    other = '' - -    root.children.sort.each do |pn| -      if pn.directory? -        dirs << pn -      elsif block_given? and yield pn -        puts pn -        other = 'other ' -      else -        remaining_root_files << pn unless pn.basename.to_s == '.DS_Store' -      end -    end - -    dirs.each do |d| -      files = [] -      d.find { |pn| files << pn unless pn.directory? } -      print_remaining_files files, d -    end - -    print_remaining_files remaining_root_files, root, other -  end - -  def print_remaining_files files, root, other = '' -    case files.length -    when 0 -      # noop -    when 1 -      puts files -    else -      puts "#{root}/ (#{files.length} #{other}files)" -    end -  end -end - - -def gcc_42_build -  `/usr/bin/gcc-4.2 -v 2>&1` =~ /build (\d{4,})/ -  if $1 -    $1.to_i  -  elsif system "/usr/bin/which gcc" -    # Xcode 3.0 didn't come with gcc-4.2 -    # We can't change the above regex to use gcc because the version numbers -    # are different and thus, not useful. -    # FIXME I bet you 20 quid this causes a side effect — magic values tend to -    401 -  else -    nil -  end -end -alias :gcc_build :gcc_42_build # For compatibility - -def gcc_40_build -  `/usr/bin/gcc-4.0 -v 2>&1` =~ /build (\d{4,})/ -  if $1 -    $1.to_i  -  else -    nil -  end -end - -def llvm_build -  if MACOS_VERSION >= 10.6 -    xcode_path = `/usr/bin/xcode-select -print-path`.chomp -    return nil if xcode_path.empty? -    `#{xcode_path}/usr/bin/llvm-gcc -v 2>&1` =~ /LLVM build (\d{4,})/ -    $1.to_i -  end -end - -def xcode_version -  `xcodebuild -version 2>&1` =~ /Xcode (\d(\.\d)*)/ -  return $1 ? $1 : nil -end - -def _compiler_recommendation build, recommended -  message = (!build.nil? && build < recommended) ? "(#{recommended} or newer recommended)" : "" -  return build, message -end diff --git a/Library/Homebrew/cleaner.rb b/Library/Homebrew/cleaner.rb index 84335f8a3..f19efc23b 100644 --- a/Library/Homebrew/cleaner.rb +++ b/Library/Homebrew/cleaner.rb @@ -6,6 +6,21 @@ class Cleaner      unless ENV['HOMEBREW_KEEP_INFO'].nil?        f.info.rmtree if f.info.directory? and not f.skip_clean? f.info      end + +    # Hunt for empty folders and nuke them unless they are protected by +    # f.skip_clean? We want post-order traversal, so put the dirs in a stack +    # and then pop them off later. +    paths = [] +    f.prefix.find do |path| +      paths << path if path.directory? +    end + +    paths.each do |d| +      if d.children.empty? and not f.skip_clean? d +        puts "rmdir: #{d} (empty)" if ARGV.verbose? +        d.rmdir +      end +    end    end  private diff --git a/Library/Homebrew/cmd/--cache.rb b/Library/Homebrew/cmd/--cache.rb new file mode 100644 index 000000000..f0a5b22f2 --- /dev/null +++ b/Library/Homebrew/cmd/--cache.rb @@ -0,0 +1,9 @@ +module Homebrew extend self +  def __cache +    if ARGV.named.empty? +      puts HOMEBREW_CACHE +    else +      puts ARGV.formulae.map{ |f| f.cached_download } +    end +  end +end diff --git a/Library/Homebrew/cmd/--cellar.rb b/Library/Homebrew/cmd/--cellar.rb new file mode 100644 index 000000000..c7e140dc5 --- /dev/null +++ b/Library/Homebrew/cmd/--cellar.rb @@ -0,0 +1,9 @@ +module Homebrew extend self +  def __cellar +    if ARGV.named.empty? +      puts HOMEBREW_CELLAR +    else +      puts ARGV.formulae.map{ |f| HOMEBREW_CELLAR/f } +    end +  end +end diff --git a/Library/Homebrew/cmd/--config.rb b/Library/Homebrew/cmd/--config.rb new file mode 100644 index 000000000..7a70df007 --- /dev/null +++ b/Library/Homebrew/cmd/--config.rb @@ -0,0 +1,66 @@ +require 'hardware' + +module Homebrew extend self +  def __config +    puts config_s +  end + +  def llvm +    @llvm ||= MacOS.llvm_build_version +  end + +  def gcc_42 +    @gcc_42 ||= MacOS.gcc_42_build_version +  end + +  def gcc_40 +    @gcc_40 ||= MacOS.gcc_40_build_version +  end + +  def xcode_version +    `xcodebuild -version 2>&1` =~ /Xcode (\d(\.\d)*)/ +    $1 +  end + +  def llvm_recommendation +    "(#{RECOMMENDED_LLVM} or newer recommended)" if llvm and llvm < RECOMMENDED_LLVM +  end +   +  def gcc_42_recommendation +    "(#{RECOMMENDED_GCC_42} or newer recommended)" if gcc_42 and gcc_42 < RECOMMENDED_GCC_42 +  end + +  def gcc_40_recommendation +    "(#{RECOMMENDED_GCC_40} or newer recommended)" if gcc_40.nil? and gcc_40 < RECOMMENDED_GCC_40 +  end + +  def sha +    sha = `cd #{HOMEBREW_REPOSITORY} && git rev-parse --verify HEAD 2> /dev/null`.chomp +    if sha.empty? then "(none)" else sha end +  end + +  def system_ruby +    Pathname.new('/usr/bin/ruby').realpath.to_s +  end + +  def config_s; <<-EOS.undent +    HOMEBREW_VERSION: #{HOMEBREW_VERSION} +    HEAD: #{sha} +    HOMEBREW_PREFIX: #{HOMEBREW_PREFIX} +    HOMEBREW_CELLAR: #{HOMEBREW_CELLAR} +    HOMEBREW_REPOSITORY: #{HOMEBREW_REPOSITORY} +    HOMEBREW_LIBRARY_PATH: #{HOMEBREW_LIBRARY_PATH} +    Hardware: #{Hardware.cores_as_words}-core #{Hardware.bits}-bit #{Hardware.intel_family} +    OS X: #{MACOS_FULL_VERSION} +    Kernel Architecture: #{`uname -m`.chomp} +    Ruby: #{RUBY_VERSION}-#{RUBY_PATCHLEVEL} +    /usr/bin/ruby => #{system_ruby} +    Xcode: #{xcode_version} +    GCC-4.0: #{gcc_40 ? "build #{gcc_40}" : "N/A"} #{gcc_40_recommendation} +    GCC-4.2: #{gcc_42 ? "build #{gcc_42}" : "N/A"} #{gcc_42_recommendation} +    LLVM: #{llvm ? "build #{llvm}" : "N/A" } #{llvm_recommendation} +    MacPorts or Fink? #{macports_or_fink_installed?} +    X11 installed? #{x11_installed?} +    EOS +  end +end diff --git a/Library/Homebrew/cmd/--env.rb b/Library/Homebrew/cmd/--env.rb new file mode 100644 index 000000000..07623171d --- /dev/null +++ b/Library/Homebrew/cmd/--env.rb @@ -0,0 +1,32 @@ +require 'extend/ENV' +require 'hardware' + +module Homebrew extend self +  def __env +    ENV.extend(HomebrewEnvExtension) +    ENV.setup_build_environment +    dump_build_env ENV +  end + +  def dump_build_env env +    puts %["--use-llvm" was specified] if ARGV.include? '--use-llvm' + +    %w[ CC CXX LD ].each do |k| +      value = env[k] +      if value +        results = value +        if File.exists? value and File.symlink? value +          target = Pathname.new(value) +          results += " => #{target.realpath}" +        end +        puts "#{k}: #{results}" +      end +    end + +    %w[ CFLAGS CXXFLAGS CPPFLAGS LDFLAGS MACOSX_DEPLOYMENT_TARGET MAKEFLAGS PKG_CONFIG_PATH +        HOMEBREW_DEBUG HOMEBREW_VERBOSE HOMEBREW_USE_LLVM HOMEBREW_SVN ].each do |k| +      value = env[k] +      puts "#{k}: #{value}" if value +    end +  end +end diff --git a/Library/Homebrew/cmd/--prefix.rb b/Library/Homebrew/cmd/--prefix.rb new file mode 100644 index 000000000..9a15203a9 --- /dev/null +++ b/Library/Homebrew/cmd/--prefix.rb @@ -0,0 +1,9 @@ +module Homebrew extend self +  def __prefix +    if ARGV.named.empty? +      puts HOMEBREW_PREFIX +    else +      puts ARGV.formulae.map{ |f| f.prefix } +    end +  end +end diff --git a/Library/Homebrew/cmd/--repository.rb b/Library/Homebrew/cmd/--repository.rb new file mode 100644 index 000000000..f14ab9901 --- /dev/null +++ b/Library/Homebrew/cmd/--repository.rb @@ -0,0 +1,5 @@ +module Homebrew extend self +  def __repository +    puts HOMEBREW_REPOSITORY +  end +end diff --git a/Library/Homebrew/cmd/cat.rb b/Library/Homebrew/cmd/cat.rb new file mode 100644 index 000000000..a87eba61e --- /dev/null +++ b/Library/Homebrew/cmd/cat.rb @@ -0,0 +1,10 @@ +module Homebrew extend self +  def cat +    # do not "fix" this to support multiple arguments, the output would be +    # unparsable, if the user wants to cat multiple formula they can call +    # brew cat multiple times. + +    cd HOMEBREW_REPOSITORY +    exec "cat", ARGV.formulae.first.path, *ARGV.options_only +  end +end diff --git a/Library/Homebrew/cmd/cleanup.rb b/Library/Homebrew/cmd/cleanup.rb new file mode 100644 index 000000000..f5e4637ec --- /dev/null +++ b/Library/Homebrew/cmd/cleanup.rb @@ -0,0 +1,42 @@ +require 'formula' +require 'cmd/prune' + +module Homebrew extend self +  def cleanup +    if ARGV.named.empty? +      HOMEBREW_CELLAR.children.each do |rack| +        begin +          cleanup_formula rack.basename.to_s if rack.directory? +        rescue FormulaUnavailableError => e +          opoo "Formula not found for #{e.name}" +        end +      end +      # seems like a good time to do some additional cleanup +      Homebrew.prune +    else +      ARGV.formulae.each do |f| +        cleanup_formula f +      end +    end +  end + +  def cleanup_formula f +    f = Formula.factory f +    rack = f.prefix.parent + +    if f.installed? and rack.directory? +      rack.children.each do |keg| +        if f.installed_prefix != keg +          print "Uninstalling #{keg}..." +          rm_rf keg +          puts +        end +      end +    elsif rack.children.length > 1 +      # If the cellar only has one version installed, don't complain +      # that we can't tell which one to keep. +      opoo "Skipping #{name}: most recent version #{f.version} not installed" +    end +  end + +end diff --git a/Library/Homebrew/cmd/create.rb b/Library/Homebrew/cmd/create.rb new file mode 100644 index 000000000..e21c9a524 --- /dev/null +++ b/Library/Homebrew/cmd/create.rb @@ -0,0 +1,153 @@ +require 'formula' + +module Homebrew extend self +  def create +    if ARGV.include? '--macports' +      exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" +    elsif ARGV.include? '--fink' +      exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" +    elsif ARGV.named.empty? +      raise UsageError +    else +      paths = ARGV.named.map do |url| +        fc = FormulaCreator.new +        fc.url = url +        fc.mode = if ARGV.include? '--cmake' +          :cmake +        elsif ARGV.include? '--autotools' +          :autotools +        end + +        if fc.name.to_s.strip.empty? +          path = Pathname.new url +          print "Formula name [#{path.stem}]: " +          fc.name = __gets || path.stem +        end + +        unless ARGV.force? +          if msg = blacklisted?(fc.name) +            raise "#{msg}\n\nIf you really want to make this formula use --force." +          end + +          if Formula.aliases.include? fc.name +            realname = Formula.caniconical_name fc.name +            raise <<-EOS.undent +              The formula #{realname} is already aliased to #{fc.name} +              Please check that you are not creating a duplicate. +              To force creation use --force. +              EOS +          end +        end +        fc.generate +        fc.path +      end +      exec_editor *paths +    end +  end + +  def __gets +    gots = $stdin.gets.chomp +    if gots.empty? then nil else gots end +  end + +  def blacklisted? name +    case name.downcase +    when 'vim', 'screen' then <<-EOS.undent +      #{name} is blacklisted for creation +      Apple distributes this program with OS X. +      EOS +    when 'libarchive', 'libpcap' then <<-EOS.undent +      #{name} is blacklisted for creation +      Apple distributes this library with OS X, you can find it in /usr/lib. +      EOS +    when 'libxml', 'libxlst', 'freetype', 'libpng' then <<-EOS.undent +      #{name} is blacklisted for creation +      Apple distributes this library with OS X, you can find it in /usr/X11/lib. +      However not all build scripts look here, so you may need to call ENV.x11 or +      ENV.libxml2 in your formula's install function. +      EOS +    when /^rubygems?$/ +      "Sorry RubyGems comes with OS X so we don't package it." +    when 'wxwidgets' then <<-EOS.undent +      #{name} is blacklisted for creation +      An older version of wxWidgets is provided by Apple with OS X, but +      a formula for wxWidgets 2.8.10 is provided: + +      brew install wxmac +      EOS +    end +  end +end + +class FormulaCreator +  attr :url +  attr :md5 +  attr :name, true +  attr :path +  attr :mode, true + +  def url= url +    @url = url +    path = Pathname.new url +    /(.*?)[-_.]?#{path.version}/.match path.basename +    @name = $1 +    @path = Formula.path $1 +  end + +  def version +    Pathname.new(url).version +  end + +  def generate +    raise "#{path} already exists" if path.exist? +    raise VersionUndetermined if version.nil? + +    require 'digest' +    require 'erb' + +    if version.nil? +      opoo "Version cannot be determined from URL." +      puts "You'll need to add an explicit 'version' to the formula." +    else +      puts "Version detected as #{version}." +    end + +    unless ARGV.include? "--no-md5" and version +      strategy = detect_download_strategy url +      @md5 = strategy.new(url, name, version, nil).fetch.md5 if strategy == CurlDownloadStrategy +    end + +    path.write ERB.new(template, nil, '>').result(binding) +  end + +  def template; <<-EOS.undent +    require 'formula' + +    class #{Formula.class_s name} <Formula +      url '#{url}' +      homepage '' +      md5 '#{md5}' + +    <% if mode == :cmake %> +      depends_on 'cmake' +    <% elsif mode == nil %> +      # depends_on 'cmake' +    <% end %> + +      def install +    <% if mode == :cmake %> +        system "cmake . \#{std_cmake_parameters}" +    <% elsif mode == :autotools %> +        system "./configure", "--disable-debug", "--disable-dependency-tracking", +                              "--prefix=\#{prefix}" +    <% else %> +        system "./configure", "--disable-debug", "--disable-dependency-tracking", +                              "--prefix=\#{prefix}" +        # system "cmake . \#{std_cmake_parameters}" +    <% end %> +        system "make install" +      end +    end +    EOS +  end +end diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb new file mode 100644 index 000000000..2e9a723f4 --- /dev/null +++ b/Library/Homebrew/cmd/deps.rb @@ -0,0 +1,14 @@ +module Homebrew extend self +  def deps +    puts if ARGV.include?('--all') +      require 'formula' +      Formula.all.each do |f| +        "#{f.name}:#{f.deps.join(' ')}" +      end +    elsif ARGV.include?("-1") or ARGV.include?("--1") +      *ARGV.formulae.map{ |f| f.deps or [] }.flatten.uniq.sort +    else +      *ARGV.formulae.map{ |f| f.recursive_deps.map{ |f| f.name } }.flatten.uniq.sort +    end +  end +end diff --git a/Library/Homebrew/cmd/diy.rb b/Library/Homebrew/cmd/diy.rb new file mode 100644 index 000000000..174626afc --- /dev/null +++ b/Library/Homebrew/cmd/diy.rb @@ -0,0 +1,34 @@ +module Homebrew extend self +  def diy +    path = Pathname.getwd + +    version = if ARGV.include? '--set-version' +      ARGV.next +    elsif path.version.to_s.empty? +      raise "Couldn't determine version, try --set-version" +    else +      path.version +    end + +    name = if ARGV.include? '--set-name' +      ARGV.next +    else +      path.basename.to_s =~ /(.*?)-?#{version}/ +      if $1.to_s.empty? +        path.basename +      else +        $1 +      end +    end + +    prefix = HOMEBREW_CELLAR/name/version + +    if File.file? 'CMakeLists.txt' +      puts "-DCMAKE_INSTALL_PREFIX=#{prefix}" +    elsif File.file? 'Makefile.am' +      puts "--prefix=#{prefix}" +    else +      raise "Couldn't determine build system" +    end +  end +end diff --git a/Library/Homebrew/brew_doctor.rb b/Library/Homebrew/cmd/doctor.rb index b26cd4f5d..bad3a5dc0 100644 --- a/Library/Homebrew/brew_doctor.rb +++ b/Library/Homebrew/cmd/doctor.rb @@ -583,7 +583,8 @@ def check_for_other_vars    end  end -def brew_doctor +module Homebrew extend self +def doctor    read, write = IO.pipe    if fork == nil @@ -629,3 +630,4 @@ def brew_doctor      end    end  end +end diff --git a/Library/Homebrew/cmd/edit.rb b/Library/Homebrew/cmd/edit.rb new file mode 100644 index 000000000..6ab91dccf --- /dev/null +++ b/Library/Homebrew/cmd/edit.rb @@ -0,0 +1,26 @@ +require 'formula' + +module Homebrew extend self +  def edit +    if ARGV.named.empty? +      # EDITOR isn't a good fit here, we need a GUI client that actually has +      # a UI for projects, so apologies if this wasn't what you expected, +      # please improve it! :) +      exec 'mate', HOMEBREW_REPOSITORY/"bin/brew", +                   HOMEBREW_REPOSITORY/'README.md', +                   HOMEBREW_REPOSITORY/".gitignore", +              *Dir[HOMEBREW_REPOSITORY/"Library/*"] +    else +      # Don't use ARGV.formulae as that will throw if the file doesn't parse +      paths = ARGV.named.map do |name| +        HOMEBREW_REPOSITORY/"Library/Formula/#{Formula.caniconical_name name}.rb" +      end +      unless ARGV.force? +        paths.each do |path| +          raise FormulaUnavailableError, path.basename('.rb').to_s unless path.file? +        end +      end +      exec_editor *paths +    end +  end +end diff --git a/Library/Homebrew/cmd/home.rb b/Library/Homebrew/cmd/home.rb new file mode 100644 index 000000000..a91607988 --- /dev/null +++ b/Library/Homebrew/cmd/home.rb @@ -0,0 +1,9 @@ +module Homebrew extend self +  def home +    if ARGV.named.empty? +      exec "open", HOMEBREW_WWW +    else +      exec "open", *ARGV.formulae.map{ |f| f.homepage } +    end +  end +end diff --git a/Library/Homebrew/cmd/info.rb b/Library/Homebrew/cmd/info.rb new file mode 100644 index 000000000..7f9767e07 --- /dev/null +++ b/Library/Homebrew/cmd/info.rb @@ -0,0 +1,92 @@ +require 'formula' + +module Homebrew extend self +  def info +    if ARGV.named.empty? +      if ARGV.include? "--all" +        Formula.each do |f| +          info_formula f +          puts '---' +        end +      else +        puts "#{HOMEBREW_CELLAR.children.length} kegs, #{HOMEBREW_CELLAR.abv}" +      end +    elsif valid_url ARGV[0] +      path = Pathname.new(ARGV.shift) +      /(.*?)[-_.]?#{path.version}/.match path.basename +      unless $1.to_s.empty? +        name = $1 +      else +        name = path.stem +      end +      puts "#{name} #{path.version}" +    else +      ARGV.formulae.each{ |f| info_formula f } +    end +  end + +  def github_info name +    formula_name = Formula.path(name).basename +    user = 'mxcl' +    branch = 'master' + +    if system "/usr/bin/which -s git" +      gh_user=`git config --global github.user 2>/dev/null`.chomp +      /^\*\s*(.*)/.match(`git --work-tree=#{HOMEBREW_REPOSITORY} branch 2>/dev/null`) +      unless $1.nil? || $1.empty? || gh_user.empty? +        branch = $1.chomp +        user = gh_user +      end +    end + +    "http://github.com/#{user}/homebrew/commits/#{branch}/Library/Formula/#{formula_name}" +  end + +  def info_formula f +    exec 'open', github_info(f.name) if ARGV.flag? '--github' + +    puts "#{f.name} #{f.version}" +    puts f.homepage + +    puts "Depends on: #{f.deps*', '}" unless f.deps.empty? + +    rack = f.prefix.parent +    if rack.directory? +      kegs = rack.children +      kegs.each do |keg| +        next if keg.basename.to_s == '.DS_Store' +        print "#{keg} (#{keg.abv})" +        print " *" if f.installed_prefix == keg and kegs.length > 1 +        puts +      end +    else +      puts "Not installed" +    end + +    if f.caveats +      puts +      puts f.caveats +      puts +    end + +    history = github_info f.name +    puts history if history + +  rescue FormulaUnavailableError +    # check for DIY installation +    d = HOMEBREW_PREFIX/name +    if d.directory? +      ohai "DIY Installation" +      d.children.each{ |keg| puts "#{keg} (#{keg.abv})" } +    else +      raise "No such formula or keg" +    end +  end + +  private + +  def valid_url u +    u[0..6] == 'http://' or u[0..7] == 'https://' or u[0..5] == 'ftp://' +  end + +end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb new file mode 100644 index 000000000..ed5fb6777 --- /dev/null +++ b/Library/Homebrew/cmd/install.rb @@ -0,0 +1,87 @@ +require 'formula_installer' +require 'hardware' + +module Homebrew extend self +  def install +    brew_install +  end +end + +def brew_install +  ############################################################ sanity checks +  case Hardware.cpu_type when :ppc, :dunno +    abort "Sorry, Homebrew does not support your computer's CPU architecture.\n"+ +          "For PPC support, see: http://github.com/sceaga/homebrew/tree/powerpc" +  end + +  raise "Cannot write to #{HOMEBREW_CELLAR}" if HOMEBREW_CELLAR.exist? and not HOMEBREW_CELLAR.writable? +  raise "Cannot write to #{HOMEBREW_PREFIX}" unless HOMEBREW_PREFIX.writable? + +  ################################################################# warnings +  begin +    if MACOS_VERSION >= 10.6 +      opoo "You should upgrade to Xcode 3.2.3" if llvm_build < RECOMMENDED_LLVM +    else +      opoo "You should upgrade to Xcode 3.1.4" if (gcc_40_build < RECOMMENDED_GCC_40) or (gcc_42_build < RECOMMENDED_GCC_42) +    end +  rescue +    # the reason we don't abort is some formula don't require Xcode +    # TODO allow formula to declare themselves as "not needing Xcode" +    opoo "Xcode is not installed! Builds may fail!" +  end + +  if macports_or_fink_installed? +    opoo "It appears you have MacPorts or Fink installed." +    puts "Software installed with MacPorts and Fink are known to cause problems." +    puts "If you experience issues try uninstalling these tools." +  end + +  ################################################################# install! +  installer = FormulaInstaller.new +  installer.install_deps = !ARGV.include?('--ignore-dependencies') + +  ARGV.formulae.each do |f| +    if not f.installed? or ARGV.force? +      installer.install f +    else +      puts "Formula already installed: #{f.prefix}" +    end +  end +end + +def check_for_blacklisted_formula names +  return if ARGV.force? + +  names.each do |name| +    case name +    when 'tex', 'tex-live', 'texlive' then abort <<-EOS.undent +      Installing TeX from source is weird and gross, requires a lot of patches, +      and only builds 32-bit (and thus can't use Homebrew deps on Snow Leopard.) + +      We recommend using a MacTeX distribution: +        http://www.tug.org/mactex/ +    EOS + +    when 'mercurial', 'hg' then abort <<-EOS.undent +      Mercurial can be install thusly: +        brew install pip && pip install mercurial +    EOS + +    when 'npm' then abort <<-EOS.undent +      npm can be installed thusly by following the instructions at +        http://npmjs.org/ + +      To do it in one line, use this command: +        curl http://npmjs.org/install.sh | sudo sh +    EOS + +    when 'setuptools' then abort <<-EOS.undent +      When working with a Homebrew-built Python, distribute is preferred +      over setuptools, and can be used as the prerequisite for pip. + +      Install distribute using: +        brew install distribute +    EOS +    end +  end +end diff --git a/Library/Homebrew/cmd/link.rb b/Library/Homebrew/cmd/link.rb new file mode 100644 index 000000000..5565309e2 --- /dev/null +++ b/Library/Homebrew/cmd/link.rb @@ -0,0 +1,8 @@ +module Homebrew extend self +  def link +    ARGV.kegs.each do |keg| +      print "Linking #{keg}... " +      puts "#{keg.link} links created" +    end +  end +end diff --git a/Library/Homebrew/cmd/list.rb b/Library/Homebrew/cmd/list.rb new file mode 100644 index 000000000..8e35530fd --- /dev/null +++ b/Library/Homebrew/cmd/list.rb @@ -0,0 +1,88 @@ +module Homebrew extend self +  def list +    if ARGV.flag? '--unbrewed' +      dirs = HOMEBREW_PREFIX.children.select{ |pn| pn.directory? }.map{ |pn| pn.basename.to_s } +      dirs -= %w[Library Cellar .git] +      cd HOMEBREW_PREFIX +      exec 'find', *dirs + %w[-type f ( ! -iname .ds_store ! -iname brew )] +    elsif ARGV.flag? '--versions' +      if ARGV.named.empty? +        HOMEBREW_CELLAR.children.select{ |pn| pn.directory? } +      else +        ARGV.named.map{ |n| HOMEBREW_CELLAR/n }.select{ |pn| pn.exist? } +      end.each do |d| +        versions = d.children.select{ |pn| pn.directory? }.map{ |pn| pn.basename.to_s } +        puts "#{d.basename} #{versions*' '}" +      end +    elsif ARGV.named.empty? +      ENV['CLICOLOR'] = nil +      exec 'ls', *ARGV.options_only << HOMEBREW_CELLAR if HOMEBREW_CELLAR.exist? +    elsif ARGV.verbose? or not $stdout.tty? +      exec "find", *ARGV.kegs + %w[-not -type d -print] +    else +      ARGV.kegs.each{ |keg| PrettyListing.new keg } +    end +  end +end + +class PrettyListing +  def initialize path +    Pathname.new(path).children.sort{ |a,b| a.to_s.downcase <=> b.to_s.downcase }.each do |pn| +      case pn.basename.to_s +      when 'bin', 'sbin' +        pn.find { |pnn| puts pnn unless pnn.directory? } +      when 'lib' +        print_dir pn do |pnn| +          # dylibs have multiple symlinks and we don't care about them +          (pnn.extname == '.dylib' or pnn.extname == '.pc') and not pnn.symlink? +        end +      else +        if pn.directory? +          if pn.symlink? +            puts "#{pn} -> #{pn.readlink}" +          else +            print_dir pn +          end +        elsif not (FORMULA_META_FILES + ['.DS_Store']).include? pn.basename.to_s +          puts pn +        end +      end +    end +  end + +  def print_dir root +    dirs = [] +    remaining_root_files = [] +    other = '' + +    root.children.sort.each do |pn| +      if pn.directory? +        dirs << pn +      elsif block_given? and yield pn +        puts pn +        other = 'other ' +      else +        remaining_root_files << pn unless pn.basename.to_s == '.DS_Store' +      end +    end + +    dirs.each do |d| +      files = [] +      d.find { |pn| files << pn unless pn.directory? } +      print_remaining_files files, d +    end + +    print_remaining_files remaining_root_files, root, other +  end + +  def print_remaining_files files, root, other = '' +    case files.length +    when 0 +      # noop +    when 1 +      puts files +    else +      puts "#{root}/ (#{files.length} #{other}files)" +    end +  end +end diff --git a/Library/Homebrew/cmd/log.rb b/Library/Homebrew/cmd/log.rb new file mode 100644 index 000000000..ceb0c8e02 --- /dev/null +++ b/Library/Homebrew/cmd/log.rb @@ -0,0 +1,10 @@ +module Homebrew extend self +  def log +    cd HOMEBREW_REPOSITORY +    if ARGV.named.empty? +      exec "git", "log", *ARGV.options_only +    else +      exec "git", "log", *ARGV.formulae.map(&:path), *ARGV.options_only +    end +  end +end diff --git a/Library/Homebrew/cmd/outdated.rb b/Library/Homebrew/cmd/outdated.rb new file mode 100644 index 000000000..37fba0f99 --- /dev/null +++ b/Library/Homebrew/cmd/outdated.rb @@ -0,0 +1,28 @@ +require 'formula' + +module Homebrew extend self +  def outdated +    outdated_brews.each do |keg, name, version| +      if $stdout.tty? and not ARGV.flag? '--quiet' +        versions = keg.cd{ Dir['*'] }.join(', ') +        puts "#{name} (#{versions} < #{version})" +      else +        puts name +      end +    end +  end + +  def outdated_brews +    HOMEBREW_CELLAR.subdirs.map do |rack| +      # Skip kegs with no versions installed +      next unless rack.subdirs + +      # Skip HEAD formulae, consider them "evergreen" +      next if rack.subdirs.map{ |keg| keg.basename.to_s }.include? "HEAD" + +      name = rack.basename.to_s +      f = Formula.factory name rescue nil +      [rack, name, f.version] if f and not f.installed? +    end.compact +  end +end
\ No newline at end of file diff --git a/Library/Homebrew/cmd/prune.rb b/Library/Homebrew/cmd/prune.rb new file mode 100644 index 000000000..ba4bbc8e5 --- /dev/null +++ b/Library/Homebrew/cmd/prune.rb @@ -0,0 +1,31 @@ +module Homebrew extend self +  # $n and $d are used by the ObserverPathnameExtension to keep track of +  # certain filesystem actions. + +  def prune +    $n = 0 +    $d = 0 +    dirs = [] + +    %w[bin sbin etc lib include share].map{ |d| HOMEBREW_PREFIX/d }.each do |path| +      path.find do |path| +        path.extend ObserverPathnameExtension +        if path.symlink? +          path.unlink unless path.resolved_path_exists? +        elsif path.directory? +          dirs << path +        end +      end +    end + +    dirs.sort.reverse_each{ |d| d.rmdir_if_possible } + +    if $n == 0 and $d == 0 +      puts "Nothing pruned" if ARGV.verbose? +    else +      print "Pruned #{$n} symbolic links " +      print "and #{$d} directories " if $d > 0 +      puts  "from #{HOMEBREW_PREFIX}" +    end +  end +end diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb new file mode 100644 index 000000000..b6a0e971d --- /dev/null +++ b/Library/Homebrew/cmd/search.rb @@ -0,0 +1,42 @@ +require "formula" + +module Homebrew extend self +  def search +    if ARGV.include? '--macports' +      exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" +    elsif ARGV.include? '--fink' +      exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" +    end + +    require 'cmd/install' # for blacklisted? function +    blacklisted? ARGV.named do |msg, _| +      abort msg +    end unless ARGV.force? + +    puts_columns search_brews(ARGV.first) +  end + +  def search_brews text +    if text.to_s.empty? +      Formula.names +    else +      rx = if text =~ %r{^/(.*)/$} +        Regexp.new($1) +      else +        /.*#{Regexp.escape text}.*/i +      end + +      aliases = Formula.aliases +      results = (Formula.names+aliases).grep rx + +      # Filter out aliases when the full name was also found +      results.reject do |alias_name| +        if aliases.include? alias_name +          resolved_name = (HOMEBREW_REPOSITORY/"Library/Aliases"/alias_name).readlink.basename('.rb').to_s +          results.include? resolved_name +        end +      end +    end +  end + +end diff --git a/Library/Homebrew/cmd/uninstall.rb b/Library/Homebrew/cmd/uninstall.rb new file mode 100644 index 000000000..34de88130 --- /dev/null +++ b/Library/Homebrew/cmd/uninstall.rb @@ -0,0 +1,31 @@ +require 'keg' + +module Homebrew extend self +  def uninstall +    unless ARGV.force? +      ARGV.kegs.each do |keg| +        puts "Uninstalling #{keg}..." +        keg.unlink +        keg.uninstall +      end +    else +      ARGV.formulae.each do |f| +        rack = f.prefix.parent +        if rack.directory? +          puts "Uninstalling #{f}..." +          rack.children do |keg| +            if keg.directory? +              keg = Keg.new(keg) +              keg.unlink +              keg.rmtree +            end +          end +          rack.rmdir +        end +      end +    end +  rescue MultipleVersionsInstalledError => e +    onoe e +    puts "Use `brew remove --force #{e.name}` to remove all versions." +  end +end diff --git a/Library/Homebrew/cmd/unlink.rb b/Library/Homebrew/cmd/unlink.rb new file mode 100644 index 000000000..d4749ab76 --- /dev/null +++ b/Library/Homebrew/cmd/unlink.rb @@ -0,0 +1,8 @@ +module Homebrew extend self +  def unlink +    ARGV.kegs.each do |keg| +      print "Unlinking #{keg}... " +      puts "#{keg.unlink} links removed" +    end +  end +end diff --git a/Library/Homebrew/update.rb b/Library/Homebrew/cmd/update.rb index 9e4fb90d9..32f144c8e 100644 --- a/Library/Homebrew/update.rb +++ b/Library/Homebrew/cmd/update.rb @@ -1,3 +1,16 @@ +module Homebrew extend self +  def update +    abort "Please `brew install git' first." unless system "/usr/bin/which -s git" + +    updater = RefreshBrew.new +    if updater.update_from_masterbrew! +      updater.report +    else +      puts "Already up-to-date." +    end +  end +end +  class RefreshBrew    REPOSITORY_URL   = "http://github.com/mxcl/homebrew.git"    INIT_COMMAND     = "git init" diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb new file mode 100644 index 000000000..455b29b31 --- /dev/null +++ b/Library/Homebrew/cmd/uses.rb @@ -0,0 +1,25 @@ +require 'formula' + +# `brew uses foo bar` now returns formula that use both foo and bar +# Rationale: If you want the union just run the command twice and +# concatenate the results. +# The intersection is harder to achieve with shell tools. + +module Homebrew extend self +  def uses +    uses = Formula.all.select do |f| +      ARGV.formulae.all? do |ff| +        # For each formula given, show which other formulas depend on it. +        # We only go one level up, ie. direct dependencies. +        f.deps.include? ff.name +      end +    end +    if ARGV.include? "--installed" +      uses = uses.select do |f| +        keg = HOMEBREW_CELLAR/f +        keg.directory? and not keg.subdirs.empty? +      end +    end +    puts uses.sort +  end +end diff --git a/Library/Homebrew/compatibility.rb b/Library/Homebrew/compatibility.rb new file mode 100644 index 000000000..52f98416a --- /dev/null +++ b/Library/Homebrew/compatibility.rb @@ -0,0 +1,47 @@ + +# maybe never used by anyone, but alas it must continue to exist +def versions_of(keg_name) +  `/bin/ls #{HOMEBREW_CELLAR}/#{keg_name}`.collect { |version| version.strip }.reverse +end + +def dump_config +  require 'cmd/--config' +  Homebrew.__config +end + +def dump_build_env env +  require 'cmd/--env' +  Homebrew.dump_build_env env +end + +def gcc_42_build +  MacOS.gcc_42_build_version +end + +alias :gcc_build :gcc_42_build + +def gcc_40_build +  MacOS.gcc_40_build_version +end + +def llvm_build +  MacOS.llvm_build_version +end + +def x11_installed? +  MacOS.x11_installed? +end + +def macports_or_fink_installed? +  MacOS.macports_or_fink_installed? +end + +def outdated_brews +  require 'cmd/outdated' +  Homebrew.outdated_brews +end + +def search_brews text +  require 'cmd/search' +  Homebrew.search_brews text +end diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb new file mode 100644 index 000000000..73cda01e5 --- /dev/null +++ b/Library/Homebrew/exceptions.rb @@ -0,0 +1,99 @@ + +class NotAKegError < RuntimeError +end + +class FormulaUnavailableError < RuntimeError +  attr :name +  def initialize name +    @name = name +    super "No available formula for #{name}" +  end +end + +module Homebrew +  class InstallationError < RuntimeError +    attr :formula +    def initialize formula +      @formula = formula +    end +    def initialize formula, message +      super message +      @formula = formula +    end +  end +end + +class FormulaAlreadyInstalledError < Homebrew::InstallationError +  def message +    "Formula already installed: #{formula}" +  end +end + +class FormulaInstallationAlreadyAttemptedError < Homebrew::InstallationError +  def message +    "Formula installation already attempted: #{formula}" +  end +end + +class UnsatisfiedExternalDependencyError < Homebrew::InstallationError +  attr :type +   +  def initialize formula, type +    @type = type +    @formula = formula +  end + +  def message +    <<-EOS.undent +      Unsatisfied dependency: #{formula} +      Homebrew does not provide #{type.to_s.capitalize} dependencies, #{tool} does: + +          #{command_line} #{formula} +      EOS +  end + +  private + +  def tool +    case type +      when :python then 'pip' +      when :ruby, :jruby then 'rubygems' +      when :perl then 'cpan' +    end +  end + +  def command_line +    case type +      when :python +        "#{brew_pip}pip install" +      when :ruby +        "gem install" +      when :perl +        "cpan -i" +      when :jruby +        "jruby -S gem install" +    end +  end + +  def brew_pip +    'brew install pip && ' unless Formula.factory('pip').installed? +  end +end + +class BuildError < Homebrew::InstallationError +  attr :exit_status +  attr :command +  attr :env + +  def initialize formula, cmd, args, es +    @command = cmd +    @env = ENV.to_hash +    @exit_status = es.exitstatus rescue 1 +    args = args.map{ |arg| arg.gsub " ", "\\ " }.join(" ") +    super formula, "Failed executing: #{command} #{args}" +  end + +  def was_running_configure? +    @command == './configure' +  end +end diff --git a/Library/Homebrew/extend/ENV.rb b/Library/Homebrew/extend/ENV.rb index 61fb3c2b3..a4e290d5f 100644 --- a/Library/Homebrew/extend/ENV.rb +++ b/Library/Homebrew/extend/ENV.rb @@ -152,7 +152,7 @@ module HomebrewEnvExtension    end    def x11 -    opoo "You do not have X11 installed, this formula may not build." if not x11_installed? +    opoo "You do not have X11 installed, this formula may not build." if not MacOS.x11_installed?      # There are some config scripts (e.g. freetype) here that should go in the path      prepend 'PATH', '/usr/X11/bin', ':' diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 0e3266351..78e438726 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1,15 +1,6 @@  require 'download_strategy'  require 'fileutils' -class FormulaUnavailableError <RuntimeError -  def initialize name -    @name = name -    super "No available formula for #{name}" -  end - -  attr_reader :name -end -  class SoftwareSpecification    attr_reader :url, :specs, :using @@ -223,6 +214,22 @@ class Formula      end    end +  def == b +    name == b.name +  end +  def eql? b +    self == b and self.class.equal? b.class +  end +  def hash +    name.hash +  end +  def <=> b +    name <=> b.name +  end +  def to_s +    name +  end +    # Standard parameters for CMake builds.    # Using Build Type "None" tells cmake to use our CFLAGS,etc. settings.    # Setting it to Release would ignore our flags. @@ -266,15 +273,25 @@ class Formula    # an array of all Formula, instantiated    def self.all -    all = [] +    map{ |f| f } +  end +  def self.map +    rv = [] +    each{ |f| rv << yield(f) } +    rv +  end +  def self.each      names.each do |n|        begin -        all << Formula.factory(n) +        yield Formula.factory(n)        rescue          # Don't let one broken formula break commands.        end      end -    return all +  end + +  def inspect +    name    end    def self.aliases @@ -320,8 +337,9 @@ class Formula          install_type = :from_path          target_file = path.to_s        else +        name = Formula.caniconical_name(name)          # For names, map to the path and then require -        require self.path(name) +        require Formula.path(name)          install_type = :from_name        end      end @@ -344,7 +362,7 @@ class Formula    end    def self.path name -    HOMEBREW_REPOSITORY+"Library/Formula/#{name.downcase}.rb" +    HOMEBREW_REPOSITORY/"Library/Formula/#{name.downcase}.rb"    end    def deps @@ -352,7 +370,20 @@ class Formula    end    def external_deps -    self.class.external_deps +    self.class.external_deps or {} +  end + +  # deps are in an installable order +  # which means if a depends on b then b will be ordered before a in this list +  def recursive_deps +    Formula.expand_deps(self).flatten.uniq +  end + +  def self.expand_deps f +    f.deps.map do |dep| +      dep = Formula.factory dep +      expand_deps(dep) << dep +    end    end  protected @@ -381,11 +412,8 @@ protected          raise        end      end -  rescue SystemCallError -    # usually because exec could not be find the command that was requested -    raise    rescue -    raise BuildError.new(cmd, args, $?) +    raise BuildError.new(self, cmd, args, $?)    end  private diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 0555b1267..3cc1024e7 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -2,6 +2,8 @@ require 'extend/pathname'  require 'extend/ARGV'  require 'extend/string'  require 'utils' +require 'exceptions' +require 'compatibility'  ARGV.extend(HomebrewArgvExtension) @@ -40,3 +42,11 @@ HOMEBREW_USER_AGENT = "Homebrew #{HOMEBREW_VERSION} (Ruby #{RUBY_VERSION}-#{RUBY  RECOMMENDED_LLVM = 2326  RECOMMENDED_GCC_40 = (MACOS_VERSION >= 10.6) ? 5494 : 5493  RECOMMENDED_GCC_42 = (MACOS_VERSION >= 10.6) ? 5664 : 5577 + +require 'fileutils' +module Homebrew extend self +  include FileUtils +end + +FORMULA_META_FILES = %w[README README.md ChangeLog COPYING LICENSE LICENCE COPYRIGHT AUTHORS] +PLEASE_REPORT_BUG = "#{Tty.white}Please report this bug at #{Tty.em}\nhttps://github.com/mxcl/homebrew/wiki/new-issue#{Tty.reset}" diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index f3ca8d2bd..6b338ea83 100755 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -30,7 +30,7 @@ at_exit do      require 'fileutils'      require 'hardware'      require 'keg' -    require 'brew.h.rb' +    require 'compatibility'      ENV.extend(HomebrewEnvExtension)      ENV.setup_build_environment @@ -121,7 +121,8 @@ def install f    ohai 'Finishing up' if ARGV.verbose?    begin -    clean f +    require 'cleaner' +    Cleaner.new f    rescue Exception => e      opoo "The cleaning step did not complete successfully"      puts "Still, the installation was successful, so we will link it into your prefix" diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb index e278a5ce7..22bf40bc3 100644 --- a/Library/Homebrew/keg.rb +++ b/Library/Homebrew/keg.rb @@ -1,3 +1,5 @@ +require 'extend/pathname' +  class Keg <Pathname    def initialize path      super path @@ -5,8 +7,6 @@ class Keg <Pathname      raise "#{to_s} is not a directory" unless directory?    end -  class NotAKegError <RuntimeError; end -    # if path is a file in a keg then this will return the containing Keg object    def self.for path      path = path.realpath diff --git a/Library/Homebrew/test/test_ENV.rb b/Library/Homebrew/test/test_ENV.rb index 1cd716dfd..da3a9d6af 100644 --- a/Library/Homebrew/test/test_ENV.rb +++ b/Library/Homebrew/test/test_ENV.rb @@ -1,6 +1,5 @@  require 'testing_env'  require 'utils' -require 'brew.h'  require 'extend/ENV'  ENV.extend(HomebrewEnvExtension) diff --git a/Library/Homebrew/test/test_bucket.rb b/Library/Homebrew/test/test_bucket.rb index 8492b4929..086297dd2 100644 --- a/Library/Homebrew/test/test_bucket.rb +++ b/Library/Homebrew/test/test_bucket.rb @@ -5,7 +5,6 @@ ARGV.extend(HomebrewArgvExtension)  require 'test/testball'  require 'utils' -require 'brew.h'  class MockFormula <Formula    def initialize url @@ -79,19 +78,24 @@ class BeerTasting < Test::Unit::TestCase    # end    def test_brew_h +    require 'cmd/info' +    require 'cmd/prune' +    require 'cleaner' +      nostdout do        assert_nothing_raised do          f=TestBall.new -        make f.url -        info f -        clean f -        prune +        Homebrew.info_formula f +        Cleaner.new f +        Homebrew.prune          #TODO test diy function too        end      end    end    def test_brew_cleanup +    require 'cmd/cleanup' +      f1=TestBall.new      f1.instance_eval { @version = "0.1" }      f2=TestBall.new @@ -110,7 +114,7 @@ class BeerTasting < Test::Unit::TestCase      assert f3.installed?      nostdout do -      cleanup f3 +      Homebrew.cleanup_formula f3      end      assert !f1.installed? @@ -175,4 +179,16 @@ class BeerTasting < Test::Unit::TestCase      assert_equal 'foo-0.1', foo1.stem      assert_equal '0.1', foo1.version    end + +  class MockMockFormula < Struct.new(:name); end + +  def test_formula_equality +    f = MockFormula.new('http://example.com/test-0.1.tgz') +    g = MockMockFormula.new('test') + +    assert f == f +    assert f == g +    assert f.eql? f +    assert (not (f.eql? g)) +  end  end diff --git a/Library/Homebrew/test/test_external_deps.rb b/Library/Homebrew/test/test_external_deps.rb index 2b96e7678..30b3540d9 100644 --- a/Library/Homebrew/test/test_external_deps.rb +++ b/Library/Homebrew/test/test_external_deps.rb @@ -3,38 +3,12 @@ require 'testing_env'  require 'extend/ARGV' # needs to be after test/unit to avoid conflict with OptionsParser  ARGV.extend(HomebrewArgvExtension) +require 'extend/string'  require 'test/testball'  require 'formula_installer'  require 'utils' -# Custom formula installer that checks deps but does not -# run the install code. We also override the external dep -# install messages, so for instance the Python check doesnt -# look for the pip formula (which isn't avaialble in test -# mode.) -class DontActuallyInstall < FormulaInstaller -  def install_private f ; end -   -  def pyerr dep -    "Python module install message." -  end -   -  def plerr dep -    "Perl module install message." -  end -   -  def rberr dep -    "Ruby module install message." -  end - -  def jrberr dep -    "JRuby module install message." -  end -end - - -  class BadPerlBall <TestBall    depends_on "notapackage" => :perl @@ -102,14 +76,14 @@ end  class ExternalDepsTests < Test::Unit::TestCase    def check_deps_fail f -    assert_raises(RuntimeError) do -      DontActuallyInstall.new.install f.new +    assert_raises(UnsatisfiedExternalDependencyError) do +      FormulaInstaller.check_external_deps f.new      end    end    def check_deps_pass f      assert_nothing_raised do -      DontActuallyInstall.new.install f.new +      FormulaInstaller.check_external_deps f.new      end    end diff --git a/Library/Homebrew/test/test_formula.rb b/Library/Homebrew/test/test_formula.rb index aa545f6d4..fd3987168 100644 --- a/Library/Homebrew/test/test_formula.rb +++ b/Library/Homebrew/test/test_formula.rb @@ -5,7 +5,6 @@ ARGV.extend(HomebrewArgvExtension)  require 'test/testball'  require 'utils' -require 'brew.h'  class MostlyAbstractFormula <Formula diff --git a/Library/Homebrew/test/test_formula_install.rb b/Library/Homebrew/test/test_formula_install.rb index db1a2b103..059d7656b 100644 --- a/Library/Homebrew/test/test_formula_install.rb +++ b/Library/Homebrew/test/test_formula_install.rb @@ -26,7 +26,7 @@ class ConfigureTests < Test::Unit::TestCase      f=ConfigureFails.new      begin        f.brew { f.install } -    rescue ExecutionError => e +    rescue BuildError => e        assert e.was_running_configure?      end    end diff --git a/Library/Homebrew/test/test_updater.rb b/Library/Homebrew/test/test_updater.rb index 1d733560b..5659f9c67 100644 --- a/Library/Homebrew/test/test_updater.rb +++ b/Library/Homebrew/test/test_updater.rb @@ -7,7 +7,7 @@ ARGV.extend(HomebrewArgvExtension)  require 'formula'  require 'utils' -require 'update' +require 'cmd/update'  class RefreshBrewMock < RefreshBrew    def in_prefix_expect(expect, returns = '') diff --git a/Library/Homebrew/test/testing_env.rb b/Library/Homebrew/test/testing_env.rb index ffc8b3003..0933185bb 100644 --- a/Library/Homebrew/test/testing_env.rb +++ b/Library/Homebrew/test/testing_env.rb @@ -8,6 +8,7 @@ ABS__FILE__=File.expand_path(__FILE__)  $:.push(File.expand_path(__FILE__+'/../..'))  require 'extend/pathname' +require 'exceptions'  # these are defined in global.rb, but we don't want to break our actual  # homebrew tree, and we do want to test everything :) @@ -26,4 +27,9 @@ at_exit { HOMEBREW_PREFIX.parent.rmtree }  # Test fixtures and files can be found relative to this path  TEST_FOLDER = Pathname.new(ABS__FILE__).parent.realpath +require 'fileutils' +module Homebrew extend self +  include FileUtils +end +  require 'test/unit' # must be after at_exit diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index de16df994..6dedf764b 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -1,38 +1,3 @@ -class ExecutionError <RuntimeError -  attr :exit_status -  attr :command - -  def initialize cmd, args = [], es = nil -    @command = cmd -    super "Failure while executing: #{cmd} #{pretty(args)*' '}" -    @exit_status = es.exitstatus rescue 1 -  end - -  def was_running_configure? -    @command == './configure' -  end - -  private - -  def pretty args -    args.collect do |arg| -      if arg.to_s.include? ' ' -        "'#{ arg.gsub "'", "\\'" }'" -      else -        arg -      end -    end -  end -end - -class BuildError <ExecutionError -  attr :env - -  def initialize cmd, args = [], es = nil -    super -    @env = ENV.to_hash -  end -end  class Tty    class <<self @@ -112,7 +77,10 @@ end  # Kernel.system but with exceptions  def safe_system cmd, *args -  raise ExecutionError.new(cmd, args, $?) unless Homebrew.system(cmd, *args) +  unless Homebrew.system cmd, *args +    args = args.map{ |arg| arg.gsub " ", "\\ " } * " " +    raise "Failure while executing: #{cmd} #{args}" +  end  end  # prints no output @@ -263,28 +231,97 @@ def nostdout    end  end -def dump_build_env env -  puts "\"--use-llvm\" was specified" if ARGV.include? '--use-llvm' +module MacOS extend self +  def gcc_42_build_version +    `/usr/bin/gcc-4.2 -v 2>&1` =~ /build (\d{4,})/ +    if $1 +      $1.to_i +    elsif system "/usr/bin/which gcc" +      # Xcode 3.0 didn't come with gcc-4.2 +      # We can't change the above regex to use gcc because the version numbers +      # are different and thus, not useful. +      # FIXME I bet you 20 quid this causes a side effect — magic values tend to +      401 +    else +      nil +    end +  end -  %w[ CC CXX LD ].each do |k| -    value = env[k] -    if value -      results = value -      if File.exists? value and File.symlink? value -        target = Pathname.new(value) -        results += " => #{target.realpath}" -      end -      puts "#{k}: #{results}" +  def gcc_40_build_version +    `/usr/bin/gcc-4.0 -v 2>&1` =~ /build (\d{4,})/ +    if $1 +      $1.to_i +    else +      nil      end    end -  %w[ CFLAGS CXXFLAGS CPPFLAGS LDFLAGS MACOSX_DEPLOYMENT_TARGET MAKEFLAGS PKG_CONFIG_PATH -      HOMEBREW_DEBUG HOMEBREW_VERBOSE HOMEBREW_USE_LLVM HOMEBREW_SVN ].each do |k| -    value = env[k] -    puts "#{k}: #{value}" if value +  def llvm_build_version +    if MACOS_VERSION >= 10.6 +      xcode_path = `/usr/bin/xcode-select -print-path`.chomp +      return nil if xcode_path.empty? +      `#{xcode_path}/usr/bin/llvm-gcc -v 2>&1` =~ /LLVM build (\d{4,})/ +      $1.to_i +    end    end -end  def x11_installed?    Pathname.new('/usr/X11/lib/libpng.dylib').exist? -end
\ No newline at end of file +end + +  def macports_or_fink_installed? +    # See these issues for some history: +    # http://github.com/mxcl/homebrew/issues/#issue/13 +    # http://github.com/mxcl/homebrew/issues/#issue/41 +    # http://github.com/mxcl/homebrew/issues/#issue/48 + +    %w[port fink].each do |ponk| +      path = `/usr/bin/which -s #{ponk}` +      return ponk unless path.empty? +    end + +    # we do the above check because macports can be relocated and fink may be +    # able to be relocated in the future. This following check is because if +    # fink and macports are not in the PATH but are still installed it can +    # *still* break the build -- because some build scripts hardcode these paths: +    %w[/sw/bin/fink /opt/local/bin/port].each do |ponk| +      return ponk if File.exist? ponk +    end + +    # finally, sometimes people make their MacPorts or Fink read-only so they +    # can quickly test Homebrew out, but still in theory obey the README's +    # advise to rename the root directory. This doesn't work, many build scripts +    # error out when they try to read from these now unreadable directories. +    %w[/sw /opt/local].each do |path| +      path = Pathname.new(path) +      return path if path.exist? and not path.readable? +    end + +    false +  end +end + +module GitHub extend self +  def issues_for_formula name +    # bit basic as depends on the issue at github having the exact name of the +    # formula in it. Which for stuff like objective-caml is unlikely. So we +    # really should search for aliases too. + +    name = f.name if Formula === name + +    require 'open-uri' +    require 'yaml' + +    issues = [] + +    open "http://github.com/api/v2/yaml/issues/search/mxcl/homebrew/open/#{name}" do |f| +      YAML::load(f.read)['issues'].each do |issue| +        issues << 'http://github.com/mxcl/homebrew/issues/#issue/%s' % issue['number'] +      end +    end + +    issues +  rescue +    [] +  end +end @@ -38,313 +38,48 @@ if MACOS_VERSION < 10.5    abort "Homebrew requires Leopard or higher. For Tiger support, see:\nhttp://github.com/sceaga/homebrew/tree/tiger"  end -def dump_config -  require 'hardware' -  sha = `cd #{HOMEBREW_REPOSITORY} && git rev-parse --verify HEAD 2> /dev/null`.chomp -  sha = "(none)" if sha.empty? -  bits = Hardware.bits -  cores = Hardware.cores_as_words -  kernel_arch = `uname -m`.chomp -  system_ruby = Pathname.new("/usr/bin/ruby") - -  llvm,   llvm_msg   = _compiler_recommendation llvm_build,   RECOMMENDED_LLVM -  gcc_42, gcc_42_msg = _compiler_recommendation gcc_42_build, RECOMMENDED_GCC_42 -  gcc_40, gcc_40_msg = _compiler_recommendation gcc_40_build, RECOMMENDED_GCC_40 -  xcode = xcode_version || "?" - -  puts <<-EOS -HOMEBREW_VERSION: #{HOMEBREW_VERSION} -HEAD: #{sha} -HOMEBREW_PREFIX: #{HOMEBREW_PREFIX} -HOMEBREW_CELLAR: #{HOMEBREW_CELLAR} -HOMEBREW_REPOSITORY: #{HOMEBREW_REPOSITORY} -HOMEBREW_LIBRARY_PATH: #{HOMEBREW_LIBRARY_PATH} -Hardware: #{cores}-core #{bits}-bit #{Hardware.intel_family} -OS X: #{MACOS_FULL_VERSION} -Kernel Architecture: #{kernel_arch} -Ruby: #{RUBY_VERSION}-#{RUBY_PATCHLEVEL} -/usr/bin/ruby => #{system_ruby.realpath} -Xcode: #{xcode} -GCC-4.0: #{gcc_40 ? "build #{gcc_40}" : "N/A"} #{gcc_42_msg} -GCC-4.2: #{gcc_42 ? "build #{gcc_42}" : "N/A"} #{gcc_40_msg} -LLVM: #{llvm ? "build #{llvm}" : "N/A" } #{llvm_msg} -MacPorts or Fink? #{macports_or_fink_installed?} -X11 installed? #{x11_installed?} -EOS +def require? path +  require path.to_s.chomp +rescue LoadError => e +  # HACK :( because we should raise on syntax errors but +  # not if the file doesn't exist. TODO make robust! +  raise unless e.to_s.include? path  end  begin -  require 'brew.h' - -  case arg = ARGV.shift -  when '--cache' -    if ARGV.named.empty? -      puts HOMEBREW_CACHE -    else -      puts ARGV.formulae.collect {|f| f.cached_download} -    end -  when '--prefix' -    if ARGV.named.empty? -      puts HOMEBREW_PREFIX -    else -      puts ARGV.formulae.collect {|f| f.prefix} -    end -  when '--repository' -    puts HOMEBREW_REPOSITORY -  when '--cellar' -    if ARGV.named.empty? -      puts HOMEBREW_CELLAR -    else -      puts ARGV.formulae.collect {|f| HOMEBREW_CELLAR+f.name} -    end -  when '--config' -    dump_config -  when '--env' -    require 'hardware' -    require 'extend/ENV' -    ENV.extend(HomebrewEnvExtension) -    ENV.setup_build_environment -    dump_build_env ENV - -    when 'home', 'homepage' -      if ARGV.named.empty? -        exec "open", HOMEBREW_WWW -      else -        exec "open", *ARGV.formulae.collect {|f| f.homepage} -      end - -    when 'ls', 'list' -      if ARGV.flag? '--unbrewed' -        dirs = HOMEBREW_PREFIX.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s } -        dirs -= ['Library', 'Cellar', '.git'] -        Dir.chdir HOMEBREW_PREFIX -        exec 'find', *dirs + %w[-type f ( ! -iname .ds_store ! -iname brew )] -      elsif ARGV.flag? '--versions' -        if ARGV.named.empty? -          to_list = HOMEBREW_CELLAR.children.select { |pn| pn.directory? } -        else -          to_list = ARGV.named.collect { |n| HOMEBREW_CELLAR+n }.select { |pn| pn.exist? } -        end -        to_list.each do |d| -          versions = d.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s } -          puts "#{d.basename} #{versions *' '}" -        end -      elsif ARGV.named.empty? -        ENV['CLICOLOR']=nil -        exec 'ls', *ARGV.options_only<<HOMEBREW_CELLAR if HOMEBREW_CELLAR.exist? -      elsif ARGV.verbose? or not $stdout.tty? -        exec "find", *ARGV.kegs+%w[-not -type d -print] -      else -        ARGV.kegs.each { |keg| PrettyListing.new keg } -      end - -    when 'search', '-S' -      if ARGV.include? '--macports' -        exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" -      elsif ARGV.include? '--fink' -        exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" -      end - -      check_for_blacklisted_formula(ARGV.named) -      puts_columns search_brews(ARGV.first) - -    when 'edit' -      if ARGV.named.empty? -        # EDITOR isn't a good fit here, we need a GUI client that actually has -        # a UI for projects, so apologies if this wasn't what you expected, -        # please improve it! :) -        exec 'mate', *Dir["#{HOMEBREW_REPOSITORY}/Library/*"]<< -                          "#{HOMEBREW_REPOSITORY}/bin/brew"<< -                          "#{HOMEBREW_REPOSITORY}/README.md" -      else -        require 'formula' -        # Don't use ARGV.formulae as that will throw if the file doesn't parse -        paths = ARGV.named.collect do |name| -          path = Formula.path(Formula.resolve_alias(name)) -          unless File.exist? path -            raise FormulaUnavailableError, name -          else -            path -          end -        end -        exec_editor(*paths) -      end - -  when 'up', 'update' -    abort "Please `brew install git' first." unless system "/usr/bin/which -s git" - -    require 'update' -    updater = RefreshBrew.new -    unless updater.update_from_masterbrew! -      puts "Already up-to-date." -    else -      updater.report -    end - -    when 'ln', 'link' -      ARGV.kegs.each {|keg| puts "#{keg.link} links created for #{keg}"} - -    when 'unlink' -      ARGV.kegs.each {|keg| puts "#{keg.unlink} links removed for #{keg}"} - -    when 'rm', 'uninstall', 'remove' -      if ARGV.flag? "--force" -        require 'formula' -        ARGV.formulae.each do |f| -          formula_cellar = f.prefix.parent -          next unless File.exist? formula_cellar -          puts "Uninstalling #{f.name}..." -          formula_cellar.children do |k| -            keg = Keg.new(k) -            keg.unlink -          end - -          formula_cellar.rmtree -        end -      else -        begin -          ARGV.kegs.each do |keg| -            puts "Uninstalling #{keg}..." -            keg.unlink -            keg.uninstall -          end -        rescue MultipleVersionsInstalledError => e -          onoe e -          puts "Use `brew remove --force #{e.name}` to remove all versions." -        end -      end - -    when 'prune' -      prune - -    when 'create' -      if ARGV.include? '--macports' -        exec "open", "http://www.macports.org/ports.php?by=name&substr=#{ARGV.next}" -      elsif ARGV.include? '--fink' -        exec "open", "http://pdb.finkproject.org/pdb/browse.php?summary=#{ARGV.next}" -      elsif ARGV.named.empty? -        raise UsageError -      else -        exec_editor(*ARGV.named.collect {|name| make name}) -      end - -    when 'diy', 'configure' -      puts diy - -    when 'info', 'abv' -      if ARGV.named.empty? -        if ARGV.include? "--all" -          require 'formula' -          Formula.all.each do |f| -            info f -            puts '---' -          end -        else -          puts `ls #{HOMEBREW_CELLAR} | wc -l`.strip+" kegs, "+HOMEBREW_CELLAR.abv -        end -      elsif ARGV[0][0..6] == 'http://' or ARGV[0][0..7] == 'https://' or ARGV[0][0..5] == 'ftp://' -        path = Pathname.new(ARGV.shift) -        /(.*?)[-_.]?#{path.version}/.match path.basename -        unless $1.to_s.empty? -          name = $1 -        else -          name = path.stem -        end -        puts "#{name} #{path.version}" -      else -        ARGV.formulae.each{ |f| info f } -      end - -  when 'cleanup' -    if ARGV.named.empty? -      require 'formula' -      HOMEBREW_CELLAR.children.each do |rack| -        begin -          cleanup(rack.basename.to_s) if rack.directory? -        rescue FormulaUnavailableError => e -          opoo "Formula not found for #{e.name}" -        end -      end -      prune # seems like a good time to do some additional cleanup -    else -      ARGV.named.each { |name| cleanup name} -    end - -  when 'install' -    check_for_blacklisted_formula(ARGV.named) -    brew_install - -  when 'log' -    Dir.chdir HOMEBREW_REPOSITORY -    args = ARGV.options_only -    args += ARGV.formulae.map{ |formula| formula.path } unless ARGV.named.empty? -    exec "git", "log", *args - -  # For each formula given, show which other formulas depend on it. -  # We only go one level up, ie. direct dependencies. -  when 'uses' -    uses = ARGV.formulae.map{ |f| Formula.all.select{ |ff| ff.deps.include? f.name }.map{|f| f.name} }.flatten.uniq -    if ARGV.include? "--installed" -      uses = uses.select { |f| f = HOMEBREW_CELLAR+f; f.directory? and not f.subdirs.empty? } -    end -    puts uses.sort - -  when 'deps' -    if ARGV.include?('--all') -      require 'formula' -      Formula.all.each do |f| -        puts "#{f.name}:#{f.deps.join(' ')}" -      end -    elsif ARGV.include?("-1") or ARGV.include?("--1") -      puts ARGV.formulae.map {|f| f.deps or []}.flatten.uniq.sort -    else -      require 'formula_installer' -      puts ARGV.formulae.map {|f| FormulaInstaller.expand_deps(f).map {|f| f.name} }.flatten.uniq.sort -    end - -  when 'cat' -    Dir.chdir HOMEBREW_REPOSITORY -    exec "cat", ARGV.formulae.first.path, *ARGV.options_only - -  when 'outdated' -    outdated_brews.each do |keg, name, version| -      if $stdout.tty? and not ARGV.flag? '--quiet' -        versions = keg.cd{ Dir['*'] }.join(', ') -        puts "#{name} (#{versions} < #{version})" -      else -        puts name -      end +  aliases = {'ls' => :list, +             'homepage' => :home, +             '-S' => :search, +             'up' => :update, +             'ln' => :link, +             'rm' => :uninstall, +             'remove' => :uninstall, +             'configure' => :diy, +             'abv' => :info, +             'dr' => :doctor, +             '--repo' => '--repository'} + +  cmd = ARGV.shift +  cmd = aliases[cmd] if aliases[cmd] + +  # Add example external commands to PATH before checking. +  ENV['PATH'] += ":#{HOMEBREW_REPOSITORY}/Library/Contributions/examples" + +  if system "/usr/bin/which -s brew-#{cmd}" +    %w[CACHE CELLAR LIBRARY_PATH PREFIX REPOSITORY].each do |e| +      ENV["HOMEBREW_#{e}"] = eval "HOMEBREW_#{e}"      end - -  when 'doctor', 'dr' -    require 'brew_doctor' -    brew_doctor - +    exec "brew-#{cmd}", *ARGV +  elsif require? `/usr/bin/which brew-#{cmd}.rb` +    exit 0 +  elsif require? HOMEBREW_REPOSITORY/"Library/Homebrew/cmd"/cmd +    Homebrew.send cmd.to_s.gsub('-', '_')    else -    # Add example external commands to PATH before checking. -    ENV['PATH'] += ":#{HOMEBREW_REPOSITORY}/Library/Contributions/examples" - -    # Check for an external shell command -    if system "/usr/bin/which -s brew-#{arg}" -      # Add some Homebrew vars to the ENV -      %w(CACHE CELLAR LIBRARY_PATH PREFIX REPOSITORY).each do |e| -        ENV["HOMEBREW_#{e}"] = eval("HOMEBREW_#{e}") -      end -      exec("brew-#{arg}", *ARGV) -    end - -    # Check for an external ruby command -    external_rb = `/usr/bin/which brew-#{arg}.rb`.chomp -    unless external_rb.empty? -      require external_rb -      exit 0 -    end -      # Check for git commands -    if %w(branch checkout pull push rebase reset).include? arg -      onoe "Unknown command '#{arg}' (did you mean 'git #{arg}'?)" +    if %w[branch checkout pull push rebase reset].include? cmd +      onoe "Unknown command: #{cmd} (did you mean `git #{cmd}'?)"      else -      onoe "Unknown command '#{arg}'" +      onoe "Unknown command: #{cmd}"      end    end @@ -362,24 +97,28 @@ rescue Interrupt => e    puts # seemingly a newline is typical    exit 130  rescue BuildError => e +  require 'cmd/--config' +  require 'cmd/--env' +    e.backtrace[1] =~ %r{Library/Formula/(.+)\.rb:(\d+)}    formula_name = $1    error_line = $2 +    puts "Exit status: #{e.exit_status}"    puts    puts "http://github.com/mxcl/homebrew/blob/master/Library/Formula/#{formula_name}.rb#L#{error_line}"    puts    ohai "Environment" -  dump_config +  puts Homebrew.config_s    puts    ohai "Build Flags" -  dump_build_env e.env +  Homebrew.dump_build_env e.env    onoe e    puts PLEASE_REPORT_BUG    # this feature can be slow (depends on network conditions and if github is up)    # so ideally we'd show feedback, eg. "checking for existing issues..." and    # then replace that string with the following when the github api returns -  issues = issues_for_formula(formula_name) +  issues = GitHub.issues_for_formula formula_name    puts "These existing issues may help you:", *issues unless issues.empty?    if e.was_running_configure?      puts "It looks like an autotools configure failed." | 
