Subversion Repositories HelenOS

Rev

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

  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2006 Ondrej Palkovsky
  4. # Copyright (c) 2009 Martin Decky
  5. # All rights reserved.
  6. #
  7. # Redistribution and use in source and binary forms, with or without
  8. # modification, are permitted provided that the following conditions
  9. # are met:
  10. #
  11. # - Redistributions of source code must retain the above copyright
  12. #   notice, this list of conditions and the following disclaimer.
  13. # - Redistributions in binary form must reproduce the above copyright
  14. #   notice, this list of conditions and the following disclaimer in the
  15. #   documentation and/or other materials provided with the distribution.
  16. # - The name of the author may not be used to endorse or promote products
  17. #   derived from this software without specific prior written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  20. # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  21. # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  24. # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. #
  30. """
  31. HelenOS configuration system
  32. """
  33. import sys
  34. import os
  35. import re
  36. import commands
  37. import snack
  38.  
  39. INPUT = sys.argv[1]
  40. OUTPUT = 'Makefile.config'
  41.  
  42. def read_defaults(fname, defaults):
  43.     "Read saved values from last configuration run"
  44.    
  45.     inf = file(fname,'r')
  46.    
  47.     for line in inf:
  48.         res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
  49.         if (res):
  50.             defaults[res.group(1)] = res.group(2)
  51.    
  52.     inf.close()
  53.  
  54. def check_condition(text, defaults, ask_names):
  55.     "Check for condition"
  56.    
  57.     ctype = 'cnf'
  58.    
  59.     if ((')|' in text) or ('|(' in text)):
  60.         ctype = 'dnf'
  61.    
  62.     if (ctype == 'cnf'):
  63.         conds = text.split('&')
  64.     else:
  65.         conds = text.split('|')
  66.    
  67.     for cond in conds:
  68.         if (cond.startswith('(')) and (cond.endswith(')')):
  69.             cond = cond[1:-1]
  70.        
  71.         inside = check_inside(cond, defaults, ctype)
  72.        
  73.         if (ctype == 'cnf') and (not inside):
  74.             return False
  75.        
  76.         if (ctype == 'dnf') and (inside):
  77.             return True
  78.    
  79.     if (ctype == 'cnf'):
  80.         return True
  81.     return False
  82.  
  83. def check_inside(text, defaults, ctype):
  84.     "Check that the condition specified on input line is True (only CNF is supported)"
  85.    
  86.     if (ctype == 'cnf'):
  87.         conds = text.split('|')
  88.     else:
  89.         conds = text.split('&')
  90.    
  91.     for cond in conds:
  92.         res = re.match(r'^(.*?)(!?=)(.*)$', cond)
  93.         if (not res):
  94.             raise RuntimeError("Invalid condition: %s" % cond)
  95.        
  96.         condname = res.group(1)
  97.         oper = res.group(2)
  98.         condval = res.group(3)
  99.        
  100.         if (not defaults.has_key(condname)):
  101.             varval = ''
  102.         else:
  103.             varval = defaults[condname]
  104.        
  105.         if (ctype == 'cnf'):
  106.             if (oper == '=') and (condval == varval):
  107.                 return True
  108.        
  109.             if (oper == '!=') and (condval != varval):
  110.                 return True
  111.         else:
  112.             if (oper == '=') and (condval != varval):
  113.                 return False
  114.            
  115.             if (oper == '!=') and (condval == varval):
  116.                 return False
  117.    
  118.     if (ctype == 'cnf'):
  119.         return False
  120.    
  121.     return True
  122.  
  123. def parse_config(fname, ask_names):
  124.     "Parse configuration file"
  125.    
  126.     inf = file(fname, 'r')
  127.    
  128.     name = ''
  129.     choices = []
  130.    
  131.     for line in inf:
  132.        
  133.         if (line.startswith('!')):
  134.             # Ask a question
  135.             res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
  136.            
  137.             if (not res):
  138.                 raise RuntimeError("Weird line: %s" % line)
  139.            
  140.             cond = res.group(1)
  141.             varname = res.group(2)
  142.             vartype = res.group(3)
  143.            
  144.             ask_names.append((varname, vartype, name, choices, cond))
  145.             name = ''
  146.             choices = []
  147.             continue
  148.        
  149.         if (line.startswith('@')):
  150.             # Add new line into the 'choices' array
  151.             res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
  152.            
  153.             if not res:
  154.                 raise RuntimeError("Bad line: %s" % line)
  155.            
  156.             choices.append((res.group(2), res.group(3)))
  157.             continue
  158.        
  159.         if (line.startswith('%')):
  160.             # Name of the option
  161.             name = line[1:].strip()
  162.             continue
  163.        
  164.         if ((line.startswith('#')) or (line == '\n')):
  165.             # Comment or empty line
  166.             continue
  167.        
  168.        
  169.         raise RuntimeError("Unknown syntax: %s" % line)
  170.    
  171.     inf.close()
  172.  
  173. def yes_no(default):
  174.     "Return '*' if yes, ' ' if no"
  175.    
  176.     if (default == 'y'):
  177.         return '*'
  178.    
  179.     return ' '
  180.  
  181. def subchoice(screen, name, choices):
  182.     "Return choice of choices"
  183.    
  184.     maxlen = 0
  185.     for choice in choices:
  186.         length = len(choice[0])
  187.         if (length > maxlen):
  188.             maxlen = length
  189.    
  190.     options = []
  191.     for choice in choices:
  192.         options.append(" %-*s  %s " % (maxlen, choice[0], choice[1]))
  193.    
  194.     retval = snack.ListboxChoiceWindow(screen, name, 'Choose value', options)
  195.    
  196.     if (retval[0] == 'cancel'):
  197.         return None
  198.    
  199.     return choices[retval[1]][0]
  200.  
  201. def check_choices(defaults, ask_names):
  202.     "Check whether all accessible variables have a default"
  203.    
  204.     for row in ask_names:
  205.         varname = row[0]
  206.         cond = row[4]
  207.        
  208.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  209.             continue
  210.        
  211.         if (not defaults.has_key(varname)):
  212.             return False
  213.    
  214.     return True
  215.  
  216. def create_output(fname, defaults, ask_names):
  217.     "Create output configuration"
  218.    
  219.     outf = file(fname, 'w')
  220.    
  221.     outf.write('#########################################\n')
  222.     outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
  223.     outf.write('#########################################\n\n')
  224.    
  225.     for row in ask_names:
  226.         varname = row[0]
  227.         name = row[2]
  228.         cond = row[4]
  229.        
  230.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  231.             continue
  232.        
  233.         if (not defaults.has_key(varname)):
  234.             default = ''
  235.         else:
  236.             default = defaults[varname]
  237.        
  238.         outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
  239.    
  240.     outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
  241.     outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
  242.     outf.close()
  243.  
  244. def main():
  245.     defaults = {}
  246.     ask_names = []
  247.    
  248.     # Parse configuration file
  249.     parse_config(INPUT, ask_names)
  250.    
  251.     # Read defaults from previous run
  252.     if os.path.exists(OUTPUT):
  253.         read_defaults(OUTPUT, defaults)
  254.    
  255.     # Default mode: only check defaults and regenerate configuration
  256.     if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
  257.         if (check_choices(defaults, ask_names)):
  258.             create_output(OUTPUT, defaults, ask_names)
  259.             return 0
  260.    
  261.     screen = snack.SnackScreen()
  262.     try:
  263.         selname = None
  264.         while True:
  265.            
  266.             options = []
  267.             opt2row = {}
  268.             position = None
  269.             cnt = 0
  270.             for row in ask_names:
  271.                
  272.                 varname = row[0]
  273.                 vartype = row[1]
  274.                 name = row[2]
  275.                 choices = row[3]
  276.                 cond = row[4]
  277.                
  278.                 if ((cond) and (not check_condition(cond, defaults, ask_names))):
  279.                     continue
  280.                
  281.                 if (varname == selname):
  282.                     position = cnt
  283.                
  284.                 if (not defaults.has_key(varname)):
  285.                     default = None
  286.                 else:
  287.                     default = defaults[varname]
  288.                
  289.                 if (vartype == 'choice'):
  290.                     # Check if the default is an acceptable value
  291.                     if ((default) and (not default in [choice[0] for choice in choices])):
  292.                         default = None
  293.                         defaults.pop(varname)
  294.                    
  295.                     # If there is just one option, use it
  296.                     if (len(choices) == 1):
  297.                         default = choices[0][0]
  298.                         defaults[varname] = default
  299.                    
  300.                     options.append("     %s [%s] --> " % (name, default))
  301.                 elif (vartype == 'y/n'):
  302.                     if (default == None):
  303.                         default = 'y'
  304.                         defaults[varname] = default
  305.                     options.append(" <%s> %s " % (yes_no(default), name))
  306.                 elif (vartype == 'n/y'):
  307.                     if (default == None):
  308.                         default = 'n'
  309.                         defaults[varname] = default
  310.                     options.append(" <%s> %s " % (yes_no(default), name))
  311.                 else:
  312.                     raise RuntimeError("Unknown variable type: %s" % vartype)
  313.                
  314.                 opt2row[cnt] = row
  315.                
  316.                 cnt += 1
  317.            
  318.             retval = snack.ListboxChoiceWindow(screen, 'HelenOS configuration', 'Choose configuration option', options, default = position)
  319.            
  320.             if (retval[0] == 'cancel'):
  321.                 return 'Configuration canceled'
  322.            
  323.             row = opt2row[retval[1]]
  324.             if (row == None):
  325.                 raise RuntimeError("Error selecting value: %s" % retval[1])
  326.            
  327.             selname = row[0]
  328.             seltype = row[1]
  329.             name = row[2]
  330.             choices = row[3]
  331.            
  332.             if (retval[0] == 'ok'):
  333.                 if (check_choices(defaults, ask_names)):
  334.                     break
  335.                 else:
  336.                     snack.ButtonChoiceWindow(screen, 'Error', 'Some options have still undefined values.', ['Ok']);
  337.                     continue
  338.            
  339.             if (seltype == 'choice'):
  340.                 defaults[selname] = subchoice(screen, name, choices)
  341.             elif ((seltype == 'y/n') or (seltype == 'n/y')):
  342.                 if (defaults[selname] == 'y'):
  343.                     defaults[selname] = 'n'
  344.                 else:
  345.                     defaults[selname] = 'y'
  346.     finally:
  347.         screen.finish()
  348.    
  349.     create_output(OUTPUT, defaults, ask_names)
  350.     return 0
  351.  
  352. if __name__ == '__main__':
  353.     exit(main())
  354.