Subversion Repositories HelenOS

Rev

Rev 3808 | Rev 3909 | 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 xtui
  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, default):
  182.     "Return choice of choices"
  183.    
  184.     maxkey = 0
  185.     for key, val in choices:
  186.         length = len(key)
  187.         if (length > maxkey):
  188.             maxkey = length
  189.    
  190.     options = []
  191.     position = None
  192.     cnt = 0
  193.     for key, val in choices:
  194.         if ((default) and (key == default)):
  195.             position = cnt
  196.        
  197.         options.append(" %-*s  %s " % (maxkey, key, val))
  198.         cnt += 1
  199.    
  200.     (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
  201.    
  202.     if (button == 'cancel'):
  203.         return None
  204.    
  205.     return choices[value][0]
  206.  
  207. def check_choices(defaults, ask_names):
  208.     "Check whether all accessible variables have a default"
  209.    
  210.     for varname, vartype, name, choices, cond in ask_names:
  211.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  212.             continue
  213.        
  214.         if (not defaults.has_key(varname)):
  215.             return False
  216.    
  217.     return True
  218.  
  219. def create_output(fname, defaults, ask_names):
  220.     "Create output configuration"
  221.    
  222.     outf = file(fname, 'w')
  223.    
  224.     outf.write('#########################################\n')
  225.     outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
  226.     outf.write('#########################################\n\n')
  227.    
  228.     for varname, vartype, name, choices, cond in ask_names:
  229.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  230.             continue
  231.        
  232.         if (not defaults.has_key(varname)):
  233.             default = ''
  234.         else:
  235.             default = defaults[varname]
  236.        
  237.         outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
  238.    
  239.     outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
  240.     outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
  241.     outf.close()
  242.  
  243. def main():
  244.     defaults = {}
  245.     ask_names = []
  246.    
  247.     # Parse configuration file
  248.     parse_config(INPUT, ask_names)
  249.    
  250.     # Read defaults from previous run
  251.     if os.path.exists(OUTPUT):
  252.         read_defaults(OUTPUT, defaults)
  253.    
  254.     # Default mode: only check defaults and regenerate configuration
  255.     if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
  256.         if (check_choices(defaults, ask_names)):
  257.             create_output(OUTPUT, defaults, ask_names)
  258.             return 0
  259.    
  260.     # Check mode: only check defaults
  261.     if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
  262.         if (check_choices(defaults, ask_names)):
  263.             return 0
  264.         return 1
  265.    
  266.     screen = xtui.screen_init()
  267.     try:
  268.         selname = None
  269.         while True:
  270.            
  271.             options = []
  272.             opt2row = {}
  273.             position = None
  274.             cnt = 0
  275.             for varname, vartype, name, choices, cond in ask_names:
  276.                
  277.                 if ((cond) and (not check_condition(cond, defaults, ask_names))):
  278.                     continue
  279.                
  280.                 if (varname == selname):
  281.                     position = cnt
  282.                
  283.                 if (not defaults.has_key(varname)):
  284.                     default = None
  285.                 else:
  286.                     default = defaults[varname]
  287.                
  288.                 if (vartype == 'choice'):
  289.                     # Check if the default is an acceptable value
  290.                     if ((default) and (not default in [choice[0] for choice in choices])):
  291.                         default = None
  292.                         defaults.pop(varname)
  293.                    
  294.                     # If there is just one option, use it
  295.                     if (len(choices) == 1):
  296.                         default = choices[0][0]
  297.                         defaults[varname] = default
  298.                    
  299.                     options.append("     %s [%s] --> " % (name, default))
  300.                 elif (vartype == 'y/n'):
  301.                     if (default == None):
  302.                         default = 'y'
  303.                         defaults[varname] = default
  304.                     options.append(" <%s> %s " % (yes_no(default), name))
  305.                 elif (vartype == 'n/y'):
  306.                     if (default == None):
  307.                         default = 'n'
  308.                         defaults[varname] = default
  309.                     options.append(" <%s> %s " % (yes_no(default), name))
  310.                 else:
  311.                     raise RuntimeError("Unknown variable type: %s" % vartype)
  312.                
  313.                 opt2row[cnt] = (varname, vartype, name, choices)
  314.                
  315.                 cnt += 1
  316.            
  317.             (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
  318.            
  319.             if (button == 'cancel'):
  320.                 return 'Configuration canceled'
  321.            
  322.             if (not opt2row.has_key(value)):
  323.                 raise RuntimeError("Error selecting value: %s" % value)
  324.            
  325.             (selname, seltype, name, choices) = opt2row[value]
  326.            
  327.             if (not defaults.has_key(selname)):
  328.                     default = None
  329.             else:
  330.                 default = defaults[selname]
  331.            
  332.             if (button == 'done'):
  333.                 if (check_choices(defaults, ask_names)):
  334.                     break
  335.                 else:
  336.                     xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
  337.                     continue
  338.            
  339.             if (seltype == 'choice'):
  340.                 defaults[selname] = subchoice(screen, name, choices, default)
  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.         xtui.screen_done(screen)
  348.    
  349.     create_output(OUTPUT, defaults, ask_names)
  350.     return 0
  351.  
  352. if __name__ == '__main__':
  353.     sys.exit(main())
  354.