xref: /kernel/linux/linux-5.10/scripts/diffconfig (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci#!/usr/bin/env python3
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
38c2ecf20Sopenharmony_ci#
48c2ecf20Sopenharmony_ci# diffconfig - a tool to compare .config files.
58c2ecf20Sopenharmony_ci#
68c2ecf20Sopenharmony_ci# originally written in 2006 by Matt Mackall
78c2ecf20Sopenharmony_ci#  (at least, this was in his bloatwatch source code)
88c2ecf20Sopenharmony_ci# last worked on 2008 by Tim Bird
98c2ecf20Sopenharmony_ci#
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ciimport sys, os
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cidef usage():
148c2ecf20Sopenharmony_ci    print("""Usage: diffconfig [-h] [-m] [<config1> <config2>]
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciDiffconfig is a simple utility for comparing two .config files.
178c2ecf20Sopenharmony_ciUsing standard diff to compare .config files often includes extraneous and
188c2ecf20Sopenharmony_cidistracting information.  This utility produces sorted output with only the
198c2ecf20Sopenharmony_cichanges in configuration values between the two files.
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ciAdded and removed items are shown with a leading plus or minus, respectively.
228c2ecf20Sopenharmony_ciChanged items show the old and new values on a single line.
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciIf -m is specified, then output will be in "merge" style, which has the
258c2ecf20Sopenharmony_cichanged and new values in kernel config option format.
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciIf no config files are specified, .config and .config.old are used.
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciExample usage:
308c2ecf20Sopenharmony_ci $ diffconfig .config config-with-some-changes
318c2ecf20Sopenharmony_ci-EXT2_FS_XATTR  n
328c2ecf20Sopenharmony_ci CRAMFS  n -> y
338c2ecf20Sopenharmony_ci EXT2_FS  y -> n
348c2ecf20Sopenharmony_ci LOG_BUF_SHIFT  14 -> 16
358c2ecf20Sopenharmony_ci PRINTK_TIME  n -> y
368c2ecf20Sopenharmony_ci""")
378c2ecf20Sopenharmony_ci    sys.exit(0)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci# returns a dictionary of name/value pairs for config items in the file
408c2ecf20Sopenharmony_cidef readconfig(config_file):
418c2ecf20Sopenharmony_ci    d = {}
428c2ecf20Sopenharmony_ci    for line in config_file:
438c2ecf20Sopenharmony_ci        line = line[:-1]
448c2ecf20Sopenharmony_ci        if line[:7] == "CONFIG_":
458c2ecf20Sopenharmony_ci            name, val = line[7:].split("=", 1)
468c2ecf20Sopenharmony_ci            d[name] = val
478c2ecf20Sopenharmony_ci        if line[-11:] == " is not set":
488c2ecf20Sopenharmony_ci            d[line[9:-11]] = "n"
498c2ecf20Sopenharmony_ci    return d
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cidef print_config(op, config, value, new_value):
528c2ecf20Sopenharmony_ci    global merge_style
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci    if merge_style:
558c2ecf20Sopenharmony_ci        if new_value:
568c2ecf20Sopenharmony_ci            if new_value=="n":
578c2ecf20Sopenharmony_ci                print("# CONFIG_%s is not set" % config)
588c2ecf20Sopenharmony_ci            else:
598c2ecf20Sopenharmony_ci                print("CONFIG_%s=%s" % (config, new_value))
608c2ecf20Sopenharmony_ci    else:
618c2ecf20Sopenharmony_ci        if op=="-":
628c2ecf20Sopenharmony_ci            print("-%s %s" % (config, value))
638c2ecf20Sopenharmony_ci        elif op=="+":
648c2ecf20Sopenharmony_ci            print("+%s %s" % (config, new_value))
658c2ecf20Sopenharmony_ci        else:
668c2ecf20Sopenharmony_ci            print(" %s %s -> %s" % (config, value, new_value))
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cidef show_diff():
698c2ecf20Sopenharmony_ci    global merge_style
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci    # parse command line args
728c2ecf20Sopenharmony_ci    if ("-h" in sys.argv or "--help" in sys.argv):
738c2ecf20Sopenharmony_ci        usage()
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci    merge_style = 0
768c2ecf20Sopenharmony_ci    if "-m" in sys.argv:
778c2ecf20Sopenharmony_ci        merge_style = 1
788c2ecf20Sopenharmony_ci        sys.argv.remove("-m")
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci    argc = len(sys.argv)
818c2ecf20Sopenharmony_ci    if not (argc==1 or argc == 3):
828c2ecf20Sopenharmony_ci        print("Error: incorrect number of arguments or unrecognized option")
838c2ecf20Sopenharmony_ci        usage()
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci    if argc == 1:
868c2ecf20Sopenharmony_ci        # if no filenames given, assume .config and .config.old
878c2ecf20Sopenharmony_ci        build_dir=""
888c2ecf20Sopenharmony_ci        if "KBUILD_OUTPUT" in os.environ:
898c2ecf20Sopenharmony_ci            build_dir = os.environ["KBUILD_OUTPUT"]+"/"
908c2ecf20Sopenharmony_ci        configa_filename = build_dir + ".config.old"
918c2ecf20Sopenharmony_ci        configb_filename = build_dir + ".config"
928c2ecf20Sopenharmony_ci    else:
938c2ecf20Sopenharmony_ci        configa_filename = sys.argv[1]
948c2ecf20Sopenharmony_ci        configb_filename = sys.argv[2]
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci    try:
978c2ecf20Sopenharmony_ci        a = readconfig(open(configa_filename))
988c2ecf20Sopenharmony_ci        b = readconfig(open(configb_filename))
998c2ecf20Sopenharmony_ci    except (IOError):
1008c2ecf20Sopenharmony_ci        e = sys.exc_info()[1]
1018c2ecf20Sopenharmony_ci        print("I/O error[%s]: %s\n" % (e.args[0],e.args[1]))
1028c2ecf20Sopenharmony_ci        usage()
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci    # print items in a but not b (accumulate, sort and print)
1058c2ecf20Sopenharmony_ci    old = []
1068c2ecf20Sopenharmony_ci    for config in a:
1078c2ecf20Sopenharmony_ci        if config not in b:
1088c2ecf20Sopenharmony_ci            old.append(config)
1098c2ecf20Sopenharmony_ci    old.sort()
1108c2ecf20Sopenharmony_ci    for config in old:
1118c2ecf20Sopenharmony_ci        print_config("-", config, a[config], None)
1128c2ecf20Sopenharmony_ci        del a[config]
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci    # print items that changed (accumulate, sort, and print)
1158c2ecf20Sopenharmony_ci    changed = []
1168c2ecf20Sopenharmony_ci    for config in a:
1178c2ecf20Sopenharmony_ci        if a[config] != b[config]:
1188c2ecf20Sopenharmony_ci            changed.append(config)
1198c2ecf20Sopenharmony_ci        else:
1208c2ecf20Sopenharmony_ci            del b[config]
1218c2ecf20Sopenharmony_ci    changed.sort()
1228c2ecf20Sopenharmony_ci    for config in changed:
1238c2ecf20Sopenharmony_ci        print_config("->", config, a[config], b[config])
1248c2ecf20Sopenharmony_ci        del b[config]
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci    # now print items in b but not in a
1278c2ecf20Sopenharmony_ci    # (items from b that were in a were removed above)
1288c2ecf20Sopenharmony_ci    new = sorted(b.keys())
1298c2ecf20Sopenharmony_ci    for config in new:
1308c2ecf20Sopenharmony_ci        print_config("+", config, None, b[config])
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cidef main():
1338c2ecf20Sopenharmony_ci    try:
1348c2ecf20Sopenharmony_ci        show_diff()
1358c2ecf20Sopenharmony_ci    except BrokenPipeError:
1368c2ecf20Sopenharmony_ci        # Python flushes standard streams on exit; redirect remaining output
1378c2ecf20Sopenharmony_ci        # to devnull to avoid another BrokenPipeError at shutdown
1388c2ecf20Sopenharmony_ci        devnull = os.open(os.devnull, os.O_WRONLY)
1398c2ecf20Sopenharmony_ci        os.dup2(devnull, sys.stdout.fileno())
1408c2ecf20Sopenharmony_ci        sys.exit(1)  # Python exits with error code 1 on EPIPE
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciif __name__ == '__main__':
1448c2ecf20Sopenharmony_ci    main()
145