xref: /aosp_15_r20/external/executorch/build/print_public_headers.py (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1*523fa7a6SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) Meta Platforms, Inc. and affiliates.
3*523fa7a6SAndroid Build Coastguard Worker# All rights reserved.
4*523fa7a6SAndroid Build Coastguard Worker#
5*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the
6*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree.
7*523fa7a6SAndroid Build Coastguard Worker
8*523fa7a6SAndroid Build Coastguard Worker"""Prints headers that are visible to executorch clients."""
9*523fa7a6SAndroid Build Coastguard Worker
10*523fa7a6SAndroid Build Coastguard Workerimport json
11*523fa7a6SAndroid Build Coastguard Workerimport os
12*523fa7a6SAndroid Build Coastguard Workerimport subprocess
13*523fa7a6SAndroid Build Coastguard Worker
14*523fa7a6SAndroid Build Coastguard Workerfrom dataclasses import dataclass
15*523fa7a6SAndroid Build Coastguard Workerfrom typing import Dict, List
16*523fa7a6SAndroid Build Coastguard Worker
17*523fa7a6SAndroid Build Coastguard Worker
18*523fa7a6SAndroid Build Coastguard Worker# Run buck2 from the same directory (and thus repo) as this script.
19*523fa7a6SAndroid Build Coastguard WorkerBUCK_CWD: str = os.path.dirname(os.path.realpath(__file__))
20*523fa7a6SAndroid Build Coastguard Worker
21*523fa7a6SAndroid Build Coastguard Worker# One of the non-executorch entries of clients.bzl
22*523fa7a6SAndroid Build Coastguard WorkerEXTERNAL_CLIENT_TARGET: str = "fbcode//pye/model_inventory/..."
23*523fa7a6SAndroid Build Coastguard Worker
24*523fa7a6SAndroid Build Coastguard Worker# The buck query covering the targets to examine.
25*523fa7a6SAndroid Build Coastguard WorkerPROJECT_QUERY: str = "//executorch/..."
26*523fa7a6SAndroid Build Coastguard Worker
27*523fa7a6SAndroid Build Coastguard Worker
28*523fa7a6SAndroid Build Coastguard Worker@dataclass
29*523fa7a6SAndroid Build Coastguard Workerclass BuildTarget:
30*523fa7a6SAndroid Build Coastguard Worker    """A buck build target and a subset of its attributes."""
31*523fa7a6SAndroid Build Coastguard Worker
32*523fa7a6SAndroid Build Coastguard Worker    name: str
33*523fa7a6SAndroid Build Coastguard Worker    exported_deps: List[str]
34*523fa7a6SAndroid Build Coastguard Worker    exported_headers: List[str]
35*523fa7a6SAndroid Build Coastguard Worker    visibility: List[str]
36*523fa7a6SAndroid Build Coastguard Worker
37*523fa7a6SAndroid Build Coastguard Worker
38*523fa7a6SAndroid Build Coastguard Workerdef query_targets(query: str) -> Dict[str, BuildTarget]:
39*523fa7a6SAndroid Build Coastguard Worker    """Returns the BuildTargets matching the query, keyed by target name."""
40*523fa7a6SAndroid Build Coastguard Worker    args: List[str] = [
41*523fa7a6SAndroid Build Coastguard Worker        "buck2",
42*523fa7a6SAndroid Build Coastguard Worker        "cquery",
43*523fa7a6SAndroid Build Coastguard Worker        query,
44*523fa7a6SAndroid Build Coastguard Worker        "--output-attribute",
45*523fa7a6SAndroid Build Coastguard Worker        "exported_deps",
46*523fa7a6SAndroid Build Coastguard Worker        "--output-attribute",
47*523fa7a6SAndroid Build Coastguard Worker        "exported_headers",
48*523fa7a6SAndroid Build Coastguard Worker        "--output-attribute",
49*523fa7a6SAndroid Build Coastguard Worker        "visibility",
50*523fa7a6SAndroid Build Coastguard Worker    ]
51*523fa7a6SAndroid Build Coastguard Worker    cp: subprocess.CompletedProcess = subprocess.run(
52*523fa7a6SAndroid Build Coastguard Worker        args, capture_output=True, cwd=BUCK_CWD, check=True
53*523fa7a6SAndroid Build Coastguard Worker    )
54*523fa7a6SAndroid Build Coastguard Worker    # stdout should be a JSON object like P643366873.
55*523fa7a6SAndroid Build Coastguard Worker    targets: dict = json.loads(cp.stdout)
56*523fa7a6SAndroid Build Coastguard Worker
57*523fa7a6SAndroid Build Coastguard Worker    ret: Dict[str, BuildTarget] = {}
58*523fa7a6SAndroid Build Coastguard Worker    for name, info in targets.items():
59*523fa7a6SAndroid Build Coastguard Worker        # Target strings may have an extra " (mode//config/string)" at the end.
60*523fa7a6SAndroid Build Coastguard Worker        name = name.split(" ", 1)[0]
61*523fa7a6SAndroid Build Coastguard Worker        exported_deps = [d.split(" ", 1)[0] for d in info.get("exported_deps", [])]
62*523fa7a6SAndroid Build Coastguard Worker        ret[name] = BuildTarget(
63*523fa7a6SAndroid Build Coastguard Worker            name=name,
64*523fa7a6SAndroid Build Coastguard Worker            exported_deps=exported_deps,
65*523fa7a6SAndroid Build Coastguard Worker            exported_headers=info.get("exported_headers", []),
66*523fa7a6SAndroid Build Coastguard Worker            visibility=info.get("visibility", []),
67*523fa7a6SAndroid Build Coastguard Worker        )
68*523fa7a6SAndroid Build Coastguard Worker    return ret
69*523fa7a6SAndroid Build Coastguard Worker
70*523fa7a6SAndroid Build Coastguard Worker
71*523fa7a6SAndroid Build Coastguard Workerdef targets_exported_by(
72*523fa7a6SAndroid Build Coastguard Worker    target: BuildTarget, targets: Dict[str, BuildTarget]
73*523fa7a6SAndroid Build Coastguard Worker) -> List[BuildTarget]:
74*523fa7a6SAndroid Build Coastguard Worker    """Returns the targets transitively exported by `target`."""
75*523fa7a6SAndroid Build Coastguard Worker    ret: List[BuildTarget] = []
76*523fa7a6SAndroid Build Coastguard Worker    for t in target.exported_deps:
77*523fa7a6SAndroid Build Coastguard Worker        if t in targets:
78*523fa7a6SAndroid Build Coastguard Worker            ret.append(targets[t])
79*523fa7a6SAndroid Build Coastguard Worker            # Recurse. Assumes there are no circular references, since buck
80*523fa7a6SAndroid Build Coastguard Worker            # should fail if they exist.
81*523fa7a6SAndroid Build Coastguard Worker            ret.extend(targets_exported_by(targets[t], targets))
82*523fa7a6SAndroid Build Coastguard Worker    return ret
83*523fa7a6SAndroid Build Coastguard Worker
84*523fa7a6SAndroid Build Coastguard Worker
85*523fa7a6SAndroid Build Coastguard Workerdef find_visible_targets(
86*523fa7a6SAndroid Build Coastguard Worker    client_target: str, targets: Dict[str, BuildTarget]
87*523fa7a6SAndroid Build Coastguard Worker) -> List[BuildTarget]:
88*523fa7a6SAndroid Build Coastguard Worker    """Returns a list of targets visible to client_target.
89*523fa7a6SAndroid Build Coastguard Worker
90*523fa7a6SAndroid Build Coastguard Worker    Returned targets may be directly visible, or transitively visible via
91*523fa7a6SAndroid Build Coastguard Worker    exported_deps.
92*523fa7a6SAndroid Build Coastguard Worker    """
93*523fa7a6SAndroid Build Coastguard Worker    visible: List[BuildTarget] = []
94*523fa7a6SAndroid Build Coastguard Worker    for target in targets.values():
95*523fa7a6SAndroid Build Coastguard Worker        if client_target in target.visibility or "PUBLIC" in target.visibility:
96*523fa7a6SAndroid Build Coastguard Worker            visible.append(target)
97*523fa7a6SAndroid Build Coastguard Worker            visible.extend(targets_exported_by(target, targets))
98*523fa7a6SAndroid Build Coastguard Worker    return visible
99*523fa7a6SAndroid Build Coastguard Worker
100*523fa7a6SAndroid Build Coastguard Worker
101*523fa7a6SAndroid Build Coastguard Workerdef index_headers(targets: List[BuildTarget]) -> Dict[str, List[BuildTarget]]:
102*523fa7a6SAndroid Build Coastguard Worker    """Returns a mapping of header paths to the BuildTargets that export them."""
103*523fa7a6SAndroid Build Coastguard Worker    ret: Dict[str, List[BuildTarget]] = {}
104*523fa7a6SAndroid Build Coastguard Worker    for target in targets:
105*523fa7a6SAndroid Build Coastguard Worker        if isinstance(target.exported_headers, dict):
106*523fa7a6SAndroid Build Coastguard Worker            # Dict of {"HeaderName.h": "fbcode//...[HeaderName.h] (mode//config)"}
107*523fa7a6SAndroid Build Coastguard Worker            for header in target.exported_headers.values():
108*523fa7a6SAndroid Build Coastguard Worker                header = header.split(" ", 1)[0]
109*523fa7a6SAndroid Build Coastguard Worker                if header not in ret:
110*523fa7a6SAndroid Build Coastguard Worker                    ret[header] = []
111*523fa7a6SAndroid Build Coastguard Worker                ret[header].append(target)
112*523fa7a6SAndroid Build Coastguard Worker        else:
113*523fa7a6SAndroid Build Coastguard Worker            # Simple list of header file paths, prefixed with "fbcode//".
114*523fa7a6SAndroid Build Coastguard Worker            assert isinstance(target.exported_headers, list)
115*523fa7a6SAndroid Build Coastguard Worker            for header in target.exported_headers:
116*523fa7a6SAndroid Build Coastguard Worker                if header not in ret:
117*523fa7a6SAndroid Build Coastguard Worker                    ret[header] = []
118*523fa7a6SAndroid Build Coastguard Worker                ret[header].append(target)
119*523fa7a6SAndroid Build Coastguard Worker    return ret
120*523fa7a6SAndroid Build Coastguard Worker
121*523fa7a6SAndroid Build Coastguard Worker
122*523fa7a6SAndroid Build Coastguard Workerdef main():
123*523fa7a6SAndroid Build Coastguard Worker    all_targets = query_targets(PROJECT_QUERY)
124*523fa7a6SAndroid Build Coastguard Worker    visible_targets = find_visible_targets(EXTERNAL_CLIENT_TARGET, all_targets)
125*523fa7a6SAndroid Build Coastguard Worker    index = index_headers(visible_targets)
126*523fa7a6SAndroid Build Coastguard Worker    # The list will be build targets like `fbcode//executorch/runtime/platform/platform.h`.
127*523fa7a6SAndroid Build Coastguard Worker    print("\n".join(sorted(index.keys())))
128*523fa7a6SAndroid Build Coastguard Worker
129*523fa7a6SAndroid Build Coastguard Worker
130*523fa7a6SAndroid Build Coastguard Workerif __name__ == "__main__":
131*523fa7a6SAndroid Build Coastguard Worker    main()
132