-
Notifications
You must be signed in to change notification settings - Fork 492
/
Copy pathprint_public_headers.py
executable file
·131 lines (107 loc) · 4.27 KB
/
print_public_headers.py
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
"""Prints headers that are visible to executorch clients."""
import json
import os
import subprocess
from dataclasses import dataclass
from typing import Dict, List
# Run buck2 from the same directory (and thus repo) as this script.
BUCK_CWD: str = os.path.dirname(os.path.realpath(__file__))
# One of the non-executorch entries of clients.bzl
EXTERNAL_CLIENT_TARGET: str = "fbcode//pye/model_inventory/..."
# The buck query covering the targets to examine.
PROJECT_QUERY: str = "//executorch/..."
@dataclass
class BuildTarget:
"""A buck build target and a subset of its attributes."""
name: str
exported_deps: List[str]
exported_headers: List[str]
visibility: List[str]
def query_targets(query: str) -> Dict[str, BuildTarget]:
"""Returns the BuildTargets matching the query, keyed by target name."""
args: List[str] = [
"buck2",
"cquery",
query,
"--output-attribute",
"exported_deps",
"--output-attribute",
"exported_headers",
"--output-attribute",
"visibility",
]
cp: subprocess.CompletedProcess = subprocess.run(
args, capture_output=True, cwd=BUCK_CWD, check=True
)
# stdout should be a JSON object like P643366873.
targets: dict = json.loads(cp.stdout)
ret: Dict[str, BuildTarget] = {}
for name, info in targets.items():
# Target strings may have an extra " (mode//config/string)" at the end.
name = name.split(" ", 1)[0]
exported_deps = [d.split(" ", 1)[0] for d in info.get("exported_deps", [])]
ret[name] = BuildTarget(
name=name,
exported_deps=exported_deps,
exported_headers=info.get("exported_headers", []),
visibility=info.get("visibility", []),
)
return ret
def targets_exported_by(
target: BuildTarget, targets: Dict[str, BuildTarget]
) -> List[BuildTarget]:
"""Returns the targets transitively exported by `target`."""
ret: List[BuildTarget] = []
for t in target.exported_deps:
if t in targets:
ret.append(targets[t])
# Recurse. Assumes there are no circular references, since buck
# should fail if they exist.
ret.extend(targets_exported_by(targets[t], targets))
return ret
def find_visible_targets(
client_target: str, targets: Dict[str, BuildTarget]
) -> List[BuildTarget]:
"""Returns a list of targets visible to client_target.
Returned targets may be directly visible, or transitively visible via
exported_deps.
"""
visible: List[BuildTarget] = []
for target in targets.values():
if client_target in target.visibility or "PUBLIC" in target.visibility:
visible.append(target)
visible.extend(targets_exported_by(target, targets))
return visible
def index_headers(targets: List[BuildTarget]) -> Dict[str, List[BuildTarget]]:
"""Returns a mapping of header paths to the BuildTargets that export them."""
ret: Dict[str, List[BuildTarget]] = {}
for target in targets:
if isinstance(target.exported_headers, dict):
# Dict of {"HeaderName.h": "fbcode//...[HeaderName.h] (mode//config)"}
for header in target.exported_headers.values():
header = header.split(" ", 1)[0]
if header not in ret:
ret[header] = []
ret[header].append(target)
else:
# Simple list of header file paths, prefixed with "fbcode//".
assert isinstance(target.exported_headers, list)
for header in target.exported_headers:
if header not in ret:
ret[header] = []
ret[header].append(target)
return ret
def main():
all_targets = query_targets(PROJECT_QUERY)
visible_targets = find_visible_targets(EXTERNAL_CLIENT_TARGET, all_targets)
index = index_headers(visible_targets)
# The list will be build targets like `fbcode//executorch/runtime/platform/platform.h`.
print("\n".join(sorted(index.keys())))
if __name__ == "__main__":
main()