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