#! /usr/bin/env ruby #-- # Copyright 2004 Austin Ziegler # Install utility. Based on the original installation script for rdoc by the # Pragmatic Programmers. # # This program is free software. It may be redistributed and/or modified under # the terms of the GPL version 2 (or later) or the Ruby licence. # # Usage # ----- # In most cases, if you have a typical project layout, you will need to do # absolutely nothing to make this work for you. This layout is: # # bin/ # executable files -- "commands" # lib/ # the source of the library # tests/ # unit tests # # The default behaviour: # 1) Run all unit test files (ending in .rb) found in all directories under # tests/. # 2) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd), # all .rb files in lib/, ./README, ./ChangeLog, and ./Install. # 3) Build ri documentation from all files in bin/ (excluding .bat and .cmd), # and all .rb files in lib/. This is disabled by default on Win32. # 4) Install commands from bin/ into the Ruby bin directory. On Windows, if a # if a corresponding batch file (.bat or .cmd) exists in the bin directory, # it will be copied over as well. Otherwise, a batch file (always .bat) will # be created to run the specified command. # 5) Install all library files ending in .rb from lib/ into Ruby's # site_lib/version directory. # #++ require 'rbconfig' require 'find' require 'fileutils' require 'optparse' require 'ostruct' begin require 'rdoc/rdoc' $haverdoc = true rescue LoadError puts "Missing rdoc; skipping documentation" $haverdoc = false end begin if $haverdoc rst2man = %x{which rst2man.py} $haveman = true else $haveman = false end rescue puts "Missing rst2man; skipping man page creation" $haveman = false end $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib') require 'facter' @operatingsystem = Facter[:operatingsystem].value PREREQS = %w{openssl xmlrpc/client xmlrpc/server cgi} InstallOptions = OpenStruct.new def glob(list) g = list.map { |i| Dir.glob(i) } g.flatten! g.compact! g.reject! { |e| e =~ /\.svn/ } g end # Set these values to what you want installed. sbins = glob(%w{sbin/*}) bins = glob(%w{bin/*}) rdoc = glob(%w{bin/* sbin/* lib/**/*.rb README README-library CHANGELOG TODO Install}).reject { |e| e=~ /\.(bat|cmd)$/ } ri = glob(%w(bin/*.rb sbin/* lib/**/*.rb)).reject { |e| e=~ /\.(bat|cmd)$/ } man = glob(%w{man/man8/*}) libs = glob(%w{lib/**/*.rb lib/**/*.py}) tests = glob(%w{tests/**/*.rb}) def do_bins(bins, target, strip = 's?bin/') bins.each do |bf| obf = bf.gsub(/#{strip}/, '') install_binfile(bf, obf, target) end end def do_libs(libs, strip = 'lib/') libs.each do |lf| olf = File.join(InstallOptions.site_dir, lf.gsub(/#{strip}/, '')) op = File.dirname(olf) FileUtils.makedirs(op, {:mode => 0755, :verbose => true}) FileUtils.chmod(0755, op) FileUtils.install(lf, olf, {:mode => 0644, :verbose => true}) end end def do_man(man, strip = 'man/') if (InstallOptions.man == true) man.each do |mf| omf = File.join(InstallOptions.man_dir, mf.gsub(/#{strip}/, '')) om = File.dirname(omf) FileUtils.makedirs(om, {:mode => 0755, :verbose => true}) FileUtils.chmod(0755, om) FileUtils.install(mf, omf, {:mode => 0644, :verbose => true}) gzip = %x{which gzip} gzip.chomp! %x{#{gzip} -f #{omf}} end else puts "Skipping Man Page Generation" end end # Verify that all of the prereqs are installed def check_prereqs PREREQS.each { |pre| begin require pre rescue LoadError puts "Could not load #{pre} Ruby library; cannot install" exit -1 end } end def is_windows? @operatingsystem == 'windows' end ## # Prepare the file installation. # def prepare_installation # Only try to do docs if we're sure they have rdoc if $haverdoc InstallOptions.rdoc = true if is_windows? InstallOptions.ri = false else InstallOptions.ri = true end else InstallOptions.rdoc = false InstallOptions.ri = false end if $haveman InstallOptions.man = true if is_windows? InstallOptions.man = false end else InstallOptions.man = false end InstallOptions.tests = true ARGV.options do |opts| opts.banner = "Usage: #{File.basename($0)} [options]" opts.separator "" opts.on('--[no-]rdoc', 'Prevents the creation of RDoc output.', 'Default on.') do |onrdoc| InstallOptions.rdoc = onrdoc end opts.on('--[no-]ri', 'Prevents the creation of RI output.', 'Default off on mswin32.') do |onri| InstallOptions.ri = onri end opts.on('--[no-]man', 'Presents the creation of man pages.', 'Default on.') do |onman| InstallOptions.man = onman end opts.on('--[no-]tests', 'Prevents the execution of unit tests.', 'Default on.') do |ontest| InstallOptions.tests = ontest end opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir| InstallOptions.destdir = destdir end opts.on('--bindir[=OPTIONAL]', 'Installation directory for binaries', 'overrides Config::CONFIG["bindir"]') do |bindir| InstallOptions.bindir = bindir end opts.on('--sbindir[=OPTIONAL]', 'Installation directory for system binaries', 'overrides Config::CONFIG["sbindir"]') do |sbindir| InstallOptions.sbindir = sbindir end opts.on('--sitelibdir[=OPTIONAL]', 'Installation directory for libraries', 'overrides Config::CONFIG["sitelibdir"]') do |sitelibdir| InstallOptions.sitelibdir = sitelibdir end opts.on('--mandir[=OPTIONAL]', 'Installation directory for man pages', 'overrides Config::CONFIG["mandir"]') do |mandir| InstallOptions.mandir = mandir end opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick| InstallOptions.rdoc = false InstallOptions.ri = false InstallOptions.tests = false end opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full| InstallOptions.rdoc = true InstallOptions.ri = true InstallOptions.tests = true end opts.separator("") opts.on_tail('--help', "Shows this help text.") do $stderr.puts opts exit end opts.parse! end tmpdirs = [ENV['TMP'], ENV['TEMP'], "/tmp", "/var/tmp", "."] version = [Config::CONFIG["MAJOR"], Config::CONFIG["MINOR"]].join(".") libdir = File.join(Config::CONFIG["libdir"], "ruby", version) # Mac OS X 10.5 and higher declare bindir and sbindir as # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/sbin # which is not generally where people expect executables to be installed # These settings are appropriate defaults for all OS X versions. if RUBY_PLATFORM =~ /^universal-darwin[\d\.]+$/ Config::CONFIG['bindir'] = "/usr/bin" Config::CONFIG['sbindir'] = "/usr/sbin" end if not InstallOptions.bindir.nil? bindir = InstallOptions.bindir else bindir = Config::CONFIG['bindir'] end if not InstallOptions.sbindir.nil? sbindir = InstallOptions.sbindir else sbindir = Config::CONFIG['sbindir'] end if not InstallOptions.sitelibdir.nil? sitelibdir = InstallOptions.sitelibdir else sitelibdir = Config::CONFIG["sitelibdir"] if sitelibdir.nil? sitelibdir = $:.find { |x| x =~ /site_ruby/ } if sitelibdir.nil? sitelibdir = File.join(libdir, "site_ruby") elsif sitelibdir !~ Regexp.quote(version) sitelibdir = File.join(sitelibdir, version) end end end if not InstallOptions.mandir.nil? mandir = InstallOptions.mandir else mandir = Config::CONFIG['mandir'] end # To be deprecated once people move over to using --destdir option if (destdir = ENV['DESTDIR']) warn "DESTDIR is deprecated. Use --destdir instead." bindir = join(destdir, bindir) sbindir = join(destdir, sbindir) mandir = join(destdir, mandir) sitelibdir = join(destdir, sitelibdir) FileUtils.makedirs(bindir) FileUtils.makedirs(sbindir) FileUtils.makedirs(mandir) FileUtils.makedirs(sitelibdir) # This is the new way forward elsif (destdir = InstallOptions.destdir) bindir = join(destdir, bindir) sbindir = join(destdir, sbindir) mandir = join(destdir, mandir) sitelibdir = join(destdir, sitelibdir) FileUtils.makedirs(bindir) FileUtils.makedirs(sbindir) FileUtils.makedirs(mandir) FileUtils.makedirs(sitelibdir) end tmpdirs << bindir InstallOptions.tmp_dirs = tmpdirs.compact InstallOptions.site_dir = sitelibdir InstallOptions.bin_dir = bindir InstallOptions.sbin_dir = sbindir InstallOptions.lib_dir = libdir InstallOptions.man_dir = mandir end ## # Join two paths. On Windows, dir must be converted to a relative path, # by stripping the drive letter, but only if the basedir is not empty. # def join(basedir, dir) return "#{basedir}#{dir[2..-1]}" if is_windows? and basedir.length > 0 and dir.length > 2 "#{basedir}#{dir}" end ## # Build the rdoc documentation. Also, try to build the RI documentation. # def build_rdoc(files) return unless $haverdoc begin r = RDoc::RDoc.new r.document(["--main", "README", "--title", "Puppet -- Site Configuration Management", "--line-numbers"] + files) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build RDoc documentation\n#{e.message}" end end def build_ri(files) return unless $haverdoc begin ri = RDoc::RDoc.new #ri.document(["--ri-site", "--merge"] + files) ri.document(["--ri-site"] + files) rescue RDoc::RDocError => e $stderr.puts e.message rescue Exception => e $stderr.puts "Couldn't build Ri documentation\n#{e.message}" $stderr.puts "Continuing with install..." end end def build_man(bins) return unless $haveman begin # Locate rst2man rst2man = %x{which rst2man.py} rst2man.chomp! bins.each do |bin| b = bin.gsub( "bin/", "") %x{#{bin} --help > ./#{b}.rst} %x{#{rst2man} ./#{b}.rst ./man/man8/#{b}.8} File.unlink("./#{b}.rst") end rescue SystemCallError $stderr.puts "Couldn't build man pages: " + $! $stderr.puts "Continuing with install..." end end def run_tests(test_list) begin require 'test/unit/ui/console/testrunner' $:.unshift "lib" test_list.each do |test| next if File.directory?(test) require test end tests = [] ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) } tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) } tests.delete_if { |o| o == Test::Unit::TestCase } tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) } $:.shift rescue LoadError puts "Missing testrunner library; skipping tests" end end ## # Install file(s) from ./bin to Config::CONFIG['bindir']. Patch it on the way # to insert a #! line; on a Unix install, the command is named as expected # (e.g., bin/rdoc becomes rdoc); the shebang line handles running it. Under # windows, we add an '.rb' extension and let file associations do their stuff. def install_binfile(from, op_file, target) tmp_dir = nil InstallOptions.tmp_dirs.each do |t| if File.directory?(t) and File.writable?(t) tmp_dir = t break end end fail "Cannot find a temporary directory" unless tmp_dir tmp_file = File.join(tmp_dir, '_tmp') ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) File.open(from) do |ip| File.open(tmp_file, "w") do |op| ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) op.puts "#!#{ruby}" contents = ip.readlines if contents[0] =~ /^#!/ contents.shift end op.write contents.join() end end if is_windows? installed_wrapper = false if File.exists?("#{from}.bat") FileUtils.install("#{from}.bat", File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true) installed_wrapper = true end if File.exists?("#{from}.cmd") FileUtils.install("#{from}.cmd", File.join(target, "#{op_file}.cmd"), :mode => 0755, :verbose => true) installed_wrapper = true end if not installed_wrapper tmp_file2 = File.join(tmp_dir, '_tmp_wrapper') cwn = File.join(Config::CONFIG['bindir'], op_file) cwv = CMD_WRAPPER.gsub('', ruby.gsub(%r{/}) { "\\" }).gsub!('', cwn.gsub(%r{/}) { "\\" } ) File.open(tmp_file2, "wb") { |cw| cw.puts cwv } FileUtils.install(tmp_file2, File.join(target, "#{op_file}.bat"), :mode => 0755, :verbose => true) File.unlink(tmp_file2) installed_wrapper = true end end FileUtils.install(tmp_file, File.join(target, op_file), :mode => 0755, :verbose => true) File.unlink(tmp_file) end CMD_WRAPPER = <<-EOS @echo off if "%OS%"=="Windows_NT" goto WinNT -x "" %1 %2 %3 %4 %5 %6 %7 %8 %9 goto done :WinNT -x "" %* goto done :done EOS check_prereqs prepare_installation run_tests(tests) if InstallOptions.tests #build_rdoc(rdoc) if InstallOptions.rdoc #build_ri(ri) if InstallOptions.ri #build_man(bins) if InstallOptions.man do_bins(sbins, InstallOptions.sbin_dir) do_bins(bins, InstallOptions.bin_dir) do_libs(libs) do_man(man)