#!/usr/bin/env ruby require 'rubygems' require 'sys/uname' require 'pathname' require 'optparse' require 'rexml/document' require 'fileutils' class XPCOMCoreRubyGemInstaller attr_accessor :no_dry_run, :install_global, :install_local, :fix_path KnownAppIds = {'Firefox' => '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}'} KnownVendors = %w[Mozilla] ScriptName = Pathname(__FILE__).basename SysName = Sys::Uname.sysname.downcase.to_sym InstallPathBuilders = {:linux => {}, :darwin => {}} GeckoCommands = %w[firefox-bin firefox xulrunner-bin] ProfilePath = Pathname("~/.profile").expand_path # Linux install path builders InstallPathBuilders[:linux][:global] = [ lambda {|vendor,app,extension| Pathname("/usr/lib") + vendor + "extensions" + app + extension }, lambda {|vendor,app,extension| Pathname("/usr/lib64") + vendor + "extensions" + app + extension }, lambda {|vendor,app,extension| Pathname("/usr/share") + vendor + "extensions" + app + extension } ] InstallPathBuilders[:linux][:local] = [ lambda {|vendor,app,extension| Pathname("~/.#{vendor}") + "extensions" + app + extension } ] # Mac OS X install path builders InstallPathBuilders[:darwin][:global] = [ lambda {|vendor,app,extension| Pathname("/Library/Application Support") + vendor + "Extensions" + app + extension } ] InstallPathBuilders[:darwin][:local] = [ lambda {|vendor,app,extension| Pathname("~/Library/Application Support") + vendor + "Extensions" + app + extension } ] GeckoLocations = {:linux => [], :darwin => []} GeckoLocations[:darwin] << Pathname("/Applications/Firefox.app/Contents/MacOS/firefox-bin") GeckoLocations[:darwin] << Pathname("/Library/Frameworks/XUL.framework/xulrunner-bin") def initialize(args, options = {}) @args = args @options = options @option_parser = OptionParser.new do |opts| opts.banner = "Usage: #{ScriptName} [options]" opts.on("--install-global", "Install the XPCOMCore gem bootstrap code globally.", method(:install_global=)) opts.on("--install-local", "Install the XPCOMCore gem bootstrap code for the current user only.", method(:install_local=)) opts.on("--fix-path", "Checks the path for an executable Gecko installation and fixes it if necessary.", method(:fix_path=)) opts.on("--do-it", "By default, we do a dry run and show the user what would happen. This forces the actual install.", method(:no_dry_run=)) end end def dry_run? !no_dry_run end def go! @option_parser.parse(@args) install_opts = {:install_global => install_global, :install_local => install_local} if !install_opts.values.any? puts @option_parser exit(1) end read_install_manifest install_opts.each do |opt_name,value| send(:"do_#{opt_name}") if value log("All done!.") end rescue OptionParser::InvalidOption => e invalid_option(e) end private def invalid_option(exception) puts exception.message puts @option_parser exit(1) end def do_install_global do_install(:global) && rewrite_path end def do_install_local do_install(:local) && rewrite_path end def do_install(scope) each_vendor_and_app_name_and_app_id do |vendor, app_name, app_id| if install_extension(scope, vendor, app_id) next(true) else log("No app install location for application '#{app_name}' by vendor '#{vendor}' exists. Skipping.") next(false) end end.any? end def each_vendor_and_app_name_and_app_id KnownVendors.each do |vendor_name| KnownAppIds.each do |app_name, app_id| yield(vendor_name, app_name, app_id) end end end def install_extension(scope, vendor, app_id) raise "Couldn't read extension id from install manifest" if extension_id.empty? InstallPathBuilders[SysName][scope].select do |path_builder| log("Performing a #{scope} install...") install_path = path_builder[vendor, app_id, extension_id].expand_path if !install_path.parent.exist? log("Directory '#{install_path.parent}' doesn't exist - skipping installation to this location.") next(false) else copy_extension_to(install_path) next(true) end end.any? end def read_install_manifest manifest_contents = (@options[:extension_path] + "install.rdf").read @install_manifest = REXML::Document.new(manifest_contents) end def extension_path @options[:extension_path] end def extension_id @extension_id ||= @install_manifest.root.elements["Description/em:id"].text end def copy_extension_to(install_path) if install_path.parent.writable? log("Copying '#{extension_path}' to '#{install_path}'") copy_dir(extension_path, install_path) else log("Path '#{install_path}' isn't writable. Maybe you need to run this as root and try again if installing globally?") end end def log(str) puts("#{dry_run? ? "[DRY RUN]" : "[INSTALLER]"} #{str}") end def copy_dir(src_path, install_path) return false if dry_run? FileUtils.cp_r(src_path.expand_path.to_s + "/.", install_path.to_s) end def rewrite_path return false unless fix_path gecko_available = check_path rewrite_profile unless gecko_available end def check_path GeckoCommands.collect do |cmd| `#{cmd} -v 2>/dev/null` $? == 0 end.any? end def rewrite_profile log("I couldn't find any of '#{GeckoCommands.join(", ")}'. Doing my best attempt to rectify this.") send(:"rewrite_profile_for_#{SysName}") end def rewrite_profile_for_linux log("Sorry. You're on your own here. Check your distribution's documentation for details - for example, Firefox may be installed under a different name.") end def rewrite_profile_for_darwin loc = GeckoLocations[:darwin].detect {|p| p.exist?} unless loc log("I can't find Firefox installed on your system. Sorry.") exit(1) end log("Awesome. I found a Gecko installation at '#{loc}'. Rewriting your .profile to include this in the $PATH.") rewrite_path_with_location(loc.parent) end def rewrite_path_with_location(path_entry) profile_entry = %Q[PATH="#{path_entry}:$PATH"] log("Adding .profile entry: #{profile_entry}") if dry_run? log("Not adding an entry due to running in dry run mode.") else ProfilePath.open("a") do |io| io << "\n" io.puts("# This and the following lines automatically added by xpcomcore-rubygem on #{Time.now}.") io.puts(profile_entry) end log(".profile entry added. You may need to source your .profile again or log in and out for it to take effect.") end end end installer = XPCOMCoreRubyGemInstaller.new(ARGV, :extension_path => (Pathname(__FILE__).parent.parent + "extension").expand_path) installer.go!