![]() ![]() |
![]() |
File: [Development] / JSOC / localize.py
(download)
/
(as text)
Revision: 1.5, Mon Nov 11 17:34:54 2013 UTC (9 years, 4 months ago) by arta Branch: MAIN Changes since 1.4: +31 -9 lines Changes to sort the sections in the output makefile. |
#!/home/jsoc/bin/linux_x86_64/activepython import os.path import sys import getopt import re from subprocess import check_output, CalledProcessError # Constants VERS_FILE = 'jsoc_version.h' SDP_CFG = 'configsdp.txt' NET_CFG = 'config.local' NET_CFGMAP = 'config.local.map' RET_SUCCESS = 0 RET_NOTDRMS = 1 PREFIX = """# This file was auto-generated by localize.py. Please do not edit it directly (running # configure will run localize.py, which will then overwrite any edits manually performed). """ C_PREFIX = """/* This file was auto-generated by localize.py. Please do not edit it directly (running * configure will run localize.py, which will then overwrite any edits manually performed). */ """ PERL_BINPATH = '#!/usr/bin/perl\n' PERL_INTIAL = """package drmsparams; use warnings; use strict; """ PERL_FXNS_A = """sub new { my($clname) = shift; my($self) = { _paramsH => undef }; bless($self, $clname); $self->{_paramsH} = $self->initialize(); return $self; } sub DESTROY { my($self) = shift; } """ PERL_FXNS_B = """sub get { my($self) = shift; my($name) = shift; my($rv); if (exists($self->{_paramsH}->{$name})) { return $self->{_paramsH}->{$name}; } else { return undef; } } 1;""" ICC_MAJOR = 9 ICC_MINOR = 0 GCC_MAJOR = 3 GCC_MINOR = 0 IFORT_MAJOR = 9 IFORT_MINOR = 0 GFORT_MAJOR = 4 GFORT_MINOR = 2 # Read arguments # d - localization directory # b - base name of all parameter files (e.g., -b drmsparams --> drmsparams.h, drmsparams.mk, drmsparams.pm, etc.) # m - make file def GetArgs(args): rv = bool(0) optD = {} try: opts, remainder = getopt.getopt(args, "hd:b:",["dir=", "base="]) except getopt.GetoptError: print('Usage:') print('localize.py [-h] -d <localization directory> -b <parameter file base>') rv = bool(1) if rv == bool(0): for opt, arg in opts: if opt == '-h': print('localize.py [-h] -d <localization directory> -b <parameter file base>') elif opt in ("-d", "--dir"): regexp = re.compile(r"(\S+)/?") matchobj = regexp.match(arg) if matchobj is None: rv = bool(1) else: optD['dir'] = matchobj.group(1) elif opt in ("-b", "--base"): optD['base'] = arg else: optD[opt] = arg return optD def createMacroStr(key, val, keyColLen, status): if keyColLen < len(key): status = bool(1) return None else: nsp = keyColLen - len(key) spaces = str() for isp in range(nsp): spaces += ' ' status = bool(0) return '#define ' + key + spaces + val + '\n' def createPerlConst(key, val, keyColLen, status): if keyColLen < len(key): status = bool(1) return None else: nsp = keyColLen - len(key) spaces = str() for isp in range(nsp): spaces += ' ' status = bool(0) return 'use constant ' + key + ' => ' + spaces + val + ';\n' def isSupportedPlat(plat): regexp = re.compile(r"\s*(^x86_64|^ia32|^ia64|^avx)", re.IGNORECASE) matchobj = regexp.match(plat); if not matchobj is None: return bool(1); else: return bool(0); def processMakeParam(mDefs, key, val, platDict, matchDict): varMach = None regexp = re.compile(r"(\S+):(\S+)") matchobj = regexp.match(key) if not matchobj is None: varName = matchobj.group(1) varMach = matchobj.group(2) else: varName = key varValu = val if varMach is None: mDefs.extend(list('\n' + varName + ' = ' + varValu)) else: if isSupportedPlat(varMach): # The guard will compare varValu to $JSOC_MACHINE. if not varMach in platDict: platDict[varMach] = {} platDict[varMach][varName] = varValu else: # The guard will compare varValu to $MACHINETYPE (this is just the hostname of the machine on which localize.py is running). if not varMach in machDict: machDict[varMach] = {} machDict[varMach][varName] = varValu def processParam(cfgfile, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, section): status = 0 matchobj = regexpComm.match(line) if not matchobj is None: # Skip comment line return bool(0) matchobj = regexpDefs.match(line) if not matchobj is None: del section[:] section.extend(list('defs')) return bool(0) matchobj = regexpMake.match(line) if not matchobj is None: del section[:] section.extend(list('make')) return bool(0) if ''.join(section) == 'defs' or not cfgfile: matchobj = regexp.match(line) if not matchobj is None: # We have a key-value line keyCfgSp = matchobj.group(1) val = matchobj.group(2) # Must map the indirect name to the actual name if keymap: # Map to actual name only if the keymap is not empty (which signifies NA). if keyCfgSp in keymap: key = keymap[keyCfgSp] elif keyCfgSp == 'LOCAL_CONFIG_SET' or keyCfgSp == 'DRMS_SAMPLE_NAMESPACE': # Ignore parameters that are not useful and shouldn't have been there in the first place return bool(0) elif not cfgfile: # Should not be doing mapping for addenda key = keyCfgSp else: raise Exception('badKeyMapKey', keyCfgSp) else: key = keyCfgSp matchobj = regexpQuote.match(key) if not matchobj is None: quote = matchobj.group(1) key = matchobj.group(2) # master defs dictionary defs[key] = val # C header file if quote == "q": # Add double-quotes cDefs.extend(list(createMacroStr(key, '"' + val + '"', 40, status))) elif quote == "p": # Add parentheses cDefs.extend(list(createMacroStr(key, '(' + val + ')', 40, status))) elif quote == "a": # Leave as-is cDefs.extend(list(createMacroStr(key, val, 40, status))) else: # Unknown quote type raise Exception('badQuoteQual', key) if status: raise Exception('paramNameTooLong', key) # Make file - val should never be quoted; just use as is mDefsGen.extend(list('\n' + key + ' = ' + val)) # Perl file - val should ALWAYS be single-quote quoted # Save const info to a string perlConstSection.extend(list(createPerlConst(key, "'" + val + "'", 40, status))) if status: raise Exception('paramNameTooLong', key) # Save initialization information as a string. Now that we've defined # constants (the names of which are the parameter names) # we can refer to those in the init section. The key variable holds the # name of the constant. perlInitSection.extend(list('\n $self->{_paramsH}->{' + key + '} = ' + "'" + val + "';")) else: # No quote qualifier raise Exception('missingQuoteQual', key) elif ''.join(section) == 'make' and cfgfile: # Configure the remaining make variables defined in the __MAKE__ section of the configuration file. Third-party # library make variables are specified in the __MAKE__ section. matchobj = regexp.match(line) if not matchobj is None: # We have a key-value line key = matchobj.group(1) val = matchobj.group(2) # This information is for making make variables only. We do not need to worry about quoting any values defs[key] = val processMakeParam(mDefsMake, key, val, platDict, machDict) return bool(0) # We have some extraneous line or a newline - ignore. # defs is a dictionary containing all parameters (should they be needed in this script) def parseConfig(fin, keymap, addenda, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection): rv = bool(0) # Open required config file (config.local) try: # Examine each line, looking for key=value pairs. regexpDefs = re.compile(r"^__DEFS__") regexpMake = re.compile(r"^__MAKE__") regexpComm = re.compile(r"^\s*#") regexpQuote = re.compile(r"^\s*(\w):(.+)") regexp = re.compile(r"^\s*(\S+)\s+(\S+)") platDict = {} machDict = {} section = list() # Process the parameters in the configuration file iscfg = bool(1) if not fin is None: for line in fin: ppRet = processParam(iscfg, line, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, section) if ppRet: break; # Process addenda - these are parameters that are not configurable and must be set in the # NetDRMS build. iscfg = bool(0) for key in addenda: item = key + ' ' + addenda[key] ppRet = processParam(iscfg, item, regexpComm, regexpDefs, regexpMake, regexpQuote, regexp, keymap, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection, platDict, machDict, section) if ppRet: break; except Exception as exc: msg, violator = exc.args if msg == 'badKeyMapKey': # If we are here, then there was a non-empty keymap, and the parameter came from # the configuration file. print('Unknown parameter name ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr) rv = bool(1) elif msg == 'badQuoteQual': # The bad quote qualifier came from the configuration file, not the addenda, since # we will have fixed any bad qualifiers in the addenda (which is populated by code). print('Unknown quote qualifier ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr) rv = bool(1) elif msg == 'missingQuoteQual': print('Missing quote qualifier for parameter ' + "'" + violator + "'" + ' in ' + cfgfile + '.', file=sys.stderr) rv = bool(1) elif msg == 'paramNameTooLong': print('Macro name ' + "'" + violator + "' is too long.", file=sys.stderr) rv = bool(1) else: # re-raise the exception raise # Put information collected in platDict and machDict into mDefs. Must do this here, and not in processParam, since # we need to parse all platform-specific make variables before grouping them into platform categories. if not rv: for plat in platDict: mDefsMake.extend(list('\nifeq ($(JSOC_MACHINE), linux_' + plat.lower() + ')')) for var in platDict[plat]: mDefsMake.extend(list('\n' + var + ' = ' + platDict[plat][var])) mDefsMake.extend(list('\nendif\n')) if not rv: for mach in machDict: mDefsMake.extend(list('\nifeq ($(MACHTYPE), ' + mach + ')')) for var in platDict[plat]: mDefsMake.extend(list('\n' + var + ' = ' + platDict[plat][var])) mDefsMake.extend(list('\nendif\n')) return rv def getMgrUIDLine(defs, uidParam): rv = bool(0) cmd = 'id -u ' + defs['SUMS_MANAGER'] try: ret = check_output(cmd, shell=True) uidParam['q:SUMS_MANAGER_UID'] = ret.decode("utf-8") except ValueError: print('Unable to run cmd: ' + cmd + '.') rv = bool(1) except CalledProcessError: print('Command ' + "'" + cmd + "'" + ' ran improperly.') rv = bool(1) return rv def isVersion(maj, min, majDef, minDef): res = 0 if maj > majDef or (maj == majDef and min >= minDef): res = 1 return res def configureComps(defs, mDefs): rv = bool(0) autoConfig = (not defs['AUTOSELCOMP'] == '0') if autoConfig: hasicc = bool(0) hasgcc = bool(0) hasifort = bool(0) hasgfort = bool(0) # Try icc. cmd = 'icc -V 2>&1' try: ret = check_output(cmd, shell=True) ret = ret.decode("utf-8") except CalledProcessError: print('Command ' + "'" + cmd + "'" + ' ran improperly.') rv = bool(1) if rv == bool(0): regexp = re.compile(r".+Version\s+(\d+)[.](\d+)", re.DOTALL) matchobj = regexp.match(ret) if matchobj is None: raise Exception('unexpectedIccRet', ret) else: major = matchobj.group(1) minor = matchobj.group(2) if isVersion(int(major), int(minor), ICC_MAJOR, ICC_MINOR): hasicc = bool(1) # Try gcc. if not hasicc: cmd = 'gcc -v 2>&1' try: ret = check_output(cmd, shell=True) ret = ret.decode("utf-8") except CalledProcessError: print('Command ' + "'" + cmd + "'" + ' ran improperly.') rv = bool(1) if rv == bool(0): regexp = re.compile(r".+gcc\s+version\s+(\d+)\.(\d+)", re.DOTALL) matchobj = regexp.match(ret) if matchobj is None: raise Exception('unexpectedGccRet', ret) else: major = matchobj.group(1) minor = matchobj.group(2) if isVersion(int(major), int(minor), GCC_MAJOR, GCC_MINOR): hasgcc = bool(1) # Try ifort. cmd = 'ifort -V 2>&1' try: ret = check_output(cmd, shell=True) ret = ret.decode("utf-8") except CalledProcessError: print('Command ' + "'" + cmd + "'" + ' ran improperly.') rv = bool(1) if rv == bool(0): regexp = re.compile(r".+Version\s+(\d+)\.(\d+)", re.DOTALL) matchobj = regexp.match(ret) if matchobj is None: raise Exception('unexpectedIfortRet', ret) else: major = matchobj.group(1) minor = matchobj.group(2) if isVersion(int(major), int(minor), IFORT_MAJOR, IFORT_MINOR): hasifort = bool(1) # Try gfortran if not hasifort: cmd = 'gfortran -v 2>&1' try: ret = check_output(cmd, shell=True) ret = ret.decode("utf-8") except CalledProcessError: print('Command ' + "'" + cmd + "'" + ' ran improperly.') rv = bool(1) if rv == bool(0): regexp = re.compile(r".+gcc\s+version\s+(\d+)\.(\d+)", re.DOTALL) matchobj = regexp.match(ret) if matchobj is None: raise Exception('unexpectedGfortranRet', ret) else: major = matchobj.group(1) minor = matchobj.group(2) if isVersion(int(major), int(minor), GFORT_MAJOR, GFORT_MINOR): hasgfort = bool(1) # Append the compiler make variables to the make file if not hasicc and not hasgcc: print('Fatal error: Acceptable C compiler not found! You will be unable to build the DRMS library.', file=sys.stderr) rv = bool(1) elif hasicc: mDefs.extend(list('\nCOMPILER = icc')) else: mDefs.extend(list('\nCOMPILER = gcc')) if not hasifort and not hasgfort: print('Warning: Acceptable Fortran compiler not found! Fortran interface will not be built, and you will be unable to build Fortran modules.', file=sys.stderr) elif hasifort: mDefs.extend(list('\nFCOMPILER = ifort')) else: mDefs.extend(list('\nFCOMPILER = gfortran')) # Environment overrides. These get written, regardless of the disposition of auto-configuration. mDefs.extend(list('\nifneq $(JSOC_COMPILER,)\n COMPILER = $(JSOC_COMPILER)\nendif')) mDefs.extend(list('\nifneq $(JSOC_FCOMPILER,)\n FCOMPILER = $(JSOC_FCOMPILER)\nendif')) return rv def writeFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, mDefsComps, perlConstSection, perlInitSection): rv = bool(0) # Merge mDefsGen, mDefsMake, and mDefsComps into a single string with compiler configuration first, general parameters next, then # make-specific make variables (e.g., third-party library information) last. mDefs = '\n# Compiler Selection\n' + ''.join(mDefsComps) + '\n\n# General Parameters\n' + ''.join(mDefsGen) + '\n\n# Parameters to Configure make\n' + ''.join(mDefsMake) try: with open(cfile, 'w') as cout, open(mfile, 'w') as mout, open(pfile, 'w') as pout: # C file of macros print(C_PREFIX, file=cout) print('/* This file contains a set of preprocessor macros - one for each configuration parameter. */\n', file=cout) buf = '__' + base.upper() + '_H' print('#ifndef ' + buf, file=cout) print('#define ' + buf, file=cout) print(''.join(cDefs), file=cout) print('#endif', file=cout) # Make file of make variables print(PREFIX, file=mout) print('# This file contains a set of make-variable values. The first section contains compiler-selection variables, the second contains general configuration variables, and the third section contains variables that configure how make is run.', file=mout) print(mDefs, file=mout) # Perl module print(PERL_BINPATH, file=pout) print(PREFIX, file=pout) print('# This file contains a set of constants - one for each configuration parameter.\n', file=pout) print(PERL_INTIAL, file=pout) print(''.join(perlConstSection), file=pout) print(PERL_FXNS_A, file=pout) print('sub initialize', file=pout) print('{', file=pout) print(' my($self) = shift;', file=pout, end='') print('', file=pout) print(''.join(perlInitSection), file=pout) print('}\n', file=pout) print(PERL_FXNS_B, file=pout) except IOError as exc: sys.stderr.write(exc.strerror) sys.stderr.write('Unable to open a parameter vile.') rv = bool(1) return rv def configureNet(cfgfile, cfile, mfile, pfile, base, keymap): rv = bool(0) defs = {} cDefs = list() mDefs = list() mDefsGen = list() mDefsMake = list() mDefsComps = list() perlConstSection = list() perlInitSection = list() addenda = {} # There are three parameters that are not configurable and must be set. addenda['a:USER'] = 'NULL' addenda['a:PASSWD'] = 'NULL' addenda['p:DSDS_SUPPORT'] = '0' addenda['a:BUILD_TYPE'] = 'NETDRMS' # Means a non-Stanford build. This will set two additional macros used by make: # __LOCALIZED_DEFS__ and NETDRMS_BUILD. The former is to support legacy code # which incorrectly used this macro, and the latter is for future use. # __LOCALIZED_DEFS__ is deprecated and should not be used in new code. try: with open(cfgfile, 'r') as fin: # Process configuration parameters rv = parseConfig(fin, keymap, addenda, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection) if rv == bool(0): # Must add a parameter for the SUMS_MANAGER UID (for some reason). This must be done after the # config file is processed since an input to getMgrUIDLine() is one of the config file's # parameter values. uidParam = {} rv = getMgrUIDLine(defs, uidParam) if rv == bool(0): rv = parseConfig(None, keymap, uidParam, defs, cDefs, mDefsGen, None, perlConstSection, perlInitSection) # Configure the compiler-selection make variables. if rv == bool(0): rv = configureComps(defs, mDefsComps) # Write out the parameter files. if rv == bool(0): rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, mDefsComps, perlConstSection, perlInitSection) except IOError as exc: sys.stderr.write(exc.strerror) sys.stderr.write('Unable to read configuration file ' + cfgfile + '.') except Exception as exc: type, msg = exc.args if type == 'unexpectedIccRet': print('icc -V returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedGccRet': print('gcc -v returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedIfortRet': print('ifort -V returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedGfortranRet': print('gfortran -v returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) else: # re-raise the exception raise return rv def configureSdp(cfgfile, cfile, mfile, pfile, base, keymap): rv = bool(0) defs = {} cDefs = list() mDefsGen = list() mDefsMake = list() mDefsComps = list() perlConstSection = list() perlInitSection = list() addenda = {} addenda['a:USER'] = 'NULL' addenda['a:PASSWD'] = 'NULL' addenda['p:DSDS_SUPPORT'] = '1' addenda['a:BUILD_TYPE'] = 'JSOC_SDP' # Means a Stanford build. This will set one additional macro used by make: JSOC_SDP_BUILD. try: with open(cfgfile, 'r') as fin: rv = parseConfig(cfgfile, keymap, addenda, defs, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection) if rv == bool(0): # Must add a parameter for the SUMS_MANAGER UID (for some reason) uidParam = {} rv = getMgrUIDLine(defs, uidParam) if rv == bool(0): rv = parseConfig(None, keymap, uidParam, defs, cDefs, mDefsGen, NONE, perlConstSection, perlInitSection) # Configure the compiler-selection make variables. if rv == bool(0): rv = configureComps(defs, mDefsComps) if rv == bool(0): rv = writeFiles(base, cfile, mfile, pfile, cDefs, mDefsGen, mDefsMake, perlConstSection, perlInitSection) except IOError as exc: sys.stderr.write(exc.strerror) sys.stderr.write('Unable to read configuration file ' + cfgfile + '.') except Exception as exc: type, msg = exc.args if type == 'unexpectedIccRet': print('icc -V returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedGccRet': print('gcc -v returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedIfortRet': print('ifort -V returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) elif type == 'unexpectedGfortranRet': print('gfortran -v returned this unexpected message:\n' + msg, file=sys.stderr) rv = bool(1) else: # re-raise the exception raise return rv # Beginning of program rv = RET_SUCCESS net = bool(1) # Parse arguments if __name__ == "__main__": optD = GetArgs(sys.argv[1:]) if not(optD is None): # Ensure we are configuring a DRMS tree cdir = os.path.realpath(os.getcwd()) versfile = cdir + '/base/' + VERS_FILE if not os.path.isfile(versfile): rv = RET_NOTDRMS # Determine whether we are localizing a Stanford build, or a NetDRMS build. If configsdp.txt exists, then # it is a Stanford build, otherwise it is a NetDRMS build. if rv == RET_SUCCESS: stanfordFile = cdir + '/' + SDP_CFG if os.path.isfile(stanfordFile): net = bool(0) cfile = optD['dir'] + '/' + optD['base'] + '.h' mfile = optD['dir'] + '/' + optD['base'] + '.mk' pfile = optD['dir'] + '/' + optD['base'] + '.pm' if net: try: with open(NET_CFGMAP, 'r') as fin: regexpComm = re.compile(r"^\s*#") regexp = re.compile(r"^\s*(\S+)\s+(\w:\S+)") # Must map from config.local namespace to DRMS namespace (e.g., the names used for the C macros) keymap = {} for line in fin: matchobj = regexpComm.match(line) if not matchobj is None: # Skip comment line continue matchobj = regexp.match(line) if not(matchobj is None): # We have a key-value line key = matchobj.group(1) val = matchobj.group(2) keymap[key] = val except OSError: sys.stderr.write('Unable to read configuration map-file ' + NET_CFGMAP + '.') rv = bool(1) # We also need to set the UID of the SUMS manager. We have the name of the # SUMS manager (it is in the configuration file) configureNet(NET_CFG, cfile, mfile, pfile, optD['base'], keymap) else: configureSdp(SDP_CFG, cfile, mfile, pfile, optD['base'], {})
Karen Tian |
Powered by ViewCVS 0.9.4 |