with (
complete_step(f"Installing build packages for {context.config.distribution.pretty_name()}"),
- mount_build_overlay(context),
+ setup_build_overlay(context),
):
context.config.distribution.install_packages(context, context.config.build_packages)
@contextlib.contextmanager
-def mount_build_overlay(context: Context, volatile: bool = False) -> Iterator[Path]:
+def setup_build_overlay(context: Context, volatile: bool = False) -> Iterator[None]:
d = context.workspace / "build-overlay"
if not d.is_symlink():
with umask(~0o755):
d.mkdir(exist_ok=True)
- with contextlib.ExitStack() as stack:
- lower = [context.root]
+ # We don't support multiple levels of root overlay.
+ assert not context.lowerdirs
+ assert not context.upperdir
+ assert not context.workdir
+ with contextlib.ExitStack() as stack:
if volatile:
- lower += [d]
- upper = None
+ context.lowerdirs = [d]
+ context.upperdir = Path(
+ stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay"))
+ )
+ os.chmod(context.upperdir, d.stat().st_mode)
else:
- upper = d
+ context.upperdir = d
- stack.enter_context(mount_overlay(lower, context.root, upperdir=upper))
+ context.workdir = stack.enter_context(
+ tempfile.TemporaryDirectory(
+ dir=Path(context.upperdir).parent,
+ prefix=f"{Path(context.upperdir).name}-workdir",
+ )
+ )
- yield context.root
+ try:
+ yield
+ finally:
+ context.lowerdirs = []
+ context.upperdir = None
+ context.workdir = None
@contextlib.contextmanager
env |= context.config.finalize_environment()
with (
- mount_build_overlay(context) if build else contextlib.nullcontext(),
+ setup_build_overlay(context) if build else contextlib.nullcontext(),
finalize_source_mounts(
context.config,
ephemeral=bool(context.config.build_sources_ephemeral),
env |= context.config.finalize_environment()
with (
- mount_build_overlay(context, volatile=True),
+ setup_build_overlay(context, volatile=True),
finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
finalize_config_json(context.config) as json,
):
from typing import Optional
from mkosi.config import Args, Config
-from mkosi.util import PathString
+from mkosi.util import PathString, flatten
class Context:
self.keyring_dir = keyring_dir
self.metadata_dir = metadata_dir
self.package_dir = package_dir or (self.workspace / "packages")
+ self.lowerdirs: list[PathString] = []
+ self.upperdir: Optional[PathString] = None
+ self.workdir: Optional[PathString] = None
self.package_dir.mkdir(exist_ok=True)
self.staging.mkdir()
return self.workspace / "root"
def rootoptions(self, dst: PathString = "/buildroot", *, readonly: bool = False) -> list[str]:
- return ["--ro-bind" if readonly else "--bind", os.fspath(self.root), os.fspath(dst)]
+ if self.lowerdirs or self.upperdir:
+ return [
+ "--overlay-lowerdir", os.fspath(self.root),
+ *flatten(["--overlay-lowerdir", os.fspath(lowerdir)] for lowerdir in self.lowerdirs),
+ *(
+ ["--overlay-lowerdir" if readonly else "--overlay-upperdir", os.fspath(self.upperdir)]
+ if self.upperdir
+ else []
+ ),
+ *(["--overlay-workdir", os.fspath(self.workdir)] if self.workdir and not readonly else []),
+ "--overlay", os.fspath(dst),
+ ] # fmt: skip
+ else:
+ return ["--ro-bind" if readonly else "--bind", os.fspath(self.root), os.fspath(dst)]
@property
def staging(self) -> Path: