def handleOption(e): errors.pushCtx("when processing option '%s' at %s" % (e.name, e.location())) name = evalConstExpr(e, e.props['name']) if name in mk.options: raise ReaderError(e, "option '%s' already defined" % name) if name in mk.override_vars: return # user hardcoded the value with -D=xxx default = None force_default = False desc = None values = None values_desc = None category = mk.Option.CATEGORY_UNSPECIFICED for c in e.children: if c.name == 'default-value': default = c.value force_default = 'force' in c.props and c.props['force'] == '1' elif c.name == 'description': desc = c.value elif c.name == 'values': values = evalConstExpr(e, c.value.replace('\n', '')).split(',') for i in range(len(values)): values[i] = values[i].strip() elif c.name == 'values-description': values_desc = evalConstExpr(e, c.value).split(',') o = mk.Option(name, default, force_default, desc, values, values_desc, errors.getCtx()) mk.addOption(o) if 'never_empty' in e.props and e.props['never_empty'] == '1': o.neverEmpty = 1 if 'category' in e.props: category = evalConstExpr(e, e.props['category']) if category == mk.Option.CATEGORY_PATH: o.category = category o.neverEmpty = 1 def __pathOptionCallb(var, func, caller): if caller == 'nativePaths': return '$(%s)' % var else: return None utils.addSubstituteCallback(o.name, __pathOptionCallb) else: raise ReaderError(e, "unknown category '%s'" % category) errors.popCtx()
def handleModifyTarget(e, dict=None): tg = mk.evalExpr(e.props['target'], use_options=0, add_dict=dict) if tg not in mk.targets: raise ReaderError(e, "unknown target '%s'" % tg) target = mk.targets[tg] tags = rules[target.type].getTagsDict() _processTargetNodes(e, target, tags, dict)
def handleTagInfo(e): name = e.props['name'] if name in tagInfos: info = tagInfos[name] else: info = TagInfo() if 'exclusive' in e.props: info.exclusive = e.props['exclusive'] == '1' if 'position' in e.props: tokens = e.props['position'].split(',') for token in tokens: pos = token.find(':') if pos == -1: keyword = token else: keyword = token[:pos] param = token[pos + 1:] if keyword == 'before': if param not in info.before: info.before.append(param) elif keyword == 'after': if param not in tagInfos: tagInfos[param] = TagInfo() if name not in tagInfos[param].before: tagInfos[param].before.append(name) else: raise ReaderError(e, "unrecognized position token '%s'" % token) tagInfos[name] = info
def checkConditionsSupport(e): """Raises exception of the output format does not support some form of conditions (e.g. DigitalMars).""" if mk.vars['FORMAT_SUPPORTS_CONDITIONS'] != '1' and \ mk.vars['FORMAT_SUPPORTS_CONFIGURATIONS'] != '1': raise ReaderError( e, 'output format does not support conditional processing')
def __doProcess(file=None, strdata=None, xmldata=None): if xmldata != None: m = xmldata else: # FIXME: validity checking try: if file != None: m = xmlparser.parseFile(file) else: m = xmlparser.parseString(strdata) except xmlparser.ParsingError: raise ReaderError(None, "file '%s' is invalid" % file) def processNodes(list): for e in list: if e.name == 'if': if evalWeakCondition(e): processNodes(e.children) else: try: h = HANDLERS[e.name] except (KeyError): raise ReaderError(e, "unknown tag '%s'" % e.name) h(e) try: processNodes(m.children) except ReaderError: reraise()
def handleDefineGlobalTag(e): name = e.props['name'] if name in HANDLERS: raise ReaderError(e, "global tag '%s' is already defined" % name) globalTags[name] = e HANDLERS[name] = handleGlobalTag
def checkTagDefinitions(): if len(__tagsWaitingForRules) > 0: errors = [] for r in __tagsWaitingForRules: errors.append(" target '%s':" % r) for t in __tagsWaitingForRules[r]: errors.append(' at %s' % t.location()) errors = '\n'.join(errors) raise ReaderError( None, "tag definitions for nonexistent rules:\n%s" % errors)
def evalConstExpr(e, str, target=None, add_dict=None): try: return mk.evalExpr(str, use_options=0, target=target, add_dict=add_dict) except NameError, err: raise ReaderError( e, "can't use options or conditional variables in this context (%s)" % err)
def processNodes(list): for e in list: if e.name == 'if': if evalWeakCondition(e): processNodes(e.children) else: try: h = HANDLERS[e.name] except (KeyError): raise ReaderError(e, "unknown tag '%s'" % e.name) h(e)
def evalWeakCondition(e, target=None, add_dict=None): """Evaluates e's 'cond' property, if present, and returns 0 or 1 if it can be evaluated to a constant. If it can't (i.e. it is a strong condition) do it, raises exception.""" x = evalWeakConditionDontRaise(e, target=target, add_dict=add_dict) if x == None: raise ReaderError(e, "'%s': only weak condition allowed in this context" % \ e.props['cond']) else: return x
def handleDefineRule(e): rule = Rule(evalConstExpr(e, e.props['name'])) # check that the rule name is valid if rule.name in HANDLERS: raise ReaderError( e, "global tag or rule '%s' already defined" % rule.name) rules[rule.name] = rule HANDLERS[rule.name] = handleTarget if 'pseudo' in e.props and e.props['pseudo'] == '1': rule.pseudo = 1 if 'extends' in e.props: baserules = [ evalConstExpr(e, x) for x in e.props['extends'].split(',') ] rule.baserules = [] for baserule in baserules: if baserule == '': continue if baserule not in rules: raise ReaderError(e, "unknown rule '%s'" % baserule) rule.baserules.append(rules[baserule]) if rule.name in __tagsWaitingForRules: for t in __tagsWaitingForRules[rule.name]: __addTagToRule(rule, t) del __tagsWaitingForRules[rule.name] for node in e.children: if node.name == 'define-tag': handleDefineTag(node, rule=rule) elif node.name == 'template': rule.template += applyTemplates(node, extractTemplates(node, post=0), extractTemplates(node, post=1)).children else: raise ReaderError( node, "unknown element '%s' in <define-rule>" % node.name)
def handleRequires(e): if 'version' in e.props: if not utils.checkBakefileVersion(e.props['version']): sys.stderr.write(""" ----------------------------------------------------------------------- This file cannot be processed with Bakefile version older than %s. You are using Bakefile version %s. Please install the newest version from http://www.bakefile.org. ----------------------------------------------------------------------- """ % (e.props['version'], mk.vars['BAKEFILE_VERSION'])) raise ReaderError(e, "Bakefile not new enough")
def _extractTargetNodes(parent, list, target, tags, index): def _removeNode(n): for c in n.children: _removeNode(c) index[n.node.name].remove(n) n.parent.children.remove(n) for c in n.parent.children: c.updatePosition() def _removeDuplicates(n): # Eliminate duplicates of exclusive tags: name = n.node.name if name not in index: index[name] = [n] else: if n.exclusive: if len(index[name]) > 0: if config.debug: n2 = index[name][0] print '[dbg] (thrown away <%s> @%s in favor of @%s)' % \ (name, n2.node.location(), n.node.location()) _removeNode(index[name][0]) # else: this can happen if _removeNode was called recursively index[name] = [n] else: index[name].append(n) for node in list: if node.name == 'if': condType = evalWeakConditionDontRaise(node, target=target) if condType == 1: _extractTargetNodes(parent, node.children, target, tags, index) elif condType == None: n = TgtCmdNode(target, parent, TgtCmdNode.IF, node) _removeDuplicates(n) _extractTargetNodes(n, node.children, target, tags, index) # else: condition evaluated to False, ignore this part elif node.name in COMMANDS: n = TgtCmdNode(target, parent, TgtCmdNode.COMMAND, node) _removeDuplicates(n) elif node.name in tags: if evalWeakCondition(node) == 0: continue n = TgtCmdNode(target, parent, TgtCmdNode.TAG, node) _removeDuplicates(n) _extractTargetNodes(n, tags[node.name], target, tags, index) elif node.name in globalTags: n = TgtCmdNode(target, parent, TgtCmdNode.COMMAND, node) _removeDuplicates(n) else: raise ReaderError(node, "unknown target tag '%s'" % node.name)
def handleInclude(e): file = evalConstExpr(e, e.props['file']) canIgnore = 'ignore_missing' in e.props and e.props['ignore_missing'] == '1' justOnce = 'once' in e.props and e.props['once'] == '1' lookup = [os.path.dirname(e.filename)] + config.searchPath errors.pushCtx("included from %s" % e.location()) for dir in lookup: if processFileIfExists(os.path.join(dir, file), justOnce): errors.popCtx() return if not canIgnore: raise ReaderError( e, "can't find file '%s' in %s" % (file, string.join(lookup, ':'))) errors.popCtx()
def translateSpecialCondition(e, condstr, target=None): """If the condition is of form 'target' or 'target and something', make it a normal condition by extracting condition from target's description.""" if condstr.startswith('target and '): if target == None: raise ReaderError( e, "'target' condition can't be used at global scope") if target.cond != None: condstr = '%s and %s' % (target.cond.tostr(), condstr[len('target and '):]) else: condstr = condstr[len('target and '):] return condstr elif condstr == 'target': if target == None: raise ReaderError( e, "'target' condition can't be used at global scope") if target.cond == None: return '1' else: return target.cond.tostr() else: return condstr
def extractTemplates(e, post): ch = [] if post: propname = 'template_append' else: propname = 'template' if propname in e.props: derives = e.props[propname].split(',') for d2 in derives: d = evalConstExpr(e, d2) if d == '': continue try: ch2 = mk.templates[d] ch = ch + ch2 except KeyError: raise ReaderError(e, "unknown template '%s'" % d) return ch
def processFile(filename, onlyOnce=False): if not os.path.isfile(filename): raise ReaderError(None, "file '%s' doesn't exist" % filename) filename = os.path.abspath(filename) if onlyOnce and filename in includedFiles: if config.verbose: print "file %s already included, skipping..." % filename return includedFiles.append(filename) if config.verbose: print 'loading %s...' % filename if config.track_deps: dependencies.addDependency(mk.vars['INPUT_FILE'], config.format, filename) newdir = os.path.dirname(filename) if newdir not in sys.path: sys.path.append(newdir) __doProcess(file=filename)
def handleDefineTag(e, rule=None): name = e.props['name'] if rule == None: if 'rules' in e.props: rs = e.props['rules'].split(',') else: raise ReaderError(e, "external <define-tag> must list rules") else: rs = [rule.name] for rn in rs: if not rn in rules: # delayed tags addition, rule not defined yet if rn in __tagsWaitingForRules: __tagsWaitingForRules[rn].append(e) else: __tagsWaitingForRules[rn] = [e] else: __addTagToRule(rules[rn], e)
def _loadFile(filename): if verbose: print 'loading task description from %s...' % filename try: root = xmlparser.parseFile(filename, xmlparser.NS_BAKEFILE_GEN) except xmlparser.ParsingError: raise errors.Error("can't load file '%s'" % filename) ret = [] for cmd in root.children: if cmd.name == 'include': inc = os.path.join(os.path.dirname(filename), cmd.props['file']) if os.path.isfile(inc): r2 = _loadFile(inc) ret += r2.children else: if ('ignore_missing' not in cmd.props) or \ (cmd.props['ignore_missing'] == '0'): raise ReaderError(cmd, "file '%s' doesn't exist" % inc) else: ret.append(cmd) root.children = ret return root
def handleEcho(e, target=None, add_dict=None): text = evalConstExpr(e, e.value, target=target, add_dict=add_dict) # extract echo level if 'level' in e.props: level = e.props['level'] if level not in ['verbose', 'warning', 'normal', 'debug']: raise ReaderError(e, "unknown echo level '%s'" % level) else: level = 'normal' if level == 'normal': print text elif level == 'verbose' and config.verbose: print text elif level == 'debug' and config.debug: print text elif level == 'warning': # FIXME: DEPRECATED (since 0.2.3) _printWarning(None, text) _printWarning( e, '<echo level="warning"> syntax is deprecated, use <warning> instead' )
def loadModule(m): if m in loadedModules: return if config.verbose: print "loading module '%s'..." % m loadedModules.append(m) # set USING_<MODULENAME> variable: mk.setVar('USING_%s' % m.upper(), '1') # import module's py utilities: imported = mk.importPyModule(m) # include module-specific makefiles: global availableFiles for f in availableFiles: if m in f.modules: f.modules.remove(m) if len(f.modules) == 0: processFile(f.file) imported = True availableFiles = [f for f in availableFiles if len(f.modules) > 0] if not imported: raise ReaderError(None, "unknown module '%s'" % m)
def loadTargets(filename, defaultFlags=[]): def _loadFile(filename): if verbose: print 'loading task description from %s...' % filename try: root = xmlparser.parseFile(filename, xmlparser.NS_BAKEFILE_GEN) except xmlparser.ParsingError: raise errors.Error("can't load file '%s'" % filename) ret = [] for cmd in root.children: if cmd.name == 'include': inc = os.path.join(os.path.dirname(filename), cmd.props['file']) if os.path.isfile(inc): r2 = _loadFile(inc) ret += r2.children else: if ('ignore_missing' not in cmd.props) or \ (cmd.props['ignore_missing'] == '0'): raise ReaderError(cmd, "file '%s' doesn't exist" % inc) else: ret.append(cmd) root.children = ret return root def _findMatchingFiles(node): """Returns list of FileInfo objects from 'files' variable that match node's file matching properties ("files"=comma-sep list of wildcards, "formats"=comma-separated list of formats).""" global files # match filenames: if 'files' in node.props: matches1 = [] globs = node.props['files'].replace('/', os.sep).split(',') for g in globs: for f in [x for x in files if _matchesWildcard(x, g)]: matches1.append(files[f]) else: matches1 = files.values() # match formats: if 'formats' in node.props: formats = node.props['formats'].split(',') matches2 = [] for f in matches1: formats2 = [x for x in formats if x in f.formats] matches2.append((f, formats2)) else: matches2 = [] for f in matches1: matches2.append((f, f.formats)) return matches2 def _parseFlags(cmdline): """Splits command line into individual flag arguments. Handles quoting with " properly.""" # findall returns list of tuples, 1st or 2nd item is empty, depending # on which subexpression was matched: return [a or b for a, b in re.findall(r'"(.*?)"|(\S+)', cmdline)] root = _loadFile(filename) if root.name != 'bakefile-gen': raise ReaderError(root, 'incorrect root node (not a bakefile_gen file?)') if verbose: print 'scanning directories for bakefiles...' for cmd in [x for x in root.children if x.name == 'input']: globs = cmd.value.replace('/', os.sep).split() for g in globs: for f in glob.glob(g): files[f] = FileInfo(f) if verbose: print 'building rules...' for cmd in root.children: if cmd.name == 'disable-formats': formats = cmd.value.split(',') for f in formats: if f not in disabled_formats: disabled_formats.append(f) elif cmd.name == 'enable-formats': formats = cmd.value.split(',') for f in formats: if f in disabled_formats: disabled_formats.remove(f) for cmd in root.children: if cmd.name == 'add-formats': formats = [ x for x in cmd.value.split(',') if x not in disabled_formats ] for file, fl in _findMatchingFiles(cmd): for f in formats: if f not in file.formats: file.formats.append(f) file.flags[f] = [x for x in defaultFlags] # make copy elif cmd.name == 'del-formats': formats = [ x for x in cmd.value.split(',') if x not in disabled_formats ] for file, fl in _findMatchingFiles(cmd): for f in formats: if f in file.formats: file.formats.remove(f) del file.flags[f] for cmd in root.children: if cmd.name == 'add-flags': flagsList = _parseFlags(cmd.value) for file, formats in _findMatchingFiles(cmd): for f in flagsList: flags = f flags = flags.replace('$(INPUT_FILE)', file.filename) flags = flags.replace('$(INPUT_FILE_BASENAME)', os.path.basename(file.filename)) flags = flags.replace( '$(INPUT_FILE_BASENAME_NOEXT)', os.path.splitext(os.path.basename(file.filename))[0]) inputdir = os.path.dirname(file.filename) if inputdir == '': inputdir = '.' flags = flags.replace('$(INPUT_FILE_DIR)', inputdir) for fmt in formats: file.flags[fmt].append(flags) elif cmd.name == 'del-flags': flagsList = _parseFlags(cmd.value) for file, formats in _findMatchingFiles(cmd): for fmt in formats: for f in flagsList: try: file.flags[fmt].remove(f) except ValueError: sys.stderr.write( "Warning: trying to remove flags '%s' that weren't added at %s (current flags on file %s, format %s: '%s')\n" % (f, cmd.location(), file.filename, fmt, ' '.join(file.flags[fmt])))
def handleError(e, target=None, add_dict=None): text = evalConstExpr(e, e.value, target=target, add_dict=add_dict) raise ReaderError(e, text)
def handleSet(e, target=None, add_dict=None): try: errors.pushCtx("in <set> at %s" % e.location()) name = basename = evalConstExpr(e, e.props['var'], target) if (name in mk.override_vars) and target == None: return # can't change value of variable overriden with -D=xxx doEval = not ('eval' in e.props and e.props['eval'] == '0') overwrite = not ('overwrite' in e.props and e.props['overwrite'] == '0') isCond = (len(e.children) > 0) isMakeVar = 'make_var' in e.props and e.props['make_var'] == '1' value = e.value if 'hints' in e.props: hints = e.props['hints'] else: hints = '' # Handle conditions: if isCond: if e.value: raise ReaderError( e, "cannot set unconditional value when <if> is used") noValueSet = 1 for e_if in e.children: try: errors.pushCtx(e_if) if e_if.name != 'if': raise ReaderError(e_if, "malformed <set> command") # Preprocess always true or always false conditions: condstr = evalConstExpr(e_if, e_if.props['cond'], target=target, add_dict=add_dict) condstr = translateSpecialCondition(e_if, condstr, target) typ = mk.evalCondition(condstr) # Condition never met when generating this target: if typ == '0': if config.debug: print "[dbg] removing never-met condition '%s' for variable '%s'" % ( condstr, name) continue # Condition always met: elif typ == '1': if config.debug: print "[dbg] condition '%s' for variable '%s' is always met" % ( condstr, name) noValueSet = 0 isCond = 0 value = e_if.value break elif typ != None: raise ReaderError( e, "malformed condition '%s': doesn't evaluate to boolean value" % condstr) cond = mk.makeCondition(condstr) noValueSet = 0 # Real conditions: checkConditionsSupport(e) if 'scope' in e.props: raise ReaderError( e, "conditional variable can't have nondefault scope ('%s')" % e.props['scope']) if target != None: if (not overwrite) and (name in target.vars): return name = '__%s_%s' % (target.id.replace( '-', '_').replace('.', '_').replace('/', '_'), basename) mk.setVar(e.props['var'], '$(%s)' % name, eval=0, target=target, add_dict=add_dict, hints=hints) if cond == None: raise ReaderError( e, "malformed condition: '%s': must be constant expression, equality test or conjunction of them" % condstr) if name in mk.cond_vars: if not overwrite: return var = mk.cond_vars[name] else: var = mk.CondVar(name, target) mk.addCondVar(var, hints) if doEval: value = mk.evalExpr(e_if.value, target=target, add_dict=add_dict) else: value = e_if.value var.add(cond, value) finally: errors.popCtx() if noValueSet: isCond = 0 value = '' if isCond: return # Non-conditional variables: if value == None: value = '' if 'append' in e.props and e.props['append'] == '1': doAppend = 1 else: doAppend = 0 if 'prepend' in e.props and e.props['prepend'] == '1': doPrepend = 1 else: doPrepend = 0 store_in = None if 'scope' in e.props: sc = evalConstExpr(e, e.props['scope'], target=target) if sc == 'local': pass elif sc == 'global': store_in = mk.vars else: if sc in mk.targets: store_in = mk.targets[sc].vars else: raise ReaderError( e, "invalid scope '%s': must be 'global', 'local' or target name" % sc) if isMakeVar: if doAppend or store_in != None or not doEval: raise ReaderError( e, "make variable (%s) can't be appended or stored in nondefault scope or not evaluated" % name) mk.setVar(name, value, eval=doEval, target=target, add_dict=add_dict, store_in=store_in, append=doAppend, prepend=doPrepend, overwrite=overwrite, makevar=isMakeVar, hints=hints) finally: errors.popCtx()
def handleUnset(e): name = e.props['var'] if not mk.unsetVar(name): raise ReaderError(e, "'%s' is not a variable" % name)
def handleTemplate(e): id = e.props['id'] if id in mk.templates: raise ReaderError(e, "template ID '%s' already used" % id) mk.templates[id] = applyTemplates(e, extractTemplates(e, post=0), extractTemplates(e, post=1)).children
def handleTarget(e): if e.name not in rules: raise ReaderError(e, "unknown target type") rule = rules[e.name] if rule.pseudo and 'id' not in e.props: global _pseudoTargetLastID id = 'pseudotgt%i' % _pseudoTargetLastID _pseudoTargetLastID += 1 else: if 'id' not in e.props: raise ReaderError(e, "target doesn't have id") id = e.props['id'] cond = None if 'cond' in e.props: isCond = 1 # Handle conditional targets: condstr = evalConstExpr(e, e.props['cond']) typ = mk.evalCondition(condstr) # Condition never met, ignore the target: if typ == '0': utils.deadTargets.append(id) return # Condition always met: elif typ == '1': isCond = 0 elif typ != None: raise ReaderError(e, "malformed condition: '%s'" % condstr) if isCond: checkConditionsSupport(e) cond = mk.makeCondition(condstr) if cond == None: raise ReaderError(e, "malformed condition: '%s'" % condstr) tags = rule.getTagsDict() e = applyTemplates(e, rule.getTemplates() + extractTemplates(e, post=0), extractTemplates(e, post=1)) if id in mk.targets: raise ReaderError(e, "duplicate target name '%s'" % id) if 'category' in e.props: try: cats = { 'all': mk.Target.CATEG_ALL, 'normal': mk.Target.CATEG_NORMAL, 'automatic': mk.Target.CATEG_AUTOMATIC } category = cats[e.props['category']] except KeyError: raise ReaderError(e, "unknown category '%s'" % e.props['category']) else: category = mk.Target.CATEG_NORMAL target = mk.Target(e.name, id, cond, rule.pseudo, category) mk.addTarget(target) errors.pushCtx("when processing target '%s' at %s" % (target.id, e.location())) _processTargetNodes(e, target, tags, None) errors.popCtx()