summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Kanavin <alex@linutronix.de>2025-10-08 18:56:59 +0200
committerRichard Purdie <richard.purdie@linuxfoundation.org>2025-10-09 16:46:45 +0100
commit5dda14aa9403c14c26896f6fe6ec8889d215dded (patch)
treed563975a99f549271b571264cd8099b193e64c93
parentcfc377635cf5c9a8a8dff9de7c4e2031dee2e2f2 (diff)
downloadpoky-5dda14aa9403c14c26896f6fe6ec8889d215dded.tar.gz
bitbake: bitbake-setup: rework the settings handling
This is the outcome of various discussions, suggestions and pull requests on github. What has specifically changed? 1. The sources for the settings are no longer separated, but are stacked and given priorities, from highest to lowest: a. '--setting section key value' on the command line b. a settings file in the top directory c. a global settings file in ~/.config/bitbake-setup/ (or in a file pointed to by --global-settings) Any setting can be in any of these three locations (other than top dir name and prefix which do not make sense in the settings file in the top directory). 2. A global settings file must contain all of the needed settings, while a settings file in the top directory can be empty (and this is how they are written out if they do not exist). Specifically, both dl-dir and registry settings have been relocated to the global file, and dl-dir defaults to ~/.cache/bitbake-setup/downloads, rather than somewhere in top dir. 3. The file name for both global and top dir settings is now 'settings.conf'. 4. --top-dir-prefix and --top-dir-name options have been removed and superseded by a generic, universal --setting option. 5. 'install-settings' command has been removed, as it is no longer does anything useful, and is superseded by the 'setting' command (see below). 'install-global-settings' has been retained, to be able to have a set of global defaults that can be changed without initializing a build. 6. 'change-setting', 'change-global-setting' and 'install-settings' have all been replaced by a single 'setting' command that mimics 'git config' in its parameters: a. Changing a setting: bitbake-setup setting [--global] default dl-dir /path/to/downloads b. Removing a setting: bitbake-setup setting [--global] --unset default dl-dir (Bitbake rev: dbf593e4f7441e423ce4f2a8c7b0365a1c93bd23) Signed-off-by: Alexander Kanavin <alex@linutronix.de> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rwxr-xr-xbitbake/bin/bitbake-setup287
-rw-r--r--bitbake/lib/bb/tests/setup.py12
2 files changed, 147 insertions, 152 deletions
diff --git a/bitbake/bin/bitbake-setup b/bitbake/bin/bitbake-setup
index 7878cd9394..e9e73a9270 100755
--- a/bitbake/bin/bitbake-setup
+++ b/bitbake/bin/bitbake-setup
@@ -32,9 +32,9 @@ logger = bb.msg.logger_create('bitbake-setup', sys.stdout)
32def cache_dir(top_dir): 32def cache_dir(top_dir):
33 return os.path.join(top_dir, '.bitbake-setup-cache') 33 return os.path.join(top_dir, '.bitbake-setup-cache')
34 34
35def init_bb_cache(settings, args): 35def init_bb_cache(top_dir, settings, args):
36 dldir = settings["default"]["dl-dir"] 36 dldir = settings["default"]["dl-dir"]
37 bb_cachedir = os.path.join(cache_dir(args.top_dir), 'bitbake-cache') 37 bb_cachedir = os.path.join(cache_dir(top_dir), 'bitbake-cache')
38 38
39 d = bb.data.init() 39 d = bb.data.init()
40 d.setVar("DL_DIR", dldir) 40 d.setVar("DL_DIR", dldir)
@@ -389,7 +389,7 @@ def choose_fragments(possibilities, parameters, non_interactive, skip_selection)
389 choices[k] = options_enumerated[option_n][1] 389 choices[k] = options_enumerated[option_n][1]
390 return choices 390 return choices
391 391
392def obtain_config(settings, args, source_overrides, d): 392def obtain_config(top_dir, settings, args, source_overrides, d):
393 if args.config: 393 if args.config:
394 config_id = args.config[0] 394 config_id = args.config[0]
395 config_parameters = args.config[1:] 395 config_parameters = args.config[1:]
@@ -407,7 +407,7 @@ def obtain_config(settings, args, source_overrides, d):
407 upstream_config = {'type':'network','uri':config_id,'name':get_config_name(config_id),'data':json.load(f)} 407 upstream_config = {'type':'network','uri':config_id,'name':get_config_name(config_id),'data':json.load(f)}
408 else: 408 else:
409 print("Looking up config {} in configuration registry".format(config_id)) 409 print("Looking up config {} in configuration registry".format(config_id))
410 registry_path = update_registry(settings["default"]["registry"], cache_dir(args.top_dir), d) 410 registry_path = update_registry(settings["default"]["registry"], cache_dir(top_dir), d)
411 registry_configs = list_registry(registry_path, with_expired=True) 411 registry_configs = list_registry(registry_path, with_expired=True)
412 if config_id not in registry_configs: 412 if config_id not in registry_configs:
413 raise Exception("Config {} not found in configuration registry, re-run 'init' without parameters to choose from available configurations.".format(config_id)) 413 raise Exception("Config {} not found in configuration registry, re-run 'init' without parameters to choose from available configurations.".format(config_id))
@@ -416,7 +416,7 @@ def obtain_config(settings, args, source_overrides, d):
416 if has_expired(expiry_date): 416 if has_expired(expiry_date):
417 print("This configuration is no longer supported after {}. Please consider changing to a supported configuration.".format(expiry_date)) 417 print("This configuration is no longer supported after {}. Please consider changing to a supported configuration.".format(expiry_date))
418 else: 418 else:
419 registry_path = update_registry(settings["default"]["registry"], cache_dir(args.top_dir), d) 419 registry_path = update_registry(settings["default"]["registry"], cache_dir(top_dir), d)
420 registry_configs = list_registry(registry_path, with_expired=True) 420 registry_configs = list_registry(registry_path, with_expired=True)
421 config_id = choose_config(registry_configs, args.non_interactive) 421 config_id = choose_config(registry_configs, args.non_interactive)
422 config_parameters = [] 422 config_parameters = []
@@ -429,7 +429,7 @@ def obtain_config(settings, args, source_overrides, d):
429 upstream_config['skip-selection'] = args.skip_selection 429 upstream_config['skip-selection'] = args.skip_selection
430 return upstream_config 430 return upstream_config
431 431
432def init_config(settings, args, d): 432def init_config(top_dir, settings, args, d):
433 stdout = sys.stdout 433 stdout = sys.stdout
434 def handle_task_progress(event, d): 434 def handle_task_progress(event, d):
435 rate = event.rate if event.rate else '' 435 rate = event.rate if event.rate else ''
@@ -437,10 +437,10 @@ def init_config(settings, args, d):
437 print("{}% {} ".format(progress, rate), file=stdout, end='\r') 437 print("{}% {} ".format(progress, rate), file=stdout, end='\r')
438 438
439 source_overrides = json.load(open(args.source_overrides)) if args.source_overrides else {'sources':{}} 439 source_overrides = json.load(open(args.source_overrides)) if args.source_overrides else {'sources':{}}
440 upstream_config = obtain_config(settings, args, source_overrides, d) 440 upstream_config = obtain_config(top_dir, settings, args, source_overrides, d)
441 print("\nRun 'bitbake-setup init --non-interactive {}' to select this configuration non-interactively.\n".format(" ".join(upstream_config['non-interactive-cmdline-options']))) 441 print("\nRun 'bitbake-setup init --non-interactive {}' to select this configuration non-interactively.\n".format(" ".join(upstream_config['non-interactive-cmdline-options'])))
442 442
443 builddir = os.path.join(os.path.abspath(args.top_dir), args.build_dir_name or "{}-{}".format(upstream_config['name']," ".join(upstream_config['non-interactive-cmdline-options'][1:]).replace(" ","-").replace("/","_"))) 443 builddir = os.path.join(os.path.abspath(top_dir), args.build_dir_name or "{}-{}".format(upstream_config['name']," ".join(upstream_config['non-interactive-cmdline-options'][1:]).replace(" ","-").replace("/","_")))
444 if os.path.exists(os.path.join(builddir, "layers")): 444 if os.path.exists(os.path.join(builddir, "layers")):
445 print("Build already initialized in {}\nUse 'bitbake-setup status' to check if it needs to be updated or 'bitbake-setup update' to perform the update.".format(builddir)) 445 print("Build already initialized in {}\nUse 'bitbake-setup status' to check if it needs to be updated or 'bitbake-setup update' to perform the update.".format(builddir))
446 return 446 return
@@ -511,7 +511,7 @@ def are_layers_changed(layers, layerdir, d):
511 511
512 return changed 512 return changed
513 513
514def build_status(settings, args, d, update=False): 514def build_status(top_dir, settings, args, d, update=False):
515 builddir = args.build_dir 515 builddir = args.build_dir
516 516
517 confdir = os.path.join(builddir, "config") 517 confdir = os.path.join(builddir, "config")
@@ -523,7 +523,7 @@ def build_status(settings, args, d, update=False):
523 args.non_interactive = True 523 args.non_interactive = True
524 args.skip_selection = current_upstream_config['skip-selection'] 524 args.skip_selection = current_upstream_config['skip-selection']
525 source_overrides = current_upstream_config["source-overrides"] 525 source_overrides = current_upstream_config["source-overrides"]
526 new_upstream_config = obtain_config(settings, args, source_overrides, d) 526 new_upstream_config = obtain_config(top_dir, settings, args, source_overrides, d)
527 527
528 write_config(new_upstream_config, confdir) 528 write_config(new_upstream_config, confdir)
529 config_diff = bb.process.run('git -C {} diff'.format(confdir))[0] 529 config_diff = bb.process.run('git -C {} diff'.format(confdir))[0]
@@ -544,8 +544,8 @@ def build_status(settings, args, d, update=False):
544 544
545 print("\nConfiguration in {} has not changed.".format(builddir)) 545 print("\nConfiguration in {} has not changed.".format(builddir))
546 546
547def build_update(settings, args, d): 547def build_update(top_dir, settings, args, d):
548 build_status(settings, args, d, update=True) 548 build_status(top_dir, settings, args, d, update=True)
549 549
550def do_fetch(fetcher, dir): 550def do_fetch(fetcher, dir):
551 # git fetcher simply dumps git output to stdout; in bitbake context that is redirected to temp/log.do_fetch 551 # git fetcher simply dumps git output to stdout; in bitbake context that is redirected to temp/log.do_fetch
@@ -595,8 +595,8 @@ def list_registry(registry_path, with_expired):
595 json_data[config_name] = {"description": config_desc} 595 json_data[config_name] = {"description": config_desc}
596 return json_data 596 return json_data
597 597
598def list_configs(settings, args, d): 598def list_configs(top_dir, settings, args, d):
599 registry_path = update_registry(settings["default"]["registry"], cache_dir(args.top_dir), d) 599 registry_path = update_registry(settings["default"]["registry"], cache_dir(top_dir), d)
600 json_data = list_registry(registry_path, args.with_expired) 600 json_data = list_registry(registry_path, args.with_expired)
601 print("\nAvailable configurations:") 601 print("\nAvailable configurations:")
602 for config_name, config_data in json_data.items(): 602 for config_name, config_data in json_data.items():
@@ -614,7 +614,7 @@ def list_configs(settings, args, d):
614 json.dump(json_data, f, sort_keys=True, indent=4) 614 json.dump(json_data, f, sort_keys=True, indent=4)
615 print("Available configurations written into {}".format(args.write_json)) 615 print("Available configurations written into {}".format(args.write_json))
616 616
617def install_buildtools(settings, args, d): 617def install_buildtools(top_dir, settings, args, d):
618 buildtools_install_dir = os.path.join(args.build_dir, 'buildtools') 618 buildtools_install_dir = os.path.join(args.build_dir, 'buildtools')
619 if os.path.exists(buildtools_install_dir): 619 if os.path.exists(buildtools_install_dir):
620 if not args.force: 620 if not args.force:
@@ -634,117 +634,113 @@ def install_buildtools(settings, args, d):
634 subprocess.check_call("{} -d {} --downloads-directory {}".format(install_buildtools, buildtools_install_dir, buildtools_download_dir), shell=True) 634 subprocess.check_call("{} -d {} --downloads-directory {}".format(install_buildtools, buildtools_install_dir, buildtools_download_dir), shell=True)
635 635
636def default_settings_path(top_dir): 636def default_settings_path(top_dir):
637 return os.path.join(top_dir, 'bitbake-setup.conf') 637 return os.path.join(top_dir, 'settings.conf')
638 638
639def write_settings(top_dir, force_replace, non_interactive=True): 639def create_settings(top_dir, non_interactive=True):
640 settings_path = default_settings_path(top_dir) 640 settings_path = default_settings_path(top_dir)
641 if not os.path.exists(settings_path) or force_replace: 641 settings = configparser.ConfigParser()
642 642 settings['default'] = {
643 settings = configparser.ConfigParser() 643 }
644 settings['default'] = { 644 os.makedirs(os.path.dirname(settings_path), exist_ok=True)
645 'registry':default_registry, 645
646 'dl-dir':os.path.join(top_dir, '.bitbake-setup-downloads'), 646 siteconfpath = os.path.join(top_dir, 'site.conf')
647 } 647 print('A new empty settings file will be created in (you can add settings to it to override defaults from the global settings file)\n {}\n'.format(settings_path))
648 os.makedirs(os.path.dirname(settings_path), exist_ok=True) 648 print('A common site.conf file will be created, please edit or replace before running builds\n {}\n'.format(siteconfpath))
649 649 if not non_interactive:
650 siteconfpath = os.path.join(top_dir, 'site.conf') 650 y_or_n = input('Bitbake-setup will be configured with the above settings in {}, (y/N): '.format(top_dir))
651 print('Configuration registry set to\n {}\n'.format(settings['default']['registry'])) 651 if y_or_n != 'y':
652 print('Bitbake-setup download cache (DL_DIR) set to\n {}\n'.format(settings['default']['dl-dir'])) 652 print("\nYou can run 'bitbake-setup install-settings' to edit them before setting up builds")
653 print('A new settings file will be created in\n {}\n'.format(settings_path)) 653 exit()
654 print('A common site.conf file will be created, please edit or replace before running builds\n {}\n'.format(siteconfpath)) 654 print()
655 if not non_interactive:
656 y_or_n = input('Bitbake-setup will be configured with the above settings in {}, (y/N): '.format(top_dir))
657 if y_or_n != 'y':
658 print("\nYou can run 'bitbake-setup install-settings' to edit them before setting up builds")
659 exit()
660 print()
661
662 if os.path.exists(settings_path):
663 backup_conf = settings_path + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S"))
664 os.rename(settings_path, backup_conf)
665 print("Previous settings are in {}".format(backup_conf))
666 with open(settings_path, 'w') as settingsfile:
667 settings.write(settingsfile)
668
669 if os.path.exists(siteconfpath):
670 backup_siteconf = siteconfpath + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S"))
671 os.rename(siteconfpath, backup_siteconf)
672 print("Previous settings are in {}".format(backup_siteconf))
673 with open(siteconfpath, 'w') as siteconffile:
674 siteconffile.write('# This file is intended for build host-specific bitbake settings\n')
675 655
676def load_settings(top_dir, non_interactive): 656 if os.path.exists(settings_path):
677 # This creates a new settings file if it does not yet exist 657 backup_conf = settings_path + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S"))
678 write_settings(top_dir, force_replace=False, non_interactive=non_interactive) 658 os.rename(settings_path, backup_conf)
659 print("Previous settings are in {}".format(backup_conf))
660 with open(settings_path, 'w') as settingsfile:
661 settings.write(settingsfile)
662
663 if os.path.exists(siteconfpath):
664 backup_siteconf = siteconfpath + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S"))
665 os.rename(siteconfpath, backup_siteconf)
666 print("Previous settings are in {}".format(backup_siteconf))
667 with open(siteconfpath, 'w') as siteconffile:
668 siteconffile.write('# This file is intended for build host-specific bitbake settings\n')
679 669
670def load_settings(top_dir, non_interactive):
680 settings_path = default_settings_path(top_dir) 671 settings_path = default_settings_path(top_dir)
672 if not os.path.exists(settings_path):
673 create_settings(top_dir, non_interactive=non_interactive)
674
681 settings = configparser.ConfigParser() 675 settings = configparser.ConfigParser()
682 print('Loading settings from\n {}\n'.format(settings_path)) 676 print('Loading settings from\n {}\n'.format(settings_path))
683 settings.read_file(open(settings_path)) 677 settings.read_file(open(settings_path))
684 return settings 678 return settings
685 679
686def global_settings_path(args): 680def global_settings_path(args):
687 return os.path.abspath(args.global_settings) if args.global_settings else os.path.join(os.path.expanduser('~'), '.config', 'bitbake-setup', 'config') 681 return os.path.abspath(args.global_settings) if args.global_settings else os.path.join(os.path.expanduser('~'), '.config', 'bitbake-setup', 'settings.conf')
688 682
689def write_global_settings(settings_path, force_replace, non_interactive=True): 683def create_global_settings(settings_path, non_interactive=True):
690 if not os.path.exists(settings_path) or force_replace: 684 settings = configparser.ConfigParser()
691 685 settings['default'] = {
692 settings = configparser.ConfigParser() 686 'top-dir-prefix':os.path.expanduser('~'),
693 settings['default'] = { 687 'top-dir-name':'bitbake-builds',
694 'top-dir-prefix':os.path.expanduser('~'), 688 'registry':default_registry,
695 'top-dir-name':'bitbake-builds' 689 'dl-dir':os.path.join(os.path.expanduser('~'), '.cache', 'bitbake-setup', 'downloads'),
696 } 690 }
697 os.makedirs(os.path.dirname(settings_path), exist_ok=True) 691 os.makedirs(os.path.dirname(settings_path), exist_ok=True)
698 print('Configuring global settings in\n {}\n'.format(settings_path)) 692 print('Configuring global settings in\n {}\n'.format(settings_path))
699 print('Top directory prefix (where all top level directories are created) set to\n {}\n'.format(settings['default']['top-dir-prefix'])) 693 print('Top directory prefix (where all top level directories are created) set to\n {}\n'.format(settings['default']['top-dir-prefix']))
700 print('Top directory name (this is added to the top directory prefix to form a top directory where builds are set up) set to\n {}\n'.format(settings['default']['top-dir-name'])) 694 print('Top directory name (this is added to the top directory prefix to form a top directory where builds are set up) set to\n {}\n'.format(settings['default']['top-dir-name']))
701 if not non_interactive: 695 print('Configuration registry set to\n {}\n'.format(settings['default']['registry']))
702 y_or_n = input('Write out the global settings as specified above (y/N)? ') 696 print('Bitbake-setup download cache (DL_DIR) set to\n {}\n'.format(settings['default']['dl-dir']))
703 if y_or_n != 'y': 697 if not non_interactive:
704 print("\nYou can run 'bitbake-setup install-global-settings' to edit them before setting up builds") 698 y_or_n = input('Write out the global settings as specified above (y/N)? ')
705 exit() 699 if y_or_n != 'y':
706 print() 700 print("\nYou can run 'bitbake-setup install-global-settings' to edit them before setting up builds")
707 701 exit()
708 if os.path.exists(settings_path): 702 print()
709 backup_conf = settings_path + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S")) 703
710 os.rename(settings_path, backup_conf) 704 if os.path.exists(settings_path):
711 print("Previous global settings are in {}".format(backup_conf)) 705 backup_conf = settings_path + "-backup.{}".format(time.strftime("%Y%m%d%H%M%S"))
712 with open(settings_path, 'w') as settingsfile: 706 os.rename(settings_path, backup_conf)
713 settings.write(settingsfile) 707 print("Previous global settings are in {}".format(backup_conf))
708 with open(settings_path, 'w') as settingsfile:
709 settings.write(settingsfile)
714 710
715def load_global_settings(settings_path, non_interactive): 711def load_global_settings(settings_path, non_interactive):
716 # This creates a new settings file if it does not yet exist 712 if not os.path.exists(settings_path):
717 write_global_settings(settings_path, force_replace=False, non_interactive=non_interactive) 713 create_global_settings(settings_path, non_interactive=non_interactive)
718 714
719 settings = configparser.ConfigParser() 715 settings = configparser.ConfigParser()
720 print('Loading global settings from\n {}\n'.format(settings_path)) 716 print('Loading global settings from\n {}\n'.format(settings_path))
721 settings.read_file(open(settings_path)) 717 settings.read_file(open(settings_path))
722 return settings 718 return settings
723 719
724def change_settings(top_dir, new_settings): 720def change_setting(settings_path, settings, args):
725 settings = load_settings(top_dir, non_interactive=True) 721 if args.section and args.key and args.value:
726 for section, section_settings in new_settings.items(): 722 settings[args.section][args.key] = args.value
727 for setting, value in section_settings.items(): 723 print("Setting '{}' in section '{}' is changed to '{}'".format(args.key, args.section, args.value))
728 settings[section][setting] = value 724 if args.unset:
729 print("Setting '{}' in section '{}' is changed to '{}'".format(setting, section, value)) 725 section = args.unset[0]
726 setting = args.unset[1]
727 if section in settings.keys() and setting in settings[section].keys():
728 del settings[section][setting]
729 print("Setting '{} in section '{}' is removed".format(setting, section))
730 730
731 settings_path = default_settings_path(top_dir)
732 with open(settings_path, 'w') as settingsfile: 731 with open(settings_path, 'w') as settingsfile:
733 settings.write(settingsfile) 732 settings.write(settingsfile)
734 print("New settings written to {}".format(settings_path)) 733 print("New settings written to {}".format(settings_path))
735 return settings
736 734
737def change_global_settings(settings_path, new_settings): 735def setting_global(args):
738 settings = load_global_settings(settings_path, non_interactive=True) 736 settings = load_global_settings(global_settings_path(args), args.non_interactive)
739 for section, section_settings in new_settings.items(): 737 settings_path = global_settings_path(args)
740 for setting, value in section_settings.items(): 738 change_setting(settings_path, settings, args)
741 settings[section][setting] = value
742 print("Setting '{}' in section '{}' is changed to '{}'".format(setting, section, value))
743 739
744 with open(settings_path, 'w') as settingsfile: 740def setting(top_dir, args):
745 settings.write(settingsfile) 741 settings = load_settings(top_dir, args.non_interactive)
746 print("New global settings written to {}".format(settings_path)) 742 settings_path = default_settings_path(top_dir)
747 return settings 743 change_setting(settings_path, settings, args)
748 744
749def get_build_dir_via_bbpath(): 745def get_build_dir_via_bbpath():
750 bbpath = os.environ.get('BBPATH') 746 bbpath = os.environ.get('BBPATH')
@@ -755,7 +751,7 @@ def get_build_dir_via_bbpath():
755 return build_dir 751 return build_dir
756 return None 752 return None
757 753
758def get_top_dir(args, global_settings): 754def get_top_dir(args, settings):
759 build_dir_via_bbpath = get_build_dir_via_bbpath() 755 build_dir_via_bbpath = get_build_dir_via_bbpath()
760 if build_dir_via_bbpath: 756 if build_dir_via_bbpath:
761 top_dir = os.path.dirname(build_dir_via_bbpath) 757 top_dir = os.path.dirname(build_dir_via_bbpath)
@@ -763,20 +759,25 @@ def get_top_dir(args, global_settings):
763 return top_dir 759 return top_dir
764 760
765 if hasattr(args, 'build_dir'): 761 if hasattr(args, 'build_dir'):
766 # commands without --top-dir-prefix/name arguments (status, update) still need to know where
767 # the top dir is, but it should be auto-deduced as parent of args.build_dir
768 top_dir = os.path.dirname(os.path.normpath(args.build_dir)) 762 top_dir = os.path.dirname(os.path.normpath(args.build_dir))
769 return top_dir 763 return top_dir
770 764
771 top_dir_prefix = args.top_dir_prefix if args.top_dir_prefix else global_settings['default']['top-dir-prefix'] 765 top_dir_prefix = settings['default']['top-dir-prefix']
772 top_dir_name = args.top_dir_name if args.top_dir_name else global_settings['default']['top-dir-name'] 766 top_dir_name = settings['default']['top-dir-name']
773 return os.path.join(top_dir_prefix, top_dir_name) 767 return os.path.join(top_dir_prefix, top_dir_name)
774 768
775def main(): 769def merge_settings(global_settings, local_settings, cmdline_settings):
776 def add_top_dir_arg(parser): 770 all_settings = global_settings
777 parser.add_argument('--top-dir-prefix', help='Top level directory prefix. This is where all top level directories are created.') 771 for section, section_settings in local_settings.items():
778 parser.add_argument('--top-dir-name', help='Top level directory name. Together with the top directory prefix this forms a top directory where builds are set up and downloaded configurations and layers are cached for reproducibility and offline builds.') 772 for setting, value in section_settings.items():
773 all_settings[section][setting] = value
774
775 for (section, setting, value) in cmdline_settings:
776 all_settings[section][setting] = value
777
778 return all_settings
779 779
780def main():
780 def add_build_dir_arg(parser): 781 def add_build_dir_arg(parser):
781 build_dir = get_build_dir_via_bbpath() 782 build_dir = get_build_dir_via_bbpath()
782 if build_dir: 783 if build_dir:
@@ -792,18 +793,17 @@ def main():
792 parser.add_argument('-q', '--quiet', help='Print only errors', action='store_true') 793 parser.add_argument('-q', '--quiet', help='Print only errors', action='store_true')
793 parser.add_argument('--color', choices=['auto', 'always', 'never'], default='auto', help='Colorize output (where %(metavar)s is %(choices)s)', metavar='COLOR') 794 parser.add_argument('--color', choices=['auto', 'always', 'never'], default='auto', help='Colorize output (where %(metavar)s is %(choices)s)', metavar='COLOR')
794 parser.add_argument('--no-network', action='store_true', help='Do not check whether configuration repositories and layer repositories have been updated; use only the local cache.') 795 parser.add_argument('--no-network', action='store_true', help='Do not check whether configuration repositories and layer repositories have been updated; use only the local cache.')
795 parser.add_argument('--global-settings', action='store', help='Path to the global settings file where defaults for top directory prefix and name can be specified') 796 parser.add_argument('--global-settings', action='store', help='Path to the global settings file.')
797 parser.add_argument('--setting', default=[], action='append', nargs=3, help='Modify a setting (for this bitbake-setup invocation only), for example "--setting default top-dir-prefix /path/to/top/dir".')
796 798
797 subparsers = parser.add_subparsers() 799 subparsers = parser.add_subparsers()
798 800
799 parser_list = subparsers.add_parser('list', help='List available configurations') 801 parser_list = subparsers.add_parser('list', help='List available configurations')
800 add_top_dir_arg(parser_list)
801 parser_list.add_argument('--with-expired', action='store_true', help='List also configurations that are no longer supported due to reaching their end-of-life dates.') 802 parser_list.add_argument('--with-expired', action='store_true', help='List also configurations that are no longer supported due to reaching their end-of-life dates.')
802 parser_list.add_argument('--write-json', action='store', help='Write available configurations into a json file so they can be programmatically processed.') 803 parser_list.add_argument('--write-json', action='store', help='Write available configurations into a json file so they can be programmatically processed.')
803 parser_list.set_defaults(func=list_configs) 804 parser_list.set_defaults(func=list_configs)
804 805
805 parser_init = subparsers.add_parser('init', help='Select a configuration and initialize a build from it') 806 parser_init = subparsers.add_parser('init', help='Select a configuration and initialize a build from it')
806 add_top_dir_arg(parser_init)
807 parser_init.add_argument('config', nargs='*', help="path/URL/id to a configuration file (use 'list' command to get available ids), followed by configuration options. Bitbake-setup will ask to choose from available choices if command line doesn't completely specify them.") 807 parser_init.add_argument('config', nargs='*', help="path/URL/id to a configuration file (use 'list' command to get available ids), followed by configuration options. Bitbake-setup will ask to choose from available choices if command line doesn't completely specify them.")
808 parser_init.add_argument('--non-interactive', action='store_true', help='Do not ask to interactively choose from available options; if bitbake-setup cannot make a decision it will stop with a failure.') 808 parser_init.add_argument('--non-interactive', action='store_true', help='Do not ask to interactively choose from available options; if bitbake-setup cannot make a decision it will stop with a failure.')
809 parser_init.add_argument('--source-overrides', action='store', help='Override sources information (repositories/revisions) with values from a local json file.') 809 parser_init.add_argument('--source-overrides', action='store', help='Override sources information (repositories/revisions) with values from a local json file.')
@@ -824,25 +824,16 @@ def main():
824 parser_install_buildtools.add_argument('--force', action='store_true', help='Force a reinstall of buildtools over the previous installation.') 824 parser_install_buildtools.add_argument('--force', action='store_true', help='Force a reinstall of buildtools over the previous installation.')
825 parser_install_buildtools.set_defaults(func=install_buildtools) 825 parser_install_buildtools.set_defaults(func=install_buildtools)
826 826
827 parser_install_settings = subparsers.add_parser('install-settings', help='Write a settings file with default values into the top level directory (contains the location of build configuration registry, downloads directory and other settings specific to a top directory)') 827 parser_install_global_settings = subparsers.add_parser('install-global-settings', help='Write a global settings file with default values')
828 add_top_dir_arg(parser_install_settings) 828 parser_install_global_settings.set_defaults(func=create_global_settings)
829 parser_install_settings.set_defaults(func=write_settings)
830
831 parser_install_global_settings = subparsers.add_parser('install-global-settings', help='Write a global settings file with default values (contains the default prefix and name of the top directory)')
832 parser_install_global_settings.set_defaults(func=write_global_settings)
833
834 parser_change_setting = subparsers.add_parser('change-setting', help='Change a setting in the settings file')
835 add_top_dir_arg(parser_change_setting)
836 parser_change_setting.add_argument('section', help="Section in a settings file, typically 'default'")
837 parser_change_setting.add_argument('key', help="Name of the setting")
838 parser_change_setting.add_argument('value', help="Value of the setting")
839 parser_change_setting.set_defaults(func=change_settings)
840 829
841 parser_change_global_setting = subparsers.add_parser('change-global-setting', help='Change a setting in the global settings file') 830 parser_setting = subparsers.add_parser('setting', help='Set or unset a setting in a setting file (e.g. the default prefix and name of the top directory, the location of build configuration registry, downloads directory and other settings specific to a top directory)')
842 parser_change_global_setting.add_argument('section', help="Section in a global settings file, typically 'default'") 831 parser_setting.add_argument('section', nargs='?', help="Section in a settings file, typically 'default'")
843 parser_change_global_setting.add_argument('key', help="Name of the setting") 832 parser_setting.add_argument('key', nargs='?', help="Name of the setting")
844 parser_change_global_setting.add_argument('value', help="Value of the setting") 833 parser_setting.add_argument('value', nargs='?', help="Value of the setting")
845 parser_change_global_setting.set_defaults(func=change_global_settings) 834 parser_setting.add_argument('--global', action='store_true', help="Modify the setting in a global settings file, rather than one specific to a top directory")
835 parser_setting.add_argument('--unset', nargs=2, help="Unset a setting, e.g. 'bitbake-setup setting --unset default registry' would revert to the registry setting in a global settings file")
836 parser_setting.set_defaults(func=setting)
846 837
847 args = parser.parse_args() 838 args = parser.parse_args()
848 839
@@ -859,11 +850,8 @@ def main():
859 level=logger.getEffectiveLevel()) 850 level=logger.getEffectiveLevel())
860 851
861 if 'func' in args: 852 if 'func' in args:
862 if args.func == write_global_settings: 853 if args.func == create_global_settings:
863 write_global_settings(global_settings_path(args), force_replace=True) 854 create_global_settings(global_settings_path(args))
864 return
865 elif args.func == change_global_settings:
866 change_global_settings(global_settings_path(args), {args.section:{args.key:args.value}})
867 return 855 return
868 856
869 if hasattr(args, 'build_dir'): 857 if hasattr(args, 'build_dir'):
@@ -874,19 +862,24 @@ def main():
874 if not hasattr(args, 'non_interactive'): 862 if not hasattr(args, 'non_interactive'):
875 args.non_interactive = True 863 args.non_interactive = True
876 864
865 if args.func == setting and vars(args)['global']:
866 setting_global(args)
867 return
868
877 global_settings = load_global_settings(global_settings_path(args), args.non_interactive) 869 global_settings = load_global_settings(global_settings_path(args), args.non_interactive)
878 args.top_dir = get_top_dir(args, global_settings) 870 top_dir = get_top_dir(args, merge_settings(global_settings, {}, args.setting))
879 871
880 print('Bitbake-setup is using {} as top directory (can be changed with --top-dir-prefix/name arguments or by setting them in {}).\n'.format(args.top_dir, global_settings_path(args))) 872 if args.func == setting:
881 if args.func == write_settings: 873 setting(top_dir, args)
882 write_settings(args.top_dir, force_replace=True) 874 return
883 elif args.func == change_settings: 875
884 change_settings(args.top_dir, {args.section:{args.key:args.value}}) 876 print('Bitbake-setup is using {} as top directory (can be changed by setting top dir prefix and name in {}).\n'.format(top_dir, global_settings_path(args)))
885 else: 877
886 settings = load_settings(args.top_dir, args.non_interactive) 878 settings = load_settings(top_dir, args.non_interactive)
887 d = init_bb_cache(settings, args) 879 settings = merge_settings(global_settings, settings, args.setting)
888 args.func(settings, args, d) 880 d = init_bb_cache(top_dir, settings, args)
889 save_bb_cache() 881 args.func(top_dir, settings, args, d)
882 save_bb_cache()
890 else: 883 else:
891 from argparse import Namespace 884 from argparse import Namespace
892 parser.print_help() 885 parser.print_help()
diff --git a/bitbake/lib/bb/tests/setup.py b/bitbake/lib/bb/tests/setup.py
index 495d1da203..22edda40e4 100644
--- a/bitbake/lib/bb/tests/setup.py
+++ b/bitbake/lib/bb/tests/setup.py
@@ -235,16 +235,18 @@ print("BBPATH is {{}}".format(os.environ["BBPATH"]))
235 out = self.runbbsetup("install-global-settings") 235 out = self.runbbsetup("install-global-settings")
236 settings_path = "{}/global-config".format(self.tempdir) 236 settings_path = "{}/global-config".format(self.tempdir)
237 self.assertIn(settings_path, out[0]) 237 self.assertIn(settings_path, out[0])
238 out = self.runbbsetup("change-global-setting default top-dir-prefix {}".format(self.tempdir)) 238 out = self.runbbsetup("setting --global default top-dir-prefix {}".format(self.tempdir))
239 self.assertIn("Setting 'top-dir-prefix' in section 'default' is changed to", out[0]) 239 self.assertIn("Setting 'top-dir-prefix' in section 'default' is changed to", out[0])
240 self.assertIn("New global settings written to".format(settings_path), out[0]) 240 self.assertIn("New settings written to".format(settings_path), out[0])
241 out = self.runbbsetup("setting --global default dl-dir {}".format(os.path.join(self.tempdir, 'downloads')))
242 self.assertIn("Setting 'dl-dir' in section 'default' is changed to", out[0])
243 self.assertIn("New settings written to".format(settings_path), out[0])
241 244
242 # check that writing settings works and then adjust them to point to 245 # check that writing settings works and then adjust them to point to
243 # test registry repo 246 # test registry repo
244 out = self.runbbsetup("install-settings") 247 out = self.runbbsetup("setting default registry 'git://{};protocol=file;branch=master;rev=master'".format(self.registrypath))
245 settings_path = "{}/bitbake-builds/bitbake-setup.conf".format(self.tempdir) 248 settings_path = "{}/bitbake-builds/settings.conf".format(self.tempdir)
246 self.assertIn(settings_path, out[0]) 249 self.assertIn(settings_path, out[0])
247 out = self.runbbsetup("change-setting default registry 'git://{};protocol=file;branch=master;rev=master'".format(self.registrypath))
248 self.assertIn("Setting 'registry' in section 'default' is changed to", out[0]) 250 self.assertIn("Setting 'registry' in section 'default' is changed to", out[0])
249 self.assertIn("New settings written to".format(settings_path), out[0]) 251 self.assertIn("New settings written to".format(settings_path), out[0])
250 252