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