aboutsummaryrefslogtreecommitdiff
path: root/cwp/cr-os/fetch_gn_descs.py
diff options
context:
space:
mode:
Diffstat (limited to 'cwp/cr-os/fetch_gn_descs.py')
-rwxr-xr-xcwp/cr-os/fetch_gn_descs.py196
1 files changed, 196 insertions, 0 deletions
diff --git a/cwp/cr-os/fetch_gn_descs.py b/cwp/cr-os/fetch_gn_descs.py
new file mode 100755
index 00000000..60d331c5
--- /dev/null
+++ b/cwp/cr-os/fetch_gn_descs.py
@@ -0,0 +1,196 @@
+#!/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 _find_chromium_root(search_from):
+ """Finds the chromium root directory from `search_from`."""
+ current = search_from
+ while current != '/':
+ if os.path.isfile(os.path.join(current, '.gclient')):
+ return current
+ current = os.path.dirname(current)
+ raise ValueError(
+ "%s doesn't appear to be a Chromium subdirectory" % search_from)
+
+
+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(rename_out, out_dir, chromium_root, gn_desc):
+ """Performs postprocessing on `gn desc` JSON."""
+ result = {}
+
+ rel_out = '//' + os.path.relpath(out_dir, os.path.join(chromium_root, 'src'))
+ rename_out = rename_out if rename_out.endswith('/') else rename_out + '/'
+
+ def fix_source_file(f):
+ if not f.startswith(rel_out):
+ return f
+ return rename_out + f[len(rel_out) + 1:]
+
+ for target, info in gn_desc.items():
+ sources = info.get('sources')
+ configs = info.get('configs')
+ if not sources or not configs:
+ continue
+
+ result[target] = {
+ 'configs': configs,
+ 'sources': [fix_source_file(f) for f in 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.')
+ parser.add_argument(
+ '--rename_out',
+ default='//out',
+ help='Directory to rename files in --chromium_out_dir to. '
+ 'Default: %(default)s')
+ opts = parser.parse_args(args)
+
+ logging.basicConfig(
+ format='%(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: %(message)s',
+ level=logging.INFO,
+ )
+
+ arches = opts.arch
+ rename_out = opts.rename_out
+ 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)
+ chromium_root = _find_chromium_root(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(
+ rename_out, out_dir, chromium_root,
+ _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:]))