Imported Upstream version 1.8.3
[debian/sudo] / mkdep.pl
1 #!/usr/bin/env perl
2
3 use File::Temp qw/ :mktemp  /;
4 use Fcntl;
5 use warnings;
6
7 die "usage: $0 Makefile ...\n" unless $#ARGV >= 0;
8
9 my @incpaths;
10 my %dir_vars;
11 my %implicit;
12
13 # Read in MANIFEST fail if present
14 my %manifest;
15 if (open(MANIFEST, "<MANIFEST")) {
16     while (<MANIFEST>) {
17         chomp;
18         next unless /([^\/]+\.[cly])$/;
19         $manifest{$1} = $_;
20     }
21 }
22
23 foreach (@ARGV) {
24     mkdep($_);
25 }
26
27 sub mkdep {
28     my $file = $_[0];
29     $file =~ s:^\./+::;         # strip off leading ./
30
31     my $makefile;
32     if (open(MF, "<$file")) {
33         local $/;               # enable "slurp" mode
34         $makefile = <MF>;
35     } else {
36         warn "$0: $file: $!\n";
37         return undef;
38     }
39     close(MF);
40
41     # New makefile, minus the autogenerated dependencies
42     my $separator = "# Autogenerated dependencies, do not modify";
43     my $new_makefile = $makefile;
44     $new_makefile =~ s/${separator}.*$//s;
45     $new_makefile .= "$separator\n";
46
47     # Old makefile, join lines with continuation characters
48     $makefile =~ s/\\\n//mg;
49
50     # Expand some configure bits
51     $makefile =~ s:\@COMMON_OBJS\@:aix.lo:;
52     $makefile =~ s:\@SUDO_OBJS\@:preload.o selinux.o sesh.o sudo_noexec.lo:;
53     $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo plugin_error.lo:;
54     # XXX - fill in AUTH_OBJS from contents of the auth dir instead
55     $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb4.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid.lo securid5.lo sia.lo:;
56     $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo dlopen.lo fnmatch.lo getcwd.lo getgrouplist.lo getline.lo getprogname.lo glob.lo isblank.lo memrchr.lo mksiglist.lo mktemp.lo nanosleep.lo setenv.lo siglist.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo unsetenv.lo utimes.lo globtest.o fnm_test.o:;
57
58     # Parse OBJS lines
59     my %objs;
60     while ($makefile =~ /^[A-Z0-9_]*OBJS\s*=\s*(.*)/mg) {
61         foreach (split/\s+/, $1) {
62             next if /^\$[\(\{].*[\)\}]$/; # skip included vars for now
63             $objs{$_} = 1;
64         }
65     }
66
67     # Find include paths
68     @incpaths = ();
69     while ($makefile =~ /-I(\S+)/mg) {
70         push(@incpaths, $1) unless $1 eq ".";
71     }
72
73     # Values of srcdir, top_srcdir, top_builddir, incdir
74     %dir_vars = ();
75     $file =~ m:^(.*)/+[^/]+:;
76     $dir_vars{'srcdir'} = $1 || '.';
77     $dir_vars{'devdir'} = $dir_vars{'srcdir'};
78     $dir_vars{'authdir'} = $dir_vars{'srcdir'} . "/auth";
79     $dir_vars{'top_srcdir'} = '.';
80     #$dir_vars{'top_builddir'} = '.';
81     $dir_vars{'incdir'} = 'include';
82
83     # Find implicit rules for generate .o and .lo files
84     %implicit = ();
85     while ($makefile =~ /^\.c\.(l?o):\s*\n\t+(.*)$/mg) {
86         $implicit{$1} = $2;
87     }
88
89     # Find existing .o and .lo dependencies
90     my %old_deps;
91     while ($makefile =~ /^(\w+\.l?o):\s*(\S+\.c)/mg) {
92         $old_deps{$1} = $2;
93     }
94
95     # Sort files so we do .lo files first
96     foreach my $obj (sort keys %objs) {
97         next unless $obj =~ /(\S+)\.(l?o)$/;
98         if ($2 eq "o" && exists($objs{"$1.lo"})) {
99             # If we have both .lo and .o files, make the .o depend on the .lo
100             $new_makefile .= sprintf("%s: %s.lo\n", $obj, $1);
101         } else {
102             # Use old depenencies when mapping objects to their source.
103             # If no old depenency, use the MANIFEST file to find the source.
104             my $src = $1 . '.c';
105             my $ext = $2;
106             if (exists $old_deps{$obj}) {
107                 $src = $old_deps{$obj};
108             } elsif (exists $manifest{$src}) {
109                 $src = $manifest{$src};
110                 foreach (sort { length($b) <=> length($a) } keys %dir_vars) {
111                     last if $src =~ s:^\Q$dir_vars{$_}/\E:\$\($_\)/:;
112                 }
113             } else {
114                 warn "$file: unable to find source for $obj\n";
115             }
116             my $imp = $implicit{$ext};
117             $imp =~ s/\$</$src/g;
118
119             my $deps = sprintf("%s: %s %s", $obj, $src,
120                 join(' ', find_depends($src)));
121             if (length($deps) > 80) {
122                 my $off = 0;
123                 my $indent = length($obj) + 2;
124                 while (length($deps) - $off > 80 - $indent) {
125                     my $pos;
126                     if ($off != 0) {
127                         $new_makefile .= ' ' x $indent;
128                         $pos = rindex($deps, ' ', $off + 80 - $indent - 2);
129                     } else {
130                         $pos = rindex($deps, ' ', $off + 78);
131                     }
132                     $new_makefile .= substr($deps, $off, $pos - $off) . " \\\n";
133                     $off = $pos + 1;
134                 }
135                 $new_makefile .= ' ' x $indent;
136                 $new_makefile .= substr($deps, $off) . "\n";
137             } else {
138                 $new_makefile .= "$deps\n";
139             }
140             $new_makefile .= "\t$imp\n";
141         }
142     }
143
144     rename($file, $file . ".old");
145     if (!open(MF, ">$file")) {
146         warn("cannot open $file: $!\n");
147         rename($file . ".old", $file);
148     } else {
149         print MF $new_makefile || warn("cannot write $file: $!\n");
150         close(MF);
151     }
152 }
153
154 exit(0);
155
156 sub find_depends {
157     my $src = $_[0];
158     my ($deps, $code, @headers);
159
160     if ($src !~ /\//) {
161         # XXX - want build dir not src dir
162         $src = "$dir_vars{'srcdir'}/$src";
163     }
164
165     # resolve $(srcdir) etc.
166     foreach (keys %dir_vars) {
167         $src =~ s/\$[\(\{]$_[\)\}]/$dir_vars{$_}/g;
168     }
169
170     # find open source file and find headers used by it
171     if (!open(FILE, "<$src")) {
172         warn "unable to open $src\n";
173         return "";
174     }
175     local $/;           # enable "slurp" mode
176     $code = <FILE>;
177     close(FILE);
178
179     # find all headers
180     while ($code =~ /^#\s*include\s+["<](\S+)[">]/mg) {
181         my ($hdr, $hdr_path) = find_header($1);
182         if (defined($hdr)) {
183             push(@headers, $hdr);
184             # Look for other includes in the .h file
185             push(@headers, find_depends($hdr_path));
186         }
187     }
188
189     @headers;
190 }
191
192 # find the path to a header file
193 # returns path or undef if not found
194 sub find_header {
195     my $hdr = $_[0];
196
197     # Look for .h.in files in top_builddir and build dir
198     return ("\$(top_builddir\)/$hdr", "./${hdr}.in") if -r "./${hdr}.in";
199     return ("./$hdr", "$dir_vars{'srcdir'}/${hdr}.in") if -r "$dir_vars{'srcdir'}/${hdr}.in";
200
201     foreach my $inc (@incpaths) {
202         my $hdr_path = "$inc/$hdr";
203         # resolve variables in include path
204         foreach (keys %dir_vars) {
205             $hdr_path =~ s/\$[\(\{]$_[\)\}]/$dir_vars{$_}/g;
206         }
207         return ("$inc/$hdr", $hdr_path) if -r $hdr_path;
208     }
209
210     undef;
211 }