# ***** BEGIN LICENSE BLOCK ***** # Version: MPL 1.1/GPL 2.0/LGPL 2.1 # # The contents of this file are subject to the Mozilla Public License Version # 1.1 (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS IS" basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License # for the specific language governing rights and limitations under the # License. # # The Original Code is Mozilla Corporation Code. # # The Initial Developer of the Original Code is # Clint Talbert. # Portions created by the Initial Developer are Copyright (C) 2007 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Clint Talbert # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your # decision by deleting the provisions above and replace them with the notice # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. # # ***** END LICENSE BLOCK ***** from optparse import OptionParser import platform import subprocess import re import time import string import os import shutil isDMG = re.compile(".*\.dmg") isTARBZ = re.compile(".*\.tar\.bz") isTARGZ = re.compile(".*\.tar\.gz") isZIP = re.compile(".*\.zip") isEXE = re.compile(".*\.exe") _mozInstall_debug = False def debug(s): if _mozInstall_debug: print "DEBUG: " + s cygwinmatch = re.compile(".*cygwin.*", re.I) # Copied from mozilla/testing/release/minotaur/getOSInfo.py - want this class # to be self-sufficient without requiring another "non-standard" python lib, # so duplicated this function from there. def getPlatform(): # On Vista, python reports "Microsoft" and on cygwin shells it can report # several different strings that contain the word "cygwin" if platform.system() == "Microsoft" or cygwinmatch.search(platform.system()): return "Windows" else: return platform.system() # This code is lifted directly from the buildbot code. That we use here. I # can't call it in buildbot without importing a trillion other things. # Original Source: http://mxr.mozilla.org/mozilla/source/tools/buildbot/buildbot/slave/commands.py#59 def rmdirRecursive(dir): """This is a replacement for shutil.rmtree that works better under windows. Thanks to Bear at the OSAF for the code.""" if not os.path.exists(dir): return if os.path.islink(dir): os.remove(dir) return # Verify the directory is read/write/execute for the current user os.chmod(dir, 0700) for name in os.listdir(dir): full_name = os.path.join(dir, name) # on Windows, if we don't have write permission we can't remove # the file/directory either, so turn that on if os.name == 'nt': if not os.access(full_name, os.W_OK): # I think this is now redundant, but I don't have an NT # machine to test on, so I'm going to leave it in place # -warner os.chmod(full_name, 0600) if os.path.isdir(full_name): rmdirRecursive(full_name) else: os.chmod(full_name, 0700) os.remove(full_name) os.rmdir(dir) class MozUninstaller: def __init__(self, **kwargs): debug("uninstall constructor") assert (kwargs['dest'] != "" and kwargs['dest'] != None) assert (kwargs['productName'] != "" and kwargs['productName'] != None) assert (kwargs['branch'] != "" and kwargs['dest'] != None) self.dest = kwargs['dest'] self.productName = kwargs['productName'] self.branch = kwargs['branch'] # Handle the case where we haven't installed yet if not os.path.exists(self.dest): return if getPlatform() == "Windows": try: self.doWindowsUninstall() except: debug("Windows Uninstall threw - not overly urgent or worrisome") if os.path.exists(self.dest): try: os.rmdir(self.dest) except OSError: # Directories are still there - kill them all! rmdirRecursive(self.dest) def doWindowsUninstall(self): debug("do windowsUninstall") if self.branch == "1.8.0": uninstexe = self.dest + "/uninstall/uninstall.exe" uninstini = self.dest + "/uninstall/uninstall.ini" debug("uninstexe: " + uninstexe) debug("uninstini: " + uninstini) if os.path.exists(uninstexe): # modify uninstall.ini to run silently debug("modifying uninstall.ini") args = "sed -i.bak 's/Run Mode=Normal/Run Mode=Silent/' " + uninstini proc = subprocess.Popen(args, shell=True) # Todo handle error proc.wait() proc = subprocess.Popen(uninstexe, shell=True) proc.wait() elif self.branch == "1.8.1" or self.branch == "1.8" or self.branch == "1.9": debug("we are in 1.8 uninstall land") uninst = self.dest + "/uninstall/uninst.exe" helper = self.dest + "/uninstall/helper.exe" debug("uninst: " + uninst) debug("helper: " + helper) if os.path.exists(helper): debug("helper exists") args = helper + " /S /D=" + os.path.normpath(self.dest) debug("running helper with args: " + args) proc = subprocess.Popen(args, shell=True) proc.wait() elif os.path.exists(uninst): args = uninst + " /S /D=" + os.path.normpath(self.dest) debug("running uninst with args: " + args) proc = subprocess.Popen(args, shell=True) proc.wait() else: uninst = self.dest + "/" + self.product + "/uninstall/uninstaller.exe" args = uninst + " /S /D=" + os.path.normpath(self.dest) debug("running uninstaller with args: " + args) proc = subprocess.Popen(args, shell=True) proc.wait() time.sleep(10) class MozInstaller: def __init__(self, **kwargs): debug("install constructor!") assert (kwargs['dest'] != "" and kwargs['dest'] != None) assert (kwargs['src'] != "" and kwargs['src'] != None) assert (kwargs['productName'] != "" and kwargs['productName'] != None) assert (kwargs['branch'] != "" and kwargs['branch'] != None) self.src = kwargs['src'] self.dest = kwargs['dest'] self.productName = kwargs['productName'] self.branch = kwargs['branch'] debug("running uninstall") uninstaller = MozUninstaller(dest = self.dest, productName = self.productName, branch = self.branch) if isDMG.match(self.src): self.installDmg() elif isTARBZ.match(self.src): self.installTarBz() elif isTARGZ.match(self.src): self.installTarGz() elif isZIP.match(self.src): self.installZip() elif isEXE.match(self.src): self.installExe() # Simple utility function to get around python's path module's inability # to understand ~/... style paths def normalizePath(self, path): if path[0] == "~": path = os.path.expanduser(path) debug("NORMALIZE: path: " + path) try: if not os.path.exists(path): os.makedirs(path) except: # TODO: Better catch and error message print "Error creating destination directory" return path def installDmg(self): # Ensure our destination directory exists self.dest = self.normalizePath(self.dest) args = "sh installdmg.sh " + self.src + " " + self.dest proc = subprocess.Popen(args, shell=True) proc.wait() # TODO: throw stderr def installTarBz(self): # Ensure our destination directory exists self.dest = self.normalizePath(self.dest) self.unTar("-jxvf") def installTarGz(self): # Ensure our destination directory exists self.dest = self.normalizePath(self.dest) self.unTar("-zxvf") def unTar(self, tarArgs): args = "tar " + tarArgs + " " + self.src + " -C " + self.dest proc = subprocess.Popen(args, shell=True) proc.wait() #TODO: throw stderr def installZip(self): self.dest = self.normalizePath(self.dest) args = "unzip -o -d " + self.dest + " " + self.src proc = subprocess.Popen(args, shell=True) proc.wait() # TODO: throw stderr def installExe(self): debug("running installEXE") args = self.src + " " if self.branch == "1.8.0": args += "-ms -hideBanner -dd " + self.dest else: debug("running install exe for 1.8.1") args += "/S /D=" + os.path.normpath(self.dest) # Do we need a shell=True here? proc = subprocess.Popen(args) proc.wait() # TODO: throw stderr # Enable it to be called from the command line with the options if __name__ == "__main__": parser = OptionParser() parser.add_option("-s", "--Source", dest="src", help="Installation Source File (whatever was downloaded) -\ accepts Zip, Exe, Tar.Bz, Tar.Gz, and DMG", metavar="SRC_FILE") parser.add_option("-d", "--Destination", dest="dest", help="Directory to install the build into", metavar="DEST") parser.add_option("-b", "--Branch", dest="branch", help="Branch the build is from must be one of: 1.8.0|1.8|\ 1.9", metavar="BRANCH") parser.add_option("-p", "--Product", dest="product", help="Product name - optional should be all lowercase if\ specified: firefox, thunderbird, etc", metavar="PRODUCT") parser.add_option("-o", "--Operation", dest="op", help="The operation you would like the script to perform.\ Should be either install (i) or uninstall (u) or delete\ (d) to recursively delete the directory specified in dest", metavar="OP") (options, args) = parser.parse_args() # Run it if string.upper(options.op) == "INSTALL" or string.upper(options.op) == "I": installer = MozInstaller(src = options.src, dest = options.dest, branch = options.branch, productName = options.product) elif string.upper(options.op) == "UNINSTALL" or string.upper(options.op) == "U": uninstaller = MozUninstaller(dest = options.dest, branch = options.branch, productName = options.product) elif string.upper(options.op) == "DELETE" or string.upper(options.op) == "D": rmdirRecursive(options.dest)