1#!/usr/bin/env python3
2#
3# SPDX-License-Identifier: Apache-2.0
4#
5# diffconfig - a tool to compare .config files.
6#
7# originally written in 2006 by Matt Mackall
8#  (at least, this was in his bloatwatch source code)
9# last worked on 2008 by Tim Bird
10#
11
12import sys, os
13
14def usage():
15    print("""Usage: diffconfig [-h] [-m] [<config1> <config2>]
16
17Diffconfig is a simple utility for comparing two .config files.
18Using standard diff to compare .config files often includes extraneous and
19distracting information.  This utility produces sorted output with only the
20changes in configuration values between the two files.
21
22Added and removed items are shown with a leading plus or minus, respectively.
23Changed items show the old and new values on a single line.
24
25If -m is specified, then output will be in "merge" style, which has the
26changed and new values in kernel config option format.
27
28If no config files are specified, .config and .config.old are used.
29
30Example usage:
31 $ diffconfig .config config-with-some-changes
32-EXT2_FS_XATTR  n
33-EXT2_FS_XIP  n
34 CRAMFS  n -> y
35 EXT2_FS  y -> n
36 LOG_BUF_SHIFT  14 -> 16
37 PRINTK_TIME  n -> y
38""")
39    sys.exit(0)
40
41# returns a dictionary of name/value pairs for config items in the file
42def readconfig(config_file):
43    d = {}
44    for line in config_file:
45        line = line[:-1]
46        if line[:7] == "CONFIG_":
47            name, val = line[7:].split("=", 1)
48            d[name] = val
49        if line[-11:] == " is not set":
50            d[line[9:-11]] = "n"
51    return d
52
53def print_config(op, config, value, new_value):
54    global merge_style
55
56    if merge_style:
57        if new_value:
58            if new_value=="n":
59                print("# CONFIG_%s is not set" % config)
60            else:
61                print("CONFIG_%s=%s" % (config, new_value))
62    else:
63        if op=="-":
64            print("-%s %s" % (config, value))
65        elif op=="+":
66            print("+%s %s" % (config, new_value))
67        else:
68            print(" %s %s -> %s" % (config, value, new_value))
69
70def main():
71    global merge_style
72
73    # parse command line args
74    if ("-h" in sys.argv or "--help" in sys.argv):
75        usage()
76
77    merge_style = 0
78    if "-m" in sys.argv:
79        merge_style = 1
80        sys.argv.remove("-m")
81
82    argc = len(sys.argv)
83    if not (argc==1 or argc == 3):
84        print("Error: incorrect number of arguments or unrecognized option")
85        usage()
86
87    if argc == 1:
88        # if no filenames given, assume .config and .config.old
89        build_dir=""
90        if "KBUILD_OUTPUT" in os.environ:
91            build_dir = os.environ["KBUILD_OUTPUT"]+"/"
92        configa_filename = build_dir + ".config.old"
93        configb_filename = build_dir + ".config"
94    else:
95        configa_filename = sys.argv[1]
96        configb_filename = sys.argv[2]
97
98    try:
99        a = readconfig(open(configa_filename))
100        b = readconfig(open(configb_filename))
101    except (IOError):
102        e = sys.exc_info()[1]
103        print("I/O error[%s]: %s\n" % (e.args[0],e.args[1]))
104        usage()
105
106    # print items in a but not b (accumulate, sort and print)
107    old = []
108    for config in a:
109        if config not in b:
110            old.append(config)
111    old.sort()
112    for config in old:
113        print_config("-", config, a[config], None)
114        del a[config]
115
116    # print items that changed (accumulate, sort, and print)
117    changed = []
118    for config in a:
119        if a[config] != b[config]:
120            changed.append(config)
121        else:
122            del b[config]
123    changed.sort()
124    for config in changed:
125        print_config("->", config, a[config], b[config])
126        del b[config]
127
128    # now print items in b but not in a
129    # (items from b that were in a were removed above)
130    new = sorted(b.keys())
131    for config in new:
132        print_config("+", config, None, b[config])
133
134main()
135