#!/usr/bin/perl -w use strict; use warnings; use Getopt::Std; my $RCSID = '$Id: include.perl,v 358.3 2005-11-17 17:18:35-08 - - $'; $0 =~ s|^(.*/)?([^/]+)/*$|$2|; my $EXITCODE = 0; END { exit $EXITCODE; } sub note(@){ print STDERR "$0: @_"; }; $SIG{'__WARN__'} = sub { note @_; $EXITCODE = 1; }; $SIG{'__DIE__'} = sub { warn @_; exit; }; my $MAN_PAGE = <<__END_MAN_PAGE__; # # NAME # include.perl - generate make dependencies from C source files # # SYNOPSIS # include.perl [-hsu] [filename...] # # DESCRIPTION # The include.perl utility performs a recursive search through C # source code and header files for #include directives, and prints # out a list of dependencies in sorted order with duplicates # removed. # # OPTIONS # Options may appear in any order but must precede all operands in # the manner of Getopt::Std::getopts. # # -h Print out help information in the format of a man page then # exit. Other options and operands are ignored. # # -s Scan for include statements of the form `#include <...>' and # look for files relative to the system directory `/usr/ # include'. If `-u' is not explicitly specified, ignore user # files. # # -u Scan for include statements of the form `#include "..."' and # look for files relative to the current (user) directory. # This is the default if `-s' is not explicitly specified. If # `-s' is explicitly specified, user files are processed only # if `-u' is also specified. # # OPERANDS # All operands are names of files to be scanned. Each file is # scanned in turn for `#include' directives, this process # continuing for all indirectly included files to an arbitrary # depth. If no filenames are specified, STDIN is read. If # filenames are specified, then STDIN is read when a filename is # given as `-'. Output is similar to `cc -xM' or `cc -xM1'. # Target names are generated by substituting a `.c' suffix by a # `.o' suffix. # # EXIT STATUS # 0 No errors were detected. # 1 Invalid options were detected, files could not be read, or # the argument list had files not ending in `.c'. # # DIRECTORIES # The directory hierarchy /usr/include contains the system include # files. # $RCSID __END_MAN_PAGE__ $MAN_PAGE =~ s/^#[ ]?//mg; my %OPTIONS; my %dependencies; my %foundfiles; my @worklist; my $usr = 1; my $sys = 0; getopts "hsu", \%OPTIONS; if ($OPTIONS{'s'}) { $sys=1; $usr=0; } $usr = 1 if $OPTIONS{'u'}; print $MAN_PAGE and exit if $OPTIONS{'h'}; for my $source (@ARGV) { next if $foundfiles {$source}; if (!($source =~ m/.c$/)) { warn "$source: not a c source file\n"; next; } $foundfiles {$source} = 1; unless (open FILE, $source) { warn "$source: $!\n"; }else { my $target = $source; $target =~ s/.c$/.o/; $dependencies {"$target: $source"} = 1; while (defined (my $line = )) { next unless $line =~ m/^\s*#include\s+(<.*?>|".*?")\s*$/; my $include = $1; $include =~ s|"(.*)"|$1|; $include =~ s|<(.*)>|/usr/include/$1|; if ($sys && $include =~ m|^/usr/include/|) { $dependencies {"$target: $include"} = 1; push @worklist, [$target, $include]; } if ($usr && !($include =~ m|^/usr/include/|)) { $dependencies {"$target: $include"} = 1; push @worklist, [$target, $include]; } }; }; close FILE; } while (@worklist) { my $pair = shift @worklist; my $target = $pair->[0]; my $source = $pair->[1]; next if $foundfiles {$source}; $foundfiles {$source} = 1; unless (open FILE, $source) { warn "$source: $!\n"; }else { while (defined (my $line = )) { next unless $line =~ m/^\s*#include\s+(<.*?>|".*?")\s*$/; my $include = $1; $include =~ s|"(.*)"|$1|; $include =~ s|<(.*)>|/usr/include/$1|; if ($sys && $include =~ m|^/usr/include/|) { $dependencies {"$target: $include"} = 1; push @worklist, [$target, $include]; } if ($usr && !($include =~ m|^/usr/include/|)) { $dependencies {"$target: $include"} = 1; push @worklist, [$target, $include]; } }; }; close FILE; } for my $dep (sort keys %dependencies) { print $dep, "\n"; }