OLD | NEW |
---|---|
(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 utils.ConfigureJava() | |
rmacnak
2017/02/27 18:46:31
Probably leftover from the Java-based analyzer and
zra
2017/02/27 18:54:39
Done.
| |
273 # Parse the options. | |
274 parser = BuildOptions() | |
275 (options, args) = parser.parse_args() | |
276 if not ProcessOptions(options, args): | |
277 parser.print_help() | |
278 return 1 | |
279 # Determine which targets to build. By default we build the "all" target. | |
280 if len(args) == 0: | |
281 targets = ['all'] | |
282 else: | |
283 targets = args | |
284 | |
285 # Build all targets for each requested configuration. | |
286 configs = [] | |
287 for target_os in options.os: | |
288 for mode in options.mode: | |
289 for arch in options.arch: | |
290 configs.append(BuildOneConfig(options, targets, target_os, mode, arch)) | |
291 | |
292 # Build regular configs. | |
293 goma_builds = [] | |
294 for (build_config, args, goma) in configs: | |
295 if args is None: | |
296 return 1 | |
297 if goma: | |
298 goma_builds.append(args) | |
299 elif RunOneBuildCommand(build_config, args) != 0: | |
300 return 1 | |
301 | |
302 # Run goma builds in parallel. | |
303 pool = multiprocessing.Pool(multiprocessing.cpu_count()) | |
304 results = pool.map(RunOneGomaBuildCommand, goma_builds, chunksize=1) | |
305 for r in results: | |
306 if r != 0: | |
307 return 1 | |
308 | |
309 endtime = time.time() | |
310 print ("The build took %.3f seconds" % (endtime - starttime)) | |
311 return 0 | |
312 | |
313 | |
314 if __name__ == '__main__': | |
315 sys.exit(Main()) | |
OLD | NEW |