1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
# SPDX-License-Identifier: Apache-2.0
# Copyright 2024 The Meson development team
from __future__ import annotations
from collections import defaultdict
import os
import tempfile
import typing as T
from .run_tool import run_tool_on_targets, run_with_buffered_output
from .. import build, mlog
from ..mesonlib import MachineChoice, PerMachine
from ..wrap import WrapMode, wrap
if T.TYPE_CHECKING:
from ..compilers.rust import RustCompiler
async def run_and_confirm_success(cmdlist: T.List[str], crate: str) -> int:
returncode = await run_with_buffered_output(cmdlist)
if returncode == 0:
print(mlog.green('Generated'), os.path.join('doc', crate))
return returncode
class Rustdoc:
def __init__(self, build: build.Build, tempdir: str, subprojects: T.Set[str]) -> None:
self.tools: PerMachine[T.List[str]] = PerMachine([], [])
self.warned: T.DefaultDict[str, bool] = defaultdict(lambda: False)
self.tempdir = tempdir
self.subprojects = subprojects
for machine in MachineChoice:
compilers = build.environment.coredata.compilers[machine]
if 'rust' in compilers:
compiler = T.cast('RustCompiler', compilers['rust'])
self.tools[machine] = compiler.get_rust_tool('rustdoc', build.environment)
def warn_missing_rustdoc(self, machine: str) -> None:
if self.warned[machine]:
return
mlog.warning(f'rustdoc not found for {machine} machine')
self.warned[machine] = True
def __call__(self, target: T.Dict[str, T.Any]) -> T.Iterable[T.Coroutine[None, None, int]]:
if target['subproject'] is not None and target['subproject'] not in self.subprojects:
return
for src_block in target['target_sources']:
if 'compiler' in src_block and src_block['language'] == 'rust':
rustdoc = getattr(self.tools, src_block['machine'])
if not rustdoc:
self.warn_missing_rustdoc(src_block['machine'])
continue
cmdlist = list(rustdoc)
prev = None
crate_name = None
is_test = False
for arg in src_block['parameters']:
if prev:
if prev == '--crate-name':
cmdlist.extend((prev, arg))
crate_name = arg
prev = None
continue
if arg == '--test':
is_test = True
break
elif arg in {'--crate-name', '--emit', '--out-dir', '-l'}:
prev = arg
elif arg != '-g' and not arg.startswith('-l'):
cmdlist.append(arg)
if is_test:
# --test has a completely different meaning for rustc and rustdoc;
# when using rust.test(), only the non-test target is documented
continue
if crate_name:
cmdlist.extend(src_block['sources'])
# Assume documentation is generated for the developer's use
cmdlist.append('--document-private-items')
cmdlist.append('-o')
cmdlist.append('doc')
yield run_and_confirm_success(cmdlist, crate_name)
else:
print(mlog.yellow('Skipping'), target['name'], '(no crate name)')
def get_nonwrap_subprojects(build_data: build.Build) -> T.Set[str]:
wrap_resolver = wrap.Resolver(
build_data.environment.get_source_dir(),
build_data.subproject_dir,
wrap_mode=WrapMode.nodownload)
return set(sp
for sp in build_data.environment.coredata.initialized_subprojects
if sp and (sp not in wrap_resolver.wraps or wrap_resolver.wraps[sp].type is None))
def run(args: T.List[str]) -> int:
os.chdir(args[0])
build_data = build.load(os.getcwd())
subproject_list = get_nonwrap_subprojects(build_data)
with tempfile.TemporaryDirectory() as d:
return run_tool_on_targets(Rustdoc(build_data, d, subproject_list))
|