From 768910283a40afc2f76ef196d1464f0bbb2349cd Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 11 Sep 2010 20:22:54 +0100 Subject: Refactor the brew command into one file per command The code was sucking. To the extent that maintenance was hard. It's a lot easier to work with code that is sensibly split at sensible boundaries. So now it is more like that. But the refactor is minimal. Because we don't want you to have more merge hell than absolutely necessary. If you merge you will need to pay attention to brew.h.rb (as it is deleted) and bin/brew (as command logic is gone). It will be painful, but you will just have to help git out by moving any changes around manually. Note compatibility.rb. It ensures that any function renames or removals don't break anything. We're pretty serious about backwards compatibility. And that's because we encourage you to hack around with the innards. And we couldn't do that if we would then just make stuff disappear behind your back. --- Library/Contributions/examples/brew-missing.rb | 17 +- Library/Contributions/examples/brew-server | 1 - Library/Contributions/examples/brew-upgrade.rb | 10 +- Library/Homebrew/beer_events.rb | 199 -------- Library/Homebrew/brew.h.rb | 600 ----------------------- Library/Homebrew/brew_doctor.rb | 631 ------------------------ Library/Homebrew/cleaner.rb | 15 + Library/Homebrew/cmd/--cache.rb | 9 + Library/Homebrew/cmd/--cellar.rb | 9 + Library/Homebrew/cmd/--config.rb | 66 +++ Library/Homebrew/cmd/--env.rb | 32 ++ Library/Homebrew/cmd/--prefix.rb | 9 + Library/Homebrew/cmd/--repository.rb | 5 + Library/Homebrew/cmd/cat.rb | 10 + Library/Homebrew/cmd/cleanup.rb | 42 ++ Library/Homebrew/cmd/create.rb | 153 ++++++ Library/Homebrew/cmd/deps.rb | 14 + Library/Homebrew/cmd/diy.rb | 34 ++ Library/Homebrew/cmd/doctor.rb | 633 +++++++++++++++++++++++++ Library/Homebrew/cmd/edit.rb | 26 + Library/Homebrew/cmd/home.rb | 9 + Library/Homebrew/cmd/info.rb | 92 ++++ Library/Homebrew/cmd/install.rb | 87 ++++ Library/Homebrew/cmd/link.rb | 8 + Library/Homebrew/cmd/list.rb | 88 ++++ Library/Homebrew/cmd/log.rb | 10 + Library/Homebrew/cmd/outdated.rb | 28 ++ Library/Homebrew/cmd/prune.rb | 31 ++ Library/Homebrew/cmd/search.rb | 42 ++ Library/Homebrew/cmd/uninstall.rb | 31 ++ Library/Homebrew/cmd/unlink.rb | 8 + Library/Homebrew/cmd/update.rb | 159 +++++++ Library/Homebrew/cmd/uses.rb | 25 + Library/Homebrew/compatibility.rb | 47 ++ Library/Homebrew/exceptions.rb | 99 ++++ Library/Homebrew/extend/ENV.rb | 2 +- Library/Homebrew/formula.rb | 66 ++- Library/Homebrew/global.rb | 10 + Library/Homebrew/install.rb | 5 +- Library/Homebrew/keg.rb | 4 +- Library/Homebrew/test/test_ENV.rb | 1 - Library/Homebrew/test/test_bucket.rb | 28 +- Library/Homebrew/test/test_external_deps.rb | 34 +- Library/Homebrew/test/test_formula.rb | 1 - Library/Homebrew/test/test_formula_install.rb | 2 +- Library/Homebrew/test/test_updater.rb | 2 +- Library/Homebrew/test/testing_env.rb | 6 + Library/Homebrew/update.rb | 146 ------ Library/Homebrew/utils.rb | 143 +++--- 49 files changed, 2018 insertions(+), 1711 deletions(-) delete mode 100644 Library/Homebrew/beer_events.rb delete mode 100644 Library/Homebrew/brew.h.rb delete mode 100644 Library/Homebrew/brew_doctor.rb create mode 100644 Library/Homebrew/cmd/--cache.rb create mode 100644 Library/Homebrew/cmd/--cellar.rb create mode 100644 Library/Homebrew/cmd/--config.rb create mode 100644 Library/Homebrew/cmd/--env.rb create mode 100644 Library/Homebrew/cmd/--prefix.rb create mode 100644 Library/Homebrew/cmd/--repository.rb create mode 100644 Library/Homebrew/cmd/cat.rb create mode 100644 Library/Homebrew/cmd/cleanup.rb create mode 100644 Library/Homebrew/cmd/create.rb create mode 100644 Library/Homebrew/cmd/deps.rb create mode 100644 Library/Homebrew/cmd/diy.rb create mode 100644 Library/Homebrew/cmd/doctor.rb create mode 100644 Library/Homebrew/cmd/edit.rb create mode 100644 Library/Homebrew/cmd/home.rb create mode 100644 Library/Homebrew/cmd/info.rb create mode 100644 Library/Homebrew/cmd/install.rb create mode 100644 Library/Homebrew/cmd/link.rb create mode 100644 Library/Homebrew/cmd/list.rb create mode 100644 Library/Homebrew/cmd/log.rb create mode 100644 Library/Homebrew/cmd/outdated.rb create mode 100644 Library/Homebrew/cmd/prune.rb create mode 100644 Library/Homebrew/cmd/search.rb create mode 100644 Library/Homebrew/cmd/uninstall.rb create mode 100644 Library/Homebrew/cmd/unlink.rb create mode 100644 Library/Homebrew/cmd/update.rb create mode 100644 Library/Homebrew/cmd/uses.rb create mode 100644 Library/Homebrew/compatibility.rb create mode 100644 Library/Homebrew/exceptions.rb delete mode 100644 Library/Homebrew/update.rb (limited to 'Library') 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 -# -# 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} - 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< 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/brew_doctor.rb b/Library/Homebrew/brew_doctor.rb deleted file mode 100644 index b26cd4f5d..000000000 --- a/Library/Homebrew/brew_doctor.rb +++ /dev/null @@ -1,631 +0,0 @@ -class Volumes - def initialize - @volumes = [] - raw_mounts=`mount` - raw_mounts.split("\n").each do |line| - case line - when /^(.+) on (\S+) \(/ - @volumes << [$1, $2] - end - end - # Sort volumes by longest path prefix first - @volumes.sort! {|a,b| b[1].length <=> a[1].length} - end - - def which path - @volumes.each_index do |i| - vol = @volumes[i] - return i if is_prefix?(vol[1], path) - end - - return -1 - end -end - - -def is_prefix? prefix, longer_string - p = prefix.to_s - longer_string.to_s[0,p.length] == p -end - -# Installing MacGPG2 interferes with Homebrew in a big way -# http://sourceforge.net/projects/macgpg2/files/ -def check_for_macgpg2 - if File.exist? "/Applications/start-gpg-agent.app" or - File.exist? "/Library/Receipts/libiconv1.pkg" - puts <<-EOS.undent - If you have installed MacGPG2 via the package installer, several other - checks in this script will turn up problems, such as stray .dylibs in - /usr/local and permissions issues with share and man in /usr/local/. - - EOS - end -end - -def check_for_stray_dylibs - unbrewed_dylibs = Dir['/usr/local/lib/*.dylib'].select { |f| File.file? f and not File.symlink? f } - - # Dylibs which are generally OK should be added to this list, - # with a short description of the software they come with. - white_list = { - "libfuse.2.dylib" => "MacFuse", - "libfuse_ino64.2.dylib" => "MacFuse" - } - - bad_dylibs = unbrewed_dylibs.reject {|d| white_list.key? File.basename(d) } - return if bad_dylibs.empty? - - opoo "Unbrewed dylibs were found in /usr/local/lib" - puts <<-EOS.undent - You have unbrewed dylibs in /usr/local/lib. If you didn't put them there on purpose, - they could cause problems when building Homebrew formulae. - - Unexpected dylibs (delete if they are no longer needed): - EOS - puts *bad_dylibs.collect { |f| " #{f}" } - puts -end - -def check_for_x11 - unless x11_installed? - opoo "X11 not installed." - puts <<-EOS.undent - You don't have X11 installed as part of your Xcode installation. - This isn't required for all formulae, but is expected by some. - - EOS - end -end - -def check_for_nonstandard_x11 - return unless File.exists? '/usr/X11' - x11 = Pathname.new('/usr/X11') - if x11.symlink? - puts <<-EOS.undent - "/usr/X11" was found, but it is a symlink to: - #{x11.resolved_path} - - Homebrew's X11 support has only be tested with Apple's X11. - In particular, "XQuartz" and "XDarwin" are not known to be compatible. - - EOS - end -end - -def check_for_other_package_managers - if macports_or_fink_installed? - puts <<-EOS.undent - You have Macports or Fink installed. This can cause trouble. - You don't have to uninstall them, but you may like to try temporarily - moving them away, eg. - - sudo mv /opt/local ~/macports - - EOS - end -end - -def check_gcc_versions - gcc_42 = gcc_42_build - gcc_40 = gcc_40_build - - if gcc_42 == nil - puts <<-EOS.undent - We couldn't detect gcc 4.2.x. Some formulae require this compiler. - - EOS - elsif gcc_42 < RECOMMENDED_GCC_42 - puts <<-EOS.undent - Your gcc 4.2.x version is older than the recommended version. It may be advisable - to upgrade to the latest release of Xcode. - - EOS - end - - if gcc_40 == nil - puts <<-EOS.undent - We couldn't detect gcc 4.0.x. Some formulae require this compiler. - - EOS - elsif gcc_40 < RECOMMENDED_GCC_40 - puts <<-EOS.undent - Your gcc 4.0.x version is older than the recommended version. It may be advisable - to upgrade to the latest release of Xcode. - - EOS - end -end - -def check_cc_symlink - which_cc = Pathname.new('/usr/bin/cc').realpath.basename.to_s - if which_cc == "llvm-gcc-4.2" - puts <<-EOS.undent - You changed your cc to symlink to llvm. - This bypasses LLVM checks, and some formulae may mysteriously fail to work. - You may want to change /usr/bin/cc to point back at gcc. - - To force Homebrew to use LLVM, you can set the "HOMEBREW_LLVM" environmental - variable, or pass "--use-llvm" to "brew install". - - EOS - end -end - -def __check_subdir_access base - target = HOMEBREW_PREFIX+base - return unless target.exist? - - cant_read = [] - - target.find do |d| - next unless d.directory? - cant_read << d unless d.writable? - end - - cant_read.sort! - if cant_read.length > 0 - puts <<-EOS.undent - Some folders in #{target} aren't writable. - This can happen if you "sudo make install" software that isn't managed - by Homebrew. If a brew tries to add locale information to one of these - folders, then the install will fail during the link step. - You should probably `chown` them: - - EOS - puts *cant_read.collect { |f| " #{f}" } - puts - end -end - -def check_access_share_locale - __check_subdir_access 'share/locale' -end - -def check_access_share_man - __check_subdir_access 'share/man' -end - -def check_access_pkgconfig - # If PREFIX/lib/pkgconfig already exists, "sudo make install" of - # non-brew installed software may cause installation failures. - pkgconfig = HOMEBREW_PREFIX+'lib/pkgconfig' - return unless pkgconfig.exist? - - unless pkgconfig.writable? - puts <<-EOS.undent - #{pkgconfig} isn't writable. - This can happen if you "sudo make install" software that isn't managed - by Homebrew. If a brew tries to write a .pc file to this folder, the - install will fail during the link step. - - You should probably `chown` #{pkgconfig} - - EOS - end -end - -def check_access_include - # Installing MySQL manually (for instance) can chown include to root. - include_folder = HOMEBREW_PREFIX+'include' - return unless include_folder.exist? - - unless include_folder.writable? - puts <<-EOS.undent - #{include_folder} isn't writable. - This can happen if you "sudo make install" software that isn't managed - by Homebrew. If a brew tries to write a header file to this folder, the - install will fail during the link step. - - You should probably `chown` #{include_folder} - - EOS - end -end - -def check_access_etc - etc_folder = HOMEBREW_PREFIX+'etc' - return unless etc_folder.exist? - - unless etc_folder.writable? - puts <<-EOS.undent - #{etc_folder} isn't writable. - This can happen if you "sudo make install" software that isn't managed - by Homebrew. If a brew tries to write a file to this folder, the install - will fail during the link step. - - You should probably `chown` #{etc_folder} - - EOS - end -end - -def check_usr_bin_ruby - if /^1\.9/.match RUBY_VERSION - puts <<-EOS.undent - Ruby version #{RUBY_VERSION} is unsupported. - Homebrew is developed and tested on Ruby 1.8.x, and may not work correctly - on Ruby 1.9.x. Patches are accepted as long as they don't break on 1.8.x. - - EOS - end -end - -def check_homebrew_prefix - unless HOMEBREW_PREFIX.to_s == '/usr/local' - puts <<-EOS.undent - You can install Homebrew anywhere you want, but some brews may only work - correctly if you install to /usr/local. - - EOS - end -end - -def check_user_path - seen_prefix_bin = false - seen_prefix_sbin = false - seen_usr_bin = false - - paths = ENV['PATH'].split(':').collect{|p| File.expand_path p} - - paths.each do |p| - if p == '/usr/bin' - seen_usr_bin = true - unless seen_prefix_bin - puts <<-EOS.undent - /usr/bin is in your PATH before Homebrew's bin. This means that system- - provided programs will be used before Homebrew-provided ones. This is an - issue if you install, for instance, Python. - - Consider editing your .bashrc to put: - #{HOMEBREW_PREFIX}/bin - ahead of /usr/bin in your $PATH. - - EOS - end - end - - seen_prefix_bin = true if p == "#{HOMEBREW_PREFIX}/bin" - seen_prefix_sbin = true if p == "#{HOMEBREW_PREFIX}/sbin" - end - - unless seen_prefix_bin - puts <<-EOS.undent - Homebrew's bin was not found in your path. Some brews depend - on other brews that install tools to bin. - - You should edit your .bashrc to add: - #{HOMEBREW_PREFIX}/bin - to $PATH. - - EOS - end - - unless seen_prefix_sbin - puts <<-EOS.undent - Some brews install binaries to sbin instead of bin, but Homebrew's - sbin was not found in your path. - - Consider editing your .bashrc to add: - #{HOMEBREW_PREFIX}/sbin - to $PATH. - - EOS - end -end - -def check_which_pkg_config - binary = `/usr/bin/which pkg-config`.chomp - return if binary.empty? - - unless binary == "#{HOMEBREW_PREFIX}/bin/pkg-config" - puts <<-EOS.undent - You have a non-brew 'pkg-config' in your PATH: - #{binary} - - `./configure` may have problems finding brew-installed packages using - this other pkg-config. - - EOS - end -end - -def check_pkg_config_paths - binary = `/usr/bin/which pkg-config`.chomp - return if binary.empty? - - # Use the debug output to determine which paths are searched - pkg_config_paths = [] - - debug_output = `pkg-config --debug 2>&1` - debug_output.split("\n").each do |line| - line =~ /Scanning directory '(.*)'/ - pkg_config_paths << $1 if $1 - end - - # Check that all expected paths are being searched - unless pkg_config_paths.include? "/usr/X11/lib/pkgconfig" - puts <<-EOS.undent - Your pkg-config is not checking "/usr/X11/lib/pkgconfig" for packages. - Earlier versions of the pkg-config formula did not add this path - to the search path, which means that other formula may not be able - to find certain dependencies. - - To resolve this issue, re-brew pkg-config with: - brew rm pkg-config && brew install pkg-config - - EOS - end -end - -def check_for_gettext - if %w[lib/libgettextlib.dylib - lib/libintl.dylib - include/libintl.h ].any? { |f| File.exist? "#{HOMEBREW_PREFIX}/#{f}" } - puts <<-EOS.undent - gettext was detected in your PREFIX. - - The gettext provided by Homebrew is "keg-only", meaning it does not - get linked into your PREFIX by default. - - If you `brew link gettext` then a large number of brews that don't - otherwise have a `depends_on 'gettext'` will pick up gettext anyway - during the `./configure` step. - - If you have a non-Homebrew provided gettext, other problems will happen - especially if it wasn't compiled with the proper architectures. - - EOS - end -end - -def check_for_iconv - if %w[lib/libiconv.dylib - include/iconv.h ].any? { |f| File.exist? "#{HOMEBREW_PREFIX}/#{f}" } - puts <<-EOS.undent - libiconv was detected in your PREFIX. - - Homebrew doesn't provide a libiconv formula, and expects to link against - the system version in /usr/lib. - - If you have a non-Homebrew provided libiconv, many formulae will fail - to compile or link, especially if it wasn't compiled with the proper - architectures. - - EOS - end -end - -def check_for_config_scripts - real_cellar = HOMEBREW_CELLAR.realpath - - config_scripts = [] - - paths = ENV['PATH'].split(':').collect{|p| File.expand_path p} - paths.each do |p| - next if ['/usr/bin', '/usr/sbin', '/usr/X11/bin', "#{HOMEBREW_PREFIX}/bin", "#{HOMEBREW_PREFIX}/sbin"].include? p - next if p =~ %r[^(#{real_cellar.to_s}|#{HOMEBREW_CELLAR.to_s})] - - configs = Dir["#{p}/*-config"] - # puts "#{p}\n #{configs * ' '}" unless configs.empty? - config_scripts << [p, configs.collect {|p| File.basename(p)}] unless configs.empty? - end - - unless config_scripts.empty? - puts <<-EOS.undent - Some "config" scripts were found in your path, but not in system or Homebrew folders. - - `./configure` scripts often look for *-config scripts to determine if software packages - are installed, and what additional flags to use when compiling and linking. - - Having additional scripts in your path can confuse software installed via Homebrew if - the config script overrides a system or Homebrew provided script of the same name. - - EOS - - config_scripts.each do |pair| - puts pair[0] - puts " " + pair[1] * " " - end - puts - end -end - -def check_for_dyld_vars - if ENV['DYLD_LIBRARY_PATH'] - puts <<-EOS.undent - Setting DYLD_LIBARY_PATH can break dynamic linking. - You should probably unset it. - - EOS - end -end - -def check_for_symlinked_cellar - if HOMEBREW_CELLAR.symlink? - puts <<-EOS.undent - Symlinked Cellars can cause problems. - Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR} - which resolves to: #{HOMEBREW_CELLAR.realpath} - - The recommended Homebrew installations are either: - (A) Have Cellar be a real folder inside of your HOMEBREW_PREFIX - (B) Symlink "bin/brew" into your prefix, but don't symlink "Cellar". - - Older installations of Homebrew may have created a symlinked Cellar, but this can - cause problems when two formula install to locations that are mapped on top of each - other during the linking step. - - EOS - end -end - -def check_for_multiple_volumes - volumes = Volumes.new - - # Find the volumes for the TMP folder & HOMEBREW_CELLAR - real_cellar = HOMEBREW_CELLAR.realpath - - tmp_prefix = ENV['HOMEBREW_TEMP'] || '/tmp' - tmp=Pathname.new `/usr/bin/mktemp -d #{tmp_prefix}/homebrew-brew-doctor-XXXX`.strip - real_temp = tmp.realpath.parent - - where_cellar = volumes.which real_cellar - where_temp = volumes.which real_temp - - unless where_cellar == where_temp - puts <<-EOS.undent - Your Cellar & TEMP folders are on different volumes. - - OS X won't move relative symlinks across volumes unless the target file - already exists. - - Brews known to be affected by this are Git and Narwhal. - - You should set the "HOMEBREW_TEMP" environmental variable to a suitable - folder on the same volume as your Cellar. - - EOS - end -end - -def check_for_git - git = `/usr/bin/which git`.chomp - if git.empty? - puts <<-EOS.undent - "Git" was not found in your path. - - Homebrew uses Git for several internal functions, and some formulae - use Git checkouts instead of stable tarballs. - - You may want to do: - brew install git - - EOS - end -end - -def check_for_autoconf - which_autoconf = `/usr/bin/which autoconf`.chomp - unless (which_autoconf == '/usr/bin/autoconf' or which_autoconf == '/Developer/usr/bin/autoconf') - puts <<-EOS.undent - You have an "autoconf" in your path blocking the system version at: - #{which_autoconf} - - Custom autoconf in general and autoconf 2.66 in particular has issues - and will cause some Homebrew formulae to fail. - - EOS - end -end - -def __check_linked_brew f - links_found = [] - - Pathname.new(f.prefix).find do |src| - dst=HOMEBREW_PREFIX+src.relative_path_from(f.prefix) - next unless dst.symlink? - - dst_points_to = dst.realpath() - next unless dst_points_to.to_s == src.to_s - - if src.directory? - Find.prune - else - links_found << dst - end - end - - return links_found -end - -def check_for_linked_kegonly_brews - require 'formula' - - warnings = Hash.new - - Formula.all.each do |f| - next unless f.keg_only? and f.installed? - links = __check_linked_brew f - warnings[f.name] = links unless links.empty? - end - - unless warnings.empty? - puts <<-EOS.undent - Some keg-only formula are linked into the Cellar. - - Linking a keg-only formula, such as gettext, into the cellar with - `brew link f` will cause other formulae to detect them during the - `./configure` step. This may cause problems when compiling those - other formulae. - - Binaries provided by keg-only formulae may override system binaries - with other strange results. - - You may wish to `brew unlink` these brews: - EOS - - puts *warnings.keys.collect { |f| " #{f}" } - end -end - -def check_for_other_vars - target_var = ENV['MACOSX_DEPLOYMENT_TARGET'] - return if target_var.nil? or target_var.empty? - - unless target_var == MACOS_VERSION.to_s - puts <<-EOS.undent - $MACOSX_DEPLOYMENT_TARGET was set to #{target_var} - This is used by Fink, but having it set to a value different from the - current system version (#{MACOS_VERSION}) can cause problems, compiling - Git for instance, and should probably be removed. - - EOS - end -end - -def brew_doctor - read, write = IO.pipe - - if fork == nil - read.close - $stdout.reopen write - - check_usr_bin_ruby - check_homebrew_prefix - check_for_macgpg2 - check_for_stray_dylibs - check_gcc_versions - check_cc_symlink - check_for_other_package_managers - check_for_x11 - check_for_nonstandard_x11 - check_access_share_locale - check_access_share_man - check_access_include - check_access_etc - check_user_path - check_which_pkg_config - check_pkg_config_paths - check_access_pkgconfig - check_for_gettext - check_for_config_scripts - check_for_dyld_vars - check_for_other_vars - check_for_symlinked_cellar - check_for_multiple_volumes - check_for_git - check_for_autoconf - check_for_linked_kegonly_brews - - exit! 0 - else - write.close - - unless (out = read.read).chomp.empty? - puts out - else - puts "Your OS X is ripe for brewing." - puts "Any troubles you may be experiencing are likely purely psychosomatic." - end - end -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} + 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/cmd/doctor.rb b/Library/Homebrew/cmd/doctor.rb new file mode 100644 index 000000000..bad3a5dc0 --- /dev/null +++ b/Library/Homebrew/cmd/doctor.rb @@ -0,0 +1,633 @@ +class Volumes + def initialize + @volumes = [] + raw_mounts=`mount` + raw_mounts.split("\n").each do |line| + case line + when /^(.+) on (\S+) \(/ + @volumes << [$1, $2] + end + end + # Sort volumes by longest path prefix first + @volumes.sort! {|a,b| b[1].length <=> a[1].length} + end + + def which path + @volumes.each_index do |i| + vol = @volumes[i] + return i if is_prefix?(vol[1], path) + end + + return -1 + end +end + + +def is_prefix? prefix, longer_string + p = prefix.to_s + longer_string.to_s[0,p.length] == p +end + +# Installing MacGPG2 interferes with Homebrew in a big way +# http://sourceforge.net/projects/macgpg2/files/ +def check_for_macgpg2 + if File.exist? "/Applications/start-gpg-agent.app" or + File.exist? "/Library/Receipts/libiconv1.pkg" + puts <<-EOS.undent + If you have installed MacGPG2 via the package installer, several other + checks in this script will turn up problems, such as stray .dylibs in + /usr/local and permissions issues with share and man in /usr/local/. + + EOS + end +end + +def check_for_stray_dylibs + unbrewed_dylibs = Dir['/usr/local/lib/*.dylib'].select { |f| File.file? f and not File.symlink? f } + + # Dylibs which are generally OK should be added to this list, + # with a short description of the software they come with. + white_list = { + "libfuse.2.dylib" => "MacFuse", + "libfuse_ino64.2.dylib" => "MacFuse" + } + + bad_dylibs = unbrewed_dylibs.reject {|d| white_list.key? File.basename(d) } + return if bad_dylibs.empty? + + opoo "Unbrewed dylibs were found in /usr/local/lib" + puts <<-EOS.undent + You have unbrewed dylibs in /usr/local/lib. If you didn't put them there on purpose, + they could cause problems when building Homebrew formulae. + + Unexpected dylibs (delete if they are no longer needed): + EOS + puts *bad_dylibs.collect { |f| " #{f}" } + puts +end + +def check_for_x11 + unless x11_installed? + opoo "X11 not installed." + puts <<-EOS.undent + You don't have X11 installed as part of your Xcode installation. + This isn't required for all formulae, but is expected by some. + + EOS + end +end + +def check_for_nonstandard_x11 + return unless File.exists? '/usr/X11' + x11 = Pathname.new('/usr/X11') + if x11.symlink? + puts <<-EOS.undent + "/usr/X11" was found, but it is a symlink to: + #{x11.resolved_path} + + Homebrew's X11 support has only be tested with Apple's X11. + In particular, "XQuartz" and "XDarwin" are not known to be compatible. + + EOS + end +end + +def check_for_other_package_managers + if macports_or_fink_installed? + puts <<-EOS.undent + You have Macports or Fink installed. This can cause trouble. + You don't have to uninstall them, but you may like to try temporarily + moving them away, eg. + + sudo mv /opt/local ~/macports + + EOS + end +end + +def check_gcc_versions + gcc_42 = gcc_42_build + gcc_40 = gcc_40_build + + if gcc_42 == nil + puts <<-EOS.undent + We couldn't detect gcc 4.2.x. Some formulae require this compiler. + + EOS + elsif gcc_42 < RECOMMENDED_GCC_42 + puts <<-EOS.undent + Your gcc 4.2.x version is older than the recommended version. It may be advisable + to upgrade to the latest release of Xcode. + + EOS + end + + if gcc_40 == nil + puts <<-EOS.undent + We couldn't detect gcc 4.0.x. Some formulae require this compiler. + + EOS + elsif gcc_40 < RECOMMENDED_GCC_40 + puts <<-EOS.undent + Your gcc 4.0.x version is older than the recommended version. It may be advisable + to upgrade to the latest release of Xcode. + + EOS + end +end + +def check_cc_symlink + which_cc = Pathname.new('/usr/bin/cc').realpath.basename.to_s + if which_cc == "llvm-gcc-4.2" + puts <<-EOS.undent + You changed your cc to symlink to llvm. + This bypasses LLVM checks, and some formulae may mysteriously fail to work. + You may want to change /usr/bin/cc to point back at gcc. + + To force Homebrew to use LLVM, you can set the "HOMEBREW_LLVM" environmental + variable, or pass "--use-llvm" to "brew install". + + EOS + end +end + +def __check_subdir_access base + target = HOMEBREW_PREFIX+base + return unless target.exist? + + cant_read = [] + + target.find do |d| + next unless d.directory? + cant_read << d unless d.writable? + end + + cant_read.sort! + if cant_read.length > 0 + puts <<-EOS.undent + Some folders in #{target} aren't writable. + This can happen if you "sudo make install" software that isn't managed + by Homebrew. If a brew tries to add locale information to one of these + folders, then the install will fail during the link step. + You should probably `chown` them: + + EOS + puts *cant_read.collect { |f| " #{f}" } + puts + end +end + +def check_access_share_locale + __check_subdir_access 'share/locale' +end + +def check_access_share_man + __check_subdir_access 'share/man' +end + +def check_access_pkgconfig + # If PREFIX/lib/pkgconfig already exists, "sudo make install" of + # non-brew installed software may cause installation failures. + pkgconfig = HOMEBREW_PREFIX+'lib/pkgconfig' + return unless pkgconfig.exist? + + unless pkgconfig.writable? + puts <<-EOS.undent + #{pkgconfig} isn't writable. + This can happen if you "sudo make install" software that isn't managed + by Homebrew. If a brew tries to write a .pc file to this folder, the + install will fail during the link step. + + You should probably `chown` #{pkgconfig} + + EOS + end +end + +def check_access_include + # Installing MySQL manually (for instance) can chown include to root. + include_folder = HOMEBREW_PREFIX+'include' + return unless include_folder.exist? + + unless include_folder.writable? + puts <<-EOS.undent + #{include_folder} isn't writable. + This can happen if you "sudo make install" software that isn't managed + by Homebrew. If a brew tries to write a header file to this folder, the + install will fail during the link step. + + You should probably `chown` #{include_folder} + + EOS + end +end + +def check_access_etc + etc_folder = HOMEBREW_PREFIX+'etc' + return unless etc_folder.exist? + + unless etc_folder.writable? + puts <<-EOS.undent + #{etc_folder} isn't writable. + This can happen if you "sudo make install" software that isn't managed + by Homebrew. If a brew tries to write a file to this folder, the install + will fail during the link step. + + You should probably `chown` #{etc_folder} + + EOS + end +end + +def check_usr_bin_ruby + if /^1\.9/.match RUBY_VERSION + puts <<-EOS.undent + Ruby version #{RUBY_VERSION} is unsupported. + Homebrew is developed and tested on Ruby 1.8.x, and may not work correctly + on Ruby 1.9.x. Patches are accepted as long as they don't break on 1.8.x. + + EOS + end +end + +def check_homebrew_prefix + unless HOMEBREW_PREFIX.to_s == '/usr/local' + puts <<-EOS.undent + You can install Homebrew anywhere you want, but some brews may only work + correctly if you install to /usr/local. + + EOS + end +end + +def check_user_path + seen_prefix_bin = false + seen_prefix_sbin = false + seen_usr_bin = false + + paths = ENV['PATH'].split(':').collect{|p| File.expand_path p} + + paths.each do |p| + if p == '/usr/bin' + seen_usr_bin = true + unless seen_prefix_bin + puts <<-EOS.undent + /usr/bin is in your PATH before Homebrew's bin. This means that system- + provided programs will be used before Homebrew-provided ones. This is an + issue if you install, for instance, Python. + + Consider editing your .bashrc to put: + #{HOMEBREW_PREFIX}/bin + ahead of /usr/bin in your $PATH. + + EOS + end + end + + seen_prefix_bin = true if p == "#{HOMEBREW_PREFIX}/bin" + seen_prefix_sbin = true if p == "#{HOMEBREW_PREFIX}/sbin" + end + + unless seen_prefix_bin + puts <<-EOS.undent + Homebrew's bin was not found in your path. Some brews depend + on other brews that install tools to bin. + + You should edit your .bashrc to add: + #{HOMEBREW_PREFIX}/bin + to $PATH. + + EOS + end + + unless seen_prefix_sbin + puts <<-EOS.undent + Some brews install binaries to sbin instead of bin, but Homebrew's + sbin was not found in your path. + + Consider editing your .bashrc to add: + #{HOMEBREW_PREFIX}/sbin + to $PATH. + + EOS + end +end + +def check_which_pkg_config + binary = `/usr/bin/which pkg-config`.chomp + return if binary.empty? + + unless binary == "#{HOMEBREW_PREFIX}/bin/pkg-config" + puts <<-EOS.undent + You have a non-brew 'pkg-config' in your PATH: + #{binary} + + `./configure` may have problems finding brew-installed packages using + this other pkg-config. + + EOS + end +end + +def check_pkg_config_paths + binary = `/usr/bin/which pkg-config`.chomp + return if binary.empty? + + # Use the debug output to determine which paths are searched + pkg_config_paths = [] + + debug_output = `pkg-config --debug 2>&1` + debug_output.split("\n").each do |line| + line =~ /Scanning directory '(.*)'/ + pkg_config_paths << $1 if $1 + end + + # Check that all expected paths are being searched + unless pkg_config_paths.include? "/usr/X11/lib/pkgconfig" + puts <<-EOS.undent + Your pkg-config is not checking "/usr/X11/lib/pkgconfig" for packages. + Earlier versions of the pkg-config formula did not add this path + to the search path, which means that other formula may not be able + to find certain dependencies. + + To resolve this issue, re-brew pkg-config with: + brew rm pkg-config && brew install pkg-config + + EOS + end +end + +def check_for_gettext + if %w[lib/libgettextlib.dylib + lib/libintl.dylib + include/libintl.h ].any? { |f| File.exist? "#{HOMEBREW_PREFIX}/#{f}" } + puts <<-EOS.undent + gettext was detected in your PREFIX. + + The gettext provided by Homebrew is "keg-only", meaning it does not + get linked into your PREFIX by default. + + If you `brew link gettext` then a large number of brews that don't + otherwise have a `depends_on 'gettext'` will pick up gettext anyway + during the `./configure` step. + + If you have a non-Homebrew provided gettext, other problems will happen + especially if it wasn't compiled with the proper architectures. + + EOS + end +end + +def check_for_iconv + if %w[lib/libiconv.dylib + include/iconv.h ].any? { |f| File.exist? "#{HOMEBREW_PREFIX}/#{f}" } + puts <<-EOS.undent + libiconv was detected in your PREFIX. + + Homebrew doesn't provide a libiconv formula, and expects to link against + the system version in /usr/lib. + + If you have a non-Homebrew provided libiconv, many formulae will fail + to compile or link, especially if it wasn't compiled with the proper + architectures. + + EOS + end +end + +def check_for_config_scripts + real_cellar = HOMEBREW_CELLAR.realpath + + config_scripts = [] + + paths = ENV['PATH'].split(':').collect{|p| File.expand_path p} + paths.each do |p| + next if ['/usr/bin', '/usr/sbin', '/usr/X11/bin', "#{HOMEBREW_PREFIX}/bin", "#{HOMEBREW_PREFIX}/sbin"].include? p + next if p =~ %r[^(#{real_cellar.to_s}|#{HOMEBREW_CELLAR.to_s})] + + configs = Dir["#{p}/*-config"] + # puts "#{p}\n #{configs * ' '}" unless configs.empty? + config_scripts << [p, configs.collect {|p| File.basename(p)}] unless configs.empty? + end + + unless config_scripts.empty? + puts <<-EOS.undent + Some "config" scripts were found in your path, but not in system or Homebrew folders. + + `./configure` scripts often look for *-config scripts to determine if software packages + are installed, and what additional flags to use when compiling and linking. + + Having additional scripts in your path can confuse software installed via Homebrew if + the config script overrides a system or Homebrew provided script of the same name. + + EOS + + config_scripts.each do |pair| + puts pair[0] + puts " " + pair[1] * " " + end + puts + end +end + +def check_for_dyld_vars + if ENV['DYLD_LIBRARY_PATH'] + puts <<-EOS.undent + Setting DYLD_LIBARY_PATH can break dynamic linking. + You should probably unset it. + + EOS + end +end + +def check_for_symlinked_cellar + if HOMEBREW_CELLAR.symlink? + puts <<-EOS.undent + Symlinked Cellars can cause problems. + Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR} + which resolves to: #{HOMEBREW_CELLAR.realpath} + + The recommended Homebrew installations are either: + (A) Have Cellar be a real folder inside of your HOMEBREW_PREFIX + (B) Symlink "bin/brew" into your prefix, but don't symlink "Cellar". + + Older installations of Homebrew may have created a symlinked Cellar, but this can + cause problems when two formula install to locations that are mapped on top of each + other during the linking step. + + EOS + end +end + +def check_for_multiple_volumes + volumes = Volumes.new + + # Find the volumes for the TMP folder & HOMEBREW_CELLAR + real_cellar = HOMEBREW_CELLAR.realpath + + tmp_prefix = ENV['HOMEBREW_TEMP'] || '/tmp' + tmp=Pathname.new `/usr/bin/mktemp -d #{tmp_prefix}/homebrew-brew-doctor-XXXX`.strip + real_temp = tmp.realpath.parent + + where_cellar = volumes.which real_cellar + where_temp = volumes.which real_temp + + unless where_cellar == where_temp + puts <<-EOS.undent + Your Cellar & TEMP folders are on different volumes. + + OS X won't move relative symlinks across volumes unless the target file + already exists. + + Brews known to be affected by this are Git and Narwhal. + + You should set the "HOMEBREW_TEMP" environmental variable to a suitable + folder on the same volume as your Cellar. + + EOS + end +end + +def check_for_git + git = `/usr/bin/which git`.chomp + if git.empty? + puts <<-EOS.undent + "Git" was not found in your path. + + Homebrew uses Git for several internal functions, and some formulae + use Git checkouts instead of stable tarballs. + + You may want to do: + brew install git + + EOS + end +end + +def check_for_autoconf + which_autoconf = `/usr/bin/which autoconf`.chomp + unless (which_autoconf == '/usr/bin/autoconf' or which_autoconf == '/Developer/usr/bin/autoconf') + puts <<-EOS.undent + You have an "autoconf" in your path blocking the system version at: + #{which_autoconf} + + Custom autoconf in general and autoconf 2.66 in particular has issues + and will cause some Homebrew formulae to fail. + + EOS + end +end + +def __check_linked_brew f + links_found = [] + + Pathname.new(f.prefix).find do |src| + dst=HOMEBREW_PREFIX+src.relative_path_from(f.prefix) + next unless dst.symlink? + + dst_points_to = dst.realpath() + next unless dst_points_to.to_s == src.to_s + + if src.directory? + Find.prune + else + links_found << dst + end + end + + return links_found +end + +def check_for_linked_kegonly_brews + require 'formula' + + warnings = Hash.new + + Formula.all.each do |f| + next unless f.keg_only? and f.installed? + links = __check_linked_brew f + warnings[f.name] = links unless links.empty? + end + + unless warnings.empty? + puts <<-EOS.undent + Some keg-only formula are linked into the Cellar. + + Linking a keg-only formula, such as gettext, into the cellar with + `brew link f` will cause other formulae to detect them during the + `./configure` step. This may cause problems when compiling those + other formulae. + + Binaries provided by keg-only formulae may override system binaries + with other strange results. + + You may wish to `brew unlink` these brews: + EOS + + puts *warnings.keys.collect { |f| " #{f}" } + end +end + +def check_for_other_vars + target_var = ENV['MACOSX_DEPLOYMENT_TARGET'] + return if target_var.nil? or target_var.empty? + + unless target_var == MACOS_VERSION.to_s + puts <<-EOS.undent + $MACOSX_DEPLOYMENT_TARGET was set to #{target_var} + This is used by Fink, but having it set to a value different from the + current system version (#{MACOS_VERSION}) can cause problems, compiling + Git for instance, and should probably be removed. + + EOS + end +end + +module Homebrew extend self +def doctor + read, write = IO.pipe + + if fork == nil + read.close + $stdout.reopen write + + check_usr_bin_ruby + check_homebrew_prefix + check_for_macgpg2 + check_for_stray_dylibs + check_gcc_versions + check_cc_symlink + check_for_other_package_managers + check_for_x11 + check_for_nonstandard_x11 + check_access_share_locale + check_access_share_man + check_access_include + check_access_etc + check_user_path + check_which_pkg_config + check_pkg_config_paths + check_access_pkgconfig + check_for_gettext + check_for_config_scripts + check_for_dyld_vars + check_for_other_vars + check_for_symlinked_cellar + check_for_multiple_volumes + check_for_git + check_for_autoconf + check_for_linked_kegonly_brews + + exit! 0 + else + write.close + + unless (out = read.read).chomp.empty? + puts out + else + puts "Your OS X is ripe for brewing." + puts "Any troubles you may be experiencing are likely purely psychosomatic." + 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/cmd/update.rb b/Library/Homebrew/cmd/update.rb new file mode 100644 index 000000000..32f144c8e --- /dev/null +++ b/Library/Homebrew/cmd/update.rb @@ -0,0 +1,159 @@ +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" + CHECKOUT_COMMAND = "git checkout -q master" + UPDATE_COMMAND = "git pull #{REPOSITORY_URL} master" + REVISION_COMMAND = "git log -l -1 --pretty=format:%H 2> /dev/null" + GIT_UP_TO_DATE = "Already up-to-date." + + formula_regexp = 'Library/Formula/(.+?)\.rb' + ADDED_FORMULA = %r{^\s+create mode \d+ #{formula_regexp}$} + UPDATED_FORMULA = %r{^\s+#{formula_regexp}\s} + DELETED_FORMULA = %r{^\s+delete mode \d+ #{formula_regexp}$} + + example_regexp = 'Library/Contributions/examples/([^.\s]+).*' + ADDED_EXAMPLE = %r{^\s+create mode \d+ #{example_regexp}$} + UPDATED_EXAMPLE = %r{^\s+#{example_regexp}} + DELETED_EXAMPLE = %r{^\s+delete mode \d+ #{example_regexp}$} + + attr_reader :added_formulae, :updated_formulae, :deleted_formulae, :initial_revision + attr_reader :added_examples, :updated_examples, :deleted_examples + + def initialize + @added_formulae, @updated_formulae, @deleted_formulae = [], [], [] + @added_examples, @updated_examples, @deleted_examples = [], [], [] + @initial_revision = self.current_revision + end + + # Performs an update of the homebrew source. Returns +true+ if a newer + # version was available, +false+ if already up-to-date. + def update_from_masterbrew! + output = '' + HOMEBREW_REPOSITORY.cd do + if File.directory? '.git' + safe_system CHECKOUT_COMMAND + else + safe_system INIT_COMMAND + end + output = execute(UPDATE_COMMAND) + end + + output.split("\n").reverse.each do |line| + case line + when ADDED_FORMULA + @added_formulae << $1 + when DELETED_FORMULA + @deleted_formulae << $1 + when UPDATED_FORMULA + @updated_formulae << $1 unless @added_formulae.include?($1) or @deleted_formulae.include?($1) + when ADDED_EXAMPLE + @added_examples << $1 + when DELETED_EXAMPLE + @deleted_examples << $1 + when UPDATED_EXAMPLE + @updated_examples << $1 unless @added_examples.include?($1) or @deleted_examples.include?($1) + end + end + @added_formulae.sort! + @updated_formulae.sort! + @deleted_formulae.sort! + @added_examples.sort! + @updated_examples.sort! + @deleted_examples.sort! + + output.strip != GIT_UP_TO_DATE + end + + def pending_formulae_changes? + !@updated_formulae.empty? + end + + def pending_new_formulae? + !@added_formulae.empty? + end + + def deleted_formulae? + !@deleted_formulae.empty? + end + + def pending_examples_changes? + !@updated_examples.empty? + end + + def pending_new_examples? + !@added_examples.empty? + end + + def deleted_examples? + !@deleted_examples.empty? + end + + def current_revision + HOMEBREW_REPOSITORY.cd { execute(REVISION_COMMAND).strip } + rescue + 'TAIL' + end + + def report + puts "Updated Homebrew from #{initial_revision[0,8]} to #{current_revision[0,8]}." + ## New Formulae + if pending_new_formulae? + ohai "The following formulae are new:" + puts_columns added_formulae + end + ## Deleted Formulae + if deleted_formulae? + ohai "The following formulae were removed:" + puts_columns deleted_formulae + end + ## Updated Formulae + if pending_formulae_changes? + ohai "The following formulae were updated:" + puts_columns updated_formulae + else + puts "No formulae were updated." + end + ## New examples + if pending_new_examples? + ohai "The following external commands are new:" + puts_columns added_examples + end + ## Deleted examples + if deleted_examples? + ohai "The following external commands were removed:" + puts_columns deleted_examples + end + ## Updated Formulae + if pending_examples_changes? + ohai "The following external commands were updated:" + puts_columns updated_examples + else + puts "No external commands were updated." + end + end + + private + + def execute(cmd) + out = `#{cmd}` + if $? && !$?.success? + puts out + raise "Failed while executing #{cmd}" + end + ohai(cmd, out) if ARGV.verbose? + out + end +end 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 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 :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 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/update.rb b/Library/Homebrew/update.rb deleted file mode 100644 index 9e4fb90d9..000000000 --- a/Library/Homebrew/update.rb +++ /dev/null @@ -1,146 +0,0 @@ -class RefreshBrew - REPOSITORY_URL = "http://github.com/mxcl/homebrew.git" - INIT_COMMAND = "git init" - CHECKOUT_COMMAND = "git checkout -q master" - UPDATE_COMMAND = "git pull #{REPOSITORY_URL} master" - REVISION_COMMAND = "git log -l -1 --pretty=format:%H 2> /dev/null" - GIT_UP_TO_DATE = "Already up-to-date." - - formula_regexp = 'Library/Formula/(.+?)\.rb' - ADDED_FORMULA = %r{^\s+create mode \d+ #{formula_regexp}$} - UPDATED_FORMULA = %r{^\s+#{formula_regexp}\s} - DELETED_FORMULA = %r{^\s+delete mode \d+ #{formula_regexp}$} - - example_regexp = 'Library/Contributions/examples/([^.\s]+).*' - ADDED_EXAMPLE = %r{^\s+create mode \d+ #{example_regexp}$} - UPDATED_EXAMPLE = %r{^\s+#{example_regexp}} - DELETED_EXAMPLE = %r{^\s+delete mode \d+ #{example_regexp}$} - - attr_reader :added_formulae, :updated_formulae, :deleted_formulae, :initial_revision - attr_reader :added_examples, :updated_examples, :deleted_examples - - def initialize - @added_formulae, @updated_formulae, @deleted_formulae = [], [], [] - @added_examples, @updated_examples, @deleted_examples = [], [], [] - @initial_revision = self.current_revision - end - - # Performs an update of the homebrew source. Returns +true+ if a newer - # version was available, +false+ if already up-to-date. - def update_from_masterbrew! - output = '' - HOMEBREW_REPOSITORY.cd do - if File.directory? '.git' - safe_system CHECKOUT_COMMAND - else - safe_system INIT_COMMAND - end - output = execute(UPDATE_COMMAND) - end - - output.split("\n").reverse.each do |line| - case line - when ADDED_FORMULA - @added_formulae << $1 - when DELETED_FORMULA - @deleted_formulae << $1 - when UPDATED_FORMULA - @updated_formulae << $1 unless @added_formulae.include?($1) or @deleted_formulae.include?($1) - when ADDED_EXAMPLE - @added_examples << $1 - when DELETED_EXAMPLE - @deleted_examples << $1 - when UPDATED_EXAMPLE - @updated_examples << $1 unless @added_examples.include?($1) or @deleted_examples.include?($1) - end - end - @added_formulae.sort! - @updated_formulae.sort! - @deleted_formulae.sort! - @added_examples.sort! - @updated_examples.sort! - @deleted_examples.sort! - - output.strip != GIT_UP_TO_DATE - end - - def pending_formulae_changes? - !@updated_formulae.empty? - end - - def pending_new_formulae? - !@added_formulae.empty? - end - - def deleted_formulae? - !@deleted_formulae.empty? - end - - def pending_examples_changes? - !@updated_examples.empty? - end - - def pending_new_examples? - !@added_examples.empty? - end - - def deleted_examples? - !@deleted_examples.empty? - end - - def current_revision - HOMEBREW_REPOSITORY.cd { execute(REVISION_COMMAND).strip } - rescue - 'TAIL' - end - - def report - puts "Updated Homebrew from #{initial_revision[0,8]} to #{current_revision[0,8]}." - ## New Formulae - if pending_new_formulae? - ohai "The following formulae are new:" - puts_columns added_formulae - end - ## Deleted Formulae - if deleted_formulae? - ohai "The following formulae were removed:" - puts_columns deleted_formulae - end - ## Updated Formulae - if pending_formulae_changes? - ohai "The following formulae were updated:" - puts_columns updated_formulae - else - puts "No formulae were updated." - end - ## New examples - if pending_new_examples? - ohai "The following external commands are new:" - puts_columns added_examples - end - ## Deleted examples - if deleted_examples? - ohai "The following external commands were removed:" - puts_columns deleted_examples - end - ## Updated Formulae - if pending_examples_changes? - ohai "The following external commands were updated:" - puts_columns updated_examples - else - puts "No external commands were updated." - end - end - - private - - def execute(cmd) - out = `#{cmd}` - if $? && !$?.success? - puts out - raise "Failed while executing #{cmd}" - end - ohai(cmd, out) if ARGV.verbose? - out - end -end 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 &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 -- cgit v1.2.3