1#! /usr/bin/env perl 2# Copyright 2018-2022 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the Apache License 2.0 (the "License"). You may not use 5# this file except in compliance with the License. You can obtain a copy 6# in the file LICENSE in the source distribution or at 7# https://www.openssl.org/source/license.html 8 9use strict; 10use warnings; 11 12use lib '.'; 13use configdata; 14 15use File::Spec::Functions qw(:DEFAULT rel2abs); 16use File::Compare qw(compare_text); 17use feature 'state'; 18 19# When using stat() on Windows, we can get it to perform better by avoid some 20# data. This doesn't affect the mtime field, so we're not losing anything... 21${^WIN32_SLOPPY_STAT} = 1; 22 23my $debug = $ENV{ADD_DEPENDS_DEBUG}; 24my $buildfile = $config{build_file}; 25my $build_mtime = (stat($buildfile))[9]; 26my $configdata_mtime = (stat('configdata.pm'))[9]; 27my $rebuild = 0; 28my $depext = $target{dep_extension} || ".d"; 29my @depfiles = 30 sort 31 grep { 32 # This grep has side effects. Not only does if check the existence 33 # of the dependency file given in $_, but it also checks if it's 34 # newer than the build file or older than configdata.pm, and if it 35 # is, sets $rebuild. 36 my @st = stat($_); 37 $rebuild = 1 38 if @st && ($st[9] > $build_mtime || $st[9] < $configdata_mtime); 39 scalar @st > 0; # Determines the grep result 40 } 41 map { (my $x = $_) =~ s|\.o$|$depext|; $x; } 42 ( ( grep { $unified_info{sources}->{$_}->[0] =~ /\.cc?$/ } 43 keys %{$unified_info{sources}} ), 44 ( grep { $unified_info{shared_sources}->{$_}->[0] =~ /\.cc?$/ } 45 keys %{$unified_info{shared_sources}} ) ); 46 47exit 0 unless $rebuild; 48 49# Ok, primary checks are done, time to do some real work 50 51my $producer = shift @ARGV; 52die "Producer not given\n" unless $producer; 53 54my $srcdir = $config{sourcedir}; 55my $blddir = $config{builddir}; 56my $abs_srcdir = rel2abs($srcdir); 57my $abs_blddir = rel2abs($blddir); 58 59# Convenient cache of absolute to relative map. We start with filling it 60# with mappings for the known generated header files. They are relative to 61# the current working directory, so that's an easy task. 62# NOTE: there's more than C header files that are generated. They will also 63# generate entries in this map. We could of course deal with C header files 64# only, but in case we decide to handle more than just C files in the future, 65# we already have the mechanism in place here. 66# NOTE2: we lower case the index to make it searchable without regard for 67# character case. That could seem dangerous, but as long as we don't have 68# files we depend on in the same directory that only differ by character case, 69# we're fine. 70my %depconv_cache = 71 map { catfile($abs_blddir, $_) => $_ } 72 keys %{$unified_info{generate}}; 73 74my %procedures = ( 75 'gcc' => 76 sub { 77 (my $objfile = shift) =~ s|\.d$|.o|i; 78 my $line = shift; 79 80 # Remove the original object file 81 $line =~ s|^.*\.o: | |; 82 # All we got now is a dependency, shave off surrounding spaces 83 $line =~ s/^\s+//; 84 $line =~ s/\s+$//; 85 # Also, shave off any continuation 86 $line =~ s/\s*\\$//; 87 88 # Split the line into individual header files, and keep those 89 # that exist in some form 90 my @headers; 91 for (split(/\s+/, $line)) { 92 my $x = rel2abs($_); 93 94 if (!$depconv_cache{$x}) { 95 if (-f $x) { 96 $depconv_cache{$x} = $_; 97 } 98 } 99 100 if ($depconv_cache{$x}) { 101 push @headers, $_; 102 } else { 103 print STDERR "DEBUG[$producer]: ignoring $objfile <- $line\n" 104 if $debug; 105 } 106 } 107 return ($objfile, join(' ', @headers)) if @headers; 108 return undef; 109 }, 110 'makedepend' => 111 sub { 112 # makedepend, in its infinite wisdom, wants to have the object file 113 # in the same directory as the source file. This doesn't work too 114 # well with out-of-source-tree builds, so we must resort to tricks 115 # to get things right. Fortunately, the .d files are always placed 116 # parallel with the object files, so all we need to do is construct 117 # the object file name from the dep file name. 118 (my $objfile = shift) =~ s|\.d$|.o|i; 119 my $line = shift; 120 121 # Discard comments 122 return undef if $line =~ /^(#.*|\s*)$/; 123 124 # Remove the original object file 125 $line =~ s|^.*\.o: | |; 126 # Also, remove any dependency that starts with a /, because those 127 # are typically system headers 128 $line =~ s/\s+\/(\\.|\S)*//g; 129 # Finally, discard all empty lines 130 return undef if $line =~ /^\s*$/; 131 132 # All we got now is a dependency, just shave off surrounding spaces 133 $line =~ s/^\s+//; 134 $line =~ s/\s+$//; 135 return ($objfile, $line); 136 }, 137 'VMS C' => 138 sub { 139 state $abs_srcdir_shaved = undef; 140 state $srcdir_shaved = undef; 141 142 unless (defined $abs_srcdir_shaved) { 143 ($abs_srcdir_shaved = $abs_srcdir) =~ s|[>\]]$||; 144 ($srcdir_shaved = $srcdir) =~ s|[>\]]$||; 145 } 146 147 # current versions of DEC / Compaq / HP / VSI C strips away all 148 # directory information from the object file, so we must insert it 149 # back. To make life simpler, we simply replace it with the 150 # corresponding .D file that's had its extension changed. Since 151 # .D files are always written parallel to the object files, we 152 # thereby get the directory information for free. 153 (my $objfile = shift) =~ s|\.D$|.OBJ|i; 154 my $line = shift; 155 156 # Shave off the target. 157 # 158 # The pattern for target and dependencies will always take this 159 # form: 160 # 161 # target SPACE : SPACE deps 162 # 163 # This is so a volume delimiter (a : without any spaces around it) 164 # won't get mixed up with the target / deps delimiter. We use this 165 # to easily identify what needs to be removed. 166 m|\s:\s|; $line = $'; 167 168 # We know that VMS has system header files in text libraries, 169 # extension .TLB. We also know that our header files aren't stored 170 # in text libraries. Finally, we know that VMS C produces exactly 171 # one dependency per line, so we simply discard any line ending with 172 # .TLB. 173 return undef if /\.TLB\s*$/; 174 175 # All we got now is a dependency, just shave off surrounding spaces 176 $line =~ s/^\s+//; 177 $line =~ s/\s+$//; 178 179 # VMS C gives us absolute paths, always. Let's see if we can 180 # make them relative instead. 181 $line = canonpath($line); 182 183 unless (defined $depconv_cache{$line}) { 184 my $dep = $line; 185 # Since we have already pre-populated the cache with 186 # mappings for generated headers, we only need to deal 187 # with the source tree. 188 if ($dep =~ s|^\Q$abs_srcdir_shaved\E([\.>\]])?|$srcdir_shaved$1|i) { 189 # Also check that the header actually exists 190 if (-f $line) { 191 $depconv_cache{$line} = $dep; 192 } 193 } 194 } 195 return ($objfile, $depconv_cache{$line}) 196 if defined $depconv_cache{$line}; 197 print STDERR "DEBUG[$producer]: ignoring $objfile <- $line\n" 198 if $debug; 199 200 return undef; 201 }, 202 'VC' => 203 sub { 204 # With Microsoft Visual C the flags /Zs /showIncludes give us the 205 # necessary output to be able to create dependencies that nmake 206 # (or any 'make' implementation) should be able to read, with a 207 # bit of help. The output we're interested in looks something 208 # like this (it always starts the same) 209 # 210 # Note: including file: {whatever header file} 211 # 212 # This output is localized, so for example, the German pack gives 213 # us this: 214 # 215 # Hinweis: Einlesen der Datei: {whatever header file} 216 # 217 # To accommodate, we need to use a very general regular expression 218 # to parse those lines. 219 # 220 # Since there's no object file name at all in that information, 221 # we must construct it ourselves. 222 223 (my $objfile = shift) =~ s|\.d$|.obj|i; 224 my $line = shift; 225 226 # There are also other lines mixed in, for example compiler 227 # warnings, so we simply discard anything that doesn't start with 228 # the Note: 229 230 if (/^[^:]*: [^:]*: */) { 231 (my $tail = $') =~ s/\s*\R$//; 232 233 # VC gives us absolute paths for all include files, so to 234 # remove system header dependencies, we need to check that 235 # they don't match $abs_srcdir or $abs_blddir. 236 $tail = canonpath($tail); 237 238 unless (defined $depconv_cache{$tail}) { 239 my $dep = $tail; 240 # Since we have already pre-populated the cache with 241 # mappings for generated headers, we only need to deal 242 # with the source tree. 243 if ($dep =~ s|^\Q$abs_srcdir\E\\|\$(SRCDIR)\\|i) { 244 # Also check that the header actually exists 245 if (-f $line) { 246 $depconv_cache{$tail} = $dep; 247 } 248 } 249 } 250 return ($objfile, '"'.$depconv_cache{$tail}.'"') 251 if defined $depconv_cache{$tail}; 252 print STDERR "DEBUG[$producer]: ignoring $objfile <- $tail\n" 253 if $debug; 254 } 255 256 return undef; 257 }, 258 'embarcadero' => 259 sub { 260 # With Embarcadero C++Builder's preprocessor (cpp32.exe) the -Sx -Hp 261 # flags give us the list of #include files read, like the following: 262 # 263 # Including ->->{whatever header file} 264 # 265 # where each "->" indicates the nesting level of the #include. The 266 # logic here is otherwise the same as the 'VC' scheme. 267 # 268 # Since there's no object file name at all in that information, 269 # we must construct it ourselves. 270 271 (my $objfile = shift) =~ s|\.d$|.obj|i; 272 my $line = shift; 273 274 # There are also other lines mixed in, for example compiler 275 # warnings, so we simply discard anything that doesn't start with 276 # the Note: 277 278 if (/^Including (->)*/) { 279 (my $tail = $') =~ s/\s*\R$//; 280 281 # C++Builder gives us relative paths when possible, so to 282 # remove system header dependencies, we convert them to 283 # absolute paths and check that they don't match $abs_srcdir 284 # or $abs_blddir, just as the 'VC' scheme. 285 $tail = rel2abs($tail); 286 287 unless (defined $depconv_cache{$tail}) { 288 my $dep = $tail; 289 # Since we have already pre-populated the cache with 290 # mappings for generated headers, we only need to deal 291 # with the source tree. 292 if ($dep =~ s|^\Q$abs_srcdir\E\\|\$(SRCDIR)\\|i) { 293 # Also check that the header actually exists 294 if (-f $line) { 295 $depconv_cache{$tail} = $dep; 296 } 297 } 298 } 299 return ($objfile, '"'.$depconv_cache{$tail}.'"') 300 if defined $depconv_cache{$tail}; 301 print STDERR "DEBUG[$producer]: ignoring $objfile <- $tail\n" 302 if $debug; 303 } 304 305 return undef; 306 }, 307); 308my %continuations = ( 309 'gcc' => "\\", 310 'makedepend' => "\\", 311 'VMS C' => "-", 312 'VC' => "\\", 313 'embarcadero' => "\\", 314); 315 316die "Producer unrecognised: $producer\n" 317 unless exists $procedures{$producer} && exists $continuations{$producer}; 318 319my $procedure = $procedures{$producer}; 320my $continuation = $continuations{$producer}; 321 322my $buildfile_new = "$buildfile-$$"; 323 324my %collect = (); 325foreach my $depfile (@depfiles) { 326 open IDEP,$depfile or die "Trying to read $depfile: $!\n"; 327 while (<IDEP>) { 328 s|\R$||; # The better chomp 329 my ($target, $deps) = $procedure->($depfile, $_); 330 $collect{$target}->{$deps} = 1 if defined $target; 331 } 332 close IDEP; 333} 334 335open IBF, $buildfile or die "Trying to read $buildfile: $!\n"; 336open OBF, '>', $buildfile_new or die "Trying to write $buildfile_new: $!\n"; 337while (<IBF>) { 338 last if /^# DO NOT DELETE THIS LINE/; 339 print OBF or die "$!\n"; 340} 341close IBF; 342 343print OBF "# DO NOT DELETE THIS LINE -- make depend depends on it.\n"; 344 345foreach my $target (sort keys %collect) { 346 my $prefix = $target . ' :'; 347 my @deps = sort keys %{$collect{$target}}; 348 349 while (@deps) { 350 my $buf = $prefix; 351 $prefix = ''; 352 353 while (@deps && ($buf eq '' 354 || length($buf) + length($deps[0]) <= 77)) { 355 $buf .= ' ' . shift @deps; 356 } 357 $buf .= ' '.$continuation if @deps; 358 359 print OBF $buf,"\n" or die "Trying to print: $!\n" 360 } 361} 362 363close OBF; 364 365if (compare_text($buildfile_new, $buildfile) != 0) { 366 rename $buildfile_new, $buildfile 367 or die "Trying to rename $buildfile_new -> $buildfile: $!\n"; 368} 369 370END { 371 # On VMS, we want to remove all generations of this file, in case there 372 # are more than one, so we loop. 373 if (defined $buildfile_new) { 374 while (unlink $buildfile_new) {} 375 } 376} 377