From f06cdf0606ac1cfd946b7b2b70e4e4084baac23c Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 7 Oct 2021 19:20:58 +0200 Subject: docs: minor model refectoring --- docs/refman/model.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/refman/model.py b/docs/refman/model.py index 23515a2..8a051c9 100644 --- a/docs/refman/model.py +++ b/docs/refman/model.py @@ -44,20 +44,20 @@ class Type: # Arguments @dataclass -class ArgBase(NamedObject): +class ArgBase(NamedObject, FetureCheck): type: Type @dataclass -class PosArg(ArgBase, FetureCheck): +class PosArg(ArgBase): default: str @dataclass -class VarArgs(ArgBase, FetureCheck): +class VarArgs(ArgBase): min_varargs: int max_varargs: int @dataclass -class Kwarg(ArgBase, FetureCheck): +class Kwarg(ArgBase): required: bool default: str -- cgit v1.1 From ba93dd20ca7f987ed6f23d525963329b77dc5813 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 7 Oct 2021 19:46:06 +0200 Subject: docs: Added pickle RefMan loader --- docs/refman/loaderpickle.py | 22 ++++++++++++++++++++++ docs/refman/main.py | 7 +++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 docs/refman/loaderpickle.py diff --git a/docs/refman/loaderpickle.py b/docs/refman/loaderpickle.py new file mode 100644 index 0000000..722fedf --- /dev/null +++ b/docs/refman/loaderpickle.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifer: Apache-2.0 +# Copyright 2021 The Meson development team + +from pathlib import Path +import pickle + +from .loaderbase import LoaderBase +from .model import ReferenceManual + +class LoaderPickle(LoaderBase): + def __init__(self, in_file: Path) -> None: + super().__init__() + self.in_file = in_file + + def load_impl(self) -> ReferenceManual: + res = pickle.loads(self.in_file.read_bytes()) + assert isinstance(res, ReferenceManual) + return res + + # Assume that the pickled data is OK and skip validation + def load(self) -> ReferenceManual: + return self.load_impl() diff --git a/docs/refman/main.py b/docs/refman/main.py index 913a0f3..fdc045b 100644 --- a/docs/refman/main.py +++ b/docs/refman/main.py @@ -19,6 +19,7 @@ import typing as T from mesonbuild import mlog from .loaderbase import LoaderBase +from .loaderpickle import LoaderPickle from .loaderyaml import LoaderYAML from .generatorbase import GeneratorBase @@ -30,10 +31,11 @@ meson_root = Path(__file__).absolute().parents[2] def main() -> int: parser = argparse.ArgumentParser(description='Meson reference manual generator') - parser.add_argument('-l', '--loader', type=str, default='yaml', choices=['yaml'], help='Information loader backend') + parser.add_argument('-l', '--loader', type=str, default='yaml', choices=['yaml', 'pickle'], help='Information loader backend') parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md'], required=True, help='Generator backend') parser.add_argument('-s', '--sitemap', type=Path, default=meson_root / 'docs' / 'sitemap.txt', help='Path to the input sitemap.txt') parser.add_argument('-o', '--out', type=Path, required=True, help='Output directory for generated files') + parser.add_argument('-i', '--input', type=Path, default=meson_root / 'docs' / 'yaml', help='Input path for the selected loader') parser.add_argument('--link-defs', type=Path, help='Output file for the MD generator link definition file') parser.add_argument('--depfile', type=Path, default=None, help='Set to generate a depfile') parser.add_argument('--force-color', action='store_true', help='Force enable colors') @@ -44,7 +46,8 @@ def main() -> int: mlog.colorize_console = lambda: True loaders: T.Dict[str, T.Callable[[], LoaderBase]] = { - 'yaml': lambda: LoaderYAML(meson_root / 'docs' / 'yaml'), + 'yaml': lambda: LoaderYAML(args.input), + 'pickle': lambda: LoaderPickle(args.input), } loader = loaders[args.loader]() -- cgit v1.1 From d427c8fdb60ea9fb37673d3f23e4105501c1e42a Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Thu, 7 Oct 2021 19:48:34 +0200 Subject: docs: Added JSON generator --- docs/refman/generatorjson.py | 120 +++++++++++++++++++++++++++++++++++++++++++ docs/refman/jsonschema.py | 87 +++++++++++++++++++++++++++++++ docs/refman/main.py | 4 +- 3 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 docs/refman/generatorjson.py create mode 100644 docs/refman/jsonschema.py diff --git a/docs/refman/generatorjson.py b/docs/refman/generatorjson.py new file mode 100644 index 0000000..f5164d4 --- /dev/null +++ b/docs/refman/generatorjson.py @@ -0,0 +1,120 @@ +# SPDX-License-Identifer: Apache-2.0 +# Copyright 2021 The Meson development team + +from pathlib import Path +import json +import re + +from .generatorbase import GeneratorBase +from . import jsonschema as J +from .model import ( + ReferenceManual, + Function, + Object, + Type, + + PosArg, + VarArgs, + Kwarg, +) + +import typing as T + +class GeneratorJSON(GeneratorBase): + def __init__(self, manual: ReferenceManual, out: Path, enable_modules: bool) -> None: + super().__init__(manual) + self.out = out + self.enable_modules = enable_modules + + def _generate_type(self, typ: Type) -> T.List[J.Type]: + return [ + { + 'obj': x.data_type.name, + 'holds': self._generate_type(x.holds) if x.holds else [], + } + for x in typ.resolved + ] + + def _generate_type_str(self, typ: Type) -> str: + # Remove all whitespaces + return re.sub(r'[ \n\r\t]', '', typ.raw) + + def _generate_arg(self, arg: T.Union[PosArg, VarArgs, Kwarg], isOptarg: bool = False) -> J.Argument: + return { + 'name': arg.name, + 'description': arg.description, + 'since': arg.since if arg.since else None, + 'deprecated': arg.deprecated if arg.deprecated else None, + 'type': self._generate_type(arg.type), + 'type_str': self._generate_type_str(arg.type), + 'required': arg.required if isinstance(arg, Kwarg) else not isOptarg and not isinstance(arg, VarArgs), + 'default': arg.default if isinstance(arg, (PosArg, Kwarg)) else None, + 'min_varargs': arg.min_varargs if isinstance(arg, VarArgs) and arg.min_varargs > 0 else None, + 'max_varargs': arg.max_varargs if isinstance(arg, VarArgs) and arg.max_varargs > 0 else None, + + # Not yet supported + 'notes': [], + 'warnings': [], + } + + def _generate_function(self, func: Function) -> J.Function: + return { + 'name': func.name, + 'description': func.description, + 'since': func.since if func.since else None, + 'deprecated': func.deprecated if func.deprecated else None, + 'notes': func.notes, + 'warnings': func.warnings, + 'example': func.example if func.example else None, + 'returns': self._generate_type(func.returns), + 'returns_str': self._generate_type_str(func.returns), + 'posargs': {x.name: self._generate_arg(x) for x in func.posargs}, + 'optargs': {x.name: self._generate_arg(x, True) for x in func.optargs}, + 'kwargs': {x.name: self._generate_arg(x) for x in self.sorted_and_filtered(list(func.kwargs.values()))}, + 'varargs': self._generate_arg(func.varargs) if func.varargs else None, + } + + def _generate_objects(self, obj: Object) -> J.Object: + return { + 'name': obj.name, + 'description': obj.description, + 'since': obj.since if obj.since else None, + 'deprecated': obj.deprecated if obj.deprecated else None, + 'notes': obj.notes, + 'warnings': obj.warnings, + 'defined_by_module': obj.defined_by_module.name if obj.defined_by_module else None, + 'object_type': obj.obj_type.name, + 'is_container': obj.is_container, + 'example': obj.example if obj.example else None, + 'extends': obj.extends if obj.extends else None, + 'returned_by': [x.name for x in self.sorted_and_filtered(obj.returned_by)], + 'extended_by': [x.name for x in self.sorted_and_filtered(obj.extended_by)], + 'methods': {x.name: self._generate_function(x) for x in self.sorted_and_filtered(obj.methods)}, + } + + def _extract_meson_version(self) -> str: + # Hack around python relative imports to get to the Meson version + import sys + sys.path.append(Path(__file__).resolve().parents[2].as_posix()) + from mesonbuild.coredata import version + return version + + def generate(self) -> None: + data: J.Root = { + 'version_major': J.VERSION_MAJOR, + 'version_minor': J.VERSION_MINOR, + 'meson_version': self._extract_meson_version(), + 'functions': {x.name: self._generate_function(x) for x in self.sorted_and_filtered(self.functions)}, + 'objects': {x.name: self._generate_objects(x) for x in self.sorted_and_filtered(self.objects)}, + 'objects_by_type': { + 'elementary': [x.name for x in self.elementary], + 'builtins': [x.name for x in self.builtins], + 'returned': [x.name for x in self.returned], + 'modules': { + x.name: [y.name for y in self.sorted_and_filtered(self.extract_returned_by_module(x))] + for x in self.modules + }, + }, + } + + self.out.write_text(json.dumps(data), encoding='utf-8') diff --git a/docs/refman/jsonschema.py b/docs/refman/jsonschema.py new file mode 100644 index 0000000..6d6f98a --- /dev/null +++ b/docs/refman/jsonschema.py @@ -0,0 +1,87 @@ +# SPDX-License-Identifer: Apache-2.0 +# Copyright 2021 The Meson development team + +import typing as T + +# The following variables define the current version of +# the JSON documentation format. This is different from +# the Meson version + +VERSION_MAJOR = 1 # Changes here indicate breaking format changes (changes to existing keys) +VERSION_MINOR = 0 # Changes here indicate non-breaking changes (only new keys are added to the existing structure) + +class BaseObject(T.TypedDict): + ''' + Base object for most dicts in the JSON doc. + + All objects inheriting from BaseObject will support + the keys specified here: + ''' + name: str + description: str + since: T.Optional[str] + deprecated: T.Optional[str] + notes: T.List[str] + warnings: T.List[str] + +class Type(T.TypedDict): + obj: str # References an object from `root.objects` + holds: T.Sequence[object] # Mypy does not support recusive dicts, but this should be T.List[Type]... + +class Argument(BaseObject): + ''' + Object that represents any type of a single function or method argumet. + ''' + type: T.List[Type] # A non-empty list of types that are supported. + type_str: str # Formated version of `type`. Is guranteed to not contain any whitespaces. + required: bool + default: T.Optional[str] + min_varargs: T.Optional[int] # Only relevant for varargs, must be `null` for all other types of arguments + max_varargs: T.Optional[int] # Only relevant for varargs, must be `null` for all other types of arguments + +class Function(BaseObject): + ''' + Represents a function or method. + ''' + returns: T.List[Type] # A non-empty list of types that are supported. + returns_str: str # Formated version of `returns`. Is guranteed to not contain any whitespaces. + example: T.Optional[str] + posargs: T.Dict[str, Argument] + optargs: T.Dict[str, Argument] + kwargs: T.Dict[str, Argument] + varargs: T.Optional[Argument] + +class Object(BaseObject): + ''' + Represents all types of Meson objects. The specific object type is stored in the `object_type` field. + ''' + example: T.Optional[str] + object_type: str # Defines the object type: Must be one of: ELEMENTARY, BUILTIN, MODULE, RETURNED + methods: T.Dict[str, Function] + is_container: bool + extends: T.Optional[str] + returned_by: T.List[str] + extended_by: T.List[str] + defined_by_module: T.Optional[str] + +class ObjectsByType(T.TypedDict): + ''' + References to other objects are stored here for ease of navigation / filtering + ''' + elementary: T.List[str] + builtins: T.List[str] + returned: T.List[str] + modules: T.Dict[str, T.List[str]] + + + +class Root(T.TypedDict): + ''' + The root object of the JSON reference manual + ''' + version_major: int # See the description above for + version_minor: int # VERSION_MAJOR and VERSION_MINOR + meson_version: str + functions: T.Dict[str, Function] # A mapping of to a `Function` object for *all* Meson functions + objects: T.Dict[str, Object] # A mapping of to a `Object` object for *all* Meson objects (including modules, elementary, etc.) + objects_by_type: ObjectsByType diff --git a/docs/refman/main.py b/docs/refman/main.py index fdc045b..5bc40f7 100644 --- a/docs/refman/main.py +++ b/docs/refman/main.py @@ -23,6 +23,7 @@ from .loaderpickle import LoaderPickle from .loaderyaml import LoaderYAML from .generatorbase import GeneratorBase +from .generatorjson import GeneratorJSON from .generatorprint import GeneratorPrint from .generatorpickle import GeneratorPickle from .generatormd import GeneratorMD @@ -32,7 +33,7 @@ meson_root = Path(__file__).absolute().parents[2] def main() -> int: parser = argparse.ArgumentParser(description='Meson reference manual generator') parser.add_argument('-l', '--loader', type=str, default='yaml', choices=['yaml', 'pickle'], help='Information loader backend') - parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md'], required=True, help='Generator backend') + parser.add_argument('-g', '--generator', type=str, choices=['print', 'pickle', 'md', 'json'], required=True, help='Generator backend') parser.add_argument('-s', '--sitemap', type=Path, default=meson_root / 'docs' / 'sitemap.txt', help='Path to the input sitemap.txt') parser.add_argument('-o', '--out', type=Path, required=True, help='Output directory for generated files') parser.add_argument('-i', '--input', type=Path, default=meson_root / 'docs' / 'yaml', help='Input path for the selected loader') @@ -57,6 +58,7 @@ def main() -> int: 'print': lambda: GeneratorPrint(refMan), 'pickle': lambda: GeneratorPickle(refMan, args.out), 'md': lambda: GeneratorMD(refMan, args.out, args.sitemap, args.link_defs, not args.no_modules), + 'json': lambda: GeneratorJSON(refMan, args.out, not args.no_modules), } generator = generators[args.generator]() -- cgit v1.1 From d7ea066c5cd04c68c9ed2c3f1b7cd981d9b5f6ac Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 9 Oct 2021 15:47:34 +0200 Subject: docs: Add a validator for the new JSON docs --- docs/jsonvalidator.py | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100755 docs/jsonvalidator.py diff --git a/docs/jsonvalidator.py b/docs/jsonvalidator.py new file mode 100755 index 0000000..6a55ddb --- /dev/null +++ b/docs/jsonvalidator.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifer: Apache-2.0 +# Copyright 2021 The Meson development team + +import argparse +import json +from pathlib import Path +from copy import deepcopy + +import typing as T + +T_None = type(None) + +# Global root object +root: dict + +def assert_has_typed_keys(path: str, data: dict, keys: T.Dict[str, T.Any]) -> dict: + assert set(data.keys()).issuperset(keys.keys()), f'{path}: DIFF: {set(data.keys()).difference(keys.keys())}' + res = dict() + for key, val in keys.items(): + cur = data.pop(key) + assert isinstance(cur, val), f'{path}: type({key}: {cur}) != {val}' + res[key] = cur + return res + +def validate_base_obj(path: str, name: str, obj: dict) -> None: + expected: T.Dict[str, T.Any] = { + 'name': str, + 'description': str, + 'since': (str, T_None), + 'deprecated': (str, T_None), + 'notes': list, + 'warnings': list, + } + cur = assert_has_typed_keys(f'{path}.{name}', obj, expected) + assert cur['name'], f'{path}.{name}' + assert cur['description'], f'{path}.{name}' + assert cur['name'] == name, f'{path}.{name}' + assert all(isinstance(x, str) and x for x in cur['notes']), f'{path}.{name}' + assert all(isinstance(x, str) and x for x in cur['warnings']), f'{path}.{name}' + +def validate_type(path: str, typ: dict) -> None: + expected: T.Dict[str, T.Any] = { + 'obj': str, + 'holds': list, + } + cur = assert_has_typed_keys(path, typ, expected) + assert not typ, f'{path} has extra keys: {typ.keys()}' + assert cur['obj'] in root['objects'], path + for i in cur['holds']: + validate_type(path, i) + +def validate_arg(path: str, name: str, arg: dict) -> None: + validate_base_obj(path, name, arg) + expected: T.Dict[str, T.Any] = { + 'type': list, + 'type_str': str, + 'required': bool, + 'default': (str, T_None), + 'min_varargs': (int, T_None), + 'max_varargs': (int, T_None), + } + cur = assert_has_typed_keys(f'{path}.{name}', arg, expected) + assert not arg, f'{path}.{name} has extra keys: {arg.keys()}' + assert cur['type'], f'{path}.{name}' + assert cur['type_str'], f'{path}.{name}' + for i in cur['type']: + validate_type(f'{path}.{name}', i) + if cur['min_varargs'] is not None: + assert cur['min_varargs'] > 0, f'{path}.{name}' + if cur['max_varargs'] is not None: + assert cur['max_varargs'] > 0, f'{path}.{name}' + +def validate_function(path: str, name: str, func: dict) -> None: + validate_base_obj(path, name, func) + expected: T.Dict[str, T.Any] = { + 'returns': list, + 'returns_str': str, + 'example': (str, T_None), + 'posargs': dict, + 'optargs': dict, + 'kwargs': dict, + 'varargs': (dict, T_None), + } + cur = assert_has_typed_keys(f'{path}.{name}', func, expected) + assert not func, f'{path}.{name} has extra keys: {func.keys()}' + assert cur['returns'], f'{path}.{name}' + assert cur['returns_str'], f'{path}.{name}' + for i in cur['returns']: + validate_type(f'{path}.{name}', i) + for k, v in cur['posargs'].items(): + validate_arg(f'{path}.{name}', k, v) + for k, v in cur['optargs'].items(): + validate_arg(f'{path}.{name}', k, v) + for k, v in cur['kwargs'].items(): + validate_arg(f'{path}.{name}', k, v) + if cur['varargs']: + validate_arg(f'{path}.{name}', cur['varargs']['name'], cur['varargs']) + +def validate_object(path: str, name: str, obj: dict) -> None: + validate_base_obj(path, name, obj) + expected: T.Dict[str, T.Any] = { + 'example': (str, T_None), + 'object_type': str, + 'methods': dict, + 'is_container': bool, + 'extends': (str, T_None), + 'returned_by': list, + 'extended_by': list, + 'defined_by_module': (str, T_None), + } + cur = assert_has_typed_keys(f'{path}.{name}', obj, expected) + assert not obj, f'{path}.{name} has extra keys: {obj.keys()}' + for key, val in cur['methods'].items(): + validate_function(f'{path}.{name}', key, val) + if cur['extends'] is not None: + assert cur['extends'] in root['objects'], f'{path}.{name}' + assert all(isinstance(x, str) for x in cur['returned_by']), f'{path}.{name}' + assert all(isinstance(x, str) for x in cur['extended_by']), f'{path}.{name}' + assert all(x in root['objects'] for x in cur['extended_by']), f'{path}.{name}' + if cur['defined_by_module'] is not None: + assert cur['defined_by_module'] in root['objects'], f'{path}.{name}' + assert cur['object_type'] == 'RETURNED', f'{path}.{name}' + assert root['objects'][cur['defined_by_module']]['object_type'] == 'MODULE', f'{path}.{name}' + assert name in root['objects_by_type']['modules'][cur['defined_by_module']], f'{path}.{name}' + return + assert cur['object_type'] in {'ELEMENTARY', 'BUILTIN', 'MODULE', 'RETURNED'}, f'{path}.{name}' + if cur['object_type'] == 'ELEMENTARY': + assert name in root['objects_by_type']['elementary'], f'{path}.{name}' + if cur['object_type'] == 'BUILTIN': + assert name in root['objects_by_type']['builtins'], f'{path}.{name}' + if cur['object_type'] == 'RETURNED': + assert name in root['objects_by_type']['returned'], f'{path}.{name}' + if cur['object_type'] == 'MODULE': + assert name in root['objects_by_type']['modules'], f'{path}.{name}' + +def main() -> int: + global root + + parser = argparse.ArgumentParser(description='Meson JSON docs validator') + parser.add_argument('doc_file', type=Path, help='The JSON docs to validate') + args = parser.parse_args() + + root_tmp = json.loads(args.doc_file.read_text(encoding='utf-8')) + root = deepcopy(root_tmp) + assert isinstance(root, dict) + + expected: T.Dict[str, T.Any] = { + 'version_major': int, + 'version_minor': int, + 'meson_version': str, + 'functions': dict, + 'objects': dict, + 'objects_by_type': dict, + } + cur = assert_has_typed_keys('root', root_tmp, expected) + assert not root_tmp, f'root has extra keys: {root_tmp.keys()}' + + refs = cur['objects_by_type'] + expected = { + 'elementary': list, + 'builtins': list, + 'returned': list, + 'modules': dict, + } + assert_has_typed_keys(f'root.objects_by_type', refs, expected) + assert not refs, f'root.objects_by_type has extra keys: {refs.keys()}' + assert all(isinstance(x, str) for x in root['objects_by_type']['elementary']) + assert all(isinstance(x, str) for x in root['objects_by_type']['builtins']) + assert all(isinstance(x, str) for x in root['objects_by_type']['returned']) + assert all(isinstance(x, str) for x in root['objects_by_type']['modules']) + assert all(x in root['objects'] for x in root['objects_by_type']['elementary']) + assert all(x in root['objects'] for x in root['objects_by_type']['builtins']) + assert all(x in root['objects'] for x in root['objects_by_type']['returned']) + assert all(x in root['objects'] for x in root['objects_by_type']['modules']) + assert all(root['objects'][x]['object_type'] == 'ELEMENTARY' for x in root['objects_by_type']['elementary']) + assert all(root['objects'][x]['object_type'] == 'BUILTIN' for x in root['objects_by_type']['builtins']) + assert all(root['objects'][x]['object_type'] == 'RETURNED' for x in root['objects_by_type']['returned']) + assert all(root['objects'][x]['object_type'] == 'MODULE' for x in root['objects_by_type']['modules']) + + # Check that module references are correct + assert all(all(isinstance(x, str) for x in v) for k, v in root['objects_by_type']['modules'].items()) + assert all(all(x in root['objects'] for x in v) for k, v in root['objects_by_type']['modules'].items()) + assert all(all(root['objects'][x]['defined_by_module'] == k for x in v) for k, v in root['objects_by_type']['modules'].items()) + + for key, val in cur['functions'].items(): + validate_function('root', key, val) + for key, val in cur['objects'].items(): + validate_object('root', key, val) + + return 0 + +if __name__ == '__main__': + raise SystemExit(main()) -- cgit v1.1 From 2ab5620769f0f364c410d3da94494c123275a3a1 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 9 Oct 2021 22:37:03 +0200 Subject: docs: GitHub Action up the JSON docs --- .github/workflows/website.yml | 12 +++++++++++ docs/meson.build | 49 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index 8e79d08..d707cb5 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -12,6 +12,9 @@ on: paths: - docs/** workflow_dispatch: + release: + types: + - published # This job is copy/paster into wrapdb CI, please update it there when doing any # change here. @@ -40,6 +43,7 @@ jobs: cd docs ../meson.py setup _build ninja -C _build + ninja -C _build test - name: Update website env: SSH_AUTH_SOCK: /tmp/ssh_agent.sock @@ -47,3 +51,11 @@ jobs: cd docs ninja -C _build upload if: env.HAS_SSH_KEY == 'true' + - name: Release the current JSON docs + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: docs/_build/reference_manual.json + tag: ${{ github.ref }} + if: ${{ github.event_name == 'release' }} + diff --git a/docs/meson.build b/docs/meson.build index c53abcb..a752965 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -15,27 +15,60 @@ docs_gen = custom_target( build_by_default: true, install: false) -refman_gen = custom_target( - 'gen_refman', +genrefman = find_program('./genrefman.py') +refman_binary = custom_target( + 'gen_refman_bin', input: files('sitemap.txt'), - output: ['configured_sitemap.txt', 'refman_links.json'], + output: 'reference_manual.bin', depfile: 'reman_dep.d', command: [ - find_program('./genrefman.py'), + genrefman, + '-l', 'yaml', + '-g', 'pickle', + '-o', '@OUTPUT@', + '--depfile', '@DEPFILE@', + '--force-color', + ] +) + +refman_md = custom_target( + 'gen_refman_md', + input: refman_binary, + output: ['configured_sitemap.txt', 'refman_links.json'], + command: [ + genrefman, + '-l', 'pickle', '-g', 'md', - '-s', '@INPUT@', + '-s', files('sitemap.txt'), + '-i', '@INPUT@', '-o', '@OUTPUT0@', '--link-defs', '@OUTPUT1@', - '--depfile', '@DEPFILE@', '--force-color', '--no-modules', ], ) +refman_json = custom_target( + 'gen_refman_json', + build_by_default: true, + input: refman_binary, + output: 'reference_manual.json', + command: [ + genrefman, + '-l', 'pickle', + '-g', 'json', + '-i', '@INPUT@', + '-o', '@OUTPUT@', + '--force-color', + ], +) + +test('validate_docs', find_program('./jsonvalidator.py'), args: [refman_json]) + hotdoc = import('hotdoc') documentation = hotdoc.generate_doc(meson.project_name(), project_version: meson.project_version(), - sitemap: refman_gen[0], + sitemap: refman_md[0], build_by_default: true, depends: docs_gen, index: 'markdown/index.md', @@ -49,7 +82,7 @@ documentation = hotdoc.generate_doc(meson.project_name(), syntax_highlighting_activate: true, keep_markup_in_code_blocks: true, extra_extension: meson.current_source_dir() / 'extensions' / 'refman_links.py', - refman_data_file: refman_gen[1], + refman_data_file: refman_md[1], ) run_target('upload', -- cgit v1.1 From 88db532bf91a87149f860e982b3d8ef857111395 Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 9 Oct 2021 23:03:17 +0200 Subject: docs: Update docs --- docs/markdown/IDE-integration.md | 11 +++++++++++ docs/refman/templates/root.mustache | 10 ++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index cf1c5c1..e64eccf 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -405,6 +405,17 @@ of a node type or the removal of a key) are unlikely and will be announced in the release notes. +# JSON Reference manual + +In additional to the online [Reference manual](Reference-manual.md), Meson also +offers the manual as JSON file. The content of this file is generated from the +same source as the online documentation. The two versions are thus identical +in content. + +This JSON document is attached to every Meson release since *0.60.0*. The JSON +schema is defined by the class structure given in +[`jsonschema.py`](https://github.com/mesonbuild/meson/blob/master/docs/refman/jsonschema.py) + # Existing integrations - [Gnome Builder](https://wiki.gnome.org/Apps/Builder) diff --git a/docs/refman/templates/root.mustache b/docs/refman/templates/root.mustache index 3752a22..c793b04 100644 --- a/docs/refman/templates/root.mustache +++ b/docs/refman/templates/root.mustache @@ -5,8 +5,14 @@ render-subpages: false # Reference manual -This is the root page of the Meson reference manual. All functions -and methods are documented in detail in the following subpages: +This is the root page of the online Meson reference manual. This +manual is also available in a more machine readable format as a +JSON documented attached to every release since *0.60.0*. See our +[IDE integration](IDE-integration.md) documentation for more +information. + +All functions and methods are documented in detail in the +following subpages: ## Elementary types -- cgit v1.1 From 0eec5480fb2cff1549f7f593a8da29ee7deed01b Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sat, 9 Oct 2021 23:32:46 +0200 Subject: docs: Also check on pull-requests --- .github/workflows/website.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index d707cb5..5da6ef5 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -11,6 +11,9 @@ on: - master paths: - docs/** + pull_request: + paths: + - docs/** workflow_dispatch: release: types: -- cgit v1.1