diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2008-11-06 13:35:04 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2008-11-06 13:35:04 +0000 |
commit | 6da2ddf3cfb11e3336faecdd98a8424c9a13760e (patch) | |
tree | 4e5af0723b9952ad9ef5e409c33a3a49f06d50b5 | |
download | deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.gz deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.bz2 deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.xz deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.zip |
Initial revision
-rwxr-xr-x | deheader | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/deheader b/deheader new file mode 100755 index 0000000..e9a5429 --- /dev/null +++ b/deheader @@ -0,0 +1,122 @@ +#!/usr/bin/env python +"""\ +deheader -- find (optionally remove) unneeded includes in C or C++ sourcefiles. + +Usage: deheader [-h] [-v] [-r] [-i str] sourcefiles + -h, --help Emit this help message and quit. + -i, --ignore Ignore (don't remove) headers containing the arg string + -r, --remove Remove the final set of unneeded headers + -v, --verbose Be chatty about what you're doing. + +This tool takes a list of C or C++ sourcefiles and generates a report +on which #includes can be omitted from them -- the test, for each foo.c +or foo.cc or foo.cpp, is simply whether 'rm foo.o; make foo.o' returns a +zero status. Optionally, with the -r option, the unneeded headers are removed. + +The original sourcefile is moved to a name with an .orig suffix and restored +on interrupt or after processing with its original timestamp, unless the +-r option was given and headers removed. +""" + +import sys, os, tempfile, getopt, time + +def trim(line): + "Get file reference from an #include, retaining <> if a system header." + trimmed = line[9:].strip() + if trimmed[0] in '"': + return '"' + trimmed.split('"')[1] + '"' + elif trimmed[0] == '<': + return trimmed.split('>')[0] + ">" + else: + return `line` + +def compile(source, msg=""): + (stem, suffix) = os.path.splitext(source) + derived = stem + ".o" + if os.path.exists(derived): + os.remove(derived) + command = "make " + derived + if verbose < 2: + command += " >/dev/null 2>&1" + start = time.time() + status = os.system(command) + end = time.time() + if status: + explain = "failed" + if verbose >= 2: + explain += " (%d)" % status + else: + explain = "succeeded" + if verbose: + print "deheader: %s%s %s in %2.2f sec." \ + % (sourcefile, msg, explain, end-start) + return (status, end - start) + +if __name__ == "__main__": + (options, arguments) = getopt.getopt(sys.argv[1:], "hrvi:", + ["help", "ignore", + "remove", "verbose",]) + verbose = 0 + remove = False + ignores = [] + for (switch, val) in options: + if switch in ('-h', '--help'): + sys.stderr.write(__doc__) + sys.exit(0) + elif switch in ('-i', '--ignore'): + ignores.append(val) + elif switch in ('-r', '--remove'): + remove = True + elif switch in ('-v', '--verbose'): + verbose += 1 + + for sourcefile in arguments: + (st, t) = compile(sourcefile) + if st != 0: + continue + includes = [] + for line in open(sourcefile): + if line.startswith("#include"): + if verbose: + print "deheader: %s requires %s" % (sourcefile, `line`) + for stopper in ignores: + if stopper in line: + break + else: + includes.append(line) + # We'll remove headers in reverse order, because later unnecessary + # headers might depend on earlier ones + includes.reverse() + unneeded = [] + original = sourcefile + ".orig" + try: + os.rename(sourcefile, original) + while True: + keepgoing = False + for header in includes: + ofp = open(sourcefile, "w") + for line in open(original): + if line != header: + ofp.write(line) + ofp.close() + (st, t) = compile(sourcefile, " without %s" % trim(header)) + if st == 0: + unneeded.append(header) + includes.remove(header) + keepgoing = True + if not keepgoing: + break + finally: + os.remove(sourcefile) + os.rename(original, sourcefile) + if unneeded: + print "deheader: the following headers may be omitted from %s:" % sourcefile + print "\n".join(map(trim, unneeded)) + if remove: + os.rename(sourcefile, original) + for header in includes: + ofp = open(sourcefile, "w") + for line in open(original): + if line not in unneeded: + ofp.write(line) + ofp.close() |