xref: /aosp_15_r20/external/angle/scripts/gen_angle_gn_info_json.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/env python
2
3#  Copyright 2018 The ANGLE Project Authors. All rights reserved.
4#  Use of this source code is governed by a BSD-style license that can be
5#  found in the LICENSE file.
6
7# This tool will create a json description of the GN build environment that
8# can then be used by gen_angle_android_bp.py to build an Android.bp file for
9# the Android Soong build system.
10# The input to this tool is a list of GN labels for which to capture the build
11# information in json:
12#
13# Generating angle.json needs to be done from within a Chromium build:
14#   cd <chromium>/src
15#   gen_angle_gn_info_json.py //third_party/angle:libGLESv2 //third_party/angle:libEGL
16#
17# This will output an angle.json that can be copied to the angle directory
18# within Android.
19#
20# Optional arguments:
21#  --gn_out <file>  GN output config to use (e.g., out/Default or out/Debug.)
22#  --output <file>  json file to create, default is angle.json
23#
24
25import argparse
26import json
27import logging
28import subprocess
29import sys
30
31
32def get_json_description(gn_out, target_name):
33    try:
34        text_desc = subprocess.check_output(['gn', 'desc', '--format=json', gn_out, target_name])
35    except subprocess.CalledProcessError as e:
36        logging.error("e.retcode = %s" % e.returncode)
37        logging.error("e.cmd = %s" % e.cmd)
38        logging.error("e.output = %s" % e.output)
39    try:
40        json_out = json.loads(text_desc)
41    except ValueError:
42        raise ValueError("Unable to decode JSON\ncmd: %s\noutput:\n%s" % (subprocess.list2cmdline(
43            ['gn', 'desc', '--format=json', gn_out, target_name]), text_desc))
44
45    return json_out
46
47
48def load_json_deps(desc, gn_out, target_name, all_desc, indent="  "):
49    """Extracts dependencies from the given target json description
50       and recursively extracts json descriptions.
51
52       desc: json description for target_name that includes dependencies
53       gn_out: GN output file with configuration info
54       target_name: name of target in desc to lookup deps
55       all_desc: dependent descriptions added here
56       indent: Print with indent to show recursion depth
57    """
58    target = desc[target_name]
59    text_descriptions = []
60    for dep in target.get('deps', []):
61        if dep not in all_desc:
62            logging.debug("dep: %s%s" % (indent, dep))
63            new_desc = get_json_description(gn_out, dep)
64            all_desc[dep] = new_desc[dep]
65            load_json_deps(new_desc, gn_out, dep, all_desc, indent + "  ")
66        else:
67            logging.debug("dup: %s%s" % (indent, dep))
68
69
70def create_build_description(gn_out, targets):
71    """Creates the JSON build description by running GN."""
72
73    logging.debug("targets = %s" % targets)
74    json_descriptions = {}
75    for target in targets:
76        logging.debug("target: %s" % (target))
77        target_desc = get_json_description(gn_out, target)
78        if (target in target_desc and target not in json_descriptions):
79            json_descriptions[target] = target_desc[target]
80            load_json_deps(target_desc, gn_out, target, json_descriptions)
81        else:
82            logging.debug("Invalid target: %s" % target)
83    return json_descriptions
84
85
86def main():
87    logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
88    parser = argparse.ArgumentParser(
89        description='Generate json build information from a GN description.')
90    parser.add_argument(
91        '--gn_out',
92        help='GN output config to use (e.g., out/Default or out/Debug.)',
93        default='out/Default',
94    )
95    parser.add_argument(
96        '--output',
97        help='json file to create',
98        default='angle.json',
99    )
100    parser.add_argument(
101        'targets',
102        nargs=argparse.REMAINDER,
103        help='Targets to include in the json (e.g., "//libEGL")')
104    args = parser.parse_args()
105
106    desc = create_build_description(args.gn_out, args.targets)
107    fh = open(args.output, "w")
108    fh.write(json.dumps(desc, indent=4, sort_keys=True))
109    fh.close()
110
111    print("Output written to: %s" % args.output)
112
113
114if __name__ == '__main__':
115    sys.exit(main())
116