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