]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
Allow including builtin configs
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 22 Jan 2024 14:17:01 +0000 (15:17 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 23 Jan 2024 10:24:28 +0000 (11:24 +0100)
Let's make it possible to include builtin configurations using
e.g. Include=mkosi-initrd. This allows building the default initrd
or default tools tree independently whereas currently these can only
be built as part of another image.

mkosi/__init__.py
mkosi/__main__.py
mkosi/config.py
mkosi/resources/mkosi.md
mkosi/run.py

index f4b5e57f2b5ca70a3aef03260504fac29bc0dbb9..62ff23075a3f36d119c74169894f75ca832be418 100644 (file)
@@ -1519,7 +1519,7 @@ def build_initrd(context: Context) -> Path:
     for include in context.config.initrd_include:
         cmdline += ["--include", os.fspath(include)]
 
-    args, [config] = parse_config(cmdline + ["build"])
+    args, [config] = parse_config(cmdline + ["build"], resources=context.resources)
 
     make_executable(
         *config.prepare_scripts,
@@ -3368,7 +3368,10 @@ def finalize_default_tools(args: Args, config: Config, *, resources: Path) -> Co
         *(["-f"] * args.force),
     ]
 
-    _, [tools] = parse_config(cmdline + ["--include", os.fspath(resources / "mkosi-tools"), "build"])
+    _, [tools] = parse_config(
+        cmdline + ["--include", os.fspath(resources / "mkosi-tools"), "build"],
+        resources=resources,
+    )
 
     make_executable(
         *tools.prepare_scripts,
@@ -3540,11 +3543,10 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
         if not args.verb.needs_build() and args.verb != Verb.clean:
             continue
 
-        if config.tools_tree and config.tools_tree.name == "default":
-            tools = finalize_default_tools(args, config, resources=resources)
-            fork_and_wait(lambda: run_clean(args, tools)) # type: ignore
+        if config.tools_tree and config.tools_tree == Path("default"):
+            fork_and_wait(run_clean, args, finalize_default_tools(args, config, resources=resources))
 
-        fork_and_wait(lambda: run_clean(args, config))
+        fork_and_wait(run_clean, args, config)
 
     if args.verb == Verb.clean:
         return
@@ -3557,7 +3559,7 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
 
         tools = (
             finalize_default_tools(args, config, resources=resources)
-            if config.tools_tree and config.tools_tree.name == "default"
+            if config.tools_tree and config.tools_tree == Path("default")
             else None
         )
 
@@ -3567,12 +3569,12 @@ def run_verb(args: Args, images: Sequence[Config], *, resources: Path) -> None:
         )
 
         if tools and not (tools.output_dir_or_cwd() / tools.output_with_compression).exists():
-            fork_and_wait(lambda: run_build(args, tools, resources=resources)) # type:ignore
+            fork_and_wait(run_build, args, tools, resources=resources)
 
         if (config.output_dir_or_cwd() / config.output_with_compression).exists():
             continue
 
-        fork_and_wait(lambda: run_build(args, config, resources=resources))
+        fork_and_wait(run_build, args, config, resources=resources)
 
         build = True
 
index 7f93e7fd672fe377fda5e43153af6059c46e43e4..3293814d35aa18b07e92d88806d8a4e322c24599 100644 (file)
@@ -28,7 +28,7 @@ def main() -> None:
     INVOKING_USER.init()
 
     with resource_path(mkosi.resources) as resources:
-        args, images = parse_config(sys.argv[1:])
+        args, images = parse_config(sys.argv[1:], resources=resources)
 
         if args.debug:
             faulthandler.enable()
index 43c2fe924132e56d5055f2c453226acc281f81c9..bb97c0053a740eec677a3b0a20dfde3c8b8def95 100644 (file)
@@ -34,7 +34,14 @@ from mkosi.pager import page
 from mkosi.run import find_binary, run
 from mkosi.sandbox import sandbox_cmd
 from mkosi.types import PathString, SupportsRead
-from mkosi.util import INVOKING_USER, StrEnum, chdir, flatten, is_power_of_2
+from mkosi.util import (
+    INVOKING_USER,
+    StrEnum,
+    chdir,
+    flatten,
+    is_power_of_2,
+    make_executable,
+)
 from mkosi.versioncomp import GenericVersion
 
 __version__ = "20.2"
@@ -44,6 +51,9 @@ ConfigMatchCallback = Callable[[str, Any], bool]
 ConfigDefaultCallback = Callable[[argparse.Namespace], Any]
 
 
+BUILTIN_CONFIGS = ("mkosi-tools", "mkosi-initrd")
+
+
 class Verb(StrEnum):
     build         = enum.auto()
     clean         = enum.auto()
@@ -408,7 +418,11 @@ def parse_path(value: str,
                expanduser: bool = True,
                expandvars: bool = True,
                secret: bool = False,
-               absolute: bool = False) -> Path:
+               absolute: bool = False,
+               constants: Sequence[str] = ()) -> Path:
+    if value in constants:
+        return Path(value)
+
     if expandvars:
         value = os.path.expandvars(value)
 
@@ -718,7 +732,8 @@ def make_path_parser(*,
                      resolve: bool = True,
                      expanduser: bool = True,
                      expandvars: bool = True,
-                     secret: bool = False) -> Callable[[str], Path]:
+                     secret: bool = False,
+                     constants: Sequence[str] = ()) -> Callable[[str], Path]:
     return functools.partial(
         parse_path,
         required=required,
@@ -726,6 +741,7 @@ def make_path_parser(*,
         expanduser=expanduser,
         expandvars=expandvars,
         secret=secret,
+        constants=constants,
     )
 
 
@@ -734,7 +750,8 @@ def config_make_path_parser(*,
                             resolve: bool = True,
                             expanduser: bool = True,
                             expandvars: bool = True,
-                            secret: bool = False) -> ConfigParseCallback:
+                            secret: bool = False,
+                            constants: Sequence[str] = ()) -> ConfigParseCallback:
     def config_parse_path(value: Optional[str], old: Optional[Path]) -> Optional[Path]:
         if not value:
             return None
@@ -746,6 +763,7 @@ def config_make_path_parser(*,
             expanduser=expanduser,
             expandvars=expandvars,
             secret=secret,
+            constants=constants,
         )
 
     return config_parse_path
@@ -1508,7 +1526,11 @@ SETTINGS = (
     ConfigSetting(
         dest="include",
         section="Config",
-        parse=config_make_list_parser(delimiter=",", reset=False, parse=make_path_parser()),
+        parse=config_make_list_parser(
+            delimiter=",",
+            reset=False,
+            parse=make_path_parser(constants=BUILTIN_CONFIGS),
+        ),
         help="Include configuration from the specified file or directory",
     ),
     ConfigSetting(
@@ -2325,7 +2347,7 @@ SETTINGS = (
         dest="tools_tree",
         metavar="PATH",
         section="Host",
-        parse=config_make_path_parser(required=False),
+        parse=config_make_path_parser(required=False, constants=("default",)),
         paths=("mkosi.tools",),
         help="Look up programs to execute inside the given tree",
     ),
@@ -2731,7 +2753,7 @@ def resolve_deps(images: Sequence[Config], include: Sequence[str]) -> list[Confi
     return sorted(images, key=lambda i: order.index(i.image))
 
 
-def parse_config(argv: Sequence[str] = ()) -> tuple[Args, tuple[Config, ...]]:
+def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tuple[Args, tuple[Config, ...]]:
     # Compare inodes instead of paths so we can't get tricked by bind mounts and such.
     parsed_includes: set[tuple[int, int]] = set()
     immutable_settings: set[str] = set()
@@ -2781,13 +2803,29 @@ def parse_config(argv: Sequence[str] = ()) -> tuple[Args, tuple[Config, ...]]:
         finally:
             # Parse any includes that were added after yielding.
             for p in getattr(namespace, "include", [])[current_num_of_includes:]:
-                st = p.stat()
+                for c in BUILTIN_CONFIGS:
+                    if p == Path(c):
+                        path = resources / c
+                        break
+                else:
+                    path = p
+
+                st = path.stat()
 
                 if (st.st_dev, st.st_ino) in parsed_includes:
                     continue
 
-                with chdir(p if p.is_dir() else Path.cwd()):
-                    parse_config_one(p if p.is_file() else Path("."), namespace, defaults)
+                if any(p == Path(c) for c in BUILTIN_CONFIGS):
+                    _, [config] = parse_config(["--include", os.fspath(path)])
+                    make_executable(
+                        *config.prepare_scripts,
+                        *config.postinst_scripts,
+                        *config.finalize_scripts,
+                        *config.build_scripts,
+                    )
+
+                with chdir(path if path.is_dir() else Path.cwd()):
+                    parse_config_one(path if path.is_file() else Path("."), namespace, defaults)
                 parsed_includes.add((st.st_dev, st.st_ino))
 
     class ConfigAction(argparse.Action):
index 7be757f0879dd717e34d4be8bb6a25636cfc39f6..7c3a83e60772cff39c4c56022a42bd51e0a5fff3 100644 (file)
@@ -474,6 +474,10 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
 : Note that each path containing extra configuration is only parsed
   once, even if included more than once with `Include=`.
 
+: The builtin configs for the mkosi default initrd and default tools
+  tree can be included by including the literal value `mkosi-initrd` and
+  `mkosi-tools` respectively.
+
 `InitrdInclude=`, `--initrd-include=`
 
 : Same as `Include=`, but the extra configuration files or directories
index c72ed4eca9302156aa092cc376fe58e59eac3739..8c5cb152682f05d9ac0026e54c2a1435f6bf86ef 100644 (file)
@@ -195,12 +195,12 @@ def uncaught_exception_handler(exit: Callable[[int], NoReturn] = sys.exit) -> It
         exit(rc)
 
 
-def fork_and_wait(target: Callable[[], None]) -> None:
+def fork_and_wait(target: Callable[..., None], *args: Any, **kwargs: Any) -> None:
     pid = os.fork()
     if pid == 0:
         with uncaught_exception_handler(exit=os._exit):
             make_foreground_process()
-            target()
+            target(*args, **kwargs)
 
     try:
         _, status = os.waitpid(pid, 0)