Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e477c34
config: introduce FloatConfigOption
arichardson May 21, 2026
2fd4bf8
config: introduce a StringConfigOption class
arichardson May 21, 2026
119be0d
config: fix OptionalPathConfigOption kind registration
arichardson May 21, 2026
d51f3a2
config: support class-level fallback lookup for ComputedDefaultValue
arichardson May 21, 2026
db9c0fc
config: add OptionalStringConfigOption
arichardson May 22, 2026
63d0c56
config: introduce EnumConfigOption
arichardson May 21, 2026
9563ad0
projects: introduce OptionalEnumConfigOption option class
arichardson May 22, 2026
c448495
config: improve option mixin registration and handle overridden descr…
arichardson May 22, 2026
5b9a03f
config: add extra_condition filter to PerProjectConfigOption
arichardson May 22, 2026
bd85484
config: recursively evaluate nested ComputedDefaultValue and callable…
arichardson May 24, 2026
845eb3e
tests: add unit test for conditional config option pruning
arichardson May 22, 2026
995aaf0
simple_project: register inherited options on subclasses
arichardson May 25, 2026
201da1b
projects: migrate DLMalloc to class attribute options
arichardson May 21, 2026
1c00842
projects: migrate FreeRTOS and run-freertos options to class attributes
arichardson May 21, 2026
bc334a4
projects: migrate SNMalloc options to class attributes
arichardson May 21, 2026
b5c53d7
projects: migrate OpenSBI options to class attributes
arichardson May 21, 2026
65529c2
projects: migrate LittleKernel options to class attributes
arichardson May 21, 2026
1c3b7e3
projects: migrate MRS options to class attributes
arichardson May 21, 2026
963220f
projects: migrate CHERI-Microkit options to class attributes
arichardson May 21, 2026
238ce66
projects: migrate Benchmark options to class attributes
arichardson May 21, 2026
33164e7
projects: migrate CheriTest options to class attributes
arichardson May 21, 2026
56c80df
projects: migrate Morello Webkit options to class attributes
arichardson May 21, 2026
cb0a59f
projects: migrate U-Boot options to class attributes
arichardson May 21, 2026
7240393
projects: migrate Juliet Test Suite options to class attributes
arichardson May 21, 2026
704a22d
projects: migrate Qt5 options to class attributes
arichardson May 21, 2026
abc6de6
projects: migrate Libcxx options to class attributes
arichardson May 21, 2026
609be24
projects: migrate LLVM and SOAAP options to class attributes
arichardson May 21, 2026
7432840
projects: migrate Morello UEFI options to class attributes
arichardson May 21, 2026
a7b938b
projects: migrate TestRig options to class attributes
arichardson May 21, 2026
267da42
projects: migrate QEMU build options to class attributes
arichardson May 21, 2026
d5be91e
projects: migrate Run FVP options to class attributes
arichardson May 21, 2026
26ee820
projects: allocate unique default SSH port offsets for run-minimal an…
arichardson May 21, 2026
6926feb
tests: add test case verifying that QEMU targets have unique SSH forw…
arichardson May 21, 2026
51e70d3
projects: migrate CheriOS options to class attributes
arichardson May 21, 2026
8769ce2
cheribsd: migrate trivial options in mixin and sysroot targets to cla…
arichardson May 21, 2026
f3974e2
run_qemu: stabilize supported architectures order in run-mfs-root
arichardson May 25, 2026
00ea545
projects: migrate AutotoolsProject options to class attributes
arichardson May 25, 2026
6d39b83
projects: migrate CMakeProject options to class attributes
arichardson May 25, 2026
e592413
simple_project: support callable show_help and fallback resolution
arichardson May 25, 2026
c363933
jenkins: don't instantiate projects inside the install dir override
arichardson May 26, 2026
2599767
projects: migrate Project options to class attributes
arichardson May 25, 2026
3869a38
Convert jenkins_override_install_dirs to a context manager
arichardson May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions pycheribuild/config/config_loader_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,26 +395,25 @@ def is_default_value(self) -> bool:
return self._is_default_value

def __get__(self, instance, owner) -> T:
assert instance is not None or not callable(self.default), (
# If instance is None, we can pass the class (owner) as a fallback for instance
# so that ComputedDefaultValue can query class attributes/methods if it handles it.
lookup_instance = instance if instance is not None else owner
assert lookup_instance is not None or not callable(self.default), (
f"Tried to access read config option {self.full_option_name} without an object instance. "
f"Config options using computed defaults can only be used with an object instance. Owner = {owner}"
)

# TODO: would be nice if this was possible (but too much depends on accessing values without instances)
# if instance is None:
# return self
assert not self._owning_class or issubclass(owner, self._owning_class)
if self._cached is None:
# noinspection PyProtectedMember
self._cached = self.load_option(self._loader._cheri_config, instance, owner)
self._cached = self.load_option(self._loader._cheri_config, lookup_instance, owner)
return self._cached

def _get_default_value(self, config: "ConfigBase", instance: "Optional[object]" = None) -> Optional[T]:
if callable(self.default):
# pyrefly: ignore [bad-return]
return self.default(config, instance)
else:
return self.default
val = self.default
while callable(val):
val = val(config, instance)
return typing.cast(Optional[T], val)

def _convert_type(self, loaded_result: _LoadedConfigValue) -> "Optional[T]":
# check for None to make sure we don't call str(None) which would result in "None"
Expand Down
21 changes: 10 additions & 11 deletions pycheribuild/jenkins.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

from .config.jenkinsconfig import JenkinsAction, JenkinsConfig
from .config.loader import CommandLineConfigLoader
from .jenkins_utils import jenkins_override_install_dirs_hack
from .jenkins_utils import jenkins_override_install_dirs
from .processutils import get_program_version, run_and_kill_children_on_exit, run_command

# make sure all projects are loaded so that target_manager gets populated
Expand Down Expand Up @@ -283,17 +283,16 @@ def _jenkins_main() -> None:
fatal_error("More than one target is not supported yet.", pretend=False)
sys.exit()

jenkins_override_install_dirs_hack(cheri_config, cheri_config.installation_prefix)
with jenkins_override_install_dirs(cheri_config, cheri_config.installation_prefix):
if JenkinsAction.BUILD in cheri_config.action or JenkinsAction.TEST in cheri_config.action:
# Check system dependencies before building any targets to get an error message earlier.
for target in cheri_config.targets:
target_manager.get_target_raw(target).check_system_deps(cheri_config)
for tgt in cheri_config.targets:
build_target(cheri_config, target_manager.get_target_raw(tgt))

if JenkinsAction.BUILD in cheri_config.action or JenkinsAction.TEST in cheri_config.action:
# Check system dependencies before building any targets to get an error message earlier.
for target in cheri_config.targets:
target_manager.get_target_raw(target).check_system_deps(cheri_config)
for tgt in cheri_config.targets:
build_target(cheri_config, target_manager.get_target_raw(tgt))

if JenkinsAction.CREATE_TARBALL in cheri_config.action:
create_tarball(cheri_config)
if JenkinsAction.CREATE_TARBALL in cheri_config.action:
create_tarball(cheri_config)


def build_target(cheri_config, target: Target) -> None:
Expand Down
53 changes: 33 additions & 20 deletions pycheribuild/jenkins_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
import contextlib
import inspect
from pathlib import Path
from typing import Optional

from .config.jenkinsconfig import CheriConfig, JenkinsConfig
from .config.loader import CommandLineConfigOption
from .config.target_info import CrossCompileTarget
from .config.target_info import AbstractProject, CrossCompileTarget
from .projects.project import ComputedDefaultValue, Project
from .targets import MultiArchTargetAlias, SimpleTargetAlias, Target, target_manager
from .utils import fatal_error, status_update
Expand All @@ -45,7 +46,8 @@ def default_install_prefix(xtarget: CrossCompileTarget, cheri_config: CheriConfi
return Path("/opt", dirname)


def jenkins_override_install_dirs_hack(cheri_config: CheriConfig, install_prefix_arg: Optional[Path]):
@contextlib.contextmanager
def jenkins_override_install_dirs(cheri_config: CheriConfig, install_prefix_arg: Optional[Path]):
# Ugly workaround to override all install dirs to go to the tarball
all_targets = [
x
Expand All @@ -64,18 +66,10 @@ def jenkins_override_install_dirs_hack(cheri_config: CheriConfig, install_prefix
if target.xtarget.is_native():
fatal_error("Cannot use non-existent sysroot for native target", target.name, pretend=False)

def expected_install_root(tgt: Target, as_template=False) -> Path:
def expected_install_root(p: AbstractProject, tgt: Target) -> Path:
if tgt in sysroot_targets:
if as_template:
return Path("$SYSROOT")
# noinspection PyProtectedMember
proj = tgt._get_or_create_project_no_setup(cross_target=None, config=cheri_config, caller=None)
target_info = proj.target_info
sysroot_dir = target_info.sysroot_dir
return sysroot_dir
return p.target_info.sysroot_dir
else:
if as_template:
return Path("$OUTPUT_ROOT")
return cheri_config.output_root

def expected_install_prefix(tgt: Target) -> Path:
Expand All @@ -86,16 +80,23 @@ def expected_install_prefix(tgt: Target) -> Path:
else:
return install_prefix_arg

def expected_install_path(tgt: Target, as_template=False) -> Path:
root_dir = expected_install_root(tgt, as_template)
def expected_install_path(p: AbstractProject, tgt: Target) -> Path:
root_dir = expected_install_root(p, tgt)
install_prefix = expected_install_prefix(tgt)
return Path(f"{root_dir}{install_prefix}")

# Save original class-level defaults to restore them later
original_fns = {}
for target in all_targets:
cls = target.project_class
if "_default_install_dir_fn" in cls.__dict__:
original_fns[cls] = cls.__dict__["_default_install_dir_fn"]

for target in all_targets:
cls = target.project_class
cls._default_install_dir_fn = ComputedDefaultValue(
function=lambda config, project: expected_install_path(target),
as_string=str(expected_install_path(target, True)),
function=lambda config, p: expected_install_path(p, target),
as_string="$SYSROOT" if target in sysroot_targets else "$OUTPUT_ROOT",
)

Target.instantiating_targets_should_warn = False
Expand All @@ -119,15 +120,27 @@ def expected_install_path(tgt: Target, as_template=False) -> Path:
status_update("Install directory for", cls.target, "was specified on commandline:", from_cmdline)
project._install_dir = from_cmdline
else:
project._install_dir = expected_install_root(target)
project._install_dir = expected_install_root(project, 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 expected_install_prefix(target) == Path("/"):
project._install_prefix = expected_install_path(target)
project._install_prefix = expected_install_path(project, target)
project.destdir = Path("/")
else:
project._install_prefix = expected_install_prefix(target)
project.destdir = expected_install_root(target)
assert project.real_install_root_dir == expected_install_path(target)
project.destdir = expected_install_root(project, target)
assert project.real_install_root_dir == expected_install_path(project, target)
assert isinstance(inspect.getattr_static(project, "_install_dir"), Path)

try:
yield
finally:
# Restore original class-level defaults to avoid test pollution
for target in all_targets:
cls = target.project_class
if cls in original_fns:
cls._default_install_dir_fn = original_fns[cls]
else:
if "_default_install_dir_fn" in cls.__dict__:
del cls._default_install_dir_fn
21 changes: 10 additions & 11 deletions pycheribuild/projects/build_qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
MakeCommandKind,
Project,
)
from .simple_project import BoolConfigOption, SimpleProject, _cached_get_homebrew_prefix
from .simple_project import BoolConfigOption, SimpleProject, StringConfigOption, _cached_get_homebrew_prefix
from ..config.compilation_targets import BaremetalFreestandingTargetInfo, CompilationTargets
from ..config.config_loader_base import ConfigOptionHandle
from ..utils import OSInfo
Expand Down Expand Up @@ -96,6 +96,15 @@ class BuildQEMUBase(AutotoolsProject):
default=True,
help="Prefer full LTO over LLVM ThinLTO when using LTO",
)
qemu_targets = StringConfigOption(
"targets",
show_help=True,
help="Build QEMU for the following targets",
default=ComputedDefaultValue(
function=lambda config, proj: proj.default_targets,
as_string="QEMU default targets",
),
)

@classmethod
def is_toolchain_target(cls):
Expand All @@ -111,16 +120,6 @@ def _build_type_basic_compiler_flags(self):
return ["-O3"] # Build with -O3 instead of -O2, we want QEMU to be as fast as possible
return super()._build_type_basic_compiler_flags

@classmethod
def setup_config_options(cls, **kwargs):
super().setup_config_options(**kwargs)
cls.qemu_targets = cls.add_config_option(
"targets",
show_help=True,
help="Build QEMU for the following targets",
default=cls.default_targets,
)

@classmethod
def qemu_binary(
cls,
Expand Down
6 changes: 1 addition & 5 deletions pycheribuild/projects/cherios.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

from .cmake_project import CMakeProject
from .project import BuildType, ComputedDefaultValue, GitRepository
from .run_qemu import LaunchQEMUBase, get_default_ssh_forwarding_port
from .run_qemu import LaunchQEMUBase
from .simple_project import BoolConfigOption, IntConfigOption
from ..config.compilation_targets import CompilationTargets
from ..utils import OSInfo
Expand Down Expand Up @@ -74,10 +74,6 @@ class LaunchCheriOSQEMU(LaunchQEMUBase):
qemu_user_networking = False
hide_options_from_help = True

@classmethod
def setup_config_options(cls, **kwargs):
super().setup_config_options(default_ssh_port=get_default_ssh_forwarding_port(40), **kwargs)

@property
def source_project(self):
return BuildCheriOS.get_instance(self, self.config)
Expand Down
13 changes: 4 additions & 9 deletions pycheribuild/projects/cmake_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from typing import Optional, Sequence

from .project import MakeCommandKind, Project, _CMakeAndMesonSharedLogic
from .simple_project import _default_stdout_filter
from .simple_project import ListConfigOption, _default_stdout_filter
from ..config.chericonfig import BuildType
from ..processutils import commandline_to_str
from ..targets import target_manager
Expand Down Expand Up @@ -63,7 +63,9 @@ class CMakeProject(_CMakeAndMesonSharedLogic):
ctest_script_extra_args: Sequence[str] = tuple()
# 3.13.4 is the minimum version for LLVM and that also allows us to use "cmake --build -j <N>" unconditionally.
_minimum_cmake_or_meson_version: "tuple[int, ...]" = (3, 13, 4)
cmake_options: "list[str]"
cmake_options = ListConfigOption(
"cmake-options", metavar="OPTIONS", help="Additional command line options to pass to CMake"
)
configure_command = Path(os.getenv("CMAKE_COMMAND", "cmake"))

def _toolchain_file_list_to_str(self, value: "Sequence[str | Path]") -> str:
Expand Down Expand Up @@ -92,13 +94,6 @@ def _build_type_basic_compiler_flags(self):
# No need to add any flags here, cmake does it for us already
return []

@classmethod
def setup_config_options(cls, **kwargs) -> None:
super().setup_config_options(**kwargs)
cls.cmake_options = cls.add_list_option(
"cmake-options", metavar="OPTIONS", help="Additional command line options to pass to CMake"
)

def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
# allow a -G flag in cmake-options to override the default generator (Ninja).
Expand Down
Loading
Loading