You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.3 KiB
197 lines
5.3 KiB
#!/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:]))
|