1from __future__ import print_function 2from __future__ import absolute_import 3import sys 4if not hasattr(sys, 'version_info'): 5 print('*** Python version 1 is not supported. Please get the latest version from www.python.org ***') 6 sys.exit(4) 7 8import pickle 9 10import subprocess 11 12import nargs 13useThreads = nargs.Arg.findArgument('useThreads', sys.argv[1:]) 14if useThreads is None: 15 useThreads = 0 # workaround issue with parallel configure 16elif useThreads == 'no' or useThreads == '0': 17 useThreads = 0 18elif useThreads == 'yes' or useThreads == '1': 19 useThreads = 1 20else: 21 raise RuntimeError('Unknown option value for --useThreads ',useThreads) 22 23useSelect = nargs.Arg.findArgument('useSelect', sys.argv[1:]) 24if useSelect is None: 25 useSelect = 1 26elif useSelect == 'no' or useSelect == '0': 27 useSelect = 0 28elif useSelect == 'yes' or useSelect == '1': 29 useSelect = 1 30else: 31 raise RuntimeError('Unknown option value for --useSelect ',useSelect) 32 33import logger 34 35class Script(logger.Logger): 36 def __init__(self, clArgs = None, argDB = None, log = None): 37 self.checkPython() 38 logger.Logger.__init__(self, clArgs, argDB, log) 39 self.shell = '/bin/sh' 40 self.showHelp = 1 41 return 42 43 def hasHelpFlag(self): 44 '''Decide whether to display the help message and exit''' 45 import nargs 46 47 if not self.showHelp: 48 return 0 49 if nargs.Arg.findArgument('help', self.clArgs) is None and nargs.Arg.findArgument('h', self.clArgs) is None: 50 return 0 51 return 1 52 53 def hasListFlag(self): 54 '''Decide whether to display the list of download files and exit''' 55 import nargs 56 57 if not self.showHelp: 58 return 0 59 if nargs.Arg.findArgument('with-packages-download-dir', self.clArgs) is None: 60 return 0 61 return 1 62 63 def setupArguments(self, argDB): 64 '''This method now also creates the help and action logs''' 65 import help 66 67 argDB = logger.Logger.setupArguments(self, argDB) 68 69 self.help = help.Help(argDB) 70 self.help.title = 'Script Help' 71 72 self.actions = help.Info(argDB) 73 self.actions.title = 'Script Actions' 74 75 self.setupHelp(self.help) 76 return argDB 77 78 def setupHelp(self, help): 79 '''This method should be overidden to provide help for arguments''' 80 import nargs 81 82 help.addArgument('Script', '-h', nargs.ArgBool(None, 0, 'Print this help message', isTemporary = 1), ignoreDuplicates = 1) 83 help.addArgument('Script', '-help', nargs.ArgBool(None, 0, 'Print this help message', isTemporary = 1), ignoreDuplicates = 1) 84 help.addArgument('Script', '-with-packages-download-dir=<dir>', nargs.ArgDir(None,None, 'Skip network download of package tarballs and locate them in specified dir. If not found in dir, print package URL - so it can be obtained manually.', isTemporary = 1), ignoreDuplicates = 1) 85 return help 86 87 def setup(self): 88 ''' This method checks to see whether help was requested''' 89 if hasattr(self, '_setup'): 90 return 91 logger.Logger.setup(self) 92 self._setup = 1 93 if self.hasHelpFlag(): 94 self.argDB.readonly = True 95 if self.argDB.target == ['default']: 96 sections = None 97 else: 98 sections = self.argDB.target 99 self.help.output(sections = sections) 100 sys.exit() 101 if self.hasListFlag(): 102 self.help.outputDownload() 103 return 104 105 def cleanup(self): 106 '''This method outputs the action log''' 107 self.actions.output(self.log) 108 return 109 110 def checkPython(self): 111 if not hasattr(sys, 'version_info') or sys.version_info < (2,6): 112 raise RuntimeError('BuildSystem requires Python version 2.6 or higher. Get Python at https://www.python.org/') 113 return 114 115 def getModule(root, name): 116 '''Retrieve a specific module from the directory root, bypassing the usual paths''' 117 import imp 118 119 (fp, pathname, description) = imp.find_module(name, [root]) 120 try: 121 return imp.load_module(name, fp, pathname, description) 122 finally: 123 if fp: fp.close() 124 return 125 getModule = staticmethod(getModule) 126 127 def importModule(moduleName): 128 '''Import the named module, and return the module object 129 - Works properly for fully qualified names''' 130 module = __import__(moduleName) 131 components = moduleName.split('.') 132 for comp in components[1:]: 133 module = getattr(module, comp) 134 return module 135 importModule = staticmethod(importModule) 136 137 @staticmethod 138 def runShellCommand(command, log=None, cwd=None): 139 return Script.runShellCommandSeq([command], log=log, cwd=cwd) 140 141 @staticmethod 142 def runShellCommandSeq(commandseq, log=None, cwd=None): 143 Popen = subprocess.Popen 144 PIPE = subprocess.PIPE 145 output = '' 146 error = '' 147 for command in commandseq: 148 useShell = isinstance(command, str) or isinstance(command, bytes) 149 if log: log.write('Executing: %s\n' % (command,)) 150 try: 151 pipe = Popen(command, cwd=cwd, stdin=None, stdout=PIPE, stderr=PIPE, 152 shell=useShell) 153 (out, err) = pipe.communicate() 154 if sys.version_info >= (3,0): 155 out = out.decode(encoding='UTF-8',errors='replace') 156 err = err.decode(encoding='UTF-8',errors='replace') 157 ret = pipe.returncode 158 except OSError as e: 159 return ('', e.message, e.errno) 160 except FileNotFoundError as e: 161 return ('', e.message, e.errno) 162 output += out 163 error += err 164 if ret: 165 break 166 return (output, error, ret) 167 168 def defaultCheckCommand(command, status, output, error): 169 '''Raise an error if the exit status is nonzero''' 170 if status: raise RuntimeError('Could not execute "%s":\n%s' % (command,output+error)) 171 defaultCheckCommand = staticmethod(defaultCheckCommand) 172 173 @staticmethod 174 def executeShellCommand(command, checkCommand = None, timeout = 600.0, log = None, lineLimit = 0, cwd=None, logOutputflg = True): 175 '''Execute a shell command returning the output, and optionally provide a custom error checker 176 - This returns a tuple of the (output, error, statuscode)''' 177 return Script.executeShellCommandSeq([command], checkCommand=checkCommand, timeout=timeout, log=log, lineLimit=lineLimit, cwd=cwd,logOutputflg = logOutputflg) 178 179 @staticmethod 180 def executeShellCommandSeq(commandseq, checkCommand = None, timeout = 600.0, log = None, lineLimit = 0, cwd=None, logOutputflg = True): 181 '''Execute a sequence of shell commands (an && chain) returning the output, and optionally provide a custom error checker 182 - This returns a tuple of the (output, error, statuscode)''' 183 if not checkCommand: 184 checkCommand = Script.defaultCheckCommand 185 if log is None: 186 log = logger.Logger.defaultLog 187 def logOutput(log, output, logOutputflg): 188 import re 189 if not logOutputflg: return output 190 # get rid of multiple blank lines 191 output = re.sub('\n+','\n', output).strip() 192 if output: 193 if lineLimit: 194 output = '\n'.join(output.split('\n')[:lineLimit]) 195 if '\n' in output: # multi-line output 196 log.write('stdout:\n'+output+'\n') 197 else: 198 log.write('stdout: '+output+'\n') 199 return output 200 def runInShell(commandseq, log, cwd): 201 if useThreads: 202 import threading 203 class InShell(threading.Thread): 204 def __init__(self): 205 threading.Thread.__init__(self) 206 self.name = 'Shell Command' 207 self.setDaemon(1) 208 def run(self): 209 (self.output, self.error, self.status) = ('', '', -1) # So these fields exist even if command fails with no output 210 (self.output, self.error, self.status) = Script.runShellCommandSeq(commandseq, log, cwd) 211 thread = InShell() 212 thread.start() 213 thread.join(timeout) 214 if thread.isAlive(): 215 error = 'Runaway process exceeded time limit of '+str(timeout)+'s\n' 216 log.write(error) 217 return ('', error, -1) 218 else: 219 return (thread.output, thread.error, thread.status) 220 else: 221 return Script.runShellCommandSeq(commandseq, log, cwd) 222 223 (output, error, status) = runInShell(commandseq, log, cwd) 224 output = logOutput(log, output,logOutputflg) 225 checkCommand(commandseq, status, output, error) 226 return (output, error, status) 227 228 def loadConfigure(self, argDB = None): 229 if argDB is None: 230 argDB = self.argDB 231 if not 'configureCache' in argDB: 232 self.logPrint('No cached configure in RDict at '+str(argDB.saveFilename)) 233 return None 234 try: 235 cache = argDB['configureCache'] 236 framework = pickle.loads(cache) 237 framework.framework = framework 238 framework.argDB = argDB 239 self.logPrint('Loaded configure to cache: size '+str(len(cache))) 240 except pickle.UnpicklingError as e: 241 framework = None 242 self.logPrint('Invalid cached configure: '+str(e)) 243 return framework 244 245import args 246 247class LanguageProcessor(args.ArgumentProcessor): 248 def __init__(self, clArgs = None, argDB = None, framework = None, versionControl = None): 249 self.languageModule = {} 250 self.preprocessorObject = {} 251 self.compilerObject = {} 252 self.linkerObject = {} 253 self.sharedLinkerObject = {} 254 self.dynamicLinkerObject = {} 255 self.framework = framework 256 self.versionControl = versionControl 257 args.ArgumentProcessor.__init__(self, clArgs, argDB) 258 self.outputFiles = {} 259 self.modulePath = 'config.compile' 260 return 261 262 def getCompilers(self): 263 if self.framework is None: 264 return 265 return self.framework.require('config.compilers', None) 266 compilers = property(getCompilers, doc = 'The config.compilers configure object') 267 def getLibraries(self): 268 if self.framework is None: 269 return 270 return self.framework.require('config.libraries', None) 271 libraries = property(getLibraries, doc = 'The config.libraries configure object') 272 273 def __getstate__(self, d = None): 274 '''We only want to pickle the language module names and output files. The other objects are set by configure.''' 275 if d is None: 276 d = args.ArgumentProcessor.__getstate__(self) 277 if 'languageModule' in d: 278 d['languageModule'] = dict([(lang,mod._loadName) for lang,mod in d['languageModule'].items()]) 279 for member in ['preprocessorObject', 'compilerObject', 'linkerObject', 'sharedLinkerObject', 'dynamicLinkerObject', 'framework']: 280 if member in d: 281 del d[member] 282 return d 283 284 def __setstate__(self, d): 285 '''We must create the language modules''' 286 args.ArgumentProcessor.__setstate__(self, d) 287 self.__dict__.update(d) 288 [self.getLanguageModule(language, moduleName) for language,moduleName in self.languageModule.items()] 289 self.preprocessorObject = {} 290 self.compilerObject = {} 291 self.linkerObject = {} 292 self.sharedLinkerObject = {} 293 self.dynamicLinkerObject = {} 294 return 295 296 def setArgDB(self, argDB): 297 args.ArgumentProcessor.setArgDB(self, argDB) 298 for obj in self.preprocessorObject.values(): 299 if not hasattr(obj, 'argDB') or not obj.argDB == argDB: 300 obj.argDB = argDB 301 for obj in self.compilerObject.values(): 302 if not hasattr(obj, 'argDB') or not obj.argDB == argDB: 303 obj.argDB = argDB 304 for obj in self.linkerObject.values(): 305 if not hasattr(obj, 'argDB') or not obj.argDB == argDB: 306 obj.argDB = argDB 307 for obj in self.sharedLinkerObject.values(): 308 if not hasattr(obj, 'argDB') or not obj.argDB == argDB: 309 obj.argDB = argDB 310 for obj in self.dynamicLinkerObject.values(): 311 if not hasattr(obj, 'argDB') or not obj.argDB == argDB: 312 obj.argDB = argDB 313 if not self.compilers is None: 314 self.compilers.argDB = argDB 315 for obj in self.preprocessorObject.values(): 316 if hasattr(obj, 'configCompilers'): 317 obj.configCompilers.argDB = argDB 318 for obj in self.compilerObject.values(): 319 if hasattr(obj, 'configCompilers'): 320 obj.configCompilers.argDB = argDB 321 for obj in self.linkerObject.values(): 322 if hasattr(obj, 'configCompilers'): 323 obj.configCompilers.argDB = argDB 324 for obj in self.sharedLinkerObject.values(): 325 if hasattr(obj, 'configCompilers'): 326 obj.configCompilers.argDB = argDB 327 for obj in self.dynamicLinkerObject.values(): 328 if hasattr(obj, 'configCompilers'): 329 obj.configCompilers.argDB = argDB 330 if not self.libraries is None: 331 self.libraries.argDB = argDB 332 for obj in self.linkerObject.values(): 333 if hasattr(obj, 'configLibraries'): 334 obj.configLibraries.argDB = argDB 335 for obj in self.sharedLinkerObject.values(): 336 if hasattr(obj, 'configLibraries'): 337 obj.configLibraries.argDB = argDB 338 for obj in self.dynamicLinkerObject.values(): 339 if hasattr(obj, 'configLibraries'): 340 obj.configLibraries.argDB = argDB 341 return 342 argDB = property(args.ArgumentProcessor.getArgDB, setArgDB, doc = 'The RDict argument database') 343 344 def getLanguageModule(self, language, moduleName = None): 345 '''Return the module associated with operations for a given language 346 - Giving a moduleName explicitly forces a reimport''' 347 if not language in self.languageModule or not moduleName is None: 348 try: 349 if moduleName is None: 350 moduleName = self.modulePath+'.'+language 351 module = __import__(moduleName) 352 except ImportError as e: 353 if not moduleName is None: 354 self.logPrint('Failure to find language module: '+str(e)) 355 try: 356 moduleName = self.modulePath+'.'+language 357 module = __import__(moduleName) 358 except ImportError as e: 359 self.logPrint('Failure to find language module: '+str(e)) 360 moduleName = 'config.compile.'+language 361 module = __import__(moduleName) 362 components = moduleName.split('.') 363 for component in components[1:]: 364 module = getattr(module, component) 365 module._loadName = moduleName 366 self.languageModule[language] = module 367 return self.languageModule[language] 368 369 def getPreprocessorObject(self, language): 370 if not language in self.preprocessorObject: 371 self.preprocessorObject[language] = self.getLanguageModule(language).Preprocessor(self.argDB) 372 self.preprocessorObject[language].setup() 373 if not self.compilers is None: 374 self.preprocessorObject[language].configCompilers = self.compilers 375 if not self.versionControl is None: 376 self.preprocessorObject[language].versionControl = self.versionControl 377 return self.preprocessorObject[language] 378 379 def setPreprocessorObject(self, language, preprocessor): 380 self.preprocessorObject[language] = preprocessor 381 return self.getPreprocessorObject(language) 382 383 def getCompilerObject(self, language): 384 if not language in self.compilerObject: 385 self.compilerObject[language] = self.getLanguageModule(language).Compiler(self.argDB) 386 self.compilerObject[language].setup() 387 if not self.compilers is None: 388 self.compilerObject[language].configCompilers = self.compilers 389 if not self.versionControl is None: 390 self.compilerObject[language].versionControl = self.versionControl 391 return self.compilerObject[language] 392 393 def setCompilerObject(self, language, compiler): 394 self.compilerObject[language] = compiler 395 return self.getCompilerObject(language) 396 397 def getLinkerObject(self, language): 398 if not language in self.linkerObject: 399 self.linkerObject[language] = self.getLanguageModule(language).Linker(self.argDB) 400 self.linkerObject[language].setup() 401 if not self.compilers is None: 402 self.linkerObject[language].configCompilers = self.compilers 403 if not self.libraries is None: 404 self.linkerObject[language].configLibraries = self.libraries 405 if not self.versionControl is None: 406 self.linkerObject[language].versionControl = self.versionControl 407 return self.linkerObject[language] 408 409 def setLinkerObject(self, language, linker): 410 self.linkerObject[language] = linker 411 return self.getLinkerObject(language) 412 413 def getSharedLinkerObject(self, language): 414 if not language in self.sharedLinkerObject: 415 self.sharedLinkerObject[language] = self.getLanguageModule(language).SharedLinker(self.argDB) 416 self.sharedLinkerObject[language].setup() 417 if not self.compilers is None: 418 self.sharedLinkerObject[language].configCompilers = self.compilers 419 if not self.libraries is None: 420 self.sharedLinkerObject[language].configLibraries = self.libraries 421 if not self.versionControl is None: 422 self.sharedLinkerObject[language].versionControl = self.versionControl 423 return self.sharedLinkerObject[language] 424 425 def setSharedLinkerObject(self, language, linker): 426 self.sharedLinkerObject[language] = linker 427 return self.getSharedLinkerObject(language) 428 429 def getDynamicLinkerObject(self, language): 430 if not language in self.dynamicLinkerObject: 431 self.dynamicLinkerObject[language] = self.getLanguageModule(language).DynamicLinker(self.argDB) 432 self.dynamicLinkerObject[language].setup() 433 if not self.compilers is None: 434 self.dynamicLinkerObject[language].configCompilers = self.compilers 435 if not self.libraries is None: 436 self.dynamicLinkerObject[language].configLibraries = self.libraries 437 if not self.versionControl is None: 438 self.dynamicLinkerObject[language].versionControl = self.versionControl 439 return self.dynamicLinkerObject[language] 440 441 def setDynamicLinkerObject(self, language, linker): 442 self.dynamicLinkerObject[language] = linker 443 return self.getDynamicLinkerObject(language) 444