Blame SPECS/gen_updates2.py

Packit Service 2ad00c
#! /usr/bin/python
Packit Service 2ad00c
# SPDX-License-Identifier: CC0-1.0
Packit Service 2ad00c
Packit Service 2ad00c
import argparse
Packit Service 2ad00c
import errno
Packit Service 2ad00c
import io
Packit Service 2ad00c
import itertools
Packit Service 2ad00c
import os
Packit Service 2ad00c
import re
Packit Service 2ad00c
import shutil
Packit Service 2ad00c
import struct
Packit Service 2ad00c
import sys
Packit Service 2ad00c
import tempfile
Packit Service 2ad00c
from subprocess import PIPE, Popen, STDOUT
Packit Service 2ad00c
Packit Service 2ad00c
# Python 3 shims
Packit Service 2ad00c
try:
Packit Service 2ad00c
    from functools import reduce
Packit Service 2ad00c
except:
Packit Service 2ad00c
    pass
Packit Service 2ad00c
try:
Packit Service 2ad00c
    from itertools import zip_longest as izip_longest
Packit Service 2ad00c
except:
Packit Service 2ad00c
    from itertools import izip_longest
Packit Service 2ad00c
Packit Service 2ad00c
# revs:
Packit Service 2ad00c
# [ { "path", "cpuid", "pf", "rev", "date" } ]
Packit Service 2ad00c
Packit Service 2ad00c
# artifacts:
Packit Service 2ad00c
#  * content summary (per-file)
Packit Service 2ad00c
#   * overlay summary (per-fms/pf)
Packit Service 2ad00c
#  * changelog (per-file?)
Packit Service 2ad00c
#  * discrepancies (per-fms/pf)
Packit Service 2ad00c
Packit Service 2ad00c
log_level = 0
Packit Service 2ad00c
print_date = False
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def log_status(msg, level=0):
Packit Service 2ad00c
    global log_level
Packit Service 2ad00c
Packit Service 2ad00c
    if log_level >= level:
Packit Service 2ad00c
        sys.stderr.write(msg + "\n")
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def log_info(msg, level=2):
Packit Service 2ad00c
    global log_level
Packit Service 2ad00c
Packit Service 2ad00c
    if log_level >= level:
Packit Service 2ad00c
        sys.stderr.write("INFO: " + msg + "\n")
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def log_warn(msg, level=1):
Packit Service 2ad00c
    global log_level
Packit Service 2ad00c
Packit Service 2ad00c
    if log_level >= level:
Packit Service 2ad00c
        sys.stderr.write("WARNING: " + msg + "\n")
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def log_error(msg, level=-1):
Packit Service 2ad00c
    global log_level
Packit Service 2ad00c
Packit Service 2ad00c
    if log_level >= level:
Packit Service 2ad00c
        sys.stderr.write("ERROR: " + msg + "\n")
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def remove_prefix(text, prefix):
Packit Service 2ad00c
    if isinstance(prefix, str):
Packit Service 2ad00c
        prefix = [prefix, ]
Packit Service 2ad00c
Packit Service 2ad00c
    for p in prefix:
Packit Service 2ad00c
        pfx = p if p.endswith(os.sep) else p + os.sep
Packit Service 2ad00c
        if text.startswith(pfx):
Packit Service 2ad00c
            return text[len(pfx):]
Packit Service 2ad00c
Packit Service 2ad00c
    return text
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def file_walk(args, yield_dirs=False):
Packit Service 2ad00c
    for content in args:
Packit Service 2ad00c
        if os.path.isdir(content):
Packit Service 2ad00c
            if yield_dirs:
Packit Service 2ad00c
                yield ("", content)
Packit Service 2ad00c
            for root, dirs, files in os.walk(content):
Packit Service 2ad00c
                if yield_dirs:
Packit Service 2ad00c
                    for f in dirs:
Packit Service 2ad00c
                        p = os.path.join(root, f)
Packit Service 2ad00c
                        yield (remove_prefix(p, content), p)
Packit Service 2ad00c
                for f in files:
Packit Service 2ad00c
                    p = os.path.join(root, f)
Packit Service 2ad00c
                    yield (remove_prefix(p, content), p)
Packit Service 2ad00c
        elif os.path.exists(content):
Packit Service 2ad00c
            yield ("", content)
Packit Service 2ad00c
        else:
Packit Service 2ad00c
            raise IOError(errno.ENOENT, os.strerror(errno.ENOENT), content)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def cpuid_fname(c):
Packit Service 2ad00c
    return "%02x-%02x-%02x" % (
Packit Service 2ad00c
        ((c >> 16) & 0xff0) + ((c >> 8) & 0xf),
Packit Service 2ad00c
        ((c >> 12) & 0xf0) + ((c >> 4) & 0xf),
Packit Service 2ad00c
        c & 0xf)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def read_revs_dir(path, src=None, ret=None):
Packit Service 2ad00c
    if ret is None:
Packit Service 2ad00c
        ret = []
Packit Service 2ad00c
Packit Service 2ad00c
    ucode_re = re.compile('[0-9a-f]{2}-[0-9a-f]{2}-0[0-9a-f]$')
Packit Service 2ad00c
    ucode_dat_re = re.compile('microcode.*\.dat$')
Packit Service 2ad00c
Packit Service 2ad00c
    for rp, ap in file_walk([path, ]):
Packit Service 2ad00c
        rp_fname = os.path.basename(rp)
Packit Service 2ad00c
        if not ucode_re.match(rp_fname) and not ucode_dat_re.match(rp_fname):
Packit Service 2ad00c
            continue
Packit Service 2ad00c
Packit Service 2ad00c
        # Text-based format
Packit Service 2ad00c
        data = None
Packit Service 2ad00c
        if ucode_dat_re.match(rp_fname):
Packit Service 2ad00c
            data = io.BytesIO()
Packit Service 2ad00c
            with open(ap, "r") as f:
Packit Service 2ad00c
                for line in f:
Packit Service 2ad00c
                    if line.startswith("/"):
Packit Service 2ad00c
                        continue
Packit Service 2ad00c
                    vals = line.split(",")
Packit Service 2ad00c
                    for val in vals:
Packit Service 2ad00c
                        val = val.strip()
Packit Service 2ad00c
                        if not val:
Packit Service 2ad00c
                            continue
Packit Service 2ad00c
                        data.write(struct.pack("
Packit Service 2ad00c
            sz = data.seek(0, os.SEEK_CUR)
Packit Service 2ad00c
            data.seek(0, os.SEEK_SET)
Packit Service 2ad00c
        else:
Packit Service 2ad00c
            sz = os.stat(ap).st_size
Packit Service 2ad00c
Packit Service 2ad00c
        try:
Packit Service 2ad00c
            with data or open(ap, "rb") as f:
Packit Service 2ad00c
                log_info("Processing %s" % ap)
Packit Service 2ad00c
                offs = 0
Packit Service 2ad00c
                while offs < sz:
Packit Service 2ad00c
                    f.seek(offs, os.SEEK_SET)
Packit Service 2ad00c
                    hdr = struct.unpack("IiIIIIIIIIII", f.read(48))
Packit Service 2ad00c
                    ret.append({"path": rp, "src": src or path,
Packit Service 2ad00c
                                "cpuid": hdr[3], "pf": hdr[6], "rev": hdr[1],
Packit Service 2ad00c
                                "date": hdr[2], "offs": offs, "cksum": hdr[4],
Packit Service 2ad00c
                                "data_size": hdr[7], "total_size": hdr[8]})
Packit Service 2ad00c
Packit Service 2ad00c
                    if hdr[8] and hdr[8] - hdr[7] > 48:
Packit Service 2ad00c
                        f.seek(hdr[7], os.SEEK_CUR)
Packit Service 2ad00c
                        ext_tbl = struct.unpack("IIIII", f.read(20))
Packit Service 2ad00c
                        log_status("Found %u extended signatures for %s:%#x" %
Packit Service 2ad00c
                                   (ext_tbl[0], rp, offs), level=1)
Packit Service 2ad00c
Packit Service 2ad00c
                        cur_offs = offs + hdr[7] + 48 + 20
Packit Service 2ad00c
                        ext_sig_cnt = 0
Packit Service 2ad00c
                        while cur_offs < offs + hdr[8] \
Packit Service 2ad00c
                                and ext_sig_cnt <= ext_tbl[0]:
Packit Service 2ad00c
                            ext_sig = struct.unpack("III", f.read(12))
Packit Service 2ad00c
                            ret.append({"path": rp, "src": src or path,
Packit Service 2ad00c
                                        "cpuid": ext_sig[0], "pf": ext_sig[1],
Packit Service 2ad00c
                                        "rev": hdr[1], "date": hdr[2],
Packit Service 2ad00c
                                        "offs": offs, "ext_offs": cur_offs,
Packit Service 2ad00c
                                        "cksum": hdr[4],
Packit Service 2ad00c
                                        "ext_cksum": ext_sig[2],
Packit Service 2ad00c
                                        "data_size": hdr[7],
Packit Service 2ad00c
                                        "total_size": hdr[8]})
Packit Service 2ad00c
                            log_status(("Got ext sig %#x/%#x for " +
Packit Service 2ad00c
                                        "%s:%#x:%#x/%#x") %
Packit Service 2ad00c
                                       (ext_sig[0], ext_sig[1], rp, offs,
Packit Service 2ad00c
                                        hdr[3], hdr[6]), level=2)
Packit Service 2ad00c
Packit Service 2ad00c
                            cur_offs += 12
Packit Service 2ad00c
                            ext_sig_cnt += 1
Packit Service 2ad00c
Packit Service 2ad00c
                    offs += hdr[8] or 2048
Packit Service 2ad00c
        except Exception as e:
Packit Service 2ad00c
            log_error("a problem occurred while processing %s: %s" % (ap, e),
Packit Service 2ad00c
                      level=1)
Packit Service 2ad00c
Packit Service 2ad00c
    return ret
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def read_revs_rpm(path, ret=None):
Packit Service 2ad00c
    if ret is None:
Packit Service 2ad00c
        ret = []
Packit Service 2ad00c
Packit Service 2ad00c
    dir_tmp = tempfile.mkdtemp()
Packit Service 2ad00c
Packit Service 2ad00c
    log_status("Trying to extract files from RPM \"%s\"..." % path,
Packit Service 2ad00c
               level=1)
Packit Service 2ad00c
Packit Service 2ad00c
    rpm2cpio = Popen(args=["rpm2cpio", path], stdout=PIPE, stderr=PIPE,
Packit Service 2ad00c
                     close_fds=True)
Packit Service 2ad00c
    cpio = Popen(args=["cpio", "-idmv", "*??-??-??", "*microcode*.dat"],
Packit Service 2ad00c
                 cwd=dir_tmp, stdin=rpm2cpio.stdout,
Packit Service 2ad00c
                 stdout=PIPE, stderr=STDOUT)
Packit Service 2ad00c
    out, cpio_stderr = cpio.communicate()
Packit Service 2ad00c
    rpm2cpio_out, rpm2cpio_err = rpm2cpio.communicate()
Packit Service 2ad00c
Packit Service 2ad00c
    rpm2cpio_ret = rpm2cpio.returncode
Packit Service 2ad00c
    cpio_ret = cpio.returncode
Packit Service 2ad00c
Packit Service 2ad00c
    log_info("rpm2cpio exit code: %d, cpio exit code: %d" %
Packit Service 2ad00c
             (rpm2cpio_ret, cpio_ret))
Packit Service 2ad00c
    if rpm2cpio_err:
Packit Service 2ad00c
        log_info("rpm2cpio stderr:\n%s" % rpm2cpio_err, level=3)
Packit Service 2ad00c
    if out:
Packit Service 2ad00c
        log_info("cpio output:\n%s" % out, level=3)
Packit Service 2ad00c
    if cpio_stderr:
Packit Service 2ad00c
        log_info("cpio stderr:\n%s" % cpio_stderr, level=3)
Packit Service 2ad00c
Packit Service 2ad00c
    if rpm2cpio_ret == 0 and cpio_ret == 0:
Packit Service 2ad00c
        ret = read_revs_dir(dir_tmp, path)
Packit Service 2ad00c
Packit Service 2ad00c
    shutil.rmtree(dir_tmp)
Packit Service 2ad00c
Packit Service 2ad00c
    return ret
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def read_revs(path, ret=None):
Packit Service 2ad00c
    if ret is None:
Packit Service 2ad00c
        ret = []
Packit Service 2ad00c
    if os.path.isdir(path):
Packit Service 2ad00c
        return read_revs_dir(path, ret)
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        return read_revs_rpm(path, ret)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def gen_mc_map(mc_data, merge=False, merge_path=False):
Packit Service 2ad00c
    """
Packit Service 2ad00c
    Converts an array of microcode file information to a map with path/sig/pf
Packit Service 2ad00c
    as a key.
Packit Service 2ad00c
Packit Service 2ad00c
    merge: whether to leave only the newest mc variant in the map or leave all
Packit Service 2ad00c
           possible variants.
Packit Service 2ad00c
    """
Packit Service 2ad00c
    res = dict()
Packit Service 2ad00c
Packit Service 2ad00c
    for mc in mc_data:
Packit Service 2ad00c
        key = (None if merge_path else mc["path"], mc["cpuid"], mc["pf"])
Packit Service 2ad00c
Packit Service 2ad00c
        if key not in res:
Packit Service 2ad00c
            res[key] = dict()
Packit Service 2ad00c
Packit Service 2ad00c
        cpuid = mc["cpuid"]
Packit Service 2ad00c
        cur_pf = mc["pf"]
Packit Service 2ad00c
        pid = 1
Packit Service 2ad00c
        while cur_pf > 0:
Packit Service 2ad00c
            if cur_pf & 1 and not (merge and pid in res[key]
Packit Service 2ad00c
                                   and res[key][pid]["rev"][0] >= mc["rev"]):
Packit Service 2ad00c
                if pid not in res[cpuid] or merge:
Packit Service 2ad00c
                    res[cpuid][pid] = []
Packit Service 2ad00c
                res[cpuid][pid].append(mc)
Packit Service 2ad00c
Packit Service 2ad00c
            cur_pf = cur_pf / 2
Packit Service 2ad00c
            pid = pid * 2
Packit Service 2ad00c
Packit Service 2ad00c
    return res
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def gen_fn_map(mc_data, merge=False, merge_path=False):
Packit Service 2ad00c
    res = dict()
Packit Service 2ad00c
Packit Service 2ad00c
    for mc in mc_data:
Packit Service 2ad00c
        key = (None if merge_path else mc["path"], mc["cpuid"], mc["pf"])
Packit Service 2ad00c
        if key in res:
Packit Service 2ad00c
            log_warn("Duplicate path/cpuid/pf: %s/%#x/%#x" % key)
Packit Service 2ad00c
        else:
Packit Service 2ad00c
            res[key] = []
Packit Service 2ad00c
        if merge and len(res[key]):
Packit Service 2ad00c
            if mc["rev"] > res[key][0]["rev"]:
Packit Service 2ad00c
                res[key][0] = mc
Packit Service 2ad00c
        else:
Packit Service 2ad00c
            res[key].append(mc)
Packit Service 2ad00c
Packit Service 2ad00c
    return res
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def revcmp(a, b):
Packit Service 2ad00c
    return b["rev"] - a["rev"]
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
class ChangeLogEntry:
Packit Service 2ad00c
    ADDED = 0
Packit Service 2ad00c
    REMOVED = 1
Packit Service 2ad00c
    UPDATED = 2
Packit Service 2ad00c
    DOWNGRADED = 3
Packit Service 2ad00c
    OTHER = 4
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_stripped_path(mc):
Packit Service 2ad00c
    paths = ("usr/share/microcode_ctl/ucode_with_caveats/intel",
Packit Service 2ad00c
             "usr/share/microcode_ctl/ucode_with_caveats",
Packit Service 2ad00c
             "usr/share/microcode_ctl",
Packit Service 2ad00c
             "lib/firmware",
Packit Service 2ad00c
             "etc/firmware",
Packit Service 2ad00c
             )
Packit Service 2ad00c
Packit Service 2ad00c
    return remove_prefix(mc["path"], paths)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
class mcnm:
Packit Service 2ad00c
    MCNM_ABBREV = 0
Packit Service 2ad00c
    MCNM_FAMILIES = 1
Packit Service 2ad00c
    MCNM_MODELS = 2
Packit Service 2ad00c
    MCNM_FAMILIES_MODELS = 3
Packit Service 2ad00c
    MCNM_CODENAME = 4
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def get_mc_cnames(mc, cmap, mode=mcnm.MCNM_ABBREV):
Packit Service 2ad00c
    if not isinstance(mc, dict):
Packit Service 2ad00c
        mc = mc_from_mc_key(mc)
Packit Service 2ad00c
    sig = mc["cpuid"]
Packit Service 2ad00c
    pf = mc["pf"]
Packit Service 2ad00c
    res = []
Packit Service 2ad00c
Packit Service 2ad00c
    if not cmap:
Packit Service 2ad00c
        return None
Packit Service 2ad00c
    if sig not in cmap:
Packit Service 2ad00c
        log_info("No codename information for sig %#x" % sig)
Packit Service 2ad00c
        return None
Packit Service 2ad00c
Packit Service 2ad00c
    cnames = cmap[sig]
Packit Service 2ad00c
Packit Service 2ad00c
    if mode in (mcnm.MCNM_FAMILIES, mcnm.MCNM_MODELS,
Packit Service 2ad00c
                mcnm.MCNM_FAMILIES_MODELS):
Packit Service 2ad00c
        for c in cnames:
Packit Service 2ad00c
            if not (pf & c["pf_mask"]):
Packit Service 2ad00c
                continue
Packit Service 2ad00c
            for m, f in ((mcnm.MCNM_FAMILIES, "families"),
Packit Service 2ad00c
                         (mcnm.MCNM_MODELS, "models")):
Packit Service 2ad00c
                if m & mode == 0:
Packit Service 2ad00c
                    continue
Packit Service 2ad00c
                if f not in c or not c[f]:
Packit Service 2ad00c
                    log_info("No %s for sig %#x in %r" % (f, sig, c))
Packit Service 2ad00c
                    continue
Packit Service 2ad00c
Packit Service 2ad00c
                res.append(c[f])
Packit Service 2ad00c
Packit Service 2ad00c
        return ", ".join(res) or None
Packit Service 2ad00c
Packit Service 2ad00c
    steppings = dict()
Packit Service 2ad00c
    suffices = dict()
Packit Service 2ad00c
    for c in cnames:
Packit Service 2ad00c
        if pf and not (pf & c["pf_mask"]):
Packit Service 2ad00c
            continue
Packit Service 2ad00c
Packit Service 2ad00c
        if mode == mcnm.MCNM_ABBREV and "abbrev" in c and c["abbrev"]:
Packit Service 2ad00c
            cname = c["abbrev"]
Packit Service 2ad00c
        else:
Packit Service 2ad00c
            cname = c["codename"]
Packit Service 2ad00c
Packit Service 2ad00c
        if cname not in suffices:
Packit Service 2ad00c
            suffices[cname] = set()
Packit Service 2ad00c
        if "variant" in c and c["variant"]:
Packit Service 2ad00c
            suffices[cname] |= set(c["variant"])
Packit Service 2ad00c
Packit Service 2ad00c
        if cname not in steppings:
Packit Service 2ad00c
            steppings[cname] = set()
Packit Service 2ad00c
        if c["stepping"]:
Packit Service 2ad00c
            steppings[cname] |= set(c["stepping"])
Packit Service 2ad00c
Packit Service 2ad00c
    for cname in sorted(steppings.keys()):
Packit Service 2ad00c
        cname_str = cname
Packit Service 2ad00c
        if len(suffices[cname]):
Packit Service 2ad00c
            cname_str += "-" + "/".join(sorted(suffices[cname]))
Packit Service 2ad00c
        if len(steppings[cname]):
Packit Service 2ad00c
            cname_str += " " + "/".join(sorted(steppings[cname]))
Packit Service 2ad00c
        res.append(cname_str)
Packit Service 2ad00c
Packit Service 2ad00c
    return ", ".join(res) or None
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_from_mc_key(k):
Packit Service 2ad00c
    return dict(zip(("path", "cpuid", "pf"), k))
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_path(mc, pf_sfx=True, midword=None, cmap=None):
Packit Service 2ad00c
    if not isinstance(mc, dict):
Packit Service 2ad00c
        mc = mc_from_mc_key(mc)
Packit Service 2ad00c
    path = mc_stripped_path(mc) if mc["path"] is not None else None
Packit Service 2ad00c
    cpuid_fn = cpuid_fname(mc["cpuid"])
Packit Service 2ad00c
    fname = os.path.basename(mc["path"] or cpuid_fn)
Packit Service 2ad00c
    midword = "" if midword is None else " " + midword
Packit Service 2ad00c
    cname = get_mc_cnames(mc, cmap)
Packit Service 2ad00c
    cname_str = " (" + cname + ")" if cname else ""
Packit Service 2ad00c
Packit Service 2ad00c
    if pf_sfx:
Packit Service 2ad00c
        sfx = "/0x%02x" % mc["pf"]
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        sfx = ""
Packit Service 2ad00c
Packit Service 2ad00c
    if not path or path == os.path.join("intel-ucode", cpuid_fn):
Packit Service 2ad00c
        return "%s%s%s%s" % (fname, sfx, cname_str, midword)
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        return "%s%s%s%s (in %s)" % (cpuid_fn, sfx, cname_str, midword, path)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def gen_changelog_file(old, new):
Packit Service 2ad00c
    pass
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_cmp(old_mc, new_mc):
Packit Service 2ad00c
    res = []
Packit Service 2ad00c
Packit Service 2ad00c
    old_mc_revs = [x["rev"] for x in old_mc]
Packit Service 2ad00c
    new_mc_revs = [x["rev"] for x in new_mc]
Packit Service 2ad00c
    common = set(old_mc_revs) & set(new_mc_revs)
Packit Service 2ad00c
    old_rev_list = [x for x in sorted(old_mc_revs) if x not in common]
Packit Service 2ad00c
    new_rev_list = [x for x in sorted(new_mc_revs) if x not in common]
Packit Service 2ad00c
Packit Service 2ad00c
    if len(old_rev_list) != 1 or len(new_rev_list) != 1:
Packit Service 2ad00c
        for i in new_mc:
Packit Service 2ad00c
            if i["rev"] in new_rev_list:
Packit Service 2ad00c
                res.append((ChangeLogEntry.ADDED, None, i))
Packit Service 2ad00c
        for i in old_mc:
Packit Service 2ad00c
            if i["rev"] in old_rev_list:
Packit Service 2ad00c
                res.append((ChangeLogEntry.REMOVED, i, None))
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        for old in old_mc:
Packit Service 2ad00c
            if old["rev"] == old_rev_list[0]:
Packit Service 2ad00c
                break
Packit Service 2ad00c
        for new in new_mc:
Packit Service 2ad00c
            if new["rev"] == new_rev_list[0]:
Packit Service 2ad00c
                break
Packit Service 2ad00c
        if new["rev"] > old["rev"]:
Packit Service 2ad00c
            res.append((ChangeLogEntry.UPDATED, old, new))
Packit Service 2ad00c
        elif new["rev"] < old["rev"]:
Packit Service 2ad00c
            res.append((ChangeLogEntry.DOWNGRADED, old, new))
Packit Service 2ad00c
Packit Service 2ad00c
    return res
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def gen_changelog(old, new):
Packit Service 2ad00c
    res = []
Packit Service 2ad00c
Packit Service 2ad00c
    old_map = gen_fn_map(old)
Packit Service 2ad00c
    new_map = gen_fn_map(new)
Packit Service 2ad00c
Packit Service 2ad00c
    old_files = set(old_map.keys())
Packit Service 2ad00c
    new_files = set(new_map.keys())
Packit Service 2ad00c
Packit Service 2ad00c
    both = old_files & new_files
Packit Service 2ad00c
    added = new_files - old_files
Packit Service 2ad00c
    removed = old_files - new_files
Packit Service 2ad00c
Packit Service 2ad00c
    for f in sorted(added):
Packit Service 2ad00c
        p = mc_path(new_map[f][0])
Packit Service 2ad00c
        for old_f in sorted(removed):
Packit Service 2ad00c
            old_p = mc_path(old_map[old_f][0])
Packit Service 2ad00c
            if p == old_p and f[1] == old_f[1] and f[2] == old_f[2]:
Packit Service 2ad00c
                log_info("Matched %s (%s and %s)" %
Packit Service 2ad00c
                         (p, old_map[old_f][0]["path"], new_map[f][0]["path"]))
Packit Service 2ad00c
                added.remove(f)
Packit Service 2ad00c
                removed.remove(old_f)
Packit Service 2ad00c
Packit Service 2ad00c
                res += mc_cmp(old_map[old_f], new_map[f])
Packit Service 2ad00c
Packit Service 2ad00c
    for f in sorted(added):
Packit Service 2ad00c
        for i in new_map[f]:
Packit Service 2ad00c
            res.append((ChangeLogEntry.ADDED, None, i))
Packit Service 2ad00c
    for f in sorted(removed):
Packit Service 2ad00c
        for i in old_map[f]:
Packit Service 2ad00c
            res.append((ChangeLogEntry.REMOVED, i, None))
Packit Service 2ad00c
    for f in sorted(both):
Packit Service 2ad00c
        res += mc_cmp(old_map[f], new_map[f])
Packit Service 2ad00c
Packit Service 2ad00c
    return res
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_date(mc):
Packit Service 2ad00c
    if isinstance(mc, dict):
Packit Service 2ad00c
        mc = mc["date"]
Packit Service 2ad00c
    return "%04x-%02x-%02x" % (mc & 0xffff, mc >> 24, (mc >> 16) & 0xff)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def mc_rev(mc, date=None):
Packit Service 2ad00c
    '''
Packit Service 2ad00c
    While revision is signed for comparison purposes, historically
Packit Service 2ad00c
    it is printed as unsigned,  Oh well.
Packit Service 2ad00c
    '''
Packit Service 2ad00c
    global print_date
Packit Service 2ad00c
Packit Service 2ad00c
    if mc["rev"] < 0:
Packit Service 2ad00c
        rev = 2**32 + mc["rev"]
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        rev = mc["rev"]
Packit Service 2ad00c
Packit Service 2ad00c
    if date if date is not None else print_date:
Packit Service 2ad00c
        return "%#x (%s)" % (rev, mc_date(mc))
Packit Service 2ad00c
    else:
Packit Service 2ad00c
        return "%#x" % rev
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def print_changelog(clog, cmap, args):
Packit Service 2ad00c
    for e, old, new in sorted(clog):
Packit Service 2ad00c
        if e == ChangeLogEntry.ADDED:
Packit Service 2ad00c
            print("Addition of %s at revision %s" %
Packit Service 2ad00c
                  (mc_path(new, midword="microcode", cmap=cmap), mc_rev(new)))
Packit Service 2ad00c
        elif e == ChangeLogEntry.REMOVED:
Packit Service 2ad00c
            print("Removal of %s at revision %s" %
Packit Service 2ad00c
                  (mc_path(old, midword="microcode", cmap=cmap), mc_rev(old)))
Packit Service 2ad00c
        elif e == ChangeLogEntry.UPDATED:
Packit Service 2ad00c
            print("Update of %s from revision %s up to %s" %
Packit Service 2ad00c
                  (mc_path(old, midword="microcode", cmap=cmap),
Packit Service 2ad00c
                   mc_rev(old), mc_rev(new)))
Packit Service 2ad00c
        elif e == ChangeLogEntry.DOWNGRADED:
Packit Service 2ad00c
                print("Downgrade of %s from revision %s down to %s" %
Packit Service 2ad00c
                      (mc_path(old, midword="microcode", cmap=cmap),
Packit Service 2ad00c
                       mc_rev(old), mc_rev(new)))
Packit Service 2ad00c
        elif e == ChangeLogEntry.OTHER:
Packit Service 2ad00c
            print("Other change in %s:" % old["path"])
Packit Service 2ad00c
            print("  old: %#x/%#x: rev %s (offs %#x)" %
Packit Service 2ad00c
                  (old["cpuid"], old["pf"], mc_rev(old), old["offs"]))
Packit Service 2ad00c
            print("  new: %#x/%#x: rev %s (offs %#x)" %
Packit Service 2ad00c
                  (new["cpuid"], new["pf"], mc_rev(new), new["offs"]))
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
class TableStyles:
Packit Service 2ad00c
    TS_CSV = 0
Packit Service 2ad00c
    TS_FANCY = 1
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def print_line(line, column_sz):
Packit Service 2ad00c
    print(" | ".join([str(x).ljust(column_sz[i])
Packit Service 2ad00c
                      for i, x in zip(itertools.count(),
Packit Service 2ad00c
                                      itertools.chain(line,
Packit Service 2ad00c
                                      [""] * (len(column_sz) -
Packit Service 2ad00c
                                              len(line))))]).rstrip())
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def print_table(items, header=[], style=TableStyles.TS_CSV):
Packit Service 2ad00c
    if style == TableStyles.TS_CSV:
Packit Service 2ad00c
        for i in items:
Packit Service 2ad00c
            print(";".join(i))
Packit Service 2ad00c
    elif style == TableStyles.TS_FANCY:
Packit Service 2ad00c
        column_sz = list(reduce(lambda x, y:
Packit Service 2ad00c
                                map(max, izip_longest(x, y, fillvalue=0)),
Packit Service 2ad00c
                                [[len(x) for x in i]
Packit Service 2ad00c
                                 for i in itertools.chain(header, items)]))
Packit Service 2ad00c
        for i in header:
Packit Service 2ad00c
            print_line(i, column_sz)
Packit Service 2ad00c
        if header:
Packit Service 2ad00c
            print("-+-".join(["-" * x for x in column_sz]))
Packit Service 2ad00c
        for i in items:
Packit Service 2ad00c
            print_line(i, column_sz)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def print_summary(revs, cmap, args):
Packit Service 2ad00c
    m = gen_fn_map(revs)
Packit Service 2ad00c
    cnames_mode = mcnm.MCNM_ABBREV if args.abbrev else mcnm.MCNM_CODENAME
Packit Service 2ad00c
Packit Service 2ad00c
    header = []
Packit Service 2ad00c
    if args.header:
Packit Service 2ad00c
        header.append(["Path", "Offset", "Ext. Offset", "CPUID",
Packit Service 2ad00c
                       "Platform ID Mask", "Revision", "Date", "Checksum",
Packit Service 2ad00c
                       "Codenames"] +
Packit Service 2ad00c
                      (["Models"] if args.models else []))
Packit Service 2ad00c
    tbl = []
Packit Service 2ad00c
    for k in sorted(m.keys()):
Packit Service 2ad00c
        for mc in m[k]:
Packit Service 2ad00c
            tbl.append([mc_stripped_path(mc),
Packit Service 2ad00c
                        "0x%x" % mc["offs"],
Packit Service 2ad00c
                        "0x%x" % mc["ext_offs"] if "ext_offs" in mc else "-",
Packit Service 2ad00c
                        "0x%05x" % mc["cpuid"],
Packit Service 2ad00c
                        "0x%02x" % mc["pf"],
Packit Service 2ad00c
                        mc_rev(mc, date=False),
Packit Service 2ad00c
                        mc_date(mc),
Packit Service 2ad00c
                        "0x%08x" % mc["cksum"],
Packit Service 2ad00c
                        get_mc_cnames(mc, cmap, cnames_mode) or ""] +
Packit Service 2ad00c
                       ([get_mc_cnames(mc, cmap,
Packit Service 2ad00c
                                       mcnm.MCNM_FAMILIES_MODELS)]
Packit Service 2ad00c
                        if args.models else []))
Packit Service 2ad00c
Packit Service 2ad00c
    print_table(tbl, header, style=TableStyles.TS_FANCY)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def read_codenames_file(path):
Packit Service 2ad00c
    '''
Packit Service 2ad00c
    Supports two formats: new and old
Packit Service 2ad00c
     * old: tab-separated. Field order:
Packit Service 2ad00c
       Segment, (unused), Codename, (dash-separated) Stepping,
Packit Service 2ad00c
       Platform ID mask, CPUID, (unused) Update link, (unused) Specs link
Packit Service 2ad00c
     * new: semicolon-separated; support comments.  Distinguished
Packit Service 2ad00c
       by the first line that starts with octothorp.  Field order:
Packit Service 2ad00c
       Segment, Unused, Codename, Stepping, Platform ID mask, CPUID,
Packit Service 2ad00c
       Abbreviation, Variant(s), Families, Models
Packit Service 2ad00c
    '''
Packit Service 2ad00c
    old_fields = ["segment", "_", "codename", "stepping", "pf_mask", "sig",
Packit Service 2ad00c
                  "_update", "_specs"]
Packit Service 2ad00c
    new_fields = ["segment", "_", "codename", "stepping", "pf_mask", "sig",
Packit Service 2ad00c
                  "abbrev", "variant", "families", "models"]
Packit Service 2ad00c
    new_fmt = False
Packit Service 2ad00c
    field_names = old_fields
Packit Service 2ad00c
Packit Service 2ad00c
    res = dict()
Packit Service 2ad00c
Packit Service 2ad00c
    try:
Packit Service 2ad00c
        with open(path, "r") as f:
Packit Service 2ad00c
            for line in f:
Packit Service 2ad00c
                line = line.strip()
Packit Service 2ad00c
                if len(line) == 0:
Packit Service 2ad00c
                    continue
Packit Service 2ad00c
                if line[0] == '#':
Packit Service 2ad00c
                    new_fmt = True
Packit Service 2ad00c
                    field_names = new_fields
Packit Service 2ad00c
                    continue
Packit Service 2ad00c
Packit Service 2ad00c
                fields = line.split(";" if new_fmt else "\t",
Packit Service 2ad00c
                                    1 + len(field_names))
Packit Service 2ad00c
                fields = dict(zip(field_names, fields))
Packit Service 2ad00c
                if "sig" not in fields:
Packit Service 2ad00c
                    log_warn("Skipping %r (from \"%s\")" % (fields, line))
Packit Service 2ad00c
                    continue
Packit Service 2ad00c
Packit Service 2ad00c
                sig = fields["sig"] = int(fields["sig"], 16)
Packit Service 2ad00c
                fields["pf_mask"] = int(fields["pf_mask"], 16)
Packit Service 2ad00c
                fields["stepping"] = fields["stepping"].split(",")
Packit Service 2ad00c
                if "variant" in fields:
Packit Service 2ad00c
                    if fields["variant"]:
Packit Service 2ad00c
                        fields["variant"] = fields["variant"].split(",")
Packit Service 2ad00c
                    else:
Packit Service 2ad00c
                        fields["variant"] = []
Packit Service 2ad00c
Packit Service 2ad00c
                if sig not in res:
Packit Service 2ad00c
                    res[sig] = list()
Packit Service 2ad00c
                res[sig].append(fields)
Packit Service 2ad00c
    except Exception as e:
Packit Service 2ad00c
        log_error("a problem occurred while reading code names: %s" % e)
Packit Service 2ad00c
Packit Service 2ad00c
    return res
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def print_discrepancies(rev_map, deps, cmap, args):
Packit Service 2ad00c
    """
Packit Service 2ad00c
    rev_map: dict "name": revs
Packit Service 2ad00c
    deps: list of tuples (name, parent/None)
Packit Service 2ad00c
    """
Packit Service 2ad00c
    sigs = set()
Packit Service 2ad00c
Packit Service 2ad00c
    for p, r in rev_map.items():
Packit Service 2ad00c
        sigs |= set(r.keys())
Packit Service 2ad00c
Packit Service 2ad00c
    if args.header:
Packit Service 2ad00c
        header1 = ["sig"]
Packit Service 2ad00c
        if args.print_vs:
Packit Service 2ad00c
            header2 = [""]
Packit Service 2ad00c
        for p, n, d in deps:
Packit Service 2ad00c
            header1.append(n)
Packit Service 2ad00c
            if args.print_vs:
Packit Service 2ad00c
                add = ""
Packit Service 2ad00c
                if d:
Packit Service 2ad00c
                    for pd, nd, dd in deps:
Packit Service 2ad00c
                        if pd == d:
Packit Service 2ad00c
                            add = "(vs. %s)" % nd
Packit Service 2ad00c
                            break
Packit Service 2ad00c
                header2.append(add)
Packit Service 2ad00c
        if args.models:
Packit Service 2ad00c
            header1.append("Model names")
Packit Service 2ad00c
            if args.print_vs:
Packit Service 2ad00c
                header2.append("")
Packit Service 2ad00c
    header = [header1] + ([header2] if args.print_vs else [])
Packit Service 2ad00c
Packit Service 2ad00c
    tbl = []
Packit Service 2ad00c
    for s in sorted(sigs):
Packit Service 2ad00c
        out = [mc_path(s)]
Packit Service 2ad00c
        print_out = not args.print_filter
Packit Service 2ad00c
        print_date = args.min_date is None
Packit Service 2ad00c
Packit Service 2ad00c
        for p, n, d in deps:
Packit Service 2ad00c
            cur = dict([(x["rev"], x) for x in rev_map[p][s]]) \
Packit Service 2ad00c
                  if s in rev_map[p] else []
Packit Service 2ad00c
            v = "/".join([mc_rev(y) for x, y in sorted(cur.items())]) \
Packit Service 2ad00c
                if cur else "-"
Packit Service 2ad00c
            if d is not None:
Packit Service 2ad00c
                prev = [x["rev"] for x in rev_map[d][s]] if s in rev_map[d] \
Packit Service 2ad00c
                        else []
Packit Service 2ad00c
                if [x for x in cur if x not in prev]:
Packit Service 2ad00c
                    v += " (*)"
Packit Service 2ad00c
                    print_out = True
Packit Service 2ad00c
            if args.min_date is not None and s in rev_map[p]:
Packit Service 2ad00c
                for x in rev_map[p][s]:
Packit Service 2ad00c
                    print_date |= mc_date(x) > args.min_date
Packit Service 2ad00c
            out.append(v)
Packit Service 2ad00c
Packit Service 2ad00c
        if print_out and print_date:
Packit Service 2ad00c
            if args.models:
Packit Service 2ad00c
                out.append(get_mc_cnames(s, cmap) or "")
Packit Service 2ad00c
            tbl.append(out)
Packit Service 2ad00c
Packit Service 2ad00c
    print_table(tbl, header, style=TableStyles.TS_FANCY)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def cmd_summary(args):
Packit Service 2ad00c
    revs = []
Packit Service 2ad00c
    for p in args.filelist:
Packit Service 2ad00c
        revs = read_revs(p, ret=revs)
Packit Service 2ad00c
Packit Service 2ad00c
    codenames_map = read_codenames_file(args.codenames)
Packit Service 2ad00c
Packit Service 2ad00c
    print_summary(revs, codenames_map, args)
Packit Service 2ad00c
Packit Service 2ad00c
    return 0
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def cmd_changelog(args):
Packit Service 2ad00c
    codenames_map = read_codenames_file(args.codenames)
Packit Service 2ad00c
    base_path = args.filelist[0]
Packit Service 2ad00c
    upd_path = args.filelist[1]
Packit Service 2ad00c
Packit Service 2ad00c
    base = read_revs(base_path)
Packit Service 2ad00c
    upd = read_revs(upd_path)
Packit Service 2ad00c
Packit Service 2ad00c
    print_changelog(gen_changelog(base, upd), codenames_map, args)
Packit Service 2ad00c
Packit Service 2ad00c
    return 0
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def cmd_discrepancies(args):
Packit Service 2ad00c
    """
Packit Service 2ad00c
    filenames:
Packit Service 2ad00c
     * "<" prefix (possibly multiple times) to refer to a previous entry
Packit Service 2ad00c
       to compare against
Packit Service 2ad00c
     * "[name]" prefix is a name reference
Packit Service 2ad00c
    """
Packit Service 2ad00c
    codenames_map = read_codenames_file(args.codenames)
Packit Service 2ad00c
    rev_map = dict()
Packit Service 2ad00c
    deps = list()
Packit Service 2ad00c
    cur = -1
Packit Service 2ad00c
Packit Service 2ad00c
    for path in args.filelist:
Packit Service 2ad00c
        orig_path = path
Packit Service 2ad00c
        name = None
Packit Service 2ad00c
        cur += 1
Packit Service 2ad00c
        dep = None
Packit Service 2ad00c
        while True:
Packit Service 2ad00c
            if path[0] == '<':
Packit Service 2ad00c
                path = path[1:]
Packit Service 2ad00c
                dep = cur - 1 if dep is None else dep - 1
Packit Service 2ad00c
            elif path[0] == '[' and path.find(']') > 0:
Packit Service 2ad00c
                pos = path.find(']')
Packit Service 2ad00c
                name = path[1:pos]
Packit Service 2ad00c
                path = path[pos + 1:]
Packit Service 2ad00c
            else:
Packit Service 2ad00c
                break
Packit Service 2ad00c
        if name is None:
Packit Service 2ad00c
            name = path
Packit Service 2ad00c
        if dep is not None and dep < 0:
Packit Service 2ad00c
            log_error("Incorrect dep reference for '%s' (points to index %d)" %
Packit Service 2ad00c
                      (orig_path, dep))
Packit Service 2ad00c
            return 1
Packit Service 2ad00c
        deps.append((path, name, deps[dep][0] if dep is not None else None))
Packit Service 2ad00c
        rev_map[path] = gen_fn_map(read_revs(path), merge=args.merge,
Packit Service 2ad00c
                                   merge_path=True)
Packit Service 2ad00c
Packit Service 2ad00c
    print_discrepancies(rev_map, deps, codenames_map, args)
Packit Service 2ad00c
Packit Service 2ad00c
    return 0
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def parse_cli():
Packit Service 2ad00c
    root_parser = argparse.ArgumentParser(prog="gen_updates",
Packit Service 2ad00c
                                          description="Intel CPU Microcode " +
Packit Service 2ad00c
                                          "parser")
Packit Service 2ad00c
    root_parser.add_argument("-C", "--codenames", default='codenames',
Packit Service 2ad00c
                             help="Code names file")
Packit Service 2ad00c
    root_parser.add_argument("-v", "--verbose", action="count", default=0,
Packit Service 2ad00c
                             help="Increase output verbosity")
Packit Service 2ad00c
Packit Service 2ad00c
    cmdparsers = root_parser.add_subparsers(title="Commands",
Packit Service 2ad00c
                                            help="main gen_updates commands")
Packit Service 2ad00c
Packit Service 2ad00c
    parser_s = cmdparsers.add_parser("summary",
Packit Service 2ad00c
                                     help="Generate microcode summary")
Packit Service 2ad00c
    parser_s.add_argument("-a", "--abbreviate", action="store_const",
Packit Service 2ad00c
                          dest="abbrev", const=True, default=True,
Packit Service 2ad00c
                          help="Abbreviate code names")
Packit Service 2ad00c
    parser_s.add_argument("-A", "--no-abbreviate", action="store_const",
Packit Service 2ad00c
                          dest="abbrev", const=False,
Packit Service 2ad00c
                          help="Do not abbreviate code names")
Packit Service 2ad00c
    parser_s.add_argument("-m", "--print-models", action="store_const",
Packit Service 2ad00c
                          dest="models", const=True, default=False,
Packit Service 2ad00c
                          help="Print models")
Packit Service 2ad00c
    parser_s.add_argument("-M", "--no-print-models",
Packit Service 2ad00c
                          action="store_const", dest="models",
Packit Service 2ad00c
                          const=False, help="Do not print models")
Packit Service 2ad00c
    parser_s.add_argument("-H", "--no-print-header",
Packit Service 2ad00c
                          action="store_const", dest="header",
Packit Service 2ad00c
                          const=False, default=True,
Packit Service 2ad00c
                          help="Do not print hader")
Packit Service 2ad00c
    parser_s.add_argument("filelist", nargs="*", default=[],
Packit Service 2ad00c
                          help="List or RPMs/directories to process")
Packit Service 2ad00c
    parser_s.set_defaults(func=cmd_summary)
Packit Service 2ad00c
Packit Service 2ad00c
    parser_c = cmdparsers.add_parser("changelog",
Packit Service 2ad00c
                                     help="Generate changelog")
Packit Service 2ad00c
    parser_c.add_argument("filelist", nargs=2,
Packit Service 2ad00c
                          help="RPMs/directories to compare")
Packit Service 2ad00c
    parser_c.set_defaults(func=cmd_changelog)
Packit Service 2ad00c
Packit Service 2ad00c
    parser_d = cmdparsers.add_parser("discrepancies",
Packit Service 2ad00c
                                     help="Generate discrepancies")
Packit Service 2ad00c
    parser_d.add_argument("-s", "--merge-revs", action="store_const",
Packit Service 2ad00c
                          dest="merge", const=True, default=False,
Packit Service 2ad00c
                          help="Merge revisions that come" +
Packit Service 2ad00c
                               " from different files")
Packit Service 2ad00c
    parser_d.add_argument("-S", "--no-merge-revs", action="store_const",
Packit Service 2ad00c
                          dest="merge", const=False,
Packit Service 2ad00c
                          help="Do not Merge revisions that come" +
Packit Service 2ad00c
                               " from different files")
Packit Service 2ad00c
    parser_d.add_argument("-v", "--print-vs", action="store_const",
Packit Service 2ad00c
                          dest="print_vs", const=True, default=False,
Packit Service 2ad00c
                          help="Print base version ")
Packit Service 2ad00c
    parser_d.add_argument("-V", "--no-print-vs", action="store_const",
Packit Service 2ad00c
                          dest="print_vs", const=False,
Packit Service 2ad00c
                          help="Do not Merge revisions that come" +
Packit Service 2ad00c
                               " from different files")
Packit Service 2ad00c
    parser_d.add_argument("-m", "--print-models", action="store_const",
Packit Service 2ad00c
                          dest="models", const=True, default=True,
Packit Service 2ad00c
                          help="Print model names")
Packit Service 2ad00c
    parser_d.add_argument("-M", "--no-print-models", action="store_const",
Packit Service 2ad00c
                          dest="models", const=False,
Packit Service 2ad00c
                          help="Do not print model names")
Packit Service 2ad00c
    parser_d.add_argument("-H", "--no-print-header", action="store_const",
Packit Service 2ad00c
                          dest="header", const=False, default=True,
Packit Service 2ad00c
                          help="Do not print hader")
Packit Service 2ad00c
    parser_d.add_argument("-a", "--print-all-files", action="store_const",
Packit Service 2ad00c
                          dest="print_filter", const=False, default=True,
Packit Service 2ad00c
                          help="Print all files")
Packit Service 2ad00c
    parser_d.add_argument("-c", "--print-changed-files", action="store_const",
Packit Service 2ad00c
                          dest="print_filter", const=True,
Packit Service 2ad00c
                          help="Print only changed files")
Packit Service 2ad00c
    parser_d.add_argument("-d", "--min-date", action="store",
Packit Service 2ad00c
                          help="Minimum date filter")
Packit Service 2ad00c
    parser_d.add_argument("filelist", nargs='*',
Packit Service 2ad00c
                          help="RPMs/directories to compare")
Packit Service 2ad00c
    parser_d.set_defaults(func=cmd_discrepancies)
Packit Service 2ad00c
Packit Service 2ad00c
    args = root_parser.parse_args()
Packit Service 2ad00c
    if not hasattr(args, "func"):
Packit Service 2ad00c
        root_parser.print_help()
Packit Service 2ad00c
        return None
Packit Service 2ad00c
    return args
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
def main():
Packit Service 2ad00c
    args = parse_cli()
Packit Service 2ad00c
    if args is None:
Packit Service 2ad00c
        return 1
Packit Service 2ad00c
Packit Service 2ad00c
    return args.func(args)
Packit Service 2ad00c
Packit Service 2ad00c
Packit Service 2ad00c
if __name__ == "__main__":
Packit Service 2ad00c
    sys.exit(main())