source: mainline/tools/config.py@ dd162f6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dd162f6 was 476ac3b, checked in by jzr <zarevucky.jiri@…>, 8 years ago

Add a config option to strip revision information from build outputs.

  • Property mode set to 100755
File size: 19.4 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"
[9a0367f]51
[28f4adb]52 inf = open(fname, 'r')
[9a0367f]53
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)
[9a0367f]58
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)"
[9a0367f]63
64 ctype = 'cnf'
65
[ba8de9c3]66 if (')|' in text) or ('|(' in text):
[9a0367f]67 ctype = 'dnf'
68
[ba8de9c3]69 if ctype == 'cnf':
[9a0367f]70 conds = text.split('&')
71 else:
72 conds = text.split('|')
73
74 for cond in conds:
[ba8de9c3]75 if cond.startswith('(') and cond.endswith(')'):
[9a0367f]76 cond = cond[1:-1]
77
[62bb73e]78 inside = check_inside(cond, config, ctype)
[9a0367f]79
80 if (ctype == 'cnf') and (not inside):
81 return False
82
[ba8de9c3]83 if (ctype == 'dnf') and inside:
[9a0367f]84 return True
85
[ba8de9c3]86 if ctype == 'cnf':
[9a0367f]87 return True
[1f5c9c96]88
[9a0367f]89 return False
[98376de]90
[62bb73e]91def check_inside(text, config, ctype):
[81c8d54]92 "Check for condition"
[9a0367f]93
[ba8de9c3]94 if ctype == 'cnf':
[9a0367f]95 conds = text.split('|')
96 else:
97 conds = text.split('&')
98
99 for cond in conds:
100 res = re.match(r'^(.*?)(!?=)(.*)$', cond)
[ba8de9c3]101 if not res:
[9a0367f]102 raise RuntimeError("Invalid condition: %s" % cond)
103
104 condname = res.group(1)
105 oper = res.group(2)
106 condval = res.group(3)
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'
[9a0367f]114
[ba8de9c3]115 if ctype == 'cnf':
[9a0367f]116 if (oper == '=') and (condval == varval):
117 return True
118
119 if (oper == '!=') and (condval != varval):
120 return True
121 else:
122 if (oper == '=') and (condval != varval):
123 return False
124
125 if (oper == '!=') and (condval == varval):
126 return False
127
[ba8de9c3]128 if ctype == 'cnf':
[9a0367f]129 return False
130
131 return True
[98376de]132
[62bb73e]133def parse_rules(fname, rules):
134 "Parse rules file"
[9a0367f]135
[28f4adb]136 inf = open(fname, 'r')
[9a0367f]137
138 name = ''
139 choices = []
140
141 for line in inf:
142
[ba8de9c3]143 if line.startswith('!'):
[9a0367f]144 # Ask a question
145 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
146
[ba8de9c3]147 if not res:
[9a0367f]148 raise RuntimeError("Weird line: %s" % line)
149
150 cond = res.group(1)
151 varname = res.group(2)
152 vartype = res.group(3)
153
[62bb73e]154 rules.append((varname, vartype, name, choices, cond))
[9a0367f]155 name = ''
156 choices = []
157 continue
158
[ba8de9c3]159 if line.startswith('@'):
[9a0367f]160 # Add new line into the 'choices' array
161 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
162
163 if not res:
164 raise RuntimeError("Bad line: %s" % line)
165
166 choices.append((res.group(2), res.group(3)))
167 continue
168
[ba8de9c3]169 if line.startswith('%'):
[9a0367f]170 # Name of the option
171 name = line[1:].strip()
172 continue
173
[ba8de9c3]174 if line.startswith('#') or (line == '\n'):
[9a0367f]175 # Comment or empty line
176 continue
177
178
179 raise RuntimeError("Unknown syntax: %s" % line)
180
181 inf.close()
[98376de]182
[9a0367f]183def yes_no(default):
184 "Return '*' if yes, ' ' if no"
185
[ba8de9c3]186 if default == 'y':
[9a0367f]187 return '*'
188
189 return ' '
[98376de]190
[27fb3d6]191def subchoice(screen, name, choices, default):
[9a0367f]192 "Return choice of choices"
193
[27fb3d6]194 maxkey = 0
195 for key, val in choices:
196 length = len(key)
197 if (length > maxkey):
198 maxkey = length
[9a0367f]199
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
206
207 options.append(" %-*s %s " % (maxkey, key, val))
208 cnt += 1
[9a0367f]209
[27fb3d6]210 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
[9a0367f]211
[ba8de9c3]212 if button == 'cancel':
[9a0367f]213 return None
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."
[9a0367f]230
[ba8de9c3]231 for rule in rules:
232 varname, vartype, name, choices, cond = rule
[d40ffbb]233
[ba8de9c3]234 if cond and (not check_condition(cond, config, rules)):
[9a0367f]235 continue
236
[ba8de9c3]237 if not varname in config:
[4756634]238 value = None
[e4d540b]239 else:
[4756634]240 value = config[varname]
[d40ffbb]241
242 if not validate_rule_value(rule, value):
[4756634]243 value = None
[d40ffbb]244
245 default = get_default_rule(rule)
[1f5c9c96]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
[d40ffbb]254
[ba8de9c3]255 if not varname in config:
[9a0367f]256 return False
257
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).
272# @return True if able to find a valid configuration
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
277
278 varname, vartype, name, choices, cond = rules[start_index]
279
280 # First check that this rule would make sense
281 if cond:
282 if not check_condition(cond, config, rules):
283 return random_choices(config, rules, start_index + 1)
284
285 # Remember previous choices for backtracking
286 yes_no = 0
287 choices_indexes = range(0, len(choices))
288 random.shuffle(choices_indexes)
289
290 # Remember current configuration value
291 old_value = None
292 try:
293 old_value = config[varname]
294 except KeyError:
295 old_value = None
296
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)
322
323 config[varname] = value
324
325 ok = random_choices(config, rules, start_index + 1)
326 if ok:
327 return True
328
329 try_counter = try_counter + 1
330
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]
337
338 return random_choices(config, rules, start_index + 1)
339
340
[e4d540b]341## Get default value from a rule.
[d40ffbb]342def get_default_rule(rule):
[e4d540b]343 varname, vartype, name, choices, cond = rule
[d40ffbb]344
[e4d540b]345 default = None
[d40ffbb]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)
[d40ffbb]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
[d40ffbb]373
[e4d540b]374 option = None
[d40ffbb]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)
[d40ffbb]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
405
406 if value == None:
407 return True
[d40ffbb]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)
[d40ffbb]426
[e4d540b]427 return True
428
[1f5c9c96]429def preprocess_config(config, rules):
430 "Preprocess configuration"
431
432 varname_mode = 'CONFIG_BFB_MODE'
433 varname_width = 'CONFIG_BFB_WIDTH'
434 varname_height = 'CONFIG_BFB_HEIGHT'
435
436 if varname_mode in config:
437 mode = config[varname_mode].partition('x')
438
439 config[varname_width] = mode[0]
440 rules.append((varname_width, 'choice', 'Default framebuffer width', None, None))
441
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"
[476ac3b]447
448 varname_strip = 'CONFIG_STRIP_REVISION_INFO'
449 strip_rev_info = (varname_strip in config) and (config[varname_strip] == 'y')
450
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())
456
[41e871f]457 timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp_unix))
[fe12f9f4]458
459 sys.stderr.write("Fetching current revision identifier ... ")
[7b76744]460
461 try:
[28f4adb]462 version = subprocess.Popen(['bzr', 'version-info', '--custom', '--template={clean}:{revno}:{revision_id}'], stdout = subprocess.PIPE).communicate()[0].decode().split(':')
[7b76744]463 sys.stderr.write("ok\n")
464 except:
465 version = [1, "unknown", "unknown"]
466 sys.stderr.write("failed\n")
[5a8fbcb9]467
[476ac3b]468 if (not strip_rev_info) and (len(version) == 3):
[5a8fbcb9]469 revision = version[1]
[ba8de9c3]470 if version[0] != 1:
[5a8fbcb9]471 revision += 'M'
472 revision += ' (%s)' % version[2]
473 else:
474 revision = None
[84266669]475
[28f4adb]476 outmk = open(mkname, 'w')
477 outmc = open(mcname, 'w')
[84266669]478
479 outmk.write('#########################################\n')
480 outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
[571239a]481 outmk.write('## Generated by: tools/config.py ##\n')
[84266669]482 outmk.write('#########################################\n\n')
[9a0367f]483
[84266669]484 outmc.write('/***************************************\n')
485 outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
[571239a]486 outmc.write(' * Generated by: tools/config.py *\n')
[84266669]487 outmc.write(' ***************************************/\n\n')
488
[4e9aaf5]489 defs = 'CONFIG_DEFS ='
[9a0367f]490
[62bb73e]491 for varname, vartype, name, choices, cond in rules:
[ba8de9c3]492 if cond and (not check_condition(cond, config, rules)):
[9a0367f]493 continue
494
[ba8de9c3]495 if not varname in config:
[4756634]496 value = ''
[9a0367f]497 else:
[4756634]498 value = config[varname]
499 if (value == '*'):
500 value = 'y'
[9a0367f]501
[4756634]502 outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))
[84266669]503
[ba8de9c3]504 if vartype in ["y", "n", "y/n", "n/y"]:
505 if value == "y":
[84266669]506 outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
[4e9aaf5]507 defs += ' -D%s' % varname
[84266669]508 else:
[4756634]509 outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))
510 defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)
[9a0367f]511
[ba8de9c3]512 if revision is not None:
[5a8fbcb9]513 outmk.write('REVISION = %s\n' % revision)
514 outmc.write('#define REVISION %s\n' % revision)
[4e9aaf5]515 defs += ' "-DREVISION=%s"' % revision
[84266669]516
[41e871f]517 outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix)
518 outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix)
519 defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix
520
[5a8fbcb9]521 outmk.write('TIMESTAMP = %s\n' % timestamp)
[84266669]522 outmc.write('#define TIMESTAMP %s\n' % timestamp)
[4e9aaf5]523 defs += ' "-DTIMESTAMP=%s"\n' % timestamp
524
525 outmk.write(defs)
[84266669]526
527 outmk.close()
528 outmc.close()
[98376de]529
[31fb9a0]530def sorted_dir(root):
531 list = os.listdir(root)
532 list.sort()
533 return list
534
[6ec0acd]535## Ask user to choose a configuration profile.
[62bb73e]536#
[d40ffbb]537def choose_profile(root, fname, screen, config):
[31fb9a0]538 options = []
539 opt2path = {}
540 cnt = 0
541
542 # Look for profiles
543 for name in sorted_dir(root):
544 path = os.path.join(root, name)
545 canon = os.path.join(path, fname)
546
[ba8de9c3]547 if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):
[31fb9a0]548 subprofile = False
549
550 # Look for subprofiles
551 for subname in sorted_dir(path):
552 subpath = os.path.join(path, subname)
553 subcanon = os.path.join(subpath, fname)
554
[ba8de9c3]555 if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):
[31fb9a0]556 subprofile = True
557 options.append("%s (%s)" % (name, subname))
[6ec0acd]558 opt2path[cnt] = [name, subname]
[31fb9a0]559 cnt += 1
560
[ba8de9c3]561 if not subprofile:
[31fb9a0]562 options.append(name)
[6ec0acd]563 opt2path[cnt] = [name]
[31fb9a0]564 cnt += 1
565
566 (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
567
[ba8de9c3]568 if button == 'cancel':
[31fb9a0]569 return None
570
[6ec0acd]571 return opt2path[value]
572
573## Read presets from a configuration profile.
574#
[d40ffbb]575# @param profile Profile to load from (a list of string components)
576# @param config Output configuration
[6ec0acd]577#
[d40ffbb]578def read_presets(profile, config):
[6ec0acd]579 path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE)
580 read_config(path, config)
[d40ffbb]581
[6ec0acd]582 if len(profile) > 1:
583 path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE)
584 read_config(path, config)
585
586## Parse profile name (relative OS path) into a list of components.
587#
[d40ffbb]588# @param profile_name Relative path (using OS separator)
589# @return List of components
[6ec0acd]590#
591def parse_profile_name(profile_name):
592 profile = []
[d40ffbb]593
[6ec0acd]594 head, tail = os.path.split(profile_name)
595 if head != '':
596 profile.append(head)
[d40ffbb]597
[6ec0acd]598 profile.append(tail)
599 return profile
[31fb9a0]600
[98376de]601def main():
[6ec0acd]602 profile = None
[62bb73e]603 config = {}
604 rules = []
[9a0367f]605
[62bb73e]606 # Parse rules file
607 parse_rules(RULES_FILE, rules)
[9a0367f]608
[421250e]609 # Input configuration file can be specified on command line
610 # otherwise configuration from previous run is used.
611 if len(sys.argv) >= 4:
[6ec0acd]612 profile = parse_profile_name(sys.argv[3])
[d40ffbb]613 read_presets(profile, config)
[6ec0acd]614 elif os.path.exists(MAKEFILE):
615 read_config(MAKEFILE, config)
[9a0367f]616
[e3c3172]617 # Default mode: check values and regenerate configuration files
[ba8de9c3]618 if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'):
[62bb73e]619 if (infer_verify_choices(config, rules)):
[1f5c9c96]620 preprocess_config(config, rules)
[62bb73e]621 create_output(MAKEFILE, MACROS, config, rules)
[9a0367f]622 return 0
623
[e3c3172]624 # Hands-off mode: check values and regenerate configuration files,
625 # but no interactive fallback
626 if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'):
627 # We deliberately test sys.argv >= 4 because we do not want
628 # to read implicitly any possible previous run configuration
629 if len(sys.argv) < 4:
630 sys.stderr.write("Configuration error: No presets specified\n")
631 return 2
632
633 if (infer_verify_choices(config, rules)):
[1f5c9c96]634 preprocess_config(config, rules)
[e3c3172]635 create_output(MAKEFILE, MACROS, config, rules)
636 return 0
637
638 sys.stderr.write("Configuration error: The presets are ambiguous\n")
639 return 1
640
[62bb73e]641 # Check mode: only check configuration
[ba8de9c3]642 if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'):
643 if infer_verify_choices(config, rules):
[48c3d50]644 return 0
645 return 1
646
[f857e8b]647 # Random mode
648 if (len(sys.argv) == 3) and (sys.argv[2] == 'random'):
649 ok = random_choices(config, rules, 0)
650 if not ok:
651 sys.stderr.write("Internal error: unable to generate random config.\n")
652 return 2
653 if not infer_verify_choices(config, rules):
654 sys.stderr.write("Internal error: random configuration not consistent.\n")
655 return 2
656 preprocess_config(config, rules)
657 create_output(MAKEFILE, MACROS, config, rules)
658
659 return 0
660
[27fb3d6]661 screen = xtui.screen_init()
[9a0367f]662 try:
663 selname = None
[31fb9a0]664 position = None
[9a0367f]665 while True:
666
[62bb73e]667 # Cancel out all values which have to be deduced
668 for varname, vartype, name, choices, cond in rules:
[ba8de9c3]669 if (vartype == 'y') and (varname in config) and (config[varname] == '*'):
[62bb73e]670 config[varname] = None
[81c8d54]671
[9a0367f]672 options = []
673 opt2row = {}
[31fb9a0]674 cnt = 1
675
676 options.append(" --- Load preconfigured defaults ... ")
677
[62bb73e]678 for rule in rules:
[e4d540b]679 varname, vartype, name, choices, cond = rule
[9a0367f]680
[ba8de9c3]681 if cond and (not check_condition(cond, config, rules)):
[9a0367f]682 continue
683
[ba8de9c3]684 if varname == selname:
[9a0367f]685 position = cnt
686
[ba8de9c3]687 if not varname in config:
[4756634]688 value = None
[9a0367f]689 else:
[4756634]690 value = config[varname]
[9a0367f]691
[d40ffbb]692 if not validate_rule_value(rule, value):
[4756634]693 value = None
[d40ffbb]694
695 default = get_default_rule(rule)
[1f5c9c96]696
[c0bd08d]697 #
698 # If we don't have a value but we do have
699 # a default, use it.
700 #
701 if value == None and default != None:
702 value = default
703 config[varname] = default
[d40ffbb]704
705 option = get_rule_option(rule, value)
[e4d540b]706 if option != None:
707 options.append(option)
[8fe3f832]708 else:
709 continue
[9a0367f]710
[27fb3d6]711 opt2row[cnt] = (varname, vartype, name, choices)
[9a0367f]712
713 cnt += 1
714
[28f4adb]715 if (position != None) and (position >= len(options)):
[31fb9a0]716 position = None
717
[27fb3d6]718 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
[9a0367f]719
[ba8de9c3]720 if button == 'cancel':
[9a0367f]721 return 'Configuration canceled'
722
[ba8de9c3]723 if button == 'done':
[62bb73e]724 if (infer_verify_choices(config, rules)):
[6346efd]725 break
726 else:
727 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
728 continue
729
[ba8de9c3]730 if value == 0:
[d40ffbb]731 profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config)
[6ec0acd]732 if profile != None:
[d40ffbb]733 read_presets(profile, config)
[31fb9a0]734 position = 1
735 continue
736
737 position = None
[ba8de9c3]738 if not value in opt2row:
[27fb3d6]739 raise RuntimeError("Error selecting value: %s" % value)
740
741 (selname, seltype, name, choices) = opt2row[value]
[9a0367f]742
[ba8de9c3]743 if not selname in config:
[4756634]744 value = None
[27fb3d6]745 else:
[4756634]746 value = config[selname]
[9a0367f]747
[ba8de9c3]748 if seltype == 'choice':
[4756634]749 config[selname] = subchoice(screen, name, choices, value)
[ba8de9c3]750 elif (seltype == 'y/n') or (seltype == 'n/y'):
751 if config[selname] == 'y':
[62bb73e]752 config[selname] = 'n'
[9a0367f]753 else:
[62bb73e]754 config[selname] = 'y'
[9a0367f]755 finally:
[27fb3d6]756 xtui.screen_done(screen)
[9a0367f]757
[1f5c9c96]758 preprocess_config(config, rules)
[62bb73e]759 create_output(MAKEFILE, MACROS, config, rules)
[9a0367f]760 return 0
[98376de]761
762if __name__ == '__main__':
[43a10c4]763 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.