Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/scripts/mkdep.pl
1532 views
1
#!/usr/bin/env perl
2
#
3
# SPDX-License-Identifier: ISC
4
#
5
# Copyright (c) 2011-2022 Todd C. Miller <[email protected]>
6
#
7
# Permission to use, copy, modify, and distribute this software for any
8
# purpose with or without fee is hereby granted, provided that the above
9
# copyright notice and this permission notice appear in all copies.
10
#
11
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
#
19
20
use File::Temp qw/ :mktemp /;
21
use Fcntl;
22
use warnings;
23
24
die "usage: $0 [--builddir=dir] [--srcdir=dir] Makefile.in ...\n" unless $#ARGV >= 0;
25
26
my @incpaths;
27
my %dir_vars;
28
my %implicit;
29
my %generated;
30
my $top_builddir = ".";
31
my $top_srcdir;
32
33
# Check for srcdir and/or builddir, if present
34
while ($ARGV[0] =~ /^--(src|build)dir=(.*)/) {
35
if ($1 eq 'src') {
36
$top_srcdir = $2;
37
} else {
38
$top_builddir = $2;
39
}
40
shift @ARGV;
41
}
42
chdir($top_srcdir) if defined($top_srcdir);
43
44
# Read in MANIFEST or fail if not present
45
my %manifest;
46
die "unable to open MANIFEST: $!\n" unless open(MANIFEST, "<MANIFEST");
47
while (<MANIFEST>) {
48
chomp;
49
next unless /([^\/]+\.[cly])$/;
50
$manifest{$1} = $_;
51
}
52
53
foreach (@ARGV) {
54
mkdep($_);
55
}
56
57
sub fmt_depend {
58
my ($obj, $src) = @_;
59
my $ret;
60
61
my $deps = sprintf("%s: %s %s", $obj, $src,
62
join(' ', find_depends($src)));
63
if (length($deps) > 80) {
64
my $off = 0;
65
my $indent = length($obj) + 2;
66
while (length($deps) - $off > 80 - $indent) {
67
my $pos;
68
if ($off != 0) {
69
$ret .= ' ' x $indent;
70
$pos = rindex($deps, ' ', $off + 80 - $indent - 2);
71
if ($pos <= $off) {
72
# No space found within 78 columns, check beyond
73
$pos = index($deps, ' ', $off + 80 - $indent - 2);
74
}
75
} else {
76
$pos = rindex($deps, ' ', 78);
77
}
78
$ret .= substr($deps, $off, $pos - $off) . " \\\n";
79
$off = $pos + 1;
80
}
81
$ret .= ' ' x $indent;
82
$ret .= substr($deps, $off) . "\n";
83
} else {
84
$ret = "$deps\n";
85
}
86
87
$ret;
88
}
89
90
sub mkdep {
91
my $file = $_[0];
92
$file =~ s:^\./+::; # strip off leading ./
93
$file =~ m:^(.*)/[^/]+$:;
94
my $srcdir = $1; # parent dir of Makefile
95
96
my $makefile;
97
if (open(MF, "<$file")) {
98
local $/; # enable "slurp" mode
99
$makefile = <MF>;
100
} else {
101
warn "$0: $file: $!\n";
102
return undef;
103
}
104
close(MF);
105
106
# New makefile, minus the autogenerated dependencies
107
my $separator = "# Autogenerated dependencies, do not modify";
108
my $new_makefile = $makefile;
109
$new_makefile =~ s/${separator}.*$//s;
110
$new_makefile .= "$separator\n";
111
112
# Old makefile, join lines with continuation characters
113
$makefile =~ s/\\\n//mg;
114
115
# Expand some configure bits
116
$makefile =~ s:\@DEV\@::g;
117
$makefile =~ s:\@COMMON_OBJS\@:aix.lo event_poll.lo event_select.lo:;
118
$makefile =~ s:\@SUDO_OBJS\@:intercept.pb-c.o openbsd.o preload.o apparmor.o selinux.o sesh.o solaris.o:;
119
$makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo ldap_util.lo ldap_conf.lo ldap_innetgr.lo solaris_audit.lo sssd.lo:;
120
# XXX - fill in AUTH_OBJS from contents of the auth dir instead
121
$makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
122
$makefile =~ s:\@DIGEST\@:digest.lo digest_openssl.lo digest_gcrypt.lo:;
123
$makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_buf.lo arc4random_uniform.lo cfmakeraw.lo closefrom.lo dup3.lo explicit_bzero.lo fchmodat.lo fchownat.lo freezero.lo fstatat.lo fnmatch.lo getaddrinfo.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo getusershell.lo glob.lo gmtime_r.lo inet_ntop_lo inet_pton.lo isblank.lo localtime_r.lo memrchr.lo mkdirat.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo openat.lo pipe2.lo pread.lo pwrite.lo pw_dup.lo reallocarray.lo realpath.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo timegm.lo unlinkat.lo utimens.lo:;
124
125
# Parse OBJS lines
126
my %objs;
127
while ($makefile =~ /^[A-Z0-9_]*OBJS\s*=\s*(.*)/mg) {
128
foreach (split/\s+/, $1) {
129
next if /^\$[\(\{].*[\)\}]$/; # skip included vars for now
130
$objs{$_} = 1;
131
}
132
}
133
134
# Find include paths
135
@incpaths = ();
136
while ($makefile =~ /-I(\S+)/mg) {
137
push(@incpaths, $1) unless $1 eq ".";
138
}
139
140
# Check for generated files
141
if ($makefile =~ /GENERATED\s*=\s*(.+)$/m) {
142
foreach (split(/\s+/, $1)) {
143
$generated{$_} = 1;
144
}
145
}
146
147
# Values of srcdir, top_srcdir, top_builddir, incdir
148
%dir_vars = ();
149
$file =~ m:^(.*)/+[^/]+:;
150
$dir_vars{'srcdir'} = $1 || '.';
151
$dir_vars{'devdir'} = $dir_vars{'srcdir'};
152
$dir_vars{'authdir'} = $dir_vars{'srcdir'} . "/auth";
153
$dir_vars{'builddir'} = $top_builddir . "/" . $dir_vars{'srcdir'};
154
$dir_vars{'top_srcdir'} = $top_srcdir;
155
$dir_vars{'sudoers_srcdir'} = $top_srcdir . "/plugins/sudoers";
156
#$dir_vars{'top_builddir'} = '.';
157
$dir_vars{'incdir'} = 'include';
158
159
# Find implicit rules for generated .i, .lo, .o and .plog files
160
%implicit = ();
161
while ($makefile =~ /^\.[ci]\.(l?o|i|plog):\s*\n\t+(.*)$/mg) {
162
$implicit{$1} = $2;
163
}
164
165
# Find existing .o and .lo dependencies
166
my %old_deps;
167
while ($makefile =~ /^(\w+\.l?o):\s*(\S+\.c)/mg) {
168
$old_deps{$1} = $2;
169
}
170
171
# Check whether static objs are disabled for .lo files
172
my $disable_static;
173
if ($makefile =~ /LTFLAGS\s*=\s*(.+)$/m) {
174
my $ltflags = $1;
175
$_ = $implicit{"lo"};
176
if (defined($_)) {
177
s/\$[\(\{]LTFLAGS[\)\}]/$ltflags/;
178
$disable_static = /--tag=disable-static/;
179
}
180
}
181
182
# Sort files so we do .lo files first
183
foreach my $obj (sort keys %objs) {
184
next unless $obj =~ /(\S+)\.(l?o)$/;
185
if (!$disable_static && $2 eq "o" && exists($objs{"$1.lo"})) {
186
# We have both .lo and .o files, only the .lo should be used
187
warn "$file: $obj should be $1.lo\n";
188
} else {
189
# Use old dependencies when mapping objects to their source.
190
# If no old dependency, use the MANIFEST file to find the source.
191
my $base = $1;
192
my $ext = $2;
193
my $src = $base . '.c';
194
if (exists $old_deps{$obj}) {
195
$src = $old_deps{$obj};
196
} elsif (exists $manifest{$src}) {
197
$src = $manifest{$src};
198
foreach (sort { length($b) <=> length($a) } keys %dir_vars) {
199
next if $_ eq "devdir";
200
last if $src =~ s:^\Q$dir_vars{$_}/\E:\$\($_\)/:;
201
}
202
} else {
203
warn "$file: unable to find source for $obj ($src) in MANIFEST\n";
204
if (-f "$srcdir/$src") {
205
$src = '$(srcdir)/' . $src;
206
}
207
}
208
my $imp = $implicit{$ext};
209
$imp =~ s/\$</$src/g;
210
211
my $deps = fmt_depend($obj, $src);
212
$new_makefile .= $deps;
213
$new_makefile .= "\t$imp\n";
214
215
# PVS Studio files (.i and .plog) but only do them once.
216
if ($ext ne "o" || !exists($objs{"$base.lo"})) {
217
if (exists $implicit{"i"} && exists $implicit{"plog"}) {
218
if ($src =~ /\.pb-c.c$/) {
219
# Do not check protobuf-c generated files
220
$obj =~ /(.*)\.[a-z]+$/;
221
$new_makefile .= "${1}.plog: ${src}\n";
222
$new_makefile .= "\ttouch \$@\n";
223
} else {
224
$imp = $implicit{"i"};
225
$imp =~ s/\$</$src/g;
226
$deps =~ s/\.l?o/.i/;
227
$new_makefile .= $deps;
228
$new_makefile .= "\t$imp\n";
229
230
$imp = $implicit{"plog"};
231
$imp =~ s/ifile=\$<; *//;
232
$imp =~ s/\$\$\{ifile\%i\}c/$src/;
233
$obj =~ /(.*)\.[a-z]+$/;
234
my $base = $1;
235
$imp =~ s/\$</${base}.i/g;
236
$new_makefile .= "${base}.plog: ${base}.i\n";
237
$new_makefile .= "\t$imp\n";
238
}
239
}
240
}
241
}
242
}
243
244
my $newfile = $file . ".new";
245
if (!open(MF, ">$newfile")) {
246
warn("cannot open $newfile: $!\n");
247
} else {
248
print MF $new_makefile || warn("cannot write $newfile: $!\n");
249
close(MF) || warn("cannot close $newfile: $!\n");;
250
rename($newfile, $file);
251
}
252
}
253
254
exit(0);
255
256
sub find_depends {
257
my $src = $_[0];
258
my ($deps, $code, %headers);
259
260
if ($src !~ /\//) {
261
# generated file, local to build dir
262
$src = "$dir_vars{'builddir'}/$src";
263
}
264
265
# resolve $(srcdir) etc.
266
foreach (keys %dir_vars) {
267
$src =~ s/\$[\(\{]$_[\)\}]/$dir_vars{$_}/g;
268
}
269
270
# find open source file and find headers used by it
271
if (!open(FILE, "<$src")) {
272
warn "unable to open $src\n";
273
return "";
274
}
275
local $/; # enable "slurp" mode
276
$code = <FILE>;
277
close(FILE);
278
279
# find all headers
280
while ($code =~ /^\s*#\s*include\s+["<](\S+)[">]/mg) {
281
my ($hdr, $hdr_path) = find_header($src, $1);
282
if (defined($hdr)) {
283
$headers{$hdr} = 1;
284
# Look for other includes in the .h file
285
foreach (find_depends($hdr_path)) {
286
$headers{$_} = 1;
287
}
288
}
289
}
290
291
sort keys %headers;
292
}
293
294
# find the path to a header file
295
# returns path or undef if not found
296
sub find_header {
297
my $src = $_[0];
298
my $hdr = $_[1];
299
300
# Look for .h.in files in top_builddir and build dir
301
return ("\$(top_builddir\)/$hdr", "./${hdr}.in") if -r "./${hdr}.in";
302
return ("./$hdr", "$dir_vars{'srcdir'}/${hdr}.in") if -r "$dir_vars{'srcdir'}/${hdr}.in";
303
304
if (exists $generated{$hdr}) {
305
my $hdr_path = $dir_vars{'devdir'} . '/' . $hdr;
306
return ('$(devdir)/' . $hdr, $hdr_path) if -r $hdr_path;
307
}
308
foreach my $inc (@incpaths) {
309
my $hdr_path = "$inc/$hdr";
310
# resolve variables in include path
311
foreach (keys %dir_vars) {
312
next if $_ eq "devdir";
313
$hdr_path =~ s/\$[\(\{]$_[\)\}]/$dir_vars{$_}/g;
314
}
315
return ("$inc/$hdr", $hdr_path) if -r $hdr_path;
316
}
317
# Check path relative to src dir (XXX - should be for "include" only)
318
if ($src =~ m#^(.*)/[^/]+$# && -r "$1/$hdr") {
319
my $hdr_path = "$1/$hdr";
320
$hdr_path =~ s#/[^/]+/\.\.##g; # resolve ..
321
my $hdr_pretty = $hdr_path;
322
foreach (sort { length($dir_vars{$b}) <=> length($dir_vars{$a}) } keys %dir_vars) {
323
next if $_ eq "devdir";
324
$hdr_pretty =~ s/$dir_vars{$_}/\$($_)/;
325
}
326
return ($hdr_pretty, $hdr_path);
327
}
328
329
undef;
330
}
331
332