| #!/usr/bin/env python |
| # Copyright 2016 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import json |
| import multiprocessing |
| import os |
| import paths |
| import Queue |
| import subprocess |
| import sys |
| import threading |
| |
| |
| def gn_describe(out, path): |
| gn = os.path.join(paths.FUCHSIA_ROOT, 'buildtools', 'gn') |
| data = subprocess.check_output( |
| [gn, 'desc', out, path, '--format=json'], cwd=paths.FUCHSIA_ROOT) |
| return json.loads(data) |
| |
| |
| class WorkerThread(threading.Thread): |
| ''' |
| A worker thread to run scripts from a queue and return exit codes and output |
| on a queue. |
| ''' |
| |
| def __init__(self, script_queue, result_queue, args): |
| threading.Thread.__init__(self) |
| self.script_queue = script_queue |
| self.result_queue = result_queue |
| self.args = args |
| self.daemon = True |
| |
| def run(self): |
| while True: |
| try: |
| script = self.script_queue.get(False) |
| except Queue.Empty, e: |
| # No more scripts to run. |
| return |
| if not os.path.exists(script): |
| self.result_queue.put((script, -1, 'Script does not exist.')) |
| continue |
| job = subprocess.Popen( |
| [script] + self.args, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| stdout, stderr = job.communicate() |
| self.result_queue.put((script, job.returncode, stdout + stderr)) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| '''Run Dart actions (analysis, test, target-test) for Dart build |
| targets. Extra flags will be passed to the supporting Dart tool if applicable. |
| ''') |
| parser.add_argument( |
| '--out', |
| help='Path to the base output directory, e.g. out/debug-x64', |
| required=True) |
| parser.add_argument( |
| '--tree', |
| help='Restrict analysis to a source subtree, e.g. //topaz/shell/*', |
| default='*') |
| parser.add_argument( |
| '--jobs', '-j', |
| help='Number of concurrent instances to run', |
| type=int, |
| default=multiprocessing.cpu_count()) |
| parser.add_argument( |
| '--verbose', '-v', |
| help='Show output from tests that pass', |
| action='store_true') |
| parser.add_argument( |
| 'action', |
| help='Action to perform on the targets', |
| choices=('analyze', 'test', 'target-test')) |
| args, extras = parser.parse_known_args() |
| |
| if not os.path.isdir(os.path.join(paths.FUCHSIA_ROOT, args.out)): |
| print 'Invalid output directory: %s' % args.out |
| return 1 |
| |
| tree = args.tree |
| if args.action == 'analyze': |
| tree = '%s(//build/dart:dartlang)' % tree |
| |
| # Ask gn about all the dart analyzer scripts. |
| scripts = [] |
| targets = gn_describe(args.out, tree) |
| if not targets: |
| print 'No targets found...' |
| exit(1) |
| |
| for target_name, properties in targets.items(): |
| if args.action == 'analyze': |
| script_valid = ( |
| 'script' in properties and properties['script'] == |
| '//build/dart/gen_analyzer_invocation.py' |
| ) |
| elif args.action == 'test': |
| script_valid = ( |
| 'script' in properties and |
| properties['script'] == '//build/dart/gen_test_invocation.py' |
| ) |
| else: # 'target-test' |
| script_valid = ( |
| 'script' in properties and |
| properties['script'] == |
| '//build/dart/gen_remote_test_invocation.py' |
| ) |
| if ('type' not in properties or |
| properties['type'] != 'action' or |
| 'script' not in properties or |
| not script_valid or |
| 'outputs' not in properties or |
| not len(properties['outputs'])): |
| continue |
| script_path = properties['outputs'][0] |
| script_path = script_path[2:] # Remove the leading // |
| scripts.append(os.path.join(paths.FUCHSIA_ROOT, script_path)) |
| |
| # Put all the analyzer scripts in a queue that workers will work from |
| script_queue = Queue.Queue() |
| for script in scripts: |
| script_queue.put(script) |
| # Make a queue to receive results from workers. |
| result_queue = Queue.Queue() |
| # Track return codes from scripts. |
| script_results = [] |
| failed_scripts = [] |
| |
| # Create a worker thread for each CPU on the machine. |
| for i in range(args.jobs): |
| WorkerThread(script_queue, result_queue, extras).start() |
| |
| def print_progress(): |
| sys.stdout.write('\rProgress: %d/%d\033[K' % (len(script_results), |
| len(scripts))) |
| sys.stdout.flush() |
| |
| print_progress() |
| |
| # Handle results from workers. |
| while len(script_results) < len(scripts): |
| script, returncode, output = result_queue.get(True) |
| script_results.append(returncode) |
| print_progress() |
| if returncode != 0: |
| failed_scripts.append(script) |
| if args.verbose or returncode != 0: |
| print '\r----------------------------------------------------------' |
| print script |
| print output |
| |
| print '' |
| if len(failed_scripts): |
| failed_scripts.sort() |
| print 'Failures in:' |
| for script in failed_scripts: |
| print ' %s' % script |
| exit(1) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |