]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Add option to save the BuildSourcesEphemeral overlay.
authorJörg Behrmann <behrmann@physik.fu-berlin.de>
Wed, 8 Jan 2025 15:14:33 +0000 (16:14 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 23 Jan 2025 11:16:02 +0000 (12:16 +0100)
mkosi/__init__.py
mkosi/config.py
mkosi/mounts.py
mkosi/resources/man/mkosi.1.md
tests/test_json.py

index 7cd65cbff901c17dcbbf357895d1458abc0c903b..ae022dddcbc1fe3f8a7548e552e6c32edd12d645 100644 (file)
@@ -729,7 +729,10 @@ def run_prepare_scripts(context: Context, build: bool) -> None:
 
     with (
         mount_build_overlay(context) if build else contextlib.nullcontext(),
-        finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
+        finalize_source_mounts(
+            context.config,
+            ephemeral=bool(context.config.build_sources_ephemeral),
+        ) as sources,
         finalize_config_json(context.config) as json,
     ):
         if build:
@@ -871,7 +874,10 @@ def run_postinst_scripts(context: Context) -> None:
     env |= context.config.environment
 
     with (
-        finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
+        finalize_source_mounts(
+            context.config,
+            ephemeral=bool(context.config.build_sources_ephemeral),
+        ) as sources,
         finalize_config_json(context.config) as json,
     ):
         for script in context.config.postinst_scripts:
@@ -937,7 +943,10 @@ def run_finalize_scripts(context: Context) -> None:
     env |= context.config.environment
 
     with (
-        finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
+        finalize_source_mounts(
+            context.config,
+            ephemeral=bool(context.config.build_sources_ephemeral),
+        ) as sources,
         finalize_config_json(context.config) as json,
     ):
         for script in context.config.finalize_scripts:
@@ -989,7 +998,10 @@ def run_postoutput_scripts(context: Context) -> None:
         env["PROFILES"] = " ".join(context.config.profiles)
 
     with (
-        finalize_source_mounts(context.config, ephemeral=context.config.build_sources_ephemeral) as sources,
+        finalize_source_mounts(
+            context.config,
+            ephemeral=bool(context.config.build_sources_ephemeral),
+        ) as sources,
         finalize_config_json(context.config) as json,
     ):
         for script in context.config.postoutput_scripts:
index 4e5da727ce6c6e9c253169dfbdf8fc3861dc8a87..bffd84ee133d5b136081615a65a81248db8844aa 100644 (file)
@@ -354,6 +354,15 @@ class Incremental(StrEnum):
         return self != Incremental.no
 
 
+class BuildSourcesEphemeral(StrEnum):
+    yes = enum.auto()
+    no = enum.auto()
+    buildcache = enum.auto()
+
+    def __bool__(self) -> bool:
+        return self != BuildSourcesEphemeral.no
+
+
 class Architecture(StrEnum):
     alpha = enum.auto()
     arc = enum.auto()
@@ -1897,7 +1906,7 @@ class Config:
     repart_offline: bool
     history: bool
     build_sources: list[ConfigTree]
-    build_sources_ephemeral: bool
+    build_sources_ephemeral: BuildSourcesEphemeral
     environment: dict[str, str]
     environment_files: list[Path]
     with_tests: bool
@@ -3360,11 +3369,15 @@ SETTINGS: list[ConfigSetting[Any]] = [
     ),
     ConfigSetting(
         dest="build_sources_ephemeral",
-        metavar="BOOL",
+        nargs="?",
         section="Build",
-        parse=config_parse_boolean,
+        parse=config_make_enum_parser_with_boolean(
+            BuildSourcesEphemeral, yes=BuildSourcesEphemeral.yes, no=BuildSourcesEphemeral.no
+        ),
+        default=BuildSourcesEphemeral.no,
         help="Make build sources ephemeral when running scripts",
         scope=SettingScope.universal,
+        choices=BuildSourcesEphemeral.values(),
     ),
     ConfigSetting(
         dest="environment",
@@ -4943,7 +4956,7 @@ def summary(config: Config) -> str:
                      Repart Offline: {yes_no(config.repart_offline)}
                        Save History: {yes_no(config.history)}
                       Build Sources: {line_join_list(config.build_sources)}
-            Build Sources Ephemeral: {yes_no(config.build_sources_ephemeral)}
+            Build Sources Ephemeral: {config.build_sources_ephemeral}
                  Script Environment: {line_join_list(env)}
                   Environment Files: {line_join_list(config.environment_files)}
          Run Tests in Build Scripts: {yes_no(config.with_tests)}
@@ -5134,6 +5147,7 @@ def json_type_transformer(refcls: Union[type[Args], type[Config]]) -> Callable[[
         Firmware: enum_transformer,
         SecureBootSignTool: enum_transformer,
         Incremental: enum_transformer,
+        BuildSourcesEphemeral: enum_transformer,
         Optional[Distribution]: optional_enum_transformer,
         list[ManifestFormat]: enum_list_transformer,
         Verb: enum_transformer,
index 3b83d6e92caae9039db5ea8419b16b4fb97e4f4c..edca3c3e02c8208d98c75560e0945cc142b44d2e 100644 (file)
@@ -6,9 +6,10 @@ import stat
 import tempfile
 from collections.abc import Iterator, Sequence
 from pathlib import Path
-from typing import Optional
+from typing import Optional, Union
 
-from mkosi.config import Config
+from mkosi.config import BuildSourcesEphemeral, Config
+from mkosi.log import die
 from mkosi.sandbox import OverlayOperation
 from mkosi.util import PathString, flatten
 
@@ -56,7 +57,11 @@ def mount_overlay(
 
 
 @contextlib.contextmanager
-def finalize_source_mounts(config: Config, *, ephemeral: bool) -> Iterator[list[PathString]]:
+def finalize_source_mounts(
+    config: Config,
+    *,
+    ephemeral: Union[BuildSourcesEphemeral, bool],
+) -> Iterator[list[PathString]]:
     with contextlib.ExitStack() as stack:
         options: list[PathString] = []
 
@@ -64,12 +69,24 @@ def finalize_source_mounts(config: Config, *, ephemeral: bool) -> Iterator[list[
             src, dst = t.with_prefix("/work/src")
 
             if ephemeral:
-                upperdir = Path(stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay")))
-                os.chmod(upperdir, src.stat().st_mode)
+                if ephemeral == BuildSourcesEphemeral.buildcache:
+                    if config.build_dir is None:
+                        die(
+                            "BuildSourcesEphemeral=buildcache was configured, but no build directory exists.",  # noqa: E501
+                            hint="Configure BuildDirectory= or create mkosi.builddir.",
+                        )
+                    assert config.build_dir
+                    upperdir = config.build_dir / f"mkosi.buildovl.{src.name}"
+                    upperdir.mkdir(mode=src.stat().st_mode, exist_ok=True)
+                else:
+                    upperdir = Path(
+                        stack.enter_context(tempfile.TemporaryDirectory(prefix="volatile-overlay."))
+                    )
+                    os.chmod(upperdir, src.stat().st_mode)
 
                 workdir = Path(
                     stack.enter_context(
-                        tempfile.TemporaryDirectory(dir=upperdir.parent, prefix=f"{upperdir.name}-workdir")
+                        tempfile.TemporaryDirectory(dir=upperdir.parent, prefix=f"{upperdir.name}-workdir.")
                     )
                 )
 
index 5444a8f8ddb814357ebe776501463d4b1f3e9459..bb3a6ab229572690464b68792f759a15239bf6d9 100644 (file)
@@ -1476,12 +1476,17 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     working directory is mounted to `/work/src`.
 
 `BuildSourcesEphemeral=`, `--build-sources-ephemeral=`
-:   Takes a boolean. Disabled by default. Configures whether changes to
-    source directories (the working directory and configured using
-    `BuildSources=`) are persisted. If enabled, all source directories
-    will be reset to their original state every time after running all
+:   Takes a boolean or the special value `buildcache`. Disabled by default. Configures whether changes to
+    source directories, the working directory and configured using `BuildSources=`, are persisted. If
+    enabled, all source directories will be reset to their original state every time after running all
     scripts of a specific type (except sync scripts).
 
+    💥💣💥 If set to `buildcache` the overlay is not discarded when running build scripts, but saved to the
+    build directory, configured via `BuildDirectory=`, and will be reused on subsequent runs. The overlay is
+    still discarded for all other scripts. This option can be used to implement more advanced caching of
+    builds, but can lead to unexpected states of the source directory. When using this option, a build
+    directory must be configured. 💥💣💥
+
 `Environment=`, `--environment=`
 :   Adds variables to the environment that package managers and the
     prepare/build/postinstall/finalize scripts are executed with. Takes
index 3d164f53ab77290faac56e9b1b4ee38ffd9e89fa..00a138a4c89efc950f1a437fbda96aeb06b9aef9 100644 (file)
@@ -14,6 +14,7 @@ from mkosi.config import (
     ArtifactOutput,
     BiosBootloader,
     Bootloader,
+    BuildSourcesEphemeral,
     Cacheonly,
     CertificateSource,
     CertificateSourceType,
@@ -117,7 +118,7 @@ def test_config() -> None:
                     "Target": "/frob"
                 }
             ],
-            "BuildSourcesEphemeral": true,
+            "BuildSourcesEphemeral": "yes",
             "CDROM": false,
             "CPUs": 2,
             "CacheDirectory": "/is/this/the/cachedir",
@@ -424,7 +425,7 @@ def test_config() -> None:
         build_dir=None,
         build_packages=["pkg1", "pkg2"],
         build_scripts=[Path("/path/to/buildscript")],
-        build_sources_ephemeral=True,
+        build_sources_ephemeral=BuildSourcesEphemeral.yes,
         build_sources=[ConfigTree(Path("/qux"), Path("/frob"))],
         cache_dir=Path("/is/this/the/cachedir"),
         cacheonly=Cacheonly.always,