OSDN Git Service

ide/ioport: move ide_portio_list[] and ide_portio_list2[] definitions to IDE core
[qmiga/qemu.git] / scripts / minikconf.py
1 #!/usr/bin/env python3
2 #
3 # Mini-Kconfig parser
4 #
5 # Copyright (c) 2015 Red Hat Inc.
6 #
7 # Authors:
8 #  Paolo Bonzini <pbonzini@redhat.com>
9 #
10 # This work is licensed under the terms of the GNU GPL, version 2
11 # or, at your option, any later version.  See the COPYING file in
12 # the top-level directory.
13
14 import os
15 import sys
16 import re
17 import random
18
19 __all__ = [ 'KconfigDataError', 'KconfigParserError',
20             'KconfigData', 'KconfigParser' ,
21             'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ]
22
23 def debug_print(*args):
24     #print('# ' + (' '.join(str(x) for x in args)))
25     pass
26
27 # -------------------------------------------
28 # KconfigData implements the Kconfig semantics.  For now it can only
29 # detect undefined symbols, i.e. symbols that were referenced in
30 # assignments or dependencies but were not declared with "config FOO".
31 #
32 # Semantic actions are represented by methods called do_*.  The do_var
33 # method return the semantic value of a variable (which right now is
34 # just its name).
35 # -------------------------------------------
36
37 class KconfigDataError(Exception):
38     def __init__(self, msg):
39         self.msg = msg
40
41     def __str__(self):
42         return self.msg
43
44 allyesconfig = lambda x: True
45 allnoconfig = lambda x: False
46 defconfig = lambda x: x
47 randconfig = lambda x: random.randint(0, 1) == 1
48
49 class KconfigData:
50     class Expr:
51         def __and__(self, rhs):
52             return KconfigData.AND(self, rhs)
53         def __or__(self, rhs):
54             return KconfigData.OR(self, rhs)
55         def __invert__(self):
56             return KconfigData.NOT(self)
57
58         # Abstract methods
59         def add_edges_to(self, var):
60             pass
61         def evaluate(self):
62             assert False
63
64     class AND(Expr):
65         def __init__(self, lhs, rhs):
66             self.lhs = lhs
67             self.rhs = rhs
68         def __str__(self):
69             return "(%s && %s)" % (self.lhs, self.rhs)
70
71         def add_edges_to(self, var):
72             self.lhs.add_edges_to(var)
73             self.rhs.add_edges_to(var)
74         def evaluate(self):
75             return self.lhs.evaluate() and self.rhs.evaluate()
76
77     class OR(Expr):
78         def __init__(self, lhs, rhs):
79             self.lhs = lhs
80             self.rhs = rhs
81         def __str__(self):
82             return "(%s || %s)" % (self.lhs, self.rhs)
83
84         def add_edges_to(self, var):
85             self.lhs.add_edges_to(var)
86             self.rhs.add_edges_to(var)
87         def evaluate(self):
88             return self.lhs.evaluate() or self.rhs.evaluate()
89
90     class NOT(Expr):
91         def __init__(self, lhs):
92             self.lhs = lhs
93         def __str__(self):
94             return "!%s" % (self.lhs)
95
96         def add_edges_to(self, var):
97             self.lhs.add_edges_to(var)
98         def evaluate(self):
99             return not self.lhs.evaluate()
100
101     class Var(Expr):
102         def __init__(self, name):
103             self.name = name
104             self.value = None
105             self.outgoing = set()
106             self.clauses_for_var = list()
107         def __str__(self):
108             return self.name
109
110         def has_value(self):
111             return not (self.value is None)
112         def set_value(self, val, clause):
113             self.clauses_for_var.append(clause)
114             if self.has_value() and self.value != val:
115                 print("The following clauses were found for " + self.name)
116                 for i in self.clauses_for_var:
117                     print("    " + str(i), file=sys.stderr)
118                 raise KconfigDataError('contradiction between clauses when setting %s' % self)
119             debug_print("=> %s is now %s" % (self.name, val))
120             self.value = val
121
122         # depth first search of the dependency graph
123         def dfs(self, visited, f):
124             if self in visited:
125                 return
126             visited.add(self)
127             for v in self.outgoing:
128                 v.dfs(visited, f)
129             f(self)
130
131         def add_edges_to(self, var):
132             self.outgoing.add(var)
133         def evaluate(self):
134             if not self.has_value():
135                 raise KconfigDataError('cycle found including %s' % self)
136             return self.value
137
138     class Clause:
139         def __init__(self, dest):
140             self.dest = dest
141         def priority(self):
142             return 0
143         def process(self):
144             pass
145
146     class AssignmentClause(Clause):
147         def __init__(self, dest, value):
148             KconfigData.Clause.__init__(self, dest)
149             self.value = value
150         def __str__(self):
151             return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n')
152
153         def process(self):
154             self.dest.set_value(self.value, self)
155
156     class DefaultClause(Clause):
157         def __init__(self, dest, value, cond=None):
158             KconfigData.Clause.__init__(self, dest)
159             self.value = value
160             self.cond = cond
161             if not (self.cond is None):
162                 self.cond.add_edges_to(self.dest)
163         def __str__(self):
164             value = 'y' if self.value else 'n'
165             if self.cond is None:
166                 return "config %s default %s" % (self.dest, value)
167             else:
168                 return "config %s default %s if %s" % (self.dest, value, self.cond)
169
170         def priority(self):
171             # Defaults are processed just before leaving the variable
172             return -1
173         def process(self):
174             if not self.dest.has_value() and \
175                     (self.cond is None or self.cond.evaluate()):
176                 self.dest.set_value(self.value, self)
177
178     class DependsOnClause(Clause):
179         def __init__(self, dest, expr):
180             KconfigData.Clause.__init__(self, dest)
181             self.expr = expr
182             self.expr.add_edges_to(self.dest)
183         def __str__(self):
184             return "config %s depends on %s" % (self.dest, self.expr)
185
186         def process(self):
187             if not self.expr.evaluate():
188                 self.dest.set_value(False, self)
189
190     class SelectClause(Clause):
191         def __init__(self, dest, cond):
192             KconfigData.Clause.__init__(self, dest)
193             self.cond = cond
194             self.cond.add_edges_to(self.dest)
195         def __str__(self):
196             return "select %s if %s" % (self.dest, self.cond)
197
198         def process(self):
199             if self.cond.evaluate():
200                 self.dest.set_value(True, self)
201
202     def __init__(self, value_mangler=defconfig):
203         self.value_mangler = value_mangler
204         self.previously_included = []
205         self.incl_info = None
206         self.defined_vars = set()
207         self.referenced_vars = dict()
208         self.clauses = list()
209
210     # semantic analysis -------------
211
212     def check_undefined(self):
213         undef = False
214         for i in self.referenced_vars:
215             if not (i in self.defined_vars):
216                 print("undefined symbol %s" % (i), file=sys.stderr)
217                 undef = True
218         return undef
219
220     def compute_config(self):
221         if self.check_undefined():
222             raise KconfigDataError("there were undefined symbols")
223             return None
224
225         debug_print("Input:")
226         for clause in self.clauses:
227             debug_print(clause)
228
229         debug_print("\nDependency graph:")
230         for i in self.referenced_vars:
231             debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing])
232
233         # The reverse of the depth-first order is the topological sort
234         dfo = dict()
235         visited = set()
236         debug_print("\n")
237         def visit_fn(var):
238             debug_print(var, "has DFS number", len(dfo))
239             dfo[var] = len(dfo)
240
241         for name, v in self.referenced_vars.items():
242             self.do_default(v, False)
243             v.dfs(visited, visit_fn)
244
245         # Put higher DFS numbers and higher priorities first.  This
246         # places the clauses in topological order and places defaults
247         # after assignments and dependencies.
248         self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority()))
249
250         debug_print("\nSorted clauses:")
251         for clause in self.clauses:
252             debug_print(clause)
253             clause.process()
254
255         debug_print("")
256         values = dict()
257         for name, v in self.referenced_vars.items():
258             debug_print("Evaluating", name)
259             values[name] = v.evaluate()
260
261         return values
262
263     # semantic actions -------------
264
265     def do_declaration(self, var):
266         if (var in self.defined_vars):
267             raise KconfigDataError('variable "' + var + '" defined twice')
268
269         self.defined_vars.add(var.name)
270
271     # var is a string with the variable's name.
272     def do_var(self, var):
273         if (var in self.referenced_vars):
274             return self.referenced_vars[var]
275
276         var_obj = self.referenced_vars[var] = KconfigData.Var(var)
277         return var_obj
278
279     def do_assignment(self, var, val):
280         self.clauses.append(KconfigData.AssignmentClause(var, val))
281
282     def do_default(self, var, val, cond=None):
283         val = self.value_mangler(val)
284         self.clauses.append(KconfigData.DefaultClause(var, val, cond))
285
286     def do_depends_on(self, var, expr):
287         self.clauses.append(KconfigData.DependsOnClause(var, expr))
288
289     def do_select(self, var, symbol, cond=None):
290         cond = (cond & var) if cond is not None else var
291         self.clauses.append(KconfigData.SelectClause(symbol, cond))
292
293     def do_imply(self, var, symbol, cond=None):
294         # "config X imply Y [if COND]" is the same as
295         # "config Y default y if X [&& COND]"
296         cond = (cond & var) if cond is not None else var
297         self.do_default(symbol, True, cond)
298
299 # -------------------------------------------
300 # KconfigParser implements a recursive descent parser for (simplified)
301 # Kconfig syntax.
302 # -------------------------------------------
303
304 # tokens table
305 TOKENS = {}
306 TOK_NONE = -1
307 TOK_LPAREN = 0;   TOKENS[TOK_LPAREN] = '"("';
308 TOK_RPAREN = 1;   TOKENS[TOK_RPAREN] = '")"';
309 TOK_EQUAL = 2;    TOKENS[TOK_EQUAL] = '"="';
310 TOK_AND = 3;      TOKENS[TOK_AND] = '"&&"';
311 TOK_OR = 4;       TOKENS[TOK_OR] = '"||"';
312 TOK_NOT = 5;      TOKENS[TOK_NOT] = '"!"';
313 TOK_DEPENDS = 6;  TOKENS[TOK_DEPENDS] = '"depends"';
314 TOK_ON = 7;       TOKENS[TOK_ON] = '"on"';
315 TOK_SELECT = 8;   TOKENS[TOK_SELECT] = '"select"';
316 TOK_IMPLY = 9;    TOKENS[TOK_IMPLY] = '"imply"';
317 TOK_CONFIG = 10;  TOKENS[TOK_CONFIG] = '"config"';
318 TOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"';
319 TOK_Y = 12;       TOKENS[TOK_Y] = '"y"';
320 TOK_N = 13;       TOKENS[TOK_N] = '"n"';
321 TOK_SOURCE = 14;  TOKENS[TOK_SOURCE] = '"source"';
322 TOK_BOOL = 15;    TOKENS[TOK_BOOL] = '"bool"';
323 TOK_IF = 16;      TOKENS[TOK_IF] = '"if"';
324 TOK_ID = 17;      TOKENS[TOK_ID] = 'identifier';
325 TOK_EOF = 18;     TOKENS[TOK_EOF] = 'end of file';
326
327 class KconfigParserError(Exception):
328     def __init__(self, parser, msg, tok=None):
329         self.loc = parser.location()
330         tok = tok or parser.tok
331         if tok != TOK_NONE:
332             location = TOKENS.get(tok, None) or ('"%s"' % tok)
333             msg = '%s before %s' % (msg, location)
334         self.msg = msg
335
336     def __str__(self):
337         return "%s: %s" % (self.loc, self.msg)
338
339 class KconfigParser:
340
341     @classmethod
342     def parse(self, fp, mode=None):
343         data = KconfigData(mode or KconfigParser.defconfig)
344         parser = KconfigParser(data)
345         parser.parse_file(fp)
346         return data
347
348     def __init__(self, data):
349         self.data = data
350
351     def parse_file(self, fp):
352         self.abs_fname = os.path.abspath(fp.name)
353         self.fname = fp.name
354         self.data.previously_included.append(self.abs_fname)
355         self.src = fp.read()
356         if self.src == '' or self.src[-1] != '\n':
357             self.src += '\n'
358         self.cursor = 0
359         self.line = 1
360         self.line_pos = 0
361         self.get_token()
362         self.parse_config()
363
364     def do_assignment(self, var, val):
365         if not var.startswith("CONFIG_"):
366             raise Error('assigned variable should start with CONFIG_')
367         var = self.data.do_var(var[7:])
368         self.data.do_assignment(var, val)
369
370     # file management -----
371
372     def error_path(self):
373         inf = self.data.incl_info
374         res = ""
375         while inf:
376             res = ("In file included from %s:%d:\n" % (inf['file'],
377                                                        inf['line'])) + res
378             inf = inf['parent']
379         return res
380
381     def location(self):
382         col = 1
383         for ch in self.src[self.line_pos:self.pos]:
384             if ch == '\t':
385                 col += 8 - ((col - 1) % 8)
386             else:
387                 col += 1
388         return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col)
389
390     def do_include(self, include):
391         incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname),
392                                       include)
393         # catch inclusion cycle
394         inf = self.data.incl_info
395         while inf:
396             if incl_abs_fname == os.path.abspath(inf['file']):
397                 raise KconfigParserError(self, "Inclusion loop for %s"
398                                     % include)
399             inf = inf['parent']
400
401         # skip multiple include of the same file
402         if incl_abs_fname in self.data.previously_included:
403             return
404         try:
405             fp = open(incl_abs_fname, 'rt', encoding='utf-8')
406         except IOError as e:
407             raise KconfigParserError(self,
408                                 '%s: %s' % (e.strerror, include))
409
410         inf = self.data.incl_info
411         self.data.incl_info = { 'file': self.fname, 'line': self.line,
412                 'parent': inf }
413         KconfigParser(self.data).parse_file(fp)
414         self.data.incl_info = inf
415
416     # recursive descent parser -----
417
418     # y_or_n: Y | N
419     def parse_y_or_n(self):
420         if self.tok == TOK_Y:
421             self.get_token()
422             return True
423         if self.tok == TOK_N:
424             self.get_token()
425             return False
426         raise KconfigParserError(self, 'Expected "y" or "n"')
427
428     # var: ID
429     def parse_var(self):
430         if self.tok == TOK_ID:
431             val = self.val
432             self.get_token()
433             return self.data.do_var(val)
434         else:
435             raise KconfigParserError(self, 'Expected identifier')
436
437     # assignment_var: ID (starting with "CONFIG_")
438     def parse_assignment_var(self):
439         if self.tok == TOK_ID:
440             val = self.val
441             if not val.startswith("CONFIG_"):
442                 raise KconfigParserError(self,
443                            'Expected identifier starting with "CONFIG_"', TOK_NONE)
444             self.get_token()
445             return self.data.do_var(val[7:])
446         else:
447             raise KconfigParserError(self, 'Expected identifier')
448
449     # assignment: var EQUAL y_or_n
450     def parse_assignment(self):
451         var = self.parse_assignment_var()
452         if self.tok != TOK_EQUAL:
453             raise KconfigParserError(self, 'Expected "="')
454         self.get_token()
455         self.data.do_assignment(var, self.parse_y_or_n())
456
457     # primary: NOT primary
458     #       | LPAREN expr RPAREN
459     #       | var
460     def parse_primary(self):
461         if self.tok == TOK_NOT:
462             self.get_token()
463             val = ~self.parse_primary()
464         elif self.tok == TOK_LPAREN:
465             self.get_token()
466             val = self.parse_expr()
467             if self.tok != TOK_RPAREN:
468                 raise KconfigParserError(self, 'Expected ")"')
469             self.get_token()
470         elif self.tok == TOK_ID:
471             val = self.parse_var()
472         else:
473             raise KconfigParserError(self, 'Expected "!" or "(" or identifier')
474         return val
475
476     # disj: primary (OR primary)*
477     def parse_disj(self):
478         lhs = self.parse_primary()
479         while self.tok == TOK_OR:
480             self.get_token()
481             lhs = lhs | self.parse_primary()
482         return lhs
483
484     # expr: disj (AND disj)*
485     def parse_expr(self):
486         lhs = self.parse_disj()
487         while self.tok == TOK_AND:
488             self.get_token()
489             lhs = lhs & self.parse_disj()
490         return lhs
491
492     # condition: IF expr
493     #       | empty
494     def parse_condition(self):
495         if self.tok == TOK_IF:
496             self.get_token()
497             return self.parse_expr()
498         else:
499             return None
500
501     # property: DEFAULT y_or_n condition
502     #       | DEPENDS ON expr
503     #       | SELECT var condition
504     #       | BOOL
505     def parse_property(self, var):
506         if self.tok == TOK_DEFAULT:
507             self.get_token()
508             val = self.parse_y_or_n()
509             cond = self.parse_condition()
510             self.data.do_default(var, val, cond)
511         elif self.tok == TOK_DEPENDS:
512             self.get_token()
513             if self.tok != TOK_ON:
514                 raise KconfigParserError(self, 'Expected "on"')
515             self.get_token()
516             self.data.do_depends_on(var, self.parse_expr())
517         elif self.tok == TOK_SELECT:
518             self.get_token()
519             symbol = self.parse_var()
520             cond = self.parse_condition()
521             self.data.do_select(var, symbol, cond)
522         elif self.tok == TOK_IMPLY:
523             self.get_token()
524             symbol = self.parse_var()
525             cond = self.parse_condition()
526             self.data.do_imply(var, symbol, cond)
527         elif self.tok == TOK_BOOL:
528             self.get_token()
529         else:
530             raise KconfigParserError(self, 'Error in recursive descent?')
531
532     # properties: properties property
533     #       | /* empty */
534     def parse_properties(self, var):
535         had_default = False
536         while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \
537               self.tok == TOK_SELECT or self.tok == TOK_BOOL or \
538               self.tok == TOK_IMPLY:
539             self.parse_property(var)
540
541         # for nicer error message
542         if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \
543            self.tok != TOK_ID and self.tok != TOK_EOF:
544             raise KconfigParserError(self, 'expected "source", "config", identifier, '
545                     + '"default", "depends on", "imply" or "select"')
546
547     # declaration: config var properties
548     def parse_declaration(self):
549         if self.tok == TOK_CONFIG:
550             self.get_token()
551             var = self.parse_var()
552             self.data.do_declaration(var)
553             self.parse_properties(var)
554         else:
555             raise KconfigParserError(self, 'Error in recursive descent?')
556
557     # clause: SOURCE
558     #       | declaration
559     #       | assignment
560     def parse_clause(self):
561         if self.tok == TOK_SOURCE:
562             val = self.val
563             self.get_token()
564             self.do_include(val)
565         elif self.tok == TOK_CONFIG:
566             self.parse_declaration()
567         elif self.tok == TOK_ID:
568             self.parse_assignment()
569         else:
570             raise KconfigParserError(self, 'expected "source", "config" or identifier')
571
572     # config: clause+ EOF
573     def parse_config(self):
574         while self.tok != TOK_EOF:
575             self.parse_clause()
576         return self.data
577
578     # scanner -----
579
580     def get_token(self):
581         while True:
582             self.tok = self.src[self.cursor]
583             self.pos = self.cursor
584             self.cursor += 1
585
586             self.val = None
587             self.tok = self.scan_token()
588             if self.tok is not None:
589                 return
590
591     def check_keyword(self, rest):
592         if not self.src.startswith(rest, self.cursor):
593             return False
594         length = len(rest)
595         if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '_':
596             return False
597         self.cursor += length
598         return True
599
600     def scan_token(self):
601         if self.tok == '#':
602             self.cursor = self.src.find('\n', self.cursor)
603             return None
604         elif self.tok == '=':
605             return TOK_EQUAL
606         elif self.tok == '(':
607             return TOK_LPAREN
608         elif self.tok == ')':
609             return TOK_RPAREN
610         elif self.tok == '&' and self.src[self.pos+1] == '&':
611             self.cursor += 1
612             return TOK_AND
613         elif self.tok == '|' and self.src[self.pos+1] == '|':
614             self.cursor += 1
615             return TOK_OR
616         elif self.tok == '!':
617             return TOK_NOT
618         elif self.tok == 'd' and self.check_keyword("epends"):
619             return TOK_DEPENDS
620         elif self.tok == 'o' and self.check_keyword("n"):
621             return TOK_ON
622         elif self.tok == 's' and self.check_keyword("elect"):
623             return TOK_SELECT
624         elif self.tok == 'i' and self.check_keyword("mply"):
625             return TOK_IMPLY
626         elif self.tok == 'c' and self.check_keyword("onfig"):
627             return TOK_CONFIG
628         elif self.tok == 'd' and self.check_keyword("efault"):
629             return TOK_DEFAULT
630         elif self.tok == 'b' and self.check_keyword("ool"):
631             return TOK_BOOL
632         elif self.tok == 'i' and self.check_keyword("f"):
633             return TOK_IF
634         elif self.tok == 'y' and self.check_keyword(""):
635             return TOK_Y
636         elif self.tok == 'n' and self.check_keyword(""):
637             return TOK_N
638         elif (self.tok == 's' and self.check_keyword("ource")) or \
639               self.tok == 'i' and self.check_keyword("nclude"):
640             # source FILENAME
641             # include FILENAME
642             while self.src[self.cursor].isspace():
643                 self.cursor += 1
644             start = self.cursor
645             self.cursor = self.src.find('\n', self.cursor)
646             self.val = self.src[start:self.cursor]
647             return TOK_SOURCE
648         elif self.tok.isalnum():
649             # identifier
650             while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_':
651                 self.cursor += 1
652             self.val = self.src[self.pos:self.cursor]
653             return TOK_ID
654         elif self.tok == '\n':
655             if self.cursor == len(self.src):
656                 return TOK_EOF
657             self.line += 1
658             self.line_pos = self.cursor
659         elif not self.tok.isspace():
660             raise KconfigParserError(self, 'invalid input')
661
662         return None
663
664 if __name__ == '__main__':
665     argv = sys.argv
666     mode = defconfig
667     if len(sys.argv) > 1:
668         if argv[1] == '--defconfig':
669             del argv[1]
670         elif argv[1] == '--randconfig':
671             random.seed()
672             mode = randconfig
673             del argv[1]
674         elif argv[1] == '--allyesconfig':
675             mode = allyesconfig
676             del argv[1]
677         elif argv[1] == '--allnoconfig':
678             mode = allnoconfig
679             del argv[1]
680
681     if len(argv) == 1:
682         print ("%s: at least one argument is required" % argv[0], file=sys.stderr)
683         sys.exit(1)
684
685     if argv[1].startswith('-'):
686         print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr)
687         sys.exit(1)
688
689     data = KconfigData(mode)
690     parser = KconfigParser(data)
691     external_vars = set()
692     for arg in argv[3:]:
693         m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg)
694         if m is not None:
695             name, value = m.groups()
696             parser.do_assignment(name, value == 'y')
697             external_vars.add(name[7:])
698         else:
699             fp = open(arg, 'rt', encoding='utf-8')
700             parser.parse_file(fp)
701             fp.close()
702
703     config = data.compute_config()
704     for key in sorted(config.keys()):
705         if key not in external_vars and config[key]:
706             print ('CONFIG_%s=y' % key)
707
708     deps = open(argv[2], 'wt', encoding='utf-8')
709     for fname in data.previously_included:
710         print ('%s: %s' % (argv[1], fname), file=deps)
711     deps.close()