#!/usr/bin/env python # Copyright 2016 The Dart project 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 multiprocessing import os import shutil import subprocess import sys import time import utils HOST_OS = utils.GuessOS() HOST_ARCH = utils.GuessArchitecture() SCRIPT_DIR = os.path.dirname(sys.argv[0]) DART_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..')) GN = os.path.join(DART_ROOT, 'buildtools', 'gn') # Environment variables for default settings. DART_USE_ASAN = "DART_USE_ASAN" # Use instead of --asan DART_USE_MSAN = "DART_USE_MSAN" # Use instead of --msan DART_USE_TSAN = "DART_USE_TSAN" # Use instead of --tsan DART_USE_TOOLCHAIN = "DART_USE_TOOLCHAIN" # Use instread of --toolchain-prefix DART_USE_SYSROOT = "DART_USE_SYSROOT" # Use instead of --target-sysroot # use instead of --platform-sdk DART_MAKE_PLATFORM_SDK = "DART_MAKE_PLATFORM_SDK" DART_GN_ARGS = "DART_GN_ARGS" def UseASAN(): return DART_USE_ASAN in os.environ def UseMSAN(): return DART_USE_MSAN in os.environ def UseTSAN(): return DART_USE_TSAN in os.environ def ToolchainPrefix(args): if args.toolchain_prefix: return args.toolchain_prefix return os.environ.get(DART_USE_TOOLCHAIN) def TargetSysroot(args): if args.target_sysroot: return args.target_sysroot return os.environ.get(DART_USE_SYSROOT) def MakePlatformSDK(): return DART_MAKE_PLATFORM_SDK in os.environ def GetGNArgs(args): if args.gn_args != None: return args.gn_args args = os.environ.get(DART_GN_ARGS) or "" return args.split() def GetOutDir(mode, arch, target_os): return utils.GetBuildRoot(HOST_OS, mode, arch, target_os) def ToCommandLine(gn_args): def merge(key, value): if type(value) is bool: return '%s=%s' % (key, 'true' if value else 'false') elif type(value) is int: return '%s=%d' % (key, value) return '%s="%s"' % (key, value) return [merge(x, y) for x, y in gn_args.iteritems()] def HostCpuForArch(arch): if arch in ['ia32', 'arm', 'armv6', 'armv5te', 'simarm', 'simarmv6', 'simarmv5te', 'simdbc', 'armsimdbc']: return 'x86' if arch in ['x64', 'arm64', 'simarm64', 'simdbc64', 'armsimdbc64']: return 'x64' # The C compiler's target. def TargetCpuForArch(arch, target_os): if arch in ['ia32', 'simarm', 'simarmv6', 'simarmv5te']: return 'x86' if arch in ['x64', 'simarm64']: return 'x64' if arch == 'simdbc': return 'arm' if target_os == 'android' else 'x86' if arch == 'simdbc64': return 'arm64' if target_os == 'android' else 'x64' if arch == 'armsimdbc': return 'arm' if arch == 'armsimdbc64': return 'arm64' return arch # The Dart compiler's target. def DartTargetCpuForArch(arch): if arch in ['ia32']: return 'ia32' if arch in ['x64']: return 'x64' if arch in ['arm', 'simarm']: return 'arm' if arch in ['armv6', 'simarmv6']: return 'armv6' if arch in ['armv5te', 'simarmv5te']: return 'armv5te' if arch in ['arm64', 'simarm64']: return 'arm64' if arch in ['simdbc', 'simdbc64', 'armsimdbc', 'armsimdbc64']: return 'dbc' return arch def HostOsForGn(host_os): if host_os.startswith('macos'): return 'mac' if host_os.startswith('win'): return 'win' return host_os # Where string_map is formatted as X1=Y1,X2=Y2 etc. # If key is X1, returns Y1. def ParseStringMap(key, string_map): for m in string_map.split(','): l = m.split('=') if l[0] == key: return l[1] return None def UseSanitizer(args): return args.asan or args.msan or args.tsan def DontUseClang(args, target_os, host_cpu, target_cpu): # We don't have clang on Windows. return target_os == 'win' def UseSysroot(args, gn_args): # Don't try to use a Linux sysroot if we aren't on Linux. if gn_args['target_os'] != 'linux': return False # Don't use the sysroot if we're given another sysroot. if TargetSysroot(args): return False # Otherwise use the sysroot. return True def ToGnArgs(args, mode, arch, target_os): gn_args = {} host_os = HostOsForGn(HOST_OS) if target_os == 'host': gn_args['target_os'] = host_os else: gn_args['target_os'] = target_os gn_args['host_cpu'] = HostCpuForArch(arch) gn_args['target_cpu'] = TargetCpuForArch(arch, target_os) gn_args['dart_target_arch'] = DartTargetCpuForArch(arch) if arch != HostCpuForArch(arch): # Training an app-jit snapshot under a simulator is slow. Use script # snapshots instead. gn_args['dart_snapshot_kind'] = 'script' else: gn_args['dart_snapshot_kind'] = 'app-jit' # We only want the fallback root certs in the standalone VM on # Linux and Windows. if gn_args['target_os'] in ['linux', 'win']: gn_args['dart_use_fallback_root_certificates'] = True gn_args['dart_zlib_path'] = "//runtime/bin/zlib" # Use tcmalloc only when targeting Linux and when not using ASAN. gn_args['dart_use_tcmalloc'] = ((gn_args['target_os'] == 'linux') and not UseSanitizer(args)) if gn_args['target_os'] == 'linux': if gn_args['target_cpu'] == 'arm': # Default to -mfloat-abi=hard and -mfpu=neon for arm on Linux as we're # specifying a gnueabihf compiler in //build/toolchain/linux/BUILD.gn. floatabi = 'hard' if args.arm_float_abi == '' else args.arm_float_abi gn_args['arm_version'] = 7 gn_args['arm_float_abi'] = floatabi gn_args['arm_use_neon'] = True elif gn_args['target_cpu'] == 'armv6': floatabi = 'softfp' if args.arm_float_abi == '' else args.arm_float_abi gn_args['target_cpu'] = 'arm' gn_args['arm_version'] = 6 gn_args['arm_float_abi'] = floatabi elif gn_args['target_cpu'] == 'armv5te': raise Exception("GN support for armv5te unimplemented") gn_args['is_debug'] = mode == 'debug' gn_args['is_release'] = mode == 'release' gn_args['is_product'] = mode == 'product' gn_args['dart_debug'] = mode == 'debug' # This setting is only meaningful for Flutter. Standalone builds of the VM # should leave this set to 'develop', which causes the build to defer to # 'is_debug', 'is_release' and 'is_product'. if mode == 'product': gn_args['dart_runtime_mode'] = 'release' else: gn_args['dart_runtime_mode'] = 'develop' gn_args['exclude_kernel_service'] = args.exclude_kernel_service dont_use_clang = DontUseClang(args, gn_args['target_os'], gn_args['host_cpu'], gn_args['target_cpu']) gn_args['is_clang'] = args.clang and not dont_use_clang gn_args['is_asan'] = args.asan and gn_args['is_clang'] gn_args['is_msan'] = args.msan and gn_args['is_clang'] gn_args['is_tsan'] = args.tsan and gn_args['is_clang'] if not args.platform_sdk and not gn_args['target_cpu'].startswith('arm'): gn_args['dart_platform_sdk'] = args.platform_sdk gn_args['dart_stripped_binary'] = 'exe.stripped/dart' # Setup the user-defined sysroot. if UseSysroot(args, gn_args): gn_args['dart_use_wheezy_sysroot'] = True else: sysroot = TargetSysroot(args) if sysroot: gn_args['target_sysroot'] = ParseStringMap(arch, sysroot) toolchain = ToolchainPrefix(args) if toolchain: gn_args['toolchain_prefix'] = ParseStringMap(arch, toolchain) goma_dir = os.environ.get('GOMA_DIR') goma_home_dir = os.path.join(os.getenv('HOME', ''), 'goma') if args.goma and goma_dir: gn_args['use_goma'] = True gn_args['goma_dir'] = goma_dir elif args.goma and os.path.exists(goma_home_dir): gn_args['use_goma'] = True gn_args['goma_dir'] = goma_home_dir else: gn_args['use_goma'] = False gn_args['goma_dir'] = None if args.debug_opt_level: gn_args['dart_debug_optimization_level'] = args.debug_opt_level gn_args['debug_optimization_level'] = args.debug_opt_level return gn_args def ProcessOsOption(os_name): if os_name == 'host': return HOST_OS return os_name def ProcessOptions(args): if args.arch == 'all': args.arch = 'ia32,x64,simarm,simarm64,simdbc64' if args.mode == 'all': args.mode = 'debug,release,product' if args.os == 'all': args.os = 'host,android' args.mode = args.mode.split(',') args.arch = args.arch.split(',') args.os = args.os.split(',') for mode in args.mode: if not mode in ['debug', 'release', 'product']: print "Unknown mode %s" % mode return False for arch in args.arch: archs = ['ia32', 'x64', 'simarm', 'arm', 'simarmv6', 'armv6', 'simarmv5te', 'armv5te', 'simarm64', 'arm64', 'simdbc', 'simdbc64', 'armsimdbc', 'armsimdbc64'] if not arch in archs: print "Unknown arch %s" % arch return False oses = [ProcessOsOption(os_name) for os_name in args.os] for os_name in oses: if not os_name in ['android', 'freebsd', 'linux', 'macos', 'win32']: print "Unknown os %s" % os_name return False if os_name != HOST_OS: if os_name != 'android': print "Unsupported target os %s" % os_name return False if not HOST_OS in ['linux', 'macos']: print ("Cross-compilation to %s is not supported on host os %s." % (os_name, HOST_OS)) return False if not arch in ['ia32', 'x64', 'arm', 'armv6', 'armv5te', 'arm64', 'simdbc', 'simdbc64']: print ("Cross-compilation to %s is not supported for architecture %s." % (os_name, arch)) return False return True def os_has_ide(host_os): return host_os.startswith('win') or host_os.startswith('mac') def ide_switch(host_os): if host_os.startswith('win'): return '--ide=vs' elif host_os.startswith('mac'): return '--ide=xcode' else: return '--ide=json' def parse_args(args): args = args[1:] parser = argparse.ArgumentParser( description='A script to run `gn gen`.', formatter_class=argparse.ArgumentDefaultsHelpFormatter) common_group = parser.add_argument_group('Common Arguments') other_group = parser.add_argument_group('Other Arguments') common_group.add_argument('--arch', '-a', type=str, help='Target architectures (comma-separated).', metavar='[all,ia32,x64,simarm,arm,simarmv6,armv6,simarmv5te,armv5te,' 'simarm64,arm64,simdbc,armsimdbc]', default='x64') common_group.add_argument('--mode', '-m', type=str, help='Build variants (comma-separated).', metavar='[all,debug,release,product]', default='debug') common_group.add_argument('--os', type=str, help='Target OSs (comma-separated).', metavar='[all,host,android]', default='host') common_group.add_argument("-v", "--verbose", help='Verbose output.', default=False, action="store_true") other_group.add_argument('--arm-float-abi', type=str, help='The ARM float ABI (soft, softfp, hard)', metavar='[soft,softfp,hard]', default='') other_group.add_argument('--asan', help='Build with ASAN', default=UseASAN(), action='store_true') other_group.add_argument('--no-asan', help='Disable ASAN', dest='asan', action='store_false') other_group.add_argument('--clang', help='Use Clang', default=True, action='store_true') other_group.add_argument('--no-clang', help='Disable Clang', dest='clang', action='store_false') other_group.add_argument('--debug-opt-level', '-d', help='The optimization level to use for debug builds', type=str) other_group.add_argument('--goma', help='Use goma', default=True, action='store_true') other_group.add_argument('--no-goma', help='Disable goma', dest='goma', action='store_false') other_group.add_argument('--ide', help='Generate an IDE file.', default=os_has_ide(HOST_OS), action='store_true') other_group.add_argument('--exclude-kernel-service', help='Exclude the kernel service.', default=False, dest='exclude_kernel_service', action='store_true') other_group.add_argument('--msan', help='Build with MSAN', default=UseMSAN(), action='store_true') other_group.add_argument('--no-msan', help='Disable MSAN', dest='msan', action='store_false') other_group.add_argument('--gn-args', help='Set extra GN args', dest='gn_args', action='append') other_group.add_argument('--platform-sdk', help='Directs the create_sdk target to create a smaller "Platform" SDK', default=MakePlatformSDK(), action='store_true') other_group.add_argument('--target-sysroot', '-s', type=str, help='Comma-separated list of arch=/path/to/sysroot mappings') other_group.add_argument('--toolchain-prefix', '-t', type=str, help='Comma-separated list of arch=/path/to/toolchain-prefix mappings') other_group.add_argument('--tsan', help='Build with TSAN', default=UseTSAN(), action='store_true') other_group.add_argument('--no-tsan', help='Disable TSAN', dest='tsan', action='store_false') other_group.add_argument('--wheezy', help='This flag is deprecated.', default=True, action='store_true') other_group.add_argument('--no-wheezy', help='This flag is deprecated', dest='wheezy', action='store_false') other_group.add_argument('--workers', '-w', type=int, help='Number of simultaneous GN invocations', dest='workers', default=multiprocessing.cpu_count()) options = parser.parse_args(args) if not ProcessOptions(options): parser.print_help() return None return options # Run the command, if it succeeds returns 0, if it fails, returns the commands # output as a string. def RunCommand(command): try: subprocess.check_output( command, cwd=DART_ROOT, stderr=subprocess.STDOUT) return 0 except subprocess.CalledProcessError as e: return ("Command failed: " + ' '.join(command) + "\n" + "output: " + e.output) def Main(argv): starttime = time.time() args = parse_args(argv) if sys.platform.startswith(('cygwin', 'win')): subdir = 'win' elif sys.platform == 'darwin': subdir = 'mac-x64' elif sys.platform.startswith('linux'): subdir = 'linux-x64' else: print 'Unknown platform: ' + sys.platform return 1 gn = os.path.join(DART_ROOT, 'buildtools', subdir, 'gn') commands = [] for target_os in args.os: for mode in args.mode: for arch in args.arch: out_dir = GetOutDir(mode, arch, target_os) # TODO(infra): Re-enable --check. Many targets fail to use # public_deps to re-expose header files to their dependents. # See dartbug.com/32364 command = [gn, 'gen', out_dir] gn_args = ToCommandLine(ToGnArgs(args, mode, arch, target_os)) gn_args += GetGNArgs(args) if args.verbose: print "gn gen --check in %s" % out_dir if args.ide: command.append(ide_switch(HOST_OS)) command.append('--args=%s' % ' '.join(gn_args)) commands.append(command) pool = multiprocessing.Pool(args.workers) results = pool.map(RunCommand, commands, chunksize=1) for r in results: if r != 0: print r.strip() return 1 endtime = time.time() if args.verbose: print ("GN Time: %.3f seconds" % (endtime - starttime)) return 0 if __name__ == '__main__': sys.exit(Main(sys.argv))