#!/usr/bin/python # script to compile IDL to C++ stubs and skeletons iff the files are out of date import os, sys, itertools _version = 1.1 _usage = '''Expecting any of the flags: -f force recompilation -h this help message -q quiet mode -s for "safe-mode" (emits commands without executing them) -v print the version and any of following the languages/ORBs as arguments: C++ uses omniORB Java uses JacORB Jacorb generates Java code Omniorb generates C++ code ''' # IDL files are listed as tuples of (, ) # the second element can be None iff the file name corresponds to an interface or # struct that is generated from the file. # Some orbs put all the generated code in a file that matches the IDL filename, # others (e.g. Java ORBs) use the name of the generated classes cipresIdlFiles_api1 = [ ('CharacterDB', None), ('Cipres', 'DataMatrix'), ('ReadNexus', None), ('Registry', None), ('Rid3TreeImprove', None), ('TreeDB', None), ('TreeDecompose', None), ('TreeEvaluate', None), ('TreeImprove', None), ('TreeInfer', None), ('TreeIterator', None), # TODO added by rav ('AsyncTreeInfer', None), # TODO added by rav ('TreeMerge', None), ('TreePrune', None), ('TreeSupplier', None), ('TreeRefine', None),] cosIdlFiles = [ ('CosEventChannelAdmin', None), ('CosEventComm', None)] class CompileIDLError(RuntimeError): pass def findNewest(files): '''Returns the modification time of the most recently touched file''' mtimes = [os.path.getmtime(i) for i in files] mtimes.sort() return mtimes[-1] def toCmdLine(arg): ''' adds double quotes to args with spaces.''' if len(arg.split()) > 1: return '"%s"' % arg return arg try: import subprocess _useSystemCall = False except ImportError: _useSystemCall = True def _invoke(cmd, args, outputObj, err): '''outputObj is currently ignored.''' global _useSystemCall if _useSystemCall: c = c + ' ' + ' '.join(args) os.system(c) return True # subprocess if outputObj is sys.stdout: if err is sys.stderr: return 0 == subprocess.call([cmd] + args, shell=True) return 0 == subprocess.call([cmd] + args, stderr=err, shell=True) if err is sys.stderr: return 0 == subprocess.call([cmd] + args, stdout=outputObj, shell=True) return 0 == subprocess.call([cmd] + args, stdout=outputObj, stderr=err, shell=True) class IDLCompileSettings: '''Base class for IDL compiler settings. Expects the invocation order to be: __init__() compileCOSIDL() compileCipresIDL''' defOpts = { 'force' : ['f', False], 'help' : ['hH', False], 'quiet' : ['q', False], 'safe' : ['s', False], 'version' : ['v', False], 'outputObj' : None, 'errObj' : None, } def __init__(self, opts = None): self.opts = opts is None and IDLCompileSettings.defOpts or opts if 'CIPRES_TOP' not in os.environ: raise CompileIDLError('CIPRES_TOP must be in the environment') self.cipresIDLDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'IDL') if not os.path.exists(self.cipresIDLDir): raise CompileIDLError('The directory ' + self.cipresIDLDir + ' was not found!') self.lastCOSModTime = 0 self.fromDir = None self.alwaysGeneratesFile = True def _generatedTagsFromTuples(self, tagTuples): '''Takes a list of idlTuples and returns a list with a tag of generated file (the name of the generated file without the extension).''' iTags = [i[0] for i in tagTuples] if self.alwaysGenerateIDLTagNamed: return iTags, iTags return iTags, [i[1] or i[0] for i in tagTuples] def compileCOSIDL(self, tagTuples): self.outputDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'C++', 'CipresIDL') self.args = ['-bcxx', '-Wbh=.hpp', '-Wbs=.cpp', '-Wbuse_quotes', '-C' + self.outputDir, '-I' + self.cosParent] if not self.mustCompileCOS: return self.lastCOSModTime = findNewest([os.path.join(self.cosParent, i[0] + '.idl') for i in tagTuples]) idlTags, genTags = self._generatedTagsFromTuples(tagTuples) for iTag, gTag in itertools.izip(idlTags, genTags): self._compileIDL(self.args, self.cosParent, iTag, gTag, self.lastCOSModTime) def compileCipresIDL(self, tagTuples, subdir): self.outputDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'C++', 'CipresIDL', subdir) self.args = ['-bcxx', '-Wbh=.hpp', '-Wbs=.cpp', '-Wbuse_quotes', '-C' + self.outputDir, '-I' + self.cosParent, '-DRENDEZ_VOUS' ] # TODO rav added self.outputDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'C++', 'CipresIDL') args = self.args + self.cipresArgs idlTags, genTags = self._generatedTagsFromTuples(tagTuples) # because CipresIDL files can include each other, touching any recompiles all lastCipresIDLModTime = findNewest([os.path.join(self.cipresIDLDir, subdir, i + '.idl') for i in idlTags]) if self.mustCompileCOS: lastModTime = max(self.lastCOSModTime, lastCipresIDLModTime) else: lastModTime = lastCipresIDLModTime for iTag, gTag in itertools.izip(idlTags, genTags): self._compileIDL(args, self.cipresIDLDir, os.path.join(subdir, iTag), os.path.join(subdir, gTag), lastModTime) def _compileIDL(self, args, parent, iTag, gTag, compileIfOlderThan = None): origDir = os.path.abspath(os.curdir) try: if self.fromDir: os.chdir(self.fromDir) cmd = toCmdLine(self.idlCompiler) escapedArgs = [toCmdLine(i) for i in args] toCompile = os.path.join(parent, iTag + '.idl') if not os.path.exists(toCompile): raise IOError('The IDL file ' + toCompile + ' was not found') # check time stamps outputFile = os.path.join(self.outputDir, gTag + self.outputSuffix) upToDate = os.path.exists(outputFile) if upToDate: outputModTime = os.path.getmtime(outputFile) if compileIfOlderThan and outputModTime < compileIfOlderThan: upToDate = False else: idlTimeStamp = os.path.getmtime(toCompile) upToDate = idlTimeStamp < outputModTime outputObj = self.opts['outputObj'] if (not upToDate) or self.opts['force'][1]: escapedArgs.append(toCmdLine(toCompile)) c = cmd + ' ' + ' '.join(escapedArgs) if outputObj is not None and not self.opts['quiet'][1]: outputObj.write(c + '\n') if not self.opts['safe'][1]: errObj = self.opts['errObj'] if not _invoke(cmd, escapedArgs, outputObj, errObj): raise CompileIDLError, 'Compilation failed.' os.utime(outputFile, None) elif outputObj is not None and not self.opts['quiet'][1]: outputObj.write(outputFile + ' is up to date.\n') finally: os.chdir(origDir) def tryCipresDistForCompiler(comp): p = os.path.join(os.environ['CIPRES_TOP'], 'cipres_dist', 'bin', comp) #if sys.platform.upper().startswith('WIN'): # p = p + '.exe' return os.path.exists(p) and p or None class OmniORBSettings(IDLCompileSettings): def __init__(self, opts): IDLCompileSettings.__init__(self, opts) #self.outputSuffix = 'S.h' self.outputSuffix = '.hpp' self.alwaysGenerateIDLTagNamed = True self.mustCompileCOS = True if sys.platform.upper().startswith('WIN'): compilerName = 'omniidl.exe' interDir = 'x86_win32' else: compilerName = 'omniidl' interDir = '' self.idlCompiler = os.path.join(os.environ['OMNIORB_TOP'], 'bin', interDir, compilerName) if self.idlCompiler is None or (not os.path.exists(self.idlCompiler)): self.idlCompiler = tryCipresDistForCompiler(compilerName) if self.idlCompiler is None: raise CompileIDLError('The omniidl, omniorb idl compiler was not found at $OMNIORB_TOP/bin or $CIPRES_TOP/cipres_dist/bin') self.cosParent = None if 'OMNIORB_TOP' in os.environ: self.cosParent = os.path.join(os.environ['OMNIORB_TOP'], 'idl', 'COS') if (self.cosParent is None) or (not os.path.exists(self.cosParent)): initCosParent = self.cosParent self.cosParent = os.path.join(os.environ['CIPRES_TOP'], 'cipres_dist', 'share', 'idl', 'omniORB', 'COS') if not os.path.exists(self.cosParent): raise CompileIDLError('The directory with the COS IDL files (' + initCosParent + 'or ' + self.cosParent + ') was not found') # Terri - self.outputDir and self.args will both be replaced with new values # before they are used to compile idl. self.outputDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'C++', 'CipresIDL') self.args = ['-bcxx', '-Wbh=.hpp', '-Wbs=.cpp', '-Wbuse_quotes', '-C' + self.outputDir, '-I' + self.cosParent] self.cipresArgs = ['-I' + self.cipresIDLDir] class JacORBSettings(IDLCompileSettings): def __init__(self, opts): IDLCompileSettings.__init__(self, opts) self.outputSuffix = '.java' self.alwaysGenerateIDLTagNamed = False self.mustCompileCOS = False # we are getting this code from jars self.alwaysGeneratesFile = False # JacORB does not overwrite the output if it is unchanged compilerName = 'idl' if sys.platform.upper().startswith('WIN'): compilerName = compilerName + '.bat' self.idlCompiler = None if 'JACORB_TOP' in os.environ: self.idlCompiler = os.path.join(os.environ['JACORB_TOP'], 'bin', compilerName) if (self.idlCompiler is None) or (not os.path.exists(self.idlCompiler)): self.idlCompiler = tryCipresDistForCompiler(compilerName) if self.idlCompiler is None: raise CompileIDLError('The Jacorb idl compiler was not found at $JACORB_TOP/bin/ or $CIPRES_TOP/cipres_dist/bin') omgDir = None if 'JACORB_TOP' in os.environ: omgDir = os.path.join(os.environ['JACORB_TOP'], 'idl', 'omg') if (omgDir is None) or (not os.path.exists(omgDir)): omgDir = os.path.join(os.environ['CIPRES_TOP'], 'cipres_dist', 'share', 'idl', 'omg') if not os.path.exists(omgDir): raise CompileIDLError('The directory with the omg IDL files was not found at $JACORB_TOP/idl/omg or $CIPRES_TOP/cipres_dist/share/idl/omg') self.fromDir = os.path.join(os.environ['CIPRES_TOP'], 'framework', 'java') self.outputDir = os.path.join(self.fromDir, 'org', 'cipres', 'CipresIDL') self.args = [ '-I' + omgDir, '-i2jpackage', 'CipresIDL:org.cipres.CipresIDL', '-DJACORB_HAS_BROKEN_FORWARD_DECL', '-I' + self.cipresIDLDir, ] self.cipresArgs = [] def parseCmdLine(argList): global _usage opts = dict(IDLCompileSettings.defOpts) #parse cmd line arg for language cmdSettingList = [] someCommands = False for arg in argList: if arg.startswith('-'): for flag in arg[1:]: known = False for legalValTuple in opts.itervalues(): if isinstance(legalValTuple, list): if flag in legalValTuple[0]: known = True legalValTuple[1] = True if not known: raise CompileIDLError('Unknown flag: ' + flag + '\n' + _usage) else: someCommands = True capArg = arg.upper() if (capArg == 'JAVA') or (capArg == 'JACORB'): x = JacORBSettings(opts) elif (capArg == 'C++') or (capArg == 'CXX') or (capArg == 'OMNIORB'): x = OmniORBSettings(opts) else: raise CompileIDLError('Unexpected argument: ' + arg + '\n' + _usage) cmdSettingList.append(x) if not someCommands: cmdSettingList.append(OmniORBSettings(opts)) return opts, cmdSettingList def runIDLCompile(argList, outputObj = sys.stdout, errObj = sys.stderr): global _usage, _version '''function that acts like a command line invocation of this script.''' opts, compilerSettingList = parseCmdLine(argList) opts['outputObj'] = outputObj opts['errObj'] = errObj if opts['version'][1] and (outputObj is not None): outputObj.write('CIPRES IDL compilation wrapper, version ' + str(_version) + '\n') if opts['help'][1] and (outputObj is not None): outputObj.write(_usage) for settings in compilerSettingList: if not os.path.exists(settings.outputDir): os.makedirs(settings.outputDir) settings.compileCOSIDL(cosIdlFiles) settings.compileCipresIDL(cipresIdlFiles_api1, 'api1') if __name__ == '__main__': try: runIDLCompile(sys.argv[1:]) except CompileIDLError, x: sys.exit(str(x))