mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:07:06 +00:00
726732dd6b
Flutter uses the same clang toolchain as Fuchsia. This CL puts Dart on that toolchain, as well. This roll should entail no changes to glibc version requirements on Linux. The Fuchsia buildtools distribute clang-format with the toolchain rather than separately, so this CL introduces forwarding scripts that are copied to the right place under //buildtools. This CL sets the default for the number of workers in //tools/gn.py to 1, as the newer GN version fails occasionally when run concurrently with other invocations. As Flutter does, this CL uses the clang toolchain to build for Android. Thus, it is now possible to build for Android on MacOS. This change is a prerequisite for assembling the SDK in GN rather than with a python script: https://codereview.chromium.org/2848943003/ and will be nicer for making prebuilt SDKs for Fuchsia. Resubmitting this with the following fixes: 1. Rolls further forward to get past a GN bug 2. Fixes the Android build. 3. Style cleanups in gn.py Review-Url: https://codereview.chromium.org/2858873005 .
318 lines
9.4 KiB
Python
Executable file
318 lines
9.4 KiB
Python
Executable file
#!/usr/bin/env python
|
|
#
|
|
# Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
|
# for details. All rights reserved. Use of this source code is governed by a
|
|
# BSD-style license that can be found in the LICENSE file.
|
|
|
|
import multiprocessing
|
|
import optparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import utils
|
|
|
|
HOST_OS = utils.GuessOS()
|
|
HOST_CPUS = utils.GuessCpus()
|
|
SCRIPT_DIR = os.path.dirname(sys.argv[0])
|
|
DART_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
|
|
|
|
usage = """\
|
|
usage: %%prog [options] [targets]
|
|
|
|
This script runs 'make' in the *current* directory. So, run it from
|
|
the Dart repo root,
|
|
|
|
%s ,
|
|
|
|
unless you really intend to use a non-default Makefile.""" % DART_ROOT
|
|
|
|
|
|
def BuildOptions():
|
|
result = optparse.OptionParser(usage=usage)
|
|
result.add_option("-m", "--mode",
|
|
help='Build variants (comma-separated).',
|
|
metavar='[all,debug,release,product]',
|
|
default='debug')
|
|
result.add_option("-v", "--verbose",
|
|
help='Verbose output.',
|
|
default=False, action="store_true")
|
|
result.add_option("-a", "--arch",
|
|
help='Target architectures (comma-separated).',
|
|
metavar='[all,ia32,x64,simarm,arm,simarmv6,armv6,simarmv5te,armv5te,'
|
|
'simmips,mips,simarm64,arm64,simdbc,armsimdbc]',
|
|
default=utils.GuessArchitecture())
|
|
result.add_option("--os",
|
|
help='Target OSs (comma-separated).',
|
|
metavar='[all,host,android]',
|
|
default='host')
|
|
result.add_option("-j",
|
|
type=int,
|
|
help='Ninja -j option for Goma builds.',
|
|
metavar=1000,
|
|
default=1000)
|
|
return result
|
|
|
|
|
|
def ProcessOsOption(os_name):
|
|
if os_name == 'host':
|
|
return HOST_OS
|
|
return os_name
|
|
|
|
|
|
def ProcessOptions(options, args):
|
|
if options.arch == 'all':
|
|
options.arch = 'ia32,x64,simarm,simarm64,simmips,simdbc64'
|
|
if options.mode == 'all':
|
|
options.mode = 'debug,release,product'
|
|
if options.os == 'all':
|
|
options.os = 'host,android'
|
|
options.mode = options.mode.split(',')
|
|
options.arch = options.arch.split(',')
|
|
options.os = options.os.split(',')
|
|
for mode in options.mode:
|
|
if not mode in ['debug', 'release', 'product']:
|
|
print "Unknown mode %s" % mode
|
|
return False
|
|
for arch in options.arch:
|
|
archs = ['ia32', 'x64', 'simarm', 'arm', 'simarmv6', 'armv6',
|
|
'simarmv5te', 'armv5te', 'simmips', 'mips', 'simarm64', 'arm64',
|
|
'simdbc', 'simdbc64', 'armsimdbc', 'armsimdbc64']
|
|
if not arch in archs:
|
|
print "Unknown arch %s" % arch
|
|
return False
|
|
options.os = [ProcessOsOption(os_name) for os_name in options.os]
|
|
for os_name in options.os:
|
|
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', 'mips',
|
|
'simdbc', 'simdbc64']:
|
|
print ("Cross-compilation to %s is not supported for architecture %s."
|
|
% (os_name, arch))
|
|
return False
|
|
# We have not yet tweaked the v8 dart build to work with the Android
|
|
# NDK/SDK, so don't try to build it.
|
|
if not args:
|
|
print "For android builds you must specify a target, such as 'runtime'."
|
|
return False
|
|
return True
|
|
|
|
|
|
def NotifyBuildDone(build_config, success, start):
|
|
if not success:
|
|
print "BUILD FAILED"
|
|
|
|
sys.stdout.flush()
|
|
|
|
# Display a notification if build time exceeded DART_BUILD_NOTIFICATION_DELAY.
|
|
notification_delay = float(
|
|
os.getenv('DART_BUILD_NOTIFICATION_DELAY', sys.float_info.max))
|
|
if (time.time() - start) < notification_delay:
|
|
return
|
|
|
|
if success:
|
|
message = 'Build succeeded.'
|
|
else:
|
|
message = 'Build failed.'
|
|
title = build_config
|
|
|
|
command = None
|
|
if HOST_OS == 'macos':
|
|
# Use AppleScript to display a UI non-modal notification.
|
|
script = 'display notification "%s" with title "%s" sound name "Glass"' % (
|
|
message, title)
|
|
command = "osascript -e '%s' &" % script
|
|
elif HOST_OS == 'linux':
|
|
if success:
|
|
icon = 'dialog-information'
|
|
else:
|
|
icon = 'dialog-error'
|
|
command = "notify-send -i '%s' '%s' '%s' &" % (icon, message, title)
|
|
elif HOST_OS == 'win32':
|
|
if success:
|
|
icon = 'info'
|
|
else:
|
|
icon = 'error'
|
|
command = ("powershell -command \""
|
|
"[reflection.assembly]::loadwithpartialname('System.Windows.Forms')"
|
|
"| Out-Null;"
|
|
"[reflection.assembly]::loadwithpartialname('System.Drawing')"
|
|
"| Out-Null;"
|
|
"$n = new-object system.windows.forms.notifyicon;"
|
|
"$n.icon = [system.drawing.systemicons]::information;"
|
|
"$n.visible = $true;"
|
|
"$n.showballoontip(%d, '%s', '%s', "
|
|
"[system.windows.forms.tooltipicon]::%s);\"") % (
|
|
5000, # Notification stays on for this many milliseconds
|
|
message, title, icon)
|
|
|
|
if command:
|
|
# Ignore return code, if this command fails, it doesn't matter.
|
|
os.system(command)
|
|
|
|
|
|
def RunGN(target_os, mode, arch):
|
|
gn_os = 'host' if target_os == HOST_OS else target_os
|
|
gn_command = [
|
|
'python',
|
|
os.path.join(DART_ROOT, 'tools', 'gn.py'),
|
|
'-m', mode,
|
|
'-a', arch,
|
|
'--os', gn_os,
|
|
'-v',
|
|
]
|
|
process = subprocess.Popen(gn_command)
|
|
process.wait()
|
|
if process.returncode != 0:
|
|
print ("Tried to run GN, but it failed. Try running it manually: \n\t$ " +
|
|
' '.join(gn_command))
|
|
|
|
|
|
def ShouldRunGN(out_dir):
|
|
return (not os.path.exists(out_dir) or
|
|
not os.path.isfile(os.path.join(out_dir, 'args.gn')))
|
|
|
|
|
|
def UseGoma(out_dir):
|
|
args_gn = os.path.join(out_dir, 'args.gn')
|
|
return 'use_goma = true' in open(args_gn, 'r').read()
|
|
|
|
|
|
# Try to start goma, but don't bail out if we can't. Instead print an error
|
|
# message, and let the build fail with its own error messages as well.
|
|
goma_started = False
|
|
def EnsureGomaStarted(out_dir):
|
|
global goma_started
|
|
if goma_started:
|
|
return True
|
|
args_gn_path = os.path.join(out_dir, 'args.gn')
|
|
goma_dir = None
|
|
with open(args_gn_path, 'r') as fp:
|
|
for line in fp:
|
|
if 'goma_dir' in line:
|
|
words = line.split()
|
|
goma_dir = words[2][1:-1] # goma_dir = "/path/to/goma"
|
|
if not goma_dir:
|
|
print 'Could not find goma for ' + out_dir
|
|
return False
|
|
if not os.path.exists(goma_dir) or not os.path.isdir(goma_dir):
|
|
print 'Could not find goma at ' + goma_dir
|
|
return False
|
|
goma_ctl = os.path.join(goma_dir, 'goma_ctl.py')
|
|
goma_ctl_command = [
|
|
'python',
|
|
goma_ctl,
|
|
'ensure_start',
|
|
]
|
|
process = subprocess.Popen(goma_ctl_command)
|
|
process.wait()
|
|
if process.returncode != 0:
|
|
print ("Tried to run goma_ctl.py, but it failed. Try running it manually: "
|
|
+ "\n\t" + ' '.join(goma_ctl_command))
|
|
return False
|
|
goma_started = True
|
|
return True
|
|
|
|
|
|
# Returns a tuple (build_config, command to run, whether goma is used)
|
|
def BuildOneConfig(options, targets, target_os, mode, arch):
|
|
build_config = utils.GetBuildConf(mode, arch, target_os)
|
|
out_dir = utils.GetBuildRoot(HOST_OS, mode, arch, target_os)
|
|
using_goma = False
|
|
if ShouldRunGN(out_dir):
|
|
RunGN(target_os, mode, arch)
|
|
command = ['ninja', '-C', out_dir]
|
|
if options.verbose:
|
|
command += ['-v']
|
|
if UseGoma(out_dir):
|
|
if EnsureGomaStarted(out_dir):
|
|
using_goma = True
|
|
command += [('-j%s' % str(options.j))]
|
|
else:
|
|
# If we couldn't ensure that goma is started, let the build start, but
|
|
# slowly so we can see any helpful error messages that pop out.
|
|
command += ['-j1']
|
|
command += targets
|
|
return (build_config, command, using_goma)
|
|
|
|
|
|
def RunOneBuildCommand(build_config, args):
|
|
start_time = time.time()
|
|
print ' '.join(args)
|
|
process = subprocess.Popen(args, stdin=None)
|
|
process.wait()
|
|
if process.returncode != 0:
|
|
NotifyBuildDone(build_config, success=False, start=start_time)
|
|
return 1
|
|
else:
|
|
NotifyBuildDone(build_config, success=True, start=start_time)
|
|
|
|
return 0
|
|
|
|
|
|
def RunOneGomaBuildCommand(args):
|
|
try:
|
|
print ' '.join(args)
|
|
process = subprocess.Popen(args, stdin=None)
|
|
process.wait()
|
|
print (' '.join(args) + " done.")
|
|
return process.returncode
|
|
except KeyboardInterrupt:
|
|
return 1
|
|
|
|
|
|
def Main():
|
|
starttime = time.time()
|
|
# Parse the options.
|
|
parser = BuildOptions()
|
|
(options, args) = parser.parse_args()
|
|
if not ProcessOptions(options, args):
|
|
parser.print_help()
|
|
return 1
|
|
# Determine which targets to build. By default we build the "all" target.
|
|
if len(args) == 0:
|
|
targets = ['all']
|
|
else:
|
|
targets = args
|
|
|
|
# Build all targets for each requested configuration.
|
|
configs = []
|
|
for target_os in options.os:
|
|
for mode in options.mode:
|
|
for arch in options.arch:
|
|
configs.append(BuildOneConfig(options, targets, target_os, mode, arch))
|
|
|
|
# Build regular configs.
|
|
goma_builds = []
|
|
for (build_config, args, goma) in configs:
|
|
if args is None:
|
|
return 1
|
|
if goma:
|
|
goma_builds.append(args)
|
|
elif RunOneBuildCommand(build_config, args) != 0:
|
|
return 1
|
|
|
|
# Run goma builds in parallel.
|
|
pool = multiprocessing.Pool(multiprocessing.cpu_count())
|
|
results = pool.map(RunOneGomaBuildCommand, goma_builds, chunksize=1)
|
|
for r in results:
|
|
if r != 0:
|
|
return 1
|
|
|
|
endtime = time.time()
|
|
print ("The build took %.3f seconds" % (endtime - starttime))
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(Main())
|