mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-09-29 23:52:55 +00:00
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
@ -68,7 +68,30 @@ let
|
|||||||
optionIdPrefix = "test-opt-";
|
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" {
|
modulesDoc = runCommand "modules.xml" {
|
||||||
nativeBuildInputs = [ pkgs.nixos-render-docs ];
|
nativeBuildInputs = [ pkgs.nixos-render-docs ];
|
||||||
|
@ -76,6 +76,10 @@ class ManualDocBookRenderer(DocBookRenderer):
|
|||||||
return f"<programlisting>\n{escape(token.content)}</programlisting>"
|
return f"<programlisting>\n{escape(token.content)}</programlisting>"
|
||||||
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
|
def fence(self, token: Token, tokens: Sequence[Token], i: int, options: OptionsDict,
|
||||||
env: MutableMapping[str, Any]) -> str:
|
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 ""
|
info = f" language={quoteattr(token.info)}" if token.info != "" else ""
|
||||||
return f"<programlisting{info}>\n{escape(token.content)}</programlisting>"
|
return f"<programlisting{info}>\n{escape(token.content)}</programlisting>"
|
||||||
|
|
||||||
@ -93,6 +97,29 @@ class DocBookSectionConverter(BaseConverter):
|
|||||||
|
|
||||||
return "\n".join(result)
|
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:
|
class Section:
|
||||||
@ -124,6 +151,14 @@ class ChaptersAction(argparse.Action):
|
|||||||
if sections is None: raise argparse.ArgumentError(self, "no active section")
|
if sections is None: raise argparse.ArgumentError(self, "no active section")
|
||||||
sections[-1].chapters.extend(map(Path, cast(Sequence[str], values)))
|
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:
|
def _build_cli_db_section(p: argparse.ArgumentParser) -> None:
|
||||||
p.add_argument('--manpage-urls', required=True)
|
p.add_argument('--manpage-urls', required=True)
|
||||||
p.add_argument("outfile")
|
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("--section-id", dest="contents", action=SectionIDAction)
|
||||||
p.add_argument("--chapters", dest="contents", action=ChaptersAction, nargs='+')
|
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:
|
def _run_cli_db_section(args: argparse.Namespace) -> None:
|
||||||
with open(args.manpage_urls, 'r') as manpage_urls:
|
with open(args.manpage_urls, 'r') as manpage_urls:
|
||||||
md = DocBookSectionConverter(json.load(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:
|
with open(args.outfile, 'w') as f:
|
||||||
f.write(md.finalize())
|
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:
|
def build_cli(p: argparse.ArgumentParser) -> None:
|
||||||
formats = p.add_subparsers(dest='format', required=True)
|
formats = p.add_subparsers(dest='format', required=True)
|
||||||
_build_cli_db_section(formats.add_parser('docbook-section'))
|
_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:
|
def run_cli(args: argparse.Namespace) -> None:
|
||||||
if args.format == 'docbook-section':
|
if args.format == 'docbook-section':
|
||||||
_run_cli_db_section(args)
|
_run_cli_db_section(args)
|
||||||
|
elif args.format == 'docbook-fragment':
|
||||||
|
_run_cli_db_fragment(args)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('format not hooked up', args)
|
raise RuntimeError('format not hooked up', args)
|
||||||
|
Loading…
Reference in New Issue
Block a user