def _parse_target(tree, target): target.parse(tree, named_token='path') # Parse disable if not target.was_quoted: target.value = util.parse_bool(target.at, target.value) if target.value is not False: log.die_print_error_at(target.at, "You can only disable targets!")
def resolve_var(self, var, kconfig): # Remember if var was a special variable var_special = var.value.startswith('$') # Resolve symbols var_is_sym = (not var.was_quoted) and (not var_special) and (util.kernel_option_regex.match(var.value) is not None) sym = None if var_is_sym: if var.value.startswith('CONFIG_'): sym_name = var.value[len('CONFIG_'):] else: sym_name = var.value sym = self.get_sym(sym_name, kconfig) value = sym.str_value var_cmp_mode = Condition._sym_cmp_type.get(sym.orig_type, 'unknown') if var_cmp_mode == 'unknown': log.die_print_error_at(var.at, "cannot compare with symbol {} which is of unknown type".format(sym.name)) elif var_special: value = resolve_special_variable(var.at, kconfig, var.value) var_cmp_mode = get_special_var_cmp_mode(var.at, var.value) else: value = var.value # Literal strings will always inherit the mode var_cmp_mode = None return VarInfo(var, value, var_special, var_is_sym, sym, var_cmp_mode)
def __init__(self, tree, compare_op, operands): super().__init__(tree) self.compare_op = compare_op self.vars = operands if self.compare_op not in _compare_op_to_str: log.die_print_error_at(self.at, "Invalid comparison op '{}'. This is a bug that should be reported.".format(_compare_op_to_str[self.compare_op]))
def parse_bool(at, s): if s in ['true', '1', 'yes', 'y', 'on']: return True elif s in ['false', '0', 'no', 'n', 'off']: return False else: log.die_print_error_at(at, "invalid value for boolean")
def stmt_root_include_module_dir(tree): include_dir = os.path.join(os.path.dirname(currently_parsed_filenames[-1]), find_named_token(tree, 'path')) if os.path.isdir(include_dir): for filename in os.listdir(include_dir): if filename.endswith('.conf'): _include_module_file(tree, os.path.join(include_dir, filename)) else: log.die_print_error_at(def_at(tree), "'{}' is not a directory".format(include_dir))
def resolve_env_variable(hint_at, var): tokens = var[len('$env['):-1].split(':', 1) envvar = tokens[0] default = None if len(tokens) == 1 else decode_quotes(tokens[1]) value = os.environ.get(envvar, default) if value is None: log.die_print_error_at( hint_at, "unknown environment variable '{}'.".format(envvar)) return value
def get_sym(self, sym_name, kconfig): try: sym = kconfig.syms[sym_name] except KeyError: log.die_print_error_at(self.at, "symbol {} does not exist".format(sym_name)) # If the symbol hadn't been encountered before, pin the current value if sym not in autokernel.symbol_tracking.symbol_changes: autokernel.symbol_tracking.symbol_changes[sym] = autokernel.symbol_tracking.SymbolChange(sym.str_value, self.at, 'used in condition') return sym
def _evaluate(self, kconfig): resolved_var = self.resolve_var(self.var, kconfig) cmp_mode = resolved_var.cmp_mode if cmp_mode is 'tristate': implicit_var = self.resolve_var(TokenRawLiteral(self.var.at, '"n"', is_quoted=True), kconfig) elif cmp_mode is 'string' and (resolved_var.is_sym or (resolved_var.special and util.is_env_var(self.var.value))): implicit_var = self.resolve_var(TokenRawLiteral(self.var.at, '""', is_quoted=True), kconfig) else: log.die_print_error_at(self.at, "cannot implicitly convert '{}' to a truth value".format(self.var.value)) return compare_variables([resolved_var, implicit_var], 'EXPR_CMP_NEQ', cmp_mode, self.at)
def find_token(tree, token_name, ignore_missing=False, strip_quotes=False): """ Finds a token by literal name in the children of the given tree. Raises an exception if the token is not found and ignore_missing is not set. """ for c in tree.children: if c.__class__ == lark.Token and c.type == token_name: return util.decode_quotes(str(c)) if strip_quotes else str(c) if not ignore_missing: log.die_print_error_at(def_at(tree), "missing token '{}'".format(token_name)) return None
def find_condition(tree, ignore_missing=True): conditions = find_conditions(tree) if len(conditions) == 0: if ignore_missing: return None else: log.die_print_error_at(def_at(tree), "missing expression") if len(conditions) == 1: return conditions[0] log.die_print_error_at(def_at(tree), "expected exactly one expression, but got {}".format(len(conditions)))
def resolve_special_variable(hint_at, kconfig, var): if var == '$kernel_version': return autokernel.kconfig.get_kernel_version(kconfig.srctree) elif var == '$uname_arch': return autokernel.kconfig.get_uname_arch() elif var == '$arch': return autokernel.kconfig.get_arch() elif var == '$true': return 'y' elif var == '$false': return 'n' elif util.is_env_var(var): return util.resolve_env_variable(hint_at, var) else: log.die_print_error_at(hint_at, "unknown special variable '{}'".format(var))
def _evaluate(self, kconfig): resolved_vars = [self.resolve_var(v, kconfig) for v in self.vars] # The comparison mode is determined by the following schema: # 1. Filter out None, as variables with mode None will inherit any other comparison type. # 2. All other variables force a comparison mode. It is an error to use two variables # of different type in the same expression resolved_vars_with_type = [v for v in resolved_vars if v.cmp_mode is not None] if len(resolved_vars_with_type) == 0: cmp_mode = 'string' # compare with string mode as fallback (e.g. when two literals were given) elif len(resolved_vars_with_type) == 1: cmp_mode = resolved_vars_with_type[0].cmp_mode else: log.die_print_error_at(self.at, "cannot compare variables of different types: [{}]".format(', '.join(["{} ({})".format(v.var.value, v.cmp_mode) for v in resolved_vars_with_type]))) return compare_variables(resolved_vars, self.compare_op, cmp_mode, self.at)
def stmt_module_if(tree): conditions = find_conditions(tree) subcontexts = find_subtrees(tree, 'ctxt_module') if len(subcontexts) - len(conditions) not in [0, 1]: log.die_print_error_at(def_at(tree), "invalid amount of subcontexts(={}) and conditions(={}) for if block; this is a bug that should be reported.".format(len(subcontexts), len(conditions))) not_previous_conditions = [] for c, s in zip(conditions, subcontexts): # The condition for an else if block is the combined negation of all previous conditions, # and its own condition conds = preconditions + not_previous_conditions + [c] self.parse_context(s, preconditions=conds) not_previous_conditions.append(c.negate()) if len(subcontexts) > len(conditions): # The condition for the else block is the combined negation of all previous conditions conds = preconditions + not_previous_conditions self.parse_context(subcontexts[-1], preconditions=conds)
def _include_module_file(tree, filename): rpath = os.path.realpath(filename) if rpath in self._include_module_files: log.verbose("Skipping duplicate inclusion of '{}'".format(rpath)) return else: self._include_module_files.add(rpath) if os.path.isfile(filename): try: subtree = load_config_tree(filename) except IOError as e: log.die_print_error_at(def_at(tree), str(e)) currently_parsed_filenames.append(filename) self.parse_tree(subtree, restrict_to_modules=True) currently_parsed_filenames.pop() else: log.die_print_error_at(def_at(tree), "'{}' does not exist or is not a file".format(filename))
def apply_tree_nodes(nodes, callbacks, on_additional=None, ignore_additional=False): """ For each node calls the callback matching its name. Raises an exception for unmatched nodes if ignore_additional is not set. """ if type(callbacks) == list: callback_dict = {} for c in callbacks: callback_dict[c.__name__] = c callbacks = callback_dict for n in nodes: if n.__class__ == lark.Tree: if n.data in callbacks: callbacks[n.data](n) elif n.data == "extra_semicolon": log.verbose("Extra semicolon at {}:{}:{}".format(currently_parsed_filenames[-1], n.line, n.column)) elif on_additional: on_additional(n) elif not ignore_additional: log.die_print_error_at(def_at(n), "unprocessed rule '{}'; this is a bug that should be reported.".format(n.data))
def parse_expr_condition(tree): if tree.data == 'expr': return ConditionOr(tree, *[parse_expr_condition(c) for c in tree.children if c.__class__ == lark.Tree and c.data == 'expr_term']) if tree.data == 'expr_term': return ConditionAnd(tree, *[parse_expr_condition(c) for c in tree.children if c.__class__ == lark.Tree and c.data == 'expr_factor']) elif tree.data == 'expr_factor': negated = find_first_child(tree, 'expr_op_neg') is not None expr_cmp = find_first_child(tree, 'expr_cmp') if expr_cmp: operands = find_all_named_tokens_raw(expr_cmp, 'expr_param') operation = None # Find operation type and assert all operation types are equal for c in expr_cmp.children: if c.data == 'expr_op_cmp': op = c.children[0].type if operation is None: operation = op elif operation != op: log.die_print_error_at(def_at(expr_cmp), "all expression operands must be the same for n-ary comparisons") return ConditionVarComparison(expr_cmp, operation, operands).negate(negated) expr_id = find_named_token(tree, 'expr_id') if expr_id: # Implicit truth value is the same as writing 'var != "n"' for tristate symbols # and 'var != ""' for others. operand = TokenRawInfo(tree, str(expr_id), is_quoted=False) return ConditionVarTruth(tree, operand).negate(negated) expr = find_first_child(tree, 'expr') if expr: return parse_expr_condition(expr).negate(negated) log.die_print_error_at(def_at(tree), "invalid expression subtree '{}' in 'expr_factor'".format(tree.data)) else: log.die_print_error_at(def_at(tree), "invalid expression subtree '{}'".format(tree.data))
def compare_variables(resolved_vars, op, cmp_mode, hint_at): # Assert that the comparison mode is supported for the given type if cmp_mode == 'string': if op not in ['EXPR_CMP_NEQ', 'EXPR_CMP_EQ']: log.die_print_error_at(hint_at, "invalid comparison '{}' for type string".format(_compare_op_to_str[op])) elif cmp_mode == 'tristate': if op not in ['EXPR_CMP_NEQ', 'EXPR_CMP_EQ']: log.die_print_error_at(hint_at, "invalid comparison operator '{}' for type tristate".format(_compare_op_to_str[op])) # Parse variables to comparable types parse_functor = _variable_parse_functors[cmp_mode] parsed_variables = [] for i, var in enumerate(resolved_vars, start=1): try: parsed_variables.append(parse_functor(var)) except ValueError as e: log.die_print_error_at(var.var.at, "could not convert operand #{} '{}' to {}: {}".format(i, var.var.value, cmp_mode, str(e))) # Compare all variables in order for i, rhs in enumerate(parsed_variables[1:], start=1): if not _compare_op_to_functor[op](parsed_variables[i - 1], rhs): return False return True
def parse_tree(self, tree, *args, **kwargs): """ Parses the given block tree node, and class parse_block_params and parse_context. """ if self.first_definition is None: self.first_definition = def_at(tree) if tree.data != ('blck_' + self.node_name): log.die_print_error_at(def_at(tree), "{} cannot parse '{}'".format(self.__class__.__name__, tree.data)) self.parse_block_params(tree, *args, **kwargs) ctxt = None for c in tree.children: if c.__class__ == lark.Tree: if c.data == 'ctxt_' + self.node_name: if ctxt: log.die_print_error_at(def_at(c), "'{}' must not have multiple children of type '{}'".format("blck_" + self.node_name, "ctxt_" + self.node_name)) ctxt = c if not ctxt: log.die_print_error_at(def_at(tree), "'{}' must have exactly one child '{}'".format("blck_" + self.node_name, "ctxt_" + self.node_name)) self.parse_context(ctxt, *args, **kwargs)
def other(tree): log.die_print_error_at(def_at(tree), "'{}' must not be used in a module config".format(tree.data))
def get_module(stmt): if stmt.module_name not in config.modules: log.die_print_error_at(stmt.at, "module '{}' is never defined".format(stmt.module_name)) return config.modules[stmt.module_name]
def find_named_token_raw(tree, token_name, ignore_missing=False): """ Finds a token by subrule name in the children of the given tree. Raises an exception if the token is not found. """ for c in tree.children: if c.__class__ == lark.Tree and c.data == token_name: if len(c.children) != 1: log.die_print_error_at(def_at(c), "subrule token '{}' has too many children".format(token_name)) if c.children[0].data not in ['string', 'string_quoted']: log.die_print_error_at(def_at(c), "subrule token '{}.{}' has an invalid name (must be either 'string' or 'string_quoted')".format(token_name, c.children[0].data)) if c.children[0].__class__ != lark.Tree: log.die_print_error_at(def_at(c), "subrule token '{}.{}' has no children tree".format(token_name, c.children[0].data)) if len(c.children[0].children) != 1: log.die_print_error_at(def_at(c.children[0]), "subrule token '{}.{}' has too many children".format(token_name, c.children[0].data)) if c.children[0].children[0].__class__ != lark.Token: log.die_print_error_at(def_at(c.children[0]), "subrule token '{}.{}' has no children literal".format(token_name, c.children[0].data)) return TokenRawInfo(c.children[0], str(c.children[0].children[0]), is_quoted=(c.children[0].data == 'string_quoted')) if not ignore_missing: log.die_print_error_at(def_at(tree), "missing token '{}'".format(token_name)) return TokenRawInfo(None, None, False)
def _parse_umask_property(prop): try: prop.value = int(prop.value, 8) except ValueError as e: log.die_print_error_at(prop.at, "Invalid value for umask: {}".format(str(e)))
def _parse_int_property(prop): try: prop.value = int(prop.value) except ValueError as e: log.die_print_error_at(prop.at, "Invalid value for integer: {}".format(str(e)))
def get_special_var_cmp_mode(hint_at, var): if util.is_env_var(var): return 'string' if var in special_var_cmp_mode: return special_var_cmp_mode[var] log.die_print_error_at(hint_at, "unknown special variable '{}'".format(var))