Changes in tools/config.py [c01f8e6:5a8fbcb] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
tools/config.py
rc01f8e6 r5a8fbcb 3 3 # Copyright (c) 2006 Ondrej Palkovsky 4 4 # Copyright (c) 2009 Martin Decky 5 # Copyright (c) 2010 Jiri Svoboda6 5 # All rights reserved. 7 6 # … … 29 28 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 29 # 31 32 30 """ 33 31 HelenOS configuration system 34 32 """ 35 36 33 import sys 37 34 import os … … 41 38 import xtui 42 39 43 RULES_FILE= sys.argv[1]40 INPUT = sys.argv[1] 44 41 MAKEFILE = 'Makefile.config' 45 42 MACROS = 'config.h' 46 PRESETS_DIR = 'defaults' 47 48 def read_config(fname, config): 49 "Read saved values from last configuration run or a preset file" 50 51 inf = open(fname, 'r') 43 DEFS = 'config.defs' 44 PRECONF = 'defaults' 45 46 def read_defaults(fname, defaults): 47 "Read saved values from last configuration run" 48 49 inf = file(fname, 'r') 52 50 53 51 for line in inf: 54 52 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line) 55 if res:56 config[res.group(1)] = res.group(2)53 if (res): 54 defaults[res.group(1)] = res.group(2) 57 55 58 56 inf.close() 59 57 60 def check_condition(text, config, rules):58 def check_condition(text, defaults, ask_names): 61 59 "Check that the condition specified on input line is True (only CNF and DNF is supported)" 62 60 63 61 ctype = 'cnf' 64 62 65 if ( ')|' in text) or ('|(' in text):63 if ((')|' in text) or ('|(' in text)): 66 64 ctype = 'dnf' 67 65 68 if ctype == 'cnf':66 if (ctype == 'cnf'): 69 67 conds = text.split('&') 70 68 else: … … 72 70 73 71 for cond in conds: 74 if cond.startswith('(') and cond.endswith(')'):72 if (cond.startswith('(')) and (cond.endswith(')')): 75 73 cond = cond[1:-1] 76 74 77 inside = check_inside(cond, config, ctype)75 inside = check_inside(cond, defaults, ctype) 78 76 79 77 if (ctype == 'cnf') and (not inside): 80 78 return False 81 79 82 if (ctype == 'dnf') and inside:80 if (ctype == 'dnf') and (inside): 83 81 return True 84 82 85 if ctype == 'cnf':83 if (ctype == 'cnf'): 86 84 return True 87 85 return False 88 86 89 def check_inside(text, config, ctype):87 def check_inside(text, defaults, ctype): 90 88 "Check for condition" 91 89 92 if ctype == 'cnf':90 if (ctype == 'cnf'): 93 91 conds = text.split('|') 94 92 else: … … 97 95 for cond in conds: 98 96 res = re.match(r'^(.*?)(!?=)(.*)$', cond) 99 if not res:97 if (not res): 100 98 raise RuntimeError("Invalid condition: %s" % cond) 101 99 … … 104 102 condval = res.group(3) 105 103 106 if not condname in config:104 if (not defaults.has_key(condname)): 107 105 varval = '' 108 106 else: 109 varval = config[condname]107 varval = defaults[condname] 110 108 if (varval == '*'): 111 109 varval = 'y' 112 110 113 if ctype == 'cnf':111 if (ctype == 'cnf'): 114 112 if (oper == '=') and (condval == varval): 115 113 return True … … 124 122 return False 125 123 126 if ctype == 'cnf':124 if (ctype == 'cnf'): 127 125 return False 128 126 129 127 return True 130 128 131 def parse_ rules(fname, rules):132 "Parse rulesfile"133 134 inf = open(fname, 'r')129 def parse_config(fname, ask_names): 130 "Parse configuration file" 131 132 inf = file(fname, 'r') 135 133 136 134 name = '' … … 139 137 for line in inf: 140 138 141 if line.startswith('!'):139 if (line.startswith('!')): 142 140 # Ask a question 143 141 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line) 144 142 145 if not res:143 if (not res): 146 144 raise RuntimeError("Weird line: %s" % line) 147 145 … … 150 148 vartype = res.group(3) 151 149 152 rules.append((varname, vartype, name, choices, cond))150 ask_names.append((varname, vartype, name, choices, cond)) 153 151 name = '' 154 152 choices = [] 155 153 continue 156 154 157 if line.startswith('@'):155 if (line.startswith('@')): 158 156 # Add new line into the 'choices' array 159 157 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line) … … 165 163 continue 166 164 167 if line.startswith('%'):165 if (line.startswith('%')): 168 166 # Name of the option 169 167 name = line[1:].strip() 170 168 continue 171 169 172 if line.startswith('#') or (line == '\n'):170 if ((line.startswith('#')) or (line == '\n')): 173 171 # Comment or empty line 174 172 continue … … 182 180 "Return '*' if yes, ' ' if no" 183 181 184 if default == 'y':182 if (default == 'y'): 185 183 return '*' 186 184 … … 200 198 cnt = 0 201 199 for key, val in choices: 202 if ( default) and (key == default):200 if ((default) and (key == default)): 203 201 position = cnt 204 202 … … 208 206 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position) 209 207 210 if button == 'cancel':208 if (button == 'cancel'): 211 209 return None 212 210 213 211 return choices[value][0] 214 212 215 ## Infer and verify configuration values. 216 # 217 # Augment @a config with values that can be inferred, purge invalid ones 218 # and verify that all variables have a value (previously specified or inferred). 219 # 220 # @param config Configuration to work on 221 # @param rules Rules 222 # 223 # @return True if configuration is complete and valid, False 224 # otherwise. 225 # 226 def infer_verify_choices(config, rules): 227 "Infer and verify configuration values." 228 229 for rule in rules: 230 varname, vartype, name, choices, cond = rule 231 232 if cond and (not check_condition(cond, config, rules)): 233 continue 234 235 if not varname in config: 236 value = None 237 else: 238 value = config[varname] 239 240 if not validate_rule_value(rule, value): 241 value = None 242 243 default = get_default_rule(rule) 244 245 # 246 # If we don't have a value but we do have 247 # a default, use it. 248 # 249 if value == None and default != None: 250 value = default 251 config[varname] = default 252 253 if not varname in config: 213 def check_choices(defaults, ask_names): 214 "Check whether all accessible variables have a default" 215 216 for varname, vartype, name, choices, cond in ask_names: 217 if ((cond) and (not check_condition(cond, defaults, ask_names))): 218 continue 219 220 if (not defaults.has_key(varname)): 254 221 return False 255 222 256 223 return True 257 224 258 ## Get default value from a rule. 259 def get_default_rule(rule): 260 varname, vartype, name, choices, cond = rule 261 262 default = None 263 264 if vartype == 'choice': 265 # If there is just one option, use it 266 if len(choices) == 1: 267 default = choices[0][0] 268 elif vartype == 'y': 269 default = '*' 270 elif vartype == 'n': 271 default = 'n' 272 elif vartype == 'y/n': 273 default = 'y' 274 elif vartype == 'n/y': 275 default = 'n' 276 else: 277 raise RuntimeError("Unknown variable type: %s" % vartype) 278 279 return default 280 281 ## Get option from a rule. 282 # 283 # @param rule Rule for a variable 284 # @param value Current value of the variable 285 # 286 # @return Option (string) to ask or None which means not to ask. 287 # 288 def get_rule_option(rule, value): 289 varname, vartype, name, choices, cond = rule 290 291 option = None 292 293 if vartype == 'choice': 294 # If there is just one option, don't ask 295 if len(choices) != 1: 296 if (value == None): 297 option = "? %s --> " % name 298 else: 299 option = " %s [%s] --> " % (name, value) 300 elif vartype == 'y': 301 pass 302 elif vartype == 'n': 303 pass 304 elif vartype == 'y/n': 305 option = " <%s> %s " % (yes_no(value), name) 306 elif vartype == 'n/y': 307 option =" <%s> %s " % (yes_no(value), name) 308 else: 309 raise RuntimeError("Unknown variable type: %s" % vartype) 310 311 return option 312 313 ## Check if variable value is valid. 314 # 315 # @param rule Rule for the variable 316 # @param value Value of the variable 317 # 318 # @return True if valid, False if not valid. 319 # 320 def validate_rule_value(rule, value): 321 varname, vartype, name, choices, cond = rule 322 323 if value == None: 324 return True 325 326 if vartype == 'choice': 327 if not value in [choice[0] for choice in choices]: 328 return False 329 elif vartype == 'y': 330 if value != 'y': 331 return False 332 elif vartype == 'n': 333 if value != 'n': 334 return False 335 elif vartype == 'y/n': 336 if not value in ['y', 'n']: 337 return False 338 elif vartype == 'n/y': 339 if not value in ['y', 'n']: 340 return False 341 else: 342 raise RuntimeError("Unknown variable type: %s" % vartype) 343 344 return True 345 346 def create_output(mkname, mcname, config, rules): 225 def create_output(mkname, mcname, dfname, defaults, ask_names): 347 226 "Create output configuration" 348 227 349 228 timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 350 351 sys.stderr.write("Fetching current revision identifier ... ") 352 353 try: 354 version = subprocess.Popen(['bzr', 'version-info', '--custom', '--template={clean}:{revno}:{revision_id}'], stdout = subprocess.PIPE).communicate()[0].decode().split(':') 355 sys.stderr.write("ok\n") 356 except: 357 version = [1, "unknown", "unknown"] 358 sys.stderr.write("failed\n") 359 360 if len(version) == 3: 229 version = subprocess.Popen(['bzr', 'version-info', '--custom', '--template={clean}:{revno}:{revision_id}'], stdout = subprocess.PIPE).communicate()[0].split(':') 230 231 if (len(version) == 3): 361 232 revision = version[1] 362 if version[0] != 1:233 if (version[0] != 1): 363 234 revision += 'M' 364 235 revision += ' (%s)' % version[2] … … 366 237 revision = None 367 238 368 outmk = open(mkname, 'w') 369 outmc = open(mcname, 'w') 239 outmk = file(mkname, 'w') 240 outmc = file(mcname, 'w') 241 outdf = file(dfname, 'w') 370 242 371 243 outmk.write('#########################################\n') … … 377 249 outmc.write(' ***************************************/\n\n') 378 250 379 defs = 'CONFIG_DEFS =' 380 381 for varname, vartype, name, choices, cond in rules: 382 if cond and (not check_condition(cond, config, rules)): 383 continue 384 385 if not varname in config: 386 value = '' 251 outdf.write('#########################################\n') 252 outdf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n') 253 outdf.write('#########################################\n\n') 254 outdf.write('CONFIG_DEFS =') 255 256 for varname, vartype, name, choices, cond in ask_names: 257 if ((cond) and (not check_condition(cond, defaults, ask_names))): 258 continue 259 260 if (not defaults.has_key(varname)): 261 default = '' 387 262 else: 388 value = config[varname]389 if ( value== '*'):390 value= 'y'391 392 outmk.write('# %s\n%s = %s\n\n' % (name, varname, value))393 394 if vartype in ["y", "n", "y/n", "n/y"]:395 if value == "y":263 default = defaults[varname] 264 if (default == '*'): 265 default = 'y' 266 267 outmk.write('# %s\n%s = %s\n\n' % (name, varname, default)) 268 269 if ((vartype == "y") or (vartype == "n") or (vartype == "y/n") or (vartype == "n/y")): 270 if (default == "y"): 396 271 outmc.write('/* %s */\n#define %s\n\n' % (name, varname)) 397 defs += ' -D%s' % varname272 outdf.write(' -D%s' % varname) 398 273 else: 399 outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value))400 defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value)401 402 if revision is not None:274 outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default)) 275 outdf.write(' -D%s=%s -D%s_%s' % (varname, default, varname, default)) 276 277 if (revision is not None): 403 278 outmk.write('REVISION = %s\n' % revision) 404 279 outmc.write('#define REVISION %s\n' % revision) 405 defs += ' "-DREVISION=%s"' % revision280 outdf.write(' "-DREVISION=%s"' % revision) 406 281 407 282 outmk.write('TIMESTAMP = %s\n' % timestamp) 408 283 outmc.write('#define TIMESTAMP %s\n' % timestamp) 409 defs += ' "-DTIMESTAMP=%s"\n' % timestamp 410 411 outmk.write(defs) 284 outdf.write(' "-DTIMESTAMP=%s"\n' % timestamp) 412 285 413 286 outmk.close() 414 287 outmc.close() 288 outdf.close() 415 289 416 290 def sorted_dir(root): … … 419 293 return list 420 294 421 ## Ask user to choose a configuration profile. 422 # 423 def choose_profile(root, fname, screen, config): 295 def read_preconfigured(root, fname, screen, defaults): 424 296 options = [] 425 297 opt2path = {} … … 431 303 canon = os.path.join(path, fname) 432 304 433 if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon):305 if ((os.path.isdir(path)) and (os.path.exists(canon)) and (os.path.isfile(canon))): 434 306 subprofile = False 435 307 … … 439 311 subcanon = os.path.join(subpath, fname) 440 312 441 if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon):313 if ((os.path.isdir(subpath)) and (os.path.exists(subcanon)) and (os.path.isfile(subcanon))): 442 314 subprofile = True 443 315 options.append("%s (%s)" % (name, subname)) 444 opt2path[cnt] = [name, subname]316 opt2path[cnt] = (canon, subcanon) 445 317 cnt += 1 446 318 447 if not subprofile:319 if (not subprofile): 448 320 options.append(name) 449 opt2path[cnt] = [name]321 opt2path[cnt] = (canon, None) 450 322 cnt += 1 451 323 452 324 (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None) 453 325 454 if button == 'cancel':326 if (button == 'cancel'): 455 327 return None 456 328 457 return opt2path[value] 458 459 ## Read presets from a configuration profile. 460 # 461 # @param profile Profile to load from (a list of string components) 462 # @param config Output configuration 463 # 464 def read_presets(profile, config): 465 path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE) 466 read_config(path, config) 467 468 if len(profile) > 1: 469 path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE) 470 read_config(path, config) 471 472 ## Parse profile name (relative OS path) into a list of components. 473 # 474 # @param profile_name Relative path (using OS separator) 475 # @return List of components 476 # 477 def parse_profile_name(profile_name): 478 profile = [] 479 480 head, tail = os.path.split(profile_name) 481 if head != '': 482 profile.append(head) 483 484 profile.append(tail) 485 return profile 329 read_defaults(opt2path[value][0], defaults) 330 if (opt2path[value][1] != None): 331 read_defaults(opt2path[value][1], defaults) 486 332 487 333 def main(): 488 profile = None 489 config = {} 490 rules = [] 491 492 # Parse rules file 493 parse_rules(RULES_FILE, rules) 494 495 # Input configuration file can be specified on command line 496 # otherwise configuration from previous run is used. 497 if len(sys.argv) >= 4: 498 profile = parse_profile_name(sys.argv[3]) 499 read_presets(profile, config) 500 elif os.path.exists(MAKEFILE): 501 read_config(MAKEFILE, config) 502 503 # Default mode: check values and regenerate configuration files 504 if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'): 505 if (infer_verify_choices(config, rules)): 506 create_output(MAKEFILE, MACROS, config, rules) 334 defaults = {} 335 ask_names = [] 336 337 # Parse configuration file 338 parse_config(INPUT, ask_names) 339 340 # Read defaults from previous run 341 if os.path.exists(MAKEFILE): 342 read_defaults(MAKEFILE, defaults) 343 344 # Default mode: only check defaults and regenerate configuration 345 if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')): 346 if (check_choices(defaults, ask_names)): 347 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names) 507 348 return 0 508 349 509 # Hands-off mode: check values and regenerate configuration files, 510 # but no interactive fallback 511 if (len(sys.argv) >= 3) and (sys.argv[2] == 'hands-off'): 512 # We deliberately test sys.argv >= 4 because we do not want 513 # to read implicitly any possible previous run configuration 514 if len(sys.argv) < 4: 515 sys.stderr.write("Configuration error: No presets specified\n") 516 return 2 517 518 if (infer_verify_choices(config, rules)): 519 create_output(MAKEFILE, MACROS, config, rules) 520 return 0 521 522 sys.stderr.write("Configuration error: The presets are ambiguous\n") 523 return 1 524 525 # Check mode: only check configuration 526 if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'): 527 if infer_verify_choices(config, rules): 350 # Check mode: only check defaults 351 if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')): 352 if (check_choices(defaults, ask_names)): 528 353 return 0 529 354 return 1 … … 535 360 while True: 536 361 537 # Cancel out all values which have to be deduced538 for varname, vartype, name, choices, cond in rules:539 if ( vartype == 'y') and (varname in config) and (config[varname] == '*'):540 config[varname] = None362 # Cancel out all defaults which have to be deduced 363 for varname, vartype, name, choices, cond in ask_names: 364 if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')): 365 defaults[varname] = None 541 366 542 367 options = [] … … 546 371 options.append(" --- Load preconfigured defaults ... ") 547 372 548 for rule in rules: 549 varname, vartype, name, choices, cond = rule 550 551 if cond and (not check_condition(cond, config, rules)): 373 for varname, vartype, name, choices, cond in ask_names: 374 375 if ((cond) and (not check_condition(cond, defaults, ask_names))): 552 376 continue 553 377 554 if varname == selname:378 if (varname == selname): 555 379 position = cnt 556 380 557 if not varname in config:558 value= None381 if (not defaults.has_key(varname)): 382 default = None 559 383 else: 560 value = config[varname] 561 562 if not validate_rule_value(rule, value): 563 value = None 564 565 default = get_default_rule(rule) 566 567 # 568 # If we don't have a value but we do have 569 # a default, use it. 570 # 571 if value == None and default != None: 572 value = default 573 config[varname] = default 574 575 option = get_rule_option(rule, value) 576 if option != None: 577 options.append(option) 384 default = defaults[varname] 385 386 if (vartype == 'choice'): 387 # Check if the default is an acceptable value 388 if ((default) and (not default in [choice[0] for choice in choices])): 389 default = None 390 defaults.pop(varname) 391 392 # If there is just one option, use it 393 if (len(choices) == 1): 394 defaults[varname] = choices[0][0] 395 continue 396 397 if (default == None): 398 options.append("? %s --> " % name) 399 else: 400 options.append(" %s [%s] --> " % (name, default)) 401 elif (vartype == 'y'): 402 defaults[varname] = '*' 403 continue 404 elif (vartype == 'n'): 405 defaults[varname] = 'n' 406 continue 407 elif (vartype == 'y/n'): 408 if (default == None): 409 default = 'y' 410 defaults[varname] = default 411 options.append(" <%s> %s " % (yes_no(default), name)) 412 elif (vartype == 'n/y'): 413 if (default == None): 414 default = 'n' 415 defaults[varname] = default 416 options.append(" <%s> %s " % (yes_no(default), name)) 578 417 else: 579 continue418 raise RuntimeError("Unknown variable type: %s" % vartype) 580 419 581 420 opt2row[cnt] = (varname, vartype, name, choices) … … 583 422 cnt += 1 584 423 585 if (position != None) and (position >= len(options)):424 if (position >= options): 586 425 position = None 587 426 588 427 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position) 589 428 590 if button == 'cancel':429 if (button == 'cancel'): 591 430 return 'Configuration canceled' 592 431 593 if button == 'done':594 if ( infer_verify_choices(config, rules)):432 if (button == 'done'): 433 if (check_choices(defaults, ask_names)): 595 434 break 596 435 else: … … 598 437 continue 599 438 600 if value == 0: 601 profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config) 602 if profile != None: 603 read_presets(profile, config) 439 if (value == 0): 440 read_preconfigured(PRECONF, MAKEFILE, screen, defaults) 604 441 position = 1 605 442 continue 606 443 607 444 position = None 608 if not value in opt2row:445 if (not opt2row.has_key(value)): 609 446 raise RuntimeError("Error selecting value: %s" % value) 610 447 611 448 (selname, seltype, name, choices) = opt2row[value] 612 449 613 if not selname in config:614 value= None450 if (not defaults.has_key(selname)): 451 default = None 615 452 else: 616 value = config[selname]617 618 if seltype == 'choice':619 config[selname] = subchoice(screen, name, choices, value)620 elif ( seltype == 'y/n') or (seltype == 'n/y'):621 if config[selname] == 'y':622 config[selname] = 'n'453 default = defaults[selname] 454 455 if (seltype == 'choice'): 456 defaults[selname] = subchoice(screen, name, choices, default) 457 elif ((seltype == 'y/n') or (seltype == 'n/y')): 458 if (defaults[selname] == 'y'): 459 defaults[selname] = 'n' 623 460 else: 624 config[selname] = 'y'461 defaults[selname] = 'y' 625 462 finally: 626 463 xtui.screen_done(screen) 627 464 628 create_output(MAKEFILE, MACROS, config, rules)465 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names) 629 466 return 0 630 467
Note:
See TracChangeset
for help on using the changeset viewer.