1#!/usr/bin/perl -w
2# find a list of #include lines in C code that might not be needed
3# usually called with something like this:
4#    minimal_includes.pl `find . -name "*.c"`
5# Andrew Tridgell <tridge@samba.org>
6
7use strict;
8use Data::Dumper;
9use Getopt::Long;
10
11my $opt_help = 0;
12my $opt_remove = 0;
13my $opt_skip_system = 0;
14my $opt_waf = 0;
15
16#####################################################################
17# write a string into a file
18sub FileSave($$)
19{
20    my($filename) = shift;
21    my($v) = shift;
22    local(*FILE);
23    open(FILE, ">$filename") || die "can't open $filename";
24    print FILE $v;
25    close(FILE);
26}
27
28sub load_lines($)
29{
30	my $fname = shift;
31	my @lines = split(/^/m, `cat $fname`);
32	return @lines;
33}
34
35sub save_lines($$)
36{
37	my $fname = shift;
38	my $lines = shift;
39	my $data = join('', @{$lines});
40	FileSave($fname, $data);
41}
42
43sub test_compile($)
44{
45	my $fname = shift;
46	my $obj;
47	if ($opt_waf) {
48		my $ret = `../buildtools/bin/waf $fname 2>&1`;
49		return $ret
50	}
51	if ($fname =~ s/(.*)\..*$/$1.o/) {
52		$obj = "$1.o";
53	} else {
54		return "NOT A C FILE";
55	}
56	unlink($obj);
57	my $ret = `make $obj 2>&1`;
58	if (!unlink("$obj")) {
59		return "COMPILE FAILED";
60	}
61	return $ret;
62}
63
64sub test_include($$$$)
65{
66	my $fname = shift;
67	my $lines = shift;
68	my $i = shift;
69	my $original = shift;
70	my $line = $lines->[$i];
71	my $testfname;
72
73	$lines->[$i] = "";
74
75	my $mname = $fname . ".misaved";
76
77	unlink($mname);
78	rename($fname, $mname) || die "failed to rename $fname";
79	save_lines($fname, $lines);
80
81	my $out = test_compile($fname);
82
83	if ($out eq $original) {
84		if ($opt_remove) {
85			if ($opt_skip_system &&
86			    $line =~ /system\//) {
87				print "$fname: not removing system include $line\n";
88			} else {
89				print "$fname: removing $line\n";
90				unlink($mname);
91				return;
92			}
93		} else {
94			print "$fname: might be able to remove $line\n";
95		}
96	}
97
98	$lines->[$i] = $line;
99	rename($mname, $fname) || die "failed to restore $fname";
100}
101
102sub process_file($)
103{
104	my $fname = shift;
105	my @lines = load_lines($fname);
106	my $num_lines = $#lines;
107
108	my $original = test_compile($fname);
109
110	if ($original eq "COMPILE FAILED") {
111		print "Failed to compile $fname\n";
112		return;
113	}
114
115	print "Processing $fname (with $num_lines lines)\n";
116
117	my $if_level = 0;
118
119	for (my $i=0;$i<=$num_lines;$i++) {
120		my $line = $lines[$i];
121		if ($line =~ /^\#\s*if/) {
122			$if_level++;
123		}
124		if ($line =~ /^\#\s*endif/) {
125			$if_level--;
126		}
127		if ($if_level == 0 &&
128		    $line =~ /^\#\s*include/ &&
129		    !($line =~ /needed/)) {
130			test_include($fname, \@lines, $i, $original);
131		}
132	}
133}
134
135
136#########################################
137# display help text
138sub ShowHelp()
139{
140    print "
141           minimise includes
142           Copyright (C) tridge\@samba.org
143
144	   Usage: minimal_includes.pl [options] <C files....>
145
146	   Options:
147                 --help         show help
148                 --remove       remove includes, don't just list them
149                 --skip-system  don't remove system/ includes
150                 --waf          use waf target conventions
151";
152}
153
154
155# main program
156GetOptions (
157	    'h|help|?' => \$opt_help,
158	    'remove' => \$opt_remove,
159	    'skip-system' => \$opt_skip_system,
160	    'waf' => \$opt_waf,
161	    );
162
163if ($opt_help) {
164	ShowHelp();
165	exit(0);
166}
167
168for (my $i=0;$i<=$#ARGV;$i++) {
169	my $fname = $ARGV[$i];
170	process_file($fname);
171}
172