diff --git a/pycheribuild/config/chericonfig.py b/pycheribuild/config/chericonfig.py index 61f697916..a57f55953 100644 --- a/pycheribuild/config/chericonfig.py +++ b/pycheribuild/config/chericonfig.py @@ -193,9 +193,6 @@ def __init__(self, loader, action_class) -> None: self.skip_kernel = loader.add_bool_option( "skip-kernel", "-skip-buildkernel", group=loader.freebsd_group, help="Skip the buildkernel step when building FreeBSD or CheriBSD") - self.freebsd_kernconf = loader.add_commandline_only_option( - "kernel-config", "-kernconf", group=loader.freebsd_group, help_hidden=True, - help="Override the default FreeBSD/CheriBSD kernel config.") self.freebsd_subdir = loader.add_commandline_only_option( "freebsd-subdir", "-subdir", group=loader.freebsd_group, type=list, metavar="SUBDIRS", help="Only build subdirs SUBDIRS of FreeBSD/CheriBSD instead of the full tree. Useful for quickly " diff --git a/pycheribuild/projects/cross/cheribsd.py b/pycheribuild/projects/cross/cheribsd.py index 07711b71a..eb782e4eb 100644 --- a/pycheribuild/projects/cross/cheribsd.py +++ b/pycheribuild/projects/cross/cheribsd.py @@ -525,13 +525,12 @@ def setup_config_options(cls, bootstrap_toolchain=False, use_upstream_llvm: bool kernel_only_target=False, **kwargs) -> None: super().setup_config_options(kernel_only_target=kernel_only_target, **kwargs) if cls._xtarget: - # KERNCONF always depends on the target, so we don't inherit this config option. The only exception is - # the global --kernel-config option that is provided for convenience and backwards compat. - cls.kernel_config = cls.add_config_option( - "kernel-config", metavar="CONFIG", show_help=True, extra_fallback_config_names=["kernel-config"], + # KERNCONF always depends on the target, so we don't inherit this config option. + cls.option_kernel_config = cls.add_config_option( + "kernel-config", metavar="CONFIG", show_help=True, nargs="+", kind=list, default=ComputedDefaultValue( function=lambda _, p: - p.default_kernel_config() if p.has_default_buildkernel_kernel_config else None, + [p.default_kernel_config()] if p.has_default_buildkernel_kernel_config else None, as_string="target-dependent, usually GENERIC"), use_default_fallback_config_names=False, # help="The kernel configuration to use for `make buildkernel`") # type: str @@ -609,6 +608,15 @@ def setup_config_options(cls, bootstrap_toolchain=False, use_upstream_llvm: bool assert not cls._xtarget.is_hybrid_or_purecap_cheri() cls.build_lib32 = False + @property + def kernel_config(self): + # Shorthand to access the default kernel specified by the kernel-config option. + # The configuration option can be a list but the kernel_config is always the default + # kernel configuration to build, whether the option overrides it or it is the default one. + if self.option_kernel_config is None: + return None + return self.option_kernel_config[0] + def get_default_kernel_platform(self) -> ConfigPlatform: if self.crosscompile_target.is_aarch64(include_purecap=True): return ConfigPlatform.FVP @@ -751,9 +759,13 @@ def __init__(self, *args, **kwargs) -> None: self.kernel_toolchain_exists: bool = False self.cross_toolchain_config = MakeOptions(MakeCommandKind.BsdMake, self) if self.has_default_buildkernel_kernel_config: - assert self.kernel_config is not None - self.make_args.set(**self.arch_build_flags) + assert self.option_kernel_config is not None self.extra_kernels = [] + if self.option_kernel_config is not None: + # The first kernel configuration is the default one, all the others are new extra configs. + # This will be non-empty when the kernel-config list is overridden from cheribuild configuration. + self.extra_kernels += self.option_kernel_config[1:] + self.make_args.set(**self.arch_build_flags) if self.subdir_override: # build only part of the tree @@ -1587,6 +1599,9 @@ def setup_config_options(cls, kernel_only_target=False, install_directory_help=N "caprevoke-kernel", show_help=True, _allow_unknown_targets=True, only_add_for_targets=CompilationTargets.ALL_CHERIBSD_CHERI_TARGETS_WITH_HYBRID, help="Build kernel with caprevoke support (experimental)") + + cls.external_configs = cls.add_config_option("extra-kernel-configs", metavar="CONFIG", default=[], kind=list, + nargs="+", help="Additional kernel configuration files to build") if kernel_only_target: return # The remaining options only affect the userspace build cls.sysroot_only = cls.add_bool_option("sysroot-only", show_help=False, @@ -1599,6 +1614,7 @@ def __init__(self, *args, **kwargs) -> None: configs = self.extra_kernel_configs() self.extra_kernels += [c.kernconf for c in configs if not c.mfsroot] self.extra_kernels_with_mfs += [c.kernconf for c in configs if c.mfsroot] + self.extra_kernels += self.external_configs def get_default_kernel_abi(self) -> KernelABI: # XXX: Because the config option has _allow_unknown_targets it exists @@ -1627,6 +1643,9 @@ def _get_config_variants(self, platforms: "set[ConfigPlatform]", kernABIs: "list def _get_kABIs_to_build(self) -> "list[KernelABI]": default_kABI = self.get_default_kernel_abi() kernABIs = [default_kABI] + # If we are ovveriding the kernel configurations list, only build the default ABI + if self.kernel_config and self.kernel_config != self.default_kernel_config(): + return kernABIs # XXX: Because the config option has _allow_unknown_targets it exists # in the base class and thus still inherited by non-purecap-kernel # targets @@ -1676,7 +1695,7 @@ def extra_kernel_configs(self) -> "list[CheriBSDConfig]": def get_kernel_configs(self, platform: "Optional[ConfigPlatform]") -> "list[str]": default = super().get_kernel_configs(platform) extra = filter_kernel_configs(self.extra_kernel_configs(), platform=platform, kABI=None) - return default + [c.kernconf for c in extra] + return default + [c.kernconf for c in extra] + self.external_configs def setup(self) -> None: super().setup() @@ -1817,9 +1836,11 @@ def default_kernel_config(self, platform: ConfigPlatform = None, **filter_kwargs def get_kernel_configs(self, platform: "Optional[ConfigPlatform]") -> "typing.List[str]": if self.kernel_config is not None: - return [self.kernel_config] + return [self.kernel_config] + self.external_configs configs = self._get_all_kernel_configs() - return [c.kernconf for c in filter_kernel_configs(configs, platform=platform, kABI=None)] + conf_names = [c.kernconf for c in filter_kernel_configs(configs, platform=platform, kABI=None)] + conf_names += self.external_configs + return conf_names def get_kernel_install_path(self, kernconf: str = None) -> Path: """ Get the installed kernel path for an MFS kernel config that has been built. """ diff --git a/tests/test_argument_parsing.py b/tests/test_argument_parsing.py index b400b13bd..0d5b83bee 100644 --- a/tests/test_argument_parsing.py +++ b/tests/test_argument_parsing.py @@ -391,58 +391,6 @@ def test_cheribsd_purecap_inherits_config_from_cheribsd(): assert not cheribsd_riscv_hybrid.debug_kernel, "riscv64-hybrid should have a JSON false override for debug-kernel" -def test_kernconf(): - # The kernel-config command line option is special: There is a global (command-line-only) flag that is used - # as the default, but otherwise there should be no inheritance - config = _parse_arguments([]) - cheribsd_riscv_hybrid = _get_cheribsd_instance("cheribsd-riscv64-hybrid", config) - cheribsd_riscv = _get_cheribsd_instance("cheribsd-riscv64", config) - freebsd_riscv = _get_target_instance("freebsd-riscv64", config, BuildFreeBSD) - freebsd_native = _get_target_instance("freebsd-amd64", config, BuildFreeBSD) - assert config.freebsd_kernconf is None - assert freebsd_riscv.kernel_config == "QEMU" - assert cheribsd_riscv_hybrid.kernel_config == "CHERI-QEMU" - assert freebsd_native.kernel_config == "GENERIC" - - # Check that --kernconf is used as the fallback - config = _parse_arguments(["--kernconf=LINT", "--freebsd-riscv64/kernel-config=FOO"]) - assert config.freebsd_kernconf == "LINT" - attr = inspect.getattr_static(freebsd_riscv, "kernel_config") - # previously we would replace the command line attribute with a string -> check this is no longer true - assert isinstance(attr, JsonAndCommandLineConfigOption) - assert freebsd_riscv.kernel_config == "FOO" - assert cheribsd_riscv_hybrid.kernel_config == "LINT" - assert freebsd_native.kernel_config == "LINT" - - config = _parse_arguments(["--kernconf=LINT", "--cheribsd-riscv64-hybrid/kernel-config=SOMETHING"]) - assert config.freebsd_kernconf == "LINT" - assert freebsd_riscv.kernel_config == "LINT" - assert cheribsd_riscv_hybrid.kernel_config == "SOMETHING" - assert freebsd_native.kernel_config == "LINT" - - config = _parse_config_file_and_args(b'{ "cheribsd-riscv64/kernel-config": "RISCV64_CONFIG" }', - "--kernconf=GENERIC") - assert config.freebsd_kernconf == "GENERIC" - assert cheribsd_riscv_hybrid.kernel_config == "GENERIC" - assert cheribsd_riscv.kernel_config == "RISCV64_CONFIG" - assert freebsd_riscv.kernel_config == "GENERIC" - assert freebsd_native.kernel_config == "GENERIC" - - # kernel-config/--kernconf should only be valid on the command line: - with pytest.raises(ValueError, match="^Unknown config option 'freebsd/kernel-config'$"): - _parse_config_file_and_args(b'{ "freebsd/kernel-config": "GENERIC" }') - # kernel-config/--kernconf should only be valid on the command line: - with pytest.raises(ValueError, match="^Option 'kernel-config' cannot be used in the config file$"): - _parse_config_file_and_args(b'{ "kernel-config": "GENERIC" }') - with pytest.raises(ValueError, match="^Option 'kernconf' cannot be used in the config file$"): - _parse_config_file_and_args(b'{ "kernconf": "GENERIC" }') - - # There should not be any unsuffixed kernel-config options: - for tgt in ("cheribsd", "freebsd", "cheribsd-mfs-root-kernel"): - with pytest.raises(KeyError, match=r"error: unknown argument '--[\w-]+/kernel-config'"): - _parse_arguments(["--" + tgt + "/source-directory=/foo", "--" + tgt + "/kernel-config", "ABC"]) - - def test_duplicate_key(): with pytest.raises(SyntaxError, match="duplicate key: 'output-root'"): _parse_config_file_and_args(b'{ "output-root": "/foo", "some-other-key": "abc", "output-root": "/bar" }') @@ -691,6 +639,19 @@ def test_disk_image_path(target, expected_name): "CHERI-PURECAP-QEMU", "CHERI-FETT", "CHERI-PURECAP-FETT"]), + pytest.param("cheribsd-riscv64-purecap", + ["--cheribsd-riscv64-purecap/kernel-config", "FOOBAR_KERNEL", + "--cheribsd-riscv64-purecap/no-build-alternate-abi-kernels"], + "FOOBAR_KERNEL", []), + pytest.param("cheribsd-riscv64-purecap", + ["--cheribsd-riscv64-purecap/kernel-config", "FOOBAR_KERNEL", + "--cheribsd-riscv64-purecap/build-alternate-abi-kernels"], + "FOOBAR_KERNEL", []), + pytest.param("cheribsd-riscv64-purecap", + ["--cheribsd-riscv64-purecap/kernel-config", "FOOBAR_KERNEL", + "BAZBAZ_KERNEL", + "--cheribsd-riscv64-purecap/build-alternate-abi-kernels"], + "FOOBAR_KERNEL", ["BAZBAZ_KERNEL"]), # Morello kernconf tests pytest.param("cheribsd-aarch64", [], @@ -704,6 +665,14 @@ def test_disk_image_path(target, expected_name): [], "GENERIC-MORELLO", ["GENERIC-MORELLO-PURECAP"]), + pytest.param("cheribsd-morello-purecap", + ["--cheribsd-morello-purecap/kernel-config", "FOOBAR_KERNEL", + "--cheribsd-morello-purecap/no-build-alternate-abi-kernels"], + "FOOBAR_KERNEL", []), + pytest.param("cheribsd-morello-purecap", + ["--cheribsd-morello-purecap/kernel-config", "FOOBAR_KERNEL", + "--cheribsd-morello-purecap/build-alternate-abi-kernels"], + "FOOBAR_KERNEL", []), # FreeBSD kernel configs pytest.param("freebsd-i386", [], "GENERIC", []), pytest.param("freebsd-aarch64", [], "GENERIC", []), @@ -954,9 +923,9 @@ def test_mfs_root_kernel_config_options(): "auto_var_init", "build_alternate_abi_kernels", "build_bench_kernels", "build_dir", "build_fett_kernels", "build_fpga_kernels", "build_type", "caprevoke_kernel", "debug_kernel", "default_kernel_abi", - "extra_make_args", "fast_rebuild", "force_configure", "kernel_config", - "mfs_root_image", "skip_update", "use_ccache", "use_lto", "with_clean", - "with_debug_files", "with_debug_info"] + "external_configs", "extra_make_args", "fast_rebuild", "force_configure", + "mfs_root_image", "option_kernel_config", "skip_update", "use_ccache", + "use_lto", "with_clean", "with_debug_files", "with_debug_info"] def test_mfs_root_kernel_inherits_defaults_from_cheribsd(): @@ -1007,13 +976,6 @@ def test_mfs_root_kernel_inherits_defaults_from_cheribsd(): assert cheribsd_riscv64_hybrid.kernel_config == "CHERI-QEMU" assert mfs_riscv64.kernel_config is None assert mfs_riscv64_hybrid.kernel_config == "MFS_CONFIG_RISCV64_HYBRID" - _parse_arguments(["--kernel-config=CONFIG_DEFAULT", - "--cheribsd-riscv64-purecap/kernel-config=BASE_CONFIG_RISCV64", - "--cheribsd-mfs-root-kernel-riscv64-hybrid/kernel-config=MFS_CONFIG_RISCV64_HYBRID"]) - assert cheribsd_riscv64_purecap.kernel_config == "BASE_CONFIG_RISCV64" - assert cheribsd_riscv64_hybrid.kernel_config == "CONFIG_DEFAULT" - assert mfs_riscv64.kernel_config == "CONFIG_DEFAULT" - assert mfs_riscv64_hybrid.kernel_config == "MFS_CONFIG_RISCV64_HYBRID" def test_relative_paths_in_config():