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

Side by Side Diff: tools/mb/mb.py

Issue 1168513006: Make swarming work w/ GN (kinda) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 6 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
« base/BUILD.gn ('K') | « base/BUILD.gn ('k') | 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
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved. 2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """MB - the Meta-Build wrapper around GYP and GN 6 """MB - the Meta-Build wrapper around GYP and GN
7 7
8 MB is a wrapper script for GYP and GN that can be used to generate build files 8 MB is a wrapper script for GYP and GN that can be used to generate build files
9 for sets of canned configurations and analyze them. 9 for sets of canned configurations and analyze them.
10 """ 10 """
11 11
12 from __future__ import print_function 12 from __future__ import print_function
13 13
14 import argparse 14 import argparse
15 import ast 15 import ast
16 import json 16 import json
17 import os 17 import os
18 import pipes 18 import pipes
19 import pprint
19 import shlex 20 import shlex
20 import shutil 21 import shutil
21 import sys 22 import sys
22 import subprocess 23 import subprocess
23 import tempfile 24 import tempfile
24 25
25 26
26 def main(args): 27 def main(args):
27 mbw = MetaBuildWrapper() 28 mbw = MetaBuildWrapper()
28 mbw.ParseArgs(args) 29 mbw.ParseArgs(args)
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 'as a JSON object.') 86 'as a JSON object.')
86 subp.set_defaults(func=self.CmdAnalyze) 87 subp.set_defaults(func=self.CmdAnalyze)
87 88
88 subp = subps.add_parser('gen', 89 subp = subps.add_parser('gen',
89 help='generate a new set of build files') 90 help='generate a new set of build files')
90 AddCommonOptions(subp) 91 AddCommonOptions(subp)
91 subp.add_argument('path', type=str, nargs=1, 92 subp.add_argument('path', type=str, nargs=1,
92 help='path to generate build into') 93 help='path to generate build into')
93 subp.set_defaults(func=self.CmdGen) 94 subp.set_defaults(func=self.CmdGen)
94 95
96 subp = subps.add_parser('isolate',
97 help='build isolates')
98 AddCommonOptions(subp)
99 subp.add_argument('path', type=str, nargs=1,
100 help='path build was generated into.')
101 subp.add_argument('input_path', nargs=1,
102 help='path to a file containing the input arguments '
103 'as a JSON object.')
104 subp.add_argument('output_path', nargs=1,
105 help='path to a file containing the output arguments '
106 'as a JSON object.')
107 subp.set_defaults(func=self.CmdIsolate)
108
95 subp = subps.add_parser('lookup', 109 subp = subps.add_parser('lookup',
96 help='look up the command for a given config or ' 110 help='look up the command for a given config or '
97 'builder') 111 'builder')
98 AddCommonOptions(subp) 112 AddCommonOptions(subp)
99 subp.set_defaults(func=self.CmdLookup) 113 subp.set_defaults(func=self.CmdLookup)
100 114
101 subp = subps.add_parser('validate', 115 subp = subps.add_parser('validate',
102 help='validate the config file') 116 help='validate the config file')
103 AddCommonOptions(subp) 117 AddCommonOptions(subp)
104 subp.set_defaults(func=self.CmdValidate) 118 subp.set_defaults(func=self.CmdValidate)
(...skipping 17 matching lines...) Expand all
122 136
123 def CmdGen(self): 137 def CmdGen(self):
124 vals = self.GetConfig() 138 vals = self.GetConfig()
125 if vals['type'] == 'gn': 139 if vals['type'] == 'gn':
126 return self.RunGNGen(self.args.path[0], vals) 140 return self.RunGNGen(self.args.path[0], vals)
127 if vals['type'] == 'gyp': 141 if vals['type'] == 'gyp':
128 return self.RunGYPGen(self.args.path[0], vals) 142 return self.RunGYPGen(self.args.path[0], vals)
129 143
130 raise MBErr('Unknown meta-build type "%s"' % vals['type']) 144 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
131 145
146 def CmdIsolate(self):
147 vals = self.GetConfig()
148 if vals['type'] == 'gn':
149 return self.RunGNIsolate(vals)
150 if vals['type'] == 'gyp':
151 return 0 # .isolated files are generated during the compile in GYP.
M-A Ruel 2015/06/03 15:47:18 No, the .isolated.gen.json files are generated dur
Dirk Pranke 2015/06/03 17:15:49 Will update.
152 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
153
132 def CmdLookup(self): 154 def CmdLookup(self):
133 vals = self.GetConfig() 155 vals = self.GetConfig()
134 if vals['type'] == 'gn': 156 if vals['type'] == 'gn':
135 cmd = self.GNCmd('gen', '<path>', vals['gn_args']) 157 cmd = self.GNCmd('gen', '<path>', vals['gn_args'])
136 elif vals['type'] == 'gyp': 158 elif vals['type'] == 'gyp':
137 cmd = self.GYPCmd('<path>', vals['gyp_defines'], vals['gyp_config']) 159 cmd = self.GYPCmd('<path>', vals['gyp_defines'], vals['gyp_config'])
138 else: 160 else:
139 raise MBErr('Unknown meta-build type "%s"' % vals['type']) 161 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
140 162
141 self.PrintCmd(cmd) 163 self.PrintCmd(cmd)
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
360 ret, _, _ = self.Run(cmd) 382 ret, _, _ = self.Run(cmd)
361 if not ret and self.args.verbose: 383 if not ret and self.args.verbose:
362 outp = json.loads(self.ReadFile(self.args.output_path[0])) 384 outp = json.loads(self.ReadFile(self.args.output_path[0]))
363 self.Print() 385 self.Print()
364 self.Print('analyze output:') 386 self.Print('analyze output:')
365 self.PrintJSON(inp) 387 self.PrintJSON(inp)
366 self.Print() 388 self.Print()
367 389
368 return ret 390 return ret
369 391
392 def RunGNIsolate(self, vals):
393 build_path = self.args.path[0]
394 inp = self.GetIsolateInput()
395 if self.args.verbose:
396 self.Print()
397 self.Print('isolate input:')
398 self.PrintJSON(inp)
399 self.Print()
400 output_path = self.args.output_path[0]
401
402 for target in inp['targets']:
403 runtime_deps_path = self.ToAbsPath(build_path, target + '.runtime_deps')
404
405 if not self.Exists(runtime_deps_path):
406 self.WriteFailureAndRaise('"%s" does not exist' % runtime_deps_path,
407 output_path)
408
409 command, extra_files, read_only = self.GetIsolateCommand(target, vals)
410
411 runtime_deps = self.ReadFile(runtime_deps_path).splitlines()
412
413
414 isolate_path = self.ToAbsPath(build_path, target + '.isolate')
415 self.WriteFile(isolate_path,
416 pprint.pformat({
417 'variables': {
418 'command': command,
419 'files': sorted(runtime_deps + extra_files),
420 'read_only': read_only,
421 }
422 }) + '\n')
423
424 self.WriteJSON(
425 {
426 'args': [
427 '--isolated',
428 self.ToSrcRelPath('%s/%s.isolated' % (build_path, target)),
429 '--isolate',
430 self.ToSrcRelPath('%s/%s.isolate' % (build_path, target)),
431 ],
432 'dir': self.chromium_src_dir,
433 'version': 1,
434 },
435 isolate_path + '.gen.json',
436 )
437
438 return 0
439
440 def GetIsolateInput(self):
M-A Ruel 2015/06/03 15:47:19 ReadIsolateInput ? And it's not really an isolate,
Dirk Pranke 2015/06/03 17:15:50 It was called GetIsolateInput because we're gettin
441 path = self.args.input_path[0]
442 output_path = self.args.output_path[0]
443 if not self.Exists(path):
444 self.WriteFailureAndRaise('"%s" does not exist' % path, output_path)
445
446 try:
447 inp = json.loads(self.ReadFile(path))
448 except Exception as e:
449 self.WriteFailureAndRaise('Failed to read JSON input from "%s": %s' %
450 (path, e), output_path)
451 if not 'targets' in inp:
452 self.WriteFailureAndRaise('input file is missing a "targets" key',
453 output_path)
454
455 return inp
456
457 def GetIsolateCommand(self, target, vals):
Dirk Pranke 2015/06/03 02:24:54 This whole function is dodgy and needs to be repla
458 output_path = self.args.output_path[0]
459
460 extra_files = []
461
462 # TODO(dpranke): We should probably pull this from
M-A Ruel 2015/06/03 15:47:18 Yes but obviously this only means that it works wh
Dirk Pranke 2015/06/03 17:15:49 Well, it would only work w/ things using the files
463 # the test list info in //testing/buildbot/*.json,
464 # and assert that the test has can_use_on_swarming_builders: True,
465 # but we hardcode it here for now.
466 test_type = {}.get(target, 'gtest_test')
467 read_only = {}.get(target, 1)
M-A Ruel 2015/06/03 15:47:18 The only test that can't run read_only is unit_tes
Dirk Pranke 2015/06/03 17:15:49 Ok, I'll keep that in mind.
468
469 # This needs to mirror the settings in //build/config/ui.gni:
470 # use_x11 = is_linux && !use_ozone.
471 # TODO(dpranke): Figure out how to keep this in sync better.
472 use_x11 = (sys.platform == 'linux2' and
473 not 'target_os="android"' in vals['gn_args'] and
474 not 'use_ozone=true' in vals['gn_args'])
475
476 asan = 'is_asan=true' in vals['gn_args']
477 msan = 'is_msan=true' in vals['gn_args']
478 tsan = 'is_tsan=true' in vals['gn_args']
479
480 executable_suffix = '.exe' if sys.platform == 'win32' else ''
481
482 if test_type == 'gtest_test':
483 extra_files.append('../../testing/test_env.py')
484
485 if use_x11:
486 extra_files.append('xdisplaycheck')
487 extra_files.append('../../testing/xvfb.py')
488
489 cmdline = [
490 '../../testing/xvfb.py',
491 '.',
492 './' + str(target),
493 '--brave-new-test-launcher',
494 '--test-launcher-bot-mode',
495 '--asan=%d' % asan,
496 '--msan=%d' % msan,
497 '--tsan=%d' % tsan,
498 ]
499 else:
500 cmdline = [
501 '../../testing/test_env.py',
502 '.',
503 './' + str(target) + executable_suffix,
504 '--brave-new-test-launcher',
505 '--test-launcher-bot-mode',
506 '--asan=%d' % asan,
507 '--msan=%d' % msan,
508 '--tsan=%d' % tsan,
509 ]
510 else:
511 # TODO(dpranke): Handle script_tests and other types of
512 # swarmed tests.
513 self.WriteFailureAndRaise('unknown test type "%s" for %s' %
514 (test_type, target),
515 output_path)
516
517
518 return cmdline, extra_files, read_only
519
520 def ToAbsPath(self, build_path, relpath):
521 return os.path.join(self.chromium_src_dir,
522 self.ToSrcRelPath(build_path),
523 relpath)
524
370 def ToSrcRelPath(self, path): 525 def ToSrcRelPath(self, path):
371 """Returns a relative path from the top of the repo.""" 526 """Returns a relative path from the top of the repo."""
372 # TODO: Support normal paths in addition to source-absolute paths. 527 # TODO: Support normal paths in addition to source-absolute paths.
373 assert(path.startswith('//')) 528 assert(path.startswith('//'))
374 return path[2:] 529 return path[2:]
375 530
376 def ParseGYPConfigPath(self, path): 531 def ParseGYPConfigPath(self, path):
377 rpath = self.ToSrcRelPath(path) 532 rpath = self.ToSrcRelPath(path)
378 output_dir, _, config = rpath.rpartition('/') 533 output_dir, _, config = rpath.rpartition('/')
379 self.CheckGYPConfigIsSupported(config, path) 534 self.CheckGYPConfigIsSupported(config, path)
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 731
577 if __name__ == '__main__': 732 if __name__ == '__main__':
578 try: 733 try:
579 sys.exit(main(sys.argv[1:])) 734 sys.exit(main(sys.argv[1:]))
580 except MBErr as e: 735 except MBErr as e:
581 print(e) 736 print(e)
582 sys.exit(1) 737 sys.exit(1)
583 except KeyboardInterrupt: 738 except KeyboardInterrupt:
584 print("interrupted, exiting", stream=sys.stderr) 739 print("interrupted, exiting", stream=sys.stderr)
585 sys.exit(130) 740 sys.exit(130)
OLDNEW
« base/BUILD.gn ('K') | « base/BUILD.gn ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698