#!/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 # NOQA: E501
import difflib
import logging
import os
import unittest
import buildconfig
import mozunit
FAILURE_CODE = 1
SUCCESS_CODE = 0
PLATFORMS = (
"linux32",
"linux64",
"macosx64",
"win32",
"win64",
"win64-aarch64",
)
log = logging.getLogger(__name__)
class ConfigError(Exception):
pass
def readConfig(configfile):
c = {}
execfile(configfile, c)
return c[
"whitelist"]
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
if not mozconfig_lines
or not nightly_mozconfig_lines:
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):
"""Consumes a path and returns a list of lines from the mozconfig file."""
with open(path,
"rb")
as fh:
return fh.readlines()
def compare(topsrcdir):
app = os.path.join(topsrcdir,
"browser")
whitelist = readConfig(os.path.join(app,
"config",
"mozconfigs",
"whitelist"))
success =
True
def normalize_lines(lines):
return {l.strip()
for l
in lines}
for platform
in PLATFORMS:
log.info(
"Comparing platform %s" % platform)
mozconfigs_path = os.path.join(app,
"config",
"mozconfigs", platform)
nightly_path = os.path.join(mozconfigs_path,
"nightly")
beta_path = os.path.join(mozconfigs_path,
"beta")
release_path = os.path.join(mozconfigs_path,
"release")
nightly_lines = get_mozconfig(nightly_path)
beta_lines = get_mozconfig(beta_path)
release_lines = get_mozconfig(release_path)
# Validate that entries in whitelist['nightly'][platform] are actually
# present.
whitelist_normalized = normalize_lines(whitelist[
"nightly"].get(platform, []))
nightly_normalized = normalize_lines(nightly_lines)
for line
in sorted(whitelist_normalized - nightly_normalized):
log.error(
"extra line in nightly whitelist: %s" % line)
success =
False
log.info(
"Comparing beta and nightly mozconfigs")
passed = verify_mozconfigs(
(beta_path, beta_lines), (nightly_path, nightly_lines), platform, whitelist
)
if not passed:
success =
False
log.info(
"Comparing release and nightly mozconfigs")
passed = verify_mozconfigs(
(release_path, release_lines),
(nightly_path, nightly_lines),
platform,
whitelist,
)
if not passed:
success =
False
return success
class TestCompareMozconfigs(unittest.TestCase):
def test_compare_mozconfigs(self):
topsrcdir = buildconfig.substs[
"top_srcdir"]
self.assertTrue(compare(topsrcdir))
if __name__ ==
"__main__":
logging.basicConfig(level=logging.INFO)
mozunit.main()