DEHEADER(1)

πŸ’– DEHEADER(1) πŸ’–

okay bestie, gather round, because this one is actually so useful and nobody talks about it. this is deheader, a development tool, and it's about to tell you which of your #includes are just... there. dead weight. not pulling their weight. we're cleaning house. πŸ’…


✨ NAME ✨

deheader β€” report which includes in C or C++ compiles can be removed πŸ¦‹


🌸 SYNOPSIS 🌸

deheader [-h] [-m command] [-b builddir] [-i pattern] [-q] [-r] [-v] [-x
pattern] [-V] [file-or-dir]

πŸ‘‘ DESCRIPTION πŸ‘‘

so basically? you hand this tool a list of C or C++ sourcefiles, and it hands you back a report on which #includes can be straight up omitted from them. and not just that β€” it also tells you what standard inclusions might be required for portability. two-for-one, slay. πŸ’–

the test it runs, for each foo.c or foo.cc or foo.cpp, is simply whether rm foo.o; make foo.o returns a zero status. that's it. that's the vibe check. (but the build command can be overridden, we'll get to that β€” -m has entered the chat.)

exception though: under cmake, foo.o is a phony target. so when a CMakeList.txt is detected, it does make clean instead of rm foo.o. noted, no cap.

optionally, with the -r switch, the unneeded headers get removed from the sourcefiles. and listen β€”

🚨 bestie DO NOT use -r unless your sourcefiles are safely under version control and you can revert. i'm so serious. this is the "i can fix him" of command-line flags. do not.

if a sourcefile argument is a directory, the report is generated on all source files beneath it. subdirectories beginning with a dot are assumed to be repository directories for version-control systems and ignored (yeah, .git, we see you πŸ‘€). and if no arguments are given at all, the program runs as if the name of the current directory had been passed to it. so deheader alone = deheader . basically. cute.

inclusions within the scope of #if/#ifdef/#else/#endif directives are left alone, because trying to reason about potential combinations of -D and U options would be too complicated and prone to weird errors. one exception though, icon behavior: headers protected only by S_SPLINT_S β€” that's the conditional for blocking scanning by the static analysis tool splint(1) β€” are scanned normally. ✨

the tool will also emit warnings about duplicate inclusions, and about inclusions required for portability but not present. helpful queen.

it's recommended that you arrange to compile with options that will stop the compiler on warnings when using this tool. otherwise it'll report headers that only declare prototypes and return types (and thus throw only warnings) as being not required. rude of it, honestly. under gcc the compiler options to accomplish this are -Werror -Wfatal-errors. and if your makefile follows normal conventions, running with -m "make CFLAGS='-Werror -Wfatal-errors' %s" may do the right thing. you can check this by running with -v -v -v to see what compilation commands are actually emitted. (yes, three -vs. stack them. it's giving ✨verbose✨.)

on each test compile, 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 were removed. so like, it does try to be polite about it. πŸ₯Ί

if the -b option is given, it tells the program that generated .o files live in a file tree parallel to the source tree but rooted at the specified argument. if that argument is a relative path, it's interpreted relative to the directory in which deheader is run.

if the first test compilation from the top-level directory fails, deheader descends into the subdirectory of the source file and retries compiling inside there. main character problem-solving energy fr fr.

okay verbosity levels, this is the part everyone asks about:

  • verbosity level 0 β€” only messages indicating removable headers are issued. minimal. clean. aesthetic.
  • verbosity level 1 β€” test compilations are timed and progress is indicated with a twirling-baton prompt. πŸŽ€ (yes a twirling baton. iconic.)
  • verbosity level 2 β€” you get verbose progress messages on the analysis.
  • verbosity level 3 β€” you see the output from the make and compilation commands. the full tea.

if the -q (--quiet) option flag was not set, the last line of the output will be a statistical summary. numbers. stats. love that for us.

and final PSA, because this one matters:

running deheader will leave a lot of binaries in your directory that were compiled in ways possibly not invoked by your normal build process. running make clean afterwards (or the equivalent under whatever build system you are using) is strongly recommended. clean up after yourself, bestie. πŸ§ΉπŸ’œ

πŸ’… OPTIONS πŸ’…

  • -h β€” display some help and exit.
  • -m command β€” set the build command used for test compiles. defaults to make %s. a %s in the build command is replaced with the make target. targetless builders such as meson can be supported with, e.g. -m 'ninja -C build'.
  • -b builddir β€” set the build directory for object files.
  • -i pattern β€” set a pattern for includes to be ignored. takes a Python regular expression.
  • -q β€” suppress statistical summary.
  • -r β€” remove header inclusions from sourcefiles where they are not required. (⚠️ version control first, i am begging)
  • -v β€” set verbosity.
  • -x pattern β€” exclude files with names matching the specified Python regexp.
  • -V β€” show version of program and exit.

🌈 RETURN VALUES 🌈

returns 1 if unneeded includes were found, 0 otherwise. which means you can use it for pre-release sanity checking in a Makefile. automation girlies rise up πŸ‘‘


πŸ’€ BUGS πŸ’€

ugh okay, the fine print. read it, it's important.

very rarely, test-compiling after running with -r may show that this tool removed some headers that are actually required for your build. 😭 this can happen because deheader doesn't know about all the strange things your build system gets up to, and the problem of analyzing your build to understand them would be Turing-complete. yeah. unhinged sentence but it's true. simply revert the altered files and continue. no big deal, just... revert.

due to minor variations in system headers, it is possible your program may not port correctly in older Unix variants after being deheadered. this is normally not a problem with the portion of the API specified by POSIX and ANSI C, but may be for headers that are not standardized or only weakly standardized. the sockets API (<sys/select.h+, sys/sockets.h, and friends such as sys/types.h and sys.stat.h) is perhaps the most serious trouble spot. 🫠 deheader has an internal table of rules that heads off the most common problems by suppressing deletion of headers that are required for portability, but your mileage may vary. (ymmv is the most ominous acronym in computing ngl.)

the dependency scanner does not ignore the text of comments. so, e.g., a reference to "log10" in a comment will produce a spurious warning that <math.h> is required for portability. ma'am that's a comment. πŸ™ˆ

sufficiently perverse C++ can silently invalidate the brute-force algorithm this tool uses. example: if an overloaded function has different overloads from two different files, removing one may expose the other, changing runtime semantics without a compile-time warning. similarly, removing a later file containing a template specialization may lead to undefined behavior from a template defined in an earlier file. use this with caution near such features, and test carefully. πŸ”₯ seriously. test carefully.


🫢 AUTHOR 🫢

Eric S. Raymond esr@snark.thyrsus.com; home page at http://www.catb.org/~esr/.


okay that's the whole page!! this was deheader, from 2026-02-20, and you're all caught up πŸ’– go forth, delete those useless includes, and for the love of everything run make clean afterwards. mwah. πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from