Home | History | Annotate | Line # | Download | only in util
      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 
      9 use strict;
     10 use warnings;
     11 
     12 use lib '.';
     13 use configdata;
     14 
     15 use File::Spec::Functions qw(:DEFAULT rel2abs);
     16 use File::Compare qw(compare_text);
     17 use 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 
     23 my $debug = $ENV{ADD_DEPENDS_DEBUG};
     24 my $buildfile = $config{build_file};
     25 my $build_mtime = (stat($buildfile))[9];
     26 my $configdata_mtime = (stat('configdata.pm'))[9];
     27 my $rebuild = 0;
     28 my $depext = $target{dep_extension} || ".d";
     29 my @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 
     47 exit 0 unless $rebuild;
     48 
     49 # Ok, primary checks are done, time to do some real work
     50 
     51 my $producer = shift @ARGV;
     52 die "Producer not given\n" unless $producer;
     53 
     54 my $srcdir = $config{sourcedir};
     55 my $blddir = $config{builddir};
     56 my $abs_srcdir = rel2abs($srcdir);
     57 my $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.
     70 my %depconv_cache =
     71     map { catfile($abs_blddir, $_) => $_ }
     72     keys %{$unified_info{generate}};
     73 
     74 my %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 );
    308 my %continuations = (
    309     'gcc' => "\\",
    310     'makedepend' => "\\",
    311     'VMS C' => "-",
    312     'VC' => "\\",
    313     'embarcadero' => "\\",
    314 );
    315 
    316 die "Producer unrecognised: $producer\n"
    317     unless exists $procedures{$producer} && exists $continuations{$producer};
    318 
    319 my $procedure = $procedures{$producer};
    320 my $continuation = $continuations{$producer};
    321 
    322 my $buildfile_new = "$buildfile-$$";
    323 
    324 my %collect = ();
    325 foreach 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 
    335 open IBF, $buildfile or die "Trying to read $buildfile: $!\n";
    336 open OBF, '>', $buildfile_new or die "Trying to write $buildfile_new: $!\n";
    337 while (<IBF>) {
    338     last if /^# DO NOT DELETE THIS LINE/;
    339     print OBF or die "$!\n";
    340 }
    341 close IBF;
    342 
    343 print OBF "# DO NOT DELETE THIS LINE -- make depend depends on it.\n";
    344 
    345 foreach 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 
    363 close OBF;
    364 
    365 if (compare_text($buildfile_new, $buildfile) != 0) {
    366     rename $buildfile_new, $buildfile
    367         or die "Trying to rename $buildfile_new -> $buildfile: $!\n";
    368 }
    369 
    370 END {
    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