DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (d38398e5144e)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 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 https://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)