1b92e7daeSPhilippe Mathieu-Daudé#!/usr/bin/env python3 2d4fdccadSPaolo Bonzini# 3d4fdccadSPaolo Bonzini# Mini-Kconfig parser 4d4fdccadSPaolo Bonzini# 5d4fdccadSPaolo Bonzini# Copyright (c) 2015 Red Hat Inc. 6d4fdccadSPaolo Bonzini# 7d4fdccadSPaolo Bonzini# Authors: 8d4fdccadSPaolo Bonzini# Paolo Bonzini <pbonzini@redhat.com> 9d4fdccadSPaolo Bonzini# 10d4fdccadSPaolo Bonzini# This work is licensed under the terms of the GNU GPL, version 2 11d4fdccadSPaolo Bonzini# or, at your option, any later version. See the COPYING file in 12d4fdccadSPaolo Bonzini# the top-level directory. 13d4fdccadSPaolo Bonzini 14d4fdccadSPaolo Bonziniimport os 15d4fdccadSPaolo Bonziniimport sys 1682f51817SPaolo Bonziniimport re 17f3494749SPaolo Bonziniimport random 18d4fdccadSPaolo Bonzini 19f7082a9aSPaolo Bonzini__all__ = [ 'KconfigDataError', 'KconfigParserError', 20f3494749SPaolo Bonzini 'KconfigData', 'KconfigParser' , 21f3494749SPaolo Bonzini 'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ] 22d4fdccadSPaolo Bonzini 23d4fdccadSPaolo Bonzinidef debug_print(*args): 24d4fdccadSPaolo Bonzini #print('# ' + (' '.join(str(x) for x in args))) 25d4fdccadSPaolo Bonzini pass 26d4fdccadSPaolo Bonzini 27d4fdccadSPaolo Bonzini# ------------------------------------------- 28d4fdccadSPaolo Bonzini# KconfigData implements the Kconfig semantics. For now it can only 29d4fdccadSPaolo Bonzini# detect undefined symbols, i.e. symbols that were referenced in 30d4fdccadSPaolo Bonzini# assignments or dependencies but were not declared with "config FOO". 31d4fdccadSPaolo Bonzini# 32d4fdccadSPaolo Bonzini# Semantic actions are represented by methods called do_*. The do_var 33d4fdccadSPaolo Bonzini# method return the semantic value of a variable (which right now is 34d4fdccadSPaolo Bonzini# just its name). 35d4fdccadSPaolo Bonzini# ------------------------------------------- 36d4fdccadSPaolo Bonzini 37f7082a9aSPaolo Bonziniclass KconfigDataError(Exception): 38f7082a9aSPaolo Bonzini def __init__(self, msg): 39f7082a9aSPaolo Bonzini self.msg = msg 40f7082a9aSPaolo Bonzini 41f7082a9aSPaolo Bonzini def __str__(self): 42f7082a9aSPaolo Bonzini return self.msg 43f7082a9aSPaolo Bonzini 44f3494749SPaolo Bonziniallyesconfig = lambda x: True 45f3494749SPaolo Bonziniallnoconfig = lambda x: False 46f3494749SPaolo Bonzinidefconfig = lambda x: x 47f3494749SPaolo Bonzinirandconfig = lambda x: random.randint(0, 1) == 1 48f3494749SPaolo Bonzini 49d4fdccadSPaolo Bonziniclass KconfigData: 5053167f56SPaolo Bonzini class Expr: 5153167f56SPaolo Bonzini def __and__(self, rhs): 5253167f56SPaolo Bonzini return KconfigData.AND(self, rhs) 5353167f56SPaolo Bonzini def __or__(self, rhs): 5453167f56SPaolo Bonzini return KconfigData.OR(self, rhs) 5553167f56SPaolo Bonzini def __invert__(self): 5653167f56SPaolo Bonzini return KconfigData.NOT(self) 5753167f56SPaolo Bonzini 58f7082a9aSPaolo Bonzini # Abstract methods 59f7082a9aSPaolo Bonzini def add_edges_to(self, var): 60f7082a9aSPaolo Bonzini pass 61f7082a9aSPaolo Bonzini def evaluate(self): 62f7082a9aSPaolo Bonzini assert False 63f7082a9aSPaolo Bonzini 6453167f56SPaolo Bonzini class AND(Expr): 6553167f56SPaolo Bonzini def __init__(self, lhs, rhs): 6653167f56SPaolo Bonzini self.lhs = lhs 6753167f56SPaolo Bonzini self.rhs = rhs 6853167f56SPaolo Bonzini def __str__(self): 6953167f56SPaolo Bonzini return "(%s && %s)" % (self.lhs, self.rhs) 7053167f56SPaolo Bonzini 71f7082a9aSPaolo Bonzini def add_edges_to(self, var): 72f7082a9aSPaolo Bonzini self.lhs.add_edges_to(var) 73f7082a9aSPaolo Bonzini self.rhs.add_edges_to(var) 74f7082a9aSPaolo Bonzini def evaluate(self): 75f7082a9aSPaolo Bonzini return self.lhs.evaluate() and self.rhs.evaluate() 76f7082a9aSPaolo Bonzini 7753167f56SPaolo Bonzini class OR(Expr): 7853167f56SPaolo Bonzini def __init__(self, lhs, rhs): 7953167f56SPaolo Bonzini self.lhs = lhs 8053167f56SPaolo Bonzini self.rhs = rhs 8153167f56SPaolo Bonzini def __str__(self): 8253167f56SPaolo Bonzini return "(%s || %s)" % (self.lhs, self.rhs) 8353167f56SPaolo Bonzini 84f7082a9aSPaolo Bonzini def add_edges_to(self, var): 85f7082a9aSPaolo Bonzini self.lhs.add_edges_to(var) 86f7082a9aSPaolo Bonzini self.rhs.add_edges_to(var) 87f7082a9aSPaolo Bonzini def evaluate(self): 88f7082a9aSPaolo Bonzini return self.lhs.evaluate() or self.rhs.evaluate() 89f7082a9aSPaolo Bonzini 9053167f56SPaolo Bonzini class NOT(Expr): 9153167f56SPaolo Bonzini def __init__(self, lhs): 9253167f56SPaolo Bonzini self.lhs = lhs 9353167f56SPaolo Bonzini def __str__(self): 9453167f56SPaolo Bonzini return "!%s" % (self.lhs) 9553167f56SPaolo Bonzini 96f7082a9aSPaolo Bonzini def add_edges_to(self, var): 97f7082a9aSPaolo Bonzini self.lhs.add_edges_to(var) 98f7082a9aSPaolo Bonzini def evaluate(self): 99f7082a9aSPaolo Bonzini return not self.lhs.evaluate() 100f7082a9aSPaolo Bonzini 10153167f56SPaolo Bonzini class Var(Expr): 10253167f56SPaolo Bonzini def __init__(self, name): 10353167f56SPaolo Bonzini self.name = name 10453167f56SPaolo Bonzini self.value = None 105f7082a9aSPaolo Bonzini self.outgoing = set() 106f7082a9aSPaolo Bonzini self.clauses_for_var = list() 10753167f56SPaolo Bonzini def __str__(self): 10853167f56SPaolo Bonzini return self.name 10953167f56SPaolo Bonzini 110f7082a9aSPaolo Bonzini def has_value(self): 111f7082a9aSPaolo Bonzini return not (self.value is None) 112f7082a9aSPaolo Bonzini def set_value(self, val, clause): 113f7082a9aSPaolo Bonzini self.clauses_for_var.append(clause) 114f7082a9aSPaolo Bonzini if self.has_value() and self.value != val: 115*f9423e9fSPaolo Bonzini print("The following clauses were found for " + self.name, file=sys.stderr) 116f7082a9aSPaolo Bonzini for i in self.clauses_for_var: 117f7082a9aSPaolo Bonzini print(" " + str(i), file=sys.stderr) 118f7082a9aSPaolo Bonzini raise KconfigDataError('contradiction between clauses when setting %s' % self) 119f7082a9aSPaolo Bonzini debug_print("=> %s is now %s" % (self.name, val)) 120f7082a9aSPaolo Bonzini self.value = val 121f7082a9aSPaolo Bonzini 122f7082a9aSPaolo Bonzini # depth first search of the dependency graph 123f7082a9aSPaolo Bonzini def dfs(self, visited, f): 124f7082a9aSPaolo Bonzini if self in visited: 125f7082a9aSPaolo Bonzini return 126f7082a9aSPaolo Bonzini visited.add(self) 127f7082a9aSPaolo Bonzini for v in self.outgoing: 128f7082a9aSPaolo Bonzini v.dfs(visited, f) 129f7082a9aSPaolo Bonzini f(self) 130f7082a9aSPaolo Bonzini 131f7082a9aSPaolo Bonzini def add_edges_to(self, var): 132f7082a9aSPaolo Bonzini self.outgoing.add(var) 133f7082a9aSPaolo Bonzini def evaluate(self): 134f7082a9aSPaolo Bonzini if not self.has_value(): 135f7082a9aSPaolo Bonzini raise KconfigDataError('cycle found including %s' % self) 136f7082a9aSPaolo Bonzini return self.value 137f7082a9aSPaolo Bonzini 13853167f56SPaolo Bonzini class Clause: 13953167f56SPaolo Bonzini def __init__(self, dest): 14053167f56SPaolo Bonzini self.dest = dest 141f7082a9aSPaolo Bonzini def priority(self): 142f7082a9aSPaolo Bonzini return 0 143f7082a9aSPaolo Bonzini def process(self): 144f7082a9aSPaolo Bonzini pass 14553167f56SPaolo Bonzini 14653167f56SPaolo Bonzini class AssignmentClause(Clause): 14753167f56SPaolo Bonzini def __init__(self, dest, value): 14853167f56SPaolo Bonzini KconfigData.Clause.__init__(self, dest) 14953167f56SPaolo Bonzini self.value = value 15053167f56SPaolo Bonzini def __str__(self): 151f7082a9aSPaolo Bonzini return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n') 152f7082a9aSPaolo Bonzini 153f7082a9aSPaolo Bonzini def process(self): 154f7082a9aSPaolo Bonzini self.dest.set_value(self.value, self) 15553167f56SPaolo Bonzini 15653167f56SPaolo Bonzini class DefaultClause(Clause): 15753167f56SPaolo Bonzini def __init__(self, dest, value, cond=None): 15853167f56SPaolo Bonzini KconfigData.Clause.__init__(self, dest) 15953167f56SPaolo Bonzini self.value = value 16053167f56SPaolo Bonzini self.cond = cond 161f7082a9aSPaolo Bonzini if not (self.cond is None): 162f7082a9aSPaolo Bonzini self.cond.add_edges_to(self.dest) 16353167f56SPaolo Bonzini def __str__(self): 16453167f56SPaolo Bonzini value = 'y' if self.value else 'n' 16553167f56SPaolo Bonzini if self.cond is None: 16653167f56SPaolo Bonzini return "config %s default %s" % (self.dest, value) 16753167f56SPaolo Bonzini else: 16853167f56SPaolo Bonzini return "config %s default %s if %s" % (self.dest, value, self.cond) 16953167f56SPaolo Bonzini 170f7082a9aSPaolo Bonzini def priority(self): 171f7082a9aSPaolo Bonzini # Defaults are processed just before leaving the variable 172f7082a9aSPaolo Bonzini return -1 173f7082a9aSPaolo Bonzini def process(self): 174f7082a9aSPaolo Bonzini if not self.dest.has_value() and \ 175f7082a9aSPaolo Bonzini (self.cond is None or self.cond.evaluate()): 176f7082a9aSPaolo Bonzini self.dest.set_value(self.value, self) 177f7082a9aSPaolo Bonzini 17853167f56SPaolo Bonzini class DependsOnClause(Clause): 17953167f56SPaolo Bonzini def __init__(self, dest, expr): 18053167f56SPaolo Bonzini KconfigData.Clause.__init__(self, dest) 18153167f56SPaolo Bonzini self.expr = expr 182f7082a9aSPaolo Bonzini self.expr.add_edges_to(self.dest) 18353167f56SPaolo Bonzini def __str__(self): 18453167f56SPaolo Bonzini return "config %s depends on %s" % (self.dest, self.expr) 18553167f56SPaolo Bonzini 186f7082a9aSPaolo Bonzini def process(self): 187f7082a9aSPaolo Bonzini if not self.expr.evaluate(): 188f7082a9aSPaolo Bonzini self.dest.set_value(False, self) 189f7082a9aSPaolo Bonzini 19053167f56SPaolo Bonzini class SelectClause(Clause): 19153167f56SPaolo Bonzini def __init__(self, dest, cond): 19253167f56SPaolo Bonzini KconfigData.Clause.__init__(self, dest) 19353167f56SPaolo Bonzini self.cond = cond 194f7082a9aSPaolo Bonzini self.cond.add_edges_to(self.dest) 19553167f56SPaolo Bonzini def __str__(self): 19653167f56SPaolo Bonzini return "select %s if %s" % (self.dest, self.cond) 19753167f56SPaolo Bonzini 198f7082a9aSPaolo Bonzini def process(self): 199f7082a9aSPaolo Bonzini if self.cond.evaluate(): 200f7082a9aSPaolo Bonzini self.dest.set_value(True, self) 201f7082a9aSPaolo Bonzini 202f3494749SPaolo Bonzini def __init__(self, value_mangler=defconfig): 203f3494749SPaolo Bonzini self.value_mangler = value_mangler 204d4fdccadSPaolo Bonzini self.previously_included = [] 205d4fdccadSPaolo Bonzini self.incl_info = None 206d4fdccadSPaolo Bonzini self.defined_vars = set() 20753167f56SPaolo Bonzini self.referenced_vars = dict() 20853167f56SPaolo Bonzini self.clauses = list() 209d4fdccadSPaolo Bonzini 210d4fdccadSPaolo Bonzini # semantic analysis ------------- 211d4fdccadSPaolo Bonzini 212d4fdccadSPaolo Bonzini def check_undefined(self): 213d4fdccadSPaolo Bonzini undef = False 214d4fdccadSPaolo Bonzini for i in self.referenced_vars: 215d4fdccadSPaolo Bonzini if not (i in self.defined_vars): 216d4fdccadSPaolo Bonzini print("undefined symbol %s" % (i), file=sys.stderr) 217d4fdccadSPaolo Bonzini undef = True 218d4fdccadSPaolo Bonzini return undef 219d4fdccadSPaolo Bonzini 220f7082a9aSPaolo Bonzini def compute_config(self): 221f7082a9aSPaolo Bonzini if self.check_undefined(): 222f7082a9aSPaolo Bonzini raise KconfigDataError("there were undefined symbols") 223f7082a9aSPaolo Bonzini return None 224f7082a9aSPaolo Bonzini 225f7082a9aSPaolo Bonzini debug_print("Input:") 226f7082a9aSPaolo Bonzini for clause in self.clauses: 227f7082a9aSPaolo Bonzini debug_print(clause) 228f7082a9aSPaolo Bonzini 229f7082a9aSPaolo Bonzini debug_print("\nDependency graph:") 230f7082a9aSPaolo Bonzini for i in self.referenced_vars: 231f7082a9aSPaolo Bonzini debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing]) 232f7082a9aSPaolo Bonzini 233f7082a9aSPaolo Bonzini # The reverse of the depth-first order is the topological sort 234f7082a9aSPaolo Bonzini dfo = dict() 235f7082a9aSPaolo Bonzini visited = set() 236f7082a9aSPaolo Bonzini debug_print("\n") 237f7082a9aSPaolo Bonzini def visit_fn(var): 238f7082a9aSPaolo Bonzini debug_print(var, "has DFS number", len(dfo)) 239f7082a9aSPaolo Bonzini dfo[var] = len(dfo) 240f7082a9aSPaolo Bonzini 241f7082a9aSPaolo Bonzini for name, v in self.referenced_vars.items(): 242f7082a9aSPaolo Bonzini self.do_default(v, False) 243f7082a9aSPaolo Bonzini v.dfs(visited, visit_fn) 244f7082a9aSPaolo Bonzini 245f7082a9aSPaolo Bonzini # Put higher DFS numbers and higher priorities first. This 246f7082a9aSPaolo Bonzini # places the clauses in topological order and places defaults 247f7082a9aSPaolo Bonzini # after assignments and dependencies. 248f7082a9aSPaolo Bonzini self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority())) 249f7082a9aSPaolo Bonzini 250f7082a9aSPaolo Bonzini debug_print("\nSorted clauses:") 251f7082a9aSPaolo Bonzini for clause in self.clauses: 252f7082a9aSPaolo Bonzini debug_print(clause) 253f7082a9aSPaolo Bonzini clause.process() 254f7082a9aSPaolo Bonzini 255f7082a9aSPaolo Bonzini debug_print("") 256f7082a9aSPaolo Bonzini values = dict() 257f7082a9aSPaolo Bonzini for name, v in self.referenced_vars.items(): 258f7082a9aSPaolo Bonzini debug_print("Evaluating", name) 259f7082a9aSPaolo Bonzini values[name] = v.evaluate() 260f7082a9aSPaolo Bonzini 261f7082a9aSPaolo Bonzini return values 262f7082a9aSPaolo Bonzini 263d4fdccadSPaolo Bonzini # semantic actions ------------- 264d4fdccadSPaolo Bonzini 265d4fdccadSPaolo Bonzini def do_declaration(self, var): 266d4fdccadSPaolo Bonzini if (var in self.defined_vars): 267f7082a9aSPaolo Bonzini raise KconfigDataError('variable "' + var + '" defined twice') 268d4fdccadSPaolo Bonzini 26953167f56SPaolo Bonzini self.defined_vars.add(var.name) 270d4fdccadSPaolo Bonzini 271d4fdccadSPaolo Bonzini # var is a string with the variable's name. 272d4fdccadSPaolo Bonzini def do_var(self, var): 27353167f56SPaolo Bonzini if (var in self.referenced_vars): 27453167f56SPaolo Bonzini return self.referenced_vars[var] 27553167f56SPaolo Bonzini 27653167f56SPaolo Bonzini var_obj = self.referenced_vars[var] = KconfigData.Var(var) 27753167f56SPaolo Bonzini return var_obj 278d4fdccadSPaolo Bonzini 279d4fdccadSPaolo Bonzini def do_assignment(self, var, val): 28053167f56SPaolo Bonzini self.clauses.append(KconfigData.AssignmentClause(var, val)) 281d4fdccadSPaolo Bonzini 282d4fdccadSPaolo Bonzini def do_default(self, var, val, cond=None): 283f3494749SPaolo Bonzini val = self.value_mangler(val) 28453167f56SPaolo Bonzini self.clauses.append(KconfigData.DefaultClause(var, val, cond)) 285d4fdccadSPaolo Bonzini 286d4fdccadSPaolo Bonzini def do_depends_on(self, var, expr): 28753167f56SPaolo Bonzini self.clauses.append(KconfigData.DependsOnClause(var, expr)) 288d4fdccadSPaolo Bonzini 289d4fdccadSPaolo Bonzini def do_select(self, var, symbol, cond=None): 29053167f56SPaolo Bonzini cond = (cond & var) if cond is not None else var 29153167f56SPaolo Bonzini self.clauses.append(KconfigData.SelectClause(symbol, cond)) 292d4fdccadSPaolo Bonzini 293d4fdccadSPaolo Bonzini def do_imply(self, var, symbol, cond=None): 29453167f56SPaolo Bonzini # "config X imply Y [if COND]" is the same as 29553167f56SPaolo Bonzini # "config Y default y if X [&& COND]" 29653167f56SPaolo Bonzini cond = (cond & var) if cond is not None else var 29753167f56SPaolo Bonzini self.do_default(symbol, True, cond) 298d4fdccadSPaolo Bonzini 299d4fdccadSPaolo Bonzini# ------------------------------------------- 300d4fdccadSPaolo Bonzini# KconfigParser implements a recursive descent parser for (simplified) 301d4fdccadSPaolo Bonzini# Kconfig syntax. 302d4fdccadSPaolo Bonzini# ------------------------------------------- 303d4fdccadSPaolo Bonzini 304d4fdccadSPaolo Bonzini# tokens table 305d4fdccadSPaolo BonziniTOKENS = {} 306d4fdccadSPaolo BonziniTOK_NONE = -1 307d4fdccadSPaolo BonziniTOK_LPAREN = 0; TOKENS[TOK_LPAREN] = '"("'; 308d4fdccadSPaolo BonziniTOK_RPAREN = 1; TOKENS[TOK_RPAREN] = '")"'; 309d4fdccadSPaolo BonziniTOK_EQUAL = 2; TOKENS[TOK_EQUAL] = '"="'; 310d4fdccadSPaolo BonziniTOK_AND = 3; TOKENS[TOK_AND] = '"&&"'; 311d4fdccadSPaolo BonziniTOK_OR = 4; TOKENS[TOK_OR] = '"||"'; 312d4fdccadSPaolo BonziniTOK_NOT = 5; TOKENS[TOK_NOT] = '"!"'; 313d4fdccadSPaolo BonziniTOK_DEPENDS = 6; TOKENS[TOK_DEPENDS] = '"depends"'; 314d4fdccadSPaolo BonziniTOK_ON = 7; TOKENS[TOK_ON] = '"on"'; 315d4fdccadSPaolo BonziniTOK_SELECT = 8; TOKENS[TOK_SELECT] = '"select"'; 316d4fdccadSPaolo BonziniTOK_IMPLY = 9; TOKENS[TOK_IMPLY] = '"imply"'; 317d4fdccadSPaolo BonziniTOK_CONFIG = 10; TOKENS[TOK_CONFIG] = '"config"'; 318d4fdccadSPaolo BonziniTOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"'; 319d4fdccadSPaolo BonziniTOK_Y = 12; TOKENS[TOK_Y] = '"y"'; 320d4fdccadSPaolo BonziniTOK_N = 13; TOKENS[TOK_N] = '"n"'; 321d4fdccadSPaolo BonziniTOK_SOURCE = 14; TOKENS[TOK_SOURCE] = '"source"'; 322d4fdccadSPaolo BonziniTOK_BOOL = 15; TOKENS[TOK_BOOL] = '"bool"'; 323d4fdccadSPaolo BonziniTOK_IF = 16; TOKENS[TOK_IF] = '"if"'; 324d4fdccadSPaolo BonziniTOK_ID = 17; TOKENS[TOK_ID] = 'identifier'; 325d4fdccadSPaolo BonziniTOK_EOF = 18; TOKENS[TOK_EOF] = 'end of file'; 326d4fdccadSPaolo Bonzini 327d4fdccadSPaolo Bonziniclass KconfigParserError(Exception): 328d4fdccadSPaolo Bonzini def __init__(self, parser, msg, tok=None): 329d4fdccadSPaolo Bonzini self.loc = parser.location() 330d4fdccadSPaolo Bonzini tok = tok or parser.tok 331d4fdccadSPaolo Bonzini if tok != TOK_NONE: 332d4fdccadSPaolo Bonzini location = TOKENS.get(tok, None) or ('"%s"' % tok) 333d4fdccadSPaolo Bonzini msg = '%s before %s' % (msg, location) 334d4fdccadSPaolo Bonzini self.msg = msg 335d4fdccadSPaolo Bonzini 336d4fdccadSPaolo Bonzini def __str__(self): 337d4fdccadSPaolo Bonzini return "%s: %s" % (self.loc, self.msg) 338d4fdccadSPaolo Bonzini 339d4fdccadSPaolo Bonziniclass KconfigParser: 340f3494749SPaolo Bonzini 341d4fdccadSPaolo Bonzini @classmethod 342f3494749SPaolo Bonzini def parse(self, fp, mode=None): 343f3494749SPaolo Bonzini data = KconfigData(mode or KconfigParser.defconfig) 344d4fdccadSPaolo Bonzini parser = KconfigParser(data) 345d4fdccadSPaolo Bonzini parser.parse_file(fp) 346d4fdccadSPaolo Bonzini return data 347d4fdccadSPaolo Bonzini 348d4fdccadSPaolo Bonzini def __init__(self, data): 349d4fdccadSPaolo Bonzini self.data = data 350d4fdccadSPaolo Bonzini 351d4fdccadSPaolo Bonzini def parse_file(self, fp): 352d4fdccadSPaolo Bonzini self.abs_fname = os.path.abspath(fp.name) 353d4fdccadSPaolo Bonzini self.fname = fp.name 354d4fdccadSPaolo Bonzini self.data.previously_included.append(self.abs_fname) 355d4fdccadSPaolo Bonzini self.src = fp.read() 356d4fdccadSPaolo Bonzini if self.src == '' or self.src[-1] != '\n': 357d4fdccadSPaolo Bonzini self.src += '\n' 358d4fdccadSPaolo Bonzini self.cursor = 0 359d4fdccadSPaolo Bonzini self.line = 1 360d4fdccadSPaolo Bonzini self.line_pos = 0 361d4fdccadSPaolo Bonzini self.get_token() 362d4fdccadSPaolo Bonzini self.parse_config() 363d4fdccadSPaolo Bonzini 36482f51817SPaolo Bonzini def do_assignment(self, var, val): 36582f51817SPaolo Bonzini if not var.startswith("CONFIG_"): 36682f51817SPaolo Bonzini raise Error('assigned variable should start with CONFIG_') 36782f51817SPaolo Bonzini var = self.data.do_var(var[7:]) 36882f51817SPaolo Bonzini self.data.do_assignment(var, val) 36982f51817SPaolo Bonzini 370d4fdccadSPaolo Bonzini # file management ----- 371d4fdccadSPaolo Bonzini 372d4fdccadSPaolo Bonzini def error_path(self): 373d4fdccadSPaolo Bonzini inf = self.data.incl_info 374d4fdccadSPaolo Bonzini res = "" 375d4fdccadSPaolo Bonzini while inf: 376d4fdccadSPaolo Bonzini res = ("In file included from %s:%d:\n" % (inf['file'], 377d4fdccadSPaolo Bonzini inf['line'])) + res 378d4fdccadSPaolo Bonzini inf = inf['parent'] 379d4fdccadSPaolo Bonzini return res 380d4fdccadSPaolo Bonzini 381d4fdccadSPaolo Bonzini def location(self): 382d4fdccadSPaolo Bonzini col = 1 383d4fdccadSPaolo Bonzini for ch in self.src[self.line_pos:self.pos]: 384d4fdccadSPaolo Bonzini if ch == '\t': 385d4fdccadSPaolo Bonzini col += 8 - ((col - 1) % 8) 386d4fdccadSPaolo Bonzini else: 387d4fdccadSPaolo Bonzini col += 1 388d4fdccadSPaolo Bonzini return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col) 389d4fdccadSPaolo Bonzini 390d4fdccadSPaolo Bonzini def do_include(self, include): 391d4fdccadSPaolo Bonzini incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname), 392d4fdccadSPaolo Bonzini include) 393d4fdccadSPaolo Bonzini # catch inclusion cycle 394d4fdccadSPaolo Bonzini inf = self.data.incl_info 395d4fdccadSPaolo Bonzini while inf: 396d4fdccadSPaolo Bonzini if incl_abs_fname == os.path.abspath(inf['file']): 397d4fdccadSPaolo Bonzini raise KconfigParserError(self, "Inclusion loop for %s" 398d4fdccadSPaolo Bonzini % include) 399d4fdccadSPaolo Bonzini inf = inf['parent'] 400d4fdccadSPaolo Bonzini 401d4fdccadSPaolo Bonzini # skip multiple include of the same file 402d4fdccadSPaolo Bonzini if incl_abs_fname in self.data.previously_included: 403d4fdccadSPaolo Bonzini return 404d4fdccadSPaolo Bonzini try: 405ddd633e5SStefan Hajnoczi fp = open(incl_abs_fname, 'rt', encoding='utf-8') 406d4fdccadSPaolo Bonzini except IOError as e: 407d4fdccadSPaolo Bonzini raise KconfigParserError(self, 408d4fdccadSPaolo Bonzini '%s: %s' % (e.strerror, include)) 409d4fdccadSPaolo Bonzini 410d4fdccadSPaolo Bonzini inf = self.data.incl_info 411d4fdccadSPaolo Bonzini self.data.incl_info = { 'file': self.fname, 'line': self.line, 412d4fdccadSPaolo Bonzini 'parent': inf } 413d4fdccadSPaolo Bonzini KconfigParser(self.data).parse_file(fp) 414d4fdccadSPaolo Bonzini self.data.incl_info = inf 415d4fdccadSPaolo Bonzini 416d4fdccadSPaolo Bonzini # recursive descent parser ----- 417d4fdccadSPaolo Bonzini 418d4fdccadSPaolo Bonzini # y_or_n: Y | N 419d4fdccadSPaolo Bonzini def parse_y_or_n(self): 420d4fdccadSPaolo Bonzini if self.tok == TOK_Y: 421d4fdccadSPaolo Bonzini self.get_token() 422d4fdccadSPaolo Bonzini return True 423d4fdccadSPaolo Bonzini if self.tok == TOK_N: 424d4fdccadSPaolo Bonzini self.get_token() 425d4fdccadSPaolo Bonzini return False 426d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected "y" or "n"') 427d4fdccadSPaolo Bonzini 428d4fdccadSPaolo Bonzini # var: ID 429d4fdccadSPaolo Bonzini def parse_var(self): 430d4fdccadSPaolo Bonzini if self.tok == TOK_ID: 431d4fdccadSPaolo Bonzini val = self.val 432d4fdccadSPaolo Bonzini self.get_token() 433d4fdccadSPaolo Bonzini return self.data.do_var(val) 434d4fdccadSPaolo Bonzini else: 435d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected identifier') 436d4fdccadSPaolo Bonzini 437d4fdccadSPaolo Bonzini # assignment_var: ID (starting with "CONFIG_") 438d4fdccadSPaolo Bonzini def parse_assignment_var(self): 439d4fdccadSPaolo Bonzini if self.tok == TOK_ID: 440d4fdccadSPaolo Bonzini val = self.val 441d4fdccadSPaolo Bonzini if not val.startswith("CONFIG_"): 442d4fdccadSPaolo Bonzini raise KconfigParserError(self, 443d4fdccadSPaolo Bonzini 'Expected identifier starting with "CONFIG_"', TOK_NONE) 444d4fdccadSPaolo Bonzini self.get_token() 445d4fdccadSPaolo Bonzini return self.data.do_var(val[7:]) 446d4fdccadSPaolo Bonzini else: 447d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected identifier') 448d4fdccadSPaolo Bonzini 449d4fdccadSPaolo Bonzini # assignment: var EQUAL y_or_n 450d4fdccadSPaolo Bonzini def parse_assignment(self): 451d4fdccadSPaolo Bonzini var = self.parse_assignment_var() 452d4fdccadSPaolo Bonzini if self.tok != TOK_EQUAL: 453d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected "="') 454d4fdccadSPaolo Bonzini self.get_token() 455d4fdccadSPaolo Bonzini self.data.do_assignment(var, self.parse_y_or_n()) 456d4fdccadSPaolo Bonzini 457d4fdccadSPaolo Bonzini # primary: NOT primary 458d4fdccadSPaolo Bonzini # | LPAREN expr RPAREN 459d4fdccadSPaolo Bonzini # | var 460d4fdccadSPaolo Bonzini def parse_primary(self): 461d4fdccadSPaolo Bonzini if self.tok == TOK_NOT: 462d4fdccadSPaolo Bonzini self.get_token() 46353167f56SPaolo Bonzini val = ~self.parse_primary() 464d4fdccadSPaolo Bonzini elif self.tok == TOK_LPAREN: 465d4fdccadSPaolo Bonzini self.get_token() 46653167f56SPaolo Bonzini val = self.parse_expr() 467d4fdccadSPaolo Bonzini if self.tok != TOK_RPAREN: 468d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected ")"') 469d4fdccadSPaolo Bonzini self.get_token() 470d4fdccadSPaolo Bonzini elif self.tok == TOK_ID: 47153167f56SPaolo Bonzini val = self.parse_var() 472d4fdccadSPaolo Bonzini else: 473d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected "!" or "(" or identifier') 47453167f56SPaolo Bonzini return val 475d4fdccadSPaolo Bonzini 476d4fdccadSPaolo Bonzini # disj: primary (OR primary)* 477d4fdccadSPaolo Bonzini def parse_disj(self): 47853167f56SPaolo Bonzini lhs = self.parse_primary() 479d4fdccadSPaolo Bonzini while self.tok == TOK_OR: 480d4fdccadSPaolo Bonzini self.get_token() 48153167f56SPaolo Bonzini lhs = lhs | self.parse_primary() 48253167f56SPaolo Bonzini return lhs 483d4fdccadSPaolo Bonzini 484d4fdccadSPaolo Bonzini # expr: disj (AND disj)* 485d4fdccadSPaolo Bonzini def parse_expr(self): 48653167f56SPaolo Bonzini lhs = self.parse_disj() 487d4fdccadSPaolo Bonzini while self.tok == TOK_AND: 488d4fdccadSPaolo Bonzini self.get_token() 48953167f56SPaolo Bonzini lhs = lhs & self.parse_disj() 49053167f56SPaolo Bonzini return lhs 491d4fdccadSPaolo Bonzini 492d4fdccadSPaolo Bonzini # condition: IF expr 493d4fdccadSPaolo Bonzini # | empty 494d4fdccadSPaolo Bonzini def parse_condition(self): 495d4fdccadSPaolo Bonzini if self.tok == TOK_IF: 496d4fdccadSPaolo Bonzini self.get_token() 497d4fdccadSPaolo Bonzini return self.parse_expr() 498d4fdccadSPaolo Bonzini else: 499d4fdccadSPaolo Bonzini return None 500d4fdccadSPaolo Bonzini 501d4fdccadSPaolo Bonzini # property: DEFAULT y_or_n condition 502d4fdccadSPaolo Bonzini # | DEPENDS ON expr 503d4fdccadSPaolo Bonzini # | SELECT var condition 504d4fdccadSPaolo Bonzini # | BOOL 505d4fdccadSPaolo Bonzini def parse_property(self, var): 506d4fdccadSPaolo Bonzini if self.tok == TOK_DEFAULT: 507d4fdccadSPaolo Bonzini self.get_token() 508d4fdccadSPaolo Bonzini val = self.parse_y_or_n() 509d4fdccadSPaolo Bonzini cond = self.parse_condition() 510d4fdccadSPaolo Bonzini self.data.do_default(var, val, cond) 511d4fdccadSPaolo Bonzini elif self.tok == TOK_DEPENDS: 512d4fdccadSPaolo Bonzini self.get_token() 513d4fdccadSPaolo Bonzini if self.tok != TOK_ON: 514d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Expected "on"') 515d4fdccadSPaolo Bonzini self.get_token() 516d4fdccadSPaolo Bonzini self.data.do_depends_on(var, self.parse_expr()) 517d4fdccadSPaolo Bonzini elif self.tok == TOK_SELECT: 518d4fdccadSPaolo Bonzini self.get_token() 519d4fdccadSPaolo Bonzini symbol = self.parse_var() 520d4fdccadSPaolo Bonzini cond = self.parse_condition() 521d4fdccadSPaolo Bonzini self.data.do_select(var, symbol, cond) 522d4fdccadSPaolo Bonzini elif self.tok == TOK_IMPLY: 523d4fdccadSPaolo Bonzini self.get_token() 524d4fdccadSPaolo Bonzini symbol = self.parse_var() 525d4fdccadSPaolo Bonzini cond = self.parse_condition() 526d4fdccadSPaolo Bonzini self.data.do_imply(var, symbol, cond) 527d4fdccadSPaolo Bonzini elif self.tok == TOK_BOOL: 528d4fdccadSPaolo Bonzini self.get_token() 529d4fdccadSPaolo Bonzini else: 530d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Error in recursive descent?') 531d4fdccadSPaolo Bonzini 532d4fdccadSPaolo Bonzini # properties: properties property 533d4fdccadSPaolo Bonzini # | /* empty */ 534d4fdccadSPaolo Bonzini def parse_properties(self, var): 535d4fdccadSPaolo Bonzini had_default = False 536d4fdccadSPaolo Bonzini while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \ 537d4fdccadSPaolo Bonzini self.tok == TOK_SELECT or self.tok == TOK_BOOL or \ 538d4fdccadSPaolo Bonzini self.tok == TOK_IMPLY: 539d4fdccadSPaolo Bonzini self.parse_property(var) 540d4fdccadSPaolo Bonzini 541d4fdccadSPaolo Bonzini # for nicer error message 542d4fdccadSPaolo Bonzini if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \ 543d4fdccadSPaolo Bonzini self.tok != TOK_ID and self.tok != TOK_EOF: 544d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'expected "source", "config", identifier, ' 545d4fdccadSPaolo Bonzini + '"default", "depends on", "imply" or "select"') 546d4fdccadSPaolo Bonzini 547d4fdccadSPaolo Bonzini # declaration: config var properties 548d4fdccadSPaolo Bonzini def parse_declaration(self): 549d4fdccadSPaolo Bonzini if self.tok == TOK_CONFIG: 550d4fdccadSPaolo Bonzini self.get_token() 551d4fdccadSPaolo Bonzini var = self.parse_var() 552d4fdccadSPaolo Bonzini self.data.do_declaration(var) 553d4fdccadSPaolo Bonzini self.parse_properties(var) 554d4fdccadSPaolo Bonzini else: 555d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'Error in recursive descent?') 556d4fdccadSPaolo Bonzini 557d4fdccadSPaolo Bonzini # clause: SOURCE 558d4fdccadSPaolo Bonzini # | declaration 559d4fdccadSPaolo Bonzini # | assignment 560d4fdccadSPaolo Bonzini def parse_clause(self): 561d4fdccadSPaolo Bonzini if self.tok == TOK_SOURCE: 562d4fdccadSPaolo Bonzini val = self.val 563d4fdccadSPaolo Bonzini self.get_token() 564d4fdccadSPaolo Bonzini self.do_include(val) 565d4fdccadSPaolo Bonzini elif self.tok == TOK_CONFIG: 566d4fdccadSPaolo Bonzini self.parse_declaration() 567d4fdccadSPaolo Bonzini elif self.tok == TOK_ID: 568d4fdccadSPaolo Bonzini self.parse_assignment() 569d4fdccadSPaolo Bonzini else: 570d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'expected "source", "config" or identifier') 571d4fdccadSPaolo Bonzini 572d4fdccadSPaolo Bonzini # config: clause+ EOF 573d4fdccadSPaolo Bonzini def parse_config(self): 574d4fdccadSPaolo Bonzini while self.tok != TOK_EOF: 575d4fdccadSPaolo Bonzini self.parse_clause() 576d4fdccadSPaolo Bonzini return self.data 577d4fdccadSPaolo Bonzini 578d4fdccadSPaolo Bonzini # scanner ----- 579d4fdccadSPaolo Bonzini 580d4fdccadSPaolo Bonzini def get_token(self): 581d4fdccadSPaolo Bonzini while True: 582d4fdccadSPaolo Bonzini self.tok = self.src[self.cursor] 583d4fdccadSPaolo Bonzini self.pos = self.cursor 584d4fdccadSPaolo Bonzini self.cursor += 1 585d4fdccadSPaolo Bonzini 586d4fdccadSPaolo Bonzini self.val = None 587d4fdccadSPaolo Bonzini self.tok = self.scan_token() 588d4fdccadSPaolo Bonzini if self.tok is not None: 589d4fdccadSPaolo Bonzini return 590d4fdccadSPaolo Bonzini 591d4fdccadSPaolo Bonzini def check_keyword(self, rest): 592d4fdccadSPaolo Bonzini if not self.src.startswith(rest, self.cursor): 593d4fdccadSPaolo Bonzini return False 594d4fdccadSPaolo Bonzini length = len(rest) 59567163caeSPaolo Bonzini if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '_': 596d4fdccadSPaolo Bonzini return False 597d4fdccadSPaolo Bonzini self.cursor += length 598d4fdccadSPaolo Bonzini return True 599d4fdccadSPaolo Bonzini 600d4fdccadSPaolo Bonzini def scan_token(self): 601d4fdccadSPaolo Bonzini if self.tok == '#': 602d4fdccadSPaolo Bonzini self.cursor = self.src.find('\n', self.cursor) 603d4fdccadSPaolo Bonzini return None 604d4fdccadSPaolo Bonzini elif self.tok == '=': 605d4fdccadSPaolo Bonzini return TOK_EQUAL 606d4fdccadSPaolo Bonzini elif self.tok == '(': 607d4fdccadSPaolo Bonzini return TOK_LPAREN 608d4fdccadSPaolo Bonzini elif self.tok == ')': 609d4fdccadSPaolo Bonzini return TOK_RPAREN 610d4fdccadSPaolo Bonzini elif self.tok == '&' and self.src[self.pos+1] == '&': 611d4fdccadSPaolo Bonzini self.cursor += 1 612d4fdccadSPaolo Bonzini return TOK_AND 613d4fdccadSPaolo Bonzini elif self.tok == '|' and self.src[self.pos+1] == '|': 614d4fdccadSPaolo Bonzini self.cursor += 1 615d4fdccadSPaolo Bonzini return TOK_OR 616d4fdccadSPaolo Bonzini elif self.tok == '!': 617d4fdccadSPaolo Bonzini return TOK_NOT 618d4fdccadSPaolo Bonzini elif self.tok == 'd' and self.check_keyword("epends"): 619d4fdccadSPaolo Bonzini return TOK_DEPENDS 620d4fdccadSPaolo Bonzini elif self.tok == 'o' and self.check_keyword("n"): 621d4fdccadSPaolo Bonzini return TOK_ON 622d4fdccadSPaolo Bonzini elif self.tok == 's' and self.check_keyword("elect"): 623d4fdccadSPaolo Bonzini return TOK_SELECT 624d4fdccadSPaolo Bonzini elif self.tok == 'i' and self.check_keyword("mply"): 625d4fdccadSPaolo Bonzini return TOK_IMPLY 626d4fdccadSPaolo Bonzini elif self.tok == 'c' and self.check_keyword("onfig"): 627d4fdccadSPaolo Bonzini return TOK_CONFIG 628d4fdccadSPaolo Bonzini elif self.tok == 'd' and self.check_keyword("efault"): 629d4fdccadSPaolo Bonzini return TOK_DEFAULT 630d4fdccadSPaolo Bonzini elif self.tok == 'b' and self.check_keyword("ool"): 631d4fdccadSPaolo Bonzini return TOK_BOOL 632d4fdccadSPaolo Bonzini elif self.tok == 'i' and self.check_keyword("f"): 633d4fdccadSPaolo Bonzini return TOK_IF 634d4fdccadSPaolo Bonzini elif self.tok == 'y' and self.check_keyword(""): 635d4fdccadSPaolo Bonzini return TOK_Y 636d4fdccadSPaolo Bonzini elif self.tok == 'n' and self.check_keyword(""): 637d4fdccadSPaolo Bonzini return TOK_N 638d4fdccadSPaolo Bonzini elif (self.tok == 's' and self.check_keyword("ource")) or \ 639d4fdccadSPaolo Bonzini self.tok == 'i' and self.check_keyword("nclude"): 640d4fdccadSPaolo Bonzini # source FILENAME 641d4fdccadSPaolo Bonzini # include FILENAME 642d4fdccadSPaolo Bonzini while self.src[self.cursor].isspace(): 643d4fdccadSPaolo Bonzini self.cursor += 1 644d4fdccadSPaolo Bonzini start = self.cursor 645d4fdccadSPaolo Bonzini self.cursor = self.src.find('\n', self.cursor) 646d4fdccadSPaolo Bonzini self.val = self.src[start:self.cursor] 647d4fdccadSPaolo Bonzini return TOK_SOURCE 6481b29af2fSMarc-André Lureau elif self.tok.isalnum(): 649d4fdccadSPaolo Bonzini # identifier 650d4fdccadSPaolo Bonzini while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_': 651d4fdccadSPaolo Bonzini self.cursor += 1 652d4fdccadSPaolo Bonzini self.val = self.src[self.pos:self.cursor] 653d4fdccadSPaolo Bonzini return TOK_ID 654d4fdccadSPaolo Bonzini elif self.tok == '\n': 655d4fdccadSPaolo Bonzini if self.cursor == len(self.src): 656d4fdccadSPaolo Bonzini return TOK_EOF 657d4fdccadSPaolo Bonzini self.line += 1 658d4fdccadSPaolo Bonzini self.line_pos = self.cursor 659d4fdccadSPaolo Bonzini elif not self.tok.isspace(): 660d4fdccadSPaolo Bonzini raise KconfigParserError(self, 'invalid input') 661d4fdccadSPaolo Bonzini 662d4fdccadSPaolo Bonzini return None 663d4fdccadSPaolo Bonzini 664d4fdccadSPaolo Bonziniif __name__ == '__main__': 66582f51817SPaolo Bonzini argv = sys.argv 666f3494749SPaolo Bonzini mode = defconfig 667f3494749SPaolo Bonzini if len(sys.argv) > 1: 668f3494749SPaolo Bonzini if argv[1] == '--defconfig': 669f3494749SPaolo Bonzini del argv[1] 670f3494749SPaolo Bonzini elif argv[1] == '--randconfig': 671f3494749SPaolo Bonzini random.seed() 672f3494749SPaolo Bonzini mode = randconfig 673f3494749SPaolo Bonzini del argv[1] 674f3494749SPaolo Bonzini elif argv[1] == '--allyesconfig': 675f3494749SPaolo Bonzini mode = allyesconfig 676f3494749SPaolo Bonzini del argv[1] 677f3494749SPaolo Bonzini elif argv[1] == '--allnoconfig': 678f3494749SPaolo Bonzini mode = allnoconfig 679f3494749SPaolo Bonzini del argv[1] 680f3494749SPaolo Bonzini 68182f51817SPaolo Bonzini if len(argv) == 1: 68282f51817SPaolo Bonzini print ("%s: at least one argument is required" % argv[0], file=sys.stderr) 68382f51817SPaolo Bonzini sys.exit(1) 68482f51817SPaolo Bonzini 685f3494749SPaolo Bonzini if argv[1].startswith('-'): 686f3494749SPaolo Bonzini print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr) 687f3494749SPaolo Bonzini sys.exit(1) 688f3494749SPaolo Bonzini 689f3494749SPaolo Bonzini data = KconfigData(mode) 69082f51817SPaolo Bonzini parser = KconfigParser(data) 6916b7ac49dSPaolo Bonzini external_vars = set() 69282f51817SPaolo Bonzini for arg in argv[3:]: 69382f51817SPaolo Bonzini m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg) 69482f51817SPaolo Bonzini if m is not None: 69582f51817SPaolo Bonzini name, value = m.groups() 69682f51817SPaolo Bonzini parser.do_assignment(name, value == 'y') 6976b7ac49dSPaolo Bonzini external_vars.add(name[7:]) 69882f51817SPaolo Bonzini else: 699ddd633e5SStefan Hajnoczi fp = open(arg, 'rt', encoding='utf-8') 70082f51817SPaolo Bonzini parser.parse_file(fp) 70182f51817SPaolo Bonzini fp.close() 70282f51817SPaolo Bonzini 70382f51817SPaolo Bonzini config = data.compute_config() 70482f51817SPaolo Bonzini for key in sorted(config.keys()): 705c5b35f03SMarc-André Lureau if key not in external_vars and config[key]: 706c5b35f03SMarc-André Lureau print ('CONFIG_%s=y' % key) 70782f51817SPaolo Bonzini 708ddd633e5SStefan Hajnoczi deps = open(argv[2], 'wt', encoding='utf-8') 70982f51817SPaolo Bonzini for fname in data.previously_included: 71082f51817SPaolo Bonzini print ('%s: %s' % (argv[1], fname), file=deps) 71182f51817SPaolo Bonzini deps.close() 712