summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2008-11-06 13:35:04 +0000
committerEric S. Raymond <esr@thyrsus.com>2008-11-06 13:35:04 +0000
commit6da2ddf3cfb11e3336faecdd98a8424c9a13760e (patch)
tree4e5af0723b9952ad9ef5e409c33a3a49f06d50b5
downloaddeheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.gz
deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.bz2
deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.tar.xz
deheader-6da2ddf3cfb11e3336faecdd98a8424c9a13760e.zip
Initial revision
-rwxr-xr-xdeheader122
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()