From 3e952d26664d030b080e9d1f994c45d4ba8c19da Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Wed, 11 Sep 2024 22:53:24 +0100 Subject: [PATCH] Add a --sysroot-install-dir-targets flag for jenkins-cheri-build When target tarballs have been unpacked to the sysroot for use as dependencies we need to tell jenkins-cheri-build not to assume they're installed in the output root directory ($WORKSPACE/tarball). Sadly this means piling more code into the existing hack. Note that jenkins-cheri-build currently has the strange "feature" that it always /opt/ regardless of what the target normally has, and so we need to preserve this but relative to the sysroot, rather than just not override the directory in the first place. This suffix also includes the -for--rootfs if present, unlike normal cheribuild. --- pycheribuild/config/jenkinsconfig.py | 4 +++ pycheribuild/jenkins_utils.py | 44 +++++++++++++++++++----- pycheribuild/targets.py | 50 +++++++++++++++------------- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/pycheribuild/config/jenkinsconfig.py b/pycheribuild/config/jenkinsconfig.py index 228fca28a..9576453b8 100644 --- a/pycheribuild/config/jenkinsconfig.py +++ b/pycheribuild/config/jenkinsconfig.py @@ -137,6 +137,10 @@ def __init__(self, loader: ConfigLoaderBase, available_targets: "list[str]") -> "sysroot-extra-archives", help="Addition archives to extract within the sysroot", ) + self.sysroot_install_dir_targets = loader.add_commandline_only_list_option( + "sysroot-install-dir-targets", + help="Targets for which to use the sysroot/rootfs rather than the output path", + ) self.keep_install_dir = loader.add_commandline_only_bool_option( "keep-install-dir", help="Don't delete the install dir prior to build" ) diff --git a/pycheribuild/jenkins_utils.py b/pycheribuild/jenkins_utils.py index 1480f59d9..ecbec026f 100644 --- a/pycheribuild/jenkins_utils.py +++ b/pycheribuild/jenkins_utils.py @@ -30,24 +30,52 @@ import inspect from pathlib import Path -from .config.chericonfig import CheriConfig +from .config.jenkinsconfig import CheriConfig, JenkinsConfig from .config.loader import CommandLineConfigOption +from .config.target_info import AbstractProject from .projects.project import Project from .targets import MultiArchTargetAlias, SimpleTargetAlias, Target, target_manager -from .utils import status_update +from .utils import fatal_error, status_update def jenkins_override_install_dirs_hack(cheri_config: CheriConfig, install_prefix: Path): - expected_install_path = Path(f"{cheri_config.output_root}{install_prefix}") # Ugly workaround to override all install dirs to go to the tarball all_targets = [ x for x in target_manager.targets(cheri_config) if not isinstance(x, (SimpleTargetAlias, MultiArchTargetAlias)) and issubclass(x.project_class, Project) ] + if isinstance(cheri_config, JenkinsConfig): + sysroot_targets = [ + target_manager.get_chosen_target(cheri_config, target_name) + for target_name in cheri_config.sysroot_install_dir_targets + ] + else: + sysroot_targets = [] + + for target in sysroot_targets: + if target.xtarget.is_native(): + fatal_error("Cannot use non-existent sysroot for native target", target.name, pretend=False) + + def expected_install_root(target): + if target in sysroot_targets: + project = target._try_get_project() + if project is None: + target_info = target.xtarget.create_target_info(AbstractProject(cheri_config)) + else: + target_info = project.target_info + sysroot_dir = target_info.sysroot_dir + return sysroot_dir + else: + return cheri_config.output_root + + def expected_install_path(target): + root_dir = expected_install_root(target) + return Path(f"{root_dir}{install_prefix}") + for target in all_targets: cls = target.project_class - cls._default_install_dir_fn = expected_install_path + cls._default_install_dir_fn = expected_install_path(target) Target.instantiating_targets_should_warn = False # Now that we have set the _install_dir member, override the prefix/destdir after instantiating. @@ -66,15 +94,15 @@ def jenkins_override_install_dirs_hack(cheri_config: CheriConfig, install_prefix status_update("Install directory for", cls.target, "was specified on commandline:", from_cmdline) project._install_dir = from_cmdline else: - project._install_dir = cheri_config.output_root + project._install_dir = expected_install_root(target) project._check_install_dir_conflict = False # Using "/" as the install prefix results inconsistently prefixing some paths with '/usr/'. # To avoid this, just use the full install path as the prefix. if install_prefix == Path("/"): - project._install_prefix = expected_install_path + project._install_prefix = expected_install_path(target) project.destdir = Path("/") else: project._install_prefix = install_prefix - project.destdir = cheri_config.output_root - assert project.real_install_root_dir == expected_install_path + project.destdir = expected_install_root(target) + assert project.real_install_root_dir == expected_install_path(target) assert isinstance(inspect.getattr_static(project, "_install_dir"), Path) diff --git a/pycheribuild/targets.py b/pycheribuild/targets.py index 3fb0dcdcc..ea6d74738 100644 --- a/pycheribuild/targets.py +++ b/pycheribuild/targets.py @@ -83,6 +83,9 @@ def get_real_target( ) -> "Target": return self + def _try_get_project(self) -> "Optional[SimpleProject]": + return self.__project + def _get_or_create_project_no_setup( self, _: Optional[CrossCompileTarget], config, caller: "Optional[AbstractProject]" ) -> "SimpleProject": @@ -542,6 +545,29 @@ def run(self, config: CheriConfig, chosen_targets=None) -> None: for target in chosen_targets: target.execute(config) + def get_chosen_target(self, config, target_name) -> Target: + if target_name not in self._all_targets: + # See if it was a target alias without a default + if target_name in self._targets_for_command_line_options_only: + alias = self._targets_for_command_line_options_only[target_name] + errmsg = coloured(AnsiColour.red, "Target", target_name, "is ambiguous.") + suggestions = sorted([tgt.name for tgt in alias.derived_targets]) + else: + import difflib + + errmsg = coloured(AnsiColour.red, "Target", target_name, "does not exist.") + suggestions = difflib.get_close_matches(target_name, self.target_names(config)) + if suggestions: + errmsg += " Did you mean " + " or ".join(coloured(AnsiColour.blue, s) for s in suggestions) + "?" + else: + errmsg += ( + " See " + + coloured(AnsiColour.yellow, os.path.basename(sys.argv[0]), "--list-targets") + + " for the list of available targets." + ) + sys.exit(errmsg) + return self.get_target(target_name, config=config, caller="cmdline parsing") + def get_all_chosen_targets(self, config) -> "list[Target]": # check that all target dependencies are correct: if os.getenv("CHERIBUILD_DEBUG"): @@ -557,29 +583,7 @@ def get_all_chosen_targets(self, config) -> "list[Target]": # assert self._all_targets["llvm"] < self._all_targets["all"] # assert self._all_targets["disk-image"] > self._all_targets["qemu"] # assert self._all_targets["sdk"] > self._all_targets["sdk-sysroot"] - explicitly_chosen_targets: "list[Target]" = [] - for target_name in config.targets: - if target_name not in self._all_targets: - # See if it was a target alias without a default - if target_name in self._targets_for_command_line_options_only: - alias = self._targets_for_command_line_options_only[target_name] - errmsg = coloured(AnsiColour.red, "Target", target_name, "is ambiguous.") - suggestions = sorted([tgt.name for tgt in alias.derived_targets]) - else: - import difflib - - errmsg = coloured(AnsiColour.red, "Target", target_name, "does not exist.") - suggestions = difflib.get_close_matches(target_name, self.target_names(config)) - if suggestions: - errmsg += " Did you mean " + " or ".join(coloured(AnsiColour.blue, s) for s in suggestions) + "?" - else: - errmsg += ( - " See " - + coloured(AnsiColour.yellow, os.path.basename(sys.argv[0]), "--list-targets") - + " for the list of available targets." - ) - sys.exit(errmsg) - explicitly_chosen_targets.append(self.get_target(target_name, config=config, caller="cmdline parsing")) + explicitly_chosen_targets = [self.get_chosen_target(config, target_name) for target_name in config.targets] chosen_targets = self.get_all_targets(explicitly_chosen_targets, config) for target in chosen_targets: disabled = self.target_disabled_reason(target, config)