Changeset a35b458 in mainline for tools/config.py
- Timestamp:
- 2018-03-02T20:10:49Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- f1380b7
- Parents:
- 3061bc1
- git-author:
- Jiří Zárevúcky <zarevucky.jiri@…> (2018-02-28 17:38:31)
- git-committer:
- Jiří Zárevúcky <zarevucky.jiri@…> (2018-03-02 20:10:49)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
tools/config.py
r3061bc1 ra35b458 49 49 def read_config(fname, config): 50 50 "Read saved values from last configuration run or a preset file" 51 51 52 52 inf = open(fname, 'r') 53 53 54 54 for line in inf: 55 55 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line) 56 56 if res: 57 57 config[res.group(1)] = res.group(2) 58 58 59 59 inf.close() 60 60 61 61 def check_condition(text, config, rules): 62 62 "Check that the condition specified on input line is True (only CNF and DNF is supported)" 63 63 64 64 ctype = 'cnf' 65 65 66 66 if (')|' in text) or ('|(' in text): 67 67 ctype = 'dnf' 68 68 69 69 if ctype == 'cnf': 70 70 conds = text.split('&') 71 71 else: 72 72 conds = text.split('|') 73 73 74 74 for cond in conds: 75 75 if cond.startswith('(') and cond.endswith(')'): 76 76 cond = cond[1:-1] 77 77 78 78 inside = check_inside(cond, config, ctype) 79 79 80 80 if (ctype == 'cnf') and (not inside): 81 81 return False 82 82 83 83 if (ctype == 'dnf') and inside: 84 84 return True 85 85 86 86 if ctype == 'cnf': 87 87 return True 88 88 89 89 return False 90 90 91 91 def check_inside(text, config, ctype): 92 92 "Check for condition" 93 93 94 94 if ctype == 'cnf': 95 95 conds = text.split('|') 96 96 else: 97 97 conds = text.split('&') 98 98 99 99 for cond in conds: 100 100 res = re.match(r'^(.*?)(!?=)(.*)$', cond) 101 101 if not res: 102 102 raise RuntimeError("Invalid condition: %s" % cond) 103 103 104 104 condname = res.group(1) 105 105 oper = res.group(2) 106 106 condval = res.group(3) 107 107 108 108 if not condname in config: 109 109 varval = '' … … 112 112 if (varval == '*'): 113 113 varval = 'y' 114 114 115 115 if ctype == 'cnf': 116 116 if (oper == '=') and (condval == varval): 117 117 return True 118 118 119 119 if (oper == '!=') and (condval != varval): 120 120 return True … … 122 122 if (oper == '=') and (condval != varval): 123 123 return False 124 124 125 125 if (oper == '!=') and (condval == varval): 126 126 return False 127 127 128 128 if ctype == 'cnf': 129 129 return False 130 130 131 131 return True 132 132 133 133 def parse_rules(fname, rules): 134 134 "Parse rules file" 135 135 136 136 inf = open(fname, 'r') 137 137 138 138 name = '' 139 139 choices = [] 140 140 141 141 for line in inf: 142 142 143 143 if line.startswith('!'): 144 144 # Ask a question 145 145 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line) 146 146 147 147 if not res: 148 148 raise RuntimeError("Weird line: %s" % line) 149 149 150 150 cond = res.group(1) 151 151 varname = res.group(2) 152 152 vartype = res.group(3) 153 153 154 154 rules.append((varname, vartype, name, choices, cond)) 155 155 name = '' 156 156 choices = [] 157 157 continue 158 158 159 159 if line.startswith('@'): 160 160 # Add new line into the 'choices' array 161 161 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line) 162 162 163 163 if not res: 164 164 raise RuntimeError("Bad line: %s" % line) 165 165 166 166 choices.append((res.group(2), res.group(3))) 167 167 continue 168 168 169 169 if line.startswith('%'): 170 170 # Name of the option 171 171 name = line[1:].strip() 172 172 continue 173 173 174 174 if line.startswith('#') or (line == '\n'): 175 175 # Comment or empty line 176 176 continue 177 178 177 178 179 179 raise RuntimeError("Unknown syntax: %s" % line) 180 180 181 181 inf.close() 182 182 183 183 def yes_no(default): 184 184 "Return '*' if yes, ' ' if no" 185 185 186 186 if default == 'y': 187 187 return '*' 188 188 189 189 return ' ' 190 190 191 191 def subchoice(screen, name, choices, default): 192 192 "Return choice of choices" 193 193 194 194 maxkey = 0 195 195 for key, val in choices: … … 197 197 if (length > maxkey): 198 198 maxkey = length 199 199 200 200 options = [] 201 201 position = None … … 204 204 if (default) and (key == default): 205 205 position = cnt 206 206 207 207 options.append(" %-*s %s " % (maxkey, key, val)) 208 208 cnt += 1 209 209 210 210 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position) 211 211 212 212 if button == 'cancel': 213 213 return None 214 214 215 215 return choices[value][0] 216 216 … … 228 228 def infer_verify_choices(config, rules): 229 229 "Infer and verify configuration values." 230 230 231 231 for rule in rules: 232 232 varname, vartype, name, choices, cond = rule 233 233 234 234 if cond and (not check_condition(cond, config, rules)): 235 235 continue 236 236 237 237 if not varname in config: 238 238 value = None 239 239 else: 240 240 value = config[varname] 241 241 242 242 if not validate_rule_value(rule, value): 243 243 value = None 244 244 245 245 default = get_default_rule(rule) 246 246 247 247 # 248 248 # If we don't have a value but we do have … … 252 252 value = default 253 253 config[varname] = default 254 254 255 255 if not varname in config: 256 256 return False 257 257 258 258 return True 259 259 … … 275 275 if start_index >= len(rules): 276 276 return True 277 277 278 278 varname, vartype, name, choices, cond = rules[start_index] 279 279 … … 282 282 if not check_condition(cond, config, rules): 283 283 return random_choices(config, rules, start_index + 1) 284 284 285 285 # Remember previous choices for backtracking 286 286 yes_no = 0 287 287 choices_indexes = range(0, len(choices)) 288 288 random.shuffle(choices_indexes) 289 289 290 290 # Remember current configuration value 291 291 old_value = None … … 294 294 except KeyError: 295 295 old_value = None 296 296 297 297 # For yes/no choices, we ran the loop at most 2 times, for select 298 298 # choices as many times as there are options. … … 320 320 else: 321 321 raise RuntimeError("Unknown variable type: %s" % vartype) 322 322 323 323 config[varname] = value 324 324 325 325 ok = random_choices(config, rules, start_index + 1) 326 326 if ok: 327 327 return True 328 328 329 329 try_counter = try_counter + 1 330 330 331 331 # Restore the old value and backtrack 332 332 # (need to delete to prevent "ghost" variables that do not exist under … … 335 335 if old_value is None: 336 336 del config[varname] 337 337 338 338 return random_choices(config, rules, start_index + 1) 339 339 340 340 341 341 ## Get default value from a rule. 342 342 def get_default_rule(rule): 343 343 varname, vartype, name, choices, cond = rule 344 344 345 345 default = None 346 346 347 347 if vartype == 'choice': 348 348 # If there is just one option, use it … … 359 359 else: 360 360 raise RuntimeError("Unknown variable type: %s" % vartype) 361 361 362 362 return default 363 363 … … 371 371 def get_rule_option(rule, value): 372 372 varname, vartype, name, choices, cond = rule 373 373 374 374 option = None 375 375 376 376 if vartype == 'choice': 377 377 # If there is just one option, don't ask … … 391 391 else: 392 392 raise RuntimeError("Unknown variable type: %s" % vartype) 393 393 394 394 return option 395 395 … … 403 403 def validate_rule_value(rule, value): 404 404 varname, vartype, name, choices, cond = rule 405 405 406 406 if value == None: 407 407 return True 408 408 409 409 if vartype == 'choice': 410 410 if not value in [choice[0] for choice in choices]: … … 424 424 else: 425 425 raise RuntimeError("Unknown variable type: %s" % vartype) 426 426 427 427 return True 428 428 429 429 def preprocess_config(config, rules): 430 430 "Preprocess configuration" 431 431 432 432 varname_mode = 'CONFIG_BFB_MODE' 433 433 varname_width = 'CONFIG_BFB_WIDTH' 434 434 varname_height = 'CONFIG_BFB_HEIGHT' 435 435 436 436 if varname_mode in config: 437 437 mode = config[varname_mode].partition('x') 438 438 439 439 config[varname_width] = mode[0] 440 440 rules.append((varname_width, 'choice', 'Default framebuffer width', None, None)) 441 441 442 442 config[varname_height] = mode[2] 443 443 rules.append((varname_height, 'choice', 'Default framebuffer height', None, None)) … … 445 445 def create_output(mkname, mcname, config, rules): 446 446 "Create output configuration" 447 447 448 448 varname_strip = 'CONFIG_STRIP_REVISION_INFO' 449 449 strip_rev_info = (varname_strip in config) and (config[varname_strip] == 'y') 450 450 451 451 if strip_rev_info: 452 452 timestamp_unix = int(0) … … 454 454 # TODO: Use commit timestamp instead of build time. 455 455 timestamp_unix = int(time.time()) 456 456 457 457 timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp_unix)) 458 458 459 459 sys.stderr.write("Fetching current revision identifier ... ") 460 460 461 461 try: 462 462 version = subprocess.Popen(['git', 'log', '-1', '--pretty=%h'], stdout = subprocess.PIPE).communicate()[0].decode().strip() … … 465 465 version = None 466 466 sys.stderr.write("failed\n") 467 467 468 468 if (not strip_rev_info) and (version is not None): 469 469 revision = version 470 470 else: 471 471 revision = None 472 472 473 473 outmk = open(mkname, 'w') 474 474 outmc = open(mcname, 'w') 475 475 476 476 outmk.write('#########################################\n') 477 477 outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n') 478 478 outmk.write('## Generated by: tools/config.py ##\n') 479 479 outmk.write('#########################################\n\n') 480 480 481 481 outmc.write('/***************************************\n') 482 482 outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n') 483 483 outmc.write(' * Generated by: tools/config.py *\n') 484 484 outmc.write(' ***************************************/\n\n') 485 485 486 486 defs = 'CONFIG_DEFS =' 487 487 488 488 for varname, vartype, name, choices, cond in rules: 489 489 if cond and (not check_condition(cond, config, rules)): 490 490 continue 491 491 492 492 if not varname in config: 493 493 value = '' … … 496 496 if (value == '*'): 497 497 value = 'y' 498 498 499 499 outmk.write('# %s\n%s = %s\n\n' % (name, varname, value)) 500 500 501 501 if vartype in ["y", "n", "y/n", "n/y"]: 502 502 if value == "y": … … 506 506 outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, value, varname, value)) 507 507 defs += ' -D%s=%s -D%s_%s' % (varname, value, varname, value) 508 508 509 509 if revision is not None: 510 510 outmk.write('REVISION = %s\n' % revision) 511 511 outmc.write('#define REVISION %s\n' % revision) 512 512 defs += ' "-DREVISION=%s"' % revision 513 513 514 514 outmk.write('TIMESTAMP_UNIX = %d\n' % timestamp_unix) 515 515 outmc.write('#define TIMESTAMP_UNIX %d\n' % timestamp_unix) 516 516 defs += ' "-DTIMESTAMP_UNIX=%d"\n' % timestamp_unix 517 517 518 518 outmk.write('TIMESTAMP = %s\n' % timestamp) 519 519 outmc.write('#define TIMESTAMP %s\n' % timestamp) 520 520 defs += ' "-DTIMESTAMP=%s"\n' % timestamp 521 521 522 522 outmk.write(defs) 523 523 524 524 outmk.close() 525 525 outmc.close() … … 536 536 opt2path = {} 537 537 cnt = 0 538 538 539 539 # Look for profiles 540 540 for name in sorted_dir(root): 541 541 path = os.path.join(root, name) 542 542 canon = os.path.join(path, fname) 543 543 544 544 if os.path.isdir(path) and os.path.exists(canon) and os.path.isfile(canon): 545 545 subprofile = False 546 546 547 547 # Look for subprofiles 548 548 for subname in sorted_dir(path): 549 549 subpath = os.path.join(path, subname) 550 550 subcanon = os.path.join(subpath, fname) 551 551 552 552 if os.path.isdir(subpath) and os.path.exists(subcanon) and os.path.isfile(subcanon): 553 553 subprofile = True … … 555 555 opt2path[cnt] = [name, subname] 556 556 cnt += 1 557 557 558 558 if not subprofile: 559 559 options.append(name) 560 560 opt2path[cnt] = [name] 561 561 cnt += 1 562 562 563 563 (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None) 564 564 565 565 if button == 'cancel': 566 566 return None 567 567 568 568 return opt2path[value] 569 569 … … 576 576 path = os.path.join(PRESETS_DIR, profile[0], MAKEFILE) 577 577 read_config(path, config) 578 578 579 579 if len(profile) > 1: 580 580 path = os.path.join(PRESETS_DIR, profile[0], profile[1], MAKEFILE) … … 588 588 def parse_profile_name(profile_name): 589 589 profile = [] 590 590 591 591 head, tail = os.path.split(profile_name) 592 592 if head != '': 593 593 profile.append(head) 594 594 595 595 profile.append(tail) 596 596 return profile … … 600 600 config = {} 601 601 rules = [] 602 602 603 603 # Parse rules file 604 604 parse_rules(RULES_FILE, rules) 605 605 606 606 # Input configuration file can be specified on command line 607 607 # otherwise configuration from previous run is used. … … 611 611 elif os.path.exists(MAKEFILE): 612 612 read_config(MAKEFILE, config) 613 613 614 614 # Default mode: check values and regenerate configuration files 615 615 if (len(sys.argv) >= 3) and (sys.argv[2] == 'default'): … … 618 618 create_output(MAKEFILE, MACROS, config, rules) 619 619 return 0 620 620 621 621 # Hands-off mode: check values and regenerate configuration files, 622 622 # but no interactive fallback … … 627 627 sys.stderr.write("Configuration error: No presets specified\n") 628 628 return 2 629 629 630 630 if (infer_verify_choices(config, rules)): 631 631 preprocess_config(config, rules) 632 632 create_output(MAKEFILE, MACROS, config, rules) 633 633 return 0 634 634 635 635 sys.stderr.write("Configuration error: The presets are ambiguous\n") 636 636 return 1 637 637 638 638 # Check mode: only check configuration 639 639 if (len(sys.argv) >= 3) and (sys.argv[2] == 'check'): … … 641 641 return 0 642 642 return 1 643 643 644 644 # Random mode 645 645 if (len(sys.argv) == 3) and (sys.argv[2] == 'random'): … … 653 653 preprocess_config(config, rules) 654 654 create_output(MAKEFILE, MACROS, config, rules) 655 655 656 656 return 0 657 657 658 658 screen = xtui.screen_init() 659 659 try: … … 661 661 position = None 662 662 while True: 663 663 664 664 # Cancel out all values which have to be deduced 665 665 for varname, vartype, name, choices, cond in rules: 666 666 if (vartype == 'y') and (varname in config) and (config[varname] == '*'): 667 667 config[varname] = None 668 668 669 669 options = [] 670 670 opt2row = {} 671 671 cnt = 1 672 672 673 673 options.append(" --- Load preconfigured defaults ... ") 674 674 675 675 for rule in rules: 676 676 varname, vartype, name, choices, cond = rule 677 677 678 678 if cond and (not check_condition(cond, config, rules)): 679 679 continue 680 680 681 681 if varname == selname: 682 682 position = cnt 683 683 684 684 if not varname in config: 685 685 value = None 686 686 else: 687 687 value = config[varname] 688 688 689 689 if not validate_rule_value(rule, value): 690 690 value = None 691 691 692 692 default = get_default_rule(rule) 693 693 694 694 # 695 695 # If we don't have a value but we do have … … 699 699 value = default 700 700 config[varname] = default 701 701 702 702 option = get_rule_option(rule, value) 703 703 if option != None: … … 705 705 else: 706 706 continue 707 707 708 708 opt2row[cnt] = (varname, vartype, name, choices) 709 709 710 710 cnt += 1 711 711 712 712 if (position != None) and (position >= len(options)): 713 713 position = None 714 714 715 715 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position) 716 716 717 717 if button == 'cancel': 718 718 return 'Configuration canceled' 719 719 720 720 if button == 'done': 721 721 if (infer_verify_choices(config, rules)): … … 724 724 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.') 725 725 continue 726 726 727 727 if value == 0: 728 728 profile = choose_profile(PRESETS_DIR, MAKEFILE, screen, config) … … 731 731 position = 1 732 732 continue 733 733 734 734 position = None 735 735 if not value in opt2row: 736 736 raise RuntimeError("Error selecting value: %s" % value) 737 737 738 738 (selname, seltype, name, choices) = opt2row[value] 739 739 740 740 if not selname in config: 741 741 value = None 742 742 else: 743 743 value = config[selname] 744 744 745 745 if seltype == 'choice': 746 746 config[selname] = subchoice(screen, name, choices, value) … … 752 752 finally: 753 753 xtui.screen_done(screen) 754 754 755 755 preprocess_config(config, rules) 756 756 create_output(MAKEFILE, MACROS, config, rules)
Note:
See TracChangeset
for help on using the changeset viewer.