| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Produces a JSON object of `gn desc`'s output for each given arch. |
| |
| A full Chromium checkout is required in order to run this script. |
| |
| The result is of the form: |
| { |
| "arch1": { |
| "//gn:target": { |
| 'configs": ["bar"], |
| "sources": ["foo"] |
| } |
| } |
| } |
| """ |
| |
| from __future__ import print_function |
| |
| import argparse |
| import json |
| # pylint: disable=cros-logging-import |
| import logging |
| import os |
| import subprocess |
| import sys |
| import tempfile |
| |
| |
| def _create_gn_args_for(arch): |
| """Creates a `gn args` listing for the given architecture.""" |
| # FIXME(gbiv): is_chromeos_device = True would be nice to support, as well. |
| # Requires playing nicely with SimpleChrome though, and this should be "close |
| # enough" for now. |
| return '\n'.join(( |
| 'target_os = "chromeos"', |
| 'target_cpu = "%s"' % arch, |
| 'is_official_build = true', |
| 'is_chrome_branded = true', |
| )) |
| |
| |
| def _parse_gn_desc_output(output): |
| """Parses the output of `gn desc --format=json`. |
| |
| Args: |
| output: a seekable file containing the JSON output of `gn desc`. |
| |
| Returns: |
| A tuple of (warnings, gn_desc_json). |
| """ |
| warnings = [] |
| desc_json = None |
| while True: |
| start_pos = output.tell() |
| next_line = next(output, None) |
| if next_line is None: |
| raise ValueError('No JSON found in the given gn file') |
| |
| if next_line.lstrip().startswith('{'): |
| output.seek(start_pos) |
| desc_json = json.load(output) |
| break |
| |
| warnings.append(next_line) |
| |
| return ''.join(warnings).strip(), desc_json |
| |
| |
| def _run_gn_desc(in_dir, gn_args): |
| logging.info('Running `gn gen`...') |
| subprocess.check_call(['gn', 'gen', '.', '--args=' + gn_args], cwd=in_dir) |
| |
| logging.info('Running `gn desc`...') |
| with tempfile.TemporaryFile(mode='r+', encoding='utf-8') as f: |
| gn_command = ['gn', 'desc', '--format=json', '.', '//*:*'] |
| exit_code = subprocess.call(gn_command, stdout=f, cwd=in_dir) |
| f.seek(0) |
| if exit_code: |
| logging.error('gn failed; stdout:\n%s', f.read()) |
| raise subprocess.CalledProcessError(exit_code, gn_command) |
| warnings, result = _parse_gn_desc_output(f) |
| |
| if warnings: |
| logging.warning('Encountered warning(s) running `gn desc`:\n%s', warnings) |
| return result |
| |
| |
| def _fix_result(gn_desc): |
| """Performs postprocessing on `gn desc` JSON.""" |
| result = {} |
| |
| for target, info in gn_desc.items(): |
| sources = info.get('sources') |
| configs = info.get('configs') |
| if sources and configs: |
| result[target] = { |
| 'configs': configs, |
| 'sources': sources, |
| } |
| |
| return result |
| |
| |
| def main(args): |
| known_arches = [ |
| 'arm', |
| 'arm64', |
| 'x64', |
| 'x86', |
| ] |
| |
| parser = argparse.ArgumentParser( |
| description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) |
| parser.add_argument( |
| 'arch', |
| nargs='+', |
| help='Architecture(s) to fetch `gn desc`s for. ' |
| 'Supported ones are %s' % known_arches) |
| parser.add_argument( |
| '--output', required=True, help='File to write results to.') |
| parser.add_argument( |
| '--chromium_out_dir', |
| required=True, |
| help='Chromium out/ directory for us to use. This directory will ' |
| 'be clobbered by this script.') |
| opts = parser.parse_args(args) |
| |
| logging.basicConfig( |
| format='%(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: %(message)s', |
| level=logging.INFO, |
| ) |
| |
| arches = opts.arch |
| for arch in arches: |
| if arch not in known_arches: |
| parser.error( |
| 'unknown architecture: %s; try one of %s' % (arch, known_arches)) |
| |
| results_file = os.path.realpath(opts.output) |
| out_dir = os.path.realpath(opts.chromium_out_dir) |
| |
| os.makedirs(out_dir, exist_ok=True) |
| results = {} |
| for arch in arches: |
| logging.info('Getting `gn` desc for %s...', arch) |
| |
| results[arch] = _fix_result( |
| _run_gn_desc( |
| in_dir=out_dir, |
| gn_args=_create_gn_args_for(arch), |
| )) |
| |
| os.makedirs(os.path.dirname(results_file), exist_ok=True) |
| |
| results_intermed = results_file + '.tmp' |
| with open(results_intermed, 'w', encoding='utf-8') as f: |
| json.dump(results, f) |
| os.rename(results_intermed, results_file) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |