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

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

Issue 1370373005: Add MB commands for generating isolates and running them. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add some basic unit tests for coverage of isolate and run Created 5 years, 2 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 | tools/mb/mb_unittest.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 # 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 """
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 subp.add_argument('-v', '--verbose', action='store_true', 68 subp.add_argument('-v', '--verbose', action='store_true',
69 help='verbose logging') 69 help='verbose logging')
70 70
71 parser = argparse.ArgumentParser(prog='mb') 71 parser = argparse.ArgumentParser(prog='mb')
72 subps = parser.add_subparsers() 72 subps = parser.add_subparsers()
73 73
74 subp = subps.add_parser('analyze', 74 subp = subps.add_parser('analyze',
75 help='analyze whether changes to a set of files ' 75 help='analyze whether changes to a set of files '
76 'will cause a set of binaries to be rebuilt.') 76 'will cause a set of binaries to be rebuilt.')
77 AddCommonOptions(subp) 77 AddCommonOptions(subp)
78 subp.add_argument('--swarming-targets-file',
79 help='save runtime dependencies for targets listed '
80 'in file.')
81 subp.add_argument('path', nargs=1, 78 subp.add_argument('path', nargs=1,
82 help='path build was generated into.') 79 help='path build was generated into.')
83 subp.add_argument('input_path', nargs=1, 80 subp.add_argument('input_path', nargs=1,
84 help='path to a file containing the input arguments ' 81 help='path to a file containing the input arguments '
85 'as a JSON object.') 82 'as a JSON object.')
86 subp.add_argument('output_path', nargs=1, 83 subp.add_argument('output_path', nargs=1,
87 help='path to a file containing the output arguments ' 84 help='path to a file containing the output arguments '
88 'as a JSON object.') 85 'as a JSON object.')
89 subp.set_defaults(func=self.CmdAnalyze) 86 subp.set_defaults(func=self.CmdAnalyze)
90 87
91 subp = subps.add_parser('gen', 88 subp = subps.add_parser('gen',
92 help='generate a new set of build files') 89 help='generate a new set of build files')
93 AddCommonOptions(subp) 90 AddCommonOptions(subp)
94 subp.add_argument('--swarming-targets-file', 91 subp.add_argument('--swarming-targets-file',
95 help='save runtime dependencies for targets listed ' 92 help='save runtime dependencies for targets listed '
96 'in file.') 93 'in file.')
97 subp.add_argument('path', nargs=1, 94 subp.add_argument('path', nargs=1,
98 help='path to generate build into') 95 help='path to generate build into')
99 subp.set_defaults(func=self.CmdGen) 96 subp.set_defaults(func=self.CmdGen)
100 97
98 subp = subps.add_parser('isolate',
99 help='generate the .isolate files for a given'
100 'binary')
101 AddCommonOptions(subp)
102 subp.add_argument('path', nargs=1,
103 help='path build was generated into')
104 subp.add_argument('target', nargs=1,
105 help='ninja target to generate the isolate for')
106 subp.set_defaults(func=self.CmdIsolate)
107
101 subp = subps.add_parser('lookup', 108 subp = subps.add_parser('lookup',
102 help='look up the command for a given config or ' 109 help='look up the command for a given config or '
103 'builder') 110 'builder')
104 AddCommonOptions(subp) 111 AddCommonOptions(subp)
105 subp.set_defaults(func=self.CmdLookup) 112 subp.set_defaults(func=self.CmdLookup)
106 113
114 subp = subps.add_parser('run',
115 help='build and run the isolated version of a '
116 'binary')
117 AddCommonOptions(subp)
118 subp.add_argument('-j', '--jobs', dest='jobs', type=int,
119 help='Number of jobs to pass to ninja')
120 subp.add_argument('--no-build', dest='build', default=True,
121 action='store_false',
122 help='Do not build, just isolate and run')
123 subp.add_argument('path', nargs=1,
124 help='path to generate build into')
125 subp.add_argument('target', nargs=1,
126 help='ninja target to build and run')
127 subp.set_defaults(func=self.CmdRun)
128
107 subp = subps.add_parser('validate', 129 subp = subps.add_parser('validate',
108 help='validate the config file') 130 help='validate the config file')
109 subp.add_argument('-f', '--config-file', metavar='PATH', 131 subp.add_argument('-f', '--config-file', metavar='PATH',
110 default=self.default_config, 132 default=self.default_config,
111 help='path to config file ' 133 help='path to config file '
112 '(default is //tools/mb/mb_config.pyl)') 134 '(default is //tools/mb/mb_config.pyl)')
113 subp.set_defaults(func=self.CmdValidate) 135 subp.set_defaults(func=self.CmdValidate)
114 136
115 subp = subps.add_parser('help', 137 subp = subps.add_parser('help',
116 help='Get help on a subcommand.') 138 help='Get help on a subcommand.')
117 subp.add_argument(nargs='?', action='store', dest='subcommand', 139 subp.add_argument(nargs='?', action='store', dest='subcommand',
118 help='The command to get help for.') 140 help='The command to get help for.')
119 subp.set_defaults(func=self.CmdHelp) 141 subp.set_defaults(func=self.CmdHelp)
120 142
121 self.args = parser.parse_args(argv) 143 self.args = parser.parse_args(argv)
122 144
123 def CmdAnalyze(self): 145 def CmdAnalyze(self):
124 vals = self.GetConfig() 146 vals = self.Lookup()
125 if vals['type'] == 'gn': 147 if vals['type'] == 'gn':
126 return self.RunGNAnalyze(vals) 148 return self.RunGNAnalyze(vals)
127 elif vals['type'] == 'gyp': 149 else:
128 return self.RunGYPAnalyze(vals) 150 return self.RunGYPAnalyze(vals)
129 else:
130 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
131 151
132 def CmdGen(self): 152 def CmdGen(self):
133 vals = self.GetConfig() 153 vals = self.Lookup()
134
135 self.ClobberIfNeeded(vals) 154 self.ClobberIfNeeded(vals)
136 155
137 if vals['type'] == 'gn': 156 if vals['type'] == 'gn':
138 return self.RunGNGen(vals) 157 return self.RunGNGen(vals)
139 if vals['type'] == 'gyp': 158 else:
140 return self.RunGYPGen(vals) 159 return self.RunGYPGen(vals)
141 160
142 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
143
144 def CmdLookup(self):
145 vals = self.GetConfig()
146 if vals['type'] == 'gn':
147 cmd = self.GNCmd('gen', '_path_', vals['gn_args'])
148 env = None
149 elif vals['type'] == 'gyp':
150 cmd, env = self.GYPCmd('_path_', vals)
151 else:
152 raise MBErr('Unknown meta-build type "%s"' % vals['type'])
153
154 self.PrintCmd(cmd, env)
155 return 0
156
157 def CmdHelp(self): 161 def CmdHelp(self):
158 if self.args.subcommand: 162 if self.args.subcommand:
159 self.ParseArgs([self.args.subcommand, '--help']) 163 self.ParseArgs([self.args.subcommand, '--help'])
160 else: 164 else:
161 self.ParseArgs(['--help']) 165 self.ParseArgs(['--help'])
162 166
167 def CmdIsolate(self):
168 vals = self.GetConfig()
169 if not vals:
170 return 1
171
172 if vals['type'] == 'gn':
173 return self.RunGNIsolate(vals)
174 else:
175 return self.Build('%s_run' % self.args.target[0])
176
177 def CmdLookup(self):
178 vals = self.Lookup()
179 if vals['type'] == 'gn':
180 cmd = self.GNCmd('gen', '_path_', vals['gn_args'])
181 env = None
182 else:
183 cmd, env = self.GYPCmd('_path_', vals)
184
185 self.PrintCmd(cmd, env)
186 return 0
187
188 def CmdRun(self):
189 vals = self.GetConfig()
190 if not vals:
191 return 1
192
193 build_dir = self.args.path[0]
194 target = self.args.target[0]
195
196 if vals['type'] == 'gn':
197 if self.args.build:
198 ret = self.Build(target)
199 if ret:
200 return ret
201 ret = self.RunGNIsolate(vals)
202 if ret:
203 return ret
204 else:
205 ret = self.Build('%s_run' % target)
206 if ret:
207 return ret
208
209 ret, _, _ = self.Run([
210 self.executable,
211 self.PathJoin('tools', 'swarming_client', 'isolate.py'),
212 'run',
213 '-s',
214 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target))],
215 force_verbose=False, buffer_output=False)
216
217 return ret
218
163 def CmdValidate(self): 219 def CmdValidate(self):
164 errs = [] 220 errs = []
165 221
166 # Read the file to make sure it parses. 222 # Read the file to make sure it parses.
167 self.ReadConfigFile() 223 self.ReadConfigFile()
168 224
169 # Figure out the whole list of configs and ensure that no config is 225 # Figure out the whole list of configs and ensure that no config is
170 # listed in more than one category. 226 # listed in more than one category.
171 all_configs = {} 227 all_configs = {}
172 for config in self.common_dev_configs: 228 for config in self.common_dev_configs:
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
227 errs.append('Unreferenced mixin "%s".' % mixin) 283 errs.append('Unreferenced mixin "%s".' % mixin)
228 284
229 if errs: 285 if errs:
230 raise MBErr(('mb config file %s has problems:' % self.args.config_file) + 286 raise MBErr(('mb config file %s has problems:' % self.args.config_file) +
231 '\n ' + '\n '.join(errs)) 287 '\n ' + '\n '.join(errs))
232 288
233 self.Print('mb config file %s looks ok.' % self.args.config_file) 289 self.Print('mb config file %s looks ok.' % self.args.config_file)
234 return 0 290 return 0
235 291
236 def GetConfig(self): 292 def GetConfig(self):
293 build_dir = self.args.path[0]
294
295 vals = {}
296 if self.args.builder or self.args.master or self.args.config:
297 vals = self.Lookup()
298 if vals['type'] == 'gn':
299 # Re-run gn gen in order to ensure the config is consistent with the
300 # build dir.
301 self.RunGNGen(vals)
302 return vals
303
304 # TODO: We can only get the config for GN build dirs, not GYP build dirs.
305 # GN stores the args that were used in args.gn in the build dir,
306 # but GYP doesn't store them anywhere. We should consider modifying
307 # gyp_chromium to record the arguments it runs with in a similar
308 # manner.
309
310 mb_type_path = self.PathJoin(self.ToAbsPath(build_dir), 'mb_type')
311 if not self.Exists(mb_type_path):
312 gn_args_path = self.PathJoin(self.ToAbsPath(build_dir), 'args.gn')
313 if not self.Exists(gn_args_path):
314 self.Print('Must either specify a path to an existing GN build dir '
315 'or pass in a -m/-b pair or a -c flag to specify the '
316 'configuration')
317 return {}
318 else:
319 mb_type = 'gn'
320 else:
321 mb_type = self.ReadFile(mb_type_path).strip()
322
323 if mb_type == 'gn':
324 vals = self.GNValsFromDir(build_dir)
325 else:
326 vals = {}
327 vals['type'] = mb_type
328
329 return vals
330
331 def GNValsFromDir(self, build_dir):
332 args_contents = self.ReadFile(
333 self.PathJoin(self.ToAbsPath(build_dir), 'args.gn'))
334 gn_args = []
335 for l in args_contents.splitlines():
336 fields = l.split(' ')
337 name = fields[0]
338 val = ' '.join(fields[2:])
339 gn_args.append('%s=%s' % (name, val))
340
341 return {
342 'gn_args': ' '.join(gn_args),
343 'type': 'gn',
344 }
345
346 def Lookup(self):
237 self.ReadConfigFile() 347 self.ReadConfigFile()
238 config = self.ConfigFromArgs() 348 config = self.ConfigFromArgs()
239 if not config in self.configs: 349 if not config in self.configs:
240 raise MBErr('Config "%s" not found in %s' % 350 raise MBErr('Config "%s" not found in %s' %
241 (config, self.args.config_file)) 351 (config, self.args.config_file))
242 352
243 return self.FlattenConfig(config) 353 vals = self.FlattenConfig(config)
354
355 # Do some basic sanity checking on the config so that we
356 # don't have to do this in every caller.
357 assert 'type' in vals, 'No meta-build type specified in the config'
358 assert vals['type'] in ('gn', 'gyp'), (
359 'Unknown meta-build type "%s"' % vals['gn_args'])
360
361 return vals
244 362
245 def ReadConfigFile(self): 363 def ReadConfigFile(self):
246 if not self.Exists(self.args.config_file): 364 if not self.Exists(self.args.config_file):
247 raise MBErr('config file not found at %s' % self.args.config_file) 365 raise MBErr('config file not found at %s' % self.args.config_file)
248 366
249 try: 367 try:
250 contents = ast.literal_eval(self.ReadFile(self.args.config_file)) 368 contents = ast.literal_eval(self.ReadFile(self.args.config_file))
251 except SyntaxError as e: 369 except SyntaxError as e:
252 raise MBErr('Failed to parse config file "%s": %s' % 370 raise MBErr('Failed to parse config file "%s": %s' %
253 (self.args.config_file, e)) 371 (self.args.config_file, e))
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 if self.args.dryrun: 464 if self.args.dryrun:
347 return 465 return
348 466
349 if needs_clobber: 467 if needs_clobber:
350 self.RemoveDirectory(build_dir) 468 self.RemoveDirectory(build_dir)
351 469
352 self.MaybeMakeDirectory(build_dir) 470 self.MaybeMakeDirectory(build_dir)
353 self.WriteFile(mb_type_path, new_mb_type) 471 self.WriteFile(mb_type_path, new_mb_type)
354 472
355 def RunGNGen(self, vals): 473 def RunGNGen(self, vals):
356 path = self.args.path[0] 474 build_dir = self.args.path[0]
357 475
358 cmd = self.GNCmd('gen', path, vals['gn_args'], extra_args=['--check']) 476 cmd = self.GNCmd('gen', build_dir, vals['gn_args'], extra_args=['--check'])
359 477
360 swarming_targets = [] 478 swarming_targets = []
361 if self.args.swarming_targets_file: 479 if getattr(self.args, 'swarming_targets_file', None):
362 # We need GN to generate the list of runtime dependencies for 480 # We need GN to generate the list of runtime dependencies for
363 # the compile targets listed (one per line) in the file so 481 # the compile targets listed (one per line) in the file so
364 # we can run them via swarming. We use ninja_to_gn.pyl to convert 482 # we can run them via swarming. We use ninja_to_gn.pyl to convert
365 # the compile targets to the matching GN labels. 483 # the compile targets to the matching GN labels.
366 contents = self.ReadFile(self.args.swarming_targets_file) 484 contents = self.ReadFile(self.args.swarming_targets_file)
367 swarming_targets = contents.splitlines() 485 swarming_targets = contents.splitlines()
368 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin( 486 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin(
369 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl'))) 487 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl')))
370 gn_labels = [] 488 gn_labels = []
371 for target in swarming_targets: 489 for target in swarming_targets:
372 if not target in gn_isolate_map: 490 if not target in gn_isolate_map:
373 raise MBErr('test target "%s" not found in %s' % 491 raise MBErr('test target "%s" not found in %s' %
374 (target, '//testing/buildbot/gn_isolate_map.pyl')) 492 (target, '//testing/buildbot/gn_isolate_map.pyl'))
375 gn_labels.append(gn_isolate_map[target]['label']) 493 gn_labels.append(gn_isolate_map[target]['label'])
376 494
377 gn_runtime_deps_path = self.ToAbsPath(path, 'runtime_deps') 495 gn_runtime_deps_path = self.ToAbsPath(build_dir, 'runtime_deps')
378 496
379 # Since GN hasn't run yet, the build directory may not even exist. 497 # Since GN hasn't run yet, the build directory may not even exist.
380 self.MaybeMakeDirectory(self.ToAbsPath(path)) 498 self.MaybeMakeDirectory(self.ToAbsPath(build_dir))
381 499
382 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n') 500 self.WriteFile(gn_runtime_deps_path, '\n'.join(gn_labels) + '\n')
383 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path) 501 cmd.append('--runtime-deps-list-file=%s' % gn_runtime_deps_path)
384 502
385 ret, _, _ = self.Run(cmd) 503 ret, _, _ = self.Run(cmd)
386 if ret: 504 if ret:
387 # If `gn gen` failed, we should exit early rather than trying to 505 # If `gn gen` failed, we should exit early rather than trying to
388 # generate isolates. Run() will have already logged any error output. 506 # generate isolates. Run() will have already logged any error output.
389 self.Print('GN gen failed: %d' % ret) 507 self.Print('GN gen failed: %d' % ret)
390 return ret 508 return ret
391 509
392 for target in swarming_targets: 510 for target in swarming_targets:
393 if gn_isolate_map[target]['type'] == 'gpu_browser_test': 511 if gn_isolate_map[target]['type'] == 'gpu_browser_test':
394 runtime_deps_target = 'browser_tests' 512 runtime_deps_target = 'browser_tests'
395 elif gn_isolate_map[target]['type'] == 'script': 513 elif gn_isolate_map[target]['type'] == 'script':
396 # For script targets, the build target is usually a group, 514 # For script targets, the build target is usually a group,
397 # for which gn generates the runtime_deps next to the stamp file 515 # for which gn generates the runtime_deps next to the stamp file
398 # for the label, which lives under the obj/ directory. 516 # for the label, which lives under the obj/ directory.
399 label = gn_isolate_map[target]['label'] 517 label = gn_isolate_map[target]['label']
400 runtime_deps_target = 'obj/%s.stamp' % label.replace(':', '/') 518 runtime_deps_target = 'obj/%s.stamp' % label.replace(':', '/')
401 else: 519 else:
402 runtime_deps_target = target 520 runtime_deps_target = target
403 if self.platform == 'win32': 521 if self.platform == 'win32':
404 deps_path = self.ToAbsPath(path, 522 deps_path = self.ToAbsPath(build_dir,
405 runtime_deps_target + '.exe.runtime_deps') 523 runtime_deps_target + '.exe.runtime_deps')
406 else: 524 else:
407 deps_path = self.ToAbsPath(path, 525 deps_path = self.ToAbsPath(build_dir,
408 runtime_deps_target + '.runtime_deps') 526 runtime_deps_target + '.runtime_deps')
409 if not self.Exists(deps_path): 527 if not self.Exists(deps_path):
410 raise MBErr('did not generate %s' % deps_path) 528 raise MBErr('did not generate %s' % deps_path)
411 529
412 command, extra_files = self.GetIsolateCommand(target, vals, 530 command, extra_files = self.GetIsolateCommand(target, vals,
413 gn_isolate_map) 531 gn_isolate_map)
414 532
415 runtime_deps = self.ReadFile(deps_path).splitlines() 533 runtime_deps = self.ReadFile(deps_path).splitlines()
416 534
417 isolate_path = self.ToAbsPath(path, target + '.isolate') 535 self.WriteIsolateFiles(build_dir, command, target, runtime_deps,
418 self.WriteFile(isolate_path, 536 extra_files)
419 pprint.pformat({
420 'variables': {
421 'command': command,
422 'files': sorted(runtime_deps + extra_files),
423 }
424 }) + '\n')
425 537
426 self.WriteJSON( 538 return 0
427 { 539
428 'args': [ 540 def RunGNIsolate(self, vals):
429 '--isolated', 541 gn_isolate_map = ast.literal_eval(self.ReadFile(self.PathJoin(
430 self.ToSrcRelPath('%s%s%s.isolated' % (path, self.sep, target)), 542 self.chromium_src_dir, 'testing', 'buildbot', 'gn_isolate_map.pyl')))
431 '--isolate', 543
432 self.ToSrcRelPath('%s%s%s.isolate' % (path, self.sep, target)), 544 build_dir = self.args.path[0]
433 ], 545 target = self.args.target[0]
434 'dir': self.chromium_src_dir, 546 command, extra_files = self.GetIsolateCommand(target, vals, gn_isolate_map)
435 'version': 1, 547
436 }, 548 label = gn_isolate_map[target]['label']
437 isolate_path + 'd.gen.json', 549 ret, out, _ = self.Call(['gn', 'desc', build_dir, label, 'runtime_deps'])
438 ) 550 if ret:
551 return ret
552
553 runtime_deps = out.splitlines()
554
555 self.WriteIsolateFiles(build_dir, command, target, runtime_deps,
556 extra_files)
557
558 ret, _, _ = self.Run([
559 self.executable,
560 self.PathJoin('tools', 'swarming_client', 'isolate.py'),
561 'check',
562 '-i',
563 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)),
564 '-s',
565 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target))],
566 buffer_output=False)
439 567
440 return ret 568 return ret
441 569
570 def WriteIsolateFiles(self, build_dir, command, target, runtime_deps,
571 extra_files):
572 isolate_path = self.ToAbsPath(build_dir, target + '.isolate')
573 self.WriteFile(isolate_path,
574 pprint.pformat({
575 'variables': {
576 'command': command,
577 'files': sorted(runtime_deps + extra_files),
578 }
579 }) + '\n')
580
581 self.WriteJSON(
582 {
583 'args': [
584 '--isolated',
585 self.ToSrcRelPath('%s/%s.isolated' % (build_dir, target)),
586 '--isolate',
587 self.ToSrcRelPath('%s/%s.isolate' % (build_dir, target)),
588 ],
589 'dir': self.chromium_src_dir,
590 'version': 1,
591 },
592 isolate_path + 'd.gen.json',
593 )
594
442 def GNCmd(self, subcommand, path, gn_args='', extra_args=None): 595 def GNCmd(self, subcommand, path, gn_args='', extra_args=None):
443 if self.platform == 'linux2': 596 if self.platform == 'linux2':
444 subdir = 'linux64' 597 subdir = 'linux64'
445 elif self.platform == 'darwin': 598 elif self.platform == 'darwin':
446 subdir = 'mac' 599 subdir = 'mac'
447 else: 600 else:
448 subdir = 'win' 601 subdir = 'win'
449 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, 'gn') 602 gn_path = self.PathJoin(self.chromium_src_dir, 'buildtools', subdir, 'gn')
450 603
451 cmd = [gn_path, subcommand, path] 604 cmd = [gn_path, subcommand, path]
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after
762 cmd = ['python'] + cmd[1:] 915 cmd = ['python'] + cmd[1:]
763 self.Print(*[shell_quoter(arg) for arg in cmd]) 916 self.Print(*[shell_quoter(arg) for arg in cmd])
764 917
765 def PrintJSON(self, obj): 918 def PrintJSON(self, obj):
766 self.Print(json.dumps(obj, indent=2, sort_keys=True)) 919 self.Print(json.dumps(obj, indent=2, sort_keys=True))
767 920
768 def Print(self, *args, **kwargs): 921 def Print(self, *args, **kwargs):
769 # This function largely exists so it can be overridden for testing. 922 # This function largely exists so it can be overridden for testing.
770 print(*args, **kwargs) 923 print(*args, **kwargs)
771 924
772 def Run(self, cmd, env=None, force_verbose=True): 925 def Build(self, target):
926 build_dir = self.ToSrcRelPath(self.args.path[0])
927 ninja_cmd = ['ninja', '-C', build_dir]
928 if self.args.jobs:
929 ninja_cmd.extend(['-j', '%d' % self.args.jobs])
930 ninja_cmd.append(target)
931 ret, _, _ = self.Run(ninja_cmd, force_verbose=False, buffer_output=False)
932 return ret
933
934 def Run(self, cmd, env=None, force_verbose=True, buffer_output=True):
773 # This function largely exists so it can be overridden for testing. 935 # This function largely exists so it can be overridden for testing.
774 if self.args.dryrun or self.args.verbose or force_verbose: 936 if self.args.dryrun or self.args.verbose or force_verbose:
775 self.PrintCmd(cmd, env) 937 self.PrintCmd(cmd, env)
776 if self.args.dryrun: 938 if self.args.dryrun:
777 return 0, '', '' 939 return 0, '', ''
778 940
779 ret, out, err = self.Call(cmd, env=env) 941 ret, out, err = self.Call(cmd, env=env, buffer_output=buffer_output)
780 if self.args.verbose or force_verbose: 942 if self.args.verbose or force_verbose:
943 if ret:
944 self.Print(' -> returned %d' % ret)
781 if out: 945 if out:
782 self.Print(out, end='') 946 self.Print(out, end='')
783 if err: 947 if err:
784 self.Print(err, end='', file=sys.stderr) 948 self.Print(err, end='', file=sys.stderr)
785 return ret, out, err 949 return ret, out, err
786 950
787 def Call(self, cmd, env=None): 951 def Call(self, cmd, env=None, buffer_output=True):
788 p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir, 952 if buffer_output:
789 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 953 p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir,
790 env=env) 954 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
791 out, err = p.communicate() 955 env=env)
956 out, err = p.communicate()
957 else:
958 p = subprocess.Popen(cmd, shell=False, cwd=self.chromium_src_dir,
959 env=env)
960 p.wait()
961 out = err = ''
792 return p.returncode, out, err 962 return p.returncode, out, err
793 963
794 def ExpandUser(self, path): 964 def ExpandUser(self, path):
795 # This function largely exists so it can be overridden for testing. 965 # This function largely exists so it can be overridden for testing.
796 return os.path.expanduser(path) 966 return os.path.expanduser(path)
797 967
798 def Exists(self, path): 968 def Exists(self, path):
799 # This function largely exists so it can be overridden for testing. 969 # This function largely exists so it can be overridden for testing.
800 return os.path.exists(path) 970 return os.path.exists(path)
801 971
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
877 1047
878 if __name__ == '__main__': 1048 if __name__ == '__main__':
879 try: 1049 try:
880 sys.exit(main(sys.argv[1:])) 1050 sys.exit(main(sys.argv[1:]))
881 except MBErr as e: 1051 except MBErr as e:
882 print(e) 1052 print(e)
883 sys.exit(1) 1053 sys.exit(1)
884 except KeyboardInterrupt: 1054 except KeyboardInterrupt:
885 print("interrupted, exiting", stream=sys.stderr) 1055 print("interrupted, exiting", stream=sys.stderr)
886 sys.exit(130) 1056 sys.exit(130)
OLDNEW
« no previous file with comments | « no previous file | tools/mb/mb_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698