Updates to DistMaker scripts:

[1] Updated variable names to reflect standardized naming convention.
[2] Added requirement check for Python 2.7 or later
[3] Added requirement check for Java 1.8 or later when building a distribution
[4] Added preliminary support for platform architectures (initial support for x64)
[5] Switched over to GNU style argument conventions rather than UNIX style conventions. Essentially DistMaker arguments require double dashes rather than single dashes.
[6] Added support for zip JREs (in addition to tar.gz files)
[7] Switched over to JRE catalog mechanism.
[7.1] A catalog file must be specified via '--jreCatalog' that lists all available JREs. See example file ./template/JreCatalog.txt
[7.2]  Eliminated file naming convention criteria for JRE file names. The only requirement is the file must end in .tar.gz or .zip
[8] Updated platform naming conventions ('Apple' → 'Macosx')
This commit is contained in:
Norberto Lopez
2020-05-01 23:46:23 +00:00
parent 95b2ffb32a
commit 5961d129f9
10 changed files with 767 additions and 694 deletions

View File

@@ -15,68 +15,72 @@ import miscUtils
import deployJreDist
def buildRelease(args, buildPath):
def buildRelease(aArgs, aBuildPath, aJreNodeL):
# We mutate args - thus make a custom copy
args = copy.copy(args)
args = copy.copy(aArgs)
# Retrieve vars of interest
appName = args.name
version = args.version
jreVerSpec = args.jreVerSpec
platformStr = 'apple'
archStr = 'x64'
platStr = 'macosx'
# Determine the types of builds we should do
platformType = miscUtils.getPlatformTypes(args.platform, platformStr)
platformType = miscUtils.getPlatformTypes(args.platform, platStr)
if platformType.nonJre == False and platformType.withJre == False:
return;
# Warn if a request for a non-JRE build. We do not support that for the Apple platform.
if 'apple-' in args.platform:
print('Building an Apple release without a JRE is currently not supported. This release will not be made.')
# Warn if a request for a non-JRE build. We do not support that for the Macosx platform.
if 'macosx-' in args.platform:
print('Building a Macosx release without a JRE is currently not supported. This release will not be made.')
# Check our system environment before proceeding
if checkSystemEnvironment() == False:
return
# Form the list of distributions to build (dynamic and static JREs)
distList = []
distL = []
# Note as of 2016May01 there is no longer support for a dynamic Apple release
# if platformType.nonJre == True:
# distList = [(appName + '-' + version, None)]
# distL = [(appName + '-' + version, None)]
if platformType.withJre == True:
# Select the jreTarGzFile to utilize for static releases
jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec)
if jreTarGzFile == None:
# Select the JreNode to utilize for static releases
tmpJreNode = jreUtils.getJreNode(aJreNodeL, archStr, platStr, jreVerSpec)
if tmpJreNode == None:
# Let the user know that a compatible JRE was not found - thus no static release will be made.
print('[Warning] No compatible JRE ({0}) is available for the {1} platform. A static release will not be provided for the platform.'.format(jreVerSpec, platformStr.capitalize()))
print('[Warning] No compatible JRE ({}) is available for the ({}) {} platform. A static release will not be provided for the platform.'.format(jreVerSpec, archStr, platStr.capitalize()))
# # Let the user know that a compatible JRE was not found - and thus no Apple builds will be made
print('Only static Apple distributions are supported - thus there will be no Apple distribution of the application: ' + appName + '\n')
print('Only static Macosx distributions are supported - thus there will be no Apple distribution of the application: ' + appName + '\n')
return
else:
distList.append((appName + '-' + version + '-jre', jreTarGzFile))
distL.append((appName + '-' + version + '-jre', tmpJreNode))
# Create the various distributions
for (aDistName, aJreTarGzFile) in distList:
print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName))
for (aDistName, aJreNode) in distL:
print('Building {} distribution: {}'.format(platStr.capitalize(), aDistName))
# Let the user know of the JRE release we are going to build with
if aJreTarGzFile != None:
print('\tUtilizing JRE: ' + aJreTarGzFile)
if aJreNode != None:
print('\tUtilizing JRE: ' + aJreNode.getFile())
# Create a tmp folder and build the static release to the tmp folder
tmpPath = tempfile.mkdtemp(prefix=platformStr, dir=buildPath)
tmpPath = tempfile.mkdtemp(prefix=platStr, dir=aBuildPath)
# Build the contents of the distribution folder
buildDistTree(buildPath, tmpPath, args, aJreTarGzFile)
buildDistTree(aBuildPath, tmpPath, args, aJreNode)
# Create the DMG image via genisoimage or hdiutil
dmgFile = os.path.join(buildPath, aDistName + '.dmg')
dmgFile = os.path.join(aBuildPath, aDistName + '.dmg')
if platform.system() == 'Darwin':
# cmd = ['hdiutil', 'create', '-fs', 'HFS+' '-o', dmgFile, '-quiet', '-volname', appName, '-srcfolder', tmpPath]
cmd = ['hdiutil', 'makehybrid', '-hfs', '-o', dmgFile, '-default-volume-name', appName, tmpPath]
indentStr = 'hdiutil'
else:
# cmd = ['genisoimage', '-o', dmgFile, '-quiet', '-V', appName, '-max-iso9660-filenames', '-hfs-unlock', '-uid', '501', '-gid', '80', '-r', '-D', '-apple', tmpPath]
cmd = ['genisoimage', '-o', dmgFile, '-quiet', '-V', appName, '-max-iso9660-filenames', '-hfs-unlock', '-D', '-r', '-apple', tmpPath]
indentStr = 'genisoimage'
print('\tForming DMG image. File: ' + dmgFile)
proc = miscUtils.executeAndLog(cmd, "\t\tgenisoimage: ")
indentStr = '\t\t' + indentStr + ': '
proc = miscUtils.executeAndLog(cmd, indentStr)
if proc.returncode != 0:
print('\tError: Failed to form DMG image. Return code: ' + str(proc.returncode) + '\n')
else:
@@ -144,15 +148,15 @@ def buildRelease(args, buildPath):
# os.rmdir(tmpPath)
def normGuid(mountPt):
def normGuid(aMountPt):
# Ensure we are running as root
miscUtils.checkRoot()
# Ensure mountPt is a loopback mountPt
# Ensure aMountPt is a loopback mountPt
# TODO
# The standard Apple group/user id is 501:80
cmd = ['chown', '-R', '501:80', mountPt]
cmd = ['chown', '-R', '501:80', aMountPt]
subprocess.check_call(cmd, stderr=None, stdout=None)
# # The more pythonic way below is disabled because the command does not handle paths with spaces
# for path, dirs, files in os.walk(rootPath):
@@ -165,76 +169,76 @@ def normGuid(mountPt):
def mount(dmgFile, mountPt):
def mount(dmgFile, aMountPt):
# Ensure we are running as root
miscUtils.checkRoot()
# Mount the dmgFile (silently ???)
# mount -o loop,ro -t hfsplus imagefile.dmg /mnt/mountpoint
cmd = ['mount', '-n', '-o', 'loop,rw', '-t', 'hfsplus', dmgFile, mountPt]
cmd = ['mount', '-n', '-o', 'loop,rw', '-t', 'hfsplus', dmgFile, aMountPt]
subprocess.call(cmd, stderr=subprocess.STDOUT)
def umount(mountPt):
def umount(aMountPt):
# Ensure we are running as root
miscUtils.checkRoot()
# Release the mount
cmd = ['umount', mountPt]
cmd = ['umount', aMountPt]
subprocess.call(cmd, stderr=subprocess.STDOUT)
def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
def buildDistTree(aBuildPath, aRootPath, aArgs, aJreNode):
# Retrieve vars of interest
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
appTemplatePath = os.path.join(appInstallRoot, 'template')
appName = args.name
bgFile = args.bgFile
icnsFile = args.icnsFile
appName = aArgs.name
bgFile = aArgs.bgFile
icnsFile = aArgs.icnsFile
# Form the symbolic link which points to /Applications
srcPath = '/Applications'
dstPath = os.path.join(rootPath, 'Applications');
dstPath = os.path.join(aRootPath, 'Applications');
os.symlink(srcPath, dstPath)
# Construct the app folder
appNodes = ['MacOS', 'Resources']
for aPath in appNodes:
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', aPath)
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', aPath)
os.makedirs(dstPath)
# Copy over the executable launcher
srcPath = os.path.join(appTemplatePath, 'apple', 'JavaAppLauncher')
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'MacOS')
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', 'MacOS')
shutil.copy(srcPath, dstPath)
# Unpack the JRE and set up the JRE tree
if jreTarGzFile != None:
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'PlugIns')
if aJreNode != None:
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', 'PlugIns')
os.makedirs(dstPath)
jreUtils.unpackAndRenameToStandard(jreTarGzFile, dstPath)
jreUtils.unpackAndRenameToStandard(aJreNode, dstPath)
# Write out the PkgInfo file
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', "PkgInfo")
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', "PkgInfo")
f = open(dstPath, 'wb')
f.write('APPL????')
f.close()
# Define the payloadPath for where to store the appLauncher
payloadPath = os.path.join(rootPath, appName + '.app', 'Contents')
payloadPath = os.path.join(aRootPath, appName + '.app', 'Contents')
# Form the app contents folder
srcPath = os.path.join(buildPath, "delta")
srcPath = os.path.join(aBuildPath, "delta")
dstPath = os.path.join(payloadPath, 'app')
shutil.copytree(srcPath, dstPath, symlinks=True)
# Link dlls to the MacOS directory so they can be found at launch
jarDir = os.path.join(rootPath, appName + '.app', 'Contents', 'app', 'code', 'osx')
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'MacOS')
jarDir = os.path.join(aRootPath, appName + '.app', 'Contents', 'app', 'code', 'osx')
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', 'MacOS')
for jniPath in glob.iglob(os.path.join(jarDir, "*.jnilib")):
jniFileName = os.path.basename(jniPath)
srcPath = os.path.join('..', 'app', 'code', 'osx', jniFileName)
@@ -253,98 +257,98 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
shutil.copy(srcPath, dstPath);
# Build the java component of the distribution
if args.javaCode != None:
if aArgs.javaCode != None:
# Form the Info.plist file
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'Info.plist')
buildPListInfo(dstPath, args, jreTarGzFile)
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', 'Info.plist')
buildPListInfo(dstPath, aArgs, aJreNode)
# Copy over the icon file *.icns
if icnsFile != None and os.path.exists(icnsFile) == True:
srcPath = icnsFile
dstPath = os.path.join(rootPath, appName + '.app', 'Contents', 'Resources')
dstPath = os.path.join(aRootPath, appName + '.app', 'Contents', 'Resources')
shutil.copy(srcPath, dstPath)
# Copy over the background file
srcPath = bgFile
if srcPath == None:
srcPath = os.path.join(appTemplatePath, 'background', 'background.png');
dstPath = os.path.join(rootPath, '.background')
dstPath = os.path.join(aRootPath, '.background')
os.mkdir(dstPath)
dstPath = os.path.join(rootPath, '.background', 'background.png')
dstPath = os.path.join(aRootPath, '.background', 'background.png')
shutil.copy(srcPath, dstPath)
# Copy over the .DS_Store
srcPath = os.path.join(appTemplatePath, '.DS_Store.template')
dstPath = os.path.join(rootPath, '.DS_Store')
srcPath = os.path.join(appTemplatePath, 'apple', '.DS_Store.template')
dstPath = os.path.join(aRootPath, '.DS_Store')
shutil.copy(srcPath, dstPath)
# Update the .DS_Store file to reflect the new volume name
srcPath = os.path.join(rootPath, '.DS_Store')
classPath = appInstallRoot + '/lib/glum.jar:' + appInstallRoot + '/lib/distMaker.jar:' + appInstallRoot + '/lib/guava-18.0.jar'
srcPath = os.path.join(aRootPath, '.DS_Store')
classPath = appInstallRoot + '/lib/glum-1.3.jar:' + appInstallRoot + '/lib/distMaker.jar:' + appInstallRoot + '/lib/guava-18.0.jar'
cmd = ['java', '-cp', classPath, 'dsstore.MainApp', srcPath, appName]
proc = miscUtils.executeAndLog(cmd, "\t\tdsstore.MainApp: ")
if proc.returncode != 0:
print('\tError: Failed to update .DS_Store. Return code: ' + str(proc.returncode))
def buildPListInfo(destFile, args, jreTarGzFile):
def buildPListInfo(aDestFile, aArgs, aJreNode):
"""Method that will construct and populate the Info.plist file. This file
defines the attributes associated with the (Apple) app."""
# Retrieve vars of interest
icnsStr = None
if args.icnsFile != None:
icnsStr = os.path.basename(args.icnsFile)
if aArgs.icnsFile != None:
icnsStr = os.path.basename(aArgs.icnsFile)
f = open(destFile, 'wb')
f = open(aDestFile, 'wb')
# writeln(f, 0, '<?xml version="1.0" encoding="UTF-8" standalone="no"?>')
writeln(f, 0, '<?xml version="1.0" ?>')
writeln(f, 0, '<plist version="1.0">')
writeln(f, 1, '<dict>')
tupList = []
tupList.append(('CFBundleDevelopmentRegion', 'English'))
tupList.append(('CFBundleExecutable', 'JavaAppLauncher'))
tupList.append(('CFBundleGetInfoString', args.company))
tupList.append(('CFBundleInfoDictionaryVersion', 6.0))
tupList.append(('CFBundleIconFile', icnsStr))
tupList.append(('CFBundleIdentifier', args.name.lower()))
tupList.append(('CFBundleDisplayName', args.name))
tupList.append(('CFBundleName', args.name))
tupList.append(('CFBundlePackageType', 'APPL'))
tupList.append(('CFBundleSignature', '????'))
tupList.append(('CFBundleVersion', args.version))
tupList.append(('NSHighResolutionCapable', 'true'))
tupList.append(('NSHumanReadableCopyright', ''))
tupL = []
tupL.append(('CFBundleDevelopmentRegion', 'English'))
tupL.append(('CFBundleExecutable', 'JavaAppLauncher'))
tupL.append(('CFBundleGetInfoString', aArgs.company))
tupL.append(('CFBundleInfoDictionaryVersion', 6.0))
tupL.append(('CFBundleIconFile', icnsStr))
tupL.append(('CFBundleIdentifier', aArgs.name.lower()))
tupL.append(('CFBundleDisplayName', aArgs.name))
tupL.append(('CFBundleName', aArgs.name))
tupL.append(('CFBundlePackageType', 'APPL'))
tupL.append(('CFBundleSignature', '????'))
tupL.append(('CFBundleVersion', aArgs.version))
tupL.append(('NSHighResolutionCapable', 'true'))
tupL.append(('NSHumanReadableCopyright', ''))
# Define the JVM that is to be uesd
if jreTarGzFile != None:
jrePath = jreUtils.getBasePathForJreTarGzFile(jreTarGzFile)
tupList.append(('JVMRuntime', jrePath))
if aJreNode != None:
jrePath = jreUtils.getBasePathFor(aJreNode)
tupL.append(('JVMRuntime', jrePath))
else:
# tupList.append(('JVMVersion', '1.7+'))
# tupL.append(('JVMVersion', '1.7+'))
raise Exception('Support for utilizing the system JRE has not been added yet.')
# Define the main entry point (AppLauncher) and the working directory
tupList.append(('JVMMainClassName', 'appLauncher.AppLauncher'))
tupL.append(('JVMMainClassName', 'appLauncher.AppLauncher'))
cwdPath = os.path.join('$APP_ROOT', 'Contents', 'app')
tupList.append(('WorkingDirectory', cwdPath))
tupL.append(('WorkingDirectory', cwdPath))
# Application configuration
for (key, val) in tupList:
for (key, val) in tupL:
writeln(f, 2, '<key>' + key + '</key>')
writeln(f, 2, '<string>' + str(val) + '</string>')
# JVM options
jvmArgs = list(args.jvmArgs)
jvmArgs = list(aArgs.jvmArgs)
if any(aStr.startswith('-Dapple.laf.useScreenMenuBar') == False for aStr in jvmArgs) == True:
jvmArgs.append('-Dapple.laf.useScreenMenuBar=true')
if any(aStr.startswith('-Dcom.apple.macos.useScreenMenuBar') == False for aStr in jvmArgs) == True:
jvmArgs.append('-Dcom.apple.macos.useScreenMenuBar=true')
if any(aStr.startswith('-Dcom.apple.macos.use-file-dialog-packages') == False for aStr in jvmArgs) == True:
jvmArgs.append('-Dcom.apple.macos.use-file-dialog-packages=true')
jvmArgs.append('-Dcom.apple.mrj.application.apple.menu.about.name=' + args.name)
jvmArgs.append('-Dapple.awt.application.name=' + args.name)
jvmArgs.append('-Dcom.apple.mrj.application.apple.menu.about.name=' + aArgs.name)
jvmArgs.append('-Dapple.awt.application.name=' + aArgs.name)
jvmArgs.append('-Djava.system.class.loader=appLauncher.RootClassLoader')
# if icnsStr != None:
# jvmArgs.append('-Xdock:icon=Contents/Resources/' + icnsStr)
@@ -361,10 +365,10 @@ def buildPListInfo(destFile, args, jreTarGzFile):
#
# classPathStr = '$JAVAROOT/' + deployJreDist.getAppLauncherFileName()
#
# tupList = []
# tupList.append(('ClassPath', classPathStr))
# tupL = []
# tupL.append(('ClassPath', classPathStr))
#
# for (key, val) in tupList:
# for (key, val) in tupL:
# writeln(f, 3, '<key>' + key + '</key>')
# writeln(f, 3, '<string>' + str(val) + '</string>')
#
@@ -377,7 +381,7 @@ def buildPListInfo(destFile, args, jreTarGzFile):
def checkSystemEnvironment():
"""Checks to ensure that all system application / environment variables
needed to build a Apple distribution are installed and properly configured.
needed to build a Macosx distribution are installed and properly configured.
Returns False if the system environment is insufficient"""
return True

View File

@@ -4,6 +4,7 @@ from __future__ import print_function
import argparse
import os
import platform
import re
import shutil
import signal
import subprocess
@@ -20,25 +21,23 @@ from miscUtils import ErrorDM
from miscUtils import FancyArgumentParser
def buildCatalogFile(args, deltaPath):
def buildCatalogFile(aArgs, aDeltaPath):
# Build the delta catalog
records = []
# Record the digest used
digestType = args.digest
digestType = aArgs.digest
record = ('digest', digestType)
records.append(record)
# Record the JRE required
jreVerSpec = args.jreVerSpec
if jreVerSpec == None:
jreVerSpec = [jreUtils.getDefaultJreVerStr()]
# Record the required JRE version
jreVerSpec = aArgs.jreVerSpec
record = ('jre', ",".join(jreVerSpec))
records.append(record)
snipLen = len(deltaPath) + 1
# for root, dirNames, fileNames in os.walk(deltaPath, onerror=failTracker.recordError):
for root, dirNames, fileNames in os.walk(deltaPath):
snipLen = len(aDeltaPath) + 1
# for root, dirNames, fileNames in os.walk(aDeltaPath, onerror=failTracker.recordError):
for root, dirNames, fileNames in os.walk(aDeltaPath):
# Presort the results alphabetically
dirNames.sort()
@@ -73,7 +72,7 @@ def buildCatalogFile(args, deltaPath):
print("Undefined node. Full path: " + fullPath + "\n")
# Save the records to the catalog file
dstPath = os.path.join(deltaPath, "catalog.txt")
dstPath = os.path.join(aDeltaPath, "catalog.txt")
f = open(dstPath, 'wb')
for aRecord in records:
f.write(','.join(aRecord) + '\n')
@@ -87,36 +86,47 @@ def checkForRequiredApplicationsAndExit():
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 or hdiutil)"""
# Check for java (version 1.8 or later)
evalPath = distutils.spawn.find_executable('java')
errList = []
errL = []
if evalPath == None:
errList.append('Failed while trying to locate java. Please install Java')
errL.append('Failed while trying to locate java. Please install Java')
else:
tmpStr = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)
pattern = '\"(\d+\.\d+).*\"'
verStr = re.search(pattern, tmpStr).groups()[0]
verVal = float(verStr)
if verVal < 1.8:
errL.append('Installed version of Java is too old. Require Java 1.8. Installed version: {}'.format(verVal))
# Check for jar
evalPath = distutils.spawn.find_executable('jar')
if evalPath == None:
errList.append('Failed while trying to locate jar. Please install jar (typically included with Java)')
errL.append('Failed while trying to locate jar. Please install jar (typically included with Java)')
# Check for genisoimage or hdiutil
genisoimagePath = distutils.spawn.find_executable('genisoimage')
hdiutilPath = distutils.spawn.find_executable('hdiutil')
if genisoimagePath == None and hdiutilPath == None:
if platform.system() == 'Darwin':
errList.append('Failed while trying to locate executable hdiutil. Please install hdiutil')
errL.append('Failed while trying to locate executable hdiutil. Please install hdiutil')
else:
errList.append('Failed while trying to locate executable genisoimage. Please install genisoimage')
errL.append('Failed while trying to locate executable genisoimage. Please install genisoimage')
# Bail if there are no issues
if len(errList) == 0:
if len(errL) == 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:
for aError in errL:
print('\t' + aError)
warnList = checkForSuggestedApplications()
if len(warnList) > 0:
warnL = checkForSuggestedApplications()
if len(warnL) > 0:
print('In addition please fix the following for full program functionality:')
for aWarn in warnList:
for aWarn in warnL:
print('\t' + aWarn)
sys.exit(0)
@@ -126,12 +136,12 @@ def checkForSuggestedApplications():
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 = []
retL = []
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.')
return warnList
retL.append('Application \'convert\' was not found. Please install (ImageMagick) convert')
retL.append('\tWindows icons will not be supported when using argument: -iconFile.')
return retL
def checkReadable(src, names):
@@ -152,110 +162,91 @@ def checkReadable(src, names):
return []
def getClassPath(javaCodePath):
retList = []
def getClassPath(aJavaCodePath):
retL = []
# Ensure the javaCodePath has a trailing slash
# Ensure the aJavaCodePath has a trailing slash
# to allow for proper computation of clipLen
if javaCodePath.endswith('/') == False:
javaCodePath += '/'
clipLen = len(javaCodePath)
if aJavaCodePath.endswith('/') == False:
aJavaCodePath += '/'
clipLen = len(aJavaCodePath)
# Form the default list of all jar files
for path, dirs, files in os.walk(javaCodePath):
for path, dirs, files in os.walk(aJavaCodePath):
files.sort()
for file in files:
if len(file) > 4 and file[-4:] == '.jar':
filePath = os.path.join(path, file)
filePath = filePath[clipLen:]
retList.append(filePath)
retL.append(filePath)
# print('Found jar file at: ' + filePath)
return retList
return retL
if __name__ == "__main__":
# Require Python version 2.7 or later
targVer = (2, 7)
miscUtils.requirePythonVersion(targVer)
# Logic to capture Ctrl-C and bail
signal.signal(signal.SIGINT, miscUtils.handleSignal)
# Set up the argument parser
parser = FancyArgumentParser(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('-name', help='The name of the application.')
parser.add_argument('-version', default='0.0.1', help='The version of the application.')
parser.add_argument('-mainClass', help='Application main entry point.')
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 files or folders for the application. All items will be copied to the data folder. Symbolic links will not be presereved.', nargs='+', default=[])
parser.add_argument('-javaCode', '-jc', help='A folder which contains the Java build.')
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)
parser.add_argument('-company', help='Company / Provider info.')
parser.add_argument('-bgFile', help='Background file used for apple dmg file.')
parser.add_argument('-iconFile', help='PNG file used for linux/windows icon.')
parser.add_argument('-icnsFile', help='Icon file used for apple build.')
parser.add_argument('-forceSingleInstance', help='Force the application to have only one instance.', default=False)
parser.add_argument('-digest', help='Digest used to ensure integrity of application upgrades. Default: sha256', choices=['md5', 'sha256', 'sha512'], default='sha256')
parser.add_argument('-enableJmx', help='Enables JMX technology on the target client. Allows one to attach jconsole, jvisualvm, or other JMX tools.', action='store_true', default=False)
parser.add_argument('-platform', help='Target platforms to build. Choices are: [apple, linux, windows]. Note the following (append) modifiers.'
+ ' Modifier \'-\' results in only the non-JRE build. Modifier \'+\' results in only the JRE build. Default: apple+, linux, windows', nargs='+', default=['apple+', 'linux', 'windows'],
choices=['apple', 'apple-', 'apple+', 'linux', 'linux-', 'linux+', 'windows', 'windows-', 'windows+'], metavar='PLATFORM')
parser.add_argument('--help', '-h', help='Show this help message and exit.', action='help')
parser.add_argument('--name', help='The name of the application.')
parser.add_argument('--version', default='0.0.1', help='The version of the application.')
parser.add_argument('--mainClass', help='Application main entry point.')
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', help='A list of supporting files or folders for the application. All items will be copied to the data folder. Symbolic links will not be presereved.', nargs='+', default=[])
parser.add_argument('--javaCode', help='A folder which contains the Java build.')
parser.add_argument('--jreCatalog', help='A JRE catalog file. This file provides the listing of available JREs for DistMaker to utilize.')
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 JREs for each relevant platform specified via arg: --jreCatalog', 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)
parser.add_argument('--company', help='Company / Provider info.')
parser.add_argument('--bgFile', help='Background file used for apple dmg file.')
parser.add_argument('--iconFile', help='PNG file used for linux/windows icon.')
parser.add_argument('--icnsFile', help='Icon file used for apple build.')
parser.add_argument('--forceSingleInstance', help='Force the application to have only one instance.', default=False)
parser.add_argument('--digest', help='Digest used to ensure integrity of application upgrades. Default: sha256', choices=['md5', 'sha256', 'sha512'], default='sha256')
parser.add_argument('--enableJmx', help='Enables JMX technology on the target client. Allows one to attach jconsole, jvisualvm, or other JMX tools.', action='store_true', default=False)
parser.add_argument('--platform', help='Target platforms to build. Choices are: [linux, macosx, windows]. Note the following (append) modifiers.'
+ ' Modifier \'-\' results in only the non-JRE build. Modifier \'+\' results in only the JRE build. Default: linux, macosx+, windows', nargs='+', default=['linux', 'macosx+', 'windows'],
choices=['linux', 'linux-', 'linux+', 'macosx', 'macosx-', 'macosx+', 'windows', 'windows-', 'windows+'], metavar='PLATFORM')
# parser.add_argument('-bundleId', help='Apple specific id descriptor.')
# Intercept any request for a help message and bail
argv = sys.argv;
if '-h' in argv or '-help' in argv:
if '-h' in argv or '-help' in argv or '--help' in argv:
parser.print_help()
exit()
# Check to ensure all of the required applications are installed before proceeding
checkForRequiredApplicationsAndExit()
# Check to ensure that the JRE path is a folder (or symbolic link to a folder)
installRoot = miscUtils.getInstallRoot()
installRoot = os.path.dirname(installRoot)
jrePath = os.path.join(installRoot, 'jre')
if os.path.islink(jrePath) == True and os.path.exists(jrePath) == False:
print('The specified JRE path refers to a broken symbol link. Please fix the JRE path to reflect a proper location.')
print(' The broken JRE symbolic link is at: {}'.format(jrePath))
print()
exit()
if os.path.exists(jrePath) == False:
print('The JRE folder does not exist. Please create a JRE folder (or a symolic link to a proper JRE folder).')
print(' The JRE path should be at: {}'.format(jrePath))
print(' Populate the folder with the JRE tar.gz release files for each platform of interest.')
print(' JRE tar.gz files may be acquired from: {}'.format('http://www.oracle.com/technetwork/java/javase/downloads'))
print()
exit()
if os.path.isdir(jrePath) == False:
print('The specified JRE path does not refer to a folder.')
print(' The JRE path should be a folder which contains the proper JRE tar.gz files.')
print(' The JRE folder should be located at: {}'.format(jrePath))
print()
exit()
# Parse the args
parser.formatter_class.max_help_position = 50
args = parser.parse_args()
# Warn if there are not any valid targets
if args.platform == ['apple-']:
print('The only release specified is Apple without JRE. This is currently unsupported.\nExiting...')
exit()
# Ensure we are getting the bare minimum options
errList = [];
# Ensure the bare minimum options were specified
errL = [];
if args.name == None:
errList.append('-name')
errL.append('--name')
if args.javaCode == None:
errList.append('-javaCode')
errL.append('--javaCode')
if args.jreCatalog == None:
errL.append('--jreCatalog')
if args.jreVerSpec == None:
errL.append('--jreVersion')
if args.mainClass == None:
errList.append('-mainClass')
if len(errList) != 0:
print('At a minimum the following must be specified: ' + str(errList) + '.\nExiting...')
errL.append('--mainClass')
if len(errL) != 0:
print('At a minimum the following must be specified: ' + str(errL) + '.\nExiting...')
exit()
# Ensure the name is not reserved: ['jre', 'launcher']
@@ -263,6 +254,27 @@ if __name__ == "__main__":
print('The application can not be named: {}. That name is reserved.\n'.format(args.name))
exit()
# Load the JRE catalog
errMsg = None
# if args.jreCatalog == None:
# errMsg = 'A JRE catalog must be specified! Please specify --jreCatalog'
if os.path.exists(args.jreCatalog) == False:
errMsg = 'The specified JRE catalog does not exist! File: ' + args.jreCatalog
elif os.path.isfile(args.jreCatalog) == False:
errMsg = 'The specified JRE catalog is not a valid file! File: ' + args.jreCatalog
if errMsg != None:
print(errMsg + '\n')
exit()
jreNodeL = jreUtils.loadJreCatalog(args.jreCatalog)
if len(jreNodeL) == 0:
print('Warning: Failed to load any JREs from the JRE catalog. Only non bundled JRE applications can be built!\n')
# Warn if there are not any valid targets
if args.platform == ['macosx-']:
print('The only release specified is Macosx without JRE. This is currently unsupported.\nExiting...')
exit()
#
# # Ensure java options are specified properly
# if (args.javaCode == None and args.mainClass != None) or (args.javaCode != None and args.mainClass == None):
@@ -309,10 +321,10 @@ if __name__ == "__main__":
exit(-1)
# Let the user know of any missing functionality
warnList = checkForSuggestedApplications()
if len(warnList) > 0:
warnL = checkForSuggestedApplications()
if len(warnL) > 0:
print('All suggested applications are not installed. There will be reduced functionality:')
for aWarn in warnList:
for aWarn in warnL:
print('\t' + aWarn)
print()
@@ -329,9 +341,9 @@ if __name__ == "__main__":
srcPath = args.dataCode[0].rstrip('/')
print(' [ERROR] The specified dataCode path will result in a data folder inside another data folder. Refusing action. Please specify the individual data files/folders.')
print(' Consider using:')
print(' -dataCode ' + srcPath + '/*')
print(' --dataCode ' + srcPath + '/*')
print(' instead of:')
print(' -dataCode ' + args.dataCode[0] + '\n')
print(' --dataCode ' + args.dataCode[0] + '\n')
shutil.rmtree(buildPath)
exit(-1)
@@ -380,13 +392,13 @@ if __name__ == "__main__":
buildCatalogFile(args, deltaPath)
# Build the Apple release
appleUtils.buildRelease(args, buildPath)
appleUtils.buildRelease(args, buildPath, jreNodeL)
# Build the Linux release
linuxUtils.buildRelease(args, buildPath)
linuxUtils.buildRelease(args, buildPath, jreNodeL)
# Build the Windows release
windowsUtils.buildRelease(args, buildPath)
windowsUtils.buildRelease(args, buildPath, jreNodeL)
# Copy over the deploy script
srcPath = os.path.join(miscUtils.getInstallRoot(), "deployAppDist.py")

View File

@@ -9,16 +9,16 @@ import signal
import subprocess
import sys
def getDistInfo(distPath):
def getDistInfo(aDistPath):
appName = None
version = None
buildDate = None
isLegacyJre = None
# Process the app.cfg file
cfgFile = os.path.join(distPath, 'delta', 'app.cfg')
cfgFile = os.path.join(aDistPath, 'delta', 'app.cfg')
if os.path.isfile(cfgFile) == False:
print('Distribution corresponding to the folder: ' + distPath + ' does not appear to be valid!')
print('Distribution corresponding to the folder: ' + aDistPath + ' does not appear to be valid!')
print('File does not exist: ' + cfgFile)
print('Release will not be deployed...')
exit()
@@ -38,15 +38,15 @@ def getDistInfo(distPath):
f.close()
if appName == None or version == None or buildDate == None:
print('Distribution corresponding to the folder: ' + distPath + ' does not appear to be valid!')
print('Distribution corresponding to the folder: ' + aDistPath + ' does not appear to be valid!')
print('The configuration file, ' + cfgFile + ', is not valid.')
print('Release will not be made for app ' + appName)
exit()
# Process the catalog.txt file
catFile = os.path.join(distPath, 'delta', 'catalog.txt')
catFile = os.path.join(aDistPath, 'delta', 'catalog.txt')
if os.path.isfile(catFile) == False:
print('Distribution corresponding to the folder: ' + distPath + ' does not appear to be valid!')
print('Distribution corresponding to the folder: ' + aDistPath + ' does not appear to be valid!')
print('File does not exist: ' + catFile)
print('Release will not be deployed...')
exit()
@@ -63,7 +63,7 @@ def getDistInfo(distPath):
f.close()
if isLegacyJre == None:
print('Distribution corresponding to the folder: ' + distPath + ' does not appear to be valid!')
print('Distribution corresponding to the folder: ' + aDistPath + ' does not appear to be valid!')
print('The catalog file, ' + catFile + ', is not valid.')
print('Release will not be made for app ' + appName)
exit()
@@ -77,11 +77,11 @@ def handleSignal(signal, frame):
sys.exit(0)
def addReleaseInfo(deployPath, appName, version, buildDate, isLegacyJre):
def addReleaseInfo(aDeployPath, aAppName, aVerStr, aBuildDate, aIsLegacyJre):
# Determine if this Application was deployed with a a legacy DistMaker release
legacyReleaseL = []
isLegacyRelease = False
verFile = os.path.join(deployPath, 'releaseInfo.txt')
verFile = os.path.join(aDeployPath, 'releaseInfo.txt')
if os.path.isfile(verFile) == True:
# Read the legacy releases
f = open(verFile, 'r')
@@ -109,18 +109,18 @@ def addReleaseInfo(deployPath, appName, version, buildDate, isLegacyJre):
f.close()
# Create the appCatalog.txt file
catFile = os.path.join(deployPath, 'appCatalog.txt')
catFile = os.path.join(aDeployPath, 'appCatalog.txt')
if os.path.isfile(catFile) == False:
if isLegacyRelease == True and len(legacyReleaseL) > 0:
f = open(catFile, 'w')
f.write('name' + ',' + appName + '\n\n')
f.write('name' + ',' + aAppName + '\n\n')
# Copy the legacy releases
for (aLegacyVer, aLegacyDate) in legacyReleaseL:
f.write('R,{},{}\n'.format(aLegacyVer, aLegacyDate))
f.write('info,msg,This is a legacy release.\n')
f.write('info,msg,\n')
if isLegacyJre == True:
f.write('info,msg,Downgrading to this version may require a mandatory upgrade (ver: ' + version + ') before further upgrades are allowed.\n\n')
if aIsLegacyJre == True:
f.write('info,msg,Downgrading to this version may require a mandatory upgrade (ver: ' + aVerStr + ') before further upgrades are allowed.\n\n')
else:
f.write('# A release should be made using a legacy JRE (1.8+) and this DistMaker release. The release notes will need to be manually.\n')
f.write('info,msg,Downgrading to this version will require a mandatory 2-step upgrade in order to use releases made with non legacy JREs.\n\n')
@@ -129,25 +129,25 @@ def addReleaseInfo(deployPath, appName, version, buildDate, isLegacyJre):
else:
# Form the default (empty) appCatalog.txt
f = open(catFile, 'w')
f.write('name' + ',' + appName + '\n\n')
f.write('name' + ',' + aAppName + '\n\n')
f.close()
os.chmod(catFile, 0o644)
# Updated the appCatalog.txt info file
f = open(catFile, 'a')
f.write('R,{},{}\n'.format(version, buildDate))
f.write('R,{},{}\n'.format(aVerStr, aBuildDate))
f.write('info,msg,There are no release notes available.\n\n')
f.close()
# Update the (legacy) releaseInfo.txt file
if isLegacyRelease == True and isLegacyJre == True:
if isLegacyRelease == True and aIsLegacyJre == True:
f = open(verFile, 'a')
f.write(version + ',' + buildDate + '\n')
f.write(aVerStr + ',' + aBuildDate + '\n')
f.close()
def delReleaseInfoLegacy(deployPath, appName, version, buildDate):
verFile = os.path.join(deployPath, 'releaseInfo.txt')
def delReleaseInfoLegacy(aDeployPath, aAppName, aVerStr, aBuildDate):
verFile = os.path.join(aDeployPath, 'releaseInfo.txt')
# Bail if the release info file does not exist
if os.path.isfile(verFile) == False:
@@ -158,31 +158,31 @@ def delReleaseInfoLegacy(deployPath, appName, version, buildDate):
f = open(verFile, 'r')
for line in f:
tokens = line[:-1].split(',', 1);
if len(tokens) == 2 and tokens[0] == version:
if len(tokens) == 2 and tokens[0] == aVerStr:
# By not adding the current record to the releaseInfo list, we are effectively removing the record
print('Removing release record from info file. Version: ' + version)
print('Removing release record from info file. Version: ' + aVerStr)
elif len(tokens) == 2 and tokens[0] != 'name':
releaseInfo.append((tokens[0], tokens[1]))
f.close()
# Write the updated file
f = open(verFile, 'w')
f.write('name' + ',' + appName + '\n')
f.write('name' + ',' + aAppName + '\n')
for verTup in releaseInfo:
f.write(verTup[0] + ',' + verTup[1] + '\n')
f.close()
def delReleaseInfo(deployPath, appName, version, buildDate):
def delReleaseInfo(aDeployPath, aAppName, aVerStr, aBuildDate):
# Remove any legacy releases
delReleaseInfoLegacy(deployPath, appName, version, buildDate)
delReleaseInfoLegacy(aDeployPath, aAppName, aVerStr, aBuildDate)
catFile = os.path.join(deployPath, 'appCatalog.txt')
catFile = os.path.join(aDeployPath, 'appCatalog.txt')
# Bail if the appCatalog.txt file does not exist
if os.path.isfile(catFile) == False:
print('Failed to locate deployment appCatalog file: ' + catFile)
print('Aborting removal action for version: ' + version)
print('Aborting removal action for version: ' + aVerStr)
exit()
# Read the file (and skip over all lines found after the release we are searching for)
@@ -191,14 +191,13 @@ def delReleaseInfo(deployPath, appName, version, buildDate):
f = open(catFile, 'r')
for aLine in f:
aLine = aLine[:-1]
tokenL = aLine.split(',', 1);
tokenL = aLine.split(',');
# Determine when to enter / exit isDeleteMode
if len(tokenL) == 3 and tokenL[0] == 'R' and tokenL[1] == version:
if len(tokenL) == 3 and tokenL[0] == 'R' and tokenL[1] == aVerStr:
# By not adding the current record to the releaseInfo list, we are effectively removing the record
isDeleteMode = True
print('Removing release record from info file. Version: ' + version)
# We exit deleteMode when see a different release or exit instruction
elif len(tokenL) == 3 and tokenL[0] == 'R' and tokenL[1] != version:
elif len(tokenL) == 3 and tokenL[0] == 'R' and tokenL[1] != aVerStr:
isDeleteMode = False
elif len(tokenL) >= 1 and tokenL[0] == 'exit':
isDeleteMode = False
@@ -208,35 +207,35 @@ def delReleaseInfo(deployPath, appName, version, buildDate):
continue
# Save off all lines when we are not in delete mode
passLineL += aLine
passLineL += [aLine]
f.close()
# Write the updated file
f = open(verFile, 'w')
f = open(catFile, 'w')
for aLine in passLineL:
f.write(aLine + '\n')
f.close()
def addRelease(appName, version, buildDate, isLegacyJre):
def addRelease(aRootPath, aAppName, aVerStr, aBuildDate, aIsLegacyJre):
# Check to see if the deployed location already exists
deployPath = os.path.join(rootPath, appName)
deployPath = os.path.join(aRootPath, aAppName)
if os.path.isdir(deployPath) == False:
print('Application ' + appName + ' has never been deployed to the root location: ' + args.deployRoot)
print('Application ' + aAppName + ' has never been deployed to the root location: ' + aRootPath)
print('Create a new release of the application at the specified location?')
input = raw_input('--> ').upper()
if input != 'Y' and input != 'YES':
print('Release will not be made for app ' + appName)
print('Release will not be made for app ' + aAppName)
exit()
# Build the deployed location
os.makedirs(deployPath, 0o755)
# Check to see if the deploy version already exists
versionPath = os.path.join(deployPath, version)
versionPath = os.path.join(deployPath, aVerStr)
if os.path.isdir(versionPath) == True:
print('Application ' + appName + ' with version, ' + version + ', has already been deployed.')
print('Release will not be made for app ' + appName)
print('Application ' + aAppName + ' with version, ' + aVerStr + ', has already been deployed.')
print('Release will not be made for app ' + aAppName)
exit()
# Copy over the contents of the release folder to the deploy location
@@ -251,33 +250,42 @@ def addRelease(appName, version, buildDate, isLegacyJre):
os.chmod(versionPath, 0o755)
# Update the version info
addReleaseInfo(deployPath, appName, version, buildDate, isLegacyJre)
print('Application {} ({}) has been deployed to location: {}'.format(appName, version, args.deployRoot))
addReleaseInfo(deployPath, aAppName, aVerStr, aBuildDate, aIsLegacyJre)
print('Application {} ({}) has been deployed to location: {}'.format(aAppName, aVerStr, aRootPath))
def delRelease(appName, version, buildDate):
def delRelease(aRootPath, aAppName, aVerStr, aBuildDate):
# Check to see if the deployed location already exists
deployPath = os.path.join(rootPath, appName)
deployPath = os.path.join(aRootPath, aAppName)
if os.path.isdir(deployPath) == False:
print('Application ' + appName + ' has never been deployed to the root location: ' + args.deployRoot)
print('Application ' + aAppName + ' has never been deployed to the root location: ' + aRootPath)
print('There are no releases to remove. ')
exit()
# Check to see if the deploy version already exists
versionPath = os.path.join(deployPath, version)
versionPath = os.path.join(deployPath, aVerStr)
if os.path.isdir(versionPath) == False:
print('Application ' + appName + ' with version, ' + version + ', has not been deployed.')
print('Release will not be removed for app ' + appName)
print('Application ' + aAppName + ' with version, ' + aVerStr + ', has not been deployed.')
print('Release will not be removed for app ' + aAppName)
exit()
# Remove the release from the deployed location
shutil.rmtree(versionPath)
# Update the version info
delReleaseInfo(deployPath, appName, version, buildDate)
delReleaseInfo(deployPath, aAppName, aVerStr, aBuildDate)
print('Application {} ({}) has been removed from location: {}'.format(aAppName, aVerStr, aRootPath))
if __name__ == "__main__":
# Require python version 2.7 or later
targVer = (2, 7)
if sys.version_info < targVer:
print('The installed version of python is too old. Please upgrade.')
print(' Current version: ' + '.'.join(str(i) for i in sys.version_info))
print(' Require version: ' + '.'.join(str(i) for i in targVer))
sys.exit(-1)
# Logic to capture Ctrl-C and bail
signal.signal(signal.SIGINT, handleSignal)
@@ -287,14 +295,14 @@ if __name__ == "__main__":
# 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 distribution.', action='store_true', default=False)
parser.add_argument('--help', '-h', help='Show this help message and exit.', action='help')
parser.add_argument('--remove', help='Remove the specified distribution.', action='store_true', default=False)
parser.add_argument('deployRoot', help='Root location to deploy the specified distribution.')
parser.add_argument('distLoc', nargs='?', default=scriptPath, help='The location of the distribution to deploy.')
# Intercept any request for a help message and bail
argv = sys.argv;
if '-h' in argv or '-help' in argv:
if '-h' in argv or '-help' in argv or '--help' in argv:
parser.print_help()
exit()
@@ -302,10 +310,8 @@ if __name__ == "__main__":
parser.formatter_class.max_help_position = 50
args = parser.parse_args()
distPath = args.distLoc
rootPath = args.deployRoot
# Retrieve the distPath and ensure that it exists
distPath = args.distLoc
if os.path.isdir(distPath) == False:
print('Distribution corresponding to the folder: ' + distPath + ' does not exist!')
print('Release will not be deployed...')
@@ -314,9 +320,12 @@ if __name__ == "__main__":
# Determine the appName, version, and buildDate of the distribution
(appName, version, buildDate, isLegacyJre) = getDistInfo(distPath)
# Execute the appropriate action
rootPath = args.deployRoot
# Uninstall the app, if remove argument is specified
if args.remove == True:
delRelease(appName, version, buildDate)
delRelease(rootPath, appName, version, buildDate)
else:
addRelease(appName, version, buildDate, isLegacyJre)
addRelease(rootPath, appName, version, buildDate, isLegacyJre)

View File

@@ -2,6 +2,7 @@
from __future__ import print_function
import argparse
import collections
import getpass
import glob
import math
@@ -10,6 +11,7 @@ import shutil
import signal
import subprocess
import sys
from collections import OrderedDict
import jreUtils
import logUtils
@@ -40,8 +42,8 @@ def getAppLauncherVersion():
# Check for appLauncher.jar prerequisite
jarFile = getAppLauncherSourceFile()
if os.path.exists(jarFile) == False:
errPrintln('This installation of DistMaker appears to be broken. Please ensure there is an appLauncher.jar file in the template folder.', indent=1)
errPrintln('File does not exist: ' + jarFile, indent=1)
errPrintln('\tThis installation of DistMaker appears to be broken. Please ensure there is an appLauncher.jar file in the template folder.')
errPrintln('\tFile does not exist: ' + jarFile)
exit(-1)
try:
@@ -50,12 +52,12 @@ def getAppLauncherVersion():
version = output.split()[1]
return version
except Exception as aExp:
errPrintln('This installation of DistMaker appears to be broken. Failed to determine the AppLauncher version.', indent=1)
errPrintln('\tThis installation of DistMaker appears to be broken. Failed to determine the AppLauncher version.')
errPrintln(str(aExp))
exit(-1)
def addAppLauncherRelease():
def addAppLauncherRelease(aRootPath):
""" Adds the appLauncher.jar file to a well defined location under the deploy tree.
The appLauncher.jar file will be stored under ~/deploy/launcher/. The appLauncher.jar is
responsible for launching a DistMaker enabled application. This jar file will typically
@@ -65,7 +67,7 @@ def addAppLauncherRelease():
srcFile = getAppLauncherSourceFile()
# Ensure that the AppLauncher deployed location exists
deployPath = os.path.join(rootPath, 'launcher')
deployPath = os.path.join(aRootPath, 'launcher')
if os.path.isdir(deployPath) == False:
os.makedirs(deployPath, 0o755)
@@ -101,49 +103,53 @@ def addAppLauncherRelease():
os.chmod(dstFile, 0o644)
def addRelease(version):
def addRelease(aRootPath, aJreNodeL, aVerStr):
# Normalize the JVM version for consistency
version = jreUtils.normalizeJvmVerStr(version)
aVerStr = jreUtils.normalizeJvmVerStr(aVerStr)
# Check to see if the deployed location already exists
installPath = os.path.join(rootPath, 'jre')
installPath = os.path.join(aRootPath, '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?')
regPrintln('A JRE has never been deployed to the root location: ' + aRootPath)
regPrintln('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)
regPrintln('Release will not be made for JRE version: ' + aVerStr)
exit()
# Build the deployed location
os.makedirs(installPath, 0o755)
# Check to see if the deploy version already exists
versionPath = os.path.join(installPath, version)
versionPath = os.path.join(installPath, aVerStr)
if os.path.isdir(versionPath) == True:
print('JREs with version, ' + version + ', has already been deployed.')
print(' The JREs have already been deployed.')
regPrintln('JREs with version, ' + aVerStr + ', has already been deployed.')
regPrintln('\tThe JREs have already been deployed.')
exit()
# Update the version info
addReleaseInfo(installPath, version)
addAppLauncherRelease()
print('JRE ({}) has been deployed to location: {}'.format(version, args.deployRoot))
addReleaseInfo(installPath, aJreNodeL, aVerStr)
addAppLauncherRelease(aRootPath)
regPrintln('JRE ({}) has been deployed to location: {}'.format(aVerStr, aRootPath))
def addReleaseInfo(installPath, verStr):
def addReleaseInfo(aInstallPath, aJreNodeL, aVerStr):
# Var that holds the last recorded exit (distmaker) command
# By default assume the command has not been set
exitVerDM = None
# Create the jreCatalogfile
catFile = os.path.join(installPath, 'jreCatalog.txt')
catFile = os.path.join(aInstallPath, 'jreCatalog.txt')
if os.path.isfile(catFile) == False:
f = open(catFile, 'w')
f.write('name' + ',' + 'JRE' + '\n')
f.write('digest' + ',' + 'sha256' + '\n\n')
# Note new JRE catalogs require DistMaker versions 0.55 or later
f.write('exit,DistMaker,0.55' + '\n\n')
f.close()
os.chmod(catFile, 0o644)
exitVerDM = [0, 55]
# Determine the last exit,DistMaker instruction specified
else:
f = open(catFile, 'r')
@@ -151,38 +157,23 @@ def addReleaseInfo(installPath, verStr):
tokens = line[:-1].split(',');
# Record the (exit) version of interest
if len(tokens) == 3 and tokens[0] == 'exit' and tokens[1] == 'DistMaker':
exitVerDM = tokens[2]
try:
exitVerDM = jreUtils.verStrToVerArr(tokens[2])
except:
exitVerDM = None
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)
# Determine if the user is deploying a legacy JREs. Legacy JRE versions match the pattern 1.*
isLegacyJre = verStr.startswith('1.') == True
# Let the user know that legacy JREs can NOT be deployed once an exit,DistMaker instruction
# has been found. Basically once non-legacy JREs have been deployed then legacy JREs are no
# longer allowed.
if isLegacyJre == True and exitVerDM != None:
logUtils.errPrintln('Legacy JREs can not be deployed once any non legacy JRE has been deployed.')
logUtils.errPrintln('The specified JRE ({}) is considered legacy.'.format(verStr), indent=1)
exit()
# Locate the list of JREs with a matching version
matchJreNodeL = jreUtils.getJreNodesForVerStr(aJreNodeL, aVerStr)
if len(matchJreNodeL) == 0:
raise ErrorDM('No JREs were located for the version: ' + aVerStr)
# Determine if we need to record an exit instruction. An exit instruction is needed if the
# deployed JRE is non-legacy. Legacy DistMaker apps will not be able to handle non-legacy JREs.
# the catalog was built with an old version of DistMaker. Old versions of DistMaker do
# not specify exit instructions. If none is specified - ensure one is added.
needExitInstr = False
if isLegacyJre == False:
try:
tokenArr = exitVerDM.split('.')
majorVerAL = int(tokenArr[0])
minorVerAL = int(tokenArr[1])
if majorVerAL == 0 and minorVerAL < 1:
needExitInstr = True
except:
needExitInstr = True
pass
if exitVerDM == None:
needExitInstr = True
# Updated the jreCatalogfile
f = open(catFile, 'a')
@@ -190,62 +181,68 @@ def addReleaseInfo(installPath, verStr):
if needExitInstr == True:
f.write('exit,DistMaker,0.48\n\n')
# Write out the JRE release info
f.write("jre,{}\n".format(verStr))
f.write("jre,{}\n".format(aVerStr))
if needExitInstr == True:
f.write("require,AppLauncher,0.1,0.2\n")
for aFile in jreFiles:
stat = os.stat(aFile)
digestStr = miscUtils.computeDigestForFile(aFile, 'sha256')
for aJreNode in matchJreNodeL:
tmpFile = aJreNode.getFile()
stat = os.stat(tmpFile)
digestStr = miscUtils.computeDigestForFile(tmpFile, 'sha256')
fileLen = stat.st_size
platformStr = jreUtils.getPlatformForJreTarGzFile(aFile)
if isLegacyJre == False:
f.write("F,{},{},{},{}\n".format(digestStr, fileLen, platformStr, os.path.basename(aFile)))
archStr = aJreNode.getArchitecture();
platStr = aJreNode.getPlatform()
if exitVerDM != None and exitVerDM > [0, 54]:
f.write("F,{},{},{},{},{}\n".format(archStr, platStr, os.path.basename(tmpFile), digestStr, fileLen))
elif exitVerDM != None:
f.write("F,{},{},{},{}\n".format(digestStr, fileLen, platStr, os.path.basename(tmpFile)))
else:
f.write("F,{},{},{}\n".format(digestStr, fileLen, os.path.basename(aFile)))
f.write("F,{},{},{}\n".format(digestStr, fileLen, os.path.basename(tmpFile)))
f.write('\n')
f.close()
destPath = os.path.join(installPath, verStr)
destPath = os.path.join(aInstallPath, aVerStr)
os.makedirs(destPath, 0o755)
# Copy over the JRE files to the proper path
for aFile in jreFiles:
shutil.copy2(aFile, destPath)
destFile = os.path.join(destPath, aFile)
for aJreNode in matchJreNodeL:
tmpFile = aJreNode.getFile()
shutil.copy2(tmpFile, destPath)
destFile = os.path.join(destPath, os.path.basename(tmpFile))
os.chmod(destFile, 0o644)
def delRelease(verStr):
def delRelease(aRootPath, aVerStr):
# Normalize the JVM version for consistency
verStr = jreUtils.normalizeJvmVerStr(verStr)
aVerStr = jreUtils.normalizeJvmVerStr(aVerStr)
# Check to see if the deployed location already exists
installPath = os.path.join(rootPath, 'jre')
installPath = os.path.join(aRootPath, '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. ')
regPrintln('A JRE has never been deployed to the root location: ' + aRootPath)
regPrintln('There are no JRE releases to remove. ')
exit()
# Check to see if the deploy version already exists
versionPath = os.path.join(installPath, verStr)
versionPath = os.path.join(installPath, aVerStr)
if os.path.isdir(versionPath) == False:
print('JREs with version, ' + verStr + ', has not been deployed.')
print(' There is nothing to remove.')
regPrintln('JREs with version, ' + aVerStr + ', has not been deployed.')
regPrintln('There is nothing to remove.')
exit()
# Update the version info
delReleaseInfo(installPath, verStr)
delReleaseInfo(installPath, aVerStr)
# Remove the release from the deployed location
shutil.rmtree(versionPath)
regPrintln('JRE ({}) has been removed from location: {}'.format(aVerStr, aRootPath))
def delReleaseInfo(installPath, version):
def delReleaseInfo(aInstallPath, aVerStr):
# Bail if the jreCatalogfile does not exist
catFile = os.path.join(installPath, 'jreCatalog.txt')
catFile = os.path.join(aInstallPath, 'jreCatalog.txt')
if os.path.isfile(catFile) == False:
print('Failed to locate deployment catalog file: ' + catFile)
print('Aborting removal action for version: ' + version)
errPrintln('Failed to locate deployment catalog file: ' + catFile)
errPrintln('Aborting removal action for version: ' + aVerStr)
exit()
# Read the file
@@ -255,11 +252,11 @@ def delReleaseInfo(installPath, version):
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:
if len(tokens) == 2 and tokens[0] == 'jre' and tokens[1] == aVerStr:
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:
elif len(tokens) == 2 and tokens[0] == 'jre' and tokens[1] != aVerStr:
isDeleteMode = False
# Skip over the input line if we are in deleteMode
elif isDeleteMode == True:
@@ -276,48 +273,45 @@ def delReleaseInfo(installPath, version):
f.close()
def showReleaseInfo():
def showReleaseInfo(aRootPath, aJreNodeL):
"""This action will display information on the deployed / undeployed JREs to stdout."""
# Header section
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
regPrintln('Install Path: ' + os.path.abspath(appInstallRoot))
regPrintln(' Deploy Root: ' + aRootPath + '\n')
logUtils.regPrintln('Install Path: ' + os.path.abspath(appInstallRoot))
logUtils.regPrintln(' Deploy Root: ' + rootPath + '\n')
# Validate that the deployRoot location
if os.path.exists(rootPath) == False:
logUtils.errPrintln('The specified deployRoot does not exits.')
# Validate the (deploy) root location
if os.path.exists(aRootPath) == False:
errPrintln('The specified deployRoot does not exits.')
exit()
if os.path.isdir(rootPath) == False:
logUtils.errPrintln('The specified deployRoot does not appear to be a valid folder.')
if os.path.isdir(aRootPath) == False:
errPrintln('The specified deployRoot does not appear to be a valid folder.')
exit()
# Check to see if the jre folder exists in the deployRoot
installPath = os.path.join(rootPath, 'jre')
installPath = os.path.join(aRootPath, 'jre')
if os.path.isdir(installPath) == False:
logUtils.errPrintln('The specified deployRoot does not have any deployed JREs...')
errPrintln('The specified deployRoot does not have any deployed JREs...')
exit();
# Form a dictionary of all the JRE version to corresponding (JRE) tar.gz files
# This dictionary will include all the JREs that are known by this release of DistMaker
searchName = "jre-*.tar.gz";
searchPath = os.path.join(os.path.abspath(appInstallRoot), 'jre', searchName)
fullD = dict()
for aFile in glob.glob(searchPath):
# Skip to next if aFile is not a valid JRE tar.gz file
aFile = os.path.basename(aFile)
tmpVerArr = jreUtils.getJreTarGzVerArr(aFile)
if tmpVerArr == None:
continue
# Form 2 dictionaries:
# [1] File name (excluding path) to corresponding JreNode
# [2] JRE version to corresponding JreNodes
nameD = OrderedDict()
verD = OrderedDict()
for aJreNode in aJreNodeL:
tmpFileName = os.path.basename(aJreNode.getFile())
nameD[tmpFileName] = aJreNode
tmpVerStr = jreUtils.verArrToVerStr(tmpVerArr)
fullD.setdefault(tmpVerStr, [])
fullD[tmpVerStr].append(aFile)
tmpVerArr = aJreNode.getVersion()
tmpVerStr = jreUtils.verArrToVerStr(tmpVerArr)
verD.setdefault(tmpVerStr, [])
verD[tmpVerStr].append(aJreNode)
# Get the list of available (installable) JREs
availablePathL = []
for aVer in sorted(fullD.keys()):
for aVer in sorted(verD.keys()):
# Skip to next if this version has already been installed
versionPath = os.path.join(installPath, aVer)
if os.path.isdir(versionPath) == True:
@@ -325,14 +319,17 @@ def showReleaseInfo():
availablePathL.append(aVer)
# Show the list of available (installable) JREs
print('Available JREs:')
regPrintln('Available JREs:')
if len(availablePathL) == 0:
logUtils.regPrintln('There are no installable JREs.', indent=2)
regPrintln('\t\tThere are no installable JREs.\n')
for aVer in availablePathL:
logUtils.regPrintln('Version: {}'.format(aVer), indent=1)
for aFile in sorted(fullD[aVer]):
logUtils.regPrintln('{}'.format(aFile), indent=2)
logUtils.regPrintln('')
regPrintln('\tVersion: {}'.format(aVer))
for aJreNode in verD[aVer]:
archStr = aJreNode.getArchitecture();
platStr = aJreNode.getPlatform()
pathStr = aJreNode.getFile()
regPrintln('\t\t{} {:<7} {}'.format(archStr, platStr, pathStr))
regPrintln('')
# Get the list of all installed (version) folders
installedPathL = []
@@ -347,22 +344,35 @@ def showReleaseInfo():
installedPathL.append(verStr)
# Show the list of installed JREs
print('Installed JREs:')
regPrintln('Installed JREs:')
if len(installedPathL) == 0:
logUtils.regPrintln('There are no installed JREs.', indent=2)
regPrintln('\t\tThere are no installed JREs.')
for aVer in sorted(installedPathL):
logUtils.regPrintln('Version: {}'.format(aVer), indent=1)
regPrintln('\tVersion: {}'.format(aVer))
# Show all of the JREs in the specified (version) folder
for aFile in sorted(glob.glob(os.path.join(installPath, aVer) + '/*')):
tmpVerArr = jreUtils.getJreTarGzVerArr(aFile)
if tmpVerArr == None:
continue;
aFile = os.path.basename(aFile)
logUtils.regPrintln('{}'.format(aFile), indent=2)
logUtils.regPrintln('')
tmpFileName = os.path.basename(aFile)
# Bail if the file name does not correspond to a JRE
tmpJreNode = nameD.get(tmpFileName, None)
if tmpJreNode == None:
errPrintln('\t\tJRE file is not in the JRE catalog. File: ' + aFile)
continue
archStr = tmpJreNode.getArchitecture();
platStr = tmpJreNode.getPlatform()
pathStr = tmpJreNode.getFile()
regPrintln('\t\t{} {:<7} {}'.format(archStr, platStr, pathStr))
regPrintln('')
if __name__ == "__main__":
# Require python version 2.7 or later
targVer = (2, 7)
miscUtils.requirePythonVersion(targVer)
# Logic to capture Ctrl-C and bail
signal.signal(signal.SIGINT, miscUtils.handleSignal)
@@ -375,15 +385,16 @@ if __name__ == "__main__":
tmpDescr += 'top level deployment location. This location is typically made available via a public web server. The ';
tmpDescr += 'deployRoot should NOT refer to the child jre folder but rather the top level folder!'
parser = argparse.ArgumentParser(prefix_chars='-', add_help=False, fromfile_prefix_chars='@', description=tmpDescr)
parser.add_argument('-help', '-h', help='Show this help message and exit.', action='help')
parser.add_argument('-deploy', metavar='version', help='Deploy the specified JRE distribution to the deployRoot.', action='store', default=None)
parser.add_argument('-remove', metavar='version', help='Remove the specified JRE distribution from the deployRoot.', action='store', default=None)
parser.add_argument('-status', help='Display stats of all deployed/undeployed JREs relative to the deployRoot.', action='store_true', default=False)
parser.add_argument('--help', '-h', help='Show this help message and exit.', action='help')
parser.add_argument('--jreCatalog', help='A JRE catalog file. This file provides the listing of available JREs for DistMaker to utilize.')
parser.add_argument('--deploy', metavar='version', help='Deploy the specified JRE distribution to the deployRoot.', action='store', default=None)
parser.add_argument('--remove', metavar='version', help='Remove the specified JRE distribution from the deployRoot.', action='store', default=None)
parser.add_argument('--status', help='Display stats of all deployed/undeployed JREs relative to the deployRoot.', action='store_true', default=False)
parser.add_argument('deployRoot', help='Top level folder to the deployment root.')
# Intercept any request for a help message and bail
argv = sys.argv;
if '-h' in argv or '-help' in argv:
if '-h' in argv or '-help' in argv or '--help' in argv:
parser.print_help()
exit()
@@ -391,28 +402,49 @@ if __name__ == "__main__":
parser.formatter_class.max_help_position = 50
args = parser.parse_args()
# Process the args
# Load the JRE catalog
jreCatalog = args.jreCatalog
errMsg = None
if jreCatalog == None:
errMsg = 'A JRE catalog must be specified! Please specify -jreCatalog'
elif os.path.exists(jreCatalog) == False:
errMsg = 'The specified JRE catalog does not exist! File: ' + jreCatalog
elif os.path.isfile(jreCatalog) == False:
errMsg = 'The specified JRE catalog is not a valid file! File: ' + jreCatalog
if errMsg != None:
errPrintln(errMsg + '\n')
exit()
jreNodeL = jreUtils.loadJreCatalog(jreCatalog)
if len(jreNodeL) == 0:
errPrintln('Failed to load any JREs from the JRE catalog.')
errPrintln('\tA valid populated JRE catalog must be specified!')
errPrintln('\tJRE Catalog specified: {}\n'.format(jreCatalog))
exit()
# Execute the approriate action
rootPath = args.deployRoot
if args.status == True:
showReleaseInfo()
showReleaseInfo(rootPath, jreNodeL)
exit()
elif args.deploy != None:
# Deploy the specified JRE
version = args.deploy
try:
addRelease(version)
addRelease(rootPath, jreNodeL, version)
except ErrorDM as aExp:
print('Failed to deploy JREs with version: ' + version)
print(' ' + aExp.message, file=sys.stderr)
errPrintln('Failed to deploy JREs with version: ' + version)
errPrintln('\t' + aExp.message)
elif args.remove != None:
# Remove the specified JRE
version = args.remove
try:
delRelease(version)
delRelease(rootPath, version)
except ErrorDM as aExp:
print('Failed to deploy JREs with version: ' + version)
print(' ' + aExp.message, file=sys.stderr)
errPrintln('Failed to deploy JREs with version: ' + version)
errPrintln('\t' + aExp.message)
else:
print('Please specify one of the valid actions: [-deploy, -remove, -status]')
regPrintln('Please specify one of the valid actions: [--deploy, --remove, --status]')

View File

@@ -6,60 +6,60 @@ import re
import subprocess
import tempfile
import logUtils
import miscUtils
from logUtils import errPrintln, regPrintln
from miscUtils import ErrorDM
# Globals
# 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 <installPath>/jre directory will be utilized.
#
# There should be corresponding tar.gz files with patterns that match one of the below:
# <installpath>/jre/jre-<VERSION>-<PLATFORM>-x64.tar.gz
# <installpath>/jre/jre-<VERSION>_<PLATFORM>-x64_bin.tar.gz
# where PLATFORM is one of the following: apple, linux, windows
# where VERSION is something like: 8u73 or 9.0.4
#
# Following are valid examples of named JRE tar.gz files:
# The Linux JRE 1.8.0_73 tar.gz release would be:
# <installpath>/jre/jre-8u73-linux-x64.tar.gz
# The Apple JRE 9.0.4 tar.gz release would be:
# <installpath>/jre/jre-9.0.4_osx-x64_bin.tar.gz
defaultVersionStr = '1.8'
# JreNode class definition
class JreNode:
def __init__(self, aArchitecture, aPlatform, aVersion, aFilePath):
self.architecture = aArchitecture
self.platform = aPlatform
self.version = aVersion
self.filePath = aFilePath
def getArchitecture(self):
"""Returns the JRE's architecture."""
return self.architecture
def getPlatform(self):
"""Returns the JRE's platform."""
return self.platform
def getVersion(self):
"""Returns the JRE's version."""
return self.version
def getFile(self):
"""Returns the (tar.gz or zip) file which contains the packaged JRE."""
return self.filePath
def getBasePathForJreTarGzFile(aJreTarGzFile):
"""Returns the JRE (base) path that should be used to access the JRE found in the specified JRE tar.gz.
def getBasePathFor(aJreNode):
"""Returns the JRE (base) path that should be used to access the JRE found in the specified JreNode.
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.
Please note that legacy JREs will expand to a different path than non-legacy JREs.
"""
verArr = getJreTarGzVerArr(aJreTarGzFile);
if verArr == None:
raise ErrorDM('File name (' + aJreTarGzFile + ') does not conform to proper JRE tar.gz name specification.')
verArr = aJreNode.getVersion()
verStr = verArrToVerStr(verArr)
if verStr.startswith("1.") == True:
basePath = 'jre' + verArrToVerStr(verArr)
basePath = 'jre' + verStr
else:
basePath = 'jre-' + verArrToVerStr(verArr)
basePath = 'jre-' + verStr
return basePath;
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 getJreNodesForVerStr(aJreNodeL, aVerStr):
"""Returns the JREs matching the specified specific JRE version specification. This will return an empty
list if there is no JRE that is sufficient for the request.
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.
aVerStr --- The version of interest. The version must be fully qualified.
aJreNodeL --- The list of valid JREs
aVerStr --- The version of interest. The version must be fully qualified.
Fully qualified is defined as having the following:
Pre Java 9 ---> Exactly 4 fields:
@@ -81,9 +81,6 @@ def getJreTarGzFilesForVerStr(aVerStr):
'1.8.0_73'
'9.0.4'
"""
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
# Retrieve the target version - and ensure it is fully qualified
targVer = verStrToVerArr(aVerStr)
if len(targVer) < 3:
@@ -91,42 +88,31 @@ def getJreTarGzFilesForVerStr(aVerStr):
if targVer[0] == 1 and len(targVer) != 4:
raise ErrorDM('Legacy JRE releases require exactly 4 elements for the release. Legacy releases refer to any release before Java 9. Version: ' + aVerStr)
# Search all the appropriate tar.gz JREs for exact matches in our JRE folder
retList = []
searchName = "jre-*.tar.gz";
searchPath = os.path.join(os.path.abspath(appInstallRoot), 'jre', searchName)
# 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
# Search the appropriate JREs for exact matches (of targVer)
retL = []
for aJreNode in aJreNodeL:
if aJreNode.getVersion() == targVer:
retL.append(aJreNode)
retList.append(aFile);
return sorted(retList)
return retL;
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 returned.
aPlatform --- The platform of the JRE tar.gz file of interest. Platform will typically be one of: 'apple',
'linux', 'windows'
def getJreNode(aJreNodeL, aArchStr, aPlatStr, aJvmVerSpec):
"""Returns the JRE for the appropriate platform and JRE release. If there are several possible matches then
the JRE with the latest version will be returned.
aJreNodeL --- The list of available JREs.
aArchStr --- The architecture of the JRE of interest. Architecture will typically be one of: 'x64'
aPlatStr --- The platform of the JRE of interest. Platform will typically be one of: 'linux', 'macosx', 'windows'
aJvmVerSpec --- A list of 1 or 2 items that define the range of JRE versions you are interested in. If the
list has just one item then that version will be used as the minimum version.
Method will return None if there is no file that is sufficient for the request. Note if you do not care about
Method will return None if there is no JRE 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'"""
# Transform a single string to a list of size 1
if isinstance(aJvmVerSpec, basestring):
aJvmVerSpec = [aJvmVerSpec]
# Retrieve the application installation location
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
# Retrieve the min and max JVM versions from aJvmVerSpec
minJvmVer = None
if aJvmVerSpec != None and len(aJvmVerSpec) >= 1:
@@ -138,119 +124,124 @@ def getJreTarGzFile(aPlatform, aJvmVerSpec):
errorMsg = 'At most only 2 elements are allowed. Number of elements specified: {}'.format(aJvmVerSpec)
raise ValueError(errorMsg)
# Search all the appropriate tar.gz JREs for the best match from our JRE folder
aPlatform = aPlatform.lower()
matchList = []
searchName = "jre-*.tar.gz";
searchPath = os.path.join(os.path.abspath(appInstallRoot), 'jre', searchName)
for aFile in glob.glob(searchPath):
# Retrieve the platform and skip to next if it is not a match
platStr = getPlatformForJreTarGzFile(aFile)
if platStr != aPlatform:
# Evaluate all available JREs for potential matches
matchL = []
for aJreNode in aJreNodeL:
# Ensure the architecture is a match
if aArchStr != aJreNode.getArchitecture():
continue
# Ensure the platform is a match
if aPlatStr != aJreNode.getPlatform():
continue
# Ensure that the file's JVM version is in range of minJvmVer and maxJvmVer
tmpVer = getJreTarGzVerArr(aFile)
if minJvmVer != None and isVerAfterAB(minJvmVer, tmpVer) == True:
# Ensure that the JRE's version is in range of minJvmVer and maxJvmVer
evalVer = aJreNode.getVersion()
if minJvmVer != None and isVerAfterAB(minJvmVer, evalVer) == True:
continue
if maxJvmVer != None and isVerAfterAB(tmpVer, maxJvmVer) == True:
if maxJvmVer != None and isVerAfterAB(evalVer, maxJvmVer) == True:
continue
matchList.append(aFile);
matchL.append(aJreNode);
# Bail if no matches were found
if len(matchL) == 0:
return None
# Determine the best match of all the matches
bestMatch = None
for aFile in matchList:
if bestMatch == None:
bestMatch = aFile
continue
bestJreNode = matchL[0]
bestVer = getJreTarGzVerArr(bestMatch)
testVer = getJreTarGzVerArr(aFile)
for aJreNode in matchL:
bestVer = bestJreNode.getVersion()
testVer = aJreNode.getVersion()
for b, t in zip(bestVer, testVer):
if b == t:
continue
try:
if t > b:
bestMatch = aFile
bestJreNode = aJreNode
break;
except:
continue
return bestMatch
return bestJreNode
def getJreTarGzVerArr(aFile):
"""Returns the version corresponding to the passed in JRE tar.gz file.
The returned value will be a list consisting of intergers defining the version associated with the
tar.gz file.
See the following references:
https://docs.oracle.com/javase/9/migrate/#GUID-3A71ECEF-5FC5-46FE-9BA9-88CBFCE828CB
http://openjdk.java.net/jeps/223
The file naming convention is expected to follow the standard:
jre-<Version>-<Platform>.tar.gz
where <Version> can be:
Pre Java 9:
<B>u<D>
where:
B ---> The major version (stored in minor index)
D ---> The update version (stored as the security)
- Note the major version will be assumed to be: 1
Java 9 or later:
<A>.<B>.<C>.<D>
A ---> The major version
B ---> The minor version
C ---> The security release
def loadJreCatalog(aFile):
"""
# Retrieve the base file name of the path
fileName = os.path.basename(aFile)
# Tokenize the filename by spliting along chars: '_', '-'
compL = re.split('[_-]', fileName)
if len(compL) < 3:
return None
# raise Error('Failed to tokenize the file name: ' + fileName)
# The version string should be the second item
verStr = compL[1]
# Retrieve the version component of the fileName
# Based on the old naming convention - prior to Java 9
if verStr.find('u') != -1:
# Determine the version based on the pattern '<A>u<B>' where:
# if there is no <B> component then just assume 0 for minor version
tokenL = verStr.split('u')
retVerL = [1, int(tokenL[0]), 0]
if len(tokenL) == 1:
retVerL.append(0)
else:
retVerL.append(int(tokenL[1]))
return retVerL
# Retrieve the version component of the fileName
# Based on the new naming convention - Java 9 and later
else:
retVerL = [int(aVal) for aVal in verStr.split('.')]
return retVerL
def getPlatformForJreTarGzFile(aFile):
"""Returns a string representing the platform of the specified JRE file. The platform is computed by evaluating the name of the
JRE tar.gz file. The returned values will be in lowercase. Currently the known returned values are one of the following: (apple,
linux, windows). These returned values should correspond to x86-64 JRE releases. On failure None will be returned.
Utility method that loads the specified (DistMaker) JRE catalog file and returns a list of
JREs. The list of JREs will be grouped by JVM version and each individual JRE will have the
following information:
- architecture
- platform
- file path
"""
# Tokenize the filename by spliting along chars: '_', '-'
fileName = os.path.basename(aFile)
compL = re.split('[_-]', fileName)
if len(compL) != 4 and len(compL) != 5:
return None
retL = []
# The platform component is stored in the 3rd token. Any string matching osx or macosx will be transformed to apple
platStr = compL[2].lower()
if platStr == 'osx' or platStr == 'macosx':
platStr = 'apple'
validArchL = ['x64']
validPlatL = ['linux', 'macosx', 'windows']
workJreVer = None;
return platStr;
pathS = set()
# Read the file
regPrintln('Loading JRE catalog: {}'.format(aFile))
with open(aFile, 'r') as workF:
for (lineNum, aLine) in enumerate(workF, 1):
# Skip empty lines / comments
line = aLine.strip()
if len(line) == 0 or line[0] == '#':
continue
# Parse the JRE version
tokenL = line.split(',')
if tokenL[0] == 'jre' and len(tokenL) == 2:
workJreVer = verStrToVerArr(tokenL[1])
continue
errMsgL = []
# Parse the JRE Node
if tokenL[0] == 'F' and len(tokenL) == 4:
archStr = tokenL[1]
platStr = tokenL[2]
pathStr = tokenL[3]
# Ensure the JRE version section has been declared
if workJreVer == None:
errMsg = 'JRE version section must be declared first. Skipping input: {}'.format(aLine[:-1])
errPrintln('\tERROR: [L: {}] {}'.format(lineNum, errMsg))
continue
# Ensure the path is a file
if os.path.isfile(pathStr) == False:
errMsgL += ['JRE file does not exist! Path: {}'.format(pathStr)];
# Ensure the architecture is recognized
if (archStr in validArchL) == False:
errMsgL += ['Architecture is not recognized. Valid: {} -> Input: {}'.format(validArchL, archStr)];
# Ensure the platform is recognized
if (platStr in validPlatL) == False:
errMsgL += ['Platform is not recognized. Valid: {} -> Input: {}'.format(validPlatL, platStr)];
# Ensure the reference JRE file has not been seen before
if pathStr in pathS:
errMsgL += ['JRE file has already been specified earlier! Path: {}'.format(pathStr)];
# If no errors then form the JreNode
if len(errMsgL) == 0:
retL += [JreNode(archStr, platStr, workJreVer, pathStr)]
pathS.add(pathStr)
continue
# Unrecognized line
else:
errMsgL += ['Input line is not recognized. Input: {}'.format(aLine[:-1])]
# Log the errors
for aErrMsg in errMsgL:
errPrintln('\tERROR: [L: {}] {}'.format(lineNum, aErrMsg))
regPrintln('\tLoaded JRE declarations: {}\n'.format(len(retL)))
return retL;
def normalizeJvmVerStr(aVerStr):
@@ -267,8 +258,8 @@ def normalizeJvmVerStr(aVerStr):
return retVerStr
def unpackAndRenameToStandard(aJreTarGzFile, aDestPath):
"""Method that will unpack the specified JRE tar.gz into the folder aDestPath. The unpacked JRE folder will also
def unpackAndRenameToStandard(aJreNode, aDestPath):
"""Method that will unpack the specified JRE (tar.gz or zip) into the folder aDestPath. The unpacked JRE folder will also
be renamed to a standard convention: jre<A>.<B> where
A: represents the major (classical) version
B: represents the minor version
@@ -279,20 +270,32 @@ def unpackAndRenameToStandard(aJreTarGzFile, aDestPath):
# Unpack to a temporary folder at aDestPath
tmpPath = tempfile.mkdtemp(dir=aDestPath)
subprocess.check_call(["tar", "-xf", aJreTarGzFile, "-C", tmpPath], stderr=subprocess.STDOUT)
jreFile = aJreNode.getFile()
if len(jreFile) > 4 and jreFile.upper()[-3:] == 'ZIP':
subprocess.check_call(["unzip", "-d", tmpPath, "-q", jreFile], stderr=subprocess.STDOUT)
else:
subprocess.check_call(["tar", "-C", tmpPath, "-xf", jreFile], stderr=subprocess.STDOUT)
# Rename the single extracted folder to a standard convention and move to aDestPath
fileList = glob.glob(os.path.join(tmpPath, '*'))
if len(fileList) != 1 or os.path.isdir(fileList[0]) == False:
print('Fatal error while unpacking JRE tar.gz file. Did not resolve to single folder! Terminating build process!')
print('\tJRE tar.gz: ' + aJreTarGzFile)
print('\tUnpacked files: ' + str(fileList))
fileL = glob.glob(os.path.join(tmpPath, '*'))
if len(fileL) != 1 or os.path.isdir(fileL[0]) == False:
errPrintln('Fatal error while unpacking JRE package file. Did not resolve to single folder! Terminating build process!')
errPrintln('\tJRE package file: ' + aJreNode.getFile())
errPrintln('\tUnpacked files: ' + str(fileL))
exit(-1)
jreBasePath = getBasePathForJreTarGzFile(aJreTarGzFile)
jreBasePath = getBasePathFor(aJreNode)
targPath = os.path.join(aDestPath, jreBasePath)
os.rename(fileList[0], targPath)
os.rename(fileL[0], targPath)
# Remove the the temporary path
# Warn if hidden files are located (and remove them)
# It appears that some JREs (Macosx specific? may include spurious hidden files.)
for aName in os.listdir(tmpPath):
spurFile = os.path.join(tmpPath, aName)
errPrintln('\tFound spurious (hidden) file: ' + spurFile)
errPrintln('\t\tAuto removing file: ' + spurFile)
os.remove(spurFile)
# Remove the temporary path
os.rmdir(tmpPath)
@@ -309,7 +312,7 @@ def validateJreVersionSpec(aVerSpec):
# 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)))
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])
@@ -318,7 +321,7 @@ def validateJreVersionSpec(aVerSpec):
# Ensure the minVer is not before Java 1.7
if isVerAfterAB([1, 7], minVer) == True:
raise ErrorDM('In the parameter jreVersion, the minVer ({0}) must be later than 1.7.'.format(aVerSpec[0]))
raise ErrorDM('In the parameter jreVersion, the minVer ({}) must be later than 1.7.'.format(aVerSpec[0]))
# Bail if only the minVer was specified
if len(aVerSpec) == 1:
@@ -331,16 +334,15 @@ def validateJreVersionSpec(aVerSpec):
# 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]))
raise ErrorDM('In the parameter jreVersion, the minVer ({}) is later than the maxVer ({}).'.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: <langVer>.<majVer>.<minVer>_<upVer>
"""Utility method to convert a jvm version string to the equivalent integral version (list) component. Each component in the array will be integral.
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]"""
'1.7.0 ---> [1, 7, 0]
'1.8' ---> [1, 8]
'1.8.0_73' ---> [1, 8, 0, 73]"""
verStrL = re.compile("[._]").split(aVerStr)
# Transform from string list to integer list
try:
@@ -351,14 +353,11 @@ def verStrToVerArr(aVerStr):
def verArrToVerStr(aVerL):
"""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 that as of Java 9 the version scheme has changed. Below defines the version scheme
Prior to Java 9: <language>.<major>.<minor>_<update> to <majVer>.<minVer>.<s>
Java 9 and later: <major>.<minor>.<security>.<patch>
[1, 8] ---> '1.8'
[1, 8, 0, 73] ---> '1.8.0_73'
[9, 0, 4] ---> '9.0.4'"""
"""Utility method to convert an integral version (list) to the equivalent jvm version string. Each component in the list must be integral.
Thus the following will get transformed to:
[1, 8] ---> '1.8'
[1, 8, 0, 73] ---> '1.8.0_73'
[9, 0, 4] ---> '9.0.4'"""
# Transform from integer list to string
if len(aVerL) <= 3:
retVerStr = ".".join(str(x) for x in aVerL)

View File

@@ -12,18 +12,19 @@ import miscUtils
import deployJreDist
def buildRelease(args, buildPath):
def buildRelease(aArgs, aBuildPath, aJreNodeL):
# We mutate args - thus make a custom copy
args = copy.copy(args)
args = copy.copy(aArgs)
# Retrieve vars of interest
appName = args.name
version = args.version
jreVerSpec = args.jreVerSpec
platformStr = 'linux'
archStr = 'x64'
platStr = 'linux'
# Determine the types of builds we should do
platformType = miscUtils.getPlatformTypes(args.platform, platformStr)
platformType = miscUtils.getPlatformTypes(args.platform, platStr)
if platformType.nonJre == False and platformType.withJre == False:
return;
@@ -32,37 +33,37 @@ def buildRelease(args, buildPath):
return
# Form the list of distributions to build (dynamic and static JREs)
distList = []
distL = []
if platformType.nonJre == True:
distList = [(appName + '-' + version, None)]
distL = [(appName + '-' + version, None)]
if platformType.withJre == True:
# Select the jreTarGzFile to utilize for static releases
jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec)
if jreTarGzFile == None:
# Select the JreNode to utilize for static releases
tmpJreNode = jreUtils.getJreNode(aJreNodeL, archStr, platStr, jreVerSpec)
if tmpJreNode == None:
# Let the user know that a compatible JRE was not found - thus no static release will be made.
print('[Warning] No compatible JRE ({0}) is available for the {1} platform. A static release will not be provided for the platform.'.format(jreVerSpec, platformStr.capitalize()))
print('[Warning] No compatible JRE ({}) is available for the ({}) {} platform. A static release will not be provided for the platform.'.format(jreVerSpec, archStr, platStr.capitalize()))
else:
distList.append((appName + '-' + version + '-jre', jreTarGzFile))
distL.append((appName + '-' + version + '-jre', tmpJreNode))
# Create a tmp (working) folder
tmpPath = tempfile.mkdtemp(prefix=platformStr, dir=buildPath)
tmpPath = tempfile.mkdtemp(prefix=platStr, dir=aBuildPath)
# Create the various distributions
for (aDistName, aJreTarGzFile) in distList:
print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName))
for (aDistName, aJreNode) in distL:
print('Building {} distribution: {}'.format(platStr.capitalize(), aDistName))
# Let the user know of the JRE release we are going to build with
if aJreTarGzFile != None:
print('\tUtilizing JRE: ' + aJreTarGzFile)
if aJreNode != None:
print('\tUtilizing JRE: ' + aJreNode.getFile())
# Create the (top level) distribution folder
dstPath = os.path.join(tmpPath, aDistName)
os.mkdir(dstPath)
# Build the contents of the distribution folder
buildDistTree(buildPath, dstPath, args, aJreTarGzFile)
buildDistTree(aBuildPath, dstPath, args, aJreNode)
# Create the tar.gz archive
tarFile = os.path.join(buildPath, aDistName + '.tar.gz')
tarFile = os.path.join(aBuildPath, aDistName + '.tar.gz')
print('\tForming tar.gz file: ' + tarFile)
childPath = aDistName
subprocess.check_call(["tar", "-czf", tarFile, "-C", tmpPath, childPath], stderr=subprocess.STDOUT)
@@ -72,19 +73,19 @@ def buildRelease(args, buildPath):
shutil.rmtree(tmpPath)
def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
def buildDistTree(aBuildPath, aRootPath, aArgs, aJreNode):
# Retrieve vars of interest
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
appName = args.name
appName = aArgs.name
# Form the app contents folder
srcPath = os.path.join(buildPath, "delta")
dstPath = os.path.join(rootPath, "app")
srcPath = os.path.join(aBuildPath, "delta")
dstPath = os.path.join(aRootPath, "app")
shutil.copytree(srcPath, dstPath, symlinks=True)
# Copy libs to the app directory so they can be found at launch
soDir = os.path.join(rootPath, 'app', 'code', 'linux')
soDir = os.path.join(aRootPath, 'app', 'code', 'linux')
for libPath in glob.iglob(os.path.join(soDir, "*.so")):
libFileName = os.path.basename(libPath)
srcPath = os.path.join(soDir, libFileName)
@@ -92,34 +93,34 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
shutil.copy(srcPath, linkPath)
# Setup the launcher contents
dstPath = os.path.join(rootPath, "launcher/" + deployJreDist.getAppLauncherFileName())
dstPath = os.path.join(aRootPath, "launcher/" + deployJreDist.getAppLauncherFileName())
srcPath = os.path.join(appInstallRoot, "template/appLauncher.jar")
os.makedirs(os.path.dirname(dstPath))
shutil.copy(srcPath, dstPath);
# Build the java component of the distribution
if args.javaCode != None:
if aArgs.javaCode != None:
# Form the executable bash script
dstPath = os.path.join(rootPath, 'run' + appName)
buildBashScript(dstPath, args, jreTarGzFile)
dstPath = os.path.join(aRootPath, 'run' + appName)
buildBashScript(dstPath, aArgs, aJreNode)
# Unpack the JRE and set up the JRE tree
if jreTarGzFile != None:
jreUtils.unpackAndRenameToStandard(jreTarGzFile, rootPath)
if aJreNode != None:
jreUtils.unpackAndRenameToStandard(aJreNode, aRootPath)
def buildBashScript(destFile, args, jreTarGzFile):
def buildBashScript(aDestFile, aArgs, aJreNode):
# Form the jvmArgStr but strip away the -Xmx* component if it is specified
# since the JVM maxMem is dynamically configurable (via DistMaker)
maxMem = None
jvmArgsStr = ''
for aStr in args.jvmArgs:
for aStr in aArgs.jvmArgs:
if aStr.startswith('-Xmx'):
maxMem = aStr[4:]
else:
jvmArgsStr += aStr + ' '
f = open(destFile, 'wb')
f = open(aDestFile, 'wb')
# f.write('#!/bin/bash\n')
f.write('#!/usr/bin/env bash\n')
@@ -128,10 +129,10 @@ def buildBashScript(destFile, args, jreTarGzFile):
f.write('{ # Do not remove this bracket! \n\n')
f.write('# Define where the Java executable is located\n')
if jreTarGzFile == None:
if aJreNode == None:
f.write('javaExe=java\n\n')
else:
jrePath = jreUtils.getBasePathForJreTarGzFile(jreTarGzFile)
jrePath = jreUtils.getBasePathFor(aJreNode)
f.write('javaExe=../' + jrePath + '/bin/java\n\n')
f.write('# Define the maximum memory to allow the application to utilize\n')
@@ -141,9 +142,9 @@ def buildBashScript(destFile, args, jreTarGzFile):
f.write('maxMem=' + maxMem + '\n\n')
f.write('# Get the installation path\n')
f.write('# We support the Apple / Linux variants explicitly and then default back to Linux\n')
f.write('# We support the Linux / Macosx variants explicitly and then default back to Linux\n')
f.write('if [ "$(uname -s)" == "Darwin" ]; then\n')
f.write(' # Apple platform: We assume the coreutils package has been installed...\n')
f.write(' # Macosx platform: We assume the coreutils package has been installed...\n')
f.write(' installPath=$(greadlink -f "$BASH_SOURCE")\n')
f.write('elif [ "$(uname -s)" == "Linux" ]; then\n')
f.write(' # Linux platform\n')
@@ -175,7 +176,7 @@ def buildBashScript(destFile, args, jreTarGzFile):
f.close()
# Make the script executable
os.chmod(destFile, 00755)
os.chmod(aDestFile, 00755)
def checkSystemEnvironment():

View File

@@ -3,6 +3,8 @@ import os
import time
import sys
# Globals
indentStr = ' '
def appendLogOutputWithText(aStr, aText):
""" Method which given a string from a log will insert all occurances of newlines with the
@@ -14,18 +16,21 @@ def appendLogOutputWithText(aStr, aText):
return retStr;
def errPrintln(aMsg="", indent=0):
def errPrintln(aMsg=""):
"""Print the specified string with a trailing newline to stderr."""
while indent > 0:
indent -= 1
aMsg = ' ' + aMsg
aMsg = aMsg.replace('\t', indentStr)
sys.stderr.write(aMsg + '\n')
def regPrintln(aMsg="", indent=0):
def regPrintln(aMsg=""):
"""Print the specified string with a trailing newline to stdout."""
while indent > 0:
indent -= 1
aMsg = ' ' + aMsg
aMsg = aMsg.replace('\t', indentStr)
sys.stdout.write(aMsg + '\n')
def setIndentStr(aStr):
"""Sets in the string that will be used as the "indent" text. Any future
calls to the methods errPrintln() and regPrintln() will have any textual
tab characters automatically replaced with the specified string."""
global indentStr
indentStr = aStr

View File

@@ -66,10 +66,10 @@ def computeDigestForFile(evalFile, digestType, block_size=2**20):
return hash.hexdigest()
def getPlatformTypes(platformArr, platformStr):
def getPlatformTypes(aPlatformArr, aPlatformStr):
"""Returns an object that defines the release types that should be built for the given platform. The object will
have 2 field members: [nonJre, withJre]. If the field is set to True then the corresonding platform should be
built. This is determined by examaning the platformStr and determine it's occurance in the platformArr. For
built. This is determined by examaning aPlatformStr and determine it occurs in aPlatformArr. For
example to determine the build platforms for Linux one might call getPlatformArr(someArr, 'linux'). Following are
the results of contents in someArr:
[''] ---> nonJre = False, withJre = False
@@ -81,12 +81,12 @@ def getPlatformTypes(platformArr, platformStr):
withJre = False
retObj = PlatformType()
if platformStr in platformArr:
if aPlatformStr in aPlatformArr:
retObj.nonJre = True
retObj.withJre = True
if platformStr + '-' in platformArr:
if aPlatformStr + '-' in aPlatformArr:
retObj.nonJre = True
if platformStr + '+' in platformArr:
if aPlatformStr + '+' in aPlatformArr:
retObj.withJre = True
return retObj
@@ -98,17 +98,17 @@ def getInstallRoot():
return installRoot
def executeAndLog(command, indentStr=""):
def executeAndLog(aCommand, indentStr=""):
"""Executes the specified command via subprocess and logs all (stderr,stdout) output to the console"""
try:
proc = subprocess.Popen(command, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
proc = subprocess.Popen(aCommand, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
proc.wait()
outStr = proc.stdout.read()
if outStr != "":
outStr = logUtils.appendLogOutputWithText(outStr, indentStr)
print(outStr)
except Exception as aExp:
print(indentStr + 'Failed to execute command: ' + str(command))
print(indentStr + 'Failed to execute command: ' + str(aCommand))
outStr = logUtils.appendLogOutputWithText(str(aExp), indentStr)
print(outStr)
print(indentStr + 'Stack Trace:')
@@ -152,33 +152,45 @@ def handleSignal(signal, frame):
sys.exit(0)
def buildAppLauncherConfig(destFile, args):
def requirePythonVersion(aVer):
"""Checks the version of python running and if not correct will exit with
an error message."""
if aVer <= sys.version_info:
return
print('The installed version of python is too old. Please upgrade.')
print(' Current version: ' + '.'.join(str(i) for i in sys.version_info))
print(' Require version: ' + '.'.join(str(i) for i in aVer))
exit(-1)
def buildAppLauncherConfig(aDestFile, aArgs):
classPathStr = ''
for aStr in args.classPath:
for aStr in aArgs.classPath:
classPathStr += 'java/' + aStr + ':'
if len(classPathStr) > 0:
classPathStr = classPathStr[0:-1]
jvmArgsStr = ''
for aStr in args.jvmArgs:
for aStr in aArgs.jvmArgs:
if len(aStr) > 2 and aStr[0:1] == '\\':
aStr = aStr[1:]
jvmArgsStr += aStr + ' '
appArgsStr = ''
for aStr in args.appArgs:
for aStr in aArgs.appArgs:
appArgsStr += ' ' + aStr
f = open(destFile, 'wb')
f = open(aDestFile, 'wb')
# App name section
f.write('-name\n')
f.write(args.name + '\n')
f.write(aArgs.name + '\n')
f.write('\n')
# Version section
f.write('-version\n')
f.write(args.version + '\n')
f.write(aArgs.version + '\n')
f.write('\n')
# Build date section
@@ -190,18 +202,18 @@ def buildAppLauncherConfig(destFile, args):
# MainClass section
f.write('-mainClass\n')
f.write(args.mainClass + '\n')
f.write(aArgs.mainClass + '\n')
f.write('\n')
# ClassPath section
f.write('-classPath\n')
for aStr in args.classPath:
for aStr in aArgs.classPath:
f.write(aStr + '\n')
f.write('\n')
# Application args section
f.write('-appArgs\n')
for aStr in args.appArgs:
for aStr in aArgs.appArgs:
f.write(aStr + '\n')
f.write('\n')

View File

@@ -1,29 +1,31 @@
#! /usr/bin/env python
import copy
import glob
import os
import platform
import shutil
import subprocess
import sys
import tempfile
import glob
import jreUtils
import miscUtils
import deployJreDist
def buildRelease(args, buildPath):
def buildRelease(aArgs, aBuildPath, aJreNodeL):
# We mutate args - thus make a custom copy
args = copy.copy(args)
args = copy.copy(aArgs)
# Retrieve vars of interest
appName = args.name
version = args.version
jreVerSpec = args.jreVerSpec
platformStr = 'windows'
archStr = 'x64'
platStr = 'windows'
# Determine the types of builds we should do
platformType = miscUtils.getPlatformTypes(args.platform, platformStr)
platformType = miscUtils.getPlatformTypes(args.platform, platStr)
if platformType.nonJre == False and platformType.withJre == False:
return;
@@ -32,20 +34,20 @@ def buildRelease(args, buildPath):
return
# Form the list of distributions to build (dynamic and static JREs)
distList = []
distL = []
if platformType.nonJre == True:
distList = [(appName + '-' + version, None)]
distL = [(appName + '-' + version, None)]
if platformType.withJre == True:
# Select the jreTarGzFile to utilize for static releases
jreTarGzFile = jreUtils.getJreTarGzFile(platformStr, jreVerSpec)
if jreTarGzFile == None:
# Select the JreNode to utilize for static releases
tmpJreNode = jreUtils.getJreNode(aJreNodeL, archStr, platStr, jreVerSpec)
if tmpJreNode == None:
# Let the user know that a compatible JRE was not found - thus no static release will be made.
print('[Warning] No compatible JRE ({0}) is available for the {1} platform. A static release will not be provided for the platform.'.format(jreVerSpec, platformStr.capitalize()))
print('[Warning] No compatible JRE ({}) is available for the ({}) {} platform. A static release will not be provided for the platform.'.format(jreVerSpec, archStr, platStr.capitalize()))
else:
distList.append((appName + '-' + version + '-jre', jreTarGzFile))
distL.append((appName + '-' + version + '-jre', tmpJreNode))
# Create a tmp (working) folder
tmpPath = tempfile.mkdtemp(prefix=platformStr, dir=buildPath)
tmpPath = tempfile.mkdtemp(prefix=platStr, dir=aBuildPath)
# Unpack the proper launch4j release (for the platform we are
# running on) into the tmp (working) folder
@@ -55,7 +57,11 @@ def buildRelease(args, buildPath):
if platform.system() == 'Darwin':
exeCmd = ['tar', '-C', tmpPath, '-xf', l4jPath + '/launch4j-3.12-macosx-x86.tgz']
else:
exeCmd = ['tar', '-C', tmpPath, '-xf', l4jPath + '/launch4j-3.12-linux-x64.tgz']
is64Bit = sys.maxsize > 2**32
if is64Bit == True:
exeCmd = ['tar', '-C', tmpPath, '-xf', l4jPath + '/launch4j-3.12-linux-x64.tgz']
else:
exeCmd = ['tar', '-C', tmpPath, '-xf', l4jPath + '/launch4j-3.12-linux.tgz']
retCode = subprocess.call(exeCmd)
if retCode != 0:
print('Failed to extract launch4j package...')
@@ -63,21 +69,21 @@ def buildRelease(args, buildPath):
return
# Create the various distributions
for (aDistName, aJreTarGzFile) in distList:
print('Building {0} distribution: {1}'.format(platformStr.capitalize(), aDistName))
for (aDistName, aJreNode) in distL:
print('Building {} distribution: {}'.format(platStr.capitalize(), aDistName))
# Let the user know of the JRE release we are going to build with
if aJreTarGzFile != None:
print('\tUtilizing JRE: ' + aJreTarGzFile)
if aJreNode != None:
print('\tUtilizing JRE: ' + aJreNode.getFile())
# Create the (top level) distribution folder
dstPath = os.path.join(tmpPath, aDistName)
os.mkdir(dstPath)
# Build the contents of the distribution folder
buildDistTree(buildPath, dstPath, args, aJreTarGzFile)
buildDistTree(aBuildPath, dstPath, args, aJreNode)
# Create the zip file
zipFile = os.path.join(buildPath, aDistName + ".zip")
zipFile = os.path.join(aBuildPath, aDistName + ".zip")
cmd = ["jar", "-cMf", zipFile, "-C", dstPath, '.']
print('\tForming zip file: ' + zipFile)
proc = miscUtils.executeAndLog(cmd, "\t\tjar: ")
@@ -91,19 +97,19 @@ def buildRelease(args, buildPath):
shutil.rmtree(tmpPath)
def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
def buildDistTree(aBuildPath, aRootPath, aArgs, aJreNode):
# Retrieve vars of interest
appInstallRoot = miscUtils.getInstallRoot()
appInstallRoot = os.path.dirname(appInstallRoot)
appName = args.name
appName = aArgs.name
# Form the app contents folder
srcPath = os.path.join(buildPath, "delta")
dstPath = os.path.join(rootPath, "app")
srcPath = os.path.join(aBuildPath, "delta")
dstPath = os.path.join(aRootPath, "app")
shutil.copytree(srcPath, dstPath, symlinks=True)
# Copy dlls to the app directory so they can be found at launch
dllDir = os.path.join(rootPath, 'app', 'code', 'win')
dllDir = os.path.join(aRootPath, 'app', 'code', 'win')
for libPath in glob.iglob(os.path.join(dllDir, "*.lib")):
libFileName = os.path.basename(libPath)
srcPath = os.path.join(dllDir, libFileName)
@@ -116,18 +122,18 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
shutil.copy(srcPath, linkPath)
# Setup the launcher contents
dstPath = os.path.join(rootPath, "launcher/" + deployJreDist.getAppLauncherFileName())
dstPath = os.path.join(aRootPath, "launcher/" + deployJreDist.getAppLauncherFileName())
srcPath = os.path.join(appInstallRoot, "template/appLauncher.jar")
os.makedirs(os.path.dirname(dstPath))
shutil.copy(srcPath, dstPath);
# Build the java component of the distribution
if args.javaCode != None:
if aArgs.javaCode != None:
# Generate the iconFile
winIconFile = None
origIconFile = args.iconFile
origIconFile = aArgs.iconFile
if origIconFile != None:
winIconFile = os.path.join(rootPath, os.path.basename(origIconFile) + ".ico")
winIconFile = os.path.join(aRootPath, os.path.basename(origIconFile) + ".ico")
cmd = ['convert', origIconFile, winIconFile]
proc = miscUtils.executeAndLog(cmd, "\t\t(ImageMagick) convert: ")
if proc.returncode != 0:
@@ -139,11 +145,11 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
winIconFile = None
# Form the launch4j config file
configFile = os.path.join(rootPath, appName + ".xml")
buildLaunch4JConfig(configFile, args, jreTarGzFile, winIconFile)
configFile = os.path.join(aRootPath, appName + ".xml")
buildLaunch4JConfig(configFile, aArgs, aJreNode, winIconFile)
# Build the Windows executable
tmpPath = os.path.dirname(rootPath)
tmpPath = os.path.dirname(aRootPath)
launch4jExe = os.path.join(tmpPath, "launch4j", "launch4j")
cmd = [launch4jExe, configFile]
print('\tBuilding windows executable via launch4j')
@@ -152,25 +158,25 @@ def buildDistTree(buildPath, rootPath, args, jreTarGzFile):
print('\tError: Failed to build executable. Return code: ' + str(proc.returncode))
# Unpack the JRE and set up the JRE tree
if jreTarGzFile != None:
jreUtils.unpackAndRenameToStandard(jreTarGzFile, rootPath)
if aJreNode != None:
jreUtils.unpackAndRenameToStandard(aJreNode, aRootPath)
# Perform cleanup
os.remove(configFile)
if winIconFile != None:
os.remove(winIconFile)
def buildLaunch4JConfig(destFile, args, jreTarGzFile, iconFile):
f = open(destFile, 'wb')
def buildLaunch4JConfig(aDestFile, aArgs, aJreNode, aIconFile):
f = open(aDestFile, 'wb')
writeln(f, 0, "<launch4jConfig>")
if args.debug == True:
if aArgs.debug == True:
writeln(f, 1, "<headerType>console</headerType>");
else:
writeln(f, 1, "<headerType>gui</headerType>");
writeln(f, 1, "<outfile>" + args.name + ".exe</outfile>");
writeln(f, 1, "<outfile>" + aArgs.name + ".exe</outfile>");
writeln(f, 1, "<dontWrapJar>true</dontWrapJar>");
writeln(f, 1, "<errTitle>" + args.name + "</errTitle>");
writeln(f, 1, "<errTitle>" + aArgs.name + "</errTitle>");
writeln(f, 1, "<downloadUrl>http://java.com/download</downloadUrl>");
# writeln(f, 1, "<supportUrl>url</supportUrl>");
@@ -179,41 +185,41 @@ def buildLaunch4JConfig(destFile, args, jreTarGzFile, iconFile):
writeln(f, 1, "<priority>normal</priority>");
writeln(f, 1, "<customProcName>true</customProcName>");
writeln(f, 1, "<stayAlive>false</stayAlive>");
if iconFile != None:
writeln(f, 1, "<icon>" + iconFile + "</icon>");
if aIconFile != None:
writeln(f, 1, "<icon>" + aIconFile + "</icon>");
writeln(f, 1, "<classPath>");
writeln(f, 2, "<mainClass>appLauncher.AppLauncher</mainClass>");
writeln(f, 2, "<cp>../launcher/" + deployJreDist.getAppLauncherFileName() + "</cp>");
writeln(f, 1, "</classPath>");
if args.forceSingleInstance != False:
if aArgs.forceSingleInstance != False:
writeln(f, 0, "");
writeln(f, 1, "<singleInstance>");
writeln(f, 2, "<mutexName>" + args.name + ".mutex</mutexName>");
writeln(f, 2, "<windowTitle>" + args.name + "</windowTitle>");
writeln(f, 2, "<mutexName>" + aArgs.name + ".mutex</mutexName>");
writeln(f, 2, "<windowTitle>" + aArgs.name + "</windowTitle>");
writeln(f, 1, "</singleInstance>");
writeln(f, 0, "");
writeln(f, 1, "<jre>");
if jreTarGzFile != None:
jrePath = jreUtils.getBasePathForJreTarGzFile(jreTarGzFile)
if aJreNode != None:
jrePath = jreUtils.getBasePathFor(aJreNode)
writeln(f, 2, "<path>" + jrePath + "</path>");
else:
jreVer = getJreMajorVersion(args.jreVerSpec)
jreVer = getJreMajorVersion(aArgs.jreVerSpec)
writeln(f, 2, "<minVersion>" + jreVer + "</minVersion>"); # Valid values: '1.7.0' or '1.8.0' ...
writeln(f, 2, "<jdkPreference>preferJre</jdkPreference>"); # Valid values: jreOnlyjdkOnly|preferJre|preferJdk
for aJvmArg in args.jvmArgs:
for aJvmArg in aArgs.jvmArgs:
writeln(f, 2, "<opt>" + aJvmArg + "</opt>");
writeln(f, 2, "<opt>-Djava.system.class.loader=appLauncher.RootClassLoader</opt>");
writeln(f, 1, "</jre>");
writeln(f, 0, "");
writeln(f, 1, "<messages>");
writeln(f, 2, "<startupErr>" + args.name + " error...</startupErr>");
writeln(f, 2, "<startupErr>" + aArgs.name + " error...</startupErr>");
writeln(f, 2, "<bundledJreErr>Failed to locate the bundled JRE</bundledJreErr>");
writeln(f, 2, "<jreVersionErr>Located JRE is not the proper version.</jreVersionErr>");
writeln(f, 2, "<launcherErr>Failed to launch " + args.name + "</launcherErr>");
writeln(f, 2, "<launcherErr>Failed to launch " + aArgs.name + "</launcherErr>");
writeln(f, 1, "</messages>");
writeln(f, 0, "")
@@ -229,14 +235,8 @@ def checkSystemEnvironment():
def getJreMajorVersion(aJreVerSpec):
"""Returns the minimum version of the JRE to utilize based on the passed in JreVerSpec. If aJreVerSpec is None then
the value specified in jreUtils.getDefaultJreVerStr() will be utilized. If that value is None then the value of
1.8.0 will be utilized."""
if aJreVerSpec == None:
aJreVerSpec = [jreUtils.getDefaultJreVerStr()]
"""Returns the minimum version of the JRE to utilize based on the passed in aJreVerSpec."""
minJreVerStr = aJreVerSpec[0]
if minJreVerStr == None:
minJreVerStr = '1.8.0'
try:
verArr = jreUtils.verStrToVerArr(minJreVerStr)

View File

@@ -8,37 +8,37 @@ import signal
import subprocess
import sys
def buildRelease(version, doNotClean=False):
def buildRelease(aVersion, aDoNotClean=False):
"""Method that builds a release of DistMaker. Upon sucessful execution, a
tar.gz archive will be generated named: 'DistMaker-<version>.tar.gz'. Note
that releases of DistMaker version 0.50 or later (2018May01+) will no longer
tar.gz archive will be generated named: 'DistMaker-<aVersion>.tar.gz'. Note
that releases of DistMaker aVersion 0.50 or later (2018May01+) will no longer
include static JREs."""
# Retrieve the install path
installPath = getInstallRoot()
installPath = os.path.dirname(installPath)
# Determine the workPath
workPath = os.path.join(installPath, 'release', 'DistMaker-' + version)
destFileGZ = os.path.join(installPath, 'release', 'DistMaker-' + version + '.tar.gz')
workPath = os.path.join(installPath, 'release', 'DistMaker-' + aVersion)
destFileGZ = os.path.join(installPath, 'release', 'DistMaker-' + aVersion + '.tar.gz')
# Bail if the work folder for which we compose the release already exists
if os.path.exists(workPath) == True:
errPrintln('Aborting DistMaker release build. Release folder already exists: ' + workPath, indent=1)
errPrintln('\tAborting DistMaker release build. Release folder already exists: ' + workPath)
exit(-1)
# Bail if the release already exists
if os.path.exists(destFileGZ) == True:
errPrintln('Aborting DistMaker release build. Release already exists. File: ' + destFileGZ, indent=1)
errPrintln('\tAborting DistMaker release build. Release already exists. File: ' + destFileGZ)
exit(-1)
# Laydown the structure, and let the user know of the version we are building
print('Building DistMaker release ' + version + '...')
print('Building DistMaker release ' + aVersion + '...')
os.mkdir(workPath)
# Copy the libraries
dstPath = os.path.join(workPath, 'lib')
os.mkdir(dstPath)
for aLib in ['glum.jar', 'guava-18.0.jar', 'distMaker.jar']:
for aLib in ['glum-1.3.jar', 'guava-18.0.jar', 'distMaker.jar']:
srcPath = os.path.join(installPath, 'lib', aLib)
shutil.copy2(srcPath, dstPath)
@@ -54,7 +54,7 @@ def buildRelease(version, doNotClean=False):
os.makedirs(dstPath + '/apple')
os.makedirs(dstPath + '/background')
os.makedirs(dstPath + '/launch4j')
for aFile in ['appLauncher.jar', '.DS_Store.template', 'apple/JavaAppLauncher', 'background/background.png', 'launch4j/launch4j-3.12-linux-x64.tgz', 'launch4j/launch4j-3.12-macosx-x86.tgz']:
for aFile in ['appLauncher.jar', 'JreCatalog.txt', 'apple/.DS_Store.template', 'apple/JavaAppLauncher', 'background/background.png', 'launch4j/launch4j-3.12-linux-x64.tgz', 'launch4j/launch4j-3.12-linux.tgz', 'launch4j/launch4j-3.12-macosx-x86.tgz']:
srcPath = os.path.join(installPath, 'template', aFile)
shutil.copy2(srcPath, dstPath + '/' + aFile)
@@ -66,18 +66,17 @@ def buildRelease(version, doNotClean=False):
exit(-1)
# Remove the workPath
if doNotClean == False:
if aDoNotClean == False:
shutil.rmtree(workPath)
print('DistMaker release ' + version + ' built.')
print('DistMaker release ' + aVersion + ' built.')
def errPrintln(message="", indent=0):
"""Print the specified string with a trailing newline to stderr."""
while indent > 0:
indent -= 1
message = ' ' + message
sys.stderr.write(message + '\n')
def errPrintln(aMessage=''):
"""Print the specified string with a trailing newline to stderr. Each tab
character will be replaced with: 3 spaces"""
aMessage = aMessage.replace('\t', ' ')
sys.stderr.write(aMessage + '\n')
def getDistMakerVersion():
@@ -92,8 +91,8 @@ def getDistMakerVersion():
# Check for distMaker.jar library prerequisite
testPath = os.path.join(installPath, 'lib', 'distMaker.jar')
if os.path.exists(testPath) == False:
errPrintln('Aborting DistMaker release build. The file ' + testPath + ' does not exist.', indent=1)
errPrintln('Please run the buildDistMakerBin.jardesc from your workspace.', indent=1)
errPrintln('\tAborting DistMaker release build. The file ' + testPath + ' does not exist.')
errPrintln('\tPlease run the buildDistMakerBin.jardesc from your workspace.')
exit(-1)
try:
@@ -102,7 +101,7 @@ def getDistMakerVersion():
version = output.split()[1]
return version
except:
errPrintln('Please run the buildDistMakerBin.jardesc from your workspace.', indent=1)
errPrintln('\tPlease run the buildDistMakerBin.jardesc from your workspace.')
exit(-1)
@@ -124,7 +123,7 @@ if __name__ == "__main__":
# Logic to capture Ctrl-C and bail
signal.signal(signal.SIGINT, handleSignal)
# Ensure we are running version 2.7 or greater
# Require python version 2.7 or later
targVer = (2, 7)
if sys.version_info < targVer:
print('The installed version of python is too old. Please upgrade.')