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