Repository navigation
Expand file tree
/
Copy pathexternal_needs.py
More file actions
111 lines (92 loc) · 3.96 KB
/
Copy pathexternal_needs.py
File metadata and controls
111 lines (92 loc) · 3.96 KB
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
102
103
104
105
106
107
108
109
110
111
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************
"""Shared parsing and path resolution for Bazel external documentation inputs."""
from dataclasses import dataclass
from pathlib import Path
@dataclass
class ExternalNeedsSource:
bazel_module: str
path_to_target: str
# `needs_json` and `needs_json_file` are the public inventory labels.
# A public `docs()` or `docs_bundle` label is resolved earlier by the Bazel
# macro to its private `.__internal__.needs_local` sibling.
target: str
# True for a same-repo mount (`//pkg:needs_json`), whose runfiles live under
# `_main/…`. False for a cross-module mount (`@repo//…:needs_json`), whose
# runfiles live under `{bazel_module}+/…`.
is_local: bool = False
def parse_bazel_external_need(s: str) -> ExternalNeedsSource | None:
"""Recognize supported Bazel inventory labels and ignore ordinary data.
``needs_json`` and ``needs_json_file`` are both accepted inventory labels.
The docs macro reports inventory labels in the deprecated ``data`` input
route before combining labels with explicit ``external_needs``.
"""
is_cross_module = s.startswith("@")
is_local = s.startswith("//")
if not is_cross_module and not is_local:
# Local need, not external needs
return None
if "//" not in s or ":" not in s:
raise ValueError(
f"Unsupported external data dependency: '{s}'. Must contain '//' & ':'"
)
repo_and_path, target = s.split(":", 1)
repo, path_to_target = repo_and_path.split("//", 1)
repo = repo.lstrip("@")
if target in ("needs_json", "needs_json_file") or target.endswith(
".__internal__.needs_local"
):
return ExternalNeedsSource(
bazel_module=repo,
path_to_target=path_to_target,
target=target,
is_local=is_local,
)
return None
def parse_external_needs_labels(labels: list[str]) -> list[ExternalNeedsSource]:
"""Parse supported external-needs labels and ignore ordinary data labels."""
return [
source
for label in labels
if (source := parse_bazel_external_need(label)) is not None
]
def _runfiles_module_dir(source: ExternalNeedsSource) -> str:
return "_main" if source.is_local else f"{source.bazel_module}+"
def external_needs_runfiles_path(
runfiles_dir: Path, source: ExternalNeedsSource, *suffix: str
) -> Path:
return (
runfiles_dir
/ _runfiles_module_dir(source)
/ source.path_to_target
/ Path(*suffix)
)
def external_needs_source_path(
runfiles_dir: Path | None, source: ExternalNeedsSource
) -> Path:
"""Find the inventory JSON emitted by the selected Bazel target.
Public `needs_json` and private bundle-local exports are directory outputs
containing `_build/needs/needs.json`. The `needs_json_file` target points
directly at the inventory file.
"""
if runfiles_dir is None:
raise ValueError("An external needs source has no runfiles root.")
if source.target == "needs_json":
suffix = (source.target, "_build", "needs", "needs.json")
elif source.target == "needs_json_file":
suffix = ("needs.json",)
elif source.target.endswith(".__internal__.needs_local"):
suffix = (source.target, "_build", "needs", "needs.json")
else:
raise ValueError(f"Unsupported external needs target: {source.target}")
return external_needs_runfiles_path(runfiles_dir, source, *suffix)