diff options
Diffstat (limited to 'build/compare-mozconfig/compare-mozconfigs.py')
-rw-r--r-- | build/compare-mozconfig/compare-mozconfigs.py | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/build/compare-mozconfig/compare-mozconfigs.py b/build/compare-mozconfig/compare-mozconfigs.py new file mode 100644 index 000000000..876e336fe --- /dev/null +++ b/build/compare-mozconfig/compare-mozconfigs.py @@ -0,0 +1,170 @@ +#!/usr/bin/python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# originally from http://hg.mozilla.org/build/tools/file/4ab9c1a4e05b/scripts/release/compare-mozconfigs.py + +from __future__ import unicode_literals + +import logging +import os +import site +import sys +import urllib2 +import difflib + +FAILURE_CODE = 1 +SUCCESS_CODE = 0 + +log = logging.getLogger(__name__) + +class ConfigError(Exception): + pass + +def make_hg_url(hgHost, repoPath, protocol='https', revision=None, + filename=None): + """construct a valid hg url from a base hg url (hg.mozilla.org), + repoPath, revision and possible filename""" + base = '%s://%s' % (protocol, hgHost) + repo = '/'.join(p.strip('/') for p in [base, repoPath]) + if not filename: + if not revision: + return repo + else: + return '/'.join([p.strip('/') for p in [repo, 'rev', revision]]) + else: + assert revision + return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision, + filename]]) + +def readConfig(configfile, keys=[], required=[]): + c = {} + execfile(configfile, c) + for k in keys: + c = c[k] + items = c.keys() + err = False + for key in required: + if key not in items: + err = True + log.error("Required item `%s' missing from %s" % (key, c)) + if err: + raise ConfigError("Missing at least one item in config, see above") + return c + +def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform, + mozconfigWhitelist={}): + """Compares mozconfig to nightly_mozconfig and compare to an optional + whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair + are pairs containing the mozconfig's identifier and the list of lines in + the mozconfig.""" + + # unpack the pairs to get the names, the names are just for + # identifying the mozconfigs when logging the error messages + mozconfig_name, mozconfig_lines = mozconfig_pair + nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair + + missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == [] + if missing_args: + log.info("Missing mozconfigs to compare for %s" % platform) + return False + + success = True + + diff_instance = difflib.Differ() + diff_result = diff_instance.compare(mozconfig_lines, nightly_mozconfig_lines) + diff_list = list(diff_result) + + for line in diff_list: + clean_line = line[1:].strip() + if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1: + # skip comment lines + if clean_line.startswith('#'): + continue + # compare to whitelist + message = "" + if line[0] == '-': + # handle lines that move around in diff + if '+' + line[1:] in diff_list: + continue + if platform in mozconfigWhitelist.get('release', {}): + if clean_line in \ + mozconfigWhitelist['release'][platform]: + continue + elif line[0] == '+': + if '-' + line[1:] in diff_list: + continue + if platform in mozconfigWhitelist.get('nightly', {}): + if clean_line in \ + mozconfigWhitelist['nightly'][platform]: + continue + else: + log.warning("%s not in %s %s!" % ( + clean_line, platform, + mozconfigWhitelist['nightly'][platform])) + else: + log.error("Skipping line %s!" % line) + continue + message = "found in %s but not in %s: %s" + if line[0] == '-': + log.error(message % (mozconfig_name, + nightly_mozconfig_name, clean_line)) + else: + log.error(message % (nightly_mozconfig_name, + mozconfig_name, clean_line)) + success = False + return success + +def get_mozconfig(path, options): + """Consumes a path and returns a list of lines from + the mozconfig file. If download is required, the path + specified should be relative to the root of the hg + repository e.g browser/config/mozconfigs/linux32/nightly""" + if options.no_download: + return open(path, 'r').readlines() + else: + url = make_hg_url(options.hghost, options.branch, 'http', + options.revision, path) + return urllib2.urlopen(url).readlines() + +if __name__ == '__main__': + from optparse import OptionParser + parser = OptionParser() + + parser.add_option('--branch', dest='branch') + parser.add_option('--revision', dest='revision') + parser.add_option('--hghost', dest='hghost', default='hg.mozilla.org') + parser.add_option('--whitelist', dest='whitelist') + parser.add_option('--no-download', action='store_true', dest='no_download', + default=False) + options, args = parser.parse_args() + + logging.basicConfig(level=logging.INFO) + + missing_args = options.branch is None or options.revision is None + if not options.no_download and missing_args: + logging.error('Not enough arguments to download mozconfigs') + sys.exit(FAILURE_CODE) + + mozconfig_whitelist = readConfig(options.whitelist, ['whitelist']) + + for arg in args: + platform, mozconfig_path, nightly_mozconfig_path = arg.split(',') + + mozconfig_lines = get_mozconfig(mozconfig_path, options) + nightly_mozconfig_lines = get_mozconfig(nightly_mozconfig_path, options) + + mozconfig_pair = (mozconfig_path, mozconfig_lines) + nightly_mozconfig_pair = (nightly_mozconfig_path, + nightly_mozconfig_lines) + + passed = verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, + platform, mozconfig_whitelist) + + if passed: + logging.info('Mozconfig check passed!') + else: + logging.error('Mozconfig check failed!') + sys.exit(FAILURE_CODE) + sys.exit(SUCCESS_CODE) |