lix/code-generation: clean up code properly

The code within the code-generation had tons of code-duplicates and was
overall quite meh to read, understand and expand.

This commit refactors the code-generation to make it more readable and
comprehensible, while also unifying its usage a bit more

Change-Id: I3a5df8b7d8d9b6c76e02ef47dfb151c7dab7d0ab
This commit is contained in:
Commentator2.0
2026-01-31 15:32:27 +01:00
committed by piegames
parent 279926d215
commit 8c857790a3
7 changed files with 382 additions and 390 deletions
+86 -85
View File
@@ -1,70 +1,102 @@
import dataclasses
from enum import Enum
from textwrap import dedent, indent
from typing import NamedTuple
from frontmatter import Post
from common import cxx_literal, generate_file, load_data
import argparse
from common import cxx_literal, generate_file, load_data, get_argument_parser
KNOWN_KEYS = {"name", "type", "constructorArgs", "implementation", "impure", "renameInGlobalScope"}
IMPURE_NOTE = """
> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
"""
class BuiltinConstant(NamedTuple):
name: str
type: str
implementation: str
impure: bool
rename_in_global_scope: bool
documentation: str
class TypeName(NamedTuple):
human: str
code: str
class BuiltinType(TypeName, Enum):
attrs = TypeName("set", "nAttrs")
boolean = TypeName("boolean", "nBool")
integer = TypeName("integer", "nInt")
list = TypeName("list", "nList")
null = TypeName("null", "nNull")
string = TypeName("string", "nString")
@classmethod
def parse(cls, datum: Post) -> "BuiltinConstant":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
if (constructor_args := datum.get("constructorArgs")) is not None:
args = [f"NewValueAs::{datum['type']}"] + constructor_args # type: ignore
impl = f"{{{','.join(args)}}}"
else:
impl = datum["implementation"]
return BuiltinConstant(
name=datum["name"], # type: ignore
type=datum["type"], # type: ignore
implementation=impl,
impure=datum.get("impure", False), # type: ignore
rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
documentation=datum.content,
)
def from_string(cls, t_name: str) -> "BuiltinType":
for t in cls:
if t_name == t.name:
return t
msg = f"Invalid builtin type: {t_name}"
raise ValueError(msg)
VALUE_TYPES = {
"attrs": "nAttrs",
"boolean": "nBool",
"integer": "nInt",
"list": "nList",
"null": "nNull",
"string": "nString",
}
@dataclasses.dataclass
class BuiltinConstant:
name: str
documentation: str
HUMAN_TYPES = {
"attrs": "set",
"boolean": "Boolean",
"integer": "integer",
"list": "list",
"null": "null",
"string": "string",
}
# Fields with different name in the Post than in here
# our fields
type: BuiltinType = dataclasses.field(init=False)
# Post fields
type_str: dataclasses.InitVar[str]
constructor_args: dataclasses.InitVar[list[str] | None] = None
implementation: str = ""
impure: bool = False
rename_in_global_scope: bool = True
def __post_init__(self, type_str: str, constructor_args: list[str] | None):
self.type = BuiltinType.from_string(type_str)
if constructor_args is not None:
args = [f"NewValueAs::{type_str}"] + constructor_args
self.implementation = f"{{{','.join(args)}}}"
@property
def code(self) -> str:
cond = "if (!evalSettings.pureEval) " if self.impure else ""
return dedent(f"""
{cond} {{
addConstant(
{cxx_literal(("__" if self.rename_in_global_scope else "") + self.name)},
{self.implementation},
{{
.type = {self.type.code},
.doc = {cxx_literal(self.documentation)},
.impureOnly = {cxx_literal(self.impure)},
}}
);
}}
""")
@property
def docs(self) -> str:
indentation = " " * 3
return dedent(f"""
<dt id="builtins-{self.name}">
<a href="#builtins-{self.name}"><code>{self.name}</code></a> ({self.type.human})
</dt>
<dd>
{indent(self.documentation, indentation)}
{indent(IMPURE_NOTE, indentation) if self.impure else ""}
</dd>
""")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
args = get_argument_parser().parse_args()
builtin_constants = load_data(args.defs, BuiltinConstant.parse)
builtin_constants = load_data(args.defs, BuiltinConstant)
generate_file(
args.header,
@@ -72,40 +104,9 @@ def main():
lambda constant:
# `builtins` is magic and must come first
"" if constant.name == "builtins" else constant.name,
lambda constant: f"""{"if (!evalSettings.pureEval) " if constant.impure else ""}{{
addConstant({cxx_literal(("__" if constant.rename_in_global_scope else "") + constant.name)}, {constant.implementation}, {{
.type = {VALUE_TYPES[constant.type]},
.doc = {cxx_literal(constant.documentation)},
.impureOnly = {cxx_literal(constant.impure)},
}});
}}
""",
)
generate_file(
args.docs,
builtin_constants,
lambda constant: constant.name,
lambda constant: f"""<dt id="builtins-{constant.name}">
<a href="#builtins-{constant.name}"><code>{constant.name}</code></a> ({HUMAN_TYPES[constant.type]})
</dt>
<dd>
{constant.documentation}
"""
+ (
"""> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
"""
if constant.impure
else ""
)
+ """</dd>
""",
lambda b: b.code,
)
generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda b: b.docs)
if __name__ == "__main__":
+64 -78
View File
@@ -1,103 +1,89 @@
from typing import NamedTuple
import dataclasses
from textwrap import dedent, indent
from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {"name", "implementation", "renameInGlobalScope", "args", "experimentalFeature"}
from common import (
cxx_literal,
generate_file,
load_data,
get_argument_parser,
get_experimental_features,
)
class Builtin(NamedTuple):
@dataclasses.dataclass
class Builtin:
name: str
implementation: str
rename_in_global_scope: bool
args: list[str]
experimental_feature: str | None
documentation: str
args: list[str]
experimental_feature: str | None = None
implementation: str = ""
rename_in_global_scope: bool = True
@classmethod
def parse(cls, datum: Post) -> "Builtin":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
return Builtin(
name=datum["name"], # type: ignore
implementation=datum["implementation"]
if "implementation" in datum
else f"prim_{datum['name']}",
rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
args=datum["args"], # type: ignore
experimental_feature=datum.get("experimentalFeature", None), # type: ignore
documentation=datum.content,
def __post_init__(self):
self.implementation = self.implementation or f"prim_{self.name}"
def generate_code(self, experimental_features: dict[str, str]) -> str:
xf = experimental_features[self.experimental_feature]
cond = (
f"if (experimentalFeatureSettings.isEnabled({xf})) "
if self.experimental_feature
else ""
)
return dedent(f"""
{cond}{{
addPrimOp({{
.name = {cxx_literal(("__" if self.rename_in_global_scope else "") + self.name)},
.args = {cxx_literal(self.args)},
.arity = {len(self.args)},
.doc = {cxx_literal(self.documentation)},
.fun = {self.implementation},
.experimentalFeature = {xf},
}});
}}
""")
@property
def docs(self) -> str:
return dedent(f"""
<dt id="builtins-{self.name}">
<a href="#builtins-{self.name}"><code>{self.name} {
" ".join([f"<var>{arg}</var>" for arg in self.args])
}</code></a>
</dt>
<dd>
{indent(self.documentation, " " * 3)}
{
f"This function is only available if the [{self.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{self.experimental_feature}) experimental feature is enabled."
if self.experimental_feature is not None
else ""
}
</dd>
""")
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap = get_argument_parser()
ap.add_argument(
"--experimental-features", help="Directory containing the experimental feature definitions"
)
ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
builtins = load_data(args.defs, Builtin.parse)
builtins = load_data(args.defs, Builtin)
experimental_feature_names = {builtin.experimental_feature for (_, builtin) in builtins}
experimental_feature_names.discard(None)
experimental_feature_files = [
f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = {
path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
for path_and_feature in experimental_features
}
experimental_features[None] = "std::nullopt"
experimental_features = get_experimental_features(
args.experimental_features, [b.experimental_feature for (_, b) in builtins]
)
generate_file(
args.header,
builtins,
lambda builtin: builtin.name,
lambda builtin: f"""{"" if builtin.experimental_feature is None else f"if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) "}{{
addPrimOp({{
.name = {cxx_literal(("__" if builtin.rename_in_global_scope else "") + builtin.name)},
.args = {cxx_literal(builtin.args)},
.arity = {len(builtin.args)},
.doc = {cxx_literal(builtin.documentation)},
.fun = {builtin.implementation},
.experimentalFeature = {experimental_features[builtin.experimental_feature]},
}});
}}
""",
)
generate_file(
args.docs,
builtins,
lambda builtin: builtin.name,
lambda builtin: f"""<dt id="builtins-{builtin.name}">
<a href="#builtins-{builtin.name}"><code>{builtin.name} {" ".join([f"<var>{arg}</var>" for arg in builtin.args])}</code></a>
</dt>
<dd>
{builtin.documentation}
"""
+ (
f"""This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
"""
if builtin.experimental_feature is not None
else ""
)
+ """</dd>
""",
lambda b: b.generate_code(experimental_features),
)
generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda b: b.docs)
if __name__ == "__main__":
@@ -1,77 +0,0 @@
from typing import NamedTuple
from frontmatter import Post
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {"name", "internalName"}
class ExperimentalFeature(NamedTuple):
name: str
internal_name: str
description: str
@classmethod
def parse(cls, datum: Post) -> "ExperimentalFeature":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
return ExperimentalFeature(
name=datum["name"], # type: ignore
internal_name=datum["internalName"], # type: ignore
description=datum.content,
)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
ap.add_argument("--header", help="Path of the declaration header to generate")
ap.add_argument("--impl-header", help="Path of the implementation header to generate")
ap.add_argument("--descriptions", help="Path of the description file to generate")
ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
ap.add_argument("defs", help="Experimental feature definition files", nargs="+")
args = ap.parse_args()
features = load_data(args.defs, ExperimentalFeature.parse)
generate_file(
args.header,
features,
lambda feature: feature.name,
lambda feature: f" {feature.internal_name},\n",
)
generate_file(
args.impl_header,
features,
lambda feature: feature.name,
lambda feature: f""" {{
.tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name},
.name = {cxx_literal(feature.name)},
.description = {cxx_literal(feature.description)},
}},
""",
)
generate_file(
args.descriptions,
features,
lambda feature: feature.name,
lambda feature: f"""## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
{feature.description}
""",
)
generate_file(
args.shortlist,
features,
lambda feature: feature.name,
lambda feature: f" - [`{feature.name}`](@docroot@/contributing/{'deprecated' if args.deprecated else 'experimental'}-features.md#{'dp' if args.deprecated else 'xp'}-feature-{feature.name})\n",
)
if __name__ == "__main__":
main()
@@ -0,0 +1,74 @@
import dataclasses
from enum import Enum
from textwrap import dedent
from typing import ClassVar, NamedTuple
from common import cxx_literal, generate_file, load_data, get_argument_parser
class FeatureTypeNames(NamedTuple):
code_tag: str
doc_tag: str
class FeatureType(FeatureTypeNames, Enum):
experimental = FeatureTypeNames("Xp", "xp")
deprecated = FeatureTypeNames("Dep", "dp")
@dataclasses.dataclass
class ExtraFeature:
name: str
internal_name: str
documentation: str
type: ClassVar[FeatureType]
@property
def code(self) -> str:
return dedent(f"""
{{
.tag = {ExtraFeature.type.code_tag}::{self.internal_name},
.name = {cxx_literal(self.name)},
.description = {cxx_literal(self.documentation)},
}},
""")
@property
def docs(self) -> str:
return dedent(f"""
## [`{self.name}`]{{#{ExtraFeature.type.doc_tag}-feature-{self.name}}}
{self.documentation}
""")
@property
def short_docs(self) -> str:
return f" - [`{self.name}`](@docroot@/contributing/{ExtraFeature.type.name}-features.md#{ExtraFeature.type.doc_tag}-feature-{self.name})\n"
def main():
ap = get_argument_parser()
ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
ap.add_argument("--impl-header", help="Path of the implementation header to generate")
ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
args = ap.parse_args()
ExtraFeature.type = FeatureType.deprecated if args.deprecated else FeatureType.experimental
features = load_data(args.defs, ExtraFeature)
generate_file(
args.header,
features,
lambda feature: feature.name,
lambda feature: f" {feature.internal_name},\n",
)
generate_file(args.impl_header, features, lambda feature: feature.name, lambda f: f.code)
generate_file(args.docs, features, lambda feature: feature.name, lambda f: f.docs)
generate_file(args.shortlist, features, lambda feature: feature.name, lambda f: f.short_docs)
if __name__ == "__main__":
main()
+105 -138
View File
@@ -1,64 +1,106 @@
from typing import NamedTuple, Any
import dataclasses
from textwrap import dedent
from typing import Any
from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
import argparse
KNOWN_KEYS = {
"name",
"internalName",
"platforms",
"type",
"settingType",
"default",
"defaultExpr",
"defaultText",
"aliases",
"experimentalFeature",
"deprecated",
}
from common import (
cxx_literal,
generate_file,
load_data,
get_experimental_features,
get_argument_parser,
)
class Setting(NamedTuple):
PLATFORM_WARNING = """
> **Note**
> This setting is only available on {platforms} systems.
"""
XP_WARNING = """
> **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`{feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{feature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = {feature}
{name} = ...
```
"""
DEPR_WARNING = """
> **Warning**
> This setting is deprecated and will be removed in a future version of Lix.
"""
@dataclasses.dataclass
class Setting:
name: str
internal_name: str
description: str
platforms: list[str] | None
setting_type: str
default_expr: str
default_text: str
aliases: list[str]
experimental_feature: str | None
deprecated: bool
documentation: str
@classmethod
def parse(cls, datum: Post) -> "Setting":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
msg = f"unknown keys: {unknown_keys!r}"
raise ValueError(msg)
default_text = (
f"`{nix_conf_literal(datum['default'])}`"
if "default" in datum
else datum["defaultText"]
)
if default_text == "``":
default_text = "*empty*"
return Setting(
name=datum["name"], # type: ignore
internal_name=datum["internalName"], # type: ignore
description=datum.content,
platforms=datum.get("platforms", None), # type: ignore
setting_type=f"Setting<{datum['type']}>" if "type" in datum else datum["settingType"],
default_expr=cxx_literal(datum["default"])
if "default" in datum
else datum["defaultExpr"],
default_text=default_text,
aliases=datum.get("aliases", []), # type: ignore
experimental_feature=datum.get("experimentalFeature", None), # type: ignore
deprecated=datum.get("deprecated", False), # type: ignore
default_text: str = ""
setting_type: str = ""
default_expr: str = ""
platforms: list[str] = dataclasses.field(default_factory=list)
aliases: list[str] = dataclasses.field(default_factory=list)
experimental_feature: str | None = None
deprecated: bool = False
default: dataclasses.InitVar[str | None] = None
type_str: dataclasses.InitVar[str | None] = None
def __post_init__(self, default: Any, type_str: str | None):
if default is not None: # is not None nor an empty String
self.default_text = f"`{nix_conf_literal(default)}`"
self.default_expr = self.default_expr or cxx_literal(default)
self.default_text = self.default_text or "*empty*"
if type_str is not None:
self.setting_type = f"Setting<{type_str}>"
def generate_code(self, experimental_features: dict[str | None, str]) -> str:
indentation = " " * 4
expr = (indent(indentation, self.default_expr) + indentation) if "\n" in self.default_expr else self.default_expr
return dedent(f"""
{self.setting_type} {self.internal_name} {{
this,
{expr},
{cxx_literal(self.name)},
{cxx_literal(self.documentation)},
{cxx_literal(self.aliases)},
true,
{experimental_features[self.experimental_feature]},
{cxx_literal(self.deprecated)}
}};
""")
@property
def docs(self) -> str:
indentation = " " * 3
platforms = [p.capitalize() for p in self.platforms]
aliases = [f"`{item}`" for item in self.aliases]
description = dedent(f"""
{indent(indentation, self.documentation)}
{indent(indentation, PLATFORM_WARNING.format(platforms=str(platforms)[1:-1])) if self.platforms else ""}
{indent(indentation, XP_WARNING.format(feature=self.experimental_feature, name=self.name)) if self.experimental_feature is not None else ""}
{indent(indentation, DEPR_WARNING) if self.deprecated else ""}
**Default:** {self.default_text}
{f"**Deprecated alias:** {str(aliases)[1:-1]}\n" if self.aliases else ""}
""")
return f'- <span id="conf-{self.name}">[`{self.name}`](#conf-{self.name})</span>' + indent(
" ", # indent by two space to make it part of the list point
description,
)
@@ -87,103 +129,28 @@ def indent(prefix: str, body: str) -> str:
def main():
ap = argparse.ArgumentParser()
ap = get_argument_parser()
ap.add_argument("--kernel", help="Name of the kernel Lix will run on")
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument(
"--experimental-features", help="Directory containing the experimental feature definitions"
)
ap.add_argument("defs", help="Setting definition files", nargs="+")
args = ap.parse_args()
settings = load_data(args.defs, Setting.parse)
settings = load_data(args.defs, Setting)
experimental_feature_names = {setting.experimental_feature for (_, setting) in settings}
experimental_feature_names.discard(None)
experimental_feature_files = [
f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
experimental_features = {
path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
for path_and_feature in experimental_features
}
experimental_features[None] = "std::nullopt"
experimental_features = get_experimental_features(
args.experimental_features, [s.experimental_feature for (_, s) in settings]
)
generate_file(
args.header,
settings,
lambda setting: setting.name,
lambda setting: f"""{setting.setting_type} {setting.internal_name} {{
this,
{setting.default_expr},
{cxx_literal(setting.name)},
{cxx_literal(setting.description)},
{cxx_literal(setting.aliases)},
true,
{experimental_features[setting.experimental_feature]},
{cxx_literal(setting.deprecated)}
}};
"""
if setting.platforms is None or args.kernel in setting.platforms
lambda setting: setting.generate_code(experimental_features)
if not setting.platforms or args.kernel in setting.platforms
else "",
)
generate_file(
args.docs,
settings,
lambda setting: setting.name,
lambda setting: f"""- <span id="conf-{setting.name}">[`{setting.name}`](#conf-{setting.name})</span>
{indent(" ", setting.description)}
"""
+ (
f""" > **Note**
> This setting is only available on {", ".join([platform_names[platform] for platform in setting.platforms])} systems.
"""
if setting.platforms is not None
else ""
)
+ (
f""" > **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
To change this setting, you need to make sure the corresponding experimental feature,
[`{setting.experimental_feature}`](@docroot@/contributing/experimental-features.md#xp-feature-{setting.experimental_feature}),
is enabled.
For example, include the following in [`nix.conf`](#):
```
extra-experimental-features = {setting.experimental_feature}
{setting.name} = ...
```
"""
if setting.experimental_feature is not None
else ""
)
+ (
""" > **Warning**
> This setting is deprecated and will be removed in a future version of Lix.
"""
if setting.deprecated
else ""
)
+ f""" **Default:** {setting.default_text}
"""
+ (
f""" **Deprecated alias:** {", ".join([f"`{item}`" for item in setting.aliases])}
"""
if setting.aliases != []
else ""
),
)
generate_file(args.docs, settings, lambda setting: setting.name, lambda setting: setting.docs)
if __name__ == "__main__":
+49 -8
View File
@@ -1,3 +1,5 @@
import argparse
import re
from collections.abc import Callable
from pathlib import Path
from typing import Any
@@ -42,23 +44,52 @@ def cxx_literal(v: Any) -> str:
raise NotImplementedError(msg)
def load_data[T](defs: list[str], parse_function: Callable[[frontmatter.Post], T]) -> list[T]:
def get_experimental_features(
base_path: str, human_names: list[str | None]
) -> dict[str | None, str]:
experimental_feature_files = {
f"{base_path}/{xp_name}.md" for xp_name in human_names if xp_name is not None
}
from build_extra_features import ExtraFeature # noqa: PLC0415 # Avoid cyclic import
experimental_features_data = load_data(list(experimental_feature_files), ExtraFeature)
experimental_features: dict[str | None, str] = {
xf.name: f"Xp::{xf.internal_name}" for _, xf in experimental_features_data
}
experimental_features[None] = "std::nullopt"
return experimental_features
FIELD_RENAMES = {"type": "type_str", "content": "documentation"}
def load_data[T](defs: list[str], parse_function: type[T]) -> list[tuple[str, T]]:
data = []
for path in defs:
try:
datum = frontmatter.load(path)
data.append((path, parse_function(datum)))
datum = {
# convert camelCase to snake_case
re.sub(r"(?<=.)([A-Z])", lambda m: f"_{m.group(1).lower()}", k): v
for k, v in frontmatter.load(path).to_dict().items()
}
for post_name, field_name in FIELD_RENAMES.items():
if post_name in datum:
datum[field_name] = datum.pop(post_name)
data.append((path, parse_function(**datum)))
except Exception as e:
e.add_note(f"in {path}")
raise
return data
def generate_file(
def generate_file[T](
path: str | None,
data: list[Any],
sort_key_function: Callable[[Any], str],
generate_function: Callable[[Any], str],
data: list[T],
sort_key_function: Callable[[T], str],
generate_function: Callable[[T], str],
):
if path is not None:
with Path(path).open("w") as out:
@@ -66,7 +97,17 @@ def generate_file(
data, key=lambda path_and_datum: sort_key_function(path_and_datum[1])
):
try:
out.write(generate_function(datum))
text = generate_function(datum)
out.write(text)
except Exception as e:
e.add_note(f"in {path}")
raise
def get_argument_parser() -> argparse.ArgumentParser:
ap = argparse.ArgumentParser()
ap.add_argument("--header", help="Path of the header to generate")
ap.add_argument("--docs", help="Path of the documentation file to generate")
ap.add_argument("defs", help="Builtin definition files", nargs="+")
return ap
+4 -4
View File
@@ -187,11 +187,11 @@ deprecated_feature_definitions = files(
experimental_features_gen = custom_target(
command : [
python.full_path(),
'@SOURCE_ROOT@/lix/code-generation/build_experimental_features.py',
'@SOURCE_ROOT@/lix/code-generation/build_extra_features.py',
'--header', '@OUTPUT0@',
'--impl-header', '@OUTPUT1@',
'--shortlist', '@OUTPUT2@',
'--descriptions', '@OUTPUT3@',
'--docs', '@OUTPUT3@',
'@INPUT@',
],
input : experimental_feature_definitions,
@@ -219,12 +219,12 @@ experimental_feature_descriptions_md = experimental_features_gen[3]
deprecated_features_gen = custom_target(
command : [
python.full_path(),
'@SOURCE_ROOT@/lix/code-generation/build_experimental_features.py',
'@SOURCE_ROOT@/lix/code-generation/build_extra_features.py',
'--deprecated',
'--header', '@OUTPUT0@',
'--impl-header', '@OUTPUT1@',
'--shortlist', '@OUTPUT2@',
'--descriptions', '@OUTPUT3@',
'--docs', '@OUTPUT3@',
'@INPUT@',
],
input : deprecated_feature_definitions,