OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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) |
OLD | NEW |