From: Daan De Meyer Date: Wed, 3 Apr 2024 10:21:55 +0000 (+0200) Subject: Add specifiers for various paths X-Git-Tag: v23~38 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=83ddf18bac1a1e4f441cfcdd616714719524bc23;p=thirdparty%2Fmkosi.git Add specifiers for various paths Fixes #2579 --- diff --git a/mkosi/config.py b/mkosi/config.py index 54554e074..745ed0f78 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -1133,6 +1133,12 @@ class Match: match: Callable[[str], bool] +@dataclasses.dataclass(frozen=True) +class Specifier: + char: str + callback: Callable[[argparse.Namespace, Path], str] + + class CustomHelpFormatter(argparse.HelpFormatter): def _format_action_invocation(self, action: argparse.Action) -> str: if not action.option_strings or action.nargs == 0: @@ -2916,6 +2922,23 @@ MATCHES = ( MATCH_LOOKUP = {m.name: m for m in MATCHES} +SPECIFIERS = ( + Specifier( + char="C", + callback=lambda ns, config: os.fspath(config.resolve().parent), + ), + Specifier( + char="P", + callback=lambda ns, config: os.fspath(Path.cwd()), + ), + Specifier( + char="D", + callback=lambda ns, config: os.fspath(ns.directory.resolve()), + ), +) + +SPECIFIERS_LOOKUP_BY_CHAR = {s.char: s for s in SPECIFIERS} + # This regular expression can be used to split "AutoBump" -> ["Auto", "Bump"] # and "NSpawnSettings" -> ["NSpawn", "Settings"] # The first part (?<=[a-z]) is a positive look behind for a lower case letter @@ -3128,7 +3151,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu parsed_includes: set[tuple[int, int]] = set() immutable_settings: set[str] = set() - def expand_specifiers(text: str) -> str: + def expand_specifiers(text: str, path: Path) -> str: percent = False result: list[str] = [] @@ -3138,19 +3161,18 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu if c == "%": result += "%" - else: - s = SETTINGS_LOOKUP_BY_SPECIFIER.get(c) - if not s: - logging.warning(f"Unknown specifier '%{c}' found in {text}, ignoring") - continue - - if (v := finalize_default(s)) is None: + elif setting := SETTINGS_LOOKUP_BY_SPECIFIER.get(c): + if (v := finalize_default(setting)) is None: logging.warning( - f"Setting {s.name} specified by specifier '%{c}' in {text} is not yet set, ignoring" + f"Setting {setting.name} specified by specifier '%{c}' in {text} is not yet set, ignoring" ) continue result += str(v) + elif specifier := SPECIFIERS_LOOKUP_BY_CHAR.get(c): + result += specifier.callback(namespace, path) + else: + logging.warning(f"Unknown specifier '%{c}' found in {text}, ignoring") elif c == "%": percent = True else: @@ -3273,7 +3295,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu negate = v.startswith("!") v = v.removeprefix("!") - v = expand_specifiers(v) + v = expand_specifiers(v, path) if not v: die("Match value cannot be empty") @@ -3365,7 +3387,7 @@ def parse_config(argv: Sequence[str] = (), *, resources: Path = Path("/")) -> tu canonical = s.name if k == name else f"@{s.name}" logging.warning(f"Setting {k} is deprecated, please use {canonical} instead.") - v = expand_specifiers(v) + v = expand_specifiers(v, path) with parse_new_includes(): setattr(ns, s.dest, s.parse(v, getattr(ns, s.dest, None))) diff --git a/mkosi/resources/mkosi.md b/mkosi/resources/mkosi.md index 17299b6ec..cbec10255 100644 --- a/mkosi/resources/mkosi.md +++ b/mkosi/resources/mkosi.md @@ -1875,6 +1875,31 @@ use `%%`. The following specifiers are understood: | `ImageVersion=` | `%v` | | `Profile=` | `%p` | +There are also specifiers that are independent of settings: + +| Specifier | Value | +|-----------|-----------------------------------------| +| `%C` | Parent directory of current config file | +| `%P` | Current working directory | +| `%D` | Directory that mkosi was invoked in | + +Note that the current working directory changes as mkosi parses its +configuration. Specifically, each time mkosi parses a directory +containing a `mkosi.conf` file, mkosi changes its working directory to +that directory. + +Note that the directory that mkosi was invoked in is influenced by the +`--directory=` command line argument. + +The following table shows example values for the directory specifiers +listed above: + +| | `$D/mkosi.conf` | `$D/mkosi.conf.d/abc/abc.conf` | `$D/mkosi.conf.d/abc/mkosi.conf` | +|------|-----------------|--------------------------------|----------------------------------| +| `%C` | `$D` | `$D/mkosi.conf.d` | `$D/mkosi.conf.d/qed` | +| `%P` | `$D` | `$D` | `$D/mkosi.conf.d/qed` | +| `%D` | `$D` | `$D` | `$D` | + ## Supported distributions Images may be created containing installations of the following diff --git a/tests/test_config.py b/tests/test_config.py index 10e389b9c..5fd9231d4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -920,6 +920,28 @@ def test_specifiers(tmp_path: Path) -> None: ImageVersion=%v OutputDirectory=%O Output=%o + ConfigRootDirectory=%D + ConfigRootConfdir=%C + ConfigRootPwd=%P + """ + ) + + (d / "mkosi.conf.d").mkdir() + (d / "mkosi.conf.d/abc.conf").write_text( + """\ + [Content] + Environment=ConfigAbcDirectory=%D + ConfigAbcConfdir=%C + ConfigAbcPwd=%P + """ + ) + (d / "mkosi.conf.d/qed").mkdir() + (d / "mkosi.conf.d/qed/mkosi.conf").write_text( + """ + [Content] + Environment=ConfigQedDirectory=%D + ConfigQedConfdir=%C + ConfigQedPwd=%P """ ) @@ -934,6 +956,15 @@ def test_specifiers(tmp_path: Path) -> None: "ImageVersion": "1.2.3", "OutputDirectory": str(Path.cwd() / "abcde"), "Output": "test", + "ConfigRootDirectory": os.fspath(d), + "ConfigRootConfdir": os.fspath(d), + "ConfigRootPwd": os.fspath(d), + "ConfigAbcDirectory": os.fspath(d), + "ConfigAbcConfdir": os.fspath(d / "mkosi.conf.d"), + "ConfigAbcPwd": os.fspath(d), + "ConfigQedDirectory": os.fspath(d), + "ConfigQedConfdir": os.fspath(d / "mkosi.conf.d/qed"), + "ConfigQedPwd": os.fspath(d / "mkosi.conf.d/qed"), } assert {k: v for k, v in config.environment.items() if k in expected} == expected