|
|
|
@ -18,11 +18,9 @@ from __future__ import annotations |
|
|
|
|
import os, subprocess |
|
|
|
|
import typing as T |
|
|
|
|
|
|
|
|
|
from mesonbuild import mesonlib |
|
|
|
|
from mesonbuild import mlog, build |
|
|
|
|
from mesonbuild.mesonlib import MesonException |
|
|
|
|
from . import ModuleReturnValue, ModuleInfo |
|
|
|
|
from . import ExtensionModule |
|
|
|
|
from . import ExtensionModule, ModuleReturnValue, ModuleInfo |
|
|
|
|
from .. import build, mesonlib, mlog |
|
|
|
|
from ..build import CustomTarget, CustomTargetIndex |
|
|
|
|
from ..dependencies import Dependency, InternalDependency |
|
|
|
|
from ..interpreterbase import ( |
|
|
|
|
InvalidArguments, noPosargs, noKwargs, typed_kwargs, FeatureDeprecated, |
|
|
|
@ -30,10 +28,34 @@ from ..interpreterbase import ( |
|
|
|
|
) |
|
|
|
|
from ..interpreter.interpreterobjects import _CustomTargetHolder |
|
|
|
|
from ..interpreter.type_checking import NoneType |
|
|
|
|
from ..mesonlib import File, MesonException |
|
|
|
|
from ..programs import ExternalProgram |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_list(value): |
|
|
|
|
if T.TYPE_CHECKING: |
|
|
|
|
from typing_extensions import TypedDict |
|
|
|
|
|
|
|
|
|
from . import ModuleState |
|
|
|
|
from ..environment import Environment |
|
|
|
|
from ..interpreter import Interpreter |
|
|
|
|
from ..interpreterbase import TYPE_kwargs, TYPE_var |
|
|
|
|
|
|
|
|
|
_T = T.TypeVar('_T') |
|
|
|
|
|
|
|
|
|
class GenerateDocKwargs(TypedDict): |
|
|
|
|
sitemap: T.Union[str, File, CustomTarget, CustomTargetIndex] |
|
|
|
|
index: T.Union[str, File, CustomTarget, CustomTargetIndex] |
|
|
|
|
project_version: str |
|
|
|
|
html_extra_theme: T.Optional[str] |
|
|
|
|
include_paths: T.List[str] |
|
|
|
|
dependencies: T.List[T.Union[Dependency, build.StaticLibrary, build.SharedLibrary, CustomTarget, CustomTargetIndex]] |
|
|
|
|
depends: T.List[T.Union[CustomTarget, CustomTargetIndex]] |
|
|
|
|
gi_c_source_roots: T.List[str] |
|
|
|
|
extra_assets: T.List[str] |
|
|
|
|
extra_extension_paths: T.List[str] |
|
|
|
|
subprojects: T.List['HotdocTarget'] |
|
|
|
|
install: bool |
|
|
|
|
|
|
|
|
|
def ensure_list(value: T.Union[_T, T.List[_T]]) -> T.List[_T]: |
|
|
|
|
if not isinstance(value, list): |
|
|
|
|
return [value] |
|
|
|
|
return value |
|
|
|
@ -41,7 +63,7 @@ def ensure_list(value): |
|
|
|
|
|
|
|
|
|
MIN_HOTDOC_VERSION = '0.8.100' |
|
|
|
|
|
|
|
|
|
file_types = (str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex) |
|
|
|
|
file_types = (str, File, CustomTarget, CustomTargetIndex) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HotdocExternalProgram(ExternalProgram): |
|
|
|
@ -51,29 +73,29 @@ class HotdocExternalProgram(ExternalProgram): |
|
|
|
|
|
|
|
|
|
class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
def __init__(self, name, state, hotdoc, interpreter, kwargs): |
|
|
|
|
def __init__(self, name: str, state: ModuleState, hotdoc: HotdocExternalProgram, interpreter: Interpreter, kwargs): |
|
|
|
|
self.hotdoc = hotdoc |
|
|
|
|
self.build_by_default = kwargs.pop('build_by_default', False) |
|
|
|
|
self.kwargs = kwargs |
|
|
|
|
self.name = name |
|
|
|
|
self.state = state |
|
|
|
|
self.interpreter = interpreter |
|
|
|
|
self.include_paths = mesonlib.OrderedSet() |
|
|
|
|
self.include_paths: mesonlib.OrderedSet[str] = mesonlib.OrderedSet() |
|
|
|
|
|
|
|
|
|
self.builddir = state.environment.get_build_dir() |
|
|
|
|
self.sourcedir = state.environment.get_source_dir() |
|
|
|
|
self.subdir = state.subdir |
|
|
|
|
self.build_command = state.environment.get_build_command() |
|
|
|
|
|
|
|
|
|
self.cmd = ['conf', '--project-name', name, "--disable-incremental-build", |
|
|
|
|
'--output', os.path.join(self.builddir, self.subdir, self.name + '-doc')] |
|
|
|
|
self.cmd: T.List[TYPE_var] = ['conf', '--project-name', name, "--disable-incremental-build", |
|
|
|
|
'--output', os.path.join(self.builddir, self.subdir, self.name + '-doc')] |
|
|
|
|
|
|
|
|
|
self._extra_extension_paths = set() |
|
|
|
|
self.extra_assets = set() |
|
|
|
|
self.extra_depends = [] |
|
|
|
|
self._subprojects = [] |
|
|
|
|
|
|
|
|
|
def process_known_arg(self, option, argname=None, value_processor=None): |
|
|
|
|
def process_known_arg(self, option: str, argname: T.Optional[str] = None, value_processor: T.Optional[T.Callable] = None) -> None: |
|
|
|
|
if not argname: |
|
|
|
|
argname = option.strip("-").replace("-", "_") |
|
|
|
|
|
|
|
|
@ -83,7 +105,7 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
self.set_arg_value(option, value) |
|
|
|
|
|
|
|
|
|
def set_arg_value(self, option, value): |
|
|
|
|
def set_arg_value(self, option: str, value: TYPE_var) -> None: |
|
|
|
|
if value is None: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
@ -116,18 +138,18 @@ class HotdocTargetBuilder: |
|
|
|
|
else: |
|
|
|
|
self.cmd.extend([option, value]) |
|
|
|
|
|
|
|
|
|
def check_extra_arg_type(self, arg, value): |
|
|
|
|
def check_extra_arg_type(self, arg: str, value: TYPE_var) -> None: |
|
|
|
|
if isinstance(value, list): |
|
|
|
|
for v in value: |
|
|
|
|
self.check_extra_arg_type(arg, v) |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
valid_types = (str, bool, mesonlib.File, build.IncludeDirs, build.CustomTarget, build.CustomTargetIndex, build.BuildTarget) |
|
|
|
|
valid_types = (str, bool, File, build.IncludeDirs, CustomTarget, CustomTargetIndex, build.BuildTarget) |
|
|
|
|
if not isinstance(value, valid_types): |
|
|
|
|
raise InvalidArguments('Argument "{}={}" should be of type: {}.'.format( |
|
|
|
|
arg, value, [t.__name__ for t in valid_types])) |
|
|
|
|
|
|
|
|
|
def process_extra_args(self): |
|
|
|
|
def process_extra_args(self) -> None: |
|
|
|
|
for arg, value in self.kwargs.items(): |
|
|
|
|
option = "--" + arg.replace("_", "-") |
|
|
|
|
self.check_extra_arg_type(arg, value) |
|
|
|
@ -158,7 +180,7 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
return None, None |
|
|
|
|
|
|
|
|
|
def add_extension_paths(self, paths): |
|
|
|
|
def add_extension_paths(self, paths: T.Union[T.List[str], T.Set[str]]) -> None: |
|
|
|
|
for path in paths: |
|
|
|
|
if path in self._extra_extension_paths: |
|
|
|
|
continue |
|
|
|
@ -166,10 +188,10 @@ class HotdocTargetBuilder: |
|
|
|
|
self._extra_extension_paths.add(path) |
|
|
|
|
self.cmd.extend(["--extra-extension-path", path]) |
|
|
|
|
|
|
|
|
|
def replace_dirs_in_string(self, string): |
|
|
|
|
def replace_dirs_in_string(self, string: str) -> str: |
|
|
|
|
return string.replace("@SOURCE_ROOT@", self.sourcedir).replace("@BUILD_ROOT@", self.builddir) |
|
|
|
|
|
|
|
|
|
def process_gi_c_source_roots(self): |
|
|
|
|
def process_gi_c_source_roots(self) -> None: |
|
|
|
|
if self.hotdoc.run_hotdoc(['--has-extension=gi-extension']) != 0: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
@ -181,7 +203,7 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
self.cmd += ['--gi-c-source-roots'] + value |
|
|
|
|
|
|
|
|
|
def process_dependencies(self, deps): |
|
|
|
|
def process_dependencies(self, deps: T.List[T.Union[Dependency, build.StaticLibrary, build.SharedLibrary, CustomTarget, CustomTargetIndex]]) -> T.List[str]: |
|
|
|
|
cflags = set() |
|
|
|
|
for dep in mesonlib.listify(ensure_list(deps)): |
|
|
|
|
if isinstance(dep, InternalDependency): |
|
|
|
@ -205,29 +227,29 @@ class HotdocTargetBuilder: |
|
|
|
|
self.include_paths.add(os.path.join(self.builddir, dep.hotdoc_conf.subdir)) |
|
|
|
|
self.cmd += ['--extra-assets=' + p for p in dep.extra_assets] |
|
|
|
|
self.add_extension_paths(dep.extra_extension_paths) |
|
|
|
|
elif isinstance(dep, (build.CustomTarget, build.BuildTarget)): |
|
|
|
|
elif isinstance(dep, (CustomTarget, build.BuildTarget)): |
|
|
|
|
self.extra_depends.append(dep) |
|
|
|
|
elif isinstance(dep, build.CustomTargetIndex): |
|
|
|
|
elif isinstance(dep, CustomTargetIndex): |
|
|
|
|
self.extra_depends.append(dep.target) |
|
|
|
|
|
|
|
|
|
return [f.strip('-I') for f in cflags] |
|
|
|
|
|
|
|
|
|
def process_extra_assets(self): |
|
|
|
|
def process_extra_assets(self) -> None: |
|
|
|
|
self._extra_assets = self.kwargs.pop('extra_assets') |
|
|
|
|
|
|
|
|
|
for assets_path in self._extra_assets: |
|
|
|
|
self.cmd.extend(["--extra-assets", assets_path]) |
|
|
|
|
|
|
|
|
|
def process_subprojects(self): |
|
|
|
|
def process_subprojects(self) -> None: |
|
|
|
|
value = self.kwargs.pop('subprojects') |
|
|
|
|
|
|
|
|
|
self.process_dependencies(value) |
|
|
|
|
self._subprojects.extend(value) |
|
|
|
|
|
|
|
|
|
def flatten_config_command(self): |
|
|
|
|
def flatten_config_command(self) -> T.List[str]: |
|
|
|
|
cmd = [] |
|
|
|
|
for arg in mesonlib.listify(self.cmd, flatten=True): |
|
|
|
|
if isinstance(arg, mesonlib.File): |
|
|
|
|
if isinstance(arg, File): |
|
|
|
|
arg = arg.absolute_path(self.state.environment.get_source_dir(), |
|
|
|
|
self.state.environment.get_build_dir()) |
|
|
|
|
elif isinstance(arg, build.IncludeDirs): |
|
|
|
@ -236,10 +258,10 @@ class HotdocTargetBuilder: |
|
|
|
|
cmd.append(os.path.join(self.builddir, arg.get_curdir(), inc_dir)) |
|
|
|
|
|
|
|
|
|
continue |
|
|
|
|
elif isinstance(arg, (build.BuildTarget, build.CustomTarget)): |
|
|
|
|
elif isinstance(arg, (build.BuildTarget, CustomTarget)): |
|
|
|
|
self.extra_depends.append(arg) |
|
|
|
|
arg = self.interpreter.backend.get_target_filename_abs(arg) |
|
|
|
|
elif isinstance(arg, build.CustomTargetIndex): |
|
|
|
|
elif isinstance(arg, CustomTargetIndex): |
|
|
|
|
self.extra_depends.append(arg.target) |
|
|
|
|
arg = self.interpreter.backend.get_target_filename_abs(arg) |
|
|
|
|
|
|
|
|
@ -247,7 +269,7 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
return cmd |
|
|
|
|
|
|
|
|
|
def generate_hotdoc_config(self): |
|
|
|
|
def generate_hotdoc_config(self) -> None: |
|
|
|
|
cwd = os.path.abspath(os.curdir) |
|
|
|
|
ncwd = os.path.join(self.sourcedir, self.subdir) |
|
|
|
|
mlog.log('Generating Hotdoc configuration for: ', mlog.bold(self.name)) |
|
|
|
@ -256,7 +278,7 @@ class HotdocTargetBuilder: |
|
|
|
|
raise MesonException('hotdoc failed to configure') |
|
|
|
|
os.chdir(cwd) |
|
|
|
|
|
|
|
|
|
def ensure_file(self, value): |
|
|
|
|
def ensure_file(self, value: T.Union[str, File, CustomTarget, CustomTargetIndex]) -> T.Union[File, CustomTarget, CustomTargetIndex]: |
|
|
|
|
if isinstance(value, list): |
|
|
|
|
res = [] |
|
|
|
|
for val in value: |
|
|
|
@ -264,11 +286,11 @@ class HotdocTargetBuilder: |
|
|
|
|
return res |
|
|
|
|
|
|
|
|
|
if isinstance(value, str): |
|
|
|
|
return mesonlib.File.from_source_file(self.sourcedir, self.subdir, value) |
|
|
|
|
return File.from_source_file(self.sourcedir, self.subdir, value) |
|
|
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
|
|
def ensure_dir(self, value): |
|
|
|
|
def ensure_dir(self, value: str) -> str: |
|
|
|
|
if os.path.isabs(value): |
|
|
|
|
_dir = value |
|
|
|
|
else: |
|
|
|
@ -279,12 +301,12 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
return os.path.relpath(_dir, os.path.join(self.builddir, self.subdir)) |
|
|
|
|
|
|
|
|
|
def check_forbidden_args(self): |
|
|
|
|
def check_forbidden_args(self) -> None: |
|
|
|
|
for arg in ['conf_file']: |
|
|
|
|
if arg in self.kwargs: |
|
|
|
|
raise InvalidArguments(f'Argument "{arg}" is forbidden.') |
|
|
|
|
|
|
|
|
|
def make_targets(self): |
|
|
|
|
def make_targets(self) -> T.Tuple[HotdocTarget, mesonlib.ExecutableSerialisation]: |
|
|
|
|
self.check_forbidden_args() |
|
|
|
|
self.process_known_arg("--index", value_processor=self.ensure_file) |
|
|
|
|
self.process_known_arg("--project-version") |
|
|
|
@ -330,7 +352,7 @@ class HotdocTargetBuilder: |
|
|
|
|
subdir=self.subdir, |
|
|
|
|
subproject=self.state.subproject, |
|
|
|
|
environment=self.state.environment, |
|
|
|
|
hotdoc_conf=mesonlib.File.from_built_file( |
|
|
|
|
hotdoc_conf=File.from_built_file( |
|
|
|
|
self.subdir, hotdoc_config_name), |
|
|
|
|
extra_extension_paths=self._extra_extension_paths, |
|
|
|
|
extra_assets=self._extra_assets, |
|
|
|
@ -370,28 +392,29 @@ class HotdocTargetBuilder: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HotdocTargetHolder(_CustomTargetHolder['HotdocTarget']): |
|
|
|
|
def __init__(self, target: HotdocTarget, interp): |
|
|
|
|
def __init__(self, target: HotdocTarget, interp: Interpreter): |
|
|
|
|
super().__init__(target, interp) |
|
|
|
|
self.methods.update({'config_path': self.config_path_method}) |
|
|
|
|
|
|
|
|
|
@noPosargs |
|
|
|
|
@noKwargs |
|
|
|
|
def config_path_method(self, *args, **kwargs): |
|
|
|
|
def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: |
|
|
|
|
conf = self.held_object.hotdoc_conf.absolute_path(self.interpreter.environment.source_dir, |
|
|
|
|
self.interpreter.environment.build_dir) |
|
|
|
|
return conf |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HotdocTarget(build.CustomTarget): |
|
|
|
|
def __init__(self, name, subdir, subproject, hotdoc_conf, extra_extension_paths, extra_assets, |
|
|
|
|
subprojects, environment, **kwargs): |
|
|
|
|
class HotdocTarget(CustomTarget): |
|
|
|
|
def __init__(self, name: str, subdir: str, subproject: str, hotdoc_conf: File, |
|
|
|
|
extra_extension_paths: T.Set[str], extra_assets: T.List[str], |
|
|
|
|
subprojects: T.List['HotdocTarget'], environment: Environment, **kwargs: T.Any): |
|
|
|
|
super().__init__(name, subdir, subproject, environment, **kwargs, absolute_paths=True) |
|
|
|
|
self.hotdoc_conf = hotdoc_conf |
|
|
|
|
self.extra_extension_paths = extra_extension_paths |
|
|
|
|
self.extra_assets = extra_assets |
|
|
|
|
self.subprojects = subprojects |
|
|
|
|
|
|
|
|
|
def __getstate__(self): |
|
|
|
|
def __getstate__(self) -> dict: |
|
|
|
|
# Make sure we do not try to pickle subprojects |
|
|
|
|
res = self.__dict__.copy() |
|
|
|
|
res['subprojects'] = [] |
|
|
|
@ -403,7 +426,7 @@ class HotDocModule(ExtensionModule): |
|
|
|
|
|
|
|
|
|
INFO = ModuleInfo('hotdoc', '0.48.0') |
|
|
|
|
|
|
|
|
|
def __init__(self, interpreter): |
|
|
|
|
def __init__(self, interpreter: Interpreter): |
|
|
|
|
super().__init__(interpreter) |
|
|
|
|
self.hotdoc = HotdocExternalProgram('hotdoc') |
|
|
|
|
if not self.hotdoc.found(): |
|
|
|
@ -419,7 +442,7 @@ class HotDocModule(ExtensionModule): |
|
|
|
|
|
|
|
|
|
@noKwargs |
|
|
|
|
@typed_pos_args('hotdoc.has_extensions', varargs=str, min_varargs=1) |
|
|
|
|
def has_extensions(self, state, args, kwargs): |
|
|
|
|
def has_extensions(self, state: ModuleState, args: T.Tuple[T.List[str]], kwargs: TYPE_kwargs) -> bool: |
|
|
|
|
return self.hotdoc.run_hotdoc([f'--has-extension={extension}' for extension in args[0]]) == 0 |
|
|
|
|
|
|
|
|
|
@typed_pos_args('hotdoc.generate_doc', str) |
|
|
|
@ -434,13 +457,13 @@ class HotDocModule(ExtensionModule): |
|
|
|
|
KwargInfo( |
|
|
|
|
'dependencies', |
|
|
|
|
ContainerTypeInfo(list, (Dependency, build.StaticLibrary, build.SharedLibrary, |
|
|
|
|
build.CustomTarget, build.CustomTargetIndex)), |
|
|
|
|
CustomTarget, CustomTargetIndex)), |
|
|
|
|
listify=True, |
|
|
|
|
default=[], |
|
|
|
|
), |
|
|
|
|
KwargInfo( |
|
|
|
|
'depends', |
|
|
|
|
ContainerTypeInfo(list, (build.CustomTarget, build.CustomTargetIndex)), |
|
|
|
|
ContainerTypeInfo(list, (CustomTarget, CustomTargetIndex)), |
|
|
|
|
listify=True, |
|
|
|
|
default=[], |
|
|
|
|
since='0.64.1', |
|
|
|
@ -452,21 +475,21 @@ class HotDocModule(ExtensionModule): |
|
|
|
|
KwargInfo('install', bool, default=False), |
|
|
|
|
allow_unknown=True |
|
|
|
|
) |
|
|
|
|
def generate_doc(self, state, args, kwargs): |
|
|
|
|
def generate_doc(self, state: ModuleState, args: T.Tuple[str], kwargs: GenerateDocKwargs) -> ModuleReturnValue: |
|
|
|
|
project_name = args[0] |
|
|
|
|
if any(isinstance(x, (build.CustomTarget, build.CustomTargetIndex)) for x in kwargs['dependencies']): |
|
|
|
|
if any(isinstance(x, (CustomTarget, CustomTargetIndex)) for x in kwargs['dependencies']): |
|
|
|
|
FeatureDeprecated.single_use('hotdoc.generate_doc dependencies argument with custom_target', |
|
|
|
|
'0.64.1', state.subproject, 'use `depends`', state.current_node) |
|
|
|
|
builder = HotdocTargetBuilder(project_name, state, self.hotdoc, self.interpreter, kwargs) |
|
|
|
|
target, install_script = builder.make_targets() |
|
|
|
|
targets = [target] |
|
|
|
|
targets: T.List[T.Union[HotdocTarget, mesonlib.ExecutableSerialisation]] = [target] |
|
|
|
|
if install_script: |
|
|
|
|
targets.append(install_script) |
|
|
|
|
|
|
|
|
|
return ModuleReturnValue(targets[0], targets) |
|
|
|
|
return ModuleReturnValue(target, targets) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def initialize(interpreter): |
|
|
|
|
def initialize(interpreter: Interpreter) -> HotDocModule: |
|
|
|
|
mod = HotDocModule(interpreter) |
|
|
|
|
mod.interpreter.append_holder_map(HotdocTarget, HotdocTargetHolder) |
|
|
|
|
return mod |
|
|
|
|