build/compare-mozconfig/compare-mozconfigs.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/build/compare-mozconfig/compare-mozconfigs.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,165 @@
     1.4 +#!/usr/bin/python
     1.5 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.6 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.8 +
     1.9 +# originally from http://hg.mozilla.org/build/tools/file/4ab9c1a4e05b/scripts/release/compare-mozconfigs.py
    1.10 +
    1.11 +from __future__ import unicode_literals
    1.12 +
    1.13 +import logging
    1.14 +import os
    1.15 +import site
    1.16 +import sys
    1.17 +import urllib2
    1.18 +import difflib
    1.19 +
    1.20 +FAILURE_CODE = 1
    1.21 +SUCCESS_CODE = 0
    1.22 +
    1.23 +log = logging.getLogger(__name__)
    1.24 +
    1.25 +class ConfigError(Exception):
    1.26 +    pass
    1.27 +
    1.28 +def make_hg_url(hgHost, repoPath, protocol='https', revision=None,
    1.29 +                filename=None):
    1.30 +    """construct a valid hg url from a base hg url (hg.mozilla.org),
    1.31 +    repoPath, revision and possible filename"""
    1.32 +    base = '%s://%s' % (protocol, hgHost)
    1.33 +    repo = '/'.join(p.strip('/') for p in [base, repoPath])
    1.34 +    if not filename:
    1.35 +        if not revision:
    1.36 +            return repo
    1.37 +        else:
    1.38 +            return '/'.join([p.strip('/') for p in [repo, 'rev', revision]])
    1.39 +    else:
    1.40 +        assert revision
    1.41 +        return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision,
    1.42 +                         filename]])
    1.43 +
    1.44 +def readConfig(configfile, keys=[], required=[]):
    1.45 +    c = {}
    1.46 +    execfile(configfile, c)
    1.47 +    for k in keys:
    1.48 +        c = c[k]
    1.49 +    items = c.keys()
    1.50 +    err = False
    1.51 +    for key in required:
    1.52 +        if key not in items:
    1.53 +            err = True
    1.54 +            log.error("Required item `%s' missing from %s" % (key, c))
    1.55 +    if err:
    1.56 +        raise ConfigError("Missing at least one item in config, see above")
    1.57 +    return c
    1.58 +
    1.59 +def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform,
    1.60 +                      mozconfigWhitelist={}):
    1.61 +    """Compares mozconfig to nightly_mozconfig and compare to an optional
    1.62 +    whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair
    1.63 +    are pairs containing the mozconfig's identifier and the list of lines in
    1.64 +    the mozconfig."""
    1.65 +
    1.66 +    # unpack the pairs to get the names, the names are just for
    1.67 +    # identifying the mozconfigs when logging the error messages
    1.68 +    mozconfig_name, mozconfig_lines = mozconfig_pair
    1.69 +    nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair
    1.70 +
    1.71 +    missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == []
    1.72 +    if missing_args:
    1.73 +        log.info("Missing mozconfigs to compare for %s" % platform)
    1.74 +        return False
    1.75 +
    1.76 +    success = True
    1.77 +
    1.78 +    diff_instance = difflib.Differ()
    1.79 +    diff_result = diff_instance.compare(mozconfig_lines, nightly_mozconfig_lines)
    1.80 +    diff_list = list(diff_result)
    1.81 +
    1.82 +    for line in diff_list:
    1.83 +        clean_line = line[1:].strip()
    1.84 +        if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1:
    1.85 +            # skip comment lines
    1.86 +            if clean_line.startswith('#'):
    1.87 +                continue
    1.88 +            # compare to whitelist
    1.89 +            message = ""
    1.90 +            if line[0] == '-':
    1.91 +                if platform in mozconfigWhitelist.get('release', {}):
    1.92 +                    if clean_line in \
    1.93 +                            mozconfigWhitelist['release'][platform]:
    1.94 +                        continue
    1.95 +            elif line[0] == '+':
    1.96 +                if platform in mozconfigWhitelist.get('nightly', {}):
    1.97 +                    if clean_line in \
    1.98 +                            mozconfigWhitelist['nightly'][platform]:
    1.99 +                        continue
   1.100 +                    else:
   1.101 +                        log.warning("%s not in %s %s!" % (
   1.102 +                            clean_line, platform,
   1.103 +                            mozconfigWhitelist['nightly'][platform]))
   1.104 +            else:
   1.105 +                log.error("Skipping line %s!" % line)
   1.106 +                continue
   1.107 +            message = "found in %s but not in %s: %s"
   1.108 +            if line[0] == '-':
   1.109 +                log.error(message % (mozconfig_name,
   1.110 +                                     nightly_mozconfig_name, clean_line))
   1.111 +            else:
   1.112 +                log.error(message % (nightly_mozconfig_name,
   1.113 +                                     mozconfig_name, clean_line))
   1.114 +            success = False
   1.115 +    return success
   1.116 +
   1.117 +def get_mozconfig(path, options):
   1.118 +    """Consumes a path and returns a list of lines from
   1.119 +    the mozconfig file. If download is required, the path
   1.120 +    specified should be relative to the root of the hg
   1.121 +    repository e.g browser/config/mozconfigs/linux32/nightly"""
   1.122 +    if options.no_download:
   1.123 +        return open(path, 'r').readlines()
   1.124 +    else:
   1.125 +        url = make_hg_url(options.hghost, options.branch, 'http',
   1.126 +                    options.revision, path)
   1.127 +        return urllib2.urlopen(url).readlines()
   1.128 +
   1.129 +if __name__ == '__main__':
   1.130 +    from optparse import OptionParser
   1.131 +    parser = OptionParser()
   1.132 +
   1.133 +    parser.add_option('--branch', dest='branch')
   1.134 +    parser.add_option('--revision', dest='revision')
   1.135 +    parser.add_option('--hghost', dest='hghost', default='hg.mozilla.org')
   1.136 +    parser.add_option('--whitelist', dest='whitelist')
   1.137 +    parser.add_option('--no-download', action='store_true', dest='no_download',
   1.138 +                      default=False)
   1.139 +    options, args = parser.parse_args()
   1.140 +
   1.141 +    logging.basicConfig(level=logging.INFO)
   1.142 +
   1.143 +    missing_args = options.branch is None or options.revision is None
   1.144 +    if not options.no_download and missing_args:
   1.145 +        logging.error('Not enough arguments to download mozconfigs')
   1.146 +        sys.exit(FAILURE_CODE)
   1.147 +
   1.148 +    mozconfig_whitelist = readConfig(options.whitelist, ['whitelist'])
   1.149 +
   1.150 +    for arg in args:
   1.151 +        platform, mozconfig_path, nightly_mozconfig_path = arg.split(',')
   1.152 +
   1.153 +        mozconfig_lines = get_mozconfig(mozconfig_path, options)
   1.154 +        nightly_mozconfig_lines = get_mozconfig(nightly_mozconfig_path, options)
   1.155 +
   1.156 +        mozconfig_pair = (mozconfig_path, mozconfig_lines)
   1.157 +        nightly_mozconfig_pair = (nightly_mozconfig_path,
   1.158 +                                  nightly_mozconfig_lines)
   1.159 +
   1.160 +        passed = verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair,
   1.161 +                                   platform, mozconfig_whitelist)
   1.162 +
   1.163 +        if passed:
   1.164 +            logging.info('Mozconfig check passed!')
   1.165 +        else:
   1.166 +            logging.error('Mozconfig check failed!')
   1.167 +            sys.exit(FAILURE_CODE)
   1.168 +    sys.exit(SUCCESS_CODE)

mercurial