# Copyright 2009 Max Howell and other contributors. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # 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 # bazaar don't maintain their PyPi entry properly yet # when they do we'll remove our formula and use that # when 'bazaar', 'bzr' then abort <<-EOS #Bazaar can be installed thusly: # # brew install pip && pip install bzr==2.0.1 # # EOS when 'mercurial', 'hg' then abort <<-EOS Mercurial can be install thusly: brew install pip && pip install mercurial EOS end end end def __make url, name require 'formula' path = Formula.path name raise "#{path} already exists" if path.exist? # Check if a formula aliased to this name exists. already_aka = Formulary.find_alias name if already_aka != nil opoo "Formula #{already_aka} is aliased to #{name}." puts "Please check if you are creating a duplicate." end template=<<-EOS require 'formula' class #{Formula.class_s name} /dev/null` /^\*\s*(.*)/.match all_branches branch = ($1 || '').chomp end user = 'mxcl' if user.empty? branch = 'master' if user.empty? return "http://github.com/#{user}/homebrew/commits/#{branch}/Library/Formula/#{formula_name}" end def info name require 'formula' exec 'open', github_info(name) if ARGV.flag? '--github' f=Formula.factory name 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(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 # we can't tell which one to keep in this circumstance raise "The most recent version of #{name} is not installed" unless f.installed? if f.prefix.parent.directory? kids = f.prefix.parent.children kids.each do |keg| next if f.prefix == keg print "Uninstalling #{keg}..." FileUtils.rm_rf keg puts end end end def clean f 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.nil? or version.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}" 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 ########################################################## 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? print_dir pn 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 ################################################################ class Cleaner class Cleaner def initialize f @f=f # correct common issues share=f.prefix+'share' (f.prefix+'man').mv share rescue nil [f.bin, f.sbin, f.lib].each {|d| clean_dir d} # you can read all of this stuff online nowadays, save the space # info pages are pants, everyone agrees apart from Richard Stallman # feel free to ask for build options though! http://bit.ly/Homebrew unlink = Proc.new{ |path| path.unlink unless f.skip_clean? path rescue nil } %w[doc docs info].each do |fn| unlink.call(f.share+fn) unlink.call(f.prefix+fn) end end private def strip path, args='' return if @f.skip_clean? path puts "strip #{path}" if ARGV.verbose? path.chmod 0644 # so we can strip unless path.stat.nlink > 1 system "strip", *(args+path) else path = path.to_s.gsub ' ', '\\ ' # strip unlinks the file and recreates it, thus breaking hard links! # is this expected behaviour? patch does it too… still, this fixes it tmp = `/usr/bin/mktemp -t homebrew_strip`.chomp begin `/usr/bin/strip #{args} -o #{tmp} #{path}` `/bin/cat #{tmp} > #{path}` ensure FileUtils.rm tmp end end end def clean_file path perms=0444 case `file -h '#{path}'` when /Mach-O dynamically linked shared library/ # Stripping libraries is causing no end of trouble # Lets just give up, and try to do it manually in instances where it # makes sense #strip path, '-SxX' when /Mach-O [^ ]* ?executable/ strip path perms=0555 when /script text executable/ perms=0555 end path.chmod perms end def clean_dir d d.find do |path| if path.directory? Find.prune if @f.skip_clean? path elsif not path.file? next elsif path.extname == '.la' and not @f.skip_clean? path # *.la files are stupid path.unlink elsif not path.symlink? clean_file path end end end end def gcc_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 def llvm_build if MACOS_VERSION >= 10.6 `/Developer/usr/bin/llvm-gcc-4.2 -v 2>&1` =~ /LLVM build (\d{4,})/ $1.to_i end end def x11_installed? Pathname.new('/usr/X11/lib/libpng.dylib').exist? end