xref: /aosp_15_r20/external/executorch/build/print_exported_headers.py (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1#!/usr/bin/env python3
2# Copyright (c) Meta Platforms, Inc. and affiliates.
3# All rights reserved.
4#
5# This source code is licensed under the BSD-style license found in the
6# LICENSE file in the root directory of this source tree.
7
8"""
9Prints the headers that are listed as exported headers for the
10provided targets, including their exported deps recursively.
11"""
12
13import argparse
14import json
15import os
16import subprocess
17from typing import List, Set
18
19# Run buck2 from the same directory (and thus repo) as this script.
20BUCK_CWD: str = os.path.dirname(os.path.realpath(__file__))
21
22
23def run(command: List[str]) -> str:
24    """Run subprocess and return its output."""
25    result = subprocess.run(command, capture_output=True, check=True, cwd=BUCK_CWD)
26    return result.stdout.decode()
27
28
29def query(buck2: str, target: str, attribute: str) -> str:
30    """Query an attribute of a target."""
31    output = run([buck2, "cquery", target, "--output-attribute", attribute])
32
33    try:
34        output_json = json.loads(output)
35        return output_json[next(iter(output_json))][attribute]
36    except (json.JSONDecodeError, KeyError) as e:
37        print(f"Failed to parse JSON from query({target}, {attribute}): {output}")
38        raise SystemExit("Error: " + str(e))
39
40
41def exported_headers(buck2: str, target: str) -> Set[str]:
42    """Get all exported headers of a target and its dependencies."""
43    deps = query(buck2, target, "exported_deps")
44    headers = set(query(buck2, target, "exported_headers"))
45    headers.update(
46        header
47        for dep in deps
48        for header in exported_headers(buck2, dep.split()[0])
49        if header.endswith(".h")
50    )
51    return headers
52
53
54def expand_target(buck2: str, target: str) -> List[str]:
55    """Expand a target into a list of targets if applicable."""
56    output = run([buck2, "cquery", target])
57    # Buck's output format is "<target> (<build platform>)", we take only the target part.
58    targets = [line.split(" ")[0] for line in output.strip().split("\n")]
59    return targets
60
61
62def main():
63    parser = argparse.ArgumentParser()
64    parser.add_argument(
65        "--buck2", default="buck2", help="Path to the buck2 executable."
66    )
67    parser.add_argument(
68        "--targets",
69        nargs="+",
70        required=True,
71        help="Buck targets to find the headers of.",
72    )
73    args = parser.parse_args()
74
75    targets = [
76        target
77        for input_target in args.targets
78        for target in expand_target(args.buck2, input_target)
79    ]
80
81    # Use a set to remove duplicates.
82    headers = {
83        header for target in targets for header in exported_headers(args.buck2, target)
84    }
85
86    for header in sorted(headers):
87        # Strip off the leading workspace name and //.
88        print(header.split("//", 1)[-1])
89
90
91if __name__ == "__main__":
92    main()
93