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)