Subversion Repositories HelenOS

Rev

Rev 3937 | Rev 4058 | 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. MAKEFILE = 'Makefile.config'
  41. MACROS = 'config.h'
  42. DEFS = 'config.defs'
  43.  
  44. def read_defaults(fname, defaults):
  45.     "Read saved values from last configuration run"
  46.    
  47.     inf = file(fname, 'r')
  48.    
  49.     for line in inf:
  50.         res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
  51.         if (res):
  52.             defaults[res.group(1)] = res.group(2)
  53.    
  54.     inf.close()
  55.  
  56. def check_condition(text, defaults, ask_names):
  57.     "Check that the condition specified on input line is True (only CNF and DNF is supported)"
  58.    
  59.     ctype = 'cnf'
  60.    
  61.     if ((')|' in text) or ('|(' in text)):
  62.         ctype = 'dnf'
  63.    
  64.     if (ctype == 'cnf'):
  65.         conds = text.split('&')
  66.     else:
  67.         conds = text.split('|')
  68.    
  69.     for cond in conds:
  70.         if (cond.startswith('(')) and (cond.endswith(')')):
  71.             cond = cond[1:-1]
  72.        
  73.         inside = check_inside(cond, defaults, ctype)
  74.        
  75.         if (ctype == 'cnf') and (not inside):
  76.             return False
  77.        
  78.         if (ctype == 'dnf') and (inside):
  79.             return True
  80.    
  81.     if (ctype == 'cnf'):
  82.         return True
  83.     return False
  84.  
  85. def check_inside(text, defaults, ctype):
  86.     "Check for condition"
  87.    
  88.     if (ctype == 'cnf'):
  89.         conds = text.split('|')
  90.     else:
  91.         conds = text.split('&')
  92.    
  93.     for cond in conds:
  94.         res = re.match(r'^(.*?)(!?=)(.*)$', cond)
  95.         if (not res):
  96.             raise RuntimeError("Invalid condition: %s" % cond)
  97.        
  98.         condname = res.group(1)
  99.         oper = res.group(2)
  100.         condval = res.group(3)
  101.        
  102.         if (not defaults.has_key(condname)):
  103.             varval = ''
  104.         else:
  105.             varval = defaults[condname]
  106.             if (varval == '*'):
  107.                 varval = 'y'
  108.        
  109.         if (ctype == 'cnf'):
  110.             if (oper == '=') and (condval == varval):
  111.                 return True
  112.        
  113.             if (oper == '!=') and (condval != varval):
  114.                 return True
  115.         else:
  116.             if (oper == '=') and (condval != varval):
  117.                 return False
  118.            
  119.             if (oper == '!=') and (condval == varval):
  120.                 return False
  121.    
  122.     if (ctype == 'cnf'):
  123.         return False
  124.    
  125.     return True
  126.  
  127. def parse_config(fname, ask_names):
  128.     "Parse configuration file"
  129.    
  130.     inf = file(fname, 'r')
  131.    
  132.     name = ''
  133.     choices = []
  134.    
  135.     for line in inf:
  136.        
  137.         if (line.startswith('!')):
  138.             # Ask a question
  139.             res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
  140.            
  141.             if (not res):
  142.                 raise RuntimeError("Weird line: %s" % line)
  143.            
  144.             cond = res.group(1)
  145.             varname = res.group(2)
  146.             vartype = res.group(3)
  147.            
  148.             ask_names.append((varname, vartype, name, choices, cond))
  149.             name = ''
  150.             choices = []
  151.             continue
  152.        
  153.         if (line.startswith('@')):
  154.             # Add new line into the 'choices' array
  155.             res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
  156.            
  157.             if not res:
  158.                 raise RuntimeError("Bad line: %s" % line)
  159.            
  160.             choices.append((res.group(2), res.group(3)))
  161.             continue
  162.        
  163.         if (line.startswith('%')):
  164.             # Name of the option
  165.             name = line[1:].strip()
  166.             continue
  167.        
  168.         if ((line.startswith('#')) or (line == '\n')):
  169.             # Comment or empty line
  170.             continue
  171.        
  172.        
  173.         raise RuntimeError("Unknown syntax: %s" % line)
  174.    
  175.     inf.close()
  176.  
  177. def yes_no(default):
  178.     "Return '*' if yes, ' ' if no"
  179.    
  180.     if (default == 'y'):
  181.         return '*'
  182.    
  183.     return ' '
  184.  
  185. def subchoice(screen, name, choices, default):
  186.     "Return choice of choices"
  187.    
  188.     maxkey = 0
  189.     for key, val in choices:
  190.         length = len(key)
  191.         if (length > maxkey):
  192.             maxkey = length
  193.    
  194.     options = []
  195.     position = None
  196.     cnt = 0
  197.     for key, val in choices:
  198.         if ((default) and (key == default)):
  199.             position = cnt
  200.        
  201.         options.append(" %-*s  %s " % (maxkey, key, val))
  202.         cnt += 1
  203.    
  204.     (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
  205.    
  206.     if (button == 'cancel'):
  207.         return None
  208.    
  209.     return choices[value][0]
  210.  
  211. def check_choices(defaults, ask_names):
  212.     "Check whether all accessible variables have a default"
  213.    
  214.     for varname, vartype, name, choices, cond in ask_names:
  215.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  216.             continue
  217.        
  218.         if (not defaults.has_key(varname)):
  219.             return False
  220.    
  221.     return True
  222.  
  223. def create_output(mkname, mcname, dfname, defaults, ask_names):
  224.     "Create output configuration"
  225.    
  226.     revision = commands.getoutput('svnversion . 2> /dev/null')
  227.     timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
  228.    
  229.     outmk = file(mkname, 'w')
  230.     outmc = file(mcname, 'w')
  231.     outdf = file(dfname, 'w')
  232.    
  233.     outmk.write('#########################################\n')
  234.     outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
  235.     outmk.write('#########################################\n\n')
  236.    
  237.     outmc.write('/***************************************\n')
  238.     outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
  239.     outmc.write(' ***************************************/\n\n')
  240.    
  241.     outdf.write('#########################################\n')
  242.     outdf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
  243.     outdf.write('#########################################\n\n')
  244.     outdf.write('CONFIG_DEFS =')
  245.    
  246.     for varname, vartype, name, choices, cond in ask_names:
  247.         if ((cond) and (not check_condition(cond, defaults, ask_names))):
  248.             continue
  249.        
  250.         if (not defaults.has_key(varname)):
  251.             default = ''
  252.         else:
  253.             default = defaults[varname]
  254.             if (default == '*'):
  255.                 default = 'y'
  256.        
  257.         outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
  258.        
  259.         if ((vartype == "y") or (vartype == "y/n") or (vartype == "n/y")):
  260.             if (default == "y"):
  261.                 outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
  262.                 outdf.write(' -D%s' % varname)
  263.         else:
  264.             outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default))
  265.             outdf.write(' -D%s=%s -D%s_%s' % (varname, default, varname, default))
  266.    
  267.     outmk.write('REVISION = %s\n' % revision)
  268.     outmk.write('TIMESTAMP = %s\n' % timestamp)
  269.    
  270.     outmc.write('#define REVISION %s\n' % revision)
  271.     outmc.write('#define TIMESTAMP %s\n' % timestamp)
  272.    
  273.     outdf.write(' "-DREVISION=%s" "-DTIMESTAMP=%s"\n' % (revision, timestamp))
  274.    
  275.     outmk.close()
  276.     outmc.close()
  277.     outdf.close()
  278.  
  279. def main():
  280.     defaults = {}
  281.     ask_names = []
  282.    
  283.     # Parse configuration file
  284.     parse_config(INPUT, ask_names)
  285.    
  286.     # Read defaults from previous run
  287.     if os.path.exists(MAKEFILE):
  288.         read_defaults(MAKEFILE, defaults)
  289.    
  290.     # Default mode: only check defaults and regenerate configuration
  291.     if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
  292.         if (check_choices(defaults, ask_names)):
  293.             create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
  294.             return 0
  295.    
  296.     # Check mode: only check defaults
  297.     if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
  298.         if (check_choices(defaults, ask_names)):
  299.             return 0
  300.         return 1
  301.    
  302.     screen = xtui.screen_init()
  303.     try:
  304.         selname = None
  305.         while True:
  306.            
  307.             # Cancel out all defaults which have to be deduced
  308.             for varname, vartype, name, choices, cond in ask_names:
  309.                 if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')):
  310.                     defaults[varname] = None
  311.            
  312.             options = []
  313.             opt2row = {}
  314.             position = None
  315.             cnt = 0
  316.             for varname, vartype, name, choices, cond in ask_names:
  317.                
  318.                 if ((cond) and (not check_condition(cond, defaults, ask_names))):
  319.                     continue
  320.                
  321.                 if (varname == selname):
  322.                     position = cnt
  323.                
  324.                 if (not defaults.has_key(varname)):
  325.                     default = None
  326.                 else:
  327.                     default = defaults[varname]
  328.                
  329.                 if (vartype == 'choice'):
  330.                     # Check if the default is an acceptable value
  331.                     if ((default) and (not default in [choice[0] for choice in choices])):
  332.                         default = None
  333.                         defaults.pop(varname)
  334.                    
  335.                     # If there is just one option, use it
  336.                     if (len(choices) == 1):
  337.                         defaults[varname] = choices[0][0]
  338.                         continue
  339.                    
  340.                     if (default == None):
  341.                         options.append("?     %s --> " % name)
  342.                     else:
  343.                         options.append("      %s [%s] --> " % (name, default))
  344.                 elif (vartype == 'y'):
  345.                     defaults[varname] = '*'
  346.                     continue
  347.                 elif (vartype == 'y/n'):
  348.                     if (default == None):
  349.                         default = 'y'
  350.                         defaults[varname] = default
  351.                     options.append("  <%s> %s " % (yes_no(default), name))
  352.                 elif (vartype == 'n/y'):
  353.                     if (default == None):
  354.                         default = 'n'
  355.                         defaults[varname] = default
  356.                     options.append("  <%s> %s " % (yes_no(default), name))
  357.                 else:
  358.                     raise RuntimeError("Unknown variable type: %s" % vartype)
  359.                
  360.                 opt2row[cnt] = (varname, vartype, name, choices)
  361.                
  362.                 cnt += 1
  363.            
  364.             (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
  365.            
  366.             if (button == 'cancel'):
  367.                 return 'Configuration canceled'
  368.            
  369.             if (not opt2row.has_key(value)):
  370.                 raise RuntimeError("Error selecting value: %s" % value)
  371.            
  372.             (selname, seltype, name, choices) = opt2row[value]
  373.            
  374.             if (not defaults.has_key(selname)):
  375.                     default = None
  376.             else:
  377.                 default = defaults[selname]
  378.            
  379.             if (button == 'done'):
  380.                 if (check_choices(defaults, ask_names)):
  381.                     break
  382.                 else:
  383.                     xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
  384.                     continue
  385.            
  386.             if (seltype == 'choice'):
  387.                 defaults[selname] = subchoice(screen, name, choices, default)
  388.             elif ((seltype == 'y/n') or (seltype == 'n/y')):
  389.                 if (defaults[selname] == 'y'):
  390.                     defaults[selname] = 'n'
  391.                 else:
  392.                     defaults[selname] = 'y'
  393.     finally:
  394.         xtui.screen_done(screen)
  395.    
  396.     create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
  397.     return 0
  398.  
  399. if __name__ == '__main__':
  400.     sys.exit(main())
  401.