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

Side by Side Diff: tools/build.py

Issue 2996903002: [infra] Translate _sources.gypi files to _sources.gni files (Closed)
Patch Set: Fix script Created 3 years, 4 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 | « tools/android_link.py ('k') | tools/gypi_to_gn.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 3 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
4 # for details. All rights reserved. Use of this source code is governed by a 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. 5 # BSD-style license that can be found in the LICENSE file.
6 #
7 6
8 import optparse 7 # This script simply forwards to tools/ninja.py. It is retained simply to avoid
9 import os 8 # breaking various workflows.
10 import re 9
11 import shutil 10 import ninja
12 import subprocess
13 import sys 11 import sys
14 import time
15 import utils
16
17 HOST_OS = utils.GuessOS()
18 HOST_ARCH = utils.GuessArchitecture()
19 HOST_CPUS = utils.GuessCpus()
20 SCRIPT_DIR = os.path.dirname(sys.argv[0])
21 DART_ROOT = os.path.realpath(os.path.join(SCRIPT_DIR, '..'))
22 THIRD_PARTY_ROOT = os.path.join(DART_ROOT, 'third_party')
23
24 arm_cc_error = """
25 Couldn't find the arm cross compiler.
26 To make sure that you have the arm cross compilation tools installed, run:
27
28 $ wget http://src.chromium.org/chrome/trunk/src/build/install-build-deps.sh
29 OR
30 $ svn co http://src.chromium.org/chrome/trunk/src/build; cd build
31 Then,
32 $ chmod u+x install-build-deps.sh
33 $ ./install-build-deps.sh --arm --no-chromeos-fonts
34 """
35 DEFAULT_ARM_CROSS_COMPILER_PATH = '/usr/bin'
36
37 usage = """\
38 usage: %%prog [options] [targets]
39
40 This script runs 'make' in the *current* directory. So, run it from
41 the Dart repo root,
42
43 %s ,
44
45 unless you really intend to use a non-default Makefile.""" % DART_ROOT
46
47 DART_USE_GYP = "DART_USE_GYP"
48
49
50 def use_gyp():
51 return DART_USE_GYP in os.environ
52
53
54 def BuildOptions():
55 result = optparse.OptionParser(usage=usage)
56 result.add_option("-m", "--mode",
57 help='Build variants (comma-separated).',
58 metavar='[all,debug,release,product]',
59 default='debug')
60 result.add_option("-v", "--verbose",
61 help='Verbose output.',
62 default=False, action="store_true")
63 result.add_option("-a", "--arch",
64 help='Target architectures (comma-separated).',
65 metavar='[all,ia32,x64,simarm,arm,simarmv6,armv6,simarmv5te,armv5te,'
66 'simarm64,arm64,simdbc,armsimdbc]',
67 default=utils.GuessArchitecture())
68 result.add_option("--os",
69 help='Target OSs (comma-separated).',
70 metavar='[all,host,android]',
71 default='host')
72 result.add_option("-t", "--toolchain",
73 help='Cross-compiler toolchain path',
74 default=None)
75 result.add_option("-j",
76 help='The number of parallel jobs to run.',
77 metavar=HOST_CPUS,
78 default=str(HOST_CPUS))
79 (vs_directory, vs_executable) = utils.GuessVisualStudioPath()
80 result.add_option("--devenv",
81 help='Path containing devenv.com on Windows',
82 default=vs_directory)
83 result.add_option("--executable",
84 help='Name of the devenv.com/msbuild executable on Windows (varies for '
85 'different versions of Visual Studio)',
86 default=vs_executable)
87 result.add_option("--gyp",
88 help='Build with gyp.',
89 default=use_gyp(),
90 action='store_true')
91 return result
92
93
94 def ProcessOsOption(os_name):
95 if os_name == 'host':
96 return HOST_OS
97 return os_name
98
99
100 def ProcessOptions(options, args):
101 if options.arch == 'all':
102 options.arch = 'ia32,x64,simarm,simarm64,simdbc64'
103 if options.mode == 'all':
104 options.mode = 'debug,release,product'
105 if options.os == 'all':
106 options.os = 'host,android'
107 options.mode = options.mode.split(',')
108 options.arch = options.arch.split(',')
109 options.os = options.os.split(',')
110 if not options.gyp and options.toolchain != None:
111 print "The --toolchain flag is only supported by the gyp build."
112 print "When using the GN build, set the toolchain and sysroot using gn.py."
113 return False
114 for mode in options.mode:
115 if not mode in ['debug', 'release', 'product']:
116 print "Unknown mode %s" % mode
117 return False
118 for arch in options.arch:
119 archs = ['ia32', 'x64', 'simarm', 'arm', 'simarmv6', 'armv6',
120 'simarmv5te', 'armv5te', 'simarm64', 'arm64',
121 'simdbc', 'simdbc64', 'armsimdbc', 'armsimdbc64']
122 if not arch in archs:
123 print "Unknown arch %s" % arch
124 return False
125 options.os = [ProcessOsOption(os_name) for os_name in options.os]
126 for os_name in options.os:
127 if not os_name in ['android', 'freebsd', 'linux', 'macos', 'win32']:
128 print "Unknown os %s" % os_name
129 return False
130 if os_name != HOST_OS:
131 if os_name != 'android':
132 print "Unsupported target os %s" % os_name
133 return False
134 if not HOST_OS in ['linux', 'macos']:
135 print ("Cross-compilation to %s is not supported on host os %s."
136 % (os_name, HOST_OS))
137 return False
138 if not arch in ['ia32', 'x64', 'arm', 'armv6', 'armv5te', 'arm64',
139 'simdbc', 'simdbc64']:
140 print ("Cross-compilation to %s is not supported for architecture %s."
141 % (os_name, arch))
142 return False
143 # We have not yet tweaked the v8 dart build to work with the Android
144 # NDK/SDK, so don't try to build it.
145 if not args:
146 print "For android builds you must specify a target, such as 'runtime'."
147 return False
148 return True
149
150
151 def GetToolchainPrefix(target_os, arch, options):
152 if options.toolchain != None:
153 return options.toolchain
154
155 if target_os == 'android':
156 android_toolchain = GetAndroidToolchainDir(HOST_OS, arch)
157 if arch == 'arm' or arch == 'simdbc':
158 return os.path.join(android_toolchain, 'arm-linux-androideabi')
159 if arch == 'arm64' or arch == 'simdbc64':
160 return os.path.join(android_toolchain, 'aarch64-linux-android')
161 if arch == 'ia32':
162 return os.path.join(android_toolchain, 'i686-linux-android')
163 if arch == 'x64':
164 return os.path.join(android_toolchain, 'x86_64-linux-android')
165
166 # If no cross compiler is specified, only try to figure one out on Linux.
167 if not HOST_OS in ['linux']:
168 raise Exception('Unless --toolchain is used cross-building is only '
169 'supported on Linux.')
170
171 # For ARM Linux, by default use the Linux distribution's cross-compiler.
172 if arch == 'arm' or arch == 'armsimdbc':
173 # To use a non-hf compiler, specify on the command line with --toolchain.
174 return (DEFAULT_ARM_CROSS_COMPILER_PATH + "/arm-linux-gnueabihf")
175 if arch == 'arm64':
176 return (DEFAULT_ARM_CROSS_COMPILER_PATH + "/aarch64-linux-gnu")
177
178 return None
179
180
181 def SetTools(arch, target_os, options):
182 toolsOverride = None
183
184 toolchainprefix = GetToolchainPrefix(target_os, arch, options)
185
186 # Override the Android toolchain's linker to handle some complexity in the
187 # linker arguments that gyp has trouble with.
188 linker = ""
189 if target_os == 'android':
190 linker = os.path.join(DART_ROOT, 'tools', 'android_link.py')
191 elif toolchainprefix:
192 linker = toolchainprefix + "-g++"
193
194 if toolchainprefix:
195 toolsOverride = {
196 "CC.target" : toolchainprefix + "-gcc",
197 "CXX.target" : toolchainprefix + "-g++",
198 "AR.target" : toolchainprefix + "-ar",
199 "LINK.target": linker,
200 "NM.target" : toolchainprefix + "-nm",
201 }
202 return toolsOverride
203
204
205 def CheckDirExists(path, docstring):
206 if not os.path.isdir(path):
207 raise Exception('Could not find %s directory %s'
208 % (docstring, path))
209
210
211 def GetAndroidToolchainDir(host_os, target_arch):
212 global THIRD_PARTY_ROOT
213 if host_os not in ['linux']:
214 raise Exception('Unsupported host os %s' % host_os)
215 if target_arch not in ['ia32', 'x64', 'arm', 'arm64', 'simdbc', 'simdbc64']:
216 raise Exception('Unsupported target architecture %s' % target_arch)
217
218 # Set up path to the Android NDK.
219 CheckDirExists(THIRD_PARTY_ROOT, 'third party tools')
220 android_tools = os.path.join(THIRD_PARTY_ROOT, 'android_tools')
221 CheckDirExists(android_tools, 'Android tools')
222 android_ndk_root = os.path.join(android_tools, 'ndk')
223 CheckDirExists(android_ndk_root, 'Android NDK')
224
225 # Set up the directory of the Android NDK cross-compiler toolchain.
226 toolchain_arch = 'arm-linux-androideabi-4.9'
227 if target_arch == 'arm64' or target_arch == 'simdbc64':
228 toolchain_arch = 'aarch64-linux-android-4.9'
229 if target_arch == 'ia32':
230 toolchain_arch = 'x86-4.9'
231 if target_arch == 'x64':
232 toolchain_arch = 'x86_64-4.9'
233 toolchain_dir = 'linux-x86_64'
234 android_toolchain = os.path.join(android_ndk_root,
235 'toolchains', toolchain_arch,
236 'prebuilt', toolchain_dir, 'bin')
237 CheckDirExists(android_toolchain, 'Android toolchain')
238
239 return android_toolchain
240
241
242 def Execute(args):
243 process = subprocess.Popen(args)
244 process.wait()
245 if process.returncode != 0:
246 raise Exception(args[0] + " failed")
247
248
249 def CurrentDirectoryBaseName():
250 """Returns the name of the current directory"""
251 return os.path.relpath(os.curdir, start=os.pardir)
252
253
254 def FilterEmptyXcodebuildSections(process):
255 """
256 Filter output from xcodebuild so empty sections are less verbose.
257
258 The output from xcodebuild looks like this:
259
260 Build settings from command line:
261 SYMROOT = .../xcodebuild
262
263 === BUILD TARGET samples OF PROJECT dart WITH CONFIGURATION ...
264
265 Check dependencies
266
267 === BUILD AGGREGATE TARGET upload_sdk OF PROJECT dart WITH CONFIGURATION ...
268
269 Check dependencies
270
271 PhaseScriptExecution "Action \"upload_sdk_py\"" xcodebuild/dart.build/...
272 cd ...
273 /bin/sh -c .../xcodebuild/dart.build/ReleaseIA32/upload_sdk.build/...
274
275
276 ** BUILD SUCCEEDED **
277
278 """
279
280 def is_empty_chunk(input):
281 empty_chunk = ['', 'Check dependencies', '']
282 return not input or (len(input) == 4 and input[1:] == empty_chunk)
283
284 def unbuffered(callable):
285 # Use iter to disable buffering in for-in.
286 return iter(callable, '')
287
288 section = None
289 chunk = []
290 # Is stdout a terminal which supports colors?
291 is_fancy_tty = False
292 clr_eol = None
293 if sys.stdout.isatty():
294 term = os.getenv('TERM', 'dumb')
295 # The capability "clr_eol" means clear the line from cursor to end
296 # of line. See man pages for tput and terminfo.
297 try:
298 with open('/dev/null', 'a') as dev_null:
299 clr_eol = subprocess.check_output(['tput', '-T' + term, 'el'],
300 stderr=dev_null)
301 if clr_eol:
302 is_fancy_tty = True
303 except subprocess.CalledProcessError:
304 is_fancy_tty = False
305 except AttributeError:
306 is_fancy_tty = False
307 pattern = re.compile(r'=== BUILD.* TARGET (.*) OF PROJECT (.*) WITH ' +
308 r'CONFIGURATION (.*) ===')
309 has_interesting_info = False
310 for line in unbuffered(process.stdout.readline):
311 line = line.rstrip()
312 if line.startswith('=== BUILD ') or line.startswith('** BUILD '):
313 has_interesting_info = False
314 section = line
315 if is_fancy_tty:
316 match = re.match(pattern, section)
317 if match:
318 section = '%s/%s/%s' % (
319 match.group(3), match.group(2), match.group(1))
320 # Truncate to avoid extending beyond 80 columns.
321 section = section[:80]
322 # If stdout is a terminal, emit "progress" information. The
323 # progress information is the first line of the current chunk.
324 # After printing the line, move the cursor back to the
325 # beginning of the line. This has two effects: First, if the
326 # chunk isn't empty, the first line will be overwritten
327 # (avoiding duplication). Second, the next segment line will
328 # overwrite it too avoid long scrollback. clr_eol ensures
329 # that there is no trailing garbage when a shorter line
330 # overwrites a longer line.
331 print '%s%s\r' % (clr_eol, section),
332 chunk = []
333 if not section or has_interesting_info:
334 print line
335 else:
336 length = len(chunk)
337 if length == 2 and line != 'Check dependencies':
338 has_interesting_info = True
339 elif (length == 1 or length == 3) and line:
340 has_interesting_info = True
341 elif length > 3:
342 has_interesting_info = True
343 if has_interesting_info:
344 print '\n'.join(chunk)
345 chunk = []
346 else:
347 chunk.append(line)
348 if not is_empty_chunk(chunk):
349 print '\n'.join(chunk)
350
351
352 def NotifyBuildDone(build_config, success, start):
353 if not success:
354 print "BUILD FAILED"
355
356 sys.stdout.flush()
357
358 # Display a notification if build time exceeded DART_BUILD_NOTIFICATION_DELAY.
359 notification_delay = float(
360 os.getenv('DART_BUILD_NOTIFICATION_DELAY', sys.float_info.max))
361 if (time.time() - start) < notification_delay:
362 return
363
364 if success:
365 message = 'Build succeeded.'
366 else:
367 message = 'Build failed.'
368 title = build_config
369
370 command = None
371 if HOST_OS == 'macos':
372 # Use AppleScript to display a UI non-modal notification.
373 script = 'display notification "%s" with title "%s" sound name "Glass"' % (
374 message, title)
375 command = "osascript -e '%s' &" % script
376 elif HOST_OS == 'linux':
377 if success:
378 icon = 'dialog-information'
379 else:
380 icon = 'dialog-error'
381 command = "notify-send -i '%s' '%s' '%s' &" % (icon, message, title)
382 elif HOST_OS == 'win32':
383 if success:
384 icon = 'info'
385 else:
386 icon = 'error'
387 command = ("powershell -command \""
388 "[reflection.assembly]::loadwithpartialname('System.Windows.Forms')"
389 "| Out-Null;"
390 "[reflection.assembly]::loadwithpartialname('System.Drawing')"
391 "| Out-Null;"
392 "$n = new-object system.windows.forms.notifyicon;"
393 "$n.icon = [system.drawing.systemicons]::information;"
394 "$n.visible = $true;"
395 "$n.showballoontip(%d, '%s', '%s', "
396 "[system.windows.forms.tooltipicon]::%s);\"") % (
397 5000, # Notification stays on for this many milliseconds
398 message, title, icon)
399
400 if command:
401 # Ignore return code, if this command fails, it doesn't matter.
402 os.system(command)
403
404
405 def RunGN(target_os, mode, arch):
406 gn_os = 'host' if target_os == HOST_OS else target_os
407 gn_command = [
408 'python',
409 os.path.join(DART_ROOT, 'tools', 'gn.py'),
410 '-m', mode,
411 '-a', arch,
412 '--os', gn_os,
413 '-v',
414 ]
415 process = subprocess.Popen(gn_command)
416 process.wait()
417 if process.returncode != 0:
418 print ("Tried to run GN, but it failed. Try running it manually: \n\t$ " +
419 ' '.join(gn_command))
420
421
422 def ShouldRunGN(out_dir):
423 return (not os.path.exists(out_dir) or
424 not os.path.isfile(os.path.join(out_dir, 'args.gn')))
425
426
427 def UseGoma(out_dir):
428 args_gn = os.path.join(out_dir, 'args.gn')
429 return 'use_goma = true' in open(args_gn, 'r').read()
430
431
432 # Try to start goma, but don't bail out if we can't. Instead print an error
433 # message, and let the build fail with its own error messages as well.
434 def EnsureGomaStarted(out_dir):
435 args_gn_path = os.path.join(out_dir, 'args.gn')
436 goma_dir = None
437 with open(args_gn_path, 'r') as fp:
438 for line in fp:
439 if 'goma_dir' in line:
440 words = line.split()
441 goma_dir = words[2][1:-1] # goma_dir = "/path/to/goma"
442 if not goma_dir:
443 print 'Could not find goma for ' + out_dir
444 return False
445 if not os.path.exists(goma_dir) or not os.path.isdir(goma_dir):
446 print 'Could not find goma at ' + goma_dir
447 return False
448 goma_ctl = os.path.join(goma_dir, 'goma_ctl.py')
449 goma_ctl_command = [
450 'python',
451 goma_ctl,
452 'ensure_start',
453 ]
454 process = subprocess.Popen(goma_ctl_command)
455 process.wait()
456 if process.returncode != 0:
457 print ("Tried to run goma_ctl.py, but it failed. Try running it manually: "
458 + "\n\t" + ' '.join(goma_ctl_command))
459 return False
460 return True
461
462
463
464 def BuildNinjaCommand(options, target, target_os, mode, arch):
465 out_dir = utils.GetBuildRoot(HOST_OS, mode, arch, target_os)
466 if ShouldRunGN(out_dir):
467 RunGN(target_os, mode, arch)
468 command = ['ninja', '-C', out_dir]
469 if options.verbose:
470 command += ['-v']
471 if UseGoma(out_dir):
472 if EnsureGomaStarted(out_dir):
473 command += ['-j1000']
474 else:
475 # If we couldn't ensure that goma is started, let the build start, but
476 # slowly so we can see any helpful error messages that pop out.
477 command += ['-j1']
478 command += [target]
479 return command
480
481
482 filter_xcodebuild_output = False
483 def BuildOneConfig(options, target, target_os, mode, arch):
484 global filter_xcodebuild_output
485 start_time = time.time()
486 args = []
487 build_config = utils.GetBuildConf(mode, arch, target_os)
488 if not options.gyp:
489 args = BuildNinjaCommand(options, target, target_os, mode, arch)
490 else:
491 os.environ['DART_BUILD_MODE'] = mode
492 if HOST_OS == 'macos':
493 filter_xcodebuild_output = True
494 project_file = 'dart.xcodeproj'
495 if os.path.exists('dart-%s.gyp' % CurrentDirectoryBaseName()):
496 project_file = 'dart-%s.xcodeproj' % CurrentDirectoryBaseName()
497 if target == 'all':
498 target = 'All'
499 args = ['xcodebuild',
500 '-project',
501 project_file,
502 '-target',
503 target,
504 '-configuration',
505 build_config,
506 'SYMROOT=%s' % os.path.abspath('xcodebuild')
507 ]
508 elif HOST_OS == 'win32':
509 project_file = 'dart.sln'
510 if os.path.exists('dart-%s.gyp' % CurrentDirectoryBaseName()):
511 project_file = 'dart-%s.sln' % CurrentDirectoryBaseName()
512 # Select a platform suffix to pass to devenv.
513 if arch == 'ia32':
514 platform_suffix = 'Win32'
515 elif arch == 'x64':
516 platform_suffix = 'x64'
517 else:
518 print 'Unsupported arch for MSVC build: %s' % arch
519 return 1
520 config_name = '%s|%s' % (build_config, platform_suffix)
521 if target == 'all':
522 args = [options.devenv + os.sep + options.executable,
523 '/build',
524 config_name,
525 project_file
526 ]
527 else:
528 args = [options.devenv + os.sep + options.executable,
529 '/build',
530 config_name,
531 '/project',
532 target,
533 project_file
534 ]
535 else:
536 make = 'make'
537 if HOST_OS == 'freebsd':
538 make = 'gmake'
539 # work around lack of flock
540 os.environ['LINK'] = '$(CXX)'
541 args = [make,
542 '-j',
543 options.j,
544 'BUILDTYPE=' + build_config,
545 ]
546 if target_os != HOST_OS:
547 args += ['builddir_name=' + utils.GetBuildDir(HOST_OS)]
548 if options.verbose:
549 args += ['V=1']
550
551 args += [target]
552
553 toolsOverride = None
554 if override_tools:
555 toolsOverride = SetTools(arch, target_os, options)
556 if toolsOverride:
557 for k, v in toolsOverride.iteritems():
558 args.append( k + "=" + v)
559 if options.verbose:
560 print k + " = " + v
561 if not os.path.isfile(toolsOverride['CC.target']):
562 if arch == 'arm':
563 print arm_cc_error
564 else:
565 print "Couldn't find compiler: %s" % toolsOverride['CC.target']
566 return 1
567
568 print ' '.join(args)
569 process = None
570 if filter_xcodebuild_output:
571 process = subprocess.Popen(args,
572 stdin=None,
573 bufsize=1, # Line buffered.
574 stdout=subprocess.PIPE,
575 stderr=subprocess.STDOUT)
576 FilterEmptyXcodebuildSections(process)
577 else:
578 process = subprocess.Popen(args, stdin=None)
579 process.wait()
580 if process.returncode != 0:
581 NotifyBuildDone(build_config, success=False, start=start_time)
582 return 1
583 else:
584 NotifyBuildDone(build_config, success=True, start=start_time)
585
586 return 0
587
588
589 def Main():
590 utils.ConfigureJava()
591 # Parse the options.
592 parser = BuildOptions()
593 (options, args) = parser.parse_args()
594 if not ProcessOptions(options, args):
595 parser.print_help()
596 return 1
597 # Determine which targets to build. By default we build the "all" target.
598 if len(args) == 0:
599 targets = ['all']
600 else:
601 targets = args
602
603 # Build all targets for each requested configuration.
604 for target in targets:
605 for target_os in options.os:
606 for mode in options.mode:
607 for arch in options.arch:
608 if BuildOneConfig(options, target, target_os,
609 mode, arch) != 0:
610 return 1
611
612 return 0
613
614 12
615 if __name__ == '__main__': 13 if __name__ == '__main__':
616 sys.exit(Main()) 14 sys.exit(ninja.Main())
OLDNEW
« no previous file with comments | « tools/android_link.py ('k') | tools/gypi_to_gn.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698