The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
11 KiB
383 lines
11 KiB
3 years ago
|
import re
|
||
|
from pathlib import Path
|
||
|
|
||
|
from .generatorbase import GeneratorBase
|
||
|
from .model import (
|
||
|
ReferenceManual,
|
||
|
Function,
|
||
|
Object,
|
||
|
PosArg,
|
||
|
VarArgs,
|
||
|
Kwarg,
|
||
|
)
|
||
|
|
||
|
import typing as T
|
||
|
|
||
|
|
||
|
class ManPage:
|
||
|
def __init__(self, path: Path):
|
||
|
self.path = path
|
||
|
self.text = ""
|
||
|
|
||
|
def reset_font(self) -> None:
|
||
|
self.text += ".P\n"
|
||
|
|
||
|
def title(self, name: str, section: int) -> None:
|
||
|
import datetime
|
||
|
|
||
|
date = datetime.date.today()
|
||
|
self.reset_font()
|
||
|
self.text += f'.TH "{name}" "{section}" "{date}"\n'
|
||
|
|
||
|
def section(self, name: str) -> None:
|
||
|
self.reset_font()
|
||
|
self.text += f".SH {name}\n"
|
||
|
|
||
|
def subsection(self, name: str) -> None:
|
||
|
self.reset_font()
|
||
|
self.text += f".SS {name}\n"
|
||
|
|
||
|
def par(self, text: str) -> None:
|
||
|
self.reset_font()
|
||
|
self.text += f"{text}\n"
|
||
|
|
||
|
def indent(self, amount: int = 4) -> None:
|
||
|
self.text += f".RS {amount}\n"
|
||
|
|
||
|
def unindent(self) -> None:
|
||
|
self.text += ".RE\n"
|
||
|
|
||
|
def br(self) -> None:
|
||
|
self.text += ".br\n"
|
||
|
|
||
|
def nl(self) -> None:
|
||
|
self.text += "\n"
|
||
|
|
||
|
def line(self, text: str) -> None:
|
||
|
if text and text[0] in [".", "'"]:
|
||
|
self.text += "\\"
|
||
|
self.text += f"{text}\n"
|
||
|
|
||
|
def inline(self, text: str) -> None:
|
||
|
self.text += f"{text}"
|
||
|
|
||
|
def write(self) -> None:
|
||
|
self.path.write_text(self.text, encoding="utf-8")
|
||
|
|
||
|
@staticmethod
|
||
|
def bold(text: str) -> str:
|
||
|
return f"\\fB{text}\\fR"
|
||
|
|
||
|
@staticmethod
|
||
|
def italic(text: str) -> str:
|
||
|
return f"\\fI{text}\\fR"
|
||
|
|
||
|
|
||
|
class GeneratorMan(GeneratorBase):
|
||
|
def __init__(
|
||
|
self, manual: ReferenceManual, out: Path, enable_modules: bool
|
||
|
) -> None:
|
||
|
super().__init__(manual)
|
||
|
self.out = out
|
||
|
self.enable_modules = enable_modules
|
||
|
self.links: T.List[str] = []
|
||
|
|
||
|
def generate_description(self, page: ManPage, desc: str) -> None:
|
||
|
def italicise(match: T.Match[str]) -> str:
|
||
|
v = match.group(1)
|
||
|
if v[0] == "@":
|
||
|
v = v[1:]
|
||
|
|
||
|
return ManPage.italic(v)
|
||
|
|
||
|
desc = re.sub(re.compile(r"\[\[(.*?)\]\]", re.DOTALL), italicise, desc)
|
||
|
|
||
|
def linkify(match: T.Match[str]) -> str:
|
||
|
replacement = ManPage.italic(match.group(1))
|
||
|
|
||
|
if match.group(2)[0] != "#":
|
||
|
if match.group(2) in self.links:
|
||
|
num = self.links.index(match.group(2))
|
||
|
else:
|
||
|
self.links.append(match.group(2))
|
||
|
num = len(self.links)
|
||
|
|
||
|
replacement += f"[{num}]"
|
||
|
|
||
|
return replacement
|
||
|
|
||
|
desc = re.sub(re.compile(r"\[(.*?)\]\((.*?)\)", re.DOTALL), linkify, desc)
|
||
|
|
||
|
def bold(match: T.Match[str]) -> str:
|
||
|
return ManPage.bold(match.group(1))
|
||
|
|
||
|
desc = re.sub(re.compile(r"\*(.*?)\*"), bold, desc)
|
||
|
|
||
|
isCode = False
|
||
|
for chunk in desc.split("```"):
|
||
|
if isCode:
|
||
|
page.indent()
|
||
|
lines = chunk.strip().split("\n")
|
||
|
if lines[0] == "meson":
|
||
|
lines = lines[1:]
|
||
|
|
||
|
for line in lines:
|
||
|
page.line(line)
|
||
|
page.br()
|
||
|
page.unindent()
|
||
|
else:
|
||
|
inList = False
|
||
|
for line in chunk.strip().split("\n"):
|
||
|
if len(line) == 0:
|
||
|
page.nl()
|
||
|
if inList:
|
||
|
page.nl()
|
||
|
inList = False
|
||
|
elif line[0:2] in ["- ", "* "]:
|
||
|
if inList:
|
||
|
page.nl()
|
||
|
page.br()
|
||
|
else:
|
||
|
inList = True
|
||
|
|
||
|
page.inline(line.strip() + " ")
|
||
|
elif inList and line[0] == " ":
|
||
|
page.inline(line.strip() + " ")
|
||
|
else:
|
||
|
inList = False
|
||
|
page.line(line)
|
||
|
|
||
|
if inList:
|
||
|
page.nl()
|
||
|
|
||
|
isCode = not isCode
|
||
|
|
||
|
def function_name(self, f: Function, o: Object = None) -> str:
|
||
|
name = ""
|
||
|
if o is not None:
|
||
|
name += f"{o.name}."
|
||
|
|
||
|
name += f.name
|
||
|
return name
|
||
|
|
||
|
def generate_function_signature(
|
||
|
self, page: ManPage, f: Function, o: Object = None
|
||
|
) -> None:
|
||
|
args = []
|
||
|
|
||
|
if f.posargs:
|
||
|
args += [arg.name for arg in f.posargs]
|
||
|
|
||
|
if f.varargs:
|
||
|
args += [f.varargs.name + "..."]
|
||
|
|
||
|
if f.optargs:
|
||
|
args += [f"[{arg.name}]" for arg in f.optargs]
|
||
|
|
||
|
for kwarg in self.sorted_and_filtered(list(f.kwargs.values())):
|
||
|
kw = kwarg.name + ":"
|
||
|
if kwarg.default:
|
||
|
kw += " " + ManPage.bold(kwarg.default)
|
||
|
args += [kw]
|
||
|
|
||
|
ret = ManPage.italic(f.returns.raw) + " "
|
||
|
|
||
|
prefix = f"{ret}{self.function_name(f, o)}("
|
||
|
sig = ", ".join(args)
|
||
|
suffix = ")"
|
||
|
|
||
|
if len(prefix) + len(sig) + len(suffix) > 70:
|
||
|
page.line(prefix)
|
||
|
page.br()
|
||
|
page.indent()
|
||
|
for arg in args:
|
||
|
page.line(arg + ",")
|
||
|
page.br()
|
||
|
page.unindent()
|
||
|
page.line(suffix)
|
||
|
else:
|
||
|
page.line(prefix + sig + suffix)
|
||
|
|
||
|
def base_info(
|
||
|
self, x: T.Union[PosArg, VarArgs, Kwarg, Function, Object]
|
||
|
) -> T.List[str]:
|
||
|
info = []
|
||
|
if x.deprecated:
|
||
|
info += [ManPage.bold("deprecated") + f" since {x.deprecated}"]
|
||
|
if x.since:
|
||
|
info += [f"since {x.since}"]
|
||
|
|
||
|
return info
|
||
|
|
||
|
def generate_function_arg(
|
||
|
self,
|
||
|
page: ManPage,
|
||
|
arg: T.Union[PosArg, VarArgs, Kwarg],
|
||
|
isOptarg: bool = False,
|
||
|
) -> None:
|
||
|
required = (
|
||
|
arg.required
|
||
|
if isinstance(arg, Kwarg)
|
||
|
else not isOptarg and not isinstance(arg, VarArgs)
|
||
|
)
|
||
|
|
||
|
page.line(ManPage.bold(arg.name))
|
||
|
|
||
|
info = [ManPage.italic(arg.type.raw)]
|
||
|
|
||
|
if required:
|
||
|
info += [ManPage.bold("required")]
|
||
|
if isinstance(arg, (PosArg, Kwarg)) and arg.default:
|
||
|
info += [f"default: {arg.default}"]
|
||
|
if isinstance(arg, VarArgs):
|
||
|
mn = 0 if arg.min_varargs < 0 else arg.min_varargs
|
||
|
mx = "N" if arg.max_varargs < 0 else arg.max_varargs
|
||
|
info += [f"{mn}...{mx} times"]
|
||
|
|
||
|
info += self.base_info(arg)
|
||
|
|
||
|
page.line(", ".join(info))
|
||
|
|
||
|
page.br()
|
||
|
page.indent(2)
|
||
|
self.generate_description(page, arg.description.strip())
|
||
|
page.unindent()
|
||
|
page.nl()
|
||
|
|
||
|
def generate_function_argument_section(
|
||
|
self,
|
||
|
page: ManPage,
|
||
|
name: str,
|
||
|
args: T.Sequence[T.Union[PosArg, VarArgs, Kwarg]],
|
||
|
isOptarg: bool = False,
|
||
|
) -> None:
|
||
|
if not args:
|
||
|
return
|
||
|
|
||
|
page.line(ManPage.bold(name))
|
||
|
page.indent()
|
||
|
for arg in args:
|
||
|
self.generate_function_arg(page, arg, isOptarg)
|
||
|
page.unindent()
|
||
|
|
||
|
def generate_sub_sub_section(
|
||
|
self, page: ManPage, name: str, text: T.List[str], process: bool = True
|
||
|
) -> None:
|
||
|
page.line(ManPage.bold(name))
|
||
|
page.indent()
|
||
|
if process:
|
||
|
for line in text:
|
||
|
self.generate_description(page, line.strip())
|
||
|
else:
|
||
|
page.line("\n\n".join([line.strip() for line in text]))
|
||
|
page.unindent()
|
||
|
|
||
|
def generate_function(self, page: ManPage, f: Function, obj: Object = None) -> None:
|
||
|
page.subsection(self.function_name(f, obj) + "()")
|
||
|
page.indent(0)
|
||
|
|
||
|
page.line(ManPage.bold("SYNOPSIS"))
|
||
|
page.indent()
|
||
|
self.generate_function_signature(page, f, obj)
|
||
|
|
||
|
info = self.base_info(f)
|
||
|
if info:
|
||
|
page.nl()
|
||
|
page.line(", ".join(info))
|
||
|
page.unindent()
|
||
|
page.nl()
|
||
|
|
||
|
self.generate_sub_sub_section(page, "DESCRIPTION", [f.description])
|
||
|
page.nl()
|
||
|
|
||
|
self.generate_function_argument_section(page, "POSARGS", f.posargs)
|
||
|
if f.varargs:
|
||
|
self.generate_function_argument_section(page, "VARARGS", [f.varargs])
|
||
|
self.generate_function_argument_section(page, "OPTARGS", f.optargs, True)
|
||
|
self.generate_function_argument_section(
|
||
|
page, "KWARGS", self.sorted_and_filtered(list(f.kwargs.values()))
|
||
|
)
|
||
|
|
||
|
if f.notes:
|
||
|
self.generate_sub_sub_section(page, "NOTES", f.notes)
|
||
|
if f.warnings:
|
||
|
self.generate_sub_sub_section(page, "WARNINGS", f.warnings)
|
||
|
if f.example:
|
||
|
self.generate_sub_sub_section(page, "EXAMPLE", [f.example])
|
||
|
|
||
|
page.unindent()
|
||
|
|
||
|
def generate_object(self, page: ManPage, obj: Object) -> None:
|
||
|
page.subsection(obj.name)
|
||
|
page.indent(2)
|
||
|
|
||
|
info = self.base_info(obj)
|
||
|
if info:
|
||
|
page.line(", ".join(info))
|
||
|
page.br()
|
||
|
|
||
|
if obj.extends:
|
||
|
page.line(ManPage.bold("extends: ") + obj.extends)
|
||
|
page.br()
|
||
|
|
||
|
ret = [x.name for x in self.sorted_and_filtered(obj.returned_by)]
|
||
|
if ret:
|
||
|
page.line(ManPage.bold("returned_by: ") + ", ".join(ret))
|
||
|
page.br()
|
||
|
|
||
|
ext = [x.name for x in self.sorted_and_filtered(obj.extended_by)]
|
||
|
if ext:
|
||
|
page.line(ManPage.bold("extended_by: ") + ", ".join(ext))
|
||
|
page.br()
|
||
|
|
||
|
page.nl()
|
||
|
|
||
|
self.generate_description(page, obj.description.strip())
|
||
|
page.nl()
|
||
|
|
||
|
if obj.notes:
|
||
|
self.generate_sub_sub_section(page, "NOTES", obj.notes)
|
||
|
if obj.warnings:
|
||
|
self.generate_sub_sub_section(page, "WARNINGS", obj.warnings)
|
||
|
if obj.example:
|
||
|
self.generate_sub_sub_section(page, "EXAMPLE", [obj.example])
|
||
|
|
||
|
page.unindent()
|
||
|
|
||
|
def generate(self) -> None:
|
||
|
page = ManPage(self.out)
|
||
|
|
||
|
page.title("meson-reference", 3)
|
||
|
|
||
|
page.section("NAME")
|
||
|
page.par(
|
||
|
f"meson-reference v{self._extract_meson_version()}"
|
||
|
+ " - a reference for meson functions and objects"
|
||
|
)
|
||
|
|
||
|
page.section("DESCRIPTION")
|
||
|
self.generate_description(
|
||
|
page,
|
||
|
"""This manual is divided into two sections, *FUNCTIONS* and *OBJECTS*. *FUNCTIONS* contains a reference for all meson functions and methods. Methods are denoted by [[object_name]].[[method_name]](). *OBJECTS* contains additional information about each object.""",
|
||
|
)
|
||
|
|
||
|
page.section("FUNCTIONS")
|
||
|
for f in self.sorted_and_filtered(self.functions):
|
||
|
self.generate_function(page, f)
|
||
|
|
||
|
for obj in self.sorted_and_filtered(self.objects):
|
||
|
for f in self.sorted_and_filtered(obj.methods):
|
||
|
self.generate_function(page, f, obj)
|
||
|
|
||
|
page.section("OBJECTS")
|
||
|
for obj in self.sorted_and_filtered(self.objects):
|
||
|
self.generate_object(page, obj)
|
||
|
|
||
|
page.section("SEE ALSO")
|
||
|
for i in range(len(self.links)):
|
||
|
link = self.links[i]
|
||
|
page.line(f"[{i + 1}] {link}")
|
||
|
page.br()
|
||
|
|
||
|
page.write()
|