# # Author: James M. Lawrence <quixoticsycophant@gmail.com>. # #Updated by Brett Bates and Jennifer Bottom. require 'net/ftp' require 'rbconfig' require 'ostruct' require 'fileutils' require 'optparse' require 'pathname' require 'rubygems' class Installer include FileUtils CONFIG = Config::CONFIG BIT64 = (1.size == 8) RB_BASENAME = Pathname.new "P4.rb" SO_BASENAME = Pathname.new "P4.#{CONFIG['DLEXT']}" RAW_INSTALL_FILES = [ Pathname.new(CONFIG["sitelibdir"]) + RB_BASENAME, Pathname.new(CONFIG["sitearchdir"]) + SO_BASENAME, ] GEM_INSTALL_FILES = [ Pathname.new("lib") + RB_BASENAME, Pathname.new("ext") + SO_BASENAME, ] SERVER = "ftp.perforce.com" SERVER_TOP_DIR = Pathname.new "perforce" # Mysterious "ghost" releases which lack files HOSED_VERSIONS = %w[09.3 11.1] P4API_REMOTE_BASENAME = Pathname.new "p4api.tgz" P4RUBY_REMOTE_BASENAME = Pathname.new "p4ruby.tgz" WORK_DIR = Pathname.new "work" DISTFILES_DIR = WORK_DIR + "distfiles" BUILD_DIR = WORK_DIR + "build" def parse_command_line OptionParser.new("Usage: ruby install.rb [options]", 24, "") { |parser| parser.on( "--version NN.N", "Version to download, e.g. 08.1. Default finds latest.") { |version| @s.version = version } parser.on( "--list-versions", "List available versions.") { @s.list_versions = true } parser.on( "--platform PLATFORM", "Perforce-named platform to download. Default guesses.") { |platform| @s.platform = platform } parser.on( "--list-platforms", "List available platforms for the given version.") { @s.list_platforms = true } parser.on( "--gem", "Gem configuration (for the gem installer).") { @s.gem_config = true } parser.on( "--uninstall", "Uninstall.") { @s.uninstall = true } parser.on( "--local", "Use the files in work/distfiles (manual download).") { @s.local = true } parser.parse(ARGV) } end def run @s = LazyStruct.new parse_command_line config if @s.uninstall uninstall elsif @s.list_platforms puts platforms elsif @s.list_versions puts versions elsif @s.platform.nil? platform_fail elsif @s.platform =~ %r!\Ant! windows_install else fetch build install verify_install end end def config if CONFIG["LIBRUBYARG_SHARED"].empty? raise "error: ruby must be configured with --enable-shared" end @s.ftp = Net::FTP.new(SERVER).tap { |t| t.passive = true t.login } @s.p4api = LazyStruct.new.tap { |t| t.basename = P4API_REMOTE_BASENAME } @s.p4ruby = LazyStruct.new.tap { |t| t.basename = P4RUBY_REMOTE_BASENAME } @s.specs = [ @s.p4ruby, @s.p4api ] @s.specs.each { |spec| spec.local = DISTFILES_DIR + spec.basename } unless @s.version @s.version = latest_version end @s.version_dir = SERVER_TOP_DIR + "r#{@s.version}" unless @s.platform @s.platform = guess_platform end if @s.platform =~ /nt/ @s.p4api.remote = @s.version_dir + "bin.#{@s.platform}" else @s.p4api.remote = @s.version_dir + "bin.#{@s.platform}" + @s.p4api.basename @s.p4ruby.remote = @s.version_dir + "bin.tools" + @s.p4ruby.basename end end def guess_cpu if CONFIG["target_os"] =~ %r!darwin! if CONFIG["build"] =~ /i686|x86_64/ "x86_64" else "x86" end else case CONFIG["target_cpu"] when %r!ia!i "ia64" when %r!86! # note: with '_' "x86" + (BIT64 ? "_64" : "") when %r!(ppc|sparc)!i # note: without '_' $1 + (BIT64 ? "64" : "") else "" end end end def guess_version(os) if match = `uname -a`.match(%r!#{os}\s+\S+\s+(\d+)\.(\d+)!i) version = match.captures.join cpu = guess_cpu platforms = self.platforms built_platforms = (0..version.to_i).map { |n| [os, n.to_s, cpu].join }.select { |platform| platforms.include? platform } if os =~ /darwin/ built_platforms.pop built_platforms.last else built_platforms.last end else nil end end def guess_platform(opts = {}) config_os = CONFIG["target_os"].downcase windows_cpu = BIT64 ? "x64" : "x86" if config_os =~ %r!cygwin!i "cygwin" + windows_cpu elsif config_os =~ %r!(mswin|mingw)!i "nt" + windows_cpu elsif @s.local "<local>" else if match = config_os.match(%r!\A\D+!) guess_version(match[0]) else nil end end end def platform_fail install_fail { @s.version = "<version>" @s.platform = "<platform>" message = %Q{ Auto-fetch not yet handled for this platform. Run: \truby install.rb --list-platforms to see the available platforms, then run \truby install.rb --platform PLATFORM with your platform. If all of the above fails, manually fetch \tftp://#{SERVER}/#{@s.p4api.remote} Copy it to #{@s.p4api.local} and run install.rb --local. }.gsub(%r!^ +(?=\S)!, "") mkdir_p(DISTFILES_DIR) puts message } end def install_fail yield exit(1) end def sys(*args) system(*args).tap { |result| unless result raise "system() failed: #{args.join(" ")}" end } end def unpack(distfile, target_dir) sys("tar", "zxvf", distfile.to_s, "-C", target_dir.to_s) end def fetch_spec(spec) unless @s.local mkdir_p(spec.local.dirname) puts "downloading ftp://#{SERVER}/#{spec.remote} ..." @s.ftp.getbinaryfile(spec.remote.to_s, spec.local.to_s) end end def fetch @s.specs.each { |spec| fetch_spec(spec) } end def remote_files_matching(dir, regex) @s.ftp.ls(dir.to_s).map { |entry| if match = entry.match(regex) yield match else nil end }.reject { |entry| entry.nil? } end def platforms remote_files_matching(@s.version_dir, %r!bin\.(\w+)!) { |match| match.captures.first }.reject { |platform| platform =~ %r!java! }.sort end def versions remote_files_matching(SERVER_TOP_DIR, %r!r([0-8]\d\.\d)!) { |match| match.captures.first }.reject { |version| HOSED_VERSIONS.include? version }.sort end def latest_version versions.reverse_each{ |v| begin remote_files_matching("#{SERVER_TOP_DIR}/r#{v}/bin.tools",/p4ruby/) do return v end rescue next end } end def make(*args) sys("make", *args) end def ruby(*args) exe = Pathname.new(CONFIG["bindir"]) + CONFIG["RUBY_INSTALL_NAME"] sys(exe.to_s, *args) end def build puts "building..." rm_rf(BUILD_DIR) mkdir_p(BUILD_DIR) @s.specs.each { |spec| unpack(spec.local, BUILD_DIR) } Dir.chdir(BUILD_DIR) { api_dir = Pathname.glob("p4api*").last p4ruby_dir = Pathname.glob("p4ruby*").last Dir.chdir(p4ruby_dir) { ruby("p4conf.rb", "--apidir", "../#{api_dir}") make } @s.p4ruby_build_dir = BUILD_DIR + p4ruby_dir } end def raw_install_to_gem_install RAW_INSTALL_FILES.zip(GEM_INSTALL_FILES) { |source, dest| mkdir_p(dest.dirname) puts "move #{source} --> #{dest}" mv(source, dest) } end def install puts "installing..." Dir.chdir(@s.p4ruby_build_dir) { make("install") } if @s.gem_config raw_install_to_gem_install end end def verify_install(on_error = nil) puts "verifying..." files = if @s.gem_config GEM_INSTALL_FILES else RAW_INSTALL_FILES end.map { |t| t.expand_path } if files.all? { |t| t.exist? } puts "Installed files:" puts files elsif on_error install_fail(&on_error) else install_fail { puts "These files were supposed to be installed, but were not:" puts files puts "Install failed!" } end end def find_ruby_version(spec) remote_files_matching(spec.remote, /p4ruby\d\d.exe/) {|r_ver| #Find the latest version of p4ruby for this version of ruby v_max = CONFIG["MAJOR"] v_min = CONFIG["MINOR"] version = [v_max, v_min].join if r_ver.to_s =~ /p4ruby#{version}.exe/ return "p4ruby#{version}.exe" end } nil end def windows_install # # For Windows, p4ruby is located in the p4api directory on the # perforce server -- switcharoo -- # spec = @s.p4api p4ruby_exe = find_ruby_version(spec) if p4ruby_exe && !(spec.remote.to_s =~ /p4ruby/) spec.remote += p4ruby_exe.to_s else abort("Failed to find a suitable p4ruby executable for ruby #{CONFIG["MAJOR"]}.#{CONFIG["MINOR"]}") end fetch_spec(spec) error = lambda { puts "The Perforce P4Ruby Windows installer failed!" puts "You may re-run it manually here:" puts spec.local.expand_path } puts "running Perforce P4Ruby Windows installer..." if system(spec.local.to_s, "/S", "/v", "/qn") if @s.gem_config sleep(1) raw_install_to_gem_install sleep(1) #Without the -x flag a permissions error raised on Windows. unless system(spec.local, "/V", "/S", "/x", "/v/qn") # We don't much care if this fails; just write to the log puts "Note: the Perforce P4Ruby Windows uninstaller failed." end end verify_install(error) else install_fail(&error) end end def uninstall RAW_INSTALL_FILES.each { |file| if file.exist? puts "delete #{file}" rm_f(file) end } end end # # An OpenStruct with optional lazy-evaluated fields. # class LazyStruct < OpenStruct # # For mixing into an existing OpenStruct instance singleton class. # module Mixin # # &block is evaluated when this attribute is requested. The # same result is returned for subsquent calls, until the field # is assigned a different value. # def attribute(reader, &block) singleton = (class << self ; self ; end) singleton.instance_eval { # # Define a special reader method in the singleton class. # define_method(reader) { block.call.tap { |value| # # The value has been computed. Replace this method with a # one-liner giving the value. # singleton.instance_eval { remove_method(reader) define_method(reader) { value } } } } # # Revert to the old OpenStruct behavior when the writer is called. # writer = "#{reader}=".to_sym define_method(writer) { |value| singleton.instance_eval { remove_method(reader) remove_method(writer) } method_missing(writer, value) } } end end include Mixin end # version < 1.8.7 compatibility module Kernel unless respond_to? :tap def tap yield self self end end end Installer.new.run
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#10 | 10176 | Jen Bottom |
Changed the logic for finding out if the customer has entered the required values in the variable sslbuild. Seems to function ok, but need to test that what is build with system ssl libs connects to ssl enabled P4D before submitting to RubyGems. |
||
#9 | 10169 | Jen Bottom |
Added a check for whether the variable sslbuild is empty. If it is instruct the user to select y or n. Still need to add handling for cases where the user enters something other than y or n. |
||
#8 | 10168 | Jen Bottom |
Adding a mechanism for the user to specify whether they want P4Ruby built with SSL support. Need to add error checking, for if the variable is empty or a value other than 'y' or 'n' is entered. |
||
#7 | 9464 | Jen Bottom | Pulling in Brett's changes. | ||
#6 | 9459 | Jen Bottom | Integrating Brett's changes into my branch. | ||
#5 | 9257 | Jen Bottom | deleting p4config file and adding to .ignore file. | ||
#4 | 9256 | Jen Bottom |
Fixed a typo. 'test.b' should have been 'test.rb'. |
||
#3 | 8879 | Jen Bottom | integrating Brett Bates's latest work on P4Ruby gem for further enhancement. | ||
#2 | 8483 | Jen Bottom |
submitting some minor changes to the 'install.rb' file. Have replaced 'config' with 'rbconfig'. Have also removed the build of the gem, as now it will be out of date |
||
#1 | 8366 | Jen Bottom | Adding the source code and docs for the P4Ruby Gem to the Public Depot. |