Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: tools/ninja.py

Issue 2712743009: Adds tools/ninja.py that runs goma builds in parallel (Closed)
Patch Set: Remove java configuration Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a
5 # BSD-style license that can be found in the LICENSE file.
6
7 import multiprocessing
8 import optparse
9 import os
10 import subprocess
11 import sys
12 import time
13 import utils
14
15 HOST_OS = utils.GuessOS()
16 HOST_CPUS = utils.GuessCpus()
17 SCRIPT_DIR = os.path.dirname(sys.argv[0])
18 DART_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
19
20 usage = """\
21 usage: %%prog [options] [targets]
22
23 This script runs 'make' in the *current* directory. So, run it from
24 the Dart repo root,
25
26 %s ,
27
28 unless you really intend to use a non-default Makefile.""" % DART_ROOT
29
30
31 def BuildOptions():
32 result = optparse.OptionParser(usage=usage)
33 result.add_option("-m", "--mode",
34 help='Build variants (comma-separated).',
35 metavar='[all,debug,release,product]',
36 default='debug')
37 result.add_option("-v", "--verbose",
38 help='Verbose output.',
39 default=False, action="store_true")
40 result.add_option("-a", "--arch",
41 help='Target architectures (comma-separated).',
42 metavar='[all,ia32,x64,simarm,arm,simarmv6,armv6,simarmv5te,armv5te,'
43 'simmips,mips,simarm64,arm64,simdbc,armsimdbc]',
44 default=utils.GuessArchitecture())
45 result.add_option("--os",
46 help='Target OSs (comma-separated).',
47 metavar='[all,host,android]',
48 default='host')
49 result.add_option("-j",
50 type=int,
51 help='Ninja -j option for Goma builds.',
52 metavar=1000,
53 default=1000)
54 return result
55
56
57 def ProcessOsOption(os_name):
58 if os_name == 'host':
59 return HOST_OS
60 return os_name
61
62
63 def ProcessOptions(options, args):
64 if options.arch == 'all':
65 options.arch = 'ia32,x64,simarm,simarm64,simmips,simdbc64'
66 if options.mode == 'all':
67 options.mode = 'debug,release,product'
68 if options.os == 'all':
69 options.os = 'host,android'
70 options.mode = options.mode.split(',')
71 options.arch = options.arch.split(',')
72 options.os = options.os.split(',')
73 for mode in options.mode:
74 if not mode in ['debug', 'release', 'product']:
75 print "Unknown mode %s" % mode
76 return False
77 for arch in options.arch:
78 archs = ['ia32', 'x64', 'simarm', 'arm', 'simarmv6', 'armv6',
79 'simarmv5te', 'armv5te', 'simmips', 'mips', 'simarm64', 'arm64',
80 'simdbc', 'simdbc64', 'armsimdbc', 'armsimdbc64']
81 if not arch in archs:
82 print "Unknown arch %s" % arch
83 return False
84 options.os = [ProcessOsOption(os_name) for os_name in options.os]
85 for os_name in options.os:
86 if not os_name in ['android', 'freebsd', 'linux', 'macos', 'win32']:
87 print "Unknown os %s" % os_name
88 return False
89 if os_name != HOST_OS:
90 if os_name != 'android':
91 print "Unsupported target os %s" % os_name
92 return False
93 if not HOST_OS in ['linux']:
94 print ("Cross-compilation to %s is not supported on host os %s."
95 % (os_name, HOST_OS))
96 return False
97 if not arch in ['ia32', 'x64', 'arm', 'armv6', 'armv5te', 'arm64', 'mips',
98 'simdbc', 'simdbc64']:
99 print ("Cross-compilation to %s is not supported for architecture %s."
100 % (os_name, arch))
101 return False
102 # We have not yet tweaked the v8 dart build to work with the Android
103 # NDK/SDK, so don't try to build it.
104 if not args:
105 print "For android builds you must specify a target, such as 'runtime'."
106 return False
107 return True
108
109
110 def NotifyBuildDone(build_config, success, start):
111 if not success:
112 print "BUILD FAILED"
113
114 sys.stdout.flush()
115
116 # Display a notification if build time exceeded DART_BUILD_NOTIFICATION_DELAY.
117 notification_delay = float(
118 os.getenv('DART_BUILD_NOTIFICATION_DELAY', sys.float_info.max))
119 if (time.time() - start) < notification_delay:
120 return
121
122 if success:
123 message = 'Build succeeded.'
124 else:
125 message = 'Build failed.'
126 title = build_config
127
128 command = None
129 if HOST_OS == 'macos':
130 # Use AppleScript to display a UI non-modal notification.
131 script = 'display notification "%s" with title "%s" sound name "Glass"' % (
132 message, title)
133 command = "osascript -e '%s' &" % script
134 elif HOST_OS == 'linux':
135 if success:
136 icon = 'dialog-information'
137 else:
138 icon = 'dialog-error'
139 command = "notify-send -i '%s' '%s' '%s' &" % (icon, message, title)
140 elif HOST_OS == 'win32':
141 if success:
142 icon = 'info'
143 else:
144 icon = 'error'
145 command = ("powershell -command \""
146 "[reflection.assembly]::loadwithpartialname('System.Windows.Forms')"
147 "| Out-Null;"
148 "[reflection.assembly]::loadwithpartialname('System.Drawing')"
149 "| Out-Null;"
150 "$n = new-object system.windows.forms.notifyicon;"
151 "$n.icon = [system.drawing.systemicons]::information;"
152 "$n.visible = $true;"
153 "$n.showballoontip(%d, '%s', '%s', "
154 "[system.windows.forms.tooltipicon]::%s);\"") % (
155 5000, # Notification stays on for this many milliseconds
156 message, title, icon)
157
158 if command:
159 # Ignore return code, if this command fails, it doesn't matter.
160 os.system(command)
161
162
163 def RunGN(target_os, mode, arch):
164 gn_os = 'host' if target_os == HOST_OS else target_os
165 gn_command = [
166 'python',
167 os.path.join(DART_ROOT, 'tools', 'gn.py'),
168 '-m', mode,
169 '-a', arch,
170 '--os', gn_os,
171 '-v',
172 ]
173 process = subprocess.Popen(gn_command)
174 process.wait()
175 if process.returncode != 0:
176 print ("Tried to run GN, but it failed. Try running it manually: \n\t$ " +
177 ' '.join(gn_command))
178
179
180 def ShouldRunGN(out_dir):
181 return (not os.path.exists(out_dir) or
182 not os.path.isfile(os.path.join(out_dir, 'args.gn')))
183
184
185 def UseGoma(out_dir):
186 args_gn = os.path.join(out_dir, 'args.gn')
187 return 'use_goma = true' in open(args_gn, 'r').read()
188
189
190 # Try to start goma, but don't bail out if we can't. Instead print an error
191 # message, and let the build fail with its own error messages as well.
192 goma_started = False
193 def EnsureGomaStarted(out_dir):
194 global goma_started
195 if goma_started:
196 return True
197 args_gn_path = os.path.join(out_dir, 'args.gn')
198 goma_dir = None
199 with open(args_gn_path, 'r') as fp:
200 for line in fp:
201 if 'goma_dir' in line:
202 words = line.split()
203 goma_dir = words[2][1:-1] # goma_dir = "/path/to/goma"
204 if not goma_dir:
205 print 'Could not find goma for ' + out_dir
206 return False
207 if not os.path.exists(goma_dir) or not os.path.isdir(goma_dir):
208 print 'Could not find goma at ' + goma_dir
209 return False
210 goma_ctl = os.path.join(goma_dir, 'goma_ctl.py')
211 goma_ctl_command = [
212 'python',
213 goma_ctl,
214 'ensure_start',
215 ]
216 process = subprocess.Popen(goma_ctl_command)
217 process.wait()
218 if process.returncode != 0:
219 print ("Tried to run goma_ctl.py, but it failed. Try running it manually: "
220 + "\n\t" + ' '.join(goma_ctl_command))
221 return False
222 goma_started = True
223 return True
224
225
226 # Returns a tuple (build_config, command to run, whether goma is used)
227 def BuildOneConfig(options, targets, target_os, mode, arch):
228 build_config = utils.GetBuildConf(mode, arch, target_os)
229 out_dir = utils.GetBuildRoot(HOST_OS, mode, arch, target_os)
230 using_goma = False
231 if ShouldRunGN(out_dir):
232 RunGN(target_os, mode, arch)
233 command = ['ninja', '-C', out_dir]
234 if options.verbose:
235 command += ['-v']
236 if UseGoma(out_dir):
237 if EnsureGomaStarted(out_dir):
238 using_goma = True
239 command += [('-j%s' % str(options.j))]
240 else:
241 # If we couldn't ensure that goma is started, let the build start, but
242 # slowly so we can see any helpful error messages that pop out.
243 command += ['-j1']
244 command += targets
245 return (build_config, command, using_goma)
246
247
248 def RunOneBuildCommand(build_config, args):
249 start_time = time.time()
250 print ' '.join(args)
251 process = subprocess.Popen(args, stdin=None)
252 process.wait()
253 if process.returncode != 0:
254 NotifyBuildDone(build_config, success=False, start=start_time)
255 return 1
256 else:
257 NotifyBuildDone(build_config, success=True, start=start_time)
258
259 return 0
260
261
262 def RunOneGomaBuildCommand(args):
263 print ' '.join(args)
264 process = subprocess.Popen(args, stdin=None)
265 process.wait()
266 print (' '.join(args) + " done.")
267 return process.returncode
268
269
270 def Main():
271 starttime = time.time()
272 # Parse the options.
273 parser = BuildOptions()
274 (options, args) = parser.parse_args()
275 if not ProcessOptions(options, args):
276 parser.print_help()
277 return 1
278 # Determine which targets to build. By default we build the "all" target.
279 if len(args) == 0:
280 targets = ['all']
281 else:
282 targets = args
283
284 # Build all targets for each requested configuration.
285 configs = []
286 for target_os in options.os:
287 for mode in options.mode:
288 for arch in options.arch:
289 configs.append(BuildOneConfig(options, targets, target_os, mode, arch))
290
291 # Build regular configs.
292 goma_builds = []
293 for (build_config, args, goma) in configs:
294 if args is None:
295 return 1
296 if goma:
297 goma_builds.append(args)
298 elif RunOneBuildCommand(build_config, args) != 0:
299 return 1
300
301 # Run goma builds in parallel.
302 pool = multiprocessing.Pool(multiprocessing.cpu_count())
303 results = pool.map(RunOneGomaBuildCommand, goma_builds, chunksize=1)
304 for r in results:
305 if r != 0:
306 return 1
307
308 endtime = time.time()
309 print ("The build took %.3f seconds" % (endtime - starttime))
310 return 0
311
312
313 if __name__ == '__main__':
314 sys.exit(Main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698