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

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

Issue 1794573002: Add an `mb audit` command to track the GYP->GN progress. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: update w/ review feedback, clean up Created 4 years, 9 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/mb/docs/user_guide.md ('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 errno 16 import errno
17 import json 17 import json
18 import os 18 import os
19 import pipes 19 import pipes
20 import pprint 20 import pprint
21 import re 21 import re
22 import shutil 22 import shutil
23 import sys 23 import sys
24 import subprocess 24 import subprocess
25 import tempfile 25 import tempfile
26 import urllib2
27
28 from collections import OrderedDict
26 29
27 def main(args): 30 def main(args):
28 mbw = MetaBuildWrapper() 31 mbw = MetaBuildWrapper()
29 mbw.ParseArgs(args) 32 mbw.ParseArgs(args)
30 33
31 try: 34 try:
32 ret = mbw.args.func() 35 ret = mbw.args.func()
33 if ret: 36 if ret:
34 mbw.DumpInputFiles() 37 mbw.DumpInputFiles()
35 return ret 38 return ret
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 subp.set_defaults(func=self.CmdRun) 138 subp.set_defaults(func=self.CmdRun)
136 139
137 subp = subps.add_parser('validate', 140 subp = subps.add_parser('validate',
138 help='validate the config file') 141 help='validate the config file')
139 subp.add_argument('-f', '--config-file', metavar='PATH', 142 subp.add_argument('-f', '--config-file', metavar='PATH',
140 default=self.default_config, 143 default=self.default_config,
141 help='path to config file ' 144 help='path to config file '
142 '(default is //tools/mb/mb_config.pyl)') 145 '(default is //tools/mb/mb_config.pyl)')
143 subp.set_defaults(func=self.CmdValidate) 146 subp.set_defaults(func=self.CmdValidate)
144 147
148 subp = subps.add_parser('audit',
149 help='Audit the config file to track progress')
150 subp.add_argument('-f', '--config-file', metavar='PATH',
151 default=self.default_config,
152 help='path to config file '
153 '(default is //tools/mb/mb_config.pyl)')
154 subp.add_argument('-i', '--internal', action='store_true',
155 help='check internal masters also')
156 subp.add_argument('-m', '--master', action='append',
157 help='master to audit (default is all non-internal '
158 'masters in file)')
159 subp.add_argument('-u', '--url-template', action='store',
160 default='https://build.chromium.org/p/'
161 '{master}/json/builders',
162 help='URL scheme for JSON APIs to buildbot '
163 '(default: %(default)s) ')
164 subp.set_defaults(func=self.CmdAudit)
165
145 subp = subps.add_parser('help', 166 subp = subps.add_parser('help',
146 help='Get help on a subcommand.') 167 help='Get help on a subcommand.')
147 subp.add_argument(nargs='?', action='store', dest='subcommand', 168 subp.add_argument(nargs='?', action='store', dest='subcommand',
148 help='The command to get help for.') 169 help='The command to get help for.')
149 subp.set_defaults(func=self.CmdHelp) 170 subp.set_defaults(func=self.CmdHelp)
150 171
151 self.args = parser.parse_args(argv) 172 self.args = parser.parse_args(argv)
152 173
153 def DumpInputFiles(self): 174 def DumpInputFiles(self):
154 175
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 for mixin in self.configs[config]: 348 for mixin in self.configs[config]:
328 RecurseMixins(mixin) 349 RecurseMixins(mixin)
329 350
330 if errs: 351 if errs:
331 raise MBErr(('mb config file %s has problems:' % self.args.config_file) + 352 raise MBErr(('mb config file %s has problems:' % self.args.config_file) +
332 '\n ' + '\n '.join(errs)) 353 '\n ' + '\n '.join(errs))
333 354
334 self.Print('mb config file %s looks ok.' % self.args.config_file) 355 self.Print('mb config file %s looks ok.' % self.args.config_file)
335 return 0 356 return 0
336 357
358 def CmdAudit(self):
359 """Track the progress of the GYP->GN migration on the bots."""
360
361 stats = OrderedDict()
362 STAT_MASTER_ONLY = 'Master only'
363 STAT_CONFIG_ONLY = 'Config only'
364 STAT_TBD = 'Still TBD'
365 STAT_GYP = 'Still GYP'
366 STAT_DONE = 'Done (on GN)'
367 stats[STAT_MASTER_ONLY] = 0
368 stats[STAT_CONFIG_ONLY] = 0
369 stats[STAT_TBD] = 0
370 stats[STAT_GYP] = 0
371 stats[STAT_DONE] = 0
372
373 def PrintBuilders(heading, builders):
374 stats.setdefault(heading, 0)
375 stats[heading] += len(builders)
376 if builders:
377 self.Print(' %s:' % heading)
378 for builder in sorted(builders):
379 self.Print(' %s' % builder)
380
381 self.ReadConfigFile()
382
383 masters = self.args.master or self.masters
384 for master in sorted(masters):
385 url = self.args.url_template.replace('{master}', master)
386
387 self.Print('Auditing %s' % master)
388
389 MASTERS_TO_SKIP = (
390 'client.skia',
391 'client.v8.fyi',
392 'tryserver.v8',
393 )
394 if master in MASTERS_TO_SKIP:
395 # Skip these bots because converting them is the responsibility of
396 # those teams and out of scope for the Chromium migration to GN.
397 self.Print(' Skipped (out of scope)')
398 self.Print('')
399 continue
400
401 INTERNAL_MASTERS = (
402 'chrome',
403 'chrome.continuous',
404 'official.desktop',
405 'official.desktop.continuous',
406 )
407 if master in INTERNAL_MASTERS and not self.args.internal:
408 # Skip these because the servers aren't accessible by default ...
409 self.Print(' Skipped (internal)')
410 self.Print('')
411 continue
412
413 try:
414 # Fetch the /builders contents from the buildbot master. The
415 # keys of the dict are the builder names themselves.
416 json_contents = self.Fetch(url)
417 d = json.loads(json_contents)
418 except Exception as e:
419 self.Print(str(e))
420 return 1
421
422 config_builders = set(self.masters[master])
423 master_builders = set(d.keys())
424 both = master_builders & config_builders
425 master_only = master_builders - config_builders
426 config_only = config_builders - master_builders
427 tbd = set()
428 gyp = set()
429 done = set()
430
431 for builder in both:
432 config = self.masters[master][builder]
433 if config == 'tbd':
434 tbd.add(builder)
435 else:
436 # TODO(dpranke): Check if MB is actually running?
437 vals = self.FlattenConfig(config)
438 if vals['type'] == 'gyp':
439 gyp.add(builder)
440 else:
441 done.add(builder)
442
443 if master_only or config_only or tbd or gyp:
444 PrintBuilders(STAT_MASTER_ONLY, master_only)
445 PrintBuilders(STAT_CONFIG_ONLY, config_only)
446 PrintBuilders(STAT_TBD, tbd)
447 PrintBuilders(STAT_GYP, gyp)
448 else:
449 self.Print(' ... ok')
450
451 stats[STAT_DONE] += len(done)
452
453 self.Print('')
454
455 fmt = '{:<27} {:>4}'
456 self.Print(fmt.format('Totals', str(sum(int(v) for v in stats.values()))))
457 self.Print(fmt.format('-' * 27, '----'))
458 for stat, count in stats.items():
459 self.Print(fmt.format(stat, str(count)))
460
461 return 0
462
337 def GetConfig(self): 463 def GetConfig(self):
338 build_dir = self.args.path[0] 464 build_dir = self.args.path[0]
339 465
340 vals = {} 466 vals = {}
341 if self.args.builder or self.args.master or self.args.config: 467 if self.args.builder or self.args.master or self.args.config:
342 vals = self.Lookup() 468 vals = self.Lookup()
343 if vals['type'] == 'gn': 469 if vals['type'] == 'gn':
344 # Re-run gn gen in order to ensure the config is consistent with the 470 # Re-run gn gen in order to ensure the config is consistent with the
345 # build dir. 471 # build dir.
346 self.RunGNGen(vals) 472 self.RunGNGen(vals)
(...skipping 742 matching lines...) Expand 10 before | Expand all | Expand 10 after
1089 return p.returncode, out, err 1215 return p.returncode, out, err
1090 1216
1091 def ExpandUser(self, path): 1217 def ExpandUser(self, path):
1092 # This function largely exists so it can be overridden for testing. 1218 # This function largely exists so it can be overridden for testing.
1093 return os.path.expanduser(path) 1219 return os.path.expanduser(path)
1094 1220
1095 def Exists(self, path): 1221 def Exists(self, path):
1096 # This function largely exists so it can be overridden for testing. 1222 # This function largely exists so it can be overridden for testing.
1097 return os.path.exists(path) 1223 return os.path.exists(path)
1098 1224
1225 def Fetch(self, url):
1226
1227 f = urllib2.urlopen(url)
1228 contents = f.read()
1229 f.close()
1230 return contents
1231
1099 def GNTargetName(self, target): 1232 def GNTargetName(self, target):
1100 return target[:-len('_apk')] if target.endswith('_apk') else target 1233 return target[:-len('_apk')] if target.endswith('_apk') else target
1101 1234
1102 def MaybeMakeDirectory(self, path): 1235 def MaybeMakeDirectory(self, path):
1103 try: 1236 try:
1104 os.makedirs(path) 1237 os.makedirs(path)
1105 except OSError, e: 1238 except OSError, e:
1106 if e.errno != errno.EEXIST: 1239 if e.errno != errno.EEXIST:
1107 raise 1240 raise
1108 1241
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 1310
1178 if __name__ == '__main__': 1311 if __name__ == '__main__':
1179 try: 1312 try:
1180 sys.exit(main(sys.argv[1:])) 1313 sys.exit(main(sys.argv[1:]))
1181 except MBErr as e: 1314 except MBErr as e:
1182 print(e) 1315 print(e)
1183 sys.exit(1) 1316 sys.exit(1)
1184 except KeyboardInterrupt: 1317 except KeyboardInterrupt:
1185 print("interrupted, exiting", stream=sys.stderr) 1318 print("interrupted, exiting", stream=sys.stderr)
1186 sys.exit(130) 1319 sys.exit(130)
OLDNEW
« no previous file with comments | « tools/mb/docs/user_guide.md ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698