Remove unnecessary #include headers from C files
This Perl program finds C header files in a C program which are unnecessary. It works by editing the file to remove the header files one by one and then seeing if the file still compiles. It assumes that you have a "Makefile" and it tries to compile the C file using "make".
#!/home/ben/software/install/bin/perl use warnings; use strict; use Carp; use C::Tokenize '0.18', '$include'; use File::Temp 'tempfile'; use IPC::Run3; use Cwd; use File::Slurper qw!read_text write_text!; sub minimize_headers { my ($file, %options) = @_; my $verbose = $options{verbose}; if ($file !~ /\.c$/) { croak "'$file' doesn't look like a C file"; } my $ofile = $file; $ofile =~ s/\.c$/\.o/; my $dir = getcwd (); if ($ofile =~ m!^(.*)/([^/]+)$!) { $dir = $1; $ofile = $2; } my $make = $options{make}; if (! $make) { $make = 'make'; } if (-f $ofile) { unlink $ofile or die "unlink $ofile failed: $!"; } my $command = "$make -C $dir $ofile"; my $status = run3 ($command, undef, \my $output, \my $errors); if (-f $ofile) { unlink $ofile or die "unlink $ofile failed: $!"; } if ($errors) { warn "$command failed, fix that and run again"; return; } my (undef, $tempfile) = tempfile ("$file.XXXXX"); if (! rename $file, $tempfile) { croak "Could not rename $file to $tempfile: $!"; } my $ctext = read_text ($tempfile); my @includes; while ($ctext =~ /$include/g) { my $include = $1; chomp ($include); push @includes, $include; } if (-f $file) { unlink ($file) or die "Could not remove $file: $!"; } my %uniques; for my $include (@includes) { if ($include !~ /#include\s*[<"](.*)[>"]/) { warn "Strange include '$include'"; } else { my $ifile = $1; if ($uniques{$ifile}) { warn "File '$ifile' seems to be included more than once.\n"; } else { $uniques{$ifile} = 1; } } } for my $include (@includes) { my $newtext = $ctext; $newtext =~ s!\Q$include!// C:MinimizeHeaders removed this: $include!g; write_text ($file, $newtext); run3 ("$make -C $dir $ofile", undef, \my $output, \my $errors); if (! $errors) { print "$file: '$include' may be unnecessary since it compiles without this.\n"; } else { if ($verbose) { print "$file: '$include' seems to be necessary.\n"; print "The make errors were as follows:\n$errors"; } } if (! unlink ($file)) { croak "Could not remove $file: $!"; } if (-f $ofile) { unlink $ofile or die "unlink $ofile failed: $!"; } } if (! rename $tempfile, $file) { croak "Could not rename $tempfile to $file: $!"; } return; } for my $file (@ARGV) { eval { minimize_headers ($file); }; if ($@) { print "Minimize failed for '$file': $@"; } } exit; # Local Variables: # mode: perl # End:
The program requires some Perl modules to be installed. File::Temp, Cwd, and Carp come with Perl, but you need to install C::Tokenize, File::Slurper, and IPC::Run3 using a command such as
cpan install C::Tokenize IPC::Run3 File::Slurper
or
cpanm C::Tokenize IPC::Run3 File::Slurper
if you have App::cpanminus installed already.
Web links
-
deheader
deheader — report which includes in C or C++ compiles can be removed
-
include-what-you-use
A tool for use with clang to analyze #includes in C and C++ source files
Copyright © Ben Bullock 2009-2024. All
rights reserved.
For comments, questions, and corrections, please email
Ben Bullock
(benkasminbullock@gmail.com).
/
Privacy /
Disclaimer