Home | History | Annotate | Line # | Download | only in util
add-depends.pl revision 1.1.1.1.2.2
      1 #! /usr/bin/env perl
      2 # Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
      3 #
      4 # Licensed under the OpenSSL license (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 $rebuild = 0;
     27 my $depext = $target{dep_extension} || ".d";
     28 my @depfiles =
     29     sort
     30     grep {
     31         # This grep has side effects.  Not only does if check the existence
     32         # of the dependency file given in $_, but it also checks if it's
     33         # newer than the build file, and if it is, sets $rebuild.
     34         my @st = stat($_);
     35         $rebuild = 1 if @st && $st[9] > $build_mtime;
     36         scalar @st > 0;         # Determines the grep result
     37     }
     38     map { (my $x = $_) =~ s|\.o$|$depext|; $x; }
     39     grep { $unified_info{sources}->{$_}->[0] =~ /\.cc?$/ }
     40     keys %{$unified_info{sources}};
     41 
     42 exit 0 unless $rebuild;
     43 
     44 # Ok, primary checks are done, time to do some real work
     45 
     46 my $producer = shift @ARGV;
     47 die "Producer not given\n" unless $producer;
     48 
     49 my $srcdir = $config{sourcedir};
     50 my $blddir = $config{builddir};
     51 my $abs_srcdir = rel2abs($srcdir);
     52 my $abs_blddir = rel2abs($blddir);
     53 
     54 # Convenient cache of absolute to relative map.  We start with filling it
     55 # with mappings for the known generated header files.  They are relative to
     56 # the current working directory, so that's an easy task.
     57 # NOTE: there's more than C header files that are generated.  They will also
     58 # generate entries in this map.  We could of course deal with C header files
     59 # only, but in case we decide to handle more than just C files in the future,
     60 # we already have the mechanism in place here.
     61 # NOTE2: we lower case the index to make it searchable without regard for
     62 # character case.  That could seem dangerous, but as long as we don't have
     63 # files we depend on in the same directory that only differ by character case,
     64 # we're fine.
     65 my %depconv_cache =
     66     map { lc catfile($abs_blddir, $_) => $_ }
     67     keys %{$unified_info{generate}};
     68 
     69 my %procedures = (
     70     'gcc' => undef,             # gcc style dependency files needs no mods
     71     'makedepend' =>
     72         sub {
     73             # makedepend, in its infinite wisdom, wants to have the object file
     74             # in the same directory as the source file.  This doesn't work too
     75             # well with out-of-source-tree builds, so we must resort to tricks
     76             # to get things right.  Fortunately, the .d files are always placed
     77             # parallel with the object files, so all we need to do is construct
     78             # the object file name from the dep file name.
     79             (my $objfile = shift) =~ s|\.d$|.o|i;
     80             my $line = shift;
     81 
     82             # Discard comments
     83             return undef if $line =~ /^(#.*|\s*)$/;
     84 
     85             # Remove the original object file
     86             $line =~ s|^.*\.o: | |;
     87             # Also, remove any dependency that starts with a /, because those
     88             # are typically system headers
     89             $line =~ s/\s+\/(\\.|\S)*//g;
     90             # Finally, discard all empty lines
     91             return undef if $line =~ /^\s*$/;
     92 
     93             # All we got now is a dependency, just shave off surrounding spaces
     94             $line =~ s/^\s+//;
     95             $line =~ s/\s+$//;
     96             return ($objfile, $line);
     97         },
     98     'VMS C' =>
     99         sub {
    100             state $abs_srcdir_shaved = undef;
    101             state $srcdir_shaved = undef;
    102 
    103             unless (defined $abs_srcdir_shaved) {
    104                 ($abs_srcdir_shaved = $abs_srcdir) =~ s|[>\]]$||;
    105                 ($srcdir_shaved = $srcdir) =~ s|[>\]]$||;
    106             }
    107 
    108             # current versions of DEC / Compaq / HP / VSI C strips away all
    109             # directory information from the object file, so we must insert it
    110             # back.  To make life simpler, we simply replace it with the
    111             # corresponding .D file that's had its extension changed.  Since
    112             # .D files are always written parallel to the object files, we
    113             # thereby get the directory information for free.
    114             (my $objfile = shift) =~ s|\.D$|.OBJ|i;
    115             my $line = shift;
    116 
    117             # Shave off the target.
    118             #
    119             # The pattern for target and dependencies will always take this
    120             # form:
    121             #
    122             #   target SPACE : SPACE deps
    123             #
    124             # This is so a volume delimiter (a : without any spaces around it)
    125             # won't get mixed up with the target / deps delimiter.  We use this
    126             # to easily identify what needs to be removed.
    127             m|\s:\s|; $line = $';
    128 
    129             # We know that VMS has system header files in text libraries,
    130             # extension .TLB.  We also know that our header files aren't stored
    131             # in text libraries.  Finally, we know that VMS C produces exactly
    132             # one dependency per line, so we simply discard any line ending with
    133             # .TLB.
    134             return undef if /\.TLB\s*$/;
    135 
    136             # All we got now is a dependency, just shave off surrounding spaces
    137             $line =~ s/^\s+//;
    138             $line =~ s/\s+$//;
    139 
    140             # VMS C gives us absolute paths, always.  Let's see if we can
    141             # make them relative instead.
    142             $line = lc canonpath($line);
    143 
    144             unless (defined $depconv_cache{$line}) {
    145                 my $dep = $line;
    146                 # Since we have already pre-populated the cache with
    147                 # mappings for generated headers, we only need to deal
    148                 # with the source tree.
    149                 if ($dep =~ s|^\Q$abs_srcdir_shaved\E([\.>\]])?|$srcdir_shaved$1|i) {
    150                     $depconv_cache{$line} = $dep;
    151                 }
    152             }
    153             return ($objfile, $depconv_cache{$line})
    154                 if defined $depconv_cache{$line};
    155             print STDERR "DEBUG[VMS C]: ignoring $objfile <- $line\n"
    156                 if $debug;
    157 
    158             return undef;
    159         },
    160     'VC' =>
    161         sub {
    162             # For the moment, we only support Visual C on native Windows, or
    163             # compatible compilers.  With those, the flags /Zs /showIncludes
    164             # give us the necessary output to be able to create dependencies
    165             # that nmake (or any 'make' implementation) should be able to read,
    166             # with a bit of help.  The output we're interested in looks like
    167             # this (it always starts the same)
    168             #
    169             #   Note: including file: {whatever header file}
    170             #
    171             # Since there's no object file name at all in that information,
    172             # we must construct it ourselves.
    173 
    174             (my $objfile = shift) =~ s|\.d$|.obj|i;
    175             my $line = shift;
    176 
    177             # There are also other lines mixed in, for example compiler
    178             # warnings, so we simply discard anything that doesn't start with
    179             # the Note:
    180 
    181             if (/^Note: including file: */) {
    182                 (my $tail = $') =~ s/\s*\R$//;
    183 
    184                 # VC gives us absolute paths for all include files, so to
    185                 # remove system header dependencies, we need to check that
    186                 # they don't match $abs_srcdir or $abs_blddir.
    187                 $tail = lc canonpath($tail);
    188 
    189                 unless (defined $depconv_cache{$tail}) {
    190                     my $dep = $tail;
    191                     # Since we have already pre-populated the cache with
    192                     # mappings for generated headers, we only need to deal
    193                     # with the source tree.
    194                     if ($dep =~ s|^\Q$abs_srcdir\E\\|\$(SRCDIR)\\|i) {
    195                         $depconv_cache{$tail} = $dep;
    196                     }
    197                 }
    198                 return ($objfile, '"'.$depconv_cache{$tail}.'"')
    199                     if defined $depconv_cache{$tail};
    200                 print STDERR "DEBUG[VC]: ignoring $objfile <- $tail\n"
    201                     if $debug;
    202             }
    203 
    204             return undef;
    205         },
    206 );
    207 my %continuations = (
    208     'gcc' => undef,
    209     'makedepend' => "\\",
    210     'VMS C' => "-",
    211     'VC' => "\\",
    212 );
    213 
    214 die "Producer unrecognised: $producer\n"
    215     unless exists $procedures{$producer} && exists $continuations{$producer};
    216 
    217 my $procedure = $procedures{$producer};
    218 my $continuation = $continuations{$producer};
    219 
    220 my $buildfile_new = "$buildfile-$$";
    221 
    222 my %collect = ();
    223 if (defined $procedure) {
    224     foreach my $depfile (@depfiles) {
    225         open IDEP,$depfile or die "Trying to read $depfile: $!\n";
    226         while (<IDEP>) {
    227             s|\R$||;                # The better chomp
    228             my ($target, $deps) = $procedure->($depfile, $_);
    229             $collect{$target}->{$deps} = 1 if defined $target;
    230         }
    231         close IDEP;
    232     }
    233 }
    234 
    235 open IBF, $buildfile or die "Trying to read $buildfile: $!\n";
    236 open OBF, '>', $buildfile_new or die "Trying to write $buildfile_new: $!\n";
    237 while (<IBF>) {
    238     last if /^# DO NOT DELETE THIS LINE/;
    239     print OBF or die "$!\n";
    240 }
    241 close IBF;
    242 
    243 print OBF "# DO NOT DELETE THIS LINE -- make depend depends on it.\n";
    244 
    245 if (defined $procedure) {
    246     foreach my $target (sort keys %collect) {
    247         my $prefix = $target . ' :';
    248         my @deps = sort keys %{$collect{$target}};
    249 
    250         while (@deps) {
    251             my $buf = $prefix;
    252             $prefix = '';
    253 
    254             while (@deps && ($buf eq ''
    255                                  || length($buf) + length($deps[0]) <= 77)) {
    256                 $buf .= ' ' . shift @deps;
    257             }
    258             $buf .= ' '.$continuation if @deps;
    259 
    260             print OBF $buf,"\n" or die "Trying to print: $!\n"
    261         }
    262     }
    263 } else {
    264     foreach my $depfile (@depfiles) {
    265         open IDEP,$depfile or die "Trying to read $depfile: $!\n";
    266         while (<IDEP>) {
    267             print OBF or die "Trying to print: $!\n";
    268         }
    269         close IDEP;
    270     }
    271 }
    272 
    273 close OBF;
    274 
    275 if (compare_text($buildfile_new, $buildfile) != 0) {
    276     rename $buildfile_new, $buildfile
    277         or die "Trying to rename $buildfile_new -> $buildfile: $!\n";
    278 }
    279 
    280 END {
    281     # On VMS, we want to remove all generations of this file, in case there
    282     # are more than one, so we loop.
    283     if (defined $buildfile_new) {
    284         while (unlink $buildfile_new) {}
    285     }
    286 }
    287