nixos-render-docs: render manual chapters during manual build
render all manual chapters to docbook from scratch every time the manual is built. nixos-render-docs is quick enough at this to not worry about the cost (needing only about a second), and it means we can remove md-to-db.sh in the next commit. no changes to the rendered html manual except for replacements and smartquotes.
This commit is contained in:
parent
67917ac102
commit
652a283e51
2 changed files with 76 additions and 1 deletions
|
@ -68,7 +68,30 @@ let
|
|||
optionIdPrefix = "test-opt-";
|
||||
};
|
||||
|
||||
sources = lib.sourceFilesBySuffices ./. [".xml"];
|
||||
sources = runCommand "manual-sources" {
|
||||
inputs = lib.sourceFilesBySuffices ./. [ ".xml" ".md" ];
|
||||
nativeBuildInputs = [ pkgs.nixos-render-docs ];
|
||||
} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
cp -r --no-preserve=all $inputs/* .
|
||||
rm -rf from_md
|
||||
|
||||
declare -a convert_args
|
||||
while read -r mf; do
|
||||
if [[ "$mf" = *.chapter.md ]]; then
|
||||
convert_args+=("--chapter")
|
||||
else
|
||||
convert_args+=("--section")
|
||||
fi
|
||||
|
||||
convert_args+=("from_md/''${mf%.md}.xml" "$mf")
|
||||
done < <(find . -type f -name '*.md')
|
||||
|
||||
nixos-render-docs manual docbook-fragment \
|
||||
--manpage-urls ${manpageUrls} \
|
||||
"''${convert_args[@]}"
|
||||
'';
|
||||
|
||||
modulesDoc = runCommand "modules.xml" {
|
||||
nativeBuildInputs = [ pkgs.nixos-render-docs ];
|
||||
|
|
|
@ -76,6 +76,10 @@ class ManualDocBookRenderer(DocBookRenderer):
|
|||
return f"<programlisting>\n{escape(token.content)}</programlisting>"
|
||||
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
|
||||
env: MutableMapping[str, Any]) -> str:
|
||||
# HACK for temporarily being able to replace md-to-db.sh. pandoc used this syntax to
|
||||
# allow md files to inject arbitrary docbook, and manual chapters use it.
|
||||
if token.info == '{=docbook}':
|
||||
return token.content
|
||||
info = f" language={quoteattr(token.info)}" if token.info != "" else ""
|
||||
return f"<programlisting{info}>\n{escape(token.content)}</programlisting>"
|
||||
|
||||
|
@ -93,6 +97,29 @@ class DocBookSectionConverter(BaseConverter):
|
|||
|
||||
return "\n".join(result)
|
||||
|
||||
class ManualFragmentDocBookRenderer(ManualDocBookRenderer):
|
||||
_tag: str = "chapter"
|
||||
|
||||
def _heading_tag(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
|
||||
env: MutableMapping[str, Any]) -> tuple[str, dict[str, str]]:
|
||||
(tag, attrs) = super()._heading_tag(token, tokens, i, options, env)
|
||||
if token.tag == 'h1':
|
||||
return (self._tag, attrs | { 'xmlns:xi': "http://www.w3.org/2001/XInclude" })
|
||||
return (tag, attrs)
|
||||
|
||||
class DocBookFragmentConverter(Converter):
|
||||
__renderer__ = ManualFragmentDocBookRenderer
|
||||
|
||||
def convert(self, file: Path, tag: str) -> str:
|
||||
assert isinstance(self._md.renderer, ManualFragmentDocBookRenderer)
|
||||
try:
|
||||
with open(file, 'r') as f:
|
||||
self._md.renderer._title_seen = False
|
||||
self._md.renderer._tag = tag
|
||||
return self._render(f.read())
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"failed to render manual {tag} {file}") from e
|
||||
|
||||
|
||||
|
||||
class Section:
|
||||
|
@ -124,6 +151,14 @@ class ChaptersAction(argparse.Action):
|
|||
if sections is None: raise argparse.ArgumentError(self, "no active section")
|
||||
sections[-1].chapters.extend(map(Path, cast(Sequence[str], values)))
|
||||
|
||||
class SingleFileAction(argparse.Action):
|
||||
def __call__(self, parser: argparse.ArgumentParser, ns: argparse.Namespace,
|
||||
values: Union[str, Sequence[Any], None], opt_str: Optional[str] = None) -> None:
|
||||
assert isinstance(values, Sequence)
|
||||
chapters = getattr(ns, self.dest) or []
|
||||
chapters.append((Path(values[0]), Path(values[1])))
|
||||
setattr(ns, self.dest, chapters)
|
||||
|
||||
def _build_cli_db_section(p: argparse.ArgumentParser) -> None:
|
||||
p.add_argument('--manpage-urls', required=True)
|
||||
p.add_argument("outfile")
|
||||
|
@ -131,6 +166,11 @@ def _build_cli_db_section(p: argparse.ArgumentParser) -> None:
|
|||
p.add_argument("--section-id", dest="contents", action=SectionIDAction)
|
||||
p.add_argument("--chapters", dest="contents", action=ChaptersAction, nargs='+')
|
||||
|
||||
def _build_cli_db_fragment(p: argparse.ArgumentParser) -> None:
|
||||
p.add_argument('--manpage-urls', required=True)
|
||||
p.add_argument("--chapter", action=SingleFileAction, required=True, nargs=2)
|
||||
p.add_argument("--section", action=SingleFileAction, required=True, nargs=2)
|
||||
|
||||
def _run_cli_db_section(args: argparse.Namespace) -> None:
|
||||
with open(args.manpage_urls, 'r') as manpage_urls:
|
||||
md = DocBookSectionConverter(json.load(manpage_urls))
|
||||
|
@ -139,12 +179,24 @@ def _run_cli_db_section(args: argparse.Namespace) -> None:
|
|||
with open(args.outfile, 'w') as f:
|
||||
f.write(md.finalize())
|
||||
|
||||
def _run_cli_db_fragment(args: argparse.Namespace) -> None:
|
||||
with open(args.manpage_urls, 'r') as manpage_urls:
|
||||
md = DocBookFragmentConverter(json.load(manpage_urls))
|
||||
for kind in [ 'chapter', 'section' ]:
|
||||
for (target, file) in getattr(args, kind):
|
||||
converted = md.convert(file, kind)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_text(converted)
|
||||
|
||||
def build_cli(p: argparse.ArgumentParser) -> None:
|
||||
formats = p.add_subparsers(dest='format', required=True)
|
||||
_build_cli_db_section(formats.add_parser('docbook-section'))
|
||||
_build_cli_db_fragment(formats.add_parser('docbook-fragment'))
|
||||
|
||||
def run_cli(args: argparse.Namespace) -> None:
|
||||
if args.format == 'docbook-section':
|
||||
_run_cli_db_section(args)
|
||||
elif args.format == 'docbook-fragment':
|
||||
_run_cli_db_fragment(args)
|
||||
else:
|
||||
raise RuntimeError('format not hooked up', args)
|
||||
|
|
Loading…
Reference in a new issue