mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
319 lines
11 KiB
Python
Executable File
319 lines
11 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright 2014 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""A simple script to make building/testing Mojo components easier."""
|
|
|
|
import argparse
|
|
from copy import deepcopy
|
|
import logging
|
|
from multiprocessing import cpu_count
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
from get_test_list import GetTestList
|
|
from mopy.config import Config
|
|
from mopy.paths import Paths
|
|
from mopy.gn import GNArgsForConfig, ParseGNConfig, CommandLineForGNArgs
|
|
from mopy.log import InitLogging
|
|
|
|
|
|
_logger = logging.getLogger()
|
|
_verbose_count = 0
|
|
|
|
|
|
def _args_to_config(args):
|
|
# Default to host OS.
|
|
target_os = None
|
|
if args.android:
|
|
target_os = Config.OS_ANDROID
|
|
elif args.ios:
|
|
target_os = Config.OS_IOS
|
|
|
|
target_cpu = args.target_cpu
|
|
|
|
additional_args = {}
|
|
|
|
if 'clang' in args:
|
|
additional_args['is_clang'] = args.clang
|
|
|
|
if 'asan' in args and args.asan:
|
|
additional_args['sanitizer'] = Config.SANITIZER_ASAN
|
|
|
|
# Additional non-standard config entries:
|
|
|
|
if 'goma' in args:
|
|
goma_dir = os.environ.get('GOMA_DIR')
|
|
goma_home_dir = os.path.join(os.getenv('HOME', ''), 'goma')
|
|
if args.goma and goma_dir:
|
|
additional_args['use_goma'] = True
|
|
additional_args['goma_dir'] = goma_dir
|
|
elif args.goma and os.path.exists(goma_home_dir):
|
|
additional_args['use_goma'] = True
|
|
additional_args['goma_dir'] = goma_home_dir
|
|
else:
|
|
additional_args['use_goma'] = False
|
|
additional_args['goma_dir'] = None
|
|
|
|
if 'nacl' in args:
|
|
additional_args['use_nacl'] = args.nacl
|
|
|
|
if not ('asan' in args and args.asan):
|
|
go_dir = os.path.join(Paths().src_root, 'third_party', 'go', 'tool')
|
|
if args.android:
|
|
additional_args['mojo_use_go'] = True
|
|
additional_args['go_build_tool'] = os.path.join(
|
|
go_dir, 'android_arm', 'bin', 'go')
|
|
elif target_os is None and Config.GetHostOS() == Config.OS_LINUX:
|
|
additional_args['mojo_use_go'] = True
|
|
additional_args['go_build_tool'] = os.path.join(
|
|
go_dir, 'linux_amd64', 'bin', 'go')
|
|
|
|
if 'dry_run' in args:
|
|
additional_args['dry_run'] = args.dry_run
|
|
|
|
if 'builder_name' in args:
|
|
additional_args['builder_name'] = args.builder_name
|
|
if 'build_number' in args:
|
|
additional_args['build_number'] = args.build_number
|
|
if 'master_name' in args:
|
|
additional_args['master_name'] = args.master_name
|
|
if 'test_results_server' in args:
|
|
additional_args['test_results_server'] = args.test_results_server
|
|
|
|
if 'gn_args' in args:
|
|
additional_args['gn_args'] = args.gn_args
|
|
|
|
is_debug = args.debug and not args.official
|
|
|
|
return Config(target_os=target_os, target_cpu=target_cpu,
|
|
is_debug=is_debug, is_official_build=args.official,
|
|
dcheck_always_on=args.dcheck_always_on,
|
|
is_simulator=args.simulator, **additional_args)
|
|
|
|
|
|
def _get_out_dir(config):
|
|
"""Gets the build output directory (e.g., out/Debug), relative to src, for the
|
|
given config."""
|
|
|
|
paths = Paths(config)
|
|
return paths.SrcRelPath(paths.build_dir)
|
|
|
|
|
|
def _sync(config): # pylint: disable=W0613
|
|
"""Runs gclient sync for the given config."""
|
|
|
|
_logger.debug('_sync()')
|
|
return subprocess.call(['gclient', 'sync'])
|
|
|
|
|
|
def _gn(config):
|
|
"""Runs gn gen for the given config."""
|
|
|
|
_logger.debug('_gn()')
|
|
|
|
command = ['gn', 'gen', '--check']
|
|
|
|
gn_args = CommandLineForGNArgs(GNArgsForConfig(config))
|
|
out_dir = _get_out_dir(config)
|
|
command.append(out_dir)
|
|
command.append('--args=%s' % ' '.join(gn_args))
|
|
|
|
print 'Running %s %s ...' % (command[0],
|
|
' '.join('\'%s\'' % x for x in command[1:]))
|
|
return subprocess.call(command)
|
|
|
|
|
|
def _build(config):
|
|
"""Builds for the given config."""
|
|
|
|
_logger.debug('_build()')
|
|
|
|
out_dir = _get_out_dir(config)
|
|
gn_args = ParseGNConfig(out_dir)
|
|
print 'Building in %s ...' % out_dir
|
|
if gn_args.get('use_goma'):
|
|
# Use the configured goma directory.
|
|
local_goma_dir = gn_args.get('goma_dir')
|
|
print 'Ensuring goma (in %s) started ...' % local_goma_dir
|
|
command = ['python',
|
|
os.path.join(local_goma_dir, 'goma_ctl.py'),
|
|
'ensure_start']
|
|
exit_code = subprocess.call(command)
|
|
if exit_code:
|
|
return exit_code
|
|
|
|
# Goma allows us to run many more jobs in parallel, say 32 per core/thread
|
|
# (= 1024 on a 16-core, 32-thread Z620). Limit the load average to 4 per
|
|
# core/thread (= 128 on said Z620).
|
|
jobs = cpu_count() * 32
|
|
limit = cpu_count() * 4
|
|
return subprocess.call(['ninja', '-j', str(jobs), '-l', str(limit),
|
|
'-C', out_dir])
|
|
else:
|
|
return subprocess.call(['ninja', '-C', out_dir])
|
|
|
|
|
|
def _run_tests(config, test_types):
|
|
"""Runs the tests of the given type(s) for the given config."""
|
|
|
|
assert isinstance(test_types, list)
|
|
config = deepcopy(config)
|
|
config.values['test_types'] = test_types
|
|
|
|
test_list = GetTestList(config, verbose_count=_verbose_count)
|
|
dry_run = config.values.get('dry_run')
|
|
final_exit_code = 0
|
|
failure_list = []
|
|
for entry in test_list:
|
|
print 'Running: %s' % entry['name']
|
|
print 'Command: %s' % ' '.join(entry['command'])
|
|
if dry_run:
|
|
continue
|
|
|
|
_logger.info('Starting: %s' % ' '.join(entry['command']))
|
|
exit_code = subprocess.call(entry['command'])
|
|
_logger.info('Completed: %s' % ' '.join(entry['command']))
|
|
if exit_code:
|
|
if not final_exit_code:
|
|
final_exit_code = exit_code
|
|
failure_list.append(entry['name'])
|
|
|
|
print 72 * '='
|
|
print 'SUMMARY:',
|
|
if dry_run:
|
|
print 'Dry run: no tests run'
|
|
elif not failure_list:
|
|
assert not final_exit_code
|
|
print 'All tests passed'
|
|
else:
|
|
assert final_exit_code
|
|
print 'The following had failures:', ', '.join(failure_list)
|
|
|
|
return final_exit_code
|
|
|
|
|
|
def _test(config):
|
|
_logger.debug('_test()')
|
|
return _run_tests(config, [Config.TEST_TYPE_DEFAULT])
|
|
|
|
|
|
def _perftest(config):
|
|
_logger.debug('_perftest()')
|
|
return _run_tests(config, [Config.TEST_TYPE_PERF])
|
|
|
|
|
|
def _pytest(config):
|
|
_logger.debug('_pytest()')
|
|
return _run_tests(config, ['python'])
|
|
|
|
|
|
def main():
|
|
os.chdir(Paths().src_root)
|
|
|
|
parser = argparse.ArgumentParser(description='A script to make building'
|
|
'/testing Mojo components easier.')
|
|
|
|
parent_parser = argparse.ArgumentParser(add_help=False)
|
|
|
|
parent_parser.add_argument('--verbose',
|
|
help='Be verbose (multiple times for more)',
|
|
default=0, dest='verbose_count', action='count')
|
|
|
|
parent_parser.add_argument('--asan', help='Use Address Sanitizer',
|
|
action='store_true')
|
|
parent_parser.add_argument('--dcheck_always_on',
|
|
help='DCHECK and MOJO_DCHECK are fatal even in '
|
|
'release builds',
|
|
action='store_true')
|
|
|
|
debug_group = parent_parser.add_mutually_exclusive_group()
|
|
debug_group.add_argument('--debug', help='Debug build (default)',
|
|
default=True, action='store_true')
|
|
debug_group.add_argument('--release', help='Release build', default=False,
|
|
dest='debug', action='store_false')
|
|
# The official build is a release build suitable for distribution, with a
|
|
# different package name.
|
|
debug_group.add_argument('--official', help='Official build', default=False,
|
|
dest='official', action='store_true')
|
|
|
|
os_group = parent_parser.add_mutually_exclusive_group()
|
|
os_group.add_argument('--android', help='Build for Android',
|
|
action='store_true')
|
|
os_group.add_argument('--ios', help='Build for iOS',
|
|
action='store_true')
|
|
|
|
parent_parser.add_argument('--simulator',
|
|
help='Build for a simulator of the target',
|
|
action='store_true')
|
|
|
|
parent_parser.add_argument('--target-cpu',
|
|
help='CPU architecture to build for.',
|
|
choices=['x64', 'x86', 'arm'])
|
|
|
|
subparsers = parser.add_subparsers()
|
|
|
|
sync_parser = subparsers.add_parser('sync', parents=[parent_parser],
|
|
help='Sync using gclient (does not run gn).')
|
|
sync_parser.set_defaults(func=_sync)
|
|
|
|
gn_parser = subparsers.add_parser('gn', parents=[parent_parser],
|
|
help='Run gn for mojo (does not sync).')
|
|
gn_parser.set_defaults(func=_gn)
|
|
gn_parser.add_argument('--args', help='Specify extra args',
|
|
default=None, dest='gn_args')
|
|
# Note: no default, if nothing is specified on the command line GN decides.
|
|
gn_parser.add_argument('--nacl', help='Add in NaCl', action='store_true',
|
|
default=argparse.SUPPRESS)
|
|
gn_parser.add_argument('--no-nacl', help='Remove NaCl', action='store_false',
|
|
default=argparse.SUPPRESS, dest='nacl')
|
|
|
|
clang_group = gn_parser.add_mutually_exclusive_group()
|
|
clang_group.add_argument('--clang', help='Use Clang (default)', default=None,
|
|
action='store_true')
|
|
clang_group.add_argument('--gcc', help='Use GCC',
|
|
dest='clang', action='store_false')
|
|
goma_group = gn_parser.add_mutually_exclusive_group()
|
|
goma_group.add_argument('--goma',
|
|
help='Use Goma (if $GOMA_DIR is set or $HOME/goma '
|
|
'exists; default)',
|
|
default=True,
|
|
action='store_true')
|
|
goma_group.add_argument('--no-goma', help='Don\'t use Goma', default=False,
|
|
dest='goma', action='store_false')
|
|
|
|
build_parser = subparsers.add_parser('build', parents=[parent_parser],
|
|
help='Build')
|
|
build_parser.set_defaults(func=_build)
|
|
|
|
test_parser = subparsers.add_parser('test', parents=[parent_parser],
|
|
help='Run unit tests (does not build).')
|
|
test_parser.set_defaults(func=_test)
|
|
test_parser.add_argument('--dry-run',
|
|
help='Print instead of executing commands',
|
|
default=False, action='store_true')
|
|
|
|
perftest_parser = subparsers.add_parser('perftest', parents=[parent_parser],
|
|
help='Run perf tests (does not build).')
|
|
perftest_parser.set_defaults(func=_perftest)
|
|
|
|
pytest_parser = subparsers.add_parser('pytest', parents=[parent_parser],
|
|
help='Run Python unit tests (does not build).')
|
|
pytest_parser.set_defaults(func=_pytest)
|
|
|
|
args = parser.parse_args()
|
|
global _verbose_count
|
|
_verbose_count = args.verbose_count
|
|
InitLogging(_verbose_count)
|
|
|
|
if args.simulator and not args.ios:
|
|
sys.exit("Currently, the simulator target is only configured for iOS")
|
|
|
|
return args.func(_args_to_config(args))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|