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}http://github.com/mxcl/homebrew/issues#{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 '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 = HOMEBREW_REPOSITORY.join("Library/Aliases/#{name}").realpath.basename('.rb') raise <<-EOS.undent The formula #{realname} is already aliased to #{name} 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| print "#{keg} (#{keg.abv})" print " *" if f.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.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 resolved_name = (HOMEBREW_REPOSITORY+"Library/Aliases/#{alias_name}").readlink.basename('.rb').to_s results.include? resolved_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 "Although, unlikely, this can break builds or cause obscure runtime issues." puts "If you experience problems 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 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 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 _compiler_recommendation build, recommended message = (!build.nil? && build < recommended) ? "(#{recommended} or newer recommended)" : "" return build, message end