summaryrefslogtreecommitdiff
path: root/mesonbuild/scripts/rustdoc.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/scripts/rustdoc.py')
-rw-r--r--mesonbuild/scripts/rustdoc.py101
1 files changed, 101 insertions, 0 deletions
diff --git a/mesonbuild/scripts/rustdoc.py b/mesonbuild/scripts/rustdoc.py
new file mode 100644
index 000000000..f5f74c4e3
--- /dev/null
+++ b/mesonbuild/scripts/rustdoc.py
@@ -0,0 +1,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))