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