Subversion Repositories HelenOS

Rev

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