#!/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 = <FILE>)) {
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 = <FILE>)) {
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"; }