From 04956c91721d23363cdfe3b2f30be4cb2ba2908a Mon Sep 17 00:00:00 2001 From: Norberto Lopez <106690103+nobes888@users.noreply.github.com> Date: Sat, 7 May 2016 03:53:54 +0000 Subject: [PATCH] Updated logic to support - Code cleanup / refactor - Improved Java versioning related logic - Reworked jvmRelease -> jvmVerSpec - Added support for deployed JREs --- script/appleUtils.py | 32 +++-- script/buildDist.py | 124 +++++++++++-------- script/deployAppDist.py | 1 + script/deployJreDist.py | 192 ++++++++++++++++++++++++++++++ script/jreUtils.py | 257 ++++++++++++++++++++++++++++------------ script/linuxUtils.py | 22 ++-- script/miscUtils.py | 41 +++++-- script/windowsUtils.py | 38 +++--- 8 files changed, 516 insertions(+), 191 deletions(-) diff --git a/script/appleUtils.py b/script/appleUtils.py index b075ffc..226ccfa 100644 --- a/script/appleUtils.py +++ b/script/appleUtils.py @@ -20,25 +20,22 @@ def buildRelease(args, buildPath): # Retrieve vars of interest appName = args.name version = args.version - jreRelease = args.jreVersion + jreVerSpec = args.jreVerSpec platformStr = 'apple' # Check our system environment before proceeding if checkSystemEnvironment() == False: return - # Attempt to locate a default JRE if None is specified in the args. - if jreRelease == None: - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - # Let the user know if the 'user' specified JRE is not available and locate an alternative - elif jreUtils.getJreTarGzFile(platformStr, jreRelease) == None: - print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreRelease, platformStr.capitalize())) - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - args.jreRelease = jreRelease + # Select the jreTarGzFile to utilize for static releases + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec) + if jreTarGzFile == None: + # Let the user know if the 'user' specified JRE is not available and locate an alternative + print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreVerSpec, platformStr.capitalize())) + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, None) - # Form the list of distributions to build (dynamic and static JREs) + # Form the list of distributions to build (dynamic and static releases) distList = [(appName + '-' + version, None)] - jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreRelease) if jreTarGzFile != None: distList.append((appName + '-' + version + '-jre', jreTarGzFile)) @@ -47,7 +44,7 @@ def buildRelease(args, buildPath): print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName)) # Let the user know of the JRE tar.gz we are going to build with if aJreTarGzFile != None: - print('\tUtilizing jreRelease: ' + aJreTarGzFile) + print('\tUtilizing JRE: ' + aJreTarGzFile) # Create a tmp folder and build the static release to the tmp folder tmpPath = tempfile.mkdtemp(prefix=platformStr, dir=buildPath) @@ -62,9 +59,9 @@ def buildRelease(args, buildPath): print('\tForming DMG image. File: ' + dmgFile) proc = miscUtils.executeAndLog(cmd, "\t\tgenisoimage: ") if proc.returncode != 0: - print('\tError: Failed to form DMG image. Return code: ' + str(proc.returncode)) + print('\tError: Failed to form DMG image. Return code: ' + str(proc.returncode) + '\n') else: - print('\tFinished building release: ' + os.path.basename(dmgFile)) + print('\tFinished building release: ' + os.path.basename(dmgFile) + '\n') # Perform cleanup shutil.rmtree(tmpPath) @@ -136,7 +133,6 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile): appName = args.name bgFile = args.bgFile icnsFile = args.icnsFile - jreRelease = args.jreRelease # Form the symbolic link which points to /Applications srcPath = '/Applications' @@ -208,7 +204,7 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile): # Form the Info.plist file dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'Info.plist') if jreTarGzFile != None: - buildPListInfoStatic(dstPath, args) + buildPListInfoStatic(dstPath, args, jreTarGzFile) else: buildPListInfoShared(dstPath, args) @@ -353,7 +349,7 @@ def buildPListInfoShared(destFile, args): f.close() -def buildPListInfoStatic(destFile, args): +def buildPListInfoStatic(destFile, args, jreTarGzFile): # Retrieve vars of interest icnsStr = None if args.icnsFile != None: @@ -386,7 +382,7 @@ def buildPListInfoStatic(destFile, args): tupList.append(('CFBundleVersion', args.version)) tupList.append(('NSHighResolutionCapable', 'true')) tupList.append(('NSHumanReadableCopyright', '')) - jrePath = 'jre' + args.jreRelease + jrePath = jreUtils.getBasePathForJreTarGzFile(jreTarGzFile) tupList.append(('JVMRuntime', jrePath)) tupList.append(('JVMMainClassName', 'appLauncher.AppLauncher')) diff --git a/script/buildDist.py b/script/buildDist.py index 53655d1..c0de6c0 100755 --- a/script/buildDist.py +++ b/script/buildDist.py @@ -1,7 +1,7 @@ #! /usr/bin/env python +from __future__ import print_function import argparse -import distutils.spawn import getpass import math import os @@ -12,31 +12,14 @@ import sys import tempfile import time -#import buildCatalog +import distutils.spawn +import jreUtils import miscUtils import appleUtils import linuxUtils import windowsUtils - - -class FancyArgumentParser(argparse.ArgumentParser): - def __init__(self, *args, **kwargs): - self._action_defaults = {} - argparse.ArgumentParser.__init__(self, *args, **kwargs) - - def convert_arg_line_to_args(self, arg_line): - # Add the line as an argument if it does not appear to be a comment - if len(arg_line) > 0 and arg_line.strip()[0] != '#': - yield arg_line -# argsparse.ArgumentParser.convert_arg_line_to_args(arg_line) -# else: -# print('Skipping line: ' + arg_line) - -# # Example below will break all space separated lines into individual arguments -# for arg in arg_line.split(): -# if not arg.strip(): -# continue -# yield arg +from miscUtils import ErrorDM +from miscUtils import FancyArgumentParser def buildCatalogFile(args, deltaPath): @@ -49,11 +32,11 @@ def buildCatalogFile(args, deltaPath): records.append(record) # Record the JRE required - jreVersion = args.jreVersion - if jreVersion == None: - jreVersion = jreUtils.getDefaultJrVersion() - if jreVersion != None: - record = ('jre', jreVersion) + jreVerSpec = args.jreVerSpec + if jreVerSpec == None: + jreVerSpec = [jreUtils.getDefaultJreVerStr()] + if jreVerSpec != None: + record = ('jre', ",".join(jreVerSpec)) records.append(record) snipLen = len(deltaPath) + 1 @@ -101,13 +84,14 @@ def buildCatalogFile(args, deltaPath): f.write('exit\n') f.close() -def checkForRequiredApplications(): - """Method to ensure we have all of the required applications installed to support building of distributions - The current set of applications required are: - java, jar, genisoimage, ImageMagick:convert""" + +def checkForRequiredApplicationsAndExit(): + """Method to ensure we have all of the required applications installed to support building of distributions. + If there are mandatory applications that are missing then this will be printed to stderr and the program will exit. + The current set of required applications are: + java, jar, genisoimage""" evalPath = distutils.spawn.find_executable('java') errList = [] - warnList = [] if evalPath == None: errList.append('Failed while trying to locate java. Please install Java') evalPath = distutils.spawn.find_executable('jar') @@ -116,26 +100,36 @@ def checkForRequiredApplications(): evalPath = distutils.spawn.find_executable('genisoimage') if evalPath == None: errList.append('Failed while trying to locate genisoimage. Please install genisoimage') + + # Bail if there are no issues + if len(errList) == 0: + return + + # Log the issues and exit + print('There are configuration errors with the environment or system.') +# print('System Path:' + str(sys.path)) + print('Please correct the following:') + for aError in errList: + print('\t' + aError) + warnList = checkForSuggestedApplications() + if len(warnList) > 0: + print('In addition please fix the following for full program functionality:') + for aWarn in warnList: + print('\t' + aWarn) + sys.exit(0) + + +def checkForSuggestedApplications(): + """Method to check for any suggested missing applications. If all suggested applications are installed then this method will return None, + otherwise it will return a list of messages which describe the missing applications and the corresponding missing functionality. + The current set of suggested applications are: + ImageMagick:convert""" + warnList = [] evalPath = distutils.spawn.find_executable('convert') if evalPath == None: warnList.append('Application \'convert\' was not found. Please install (ImageMagick) convert') warnList.append('\tWindows icons will not be supported when using argument: -iconFile.') - if len(errList) > 0: - print('There are configuration errors with the environment or system.') -# print('System Path:' + str(sys.path)) - print('Please correct the following:') - for aError in errList: - print('\t' + aError) - if len(warnList) > 0: - print('Please correct the following for full program functionality:') - for aWarn in warnList: - print('\t' + aWarn) - sys.exit(0) - if len(warnList) > 0: - print('There are configuration issues with the environment or system.') - print('Please correct the following for full program functionality:') - for aWarn in warnList: - print('\t' + aWarn) + return warnList def getClassPath(javaCodePath): @@ -176,7 +170,9 @@ if __name__ == "__main__": parser.add_argument('-appArgs', help='Application arguments. Note that this argument must ALWAYS be the last specified!', nargs=argparse.REMAINDER, default=[]) parser.add_argument('-dataCode', '-dc', help='A list of supporting folders for the application.', nargs='+', default=[]) parser.add_argument('-javaCode', '-jc', help='A folder which contains the Java build.') - parser.add_argument('-jreVersion', help='JRE version to utilize. This should be a value like 1.7 or 1.8 or 1.8.34. Note there should be corresponding tar.gz JREs for each platform in the folder ~/jre/', default=None) + parser.add_argument('-jreVersion', dest='jreVerSpec', help='JRE version to utilize. This should be either 1 or 2 values where each value should be something like 1.7 or 1.8 or 1.8.0_34. ' + + 'If 2 values are specified than the second value must be later than the first value. Any static build will be built with the latest allowable JRE.' + + ' Note there should be corresponding tar.gz JREs for each platform in the folder ~/jre/', nargs='+', default=None) parser.add_argument('-jvmArgs', help='JVM arguments.', nargs='+', default=[]) parser.add_argument('-classPath', help='Class path listing of jar files relative to javaCode. Leave blank for auto determination.', nargs='+', default=[]) parser.add_argument('-debug', help='Turn on debug options for built applications.', action='store_true', default=False) @@ -194,8 +190,8 @@ if __name__ == "__main__": parser.print_help() exit() - # Check to ensure all of the required applications are installed - checkForRequiredApplications() + # Check to ensure all of the required applications are installed before proceeding + checkForRequiredApplicationsAndExit() # Parse the args parser.formatter_class.max_help_position = 50 @@ -212,13 +208,27 @@ if __name__ == "__main__": errList.append('-mainClass') if len(errList) != 0: print('At a minimum the following must be specified: ' + str(errList) + '.\nExiting...') - exit(); + exit() + + # Ensure the reserved 'jre' name is not utilized + if args.name.lower() == 'jre': + print('The application can not be named: {}. That name is reserved for the JRE.'.format(args.name)) + exit() + # # # Ensure java options are specified properly # if (args.javaCode == None and args.mainClass != None) or (args.javaCode != None and args.mainClass == None): # print('Both javaCode and mainClass must be specified, if either are specified. Exiting...') # exit(); + # Validate the jreVerSpec argument + try: + jreUtils.validateJreVersionSpec(args.jreVerSpec) + except ErrorDM as aExp: + print('The specified jreVerVersion is invalid. Input: {}'.format(args.jreVerSpec)) + print(' ' + aExp.message, file=sys.stderr) + exit() + # Form the classPath if none specified if args.javaCode != None and len(args.classPath) == 0: args.classPath = getClassPath(args.javaCode) @@ -242,6 +252,14 @@ if __name__ == "__main__": print(' [ERROR] The release appears to be built. Path: ' + buildPath) exit(-1) + # Let the user know of any missing functionality + warnList = checkForSuggestedApplications() + if len(warnList) > 0: + print('All suggested applications are not installed. There will be reduced functionality:') + for aWarn in warnList: + print('\t' + aWarn) + print() + # Form the buildPath os.makedirs(buildPath) @@ -292,3 +310,5 @@ if __name__ == "__main__": srcPath = os.path.join(miscUtils.getInstallRoot(), "deployAppDist.py") shutil.copy(srcPath, buildPath) + print('Finished building all distributions.\n') + diff --git a/script/deployAppDist.py b/script/deployAppDist.py index 8e92c25..e07167a 100755 --- a/script/deployAppDist.py +++ b/script/deployAppDist.py @@ -119,6 +119,7 @@ def addRelease(appName, version, buildDate): # Update the version info addReleaseInfo(installPath, appName, version, buildDate) + print('Application {} ({}) has been deployed to location: {}'.format(appName, version, args.deployRoot)) def delRelease(appName, version, buildDate): diff --git a/script/deployJreDist.py b/script/deployJreDist.py index e69de29..33ceff5 100755 --- a/script/deployJreDist.py +++ b/script/deployJreDist.py @@ -0,0 +1,192 @@ +#! /usr/bin/env python + +from __future__ import print_function +import argparse +import getpass +import math +import os +import shutil +import signal +import subprocess +import sys + +import miscUtils +import jreUtils +from miscUtils import ErrorDM + + +def addRelease(version): + # Normalize the JVM version for consistency + version = jreUtils.normalizeJvmVerStr(version) + + # Check to see if the deployed location already exists + installPath = os.path.join(rootPath, 'jre') + if os.path.isdir(installPath) == False: + print('A JRE has never been deployed to the root location: ' + args.deployRoot) + print('Create a new release of the JRE at the specified location?') + input = raw_input('--> ').upper() + if input != 'Y' and input != 'YES': + print('Release will not be made for JRE version: ' + version) + exit() + + # Build the deployed location + os.makedirs(installPath) + + # Check to see if the deploy version already exists + versionPath = os.path.join(installPath, version) + if os.path.isdir(versionPath) == True: + print('JREs with version, ' + version + ', has already been deployed.') + print(' The JREs have already been deployed.') + exit() + + # Update the version info + addReleaseInfo(installPath, version) + print('JRE ({}) has been deployed to location: {}'.format(version, args.deployRoot)) + + +def addReleaseInfo(installPath, verStr): + verFile = os.path.join(installPath, 'jreCatalog.txt') + + # Create the release info file + if os.path.isfile(verFile) == False: + f = open(verFile, 'w') + f.write('name' + ',' + 'JRE' + '\n') + f.write('digest' + ',' + 'sha256' + '\n\n') + f.close() + + # Locate the list of JRE files with a matching verStr + jreFiles = jreUtils.getJreTarGzFilesForVerStr(verStr) + if len(jreFiles) == 0: + raise ErrorDM('No JREs were located for the version: ' + verStr) + + # Updated the release info file + f = open(verFile, 'a') + f.write("jre,{}\n".format(verStr)) + for aFile in jreFiles: + stat = os.stat(aFile) + digestStr = miscUtils.computeDigestForFile(aFile, 'sha256') +# platformStr = jreUtils.getPlatformForJre(aFile) +# stat = os.stat(aFile) +# fileLen = os.path.s +# f.write("jre,{},{},{},{},{}\n".format(verStr, platformStr, os.path.basename(aFile), fileLen, digestStr)) + f.write("F,{},{},{}\n".format(digestStr, stat.st_size, os.path.basename(aFile))) + f.write('\n') + f.close() + + destPath = os.path.join(installPath, verStr) + os.makedirs(destPath) + + # Copy over the JRE files to the proper path + for aFile in jreFiles: + shutil.copy(aFile, destPath) + destFile = os.path.join(destPath, aFile) + os.chmod(destFile, 0644) + + +def delRelease(verStr): + # Normalize the JVM version for consistency + verStr = jreUtils.normalizeJvmVerStr(verStr) + + # Check to see if the deployed location already exists + installPath = os.path.join(rootPath, 'jre') + if os.path.isdir(installPath) == False: + print('A JRE has never been deployed to the root location: ' + args.deployRoot) + print('There are no JRE releases to remove. ') + exit() + + # Check to see if the deploy version already exists + versionPath = os.path.join(installPath, verStr) + if os.path.isdir(versionPath) == False: + print('JREs with version, ' + verStr + ', has not been deployed.') + print(' There is nothing to remove.') + exit() + + # Update the version info + delReleaseInfo(installPath, verStr) + + # Remove the release from the deployed location + shutil.rmtree(versionPath) + + +def delReleaseInfo(installPath, version): + verFile = os.path.join(installPath, 'jreCatalog.txt') + + # Bail if the release info file does not exist + if os.path.isfile(verFile) == False: + print('Failed to locate deployment release info file: ' + verFile) + print('Aborting removal action for version: ' + version) + exit() + + # Read the file + inputLineL = [] + isDeleteMode = False + f = open(verFile, 'r') + for line in f: + tokens = line[:-1].split(',', 1); + # Switch to deleteMode when we find a matching JRE release + if len(tokens) == 2 and tokens[0] == 'jre' and tokens[1] == version: + isDeleteMode = True + continue + # Switch out of deleteMode when we find a different JRE release + elif len(tokens) == 2 and tokens[0] == 'jre' and tokens[1] != version: + isDeleteMode = False + # Skip over the input line if we are in deleteMode + elif isDeleteMode == True: + continue + + # Save off the input line + inputLineL.append(line) + f.close() + + # Write the updated file + f = open(verFile, 'w') + for aLine in inputLineL: + f.write(aLine) + f.close() + + +if __name__ == "__main__": + argv = sys.argv; + argc = len(argv); + + # Logic to capture Ctrl-C and bail + signal.signal(signal.SIGINT, miscUtils.handleSignal) + + # Retrieve the location of the scriptPath + scriptPath = os.path.realpath(__file__) + scriptPath = os.path.dirname(scriptPath) + + # Set up the argument parser + parser = argparse.ArgumentParser(prefix_chars='-', add_help=False, fromfile_prefix_chars='@') + parser.add_argument('-help', '-h', help='Show this help message and exit.', action='help') + parser.add_argument('-remove', help='Remove the specified JRE distribution.', action='store_true', default=False) + parser.add_argument('-version', help='The fully qualified JRE version to deploy.', required=True) + parser.add_argument('deployRoot', help='Root location to deploy the specified JRE distribution.') + + # Intercept any request for a help message and bail + for aArg in argv: + if aArg == '-h' or aArg == '-help': + parser.print_help() + exit() + + # Parse the args + parser.formatter_class.max_help_position = 50 + args = parser.parse_args() + + rootPath = args.deployRoot + + # Uninstall the JRE, if remove argument is specified + version = args.version + if args.remove == True: + try: + delRelease(version) + except ErrorDM as aExp: + print('Failed to remove JREs with version: ' + version) + print(' ' + aExp.message, file=sys.stderr) + else: + try: + addRelease(version) + except ErrorDM as aExp: + print('Failed to deploy JREs with version: ' + version) + print(' ' + aExp.message, file=sys.stderr) + diff --git a/script/jreUtils.py b/script/jreUtils.py index 3a07080..ab16bc5 100644 --- a/script/jreUtils.py +++ b/script/jreUtils.py @@ -7,9 +7,11 @@ import subprocess import tempfile import miscUtils +from miscUtils import ErrorDM + # Globals -# The global variable defaultJreVersion is a hint for which (tar.gz) JRE release to +# The global variable defaultVersionStr is a hint for which (tar.gz) JRE release to # default to. Note if this variable is not specified then the latest JRE release # located in the appropriate /jre directory will be utilized. # @@ -17,64 +19,67 @@ import miscUtils # /jre/jre---x64.tar.gz # where PLATFORM is one of the following: apple, linux, windows # where VERSION is something like: 8u73 -# For example the Linux JRE 1.8.73 tar.gz release would be: +# For example the Linux JRE 1.8.0_73 tar.gz release would be: # /jre/jre-8u73-linux-x64.tar.gz -defaultJreVersion = '1.8' +defaultVersionStr = '1.8' -def getDefaultJreRelease(aPlatform): - """Utility method to locate the default JRE to utilize. The returned value will be a string such as 1.8.73""" - # Return the default if it is hard wired (and is available) - if 'defaultJreVersion' in globals(): - jreTarGzFile = getJreTarGzFile(aPlatform, defaultJreVersion) - if jreTarGzFile == None: - print('[WARNING] The specified default JRE for the ' + aPlatform.capitalize() + ' platform is: ' + defaultJreVersion + '. However this JRE is not installed! <---') - else: - return '1.' + '.'.join(getJreTarGzVersion(jreTarGzFile)) - - jreTarGzFile = getJreTarGzFile(aPlatform, None) - if jreTarGzFile == None: - return None - return '1.' + '.'.join(getJreTarGzVersion(jreTarGzFile)) - - -def getDefaultJreVersion(): - """Returns the default JRE version. This will return None if a default JRE version has not been specified - and one can not be determined.""" - if 'defaultJreVersion' in globals(): - return defaultJreVersion; - return None - - -def doesJreVersionMatch(aRequestVer, aEvaluateVer): - """Returns true if aEvaluateVer is a match for the requested version. Note that aEvaluateVer is considered - a match if each (available) component corresponds to the component in a aRequestVer. aRequestVer must - not be any more specific than aEvaluateVer""" - if len(aRequestVer) > len(aEvaluateVer): - return False - - # check each component for equality - for r, e in zip(aRequestVer, aEvaluateVer): - if r != e: - return False - - return True - def getBasePathForJreTarGzFile(aJreTarGzFile): """Returns the JRE (base) path that should be used to access the JRE found in the specified JRE tar.gz. This is needed since different JRE tar.gz files have been found to have different top level paths. Using this method ensures consistency between JRE tar.gz releases after the tar.gz file is unpacked.""" - verDescr = getJreTarGzVersion(aJreTarGzFile); - if verDescr == None: - raise Exception("File name (' + aJreTarGzFile + ') does not conform to proper JRE tar.gz name specification.") - basePath = 'jre1.' + '.'.join(verDescr) + verArr = getJreTarGzVerArr(aJreTarGzFile); + if verArr == None: + raise ErrorDM('File name (' + aJreTarGzFile + ') does not conform to proper JRE tar.gz name specification.') + basePath = 'jre' + verArrToVerStr(verArr) return basePath; -def getJreTarGzFile(aPlatform, aJreRelease): - """Returns the JRE tar.gz file for the appropriate platform and JRE release. This will return None if - there is no file that is sufficient for the request. Note if you do not care about any specific update - for a major version of JAVA then just specify the major version. Example '1.8' instead of '1.8.73'""" +def getDefaultJreVerStr(): + """Returns the default JRE version. This will return None if a default JRE version has not been specified + and one can not be determined.""" + if 'defaultVersionStr' in globals(): + return defaultVersionStr; + return None + + +def getJreTarGzFilesForVerStr(aVerStr): + """Returns the JRE tar.gz files matching the specified specific JRE version specification. This will return an empty + list if there is no JRE tar.gz files that is sufficient for the request. + + The version must be fully qualified (must specify platform, major, minor, and update components). If it is not then + an ErrorDM will be raised. An example of a valid fully qualified version string is: '1.8.0_73'""" + appInstallRoot = miscUtils.getInstallRoot() + appInstallRoot = os.path.dirname(appInstallRoot) + + # Retrieve the target version - and ensure it is fully qualified + targVer = verStrToVerArr(aVerStr) + if len(targVer) != 4: + raise ErrorDM('The specified version is not a fully qualified version. Version: ' + aVerStr) + + # Search all the appropriate tar.gz JREs for exact matches in our JRE folder + retList = [] + matchName = "jre-*-*-x64.tar.gz"; + searchPath = os.path.join(os.path.abspath(appInstallRoot), 'jre', matchName) +# for aFile in ['jre-8u739-windows-x64.tar.gz', 'jre-8u60-windows-x64.tar.gz', 'jre-7u27-windows-x64.tar.gz']: + for aFile in glob.glob(searchPath): + # Ensure that the aFile's JVM version is an exact match of targVer + tmpVer = getJreTarGzVerArr(aFile) + if targVer != tmpVer: + continue + + retList.append(aFile); + + return sorted(retList) + + +def getJreTarGzFile(aPlatform, aJvmVerSpec): + """Returns the JRE tar.gz file for the appropriate platform and JRE release. If there are several possible + matches then the tar.gz with the latest version will be rteurned. + + This will return None if there is no file that is sufficient for the request. Note if you do not care about + any specific update for a major version of JAVA then just specify the major version. Example '1.8' instead of + 1.8.0_73'""" appInstallRoot = miscUtils.getInstallRoot() appInstallRoot = os.path.dirname(appInstallRoot) @@ -83,18 +88,13 @@ def getJreTarGzFile(aPlatform, aJreRelease): if platStr == 'apple': platStr = 'macosx' - # Transform the version from a string into a list of (version) components. Due to SUNs funny naming - # conventions we automatically strip the first part of the string if it matches '1.' - # Some examples: - # '1.8' ---> [8] [1.8.0] ---> [8, 0] - # '1.8.73' ---> [8, 73] - reqVer = [] - if aJreRelease != None: - if aJreRelease.startswith("1.") == True: - aJreRelease = aJreRelease[2:] - reqVer = aJreRelease.split(".") - reqVer = [y for y in reqVer if y != ''] -# print str(reqVer) + # Retrieve the min and max JVM versions from aJvmVerSpec + minJvmVer = None + if aJvmVerSpec != None and len(aJvmVerSpec) >= 1: + minJvmVer = verStrToVerArr(aJvmVerSpec[0]) + maxJvmVer = None + if aJvmVerSpec != None and len(aJvmVerSpec) == 2: + maxJvmVer = verStrToVerArr(aJvmVerSpec[1]) # Search all the appropriate tar.gz JREs for the best match from our JRE folder matchList = [] @@ -102,10 +102,13 @@ def getJreTarGzFile(aPlatform, aJreRelease): searchPath = os.path.join(os.path.abspath(appInstallRoot), 'jre', searchName) # for file in ['jre-8u739-windows-x64.tar.gz', 'jre-8u60-windows-x64.tar.gz', 'jre-7u27-windows-x64.tar.gz']: for file in glob.glob(searchPath): - # Determine if the tar.gz version is a match for the reqVer - tmpVer = getJreTarGzVersion(file) - if doesJreVersionMatch(reqVer, tmpVer) == True: - matchList.append(file); + # Ensure that the file's JVM version is in range of minJvmVer and maxJvmVer + tmpVer = getJreTarGzVerArr(file) + if minJvmVer != None and isVerAfterAB(minJvmVer, tmpVer) == True: + continue + if maxJvmVer != None and isVerAfterAB(tmpVer, maxJvmVer) == True: + continue + matchList.append(file); # Determine the best match of all the matches bestMatch = None @@ -114,13 +117,13 @@ def getJreTarGzFile(aPlatform, aJreRelease): bestMatch = aFile continue - bestVer = getJreTarGzVersion(bestMatch) - testVer = getJreTarGzVersion(aFile) + bestVer = getJreTarGzVerArr(bestMatch) + testVer = getJreTarGzVerArr(aFile) for b, t in zip(bestVer, testVer): if b == t: continue try: - if int(t) > int(b): + if t > b: bestMatch = aFile break; except: @@ -129,15 +132,17 @@ def getJreTarGzFile(aPlatform, aJreRelease): return bestMatch -def getJreTarGzVersion(aFile): +def getJreTarGzVerArr(aFile): """Returns the version corresponding to the passed in JRE tar.gz file. - The returned value will be a list consisting of the (major, minor) components of the version. + The returned value will be a list consisting of the (platform, major, minor, update) integer components of the version. The file naming convention is expected to be: - jre-u-*.tar.gz + jre-u-*.tar.gz where: - A ---> The major version - B ---> The minor version + B ---> The major version + D ---> The update version + Note that the platform version will be assumed to be: 1 + Note that the minor version will be assumed to be: 0 """ # Retrieve the version component of the fileName fileName = os.path.basename(aFile) @@ -151,10 +156,26 @@ def getJreTarGzVersion(aFile): # Determine the version based on the pattern 'u' where: # if there is no component then just assume 0 for minor version - retVer = verStr.split('u') - if len(retVer) == 1: - retVer.append(0) - return retVer + tokenArr = verStr.split('u') + retVerArr = [1, int(tokenArr[0]), 0] + if len(retVerArr) == 1: + retVerArr.append(0) + else: + retVerArr.append(int(tokenArr[1])) + return retVerArr + + +def normalizeJvmVerStr(aVerStr): + """Returns a normalized JVM version corresponding to the string. Normalization consists of ensuring all of the components + of the version are separated by '.' except if applicable the third '.' is changed into '_'. This is useful so that referencing + JVM versions is consistent. Some examples of normalization are: + 1.8 ---> 1.8 + 1.8.0.73 ---> 1.8.0_73 + 1.7.0_55 ---> 1.7.0_55 + """ + verArr = verStrToVerArr(aVerStr) + retVerStr = verArrToVerStr(verArr) + return retVerStr def unpackAndRenameToStandard(aJreTarGzFile, aDestPath): @@ -185,3 +206,85 @@ def unpackAndRenameToStandard(aJreTarGzFile, aDestPath): # Remove the the temporary path os.rmdir(tmpPath) + +def validateJreVersionSpec(aVerSpec): + """Method that ensures that the specified aVerSpec is a valid definition. A valid JVM version spec can be composed of the following + - None: The jreVersion was not specified and should default to DistMaker's default values. + - A list of 1: The value defines the minimum allowable JRE version + - A list of 2: The 2 value defines the 2 JRE versions. The first is the minimum value. The second is the maximum value + Furthermore each JRE version must be a valid specification. Examples of valid specifications are: + 1.7, 1.8.0, 1.8.0_73""" + # Bail if no version was specified + if aVerSpec == None: + return + + # Ensure the number of arguments are correct. Must be 1 or 2 + if (len(aVerSpec) in [1, 2]) == False: + raise ErrorDM('The parameter jreVersion can either be 1 or 2 values. The first value is the minVersion and (if specified) the later is the maxVersion. Values provided: ' + str(len(aVerSpec))) + + # Ensure the minVer is valid + minVer = verStrToVerArr(aVerSpec[0]) + if minVer == None: + raise ErrorDM('The specified string: "' + aVerSpec[0] + ' is not a valid JRE version specification.') + if len(aVerSpec) == 1: + return + + # Ensure the maxVer is valid and is after minVer + maxVer = verStrToVerArr(aVerSpec[1]) + if maxVer == None: + raise ErrorDM('The specified string: "' + aVerSpec[1] + ' is not a valid JRE version specification.') + + # Ensure the minVer is not later than the maxVer. Note it is ok if it is equal + if isVerAfterAB(minVer, maxVer) == True: + raise ErrorDM('In the parameter jreVersion, the minVer ({0}) is later than the maxVer ({1}).'.format(aVerSpec[0], aVerSpec[1])) + + +def verStrToVerArr(aVerStr): + """Utility method to convert a jvm version string to the equivalent integral version (list) component. If the specified version is not a valid jvm version + then an ErrorDM will be raised. Each component in the array will be integral. Note typical versions follow this pattern: .._ + Thus the following will get transformed to: + '1.7.0 ---> [1, 7, 0] + '1.8' ---> [1, 8] + '1.8.0_73' ---> [1, 8, 0, 73]""" + verStrArr = re.compile("[._]").split(aVerStr) + # Transform from string list to integer list + try: + retVerArr = [int(val) for val in verStrArr] + except: + raise ErrorDM('Invalid JVM version: ' + aVerStr) + return retVerArr + + +def verArrToVerStr(aVerArr): + """Utility method to convert an integral version (list) to the equivalent jvm version string. If the specified version is not a valid jvm version + then an ErrorDM will be raised. Each component in the list must be integral. Note typical versions follow this pattern: .._ + Thus the following will get transformed to: + [1, 7, 0] ---> '1.7.0' + [1, 8] ---> '1.8' + [1, 8, 0, 73] ---> '1.8.0_73'""" + # Transform from integer list to string + if len(aVerArr) <= 3: + retVerStr = ".".join(str(x) for x in aVerArr) + return retVerStr + else: + retVerStr = ".".join(str(x) for x in aVerArr[0:3]) + retVerStr += '_' + ".".join(str(x) for x in aVerArr[3:]) + return retVerStr + + +def isVerAfterAB(aJvmVerA, aJvmVerB): + """Returns True if the specified JvmVerA is later than aJvmVerB. Both versions should be composed of a list of integers. Typically this list would be formed + from the method verStrToVerArr. Note that if one version has more detail than the other and all others are previous components are equivalent then the more + detailed version will be considered later (since it can be assumed that the missing details refer to the very first issue)""" + for a, b in zip(aJvmVerA, aJvmVerB): + if a == b: + continue + if a > b: + return True + else: + return False + + # Since all other components are equal then verA is better than verB only if it is more detailed + if len(aJvmVerA) > len(aJvmVerB): + return True + return False diff --git a/script/linuxUtils.py b/script/linuxUtils.py index 0662f96..eec23f1 100644 --- a/script/linuxUtils.py +++ b/script/linuxUtils.py @@ -18,25 +18,22 @@ def buildRelease(args, buildPath): # Retrieve vars of interest appName = args.name version = args.version - jreRelease = args.jreVersion + jreVerSpec = args.jreVerSpec platformStr = 'linux' # Check our system environment before proceeding if checkSystemEnvironment() == False: return - # Attempt to locate a default JRE if None is specified in the args. - if jreRelease == None: - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - # Let the user know if the 'user' specified JRE is not available and locate an alternative - elif jreUtils.getJreTarGzFile(platformStr, jreRelease) == None: - print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreRelease, platformStr.capitalize())) - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - args.jreRelease = jreRelease + # Select the jreTarGzFile to utilize for static releases + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec) + if jreTarGzFile == None: + # Let the user know if the 'user' specified JRE is not available and locate an alternative + print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreVerSpec, platformStr.capitalize())) + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, None) # Form the list of distributions to build (dynamic and static JREs) distList = [(appName + '-' + version, None)] - jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreRelease) if jreTarGzFile != None: distList.append((appName + '-' + version + '-jre', jreTarGzFile)) @@ -48,7 +45,7 @@ def buildRelease(args, buildPath): print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName)) # Let the user know of the JRE release we are going to build with if aJreTarGzFile != None: - print('\tUtilizing jreRelease: ' + jreRelease) + print('\tUtilizing JRE: ' + aJreTarGzFile) # Create the (top level) distribution folder dstPath = os.path.join(tmpPath, aDistName) @@ -62,7 +59,7 @@ def buildRelease(args, buildPath): print('\tForming tar.gz file: ' + tarFile) childPath = aDistName subprocess.check_call(["tar", "-czf", tarFile, "-C", tmpPath, childPath], stderr=subprocess.STDOUT) - print('\tFinished building release: ' + os.path.basename(tarFile)) + print('\tFinished building release: ' + os.path.basename(tarFile) + '\n') # Perform cleanup shutil.rmtree(tmpPath) @@ -73,7 +70,6 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile): appInstallRoot = miscUtils.getInstallRoot() appInstallRoot = os.path.dirname(appInstallRoot) appName = args.name - jreRelease = args.jreRelease # Form the app contents folder srcPath = os.path.join(buildPath, "delta") diff --git a/script/miscUtils.py b/script/miscUtils.py index d2f9110..e61eef8 100644 --- a/script/miscUtils.py +++ b/script/miscUtils.py @@ -1,4 +1,5 @@ #! /usr/bin/env python +import argparse import hashlib import os import time @@ -8,6 +9,31 @@ import sys import logUtils +class FancyArgumentParser(argparse.ArgumentParser): + def __init__(self, *args, **kwargs): + self._action_defaults = {} + argparse.ArgumentParser.__init__(self, *args, **kwargs) + + def convert_arg_line_to_args(self, arg_line): + # Add the line as an argument if it does not appear to be a comment + if len(arg_line) > 0 and arg_line.strip()[0] != '#': + yield arg_line +# argsparse.ArgumentParser.convert_arg_line_to_args(arg_line) +# else: +# print('Skipping line: ' + arg_line) + +# # Example below will break all space separated lines into individual arguments +# for arg in arg_line.split(): +# if not arg.strip(): +# continue +# yield arg + + +class ErrorDM(Exception): + """Base class for exceptions in this module.""" + pass + + def checkRoot(): """Determines if the script is running with root priveleges.""" # This logic will may break on SELinux systems @@ -19,16 +45,16 @@ def checkRoot(): # Source: http://stackoverflow.com/questions/1131220/get-md5-hash-of-a-files-without-open-it-in-python -def computeDigestForFile(evalFile, digest, block_size=2**20): +def computeDigestForFile(evalFile, digestType, block_size=2**20): # Select the proper hash algorithm - if digest == 'md5': + if digestType == 'md5': hash = hashlib.md5() - elif digest == 'sha256': + elif digestType == 'sha256': hash = hashlib.sha256() - elif digest == 'sha512': + elif digestType == 'sha512': hash = hashlib.sha512() else: - raise Exception('Unrecognized hash function: ' + digest); + raise ErrorDM('Unrecognized hash function: ' + digestType); f = open(evalFile, 'rb') while True: @@ -44,14 +70,11 @@ def getInstallRoot(): """Returns the root path where the running script is installed.""" argv = sys.argv; installRoot = os.path.dirname(argv[0]) -# print('appInstallRoot: ' + appInstallRoot) return installRoot def executeAndLog(command, indentStr=""): """Executes the specified command via subprocess and logs all (stderr,stdout) output to the console""" -# proc = subprocess. -# proc.returncode = -1 try: proc = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) proc.wait() @@ -70,8 +93,6 @@ def executeAndLog(command, indentStr=""): class Proc: returncode = None proc = Proc - -# proc = None return proc # if proc.returncode != 0: diff --git a/script/windowsUtils.py b/script/windowsUtils.py index 483e6e3..98fc1cb 100644 --- a/script/windowsUtils.py +++ b/script/windowsUtils.py @@ -17,25 +17,22 @@ def buildRelease(args, buildPath): # Retrieve vars of interest appName = args.name version = args.version - jreRelease = args.jreVersion + jreVerSpec = args.jreVerSpec platformStr = 'windows' # Check our system environment before proceeding if checkSystemEnvironment() == False: return - # Attempt to locate a default JRE if none is specified in the args. - if jreRelease == None: - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - # Let the user know if the 'user' specified JRE is not available and locate an alternative - elif jreUtils.getJreTarGzFile(platformStr, jreRelease) == None: - print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreRelease, platformStr.capitalize())) - jreRelease = jreUtils.getDefaultJreRelease(platformStr) - args.jreRelease = jreRelease + # Select the jreTarGzFile to utilize for static releases + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec) + if jreTarGzFile == None: + # Let the user know if the 'user' specified JRE is not available and locate an alternative + print('[Warning] User specified JRE ({0}) is not available for {1} platform. Searching for alternative...'.format(jreVerSpec, platformStr.capitalize())) + jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, None) # Form the list of distributions to build (dynamic and static JREs) distList = [(appName + '-' + version, None)] - jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreRelease) if jreTarGzFile != None: distList.append((appName + '-' + version + '-jre', jreTarGzFile)) @@ -47,7 +44,7 @@ def buildRelease(args, buildPath): print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName)) # Let the user know of the JRE tar.gz we are going to build with if aJreTarGzFile != None: - print('\tUtilizing jreRelease: ' + aJreTarGzFile) + print('\tUtilizing JRE: ' + aJreTarGzFile) # Create the (top level) distribution folder dstPath = os.path.join(tmpPath, aDistName) @@ -63,9 +60,9 @@ def buildRelease(args, buildPath): proc = miscUtils.executeAndLog(cmd, "\t\tjar: ") if proc.returncode != 0: print('\tError: Failed to form zip file. Return code: ' + str(proc.returncode)) - print('\tAborting build of release: ' + os.path.basename(zipFile)) + print('\tAborting build of release: ' + os.path.basename(zipFile) + '\n') else: - print('\tFinished building release: ' + os.path.basename(zipFile)) + print('\tFinished building release: ' + os.path.basename(zipFile) + '\n') # Perform cleanup shutil.rmtree(tmpPath) @@ -76,7 +73,6 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile): appInstallRoot = miscUtils.getInstallRoot() appInstallRoot = os.path.dirname(appInstallRoot) appName = args.name - jreRelease = args.jreRelease # Form the app contents folder srcPath = os.path.join(buildPath, "delta") @@ -180,7 +176,7 @@ def buildLaunch4JConfig(destFile, args, jreTarGzFile, iconFile): jrePath = jreUtils.getBasePathForJreTarGzFile(jreTarGzFile) writeln(f, 2, "" + jrePath + ""); else: - jreVer = getJreMajorVersion(args.jreRelease) + jreVer = getJreMajorVersion(args.jreVerSpec[0]) writeln(f, 2, "" + jreVer + ""); # Valid values: '1.7.0' or '1.8.0' ... writeln(f, 2, "preferJre"); # Valid values: jreOnlyjdkOnly|preferJre|preferJdk for aJvmArg in args.jvmArgs: @@ -216,15 +212,15 @@ def checkSystemEnvironment(): return True -def getJreMajorVersion(aJreRelease): +def getJreMajorVersion(aJreVerStr): """Returns the minimum version of the JRE to utilize based on the passed in JRE release. - The passed in value should be of the form X.X.X ---> example: 1.8.73 would result in 1.8.0 - If aJreRelease is invalid then returns the default value of: 1.8.0""" - if aJreRelease == None: return '1.8.0' - verList = aJreRelease.split('.') + The passed in value should be of the form X.X.X ---> example: 1.8.0_73 would result in 1.8.0 + If aJreVerStr is invalid then returns the default value of: 1.8.0""" + if aJreVerStr == None: return '1.8.0' + verList = aJreVerStr.split('.') if len(verList) == 2: verList.append('0') if len(verList) == 3 and verList[2] == '': verList[2] = '0' - if len(verList) != 3: return '1.8.0' + if len(verList) < 3: return '1.8.0' try: [int(y) for y in verList] except: