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