#!/usr/bin/env python # Copyright 2018 The ANGLE Project Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This tool will create a json description of the GN build environment that # can then be used by gen_angle_android_bp.py to build an Android.bp file for # the Android Soong build system. # The input to this tool is a list of GN labels for which to capture the build # information in json: # # Generating angle.json needs to be done from within a Chromium build: # cd /src # gen_angle_gn_info_json.py //third_party/angle:libGLESv2 //third_party/angle:libEGL # # This will output an angle.json that can be copied to the angle directory # within Android. # # Optional arguments: # --gn_out GN output config to use (e.g., out/Default or out/Debug.) # --output json file to create, default is angle.json # import argparse import json import logging import subprocess import sys def get_json_description(gn_out, target_name): try: text_desc = subprocess.check_output(['gn', 'desc', '--format=json', gn_out, target_name]) except subprocess.CalledProcessError as e: logging.error("e.retcode = %s" % e.returncode) logging.error("e.cmd = %s" % e.cmd) logging.error("e.output = %s" % e.output) try: json_out = json.loads(text_desc) except ValueError: raise ValueError("Unable to decode JSON\ncmd: %s\noutput:\n%s" % (subprocess.list2cmdline( ['gn', 'desc', '--format=json', gn_out, target_name]), text_desc)) return json_out def load_json_deps(desc, gn_out, target_name, all_desc, indent=" "): """Extracts dependencies from the given target json description and recursively extracts json descriptions. desc: json description for target_name that includes dependencies gn_out: GN output file with configuration info target_name: name of target in desc to lookup deps all_desc: dependent descriptions added here indent: Print with indent to show recursion depth """ target = desc[target_name] text_descriptions = [] for dep in target.get('deps', []): if dep not in all_desc: logging.debug("dep: %s%s" % (indent, dep)) new_desc = get_json_description(gn_out, dep) all_desc[dep] = new_desc[dep] load_json_deps(new_desc, gn_out, dep, all_desc, indent + " ") else: logging.debug("dup: %s%s" % (indent, dep)) def create_build_description(gn_out, targets): """Creates the JSON build description by running GN.""" logging.debug("targets = %s" % targets) json_descriptions = {} for target in targets: logging.debug("target: %s" % (target)) target_desc = get_json_description(gn_out, target) if (target in target_desc and target not in json_descriptions): json_descriptions[target] = target_desc[target] load_json_deps(target_desc, gn_out, target, json_descriptions) else: logging.debug("Invalid target: %s" % target) return json_descriptions def main(): logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) parser = argparse.ArgumentParser( description='Generate json build information from a GN description.') parser.add_argument( '--gn_out', help='GN output config to use (e.g., out/Default or out/Debug.)', default='out/Default', ) parser.add_argument( '--output', help='json file to create', default='angle.json', ) parser.add_argument( 'targets', nargs=argparse.REMAINDER, help='Targets to include in the json (e.g., "//libEGL")') args = parser.parse_args() desc = create_build_description(args.gn_out, args.targets) fh = open(args.output, "w") fh.write(json.dumps(desc, indent=4, sort_keys=True)) fh.close() print("Output written to: %s" % args.output) if __name__ == '__main__': sys.exit(main())