Subversion Repositories HelenOS-historic

Rev

Rev 549 | Rev 552 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #!/usr/bin/env python
  2. """
  3. Kernel configuration script
  4. """
  5. import sys
  6. import os
  7. import re
  8.  
  9. INPUT = 'kernel.config'
  10. OUTPUT = 'Makefile.config'
  11. TMPOUTPUT = 'Makefile.config.tmp'
  12.  
  13. class DefaultDialog:
  14.     "Wrapper dialog that tries to return default values"
  15.     def __init__(self, dlg):
  16.         self.dlg = dlg
  17.  
  18.     def set_title(self,text):
  19.         self.dlg.set_title(text)
  20.        
  21.     def yesno(self, text, default=None):
  22.         if default is not None:
  23.             return default
  24.         return self.dlg.yesno(text, default)
  25.     def noyes(self, text, default=None):
  26.         if default is not None:
  27.             return default
  28.         return self.dlg.noyes(text, default)
  29.    
  30.     def choice(self, text, choices, defopt=None):
  31.         if defopt is not None:
  32.             return choices[defopt][0]
  33.         return self.dlg.choice(text, choices, defopt)
  34.  
  35. class NoDialog:
  36.     def __init__(self):
  37.         self.printed = None
  38.         self.title = 'HelenOS Configuration'
  39.  
  40.     def print_title(self):
  41.         if not self.printed:
  42.             sys.stdout.write("*** %s ***\n" % self.title)
  43.             self.printed = True
  44.  
  45.     def set_title(self, text):
  46.         self.title = text
  47.         self.printed = False
  48.    
  49.     def noyes(self, text, default=None):
  50.         if not default:
  51.             default = 'n'
  52.         return self.yesno(text, default)
  53.    
  54.     def yesno(self, text, default=None):
  55.         self.print_title()
  56.        
  57.         if default != 'n':
  58.             default = 'y'
  59.         while 1:
  60.             sys.stdout.write("%s (y/n)[%s]: " % (text,default))
  61.             inp = sys.stdin.readline()
  62.             if not inp:
  63.                 raise EOFError
  64.             inp = inp.strip().lower()
  65.             if not inp:
  66.                 return default
  67.             if inp == 'y':
  68.                 return 'y'
  69.             elif inp == 'n':
  70.                 return 'n'
  71.  
  72.     def _print_choice(self, text, choices, defopt):
  73.         sys.stdout.write('%s:\n' % text)
  74.         for i,(text,descr) in enumerate(choices):
  75.             sys.stdout.write('\t%2d. %s\n' % (i, descr))
  76.         if defopt is not None:
  77.             sys.stdout.write('Enter choice number[%d]: ' % defopt)
  78.         else:
  79.             sys.stdout.write('Enter choice number: ')
  80.        
  81.     def choice(self, text, choices, defopt=None):
  82.         self.print_title()
  83.         while 1:
  84.             self._print_choice(text, choices, defopt)
  85.             inp = sys.stdin.readline()
  86.             if not inp:
  87.                 raise EOFError
  88.             if not inp.strip():
  89.                 if defopt is not None:
  90.                     return choices[defopt][0]
  91.                 continue
  92.             try:
  93.                 number = int(inp.strip())
  94.             except ValueError:
  95.                 continue
  96.             if number < 0 or number >= len(choices):
  97.                 continue
  98.             return choices[number][0]
  99.  
  100.  
  101. class Dialog(NoDialog):
  102.     def __init__(self):
  103.         NoDialog.__init__(self)
  104.         self.dlgcmd = os.environ.get('DIALOG','dialog')
  105.         self.title = 'HelenOS Configuration'
  106.        
  107.         if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
  108.             raise NotImplementedError
  109.  
  110.     def set_title(self,text):
  111.         self.title = text
  112.        
  113.     def calldlg(self,*args,**kw):
  114.         "Wrapper for calling 'dialog' program"
  115.         indesc, outdesc = os.pipe()
  116.         pid = os.fork()
  117.         if not pid:
  118.             os.close(2)
  119.             os.dup(outdesc)
  120.             os.close(indesc)
  121.            
  122.             dlgargs = [self.dlgcmd,'--title',self.title]
  123.             for key,val in kw.items():
  124.                 dlgargs.append('--'+key)
  125.                 dlgargs.append(val)
  126.             dlgargs += args            
  127.             os.execlp(self.dlgcmd,*dlgargs)
  128.  
  129.         os.close(outdesc)
  130.         errout = os.fdopen(indesc,'r')
  131.         data = errout.read()
  132.         errout.close()
  133.            
  134.         pid,status = os.wait()
  135.         if not os.WIFEXITED(status):
  136.             raise EOFError
  137.         status = os.WEXITSTATUS(status)
  138.         if status == 255:
  139.             raise EOFError
  140.         return status,data
  141.        
  142.     def yesno(self, text, default=None):
  143.         text = text + ':'
  144.         width = '50'
  145.         height = '5'
  146.         if len(text) < 48:
  147.             text = ' '*int(((48-len(text))/2)) + text
  148.         else:
  149.             width = '0'
  150.             height = '0'
  151.         if default == 'n':
  152.             res,data = self.calldlg('--defaultno','--yesno',text,height,width)
  153.         else:
  154.             res,data = self.calldlg('--yesno',text,height,width)
  155.  
  156.         if res == 0:
  157.             return 'y'
  158.         return 'n'
  159.    
  160.     def choice(self, text, choices, defopt=None):
  161.         text = text + ':'
  162.         width = '50'
  163.         height = str(8 + len(choices))
  164.         args = []
  165.         for key,val in choices:
  166.             args.append(key)
  167.             args.append(val)
  168.  
  169.         kw = {}
  170.         if defopt:
  171.             kw['default-item'] = choices[defopt][0]
  172.         res,data = self.calldlg('--nocancel','--menu',text,height,width,
  173.                                 str(len(choices)),*args, **kw)
  174.         if res:
  175.             print data
  176.             raise EOFError
  177.         return data
  178.    
  179. def read_defaults(fname,defaults):
  180.     "Read saved values from last configuration run"
  181.     f = file(fname,'r')
  182.     for line in f:
  183.         res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
  184.         if res:
  185.             defaults[res.group(1)] = res.group(2)
  186.     f.close()
  187.  
  188. def check_condition(text, defaults):
  189.     result = True
  190.     conds = text.split('&')
  191.     for cond in conds:
  192.         if cond.startswith('(') and cond.endswith(')'):
  193.             cond = cond[1:-1]
  194.         if not check_dnf(cond, defaults):
  195.             return False
  196.     return True
  197.  
  198. def check_dnf(text, defaults):
  199.     """
  200.    Check that the condition specified on input line is True
  201.  
  202.    only CNF is supported
  203.    """
  204.     conds = text.split('|')
  205.     for cond in conds:
  206.         res = re.match(r'^(.*?)(!?=)(.*)$', cond)
  207.         if not res:
  208.             raise RuntimeError("Invalid condition: %s" % cond)
  209.         condname = res.group(1)
  210.         oper = res.group(2)
  211.         condval = res.group(3)
  212.         if not defaults.has_key(condname):
  213.             raise RuntimeError("Condition var %s does not exist: %s" % \
  214.                                (condname,text))
  215.  
  216.         if oper=='=' and  condval == defaults[condname]:
  217.             return True
  218.         if oper == '!=' and condval != defaults[condname]:
  219.             return True
  220.     return False
  221.  
  222. def parse_config(input, output, dlg, defaults={}):
  223.     "Parse configuration file and create Makefile.config on the fly"
  224.     f = file(input, 'r')
  225.     outf = file(output, 'w')
  226.  
  227.     outf.write('#########################################\n')
  228.     outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
  229.     outf.write('#########################################\n\n')
  230.  
  231.     comment = ''
  232.     default = None
  233.     choices = []
  234.     for line in f:
  235.         if line.startswith('%'):
  236.             res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
  237.             if not res:
  238.                 raise RuntimeError('Invalid command: %s' % line)
  239.             if res.group(1):
  240.                 if not check_condition(res.group(1), defaults):
  241.                     continue
  242.             args = res.group(2).strip().split(' ')
  243.             cmd = args[0].lower()
  244.             args = args[1:]
  245.             if cmd == 'askdefault':
  246.                 if isinstance(dlg, DefaultDialog):
  247.                     continue
  248.                 res = dlg.noyes('Change kernel configuration')
  249.                 if res == 'n':
  250.                     dlg = DefaultDialog(dlg)
  251.             elif cmd == 'saveas':
  252.                 outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
  253.                
  254.             continue
  255.            
  256.         if line.startswith('!'):
  257.             # Ask a question
  258.             res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
  259.             if not res:
  260.                 raise RuntimeError("Weird line: %s" % line)
  261.             varname = res.group(2)
  262.             vartype = res.group(3)
  263.  
  264.             default = defaults.get(varname,None)
  265.  
  266.             if res.group(1):
  267.                 if not check_condition(res.group(1), defaults):
  268.                     if default is not None:
  269.                         outf.write('#!# %s = %s\n' % (varname, default))
  270.                     # Clear cumulated values
  271.                     comment = ''
  272.                     default = None
  273.                     choices = []
  274.                     continue
  275.  
  276.             if vartype == 'y/n':
  277.                 result = dlg.yesno(comment, default)
  278.             elif vartype == 'n/y':
  279.                 result = dlg.noyes(comment, default)
  280.             elif vartype == 'choice':
  281.                 defopt = None
  282.                 if default is not None:
  283.                     for i,(key,val) in enumerate(choices):
  284.                         if key == default:
  285.                             defopt = i
  286.                             break
  287.                 result = dlg.choice(comment, choices, defopt)
  288.             else:
  289.                 raise RuntimeError("Bad method: %s" % vartype)
  290.             outf.write('%s = %s\n' % (varname, result))
  291.             # Remeber the selected value
  292.             defaults[varname] = result
  293.             # Clear cumulated values
  294.             comment = ''
  295.             default = None
  296.             choices = []
  297.             continue
  298.        
  299.         if line.startswith('@'):
  300.             # Add new line into the 'choice array'
  301.             res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
  302.             if not res:
  303.                 raise RuntimeError("Bad line: %s" % line)
  304.             if res.group(1):
  305.                 if not check_condition(res.group(1),defaults):
  306.                     continue
  307.             choices.append((res.group(2), res.group(3)))
  308.             continue
  309.  
  310.         # All other things print to output file
  311.         outf.write(line)
  312.         if re.match(r'^#[^#]', line):
  313.             # Last comment before question will be displayed to the user
  314.             comment = line[1:].strip()
  315.         elif line.startswith('##'):
  316.             # Set title of the dialog window
  317.             dlg.set_title(line[2:].strip())
  318.        
  319.     outf.close()
  320.     f.close()
  321.  
  322. def main():
  323.     defaults = {}
  324.     try:
  325.         dlg = Dialog()
  326.     except NotImplementedError:
  327.         dlg = NoDialog()
  328.  
  329.     # Default run will update the configuration file
  330.     # with newest options
  331.     if len(sys.argv) == 2 and sys.argv[1]=='default':
  332.         dlg = DefaultDialog(dlg)
  333.  
  334.     if os.path.exists(OUTPUT):
  335.         read_defaults(OUTPUT, defaults)
  336.    
  337.     parse_config(INPUT, TMPOUTPUT, dlg, defaults)
  338.     if os.path.exists(OUTPUT):
  339.         os.unlink(OUTPUT)
  340.     os.rename(TMPOUTPUT, OUTPUT)
  341.        
  342.  
  343. if __name__ == '__main__':
  344.     main()
  345.