parent
3feaea6b29
commit
ad65a699f9
9 changed files with 784 additions and 1 deletions
@ -0,0 +1,27 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
# Hack to make relative imports to mlog possible |
||||||
|
from pathlib import Path |
||||||
|
import sys |
||||||
|
root = Path(__file__).absolute().parents[1] |
||||||
|
sys.path.insert(0, str(root)) |
||||||
|
|
||||||
|
# Now run the actual code |
||||||
|
from refman.main import main |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
raise SystemExit(main()) |
@ -0,0 +1,67 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod |
||||||
|
import typing as T |
||||||
|
|
||||||
|
from .model import ReferenceManual, Function, Object, ObjectType, NamedObject |
||||||
|
|
||||||
|
_N = T.TypeVar('_N', bound=NamedObject) |
||||||
|
|
||||||
|
class GeneratorBase(metaclass=ABCMeta): |
||||||
|
def __init__(self, manual: ReferenceManual) -> None: |
||||||
|
self.manual = manual |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def generate(self) -> None: |
||||||
|
pass |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def brief(raw: _N) -> str: |
||||||
|
desc_lines = raw.description.split('\n') |
||||||
|
brief = desc_lines[0] |
||||||
|
if '.' in brief and '[[' not in brief: |
||||||
|
brief = brief[:brief.index('.')] |
||||||
|
return brief.strip() |
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def sorted_and_filtered(raw: T.List[_N]) -> T.List[_N]: |
||||||
|
return sorted([x for x in raw if not x.hidden], key=lambda x: x.name) |
||||||
|
|
||||||
|
@property |
||||||
|
def functions(self) -> T.List[Function]: |
||||||
|
return GeneratorBase.sorted_and_filtered(self.manual.functions) |
||||||
|
|
||||||
|
@property |
||||||
|
def objects(self) -> T.List[Object]: |
||||||
|
return GeneratorBase.sorted_and_filtered(self.manual.objects) |
||||||
|
|
||||||
|
@property |
||||||
|
def elementary(self) -> T.List[Object]: |
||||||
|
return [x for x in self.objects if x.obj_type == ObjectType.ELEMENTARY] |
||||||
|
|
||||||
|
@property |
||||||
|
def builtins(self) -> T.List[Object]: |
||||||
|
return [x for x in self.objects if x.obj_type == ObjectType.BUILTIN] |
||||||
|
|
||||||
|
@property |
||||||
|
def returned(self) -> T.List[Object]: |
||||||
|
return [x for x in self.objects if x.obj_type == ObjectType.RETURNED and x.defined_by_module is None] |
||||||
|
|
||||||
|
@property |
||||||
|
def modules(self) -> T.List[Object]: |
||||||
|
return [x for x in self.objects if x.obj_type == ObjectType.MODULE] |
||||||
|
|
||||||
|
def extract_returned_by_module(self, module: Object) -> T.List[Object]: |
||||||
|
return [x for x in self.objects if x.obj_type == ObjectType.RETURNED and x.defined_by_module is module] |
@ -0,0 +1,88 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from .generatorbase import GeneratorBase |
||||||
|
from .model import ReferenceManual, Object, Function, DataTypeInfo, Type, ObjectType |
||||||
|
|
||||||
|
from mesonbuild import mlog |
||||||
|
import typing as T |
||||||
|
|
||||||
|
def my_nested() -> T.ContextManager[None]: |
||||||
|
prefix = '|' * len(mlog.log_depth) |
||||||
|
return mlog.nested(prefix) |
||||||
|
|
||||||
|
class GeneratorPrint(GeneratorBase): |
||||||
|
def _types_to_string(self, typ: Type) -> str: |
||||||
|
def _data_type_to_str(dt: DataTypeInfo) -> str: |
||||||
|
if dt.holds: |
||||||
|
return f'{dt.data_type.name}[{self._types_to_string(dt.holds)}]' |
||||||
|
return dt.data_type.name |
||||||
|
return ' | '.join([_data_type_to_str(x) for x in typ.resolved]) |
||||||
|
|
||||||
|
def _generate_function(self, func: Function) -> None: |
||||||
|
mlog.log() |
||||||
|
mlog.log('Function', mlog.bold(func.name)) |
||||||
|
with my_nested(): |
||||||
|
desc = func.description |
||||||
|
if '\n' in desc: |
||||||
|
desc = desc[:desc.index('\n')] |
||||||
|
mlog.log('Description:', mlog.bold(desc)) |
||||||
|
mlog.log('Return type:', mlog.bold(self._types_to_string(func.returns))) |
||||||
|
mlog.log('Pos args: ', mlog.bold(str([x.name for x in func.posargs]))) |
||||||
|
mlog.log('Opt args: ', mlog.bold(str([x.name for x in func.optargs]))) |
||||||
|
mlog.log('Varargs: ', mlog.bold(func.varargs.name if func.varargs is not None else 'null')) |
||||||
|
mlog.log('Kwargs base:', mlog.bold(func.kwargs_inherit.name if func.kwargs_inherit else 'null')) |
||||||
|
mlog.log('Kwargs: ', mlog.bold(str(list(func.kwargs.keys())))) |
||||||
|
|
||||||
|
def _generate_object(self, obj: Object) -> None: |
||||||
|
tags = [] |
||||||
|
tags += [{ |
||||||
|
ObjectType.ELEMENTARY: mlog.yellow('[elementary]'), |
||||||
|
ObjectType.BUILTIN: mlog.green('[builtin]'), |
||||||
|
ObjectType.MODULE: mlog.blue('[module]'), |
||||||
|
ObjectType.RETURNED: mlog.cyan('[returned]'), |
||||||
|
}[obj.obj_type]] |
||||||
|
if obj.is_container: |
||||||
|
tags += [mlog.red('[container]')] |
||||||
|
mlog.log() |
||||||
|
mlog.log('Object', mlog.bold(obj.name), *tags) |
||||||
|
with my_nested(): |
||||||
|
desc = obj.description |
||||||
|
if '\n' in desc: |
||||||
|
desc = desc[:desc.index('\n')] |
||||||
|
mlog.log('Description:', mlog.bold(desc)) |
||||||
|
mlog.log('Returned by:', mlog.bold(str([x.name for x in obj.returned_by]))) |
||||||
|
mlog.log('Methods:') |
||||||
|
with my_nested(): |
||||||
|
for m in obj.methods: |
||||||
|
self._generate_function(m) |
||||||
|
|
||||||
|
def generate(self) -> None: |
||||||
|
mlog.log('\n\n', mlog.bold('=== Functions ==='), '\n') |
||||||
|
for f in self.functions: |
||||||
|
self._generate_function(f) |
||||||
|
mlog.log('\n\n', mlog.bold('=== Elementary ==='), '\n') |
||||||
|
for obj in self.elementary: |
||||||
|
self._generate_object(obj) |
||||||
|
mlog.log('\n\n', mlog.bold('=== Builtins ==='), '\n') |
||||||
|
for obj in self.builtins: |
||||||
|
self._generate_object(obj) |
||||||
|
mlog.log('\n\n', mlog.bold('=== Returned objects ==='), '\n') |
||||||
|
for obj in self.returned: |
||||||
|
self._generate_object(obj) |
||||||
|
mlog.log('\n\n', mlog.bold('=== Modules ==='), '\n') |
||||||
|
for obj in self.modules: |
||||||
|
self._generate_object(obj) |
||||||
|
for mod_obj in self.extract_returned_by_module(obj): |
||||||
|
self._generate_object(mod_obj) |
@ -0,0 +1,215 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod |
||||||
|
from pathlib import Path |
||||||
|
import re |
||||||
|
import typing as T |
||||||
|
|
||||||
|
from .model import ( |
||||||
|
NamedObject, |
||||||
|
FetureCheck, |
||||||
|
ArgBase, |
||||||
|
DataTypeInfo, |
||||||
|
Type, |
||||||
|
Function, |
||||||
|
Method, |
||||||
|
Object, |
||||||
|
ObjectType, |
||||||
|
ReferenceManual, |
||||||
|
) |
||||||
|
|
||||||
|
from mesonbuild import mlog |
||||||
|
|
||||||
|
class _Resolver: |
||||||
|
def __init__(self) -> None: |
||||||
|
self.type_map: T.Dict[str, Object] = {} |
||||||
|
self.func_map: T.Dict[str, T.Union[Function, Method]] = {} |
||||||
|
self.processed_funcs: T.Set[str] = set() |
||||||
|
|
||||||
|
def _validate_named_object(self, obj: NamedObject) -> None: |
||||||
|
name_regex = re.compile(r'[a-zA-Z0-9_]+') |
||||||
|
obj.name = obj.name.strip() |
||||||
|
obj.description = obj.description.strip() |
||||||
|
assert obj.name and obj.description, 'Both name and description must be set' |
||||||
|
assert obj.name.islower(), f'Object names must be lower case ({obj.name})' |
||||||
|
assert name_regex.match(obj.name) or obj.name == '[index]', f'Invalid name {obj.name}' |
||||||
|
|
||||||
|
def _validate_feature_check(self, obj: FetureCheck) -> None: |
||||||
|
meson_version_reg = re.compile(r'[0-9]+\.[0-9]+\.[0-9]+') |
||||||
|
obj.since = obj.since.strip() |
||||||
|
obj.deprecated = obj.deprecated.strip() |
||||||
|
if obj.since: |
||||||
|
assert meson_version_reg.match(obj.since) |
||||||
|
if obj.deprecated: |
||||||
|
assert meson_version_reg.match(obj.deprecated) |
||||||
|
|
||||||
|
def _resolve_type(self, raw: str) -> Type: |
||||||
|
typ = Type(raw) |
||||||
|
# We can't use `types = raw.split('|')`, because of `list[str | env]` |
||||||
|
types: T.List[str] = [''] |
||||||
|
stack = 0 |
||||||
|
for c in raw: |
||||||
|
if stack == 0 and c == '|': |
||||||
|
types += [''] |
||||||
|
continue |
||||||
|
if c == '[': |
||||||
|
stack += 1 |
||||||
|
if c == ']': |
||||||
|
stack -= 1 |
||||||
|
types[-1] += c |
||||||
|
types = [x.strip() for x in types] |
||||||
|
for t in types: |
||||||
|
t = t.strip() |
||||||
|
idx = t.find('[') |
||||||
|
base_type = t |
||||||
|
held_type = None |
||||||
|
if idx > 0: |
||||||
|
base_type = t[:idx] |
||||||
|
held_type = self._resolve_type(t[idx+1:-1]) |
||||||
|
assert base_type in self.type_map, f'No known object {t}' |
||||||
|
obj = self.type_map[base_type] |
||||||
|
typ.resolved += [DataTypeInfo(obj, held_type)] |
||||||
|
return typ |
||||||
|
|
||||||
|
def _validate_func(self, func: T.Union[Function, Method]) -> None: |
||||||
|
# Always run basic checks, since they also slightly post-process (strip) some strings |
||||||
|
self._validate_named_object(func) |
||||||
|
self._validate_feature_check(func) |
||||||
|
|
||||||
|
func_id = f'{func.obj.name}.{func.name}' if isinstance(func, Method) else func.name |
||||||
|
if func_id in self.processed_funcs: |
||||||
|
return |
||||||
|
|
||||||
|
func.returns = self._resolve_type(func.returns.raw) |
||||||
|
|
||||||
|
all_args: T.List[ArgBase] = [] |
||||||
|
all_args += func.posargs |
||||||
|
all_args += func.optargs |
||||||
|
all_args += func.kwargs.values() |
||||||
|
all_args += [func.varargs] if func.varargs else [] |
||||||
|
|
||||||
|
for arg in all_args: |
||||||
|
arg.type = self._resolve_type(arg.type.raw) |
||||||
|
|
||||||
|
# Handle returned_by |
||||||
|
for obj in func.returns.resolved: |
||||||
|
obj.data_type.returned_by += [func] |
||||||
|
|
||||||
|
# Handle kwargs inehritance |
||||||
|
for base_name in func.kwargs_inherit: |
||||||
|
base_name = base_name.strip() |
||||||
|
assert base_name in self.func_map, f'Unknown base function `{base_name}` for {func.name}' |
||||||
|
base = self.func_map[base_name] |
||||||
|
if base_name not in self.processed_funcs: |
||||||
|
self._validate_func(base) |
||||||
|
|
||||||
|
curr_keys = set(func.kwargs.keys()) |
||||||
|
base_keys = set(base.kwargs.keys()) |
||||||
|
|
||||||
|
# Calculate the missing kwargs from the current set |
||||||
|
missing = {k: v for k, v in base.kwargs.items() if k in base_keys - curr_keys} |
||||||
|
func.kwargs.update(missing) |
||||||
|
|
||||||
|
# Handloe other args inheritance |
||||||
|
_T = T.TypeVar('_T', bound=T.Union[ArgBase, T.List[ArgBase]]) |
||||||
|
def resolve_inherit(name: str, curr: _T, resolver: T.Callable[[Function], _T]) -> _T: |
||||||
|
if name and not curr: |
||||||
|
name = name.strip() |
||||||
|
assert name in self.func_map, f'Unknown base function `{name}` for {func.name}' |
||||||
|
if name not in self.processed_funcs: |
||||||
|
self._validate_func(self.func_map[name]) |
||||||
|
ref_args = resolver(self.func_map[name]) |
||||||
|
assert ref_args is not None, f'Inherited function `{name}` does not have inherited args set' |
||||||
|
return ref_args |
||||||
|
return curr |
||||||
|
|
||||||
|
func.posargs = resolve_inherit(func.posargs_inherit, func.posargs, lambda x: x.posargs) |
||||||
|
func.optargs = resolve_inherit(func.optargs_inherit, func.optargs, lambda x: x.optargs) |
||||||
|
func.varargs = resolve_inherit(func.varargs_inherit, func.varargs, lambda x: x.varargs) |
||||||
|
|
||||||
|
self.processed_funcs.add(func_id) |
||||||
|
|
||||||
|
def validate_and_resolve(self, manual: ReferenceManual) -> ReferenceManual: |
||||||
|
mlog.log('Validating loaded manual...') |
||||||
|
|
||||||
|
# build type map and func map for methods |
||||||
|
for obj in manual.objects: |
||||||
|
assert obj.name not in self.type_map, f'Duplicate object name {obj.name}' |
||||||
|
self.type_map[obj.name] = obj |
||||||
|
for m in obj.methods: |
||||||
|
mid = f'{obj.name}.{m.name}' |
||||||
|
assert mid not in self.type_map, f'Duplicate metod {mid}' |
||||||
|
self.func_map[mid] = m |
||||||
|
|
||||||
|
# Build func map for functions |
||||||
|
for func in manual.functions: |
||||||
|
assert func.name not in [*self.func_map.keys()], f'Duplicate function {func.name}' |
||||||
|
self.func_map[func.name] = func |
||||||
|
|
||||||
|
mlog.log('Validating functions...') |
||||||
|
for func in manual.functions: |
||||||
|
mlog.log(' -- validating', mlog.bold(func.name)) |
||||||
|
self._validate_func(func) |
||||||
|
|
||||||
|
mlog.log('Validating objects...') |
||||||
|
for obj in manual.objects: |
||||||
|
mlog.log(' -- validating', mlog.bold(obj.name)) |
||||||
|
self._validate_named_object(obj) |
||||||
|
self._validate_feature_check(obj) |
||||||
|
# Resolve and validate inheritence |
||||||
|
if obj.extends: |
||||||
|
assert obj.extends in self.type_map, f'Unknown extends object {obj.extends} in {obj.name}' |
||||||
|
obj.extends_obj = self.type_map[obj.extends] |
||||||
|
obj.extends_obj.extended_by += [obj] |
||||||
|
# Only returned objects can be associated with module |
||||||
|
if obj.obj_type is not ObjectType.RETURNED: |
||||||
|
assert obj.defined_by_module is None |
||||||
|
for m in obj.methods: |
||||||
|
assert m.obj is obj |
||||||
|
self._validate_func(m) |
||||||
|
|
||||||
|
# Resolve inherited methods |
||||||
|
for obj in manual.objects: |
||||||
|
inherited_methods = obj.inherited_methods |
||||||
|
curr = obj.extends_obj |
||||||
|
while curr is not None: |
||||||
|
inherited_methods += curr.methods |
||||||
|
curr = curr.extends_obj |
||||||
|
return manual |
||||||
|
|
||||||
|
class LoaderBase(metaclass=ABCMeta): |
||||||
|
def __init__(self) -> None: |
||||||
|
self._input_files: T.List[Path] = [] |
||||||
|
|
||||||
|
@property |
||||||
|
def input_files(self) -> T.List[Path]: |
||||||
|
return list(self._input_files) |
||||||
|
|
||||||
|
def read_file(self, f: Path) -> str: |
||||||
|
assert f.exists() |
||||||
|
assert f.is_file() |
||||||
|
self._input_files += [f.resolve()] |
||||||
|
return f.read_text(encoding='utf-8') |
||||||
|
|
||||||
|
@abstractmethod |
||||||
|
def load_impl(self) -> ReferenceManual: |
||||||
|
pass |
||||||
|
|
||||||
|
def load(self) -> ReferenceManual: |
||||||
|
self._input_files = [] # Reset input files |
||||||
|
manual = self.load_impl() |
||||||
|
resolver = _Resolver() |
||||||
|
with mlog.nested(): |
||||||
|
return resolver.validate_and_resolve(manual) |
@ -0,0 +1,203 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from .loaderbase import LoaderBase |
||||||
|
from .model import ( |
||||||
|
Type, |
||||||
|
PosArg, |
||||||
|
VarArgs, |
||||||
|
Kwarg, |
||||||
|
Function, |
||||||
|
Method, |
||||||
|
ObjectType, |
||||||
|
Object, |
||||||
|
ReferenceManual, |
||||||
|
) |
||||||
|
|
||||||
|
from mesonbuild import mlog |
||||||
|
from mesonbuild import mesonlib |
||||||
|
|
||||||
|
from strictyaml import Map, MapPattern, Optional, Str, Seq, Int, Bool, load, EmptyList, OrValidator |
||||||
|
from pathlib import Path |
||||||
|
import typing as T |
||||||
|
|
||||||
|
d_named_object = { |
||||||
|
'name': Str(), |
||||||
|
'description': Str(), |
||||||
|
} |
||||||
|
|
||||||
|
d_feture_check = { |
||||||
|
Optional('since', default=''): Str(), |
||||||
|
Optional('deprecated', default=''): Str(), |
||||||
|
} |
||||||
|
|
||||||
|
s_posarg = Map({ |
||||||
|
**d_feture_check, |
||||||
|
'description': Str(), |
||||||
|
'type': Str(), |
||||||
|
Optional('default', default=''): Str(), |
||||||
|
}) |
||||||
|
|
||||||
|
s_varargs = Map({ |
||||||
|
**d_named_object, **d_feture_check, |
||||||
|
'type': Str(), |
||||||
|
Optional('min_varargs', default=-1): Int(), |
||||||
|
Optional('max_varargs', default=-1): Int(), |
||||||
|
}) |
||||||
|
|
||||||
|
s_kwarg = Map({ |
||||||
|
**d_feture_check, |
||||||
|
'type': Str(), |
||||||
|
'description': Str(), |
||||||
|
Optional('required', default=False): Bool(), |
||||||
|
Optional('default', default=''): Str(), |
||||||
|
}) |
||||||
|
|
||||||
|
s_function = Map({ |
||||||
|
**d_named_object, **d_feture_check, |
||||||
|
'returns': Str(), |
||||||
|
Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), |
||||||
|
Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), |
||||||
|
Optional('example', default=''): Str(), |
||||||
|
Optional('posargs'): MapPattern(Str(), s_posarg), |
||||||
|
Optional('optargs'): MapPattern(Str(), s_posarg), |
||||||
|
Optional('varargs'): s_varargs, |
||||||
|
Optional('posargs_inherit', default=''): Str(), |
||||||
|
Optional('optargs_inherit', default=''): Str(), |
||||||
|
Optional('varargs_inherit', default=''): Str(), |
||||||
|
Optional('kwargs'): MapPattern(Str(), s_kwarg), |
||||||
|
Optional('kwargs_inherit', default=[]): OrValidator(OrValidator(Seq(Str()), EmptyList()), Str()), |
||||||
|
}) |
||||||
|
|
||||||
|
s_object = Map({ |
||||||
|
**d_named_object, **d_feture_check, |
||||||
|
'long_name': Str(), |
||||||
|
Optional('extends', default=''): Str(), |
||||||
|
Optional('notes', default=[]): OrValidator(Seq(Str()), EmptyList()), |
||||||
|
Optional('warnings', default=[]): OrValidator(Seq(Str()), EmptyList()), |
||||||
|
Optional('example', default=''): Str(), |
||||||
|
Optional('methods'): Seq(s_function), |
||||||
|
Optional('is_container', default=False): Bool() |
||||||
|
}) |
||||||
|
|
||||||
|
class LoaderYAML(LoaderBase): |
||||||
|
def __init__(self, yaml_dir: Path) -> None: |
||||||
|
super().__init__() |
||||||
|
self.yaml_dir = yaml_dir |
||||||
|
self.func_dir = self.yaml_dir / 'functions' |
||||||
|
self.elem_dir = self.yaml_dir / 'elementary' |
||||||
|
self.objs_dir = self.yaml_dir / 'objects' |
||||||
|
self.builtin_dir = self.yaml_dir / 'builtins' |
||||||
|
self.modules_dir = self.yaml_dir / 'modules' |
||||||
|
|
||||||
|
def _process_function_base(self, raw: T.OrderedDict, obj: T.Optional[Object] = None) -> Function: |
||||||
|
# Handle arguments |
||||||
|
posargs = raw.pop('posargs', {}) |
||||||
|
optargs = raw.pop('optargs', {}) |
||||||
|
varargs = raw.pop('varargs', None) |
||||||
|
kwargs = raw.pop('kwargs', {}) |
||||||
|
|
||||||
|
# Fix kwargs_inherit |
||||||
|
if isinstance(raw['kwargs_inherit'], str): |
||||||
|
raw['kwargs_inherit'] = [raw['kwargs_inherit']] |
||||||
|
|
||||||
|
# Parse args |
||||||
|
posargs_mapped: T.List[PosArg] = [] |
||||||
|
optargs_mapped: T.List[PosArg] = [] |
||||||
|
varargs_mapped: T.Optional[VarArgs] = None |
||||||
|
kwargs_mapped: T.Dict[str, Kwarg] = {} |
||||||
|
|
||||||
|
for k, v in posargs.items(): |
||||||
|
v['type'] = Type(v['type']) |
||||||
|
posargs_mapped += [PosArg(name=k, **v)] |
||||||
|
|
||||||
|
for k, v in optargs.items(): |
||||||
|
v['type'] = Type(v['type']) |
||||||
|
optargs_mapped += [PosArg(name=k, **v)] |
||||||
|
|
||||||
|
for k, v in kwargs.items(): |
||||||
|
v['type'] = Type(v['type']) |
||||||
|
kwargs_mapped[k] = Kwarg(name=k, **v) |
||||||
|
|
||||||
|
if varargs is not None: |
||||||
|
varargs['type'] = Type(varargs['type']) |
||||||
|
varargs_mapped = VarArgs(**varargs) |
||||||
|
|
||||||
|
raw['returns'] = Type(raw['returns']) |
||||||
|
|
||||||
|
# Build function object |
||||||
|
if obj is not None: |
||||||
|
return Method( |
||||||
|
posargs=posargs_mapped, |
||||||
|
optargs=optargs_mapped, |
||||||
|
varargs=varargs_mapped, |
||||||
|
kwargs=kwargs_mapped, |
||||||
|
obj=obj, |
||||||
|
**raw, |
||||||
|
) |
||||||
|
return Function( |
||||||
|
posargs=posargs_mapped, |
||||||
|
optargs=optargs_mapped, |
||||||
|
varargs=varargs_mapped, |
||||||
|
kwargs=kwargs_mapped, |
||||||
|
**raw, |
||||||
|
) |
||||||
|
|
||||||
|
def _load_function(self, path: Path, obj: T.Optional[Object] = None) -> Function: |
||||||
|
path_label = path.relative_to(self.yaml_dir).as_posix() |
||||||
|
mlog.log('Loading', mlog.bold(path_label)) |
||||||
|
raw = load(self.read_file(path), s_function, label=path_label).data |
||||||
|
return self._process_function_base(raw) |
||||||
|
|
||||||
|
def _load_object(self, obj_type: ObjectType, path: Path) -> Object: |
||||||
|
path_label = path.relative_to(self.yaml_dir).as_posix() |
||||||
|
mlog.log(f'Loading', mlog.bold(path_label)) |
||||||
|
raw = load(self.read_file(path), s_object, label=path_label).data |
||||||
|
|
||||||
|
def as_methods(mlist: T.List[Function]) -> T.List[Method]: |
||||||
|
res: T.List[Method] = [] |
||||||
|
for i in mlist: |
||||||
|
assert isinstance(i, Method) |
||||||
|
res += [i] |
||||||
|
return res |
||||||
|
|
||||||
|
methods = raw.pop('methods', []) |
||||||
|
obj = Object(methods=[], obj_type=obj_type, **raw) |
||||||
|
obj.methods = as_methods([self._process_function_base(x, obj) for x in methods]) |
||||||
|
return obj |
||||||
|
|
||||||
|
def _load_module(self, path: Path) -> T.List[Object]: |
||||||
|
assert path.is_dir() |
||||||
|
module = self._load_object(ObjectType.MODULE, path / 'module.yaml') |
||||||
|
objs = [] |
||||||
|
for p in path.iterdir(): |
||||||
|
if p.name == 'module.yaml': |
||||||
|
continue |
||||||
|
obj = self._load_object(ObjectType.RETURNED, p) |
||||||
|
obj.defined_by_module = module |
||||||
|
objs += [obj] |
||||||
|
return [module, *objs] |
||||||
|
|
||||||
|
def load_impl(self) -> ReferenceManual: |
||||||
|
mlog.log('Loading YAML refererence manual') |
||||||
|
with mlog.nested(): |
||||||
|
return ReferenceManual( |
||||||
|
functions=[self._load_function(x) for x in self.func_dir.iterdir()], |
||||||
|
objects=mesonlib.listify([ |
||||||
|
[self._load_object(ObjectType.ELEMENTARY, x) for x in self.elem_dir.iterdir()], |
||||||
|
[self._load_object(ObjectType.RETURNED, x) for x in self.objs_dir.iterdir()], |
||||||
|
[self._load_object(ObjectType.BUILTIN, x) for x in self.builtin_dir.iterdir()], |
||||||
|
[self._load_module(x) for x in self.modules_dir.iterdir()] |
||||||
|
], flatten=True) |
||||||
|
) |
@ -0,0 +1,68 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from pathlib import Path |
||||||
|
import argparse |
||||||
|
import typing as T |
||||||
|
|
||||||
|
from mesonbuild import mlog |
||||||
|
|
||||||
|
from .loaderbase import LoaderBase |
||||||
|
from .loaderyaml import LoaderYAML |
||||||
|
|
||||||
|
from .generatorbase import GeneratorBase |
||||||
|
from .generatorprint import GeneratorPrint |
||||||
|
|
||||||
|
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('-g', '--generator', type=str, choices=['print'], required=True, help='Generator backend') |
||||||
|
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') |
||||||
|
args = parser.parse_args() |
||||||
|
|
||||||
|
if args.force_color: |
||||||
|
mlog.colorize_console = lambda: True |
||||||
|
|
||||||
|
loaders: T.Dict[str, T.Callable[[], LoaderBase]] = { |
||||||
|
'yaml': lambda: LoaderYAML(meson_root / 'docs' / 'yaml'), |
||||||
|
} |
||||||
|
|
||||||
|
loader = loaders[args.loader]() |
||||||
|
refMan = loader.load() |
||||||
|
|
||||||
|
generators: T.Dict[str, T.Callable[[], GeneratorBase]] = { |
||||||
|
'print': lambda: GeneratorPrint(refMan), |
||||||
|
} |
||||||
|
generator = generators[args.generator]() |
||||||
|
|
||||||
|
# Generate the depfile if required |
||||||
|
if args.depfile is not None: |
||||||
|
assert isinstance(args.depfile, Path) |
||||||
|
assert isinstance(args.out, Path) |
||||||
|
|
||||||
|
# Also add all files of this package |
||||||
|
script_files = list(Path(__file__).resolve().parent.glob('**/*.py')) |
||||||
|
templates = list(Path(__file__).resolve().parent.glob('**/*.mustache')) |
||||||
|
|
||||||
|
out_text = f'{args.out.resolve().as_posix()}: \\\n' |
||||||
|
for input in loader.input_files + script_files + templates: |
||||||
|
out_text += f' {input.resolve().as_posix():<93} \\\n' |
||||||
|
|
||||||
|
args.depfile.write_text(out_text, encoding='utf-8') |
||||||
|
|
||||||
|
generator.generate() |
||||||
|
return 0 |
@ -0,0 +1,113 @@ |
|||||||
|
# Copyright 2021 The Meson development team |
||||||
|
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
|
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
|
||||||
|
from dataclasses import dataclass, field |
||||||
|
from enum import Enum |
||||||
|
import typing as T |
||||||
|
|
||||||
|
# Utils |
||||||
|
@dataclass |
||||||
|
class NamedObject: |
||||||
|
name: str |
||||||
|
description: str |
||||||
|
|
||||||
|
@property |
||||||
|
def hidden(self) -> bool: |
||||||
|
return self.name.startswith('_') |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class FetureCheck: |
||||||
|
since: str |
||||||
|
deprecated: str |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class DataTypeInfo: |
||||||
|
data_type: 'Object' |
||||||
|
holds: T.Optional['Type'] |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class Type: |
||||||
|
raw: str |
||||||
|
resolved: T.List[DataTypeInfo] = field(init=False, default_factory=list) |
||||||
|
|
||||||
|
|
||||||
|
# Arguments |
||||||
|
@dataclass |
||||||
|
class ArgBase(NamedObject): |
||||||
|
type: Type |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class PosArg(ArgBase, FetureCheck): |
||||||
|
default: str |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class VarArgs(ArgBase, FetureCheck): |
||||||
|
min_varargs: int |
||||||
|
max_varargs: int |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class Kwarg(ArgBase, FetureCheck): |
||||||
|
required: bool |
||||||
|
default: str |
||||||
|
|
||||||
|
|
||||||
|
# Function |
||||||
|
@dataclass |
||||||
|
class Function(NamedObject, FetureCheck): |
||||||
|
notes: T.List[str] |
||||||
|
warnings: T.List[str] |
||||||
|
returns: Type |
||||||
|
example: str |
||||||
|
posargs: T.List[PosArg] |
||||||
|
optargs: T.List[PosArg] |
||||||
|
varargs: T.Optional[VarArgs] |
||||||
|
kwargs: T.Dict[str, Kwarg] |
||||||
|
posargs_inherit: str |
||||||
|
optargs_inherit: str |
||||||
|
varargs_inherit: str |
||||||
|
kwargs_inherit: T.List[str] |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class Method(Function): |
||||||
|
obj: 'Object' |
||||||
|
|
||||||
|
|
||||||
|
# Types and objects |
||||||
|
class ObjectType(Enum): |
||||||
|
ELEMENTARY = 0 |
||||||
|
BUILTIN = 1 |
||||||
|
MODULE = 2 |
||||||
|
RETURNED = 3 |
||||||
|
|
||||||
|
@dataclass |
||||||
|
class Object(NamedObject, FetureCheck): |
||||||
|
notes: T.List[str] |
||||||
|
warnings: T.List[str] |
||||||
|
long_name: str |
||||||
|
example: str |
||||||
|
obj_type: ObjectType |
||||||
|
methods: T.List[Method] |
||||||
|
is_container: bool |
||||||
|
extends: str |
||||||
|
extends_obj: T.Optional['Object'] = None |
||||||
|
defined_by_module: T.Optional['Object'] = None |
||||||
|
returned_by: T.List[T.Union[Function, Method]] = field(default_factory=list) |
||||||
|
extended_by: T.List['Object'] = field(default_factory=list) |
||||||
|
inherited_methods: T.List[Method] = field(default_factory=list) |
||||||
|
|
||||||
|
# ROOT |
||||||
|
@dataclass |
||||||
|
class ReferenceManual: |
||||||
|
functions: T.List[Function] |
||||||
|
objects: T.List[Object] |
Loading…
Reference in new issue