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

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: initial patch for review 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 self.ReadConfigFile()
360
361 STAT_MISSING_ON_MASTER = 'in config but not on master'
362 STAT_MISSING_IN_CONFIG = 'on master but not in config'
363 STAT_TBD = 'still TBD'
364 STAT_GYP = 'still on GYP'
365 STAT_DONE = 'done (on GN)'
366 stats = OrderedDict()
367 stats[STAT_MISSING_ON_MASTER] = 0
368 stats[STAT_MISSING_IN_CONFIG] = 0
369 stats[STAT_TBD] = 0
370 stats[STAT_GYP] = 0
371 stats[STAT_DONE] = 0
372
373 masters = self.args.master or self.masters
374 for master in sorted(masters):
375 config_builders = sorted(self.masters[master])
376 url = self.args.url_template.replace('{master}', master)
377
378 self.Print('Auditing %s' % master)
379
380 MASTERS_TO_SKIP = (
381 'client.skia',
382 'client.v8.fyi',
383 'tryserver.v8',
384 )
385 if master in MASTERS_TO_SKIP:
386 # We skip these bots because we don't care if they're converted.
387 self.Print(' skipped (do not care if incomplete)')
388 self.Print('')
389 continue
390
391 INTERNAL_MASTERS = (
392 'chrome',
393 'chrome.continuous',
394 'official.desktop',
395 'official.desktop.continuous'
396 )
397 if master in INTERNAL_MASTERS and not self.args.internal:
398 self.Print(' skipped (internal)')
399 self.Print('')
400 continue
401
402 try:
403 json_contents = self.Fetch(url)
404 d = json.loads(json_contents)
405 except Exception as e:
406 self.Print(str(e))
407 return 1
408
409 missing_on_master = []
410 missing_in_config = []
411 tbd = []
412 gyp = []
413 done = []
414 buildbot_builders = sorted(d.keys())
415 for builder in config_builders:
416 if builder in buildbot_builders:
417 config = self.masters[master][builder]
418 if config == 'tbd':
419 tbd.append(builder)
420 else:
421 # TODO(dpranke): Check if MB is actually running?
422 vals = self.FlattenConfig(config)
423 if vals['type'] == 'gyp':
424 gyp.append(builder)
425 else:
426 done.append(builder)
427 else:
428 missing_on_master.append(builder)
429
430 for builder in buildbot_builders:
estaab 2016/03/12 04:07:27 nit: missing_in_config = sorted(set(buildbot_build
431 if builder not in config_builders:
432 missing_in_config.append(builder)
433
434 def PrintBuilders(heading, builders):
435 stats.setdefault(heading, 0)
436 stats[heading] += len(builders)
437 if builders:
438 self.Print(' %s:' % heading)
439 for builder in builders:
440 self.Print(' %s' % builder)
441
442 if missing_in_config or missing_on_master or tbd or gyp:
443 PrintBuilders(STAT_MISSING_IN_CONFIG, missing_in_config)
444 PrintBuilders(STAT_MISSING_ON_MASTER, missing_on_master)
445 PrintBuilders(STAT_TBD, tbd)
446 PrintBuilders(STAT_GYP, gyp)
447 else:
448 self.Print(' ... ok')
449
450 stats[STAT_DONE] += len(done)
451
452 self.Print('')
453
454 fmt = '{:<27} {:>4}'
455 self.Print(fmt.format('Totals', str(sum(int(v) for v in stats.values()))))
456 self.Print(fmt.format('-' * 27, '----'))
457 for stat, count in stats.items():
458 self.Print(fmt.format(stat, str(count)))
459 return 0
460
337 def GetConfig(self): 461 def GetConfig(self):
338 build_dir = self.args.path[0] 462 build_dir = self.args.path[0]
339 463
340 vals = {} 464 vals = {}
341 if self.args.builder or self.args.master or self.args.config: 465 if self.args.builder or self.args.master or self.args.config:
342 vals = self.Lookup() 466 vals = self.Lookup()
343 if vals['type'] == 'gn': 467 if vals['type'] == 'gn':
344 # Re-run gn gen in order to ensure the config is consistent with the 468 # Re-run gn gen in order to ensure the config is consistent with the
345 # build dir. 469 # build dir.
346 self.RunGNGen(vals) 470 self.RunGNGen(vals)
(...skipping 742 matching lines...) Expand 10 before | Expand all | Expand 10 after
1089 return p.returncode, out, err 1213 return p.returncode, out, err
1090 1214
1091 def ExpandUser(self, path): 1215 def ExpandUser(self, path):
1092 # This function largely exists so it can be overridden for testing. 1216 # This function largely exists so it can be overridden for testing.
1093 return os.path.expanduser(path) 1217 return os.path.expanduser(path)
1094 1218
1095 def Exists(self, path): 1219 def Exists(self, path):
1096 # This function largely exists so it can be overridden for testing. 1220 # This function largely exists so it can be overridden for testing.
1097 return os.path.exists(path) 1221 return os.path.exists(path)
1098 1222
1223 def Fetch(self, url):
1224
1225 f = urllib2.urlopen(url)
1226 contents = f.read()
1227 f.close()
1228 return contents
1229
1099 def GNTargetName(self, target): 1230 def GNTargetName(self, target):
1100 return target[:-len('_apk')] if target.endswith('_apk') else target 1231 return target[:-len('_apk')] if target.endswith('_apk') else target
1101 1232
1102 def MaybeMakeDirectory(self, path): 1233 def MaybeMakeDirectory(self, path):
1103 try: 1234 try:
1104 os.makedirs(path) 1235 os.makedirs(path)
1105 except OSError, e: 1236 except OSError, e:
1106 if e.errno != errno.EEXIST: 1237 if e.errno != errno.EEXIST:
1107 raise 1238 raise
1108 1239
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1177 1308
1178 if __name__ == '__main__': 1309 if __name__ == '__main__':
1179 try: 1310 try:
1180 sys.exit(main(sys.argv[1:])) 1311 sys.exit(main(sys.argv[1:]))
1181 except MBErr as e: 1312 except MBErr as e:
1182 print(e) 1313 print(e)
1183 sys.exit(1) 1314 sys.exit(1)
1184 except KeyboardInterrupt: 1315 except KeyboardInterrupt:
1185 print("interrupted, exiting", stream=sys.stderr) 1316 print("interrupted, exiting", stream=sys.stderr)
1186 sys.exit(130) 1317 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