source: mainline/tools/config.py@ 89e3152

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 89e3152 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100755
File size: 19.0 KB
RevLine 
[98376de]1#!/usr/bin/env python
[44882c8]2#
[5a55ae6]3# Copyright (c) 2006 Ondrej Palkovsky
[9a0367f]4# Copyright (c) 2009 Martin Decky
[62bb73e]5# Copyright (c) 2010 Jiri Svoboda
[44882c8]6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11#
12# - Redistributions of source code must retain the above copyright
13# notice, this list of conditions and the following disclaimer.
14# - Redistributions in binary form must reproduce the above copyright
15# notice, this list of conditions and the following disclaimer in the
16# documentation and/or other materials provided with the distribution.
17# - The name of the author may not be used to endorse or promote products
18# derived from this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30#
[3c80f2b]31
[98376de]32"""
[9a0367f]33HelenOS configuration system
[98376de]34"""
[3c80f2b]35
[98376de]36import sys
37import os
38import re
[5a8fbcb9]39import time
40import subprocess
[27fb3d6]41import xtui
[f857e8b]42import random
[98376de]43
[62bb73e]44RULES_FILE = sys.argv[1]
[84266669]45MAKEFILE = 'Makefile.config'
46MACROS = 'config.h'
[62bb73e]47PRESETS_DIR = 'defaults'
[98376de]48
[62bb73e]49def read_config(fname, config):
[d40ffbb]50 "Read saved values from last configuration run or a preset file"
[a35b458]51
[28f4adb]52 inf = open(fname, 'r')
[a35b458]53
[9a0367f]54 for line in inf:
55 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
[ba8de9c3]56 if res:
[62bb73e]57 config[res.group(1)] = res.group(2)
[a35b458]58
[9a0367f]59 inf.close()
[98376de]60
[62bb73e]61def check_condition(text, config, rules):
[81c8d54]62 "Check that the condition specified on input line is True (only CNF and DNF is supported)"
[a35b458]63
[9a0367f]64 ctype = 'cnf'
[a35b458]65
[ba8de9c3]66 if (')|' in text) or ('|(' in text):
[9a0367f]67 ctype = 'dnf'
[a35b458]68
[ba8de9c3]69 if ctype == 'cnf':
[9a0367f]70 conds = text.split('&')
71 else:
72 conds = text.split('|')
[a35b458]73
[9a0367f]74 for cond in conds:
[ba8de9c3]75 if cond.startswith('(') and cond.endswith(')'):
[9a0367f]76 cond = cond[1:-1]
[a35b458]77
[62bb73e]78 inside = check_inside(cond, config, ctype)
[a35b458]79
[9a0367f]80 if (ctype == 'cnf') and (not inside):
81 return False
[a35b458]82
[ba8de9c3]83 if (ctype == 'dnf') and inside:
[9a0367f]84 return True
[a35b458]85
[ba8de9c3]86 if ctype == 'cnf':
[9a0367f]87 return True
[a35b458]88
[9a0367f]89 return False
[98376de]90
[62bb73e]91def check_inside(text, config, ctype):
[81c8d54]92 "Check for condition"
[a35b458]93
[ba8de9c3]94 if ctype == 'cnf':
[9a0367f]95 conds = text.split('|')
96 else:
97 conds = text.split('&')
[a35b458]98
[9a0367f]99 for cond in conds:
100 res = re.match(r'^(.*?)(!?=)(.*)$', cond)
[ba8de9c3]101 if not res:
[9a0367f]102 raise RuntimeError("Invalid condition: %s" % cond)
[a35b458]103
[9a0367f]104 condname = res.group(1)
105 oper = res.group(2)
106 condval = res.group(3)
[a35b458]107
[ba8de9c3]108 if not condname in config:
[9a0367f]109 varval = ''
110 else:
[62bb73e]111 varval = config[condname]
[7aef7ee]112 if (varval == '*'):
113 varval = 'y'
[a35b458]114
[ba8de9c3]115 if ctype == 'cnf':
[9a0367f]116 if (oper == '=') and (condval == varval):
117 return True
[a35b458]118
[9a0367f]119 if (oper == '!=') and (condval != varval):
120 return True
121 else:
122 if (oper == '=') and (condval != varval):
123 return False
[a35b458]124
[9a0367f]125 if (oper == '!=') and (condval == varval):
126 return False
[a35b458]127
[ba8de9c3]128 if ctype == 'cnf':
[9a0367f]129 return False
[a35b458]130
[9a0367f]131 return True
[98376de]132
[62bb73e]133def parse_rules(fname, rules):
134 "Parse rules file"
[a35b458]135
[28f4adb]136 inf = open(fname, 'r')
[a35b458]137
[9a0367f]138 name = ''
139 choices = []
[a35b458]140
[9a0367f]141 for line in inf:
[a35b458]142
[ba8de9c3]143 if line.startswith('!'):
[9a0367f]144 # Ask a question
145 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
[a35b458]146
[ba8de9c3]147 if not res:
[9a0367f]148 raise RuntimeError("Weird line: %s" % line)
[a35b458]149
[9a0367f]150 cond = res.group(1)
151 varname = res.group(2)
152 vartype = res.group(3)
[a35b458]153
[62bb73e]154 rules.append((varname, vartype, name, choices, cond))
[9a0367f]155 name = ''
156 choices = []
157 continue
[a35b458]158
[ba8de9c3]159 if line.startswith('@'):
[9a0367f]160 # Add new line into the 'choices' array
161 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
[a35b458]162
[9a0367f]163 if not res:
164 raise RuntimeError("Bad line: %s" % line)
[a35b458]165
[9a0367f]166 choices.append((res.group(2), res.group(3)))
167 continue
[a35b458]168
[ba8de9c3]169 if line.startswith('%'):
[9a0367f]170 # Name of the option
171 name = line[1:].strip()
172 continue
[a35b458]173
[ba8de9c3]174 if line.startswith('#') or (line == '\n'):
[9a0367f]175 # Comment or empty line
176 continue
[a35b458]177
178
[9a0367f]179 raise RuntimeError("Unknown syntax: %s" % line)
[a35b458]180
[9a0367f]181 inf.close()
[98376de]182
[9a0367f]183def yes_no(default):
184 "Return '*' if yes, ' ' if no"
[a35b458]185
[ba8de9c3]186 if default == 'y':
[9a0367f]187 return '*'
[a35b458]188
[9a0367f]189 return ' '
[98376de]190
[27fb3d6]191def subchoice(screen, name, choices, default):
[9a0367f]192 "Return choice of choices"
[a35b458]193
[27fb3d6]194 maxkey = 0
195 for key, val in choices:
196 length = len(key)
197 if (length > maxkey):
198 maxkey = length
[a35b458]199
[9a0367f]200 options = []
[27fb3d6]201 position = None
202 cnt = 0
203 for key, val in choices:
[ba8de9c3]204 if (default) and (key == default):
[27fb3d6]205 position = cnt
[a35b458]206
[27fb3d6]207 options.append(" %-*s %s " % (maxkey, key, val))
208 cnt += 1
[a35b458]209
[27fb3d6]210 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
[a35b458]211
[ba8de9c3]212 if button == 'cancel':
[9a0367f]213 return None
[a35b458]214
[27fb3d6]215 return choices[value][0]
[98376de]216
[e4d540b]217## Infer and verify configuration values.
218#
[62bb73e]219# Augment @a config with values that can be inferred, purge invalid ones
[e4d540b]220# and verify that all variables have a value (previously specified or inferred).
221#
[d40ffbb]222# @param config Configuration to work on
223# @param rules Rules
[e4d540b]224#
[d40ffbb]225# @return True if configuration is complete and valid, False
226# otherwise.
[e4d540b]227#
[62bb73e]228def infer_verify_choices(config, rules):
[e4d540b]229 "Infer and verify configuration values."
[a35b458]230
[ba8de9c3]231 for rule in rules:
232 varname, vartype, name, choices, cond = rule
[a35b458]233
[ba8de9c3]234 if cond and (not check_condition(cond, config, rules)):
[9a0367f]235 continue
[a35b458]236
[ba8de9c3]237 if not varname in config:
[4756634]238 value = None
[e4d540b]239 else:
[4756634]240 value = config[varname]
[a35b458]241
[d40ffbb]242 if not validate_rule_value(rule, value):
[4756634]243 value = None
[a35b458]244
[d40ffbb]245 default = get_default_rule(rule)
[a35b458]246
[c0bd08d]247 #
248 # If we don't have a value but we do have
249 # a default, use it.
250 #
251 if value == None and default != None:
252 value = default
[4756634]253 config[varname] = default
[a35b458]254
[ba8de9c3]255 if not varname in config:
[9a0367f]256 return False
[a35b458]257
[9a0367f]258 return True
[98376de]259
[f857e8b]260## Fill the configuration with random (but valid) values.
261#
262# The random selection takes next rule and if the condition does
263# not violate existing configuration, random value of the variable
264# is selected.
265# This happens recursively as long as there are more rules.
266# If a conflict is found, we backtrack and try other settings of the
267# variable or ignoring the variable altogether.
268#
269# @param config Configuration to work on
270# @param rules Rules
271# @param start_index With which rule to start (initial call must specify 0 here).
[1b20da0]272# @return True if able to find a valid configuration
[f857e8b]273def random_choices(config, rules, start_index):
274 "Fill the configuration with random (but valid) values."
275 if start_index >= len(rules):
276 return True
[a35b458]277
[f857e8b]278 varname, vartype, name, choices, cond = rules[start_index]
279
[1b20da0]280 # First check that this rule would make sense
[f857e8b]281 if cond:
282 if not check_condition(cond, config, rules):
283 return random_choices(config, rules, start_index + 1)
[a35b458]284
[f857e8b]285 # Remember previous choices for backtracking
286 yes_no = 0
287 choices_indexes = range(0, len(choices))
288 random.shuffle(choices_indexes)
[a35b458]289
[f857e8b]290 # Remember current configuration value
291 old_value = None
292 try:
293 old_value = config[varname]
294 except KeyError:
295 old_value = None
[a35b458]296
[f857e8b]297 # For yes/no choices, we ran the loop at most 2 times, for select
298 # choices as many times as there are options.
299 try_counter = 0
300 while True:
301 if vartype == 'choice':
302 if try_counter >= len(choices_indexes):
303 break
304 value = choices[choices_indexes[try_counter]][0]
305 elif vartype == 'y' or vartype == 'n':
306 if try_counter > 0:
307 break
308 value = vartype
309 elif vartype == 'y/n' or vartype == 'n/y':
310 if try_counter == 0:
311 yes_no = random.randint(0, 1)
312 elif try_counter == 1:
313 yes_no = 1 - yes_no
314 else:
315 break
316 if yes_no == 0:
317 value = 'n'
318 else:
319 value = 'y'
320 else:
321 raise RuntimeError("Unknown variable type: %s" % vartype)
[a35b458]322
[f857e8b]323 config[varname] = value
[a35b458]324
[f857e8b]325 ok = random_choices(config, rules, start_index + 1)
326 if ok:
327 return True
[a35b458]328
[f857e8b]329 try_counter = try_counter + 1
[a35b458]330
[f857e8b]331 # Restore the old value and backtrack
332 # (need to delete to prevent "ghost" variables that do not exist under
333 # certain configurations)
334 config[varname] = old_value
335 if old_value is None:
336 del config[varname]
[a35b458]337
[f857e8b]338 return random_choices(config, rules, start_index + 1)
[a35b458]339
[f857e8b]340
[e4d540b]341## Get default value from a rule.
[d40ffbb]342def get_default_rule(rule):
[e4d540b]343 varname, vartype, name, choices, cond = rule
[a35b458]344
[e4d540b]345 default = None
[a35b458]346
[ba8de9c3]347 if vartype == 'choice':
[e4d540b]348 # If there is just one option, use it
[ba8de9c3]349 if len(choices) == 1:
[e4d540b]350 default = choices[0][0]
[ba8de9c3]351 elif vartype == 'y':
[e4d540b]352 default = '*'
[ba8de9c3]353 elif vartype == 'n':
[e4d540b]354 default = 'n'
[ba8de9c3]355 elif vartype == 'y/n':
[e4d540b]356 default = 'y'
[ba8de9c3]357 elif vartype == 'n/y':
[e4d540b]358 default = 'n'
359 else:
360 raise RuntimeError("Unknown variable type: %s" % vartype)
[a35b458]361
[e4d540b]362 return default
363
364## Get option from a rule.
365#
[d40ffbb]366# @param rule Rule for a variable
367# @param value Current value of the variable
[e4d540b]368#
369# @return Option (string) to ask or None which means not to ask.
370#
[d40ffbb]371def get_rule_option(rule, value):
[e4d540b]372 varname, vartype, name, choices, cond = rule
[a35b458]373
[e4d540b]374 option = None
[a35b458]375
[ba8de9c3]376 if vartype == 'choice':
[e4d540b]377 # If there is just one option, don't ask
[ba8de9c3]378 if len(choices) != 1:
[e4d540b]379 if (value == None):
380 option = "? %s --> " % name
381 else:
382 option = " %s [%s] --> " % (name, value)
[ba8de9c3]383 elif vartype == 'y':
[e4d540b]384 pass
[ba8de9c3]385 elif vartype == 'n':
[e4d540b]386 pass
[ba8de9c3]387 elif vartype == 'y/n':
[e4d540b]388 option = " <%s> %s " % (yes_no(value), name)
[ba8de9c3]389 elif vartype == 'n/y':
[e4d540b]390 option =" <%s> %s " % (yes_no(value), name)
391 else:
392 raise RuntimeError("Unknown variable type: %s" % vartype)
[a35b458]393
[e4d540b]394 return option
395
396## Check if variable value is valid.
397#
[d40ffbb]398# @param rule Rule for the variable
399# @param value Value of the variable
[e4d540b]400#
[d40ffbb]401# @return True if valid, False if not valid.
[e4d540b]402#
[d40ffbb]403def validate_rule_value(rule, value):
[e4d540b]404 varname, vartype, name, choices, cond = rule
[a35b458]405
[e4d540b]406 if value == None:
407 return True
[a35b458]408
[ba8de9c3]409 if vartype == 'choice':
410 if not value in [choice[0] for choice in choices]:
[e4d540b]411 return False
[ba8de9c3]412 elif vartype == 'y':
[e4d540b]413 if value != 'y':
414 return False
[ba8de9c3]415 elif vartype == 'n':
[e4d540b]416 if value != 'n':
417 return False
[ba8de9c3]418 elif vartype == 'y/n':
[e4d540b]419 if not value in ['y', 'n']:
420 return False
[ba8de9c3]421 elif vartype == 'n/y':
[e4d540b]422 if not value in ['y', 'n']:
423 return False
424 else:
425 raise RuntimeError("Unknown variable type: %s" % vartype)
[a35b458]426
[e4d540b]427 return True
428
[1f5c9c96]429def preprocess_config(config, rules):
430 "Preprocess configuration"
[a35b458]431
[1f5c9c96]432 varname_mode = 'CONFIG_BFB_MODE'
433 varname_width = 'CONFIG_BFB_WIDTH'
434 varname_height = 'CONFIG_BFB_HEIGHT'
[a35b458]435
[1f5c9c96]436 if varname_mode in config:
437 mode = config[varname_mode].partition('x')
[a35b458]438
[1f5c9c96]439 config[varname_width] = mode[0]
440 rules.append((varname_width, 'choice', 'Default framebuffer width', None, None))
[a35b458]441
[1f5c9c96]442 config[varname_height] = mode[2]
443 rules.append((varname_height, 'choice', 'Default framebuffer height', None, None))
444
[62bb73e]445def create_output(mkname, mcname, config, rules):
[9a0367f]446 "Create output configuration"
[a35b458]447
[476ac3b]448 varname_strip = 'CONFIG_STRIP_REVISION_INFO'
449 strip_rev_info = (varname_strip in config) and (config[varname_strip] == 'y')
[a35b458]450
[476ac3b]451 if strip_rev_info:
452 timestamp_unix = int(0)
453 else:
454 # TODO: Use commit timestamp instead of build time.
455 timestamp_unix = int(time.time())
[a35b458]456
[41e871f]457 timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp_unix))
[a35b458]458
[fe12f9f4]459 sys.stderr.write("Fetching current revision identifier ... ")
[a35b458]460
[7b76744]461 try:
[0a0b3d8]462 version = subprocess.Popen(['git', 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip()
[7b76744]463 sys.stderr.write("ok\n")
464 except:
[0a0b3d8]465 version = None
[7b76744]466 sys.stderr.write("failed\n")
[a35b458]467
[0a0b3d8]468 if (not strip_rev_info) and (version is not None):
469 revision = version
[5a8fbcb9]470 else:
471 revision = None
[a35b458]472
[28f4adb]473 outmk = open(mkname, 'w')
474 outmc = open(mcname, 'w')
[a35b458]475
[84266669]476 outmk.write('#########################################\n')
477 outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
[571239a]478 outmk.write('## Generated by: tools/config.py ##\n')
[84266669]479 outmk.write('#########################################\n\n')
[a35b458]480
[84266669]481 outmc.write('/***************************************\n')
482 outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
[571239a]483 outmc.write(' * Generated by: tools/config.py *\n')
[84266669]484 outmc.write(' ***************************************/\n\n')
[a35b458]485
[4e9aaf5]486 defs = 'CONFIG_DEFS ='
[a35b458]487
[62bb73e]488 for varname, vartype, name, choices, cond in rules:
[ba8de9c3]489 if cond and (not check_condition(cond, config, rules)):
[9a0367f]490 continue
[a35b458]491
[ba8de9c3]492 if not varname in config:
[4756634]493 value = ''
[9a0367f]494 else:
[4756634]495 value = config[varname]
496 if (value == '*'):
497 value = 'y'
[a35b458]498
[4756634]499 outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))
[a35b458]500
[ba8de9c3]501 if vartype in ["y", "n", "y/n", "n/y"]:
502 if value == "y":
[84266669]503 outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
[4e9aaf5]504 defs += ' -D%s' % varname
[84266669]505 else:
[4756634]506 outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))
507 defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)
[a35b458]508
[ba8de9c3]509 if revision is not None:
[5a8fbcb9]510 outmk.write('REVISION = %s\n' % revision)
511 outmc.write('#define REVISION %s\n' % revision)
[4e9aaf5]512 defs += ' "-DREVISION=%s"' % revision
[a35b458]513
[41e871f]514 outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
515 outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
516 defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
[a35b458]517
[5a8fbcb9]518 outmk.write('TIMESTAMP = %s\n' % timestamp)
[84266669]519 outmc.write('#define TIMESTAMP %s\n' % timestamp)
[4e9aaf5]520 defs += ' "-DTIMESTAMP=%s"\n' % timestamp
[a35b458]521
[4e9aaf5]522 outmk.write(defs)
[a35b458]523
[84266669]524 outmk.close()
525 outmc.close()
[98376de]526
[31fb9a0]527def sorted_dir(root):
528 list = os.listdir(root)
529 list.sort()
530 return list
531
[6ec0acd]532## Ask user to choose a configuration profile.
[62bb73e]533#
[d40ffbb]534def choose_profile(root, fname, screen, config):
[31fb9a0]535 options = []
536 opt2path = {}
537 cnt = 0
[a35b458]538
[31fb9a0]539 # Look for profiles
540 for name in sorted_dir(root):
541 path = os.path.join(root, name)
542 canon = os.path.join(path, fname)
[a35b458]543
[ba8de9c3]544 if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):
[31fb9a0]545 subprofile = False
[a35b458]546
[31fb9a0]547 # Look for subprofiles
548 for subname in sorted_dir(path):
549 subpath = os.path.join(path, subname)
550 subcanon = os.path.join(subpath, fname)
[a35b458]551
[ba8de9c3]552 if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):
[31fb9a0]553 subprofile = True
554 options.append("%s (%s)" % (name, subname))
[6ec0acd]555 opt2path[cnt] = [name, subname]
[31fb9a0]556 cnt += 1
[a35b458]557
[ba8de9c3]558 if not subprofile:
[31fb9a0]559 options.append(name)
[6ec0acd]560 opt2path[cnt] = [name]
[31fb9a0]561 cnt += 1
[a35b458]562
[31fb9a0]563 (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
[a35b458]564
[ba8de9c3]565 if button == 'cancel':
[31fb9a0]566 return None
[a35b458]567
[6ec0acd]568 return opt2path[value]
569
570## Read presets from a configuration profile.
571#
[d40ffbb]572# @param profile Profile to load from (a list of string components)
573# @param config Output configuration
[6ec0acd]574#
[d40ffbb]575def read_presets(profile, config):
[6ec0acd]576 path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE)
577 read_config(path, config)
[a35b458]578
[6ec0acd]579 if len(profile) > 1:
580 path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE)
581 read_config(path, config)
582
583## Parse profile name (relative OS path) into a list of components.
584#
[d40ffbb]585# @param profile_name Relative path (using OS separator)
586# @return List of components
[6ec0acd]587#
588def parse_profile_name(profile_name):
589 profile = []
[a35b458]590
[6ec0acd]591 head, tail = os.path.split(profile_name)
592 if head != '':
593 profile.append(head)
[a35b458]594
[6ec0acd]595 profile.append(tail)
596 return profile
[31fb9a0]597
[98376de]598def main():
[6ec0acd]599 profile = None
[62bb73e]600 config = {}
601 rules = []
[a35b458]602
[62bb73e]603 # Parse rules file
604 parse_rules(RULES_FILE, rules)
[a35b458]605
[421250e]606 # Input configuration file can be specified on command line
607 # otherwise configuration from previous run is used.
608 if len(sys.argv) >= 4:
[6ec0acd]609 profile = parse_profile_name(sys.argv[3])
[d40ffbb]610 read_presets(profile, config)
[6ec0acd]611 elif os.path.exists(MAKEFILE):
612 read_config(MAKEFILE, config)
[a35b458]613
[e3c3172]614 # Default mode: check values and regenerate configuration files
[ba8de9c3]615 if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
[62bb73e]616 if (infer_verify_choices(config, rules)):
[1f5c9c96]617 preprocess_config(config, rules)
[62bb73e]618 create_output(MAKEFILE, MACROS, config, rules)
[9a0367f]619 return 0
[a35b458]620
[e3c3172]621 # Hands-off mode: check values and regenerate configuration files,
622 # but no interactive fallback
623 if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
624 # We deliberately test sys.argv >= 4 because we do not want
625 # to read implicitly any possible previous run configuration
626 if len(sys.argv) < 4:
627 sys.stderr.write("Configuration error: No presets specified\n")
628 return 2
[a35b458]629
[e3c3172]630 if (infer_verify_choices(config, rules)):
[1f5c9c96]631 preprocess_config(config, rules)
[e3c3172]632 create_output(MAKEFILE, MACROS, config, rules)
633 return 0
[a35b458]634
[e3c3172]635 sys.stderr.write("Configuration error: The presets are ambiguous\n")
636 return 1
[a35b458]637
[62bb73e]638 # Check mode: only check configuration
[ba8de9c3]639 if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
640 if infer_verify_choices(config, rules):
[48c3d50]641 return 0
642 return 1
[a35b458]643
[f857e8b]644 # Random mode
645 if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
646 ok = random_choices(config, rules, 0)
647 if not ok:
648 sys.stderr.write("Internal error: unable to generate random config.\n")
649 return 2
650 if not infer_verify_choices(config, rules):
651 sys.stderr.write("Internal error: random configuration not consistent.\n")
652 return 2
653 preprocess_config(config, rules)
654 create_output(MAKEFILE, MACROS, config, rules)
[a35b458]655
[1b20da0]656 return 0
[a35b458]657
[27fb3d6]658 screen = xtui.screen_init()
[9a0367f]659 try:
660 selname = None
[31fb9a0]661 position = None
[9a0367f]662 while True:
[a35b458]663
[62bb73e]664 # Cancel out all values which have to be deduced
665 for varname, vartype, name, choices, cond in rules:
[ba8de9c3]666 if (vartype == 'y') and (varname in config) and (config[varname] == '*'):
[62bb73e]667 config[varname] = None
[a35b458]668
[9a0367f]669 options = []
670 opt2row = {}
[31fb9a0]671 cnt = 1
[a35b458]672
[31fb9a0]673 options.append(" --- Load preconfigured defaults ... ")
[a35b458]674
[62bb73e]675 for rule in rules:
[e4d540b]676 varname, vartype, name, choices, cond = rule
[a35b458]677
[ba8de9c3]678 if cond and (not check_condition(cond, config, rules)):
[9a0367f]679 continue
[a35b458]680
[ba8de9c3]681 if varname == selname:
[9a0367f]682 position = cnt
[a35b458]683
[ba8de9c3]684 if not varname in config:
[4756634]685 value = None
[9a0367f]686 else:
[4756634]687 value = config[varname]
[a35b458]688
[d40ffbb]689 if not validate_rule_value(rule, value):
[4756634]690 value = None
[a35b458]691
[d40ffbb]692 default = get_default_rule(rule)
[a35b458]693
[c0bd08d]694 #
695 # If we don't have a value but we do have
696 # a default, use it.
697 #
698 if value == None and default != None:
699 value = default
700 config[varname] = default
[a35b458]701
[d40ffbb]702 option = get_rule_option(rule, value)
[e4d540b]703 if option != None:
704 options.append(option)
[8fe3f832]705 else:
706 continue
[a35b458]707
[27fb3d6]708 opt2row[cnt] = (varname, vartype, name, choices)
[a35b458]709
[9a0367f]710 cnt += 1
[a35b458]711
[28f4adb]712 if (position != None) and (position >= len(options)):
[31fb9a0]713 position = None
[a35b458]714
[27fb3d6]715 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
[a35b458]716
[ba8de9c3]717 if button == 'cancel':
[9a0367f]718 return 'Configuration canceled'
[a35b458]719
[ba8de9c3]720 if button == 'done':
[62bb73e]721 if (infer_verify_choices(config, rules)):
[6346efd]722 break
723 else:
724 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
725 continue
[a35b458]726
[ba8de9c3]727 if value == 0:
[d40ffbb]728 profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
[6ec0acd]729 if profile != None:
[d40ffbb]730 read_presets(profile, config)
[31fb9a0]731 position = 1
732 continue
[a35b458]733
[31fb9a0]734 position = None
[ba8de9c3]735 if not value in opt2row:
[27fb3d6]736 raise RuntimeError("Error selecting value: %s" % value)
[a35b458]737
[27fb3d6]738 (selname, seltype, name, choices) = opt2row[value]
[a35b458]739
[ba8de9c3]740 if not selname in config:
[4756634]741 value = None
[27fb3d6]742 else:
[4756634]743 value = config[selname]
[a35b458]744
[ba8de9c3]745 if seltype == 'choice':
[4756634]746 config[selname] = subchoice(screen, name, choices, value)
[ba8de9c3]747 elif (seltype == 'y/n') or (seltype == 'n/y'):
748 if config[selname] == 'y':
[62bb73e]749 config[selname] = 'n'
[9a0367f]750 else:
[62bb73e]751 config[selname] = 'y'
[9a0367f]752 finally:
[27fb3d6]753 xtui.screen_done(screen)
[a35b458]754
[1f5c9c96]755 preprocess_config(config, rules)
[62bb73e]756 create_output(MAKEFILE, MACROS, config, rules)
[9a0367f]757 return 0
[98376de]758
759if __name__ == '__main__':
[43a10c4]760 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.