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