add-depends.pl 10.4 KB
Newer Older
R
Richard Levitte 已提交
1 2 3 4 5 6 7 8
#! /usr/bin/env perl
# Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

9 10 11
use strict;
use warnings;

R
Richard Levitte 已提交
12 13 14
use lib '.';
use configdata;

15
use File::Spec::Functions qw(:DEFAULT rel2abs);
R
Richard Levitte 已提交
16
use File::Compare qw(compare_text);
17
use feature 'state';
R
Richard Levitte 已提交
18

19 20 21 22
# When using stat() on Windows, we can get it to perform better by avoid some
# data.  This doesn't affect the mtime field, so we're not losing anything...
${^WIN32_SLOPPY_STAT} = 1;

23
my $debug = $ENV{ADD_DEPENDS_DEBUG};
R
Richard Levitte 已提交
24
my $buildfile = $config{build_file};
25 26
my $build_mtime = (stat($buildfile))[9];
my $rebuild = 0;
R
Richard Levitte 已提交
27
my $depext = $target{dep_extension} || ".d";
28
my @depfiles =
29
    sort
30 31 32 33 34 35 36 37
    grep {
        # This grep has side effects.  Not only does if check the existence
        # of the dependency file given in $_, but it also checks if it's
        # newer than the build file, and if it is, sets $rebuild.
        my @st = stat($_);
        $rebuild = 1 if @st && $st[9] > $build_mtime;
        scalar @st > 0;         # Determines the grep result
    }
R
Richard Levitte 已提交
38 39 40 41
    map { (my $x = $_) =~ s|\.o$|$depext|; $x; }
    grep { $unified_info{sources}->{$_}->[0] =~ /\.cc?$/ }
    keys %{$unified_info{sources}};

42 43 44 45
exit 0 unless $rebuild;

# Ok, primary checks are done, time to do some real work

46 47 48
my $producer = shift @ARGV;
die "Producer not given\n" unless $producer;

49 50 51 52
my $srcdir = $config{sourcedir};
my $blddir = $config{builddir};
my $abs_srcdir = rel2abs($srcdir);
my $abs_blddir = rel2abs($blddir);
53

54 55 56 57 58 59 60 61 62 63 64 65
# Convenient cache of absolute to relative map.  We start with filling it
# with mappings for the known generated header files.  They are relative to
# the current working directory, so that's an easy task.
# NOTE: there's more than C header files that are generated.  They will also
# generate entries in this map.  We could of course deal with C header files
# only, but in case we decide to handle more than just C files in the future,
# we already have the mechanism in place here.
# NOTE2: we lower case the index to make it searchable without regard for
# character case.  That could seem dangerous, but as long as we don't have
# files we depend on in the same directory that only differ by character case,
# we're fine.
my %depconv_cache =
66
    map { catfile($abs_blddir, $_) => $_ }
67
    keys %{$unified_info{generate}};
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

my %procedures = (
    'gcc' => undef,             # gcc style dependency files needs no mods
    'makedepend' =>
        sub {
            # makedepend, in its infinite wisdom, wants to have the object file
            # in the same directory as the source file.  This doesn't work too
            # well with out-of-source-tree builds, so we must resort to tricks
            # to get things right.  Fortunately, the .d files are always placed
            # parallel with the object files, so all we need to do is construct
            # the object file name from the dep file name.
            (my $objfile = shift) =~ s|\.d$|.o|i;
            my $line = shift;

            # Discard comments
            return undef if $line =~ /^(#.*|\s*)$/;

            # Remove the original object file
            $line =~ s|^.*\.o: | |;
            # Also, remove any dependency that starts with a /, because those
            # are typically system headers
            $line =~ s/\s+\/(\\.|\S)*//g;
            # Finally, discard all empty lines
            return undef if $line =~ /^\s*$/;

            # All we got now is a dependency, just shave off surrounding spaces
            $line =~ s/^\s+//;
            $line =~ s/\s+$//;
            return ($objfile, $line);
        },
    'VMS C' =>
        sub {
100 101 102 103 104 105 106 107
            state $abs_srcdir_shaved = undef;
            state $srcdir_shaved = undef;

            unless (defined $abs_srcdir_shaved) {
                ($abs_srcdir_shaved = $abs_srcdir) =~ s|[>\]]$||;
                ($srcdir_shaved = $srcdir) =~ s|[>\]]$||;
            }

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
            # current versions of DEC / Compaq / HP / VSI C strips away all
            # directory information from the object file, so we must insert it
            # back.  To make life simpler, we simply replace it with the
            # corresponding .D file that's had its extension changed.  Since
            # .D files are always written parallel to the object files, we
            # thereby get the directory information for free.
            (my $objfile = shift) =~ s|\.D$|.OBJ|i;
            my $line = shift;

            # Shave off the target.
            #
            # The pattern for target and dependencies will always take this
            # form:
            #
            #   target SPACE : SPACE deps
            #
            # This is so a volume delimiter (a : without any spaces around it)
            # won't get mixed up with the target / deps delimiter.  We use this
            # to easily identify what needs to be removed.
            m|\s:\s|; $line = $';

            # We know that VMS has system header files in text libraries,
            # extension .TLB.  We also know that our header files aren't stored
            # in text libraries.  Finally, we know that VMS C produces exactly
            # one dependency per line, so we simply discard any line ending with
            # .TLB.
            return undef if /\.TLB\s*$/;

            # All we got now is a dependency, just shave off surrounding spaces
            $line =~ s/^\s+//;
            $line =~ s/\s+$//;
139 140 141

            # VMS C gives us absolute paths, always.  Let's see if we can
            # make them relative instead.
142
            $line = canonpath($line);
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

            unless (defined $depconv_cache{$line}) {
                my $dep = $line;
                # Since we have already pre-populated the cache with
                # mappings for generated headers, we only need to deal
                # with the source tree.
                if ($dep =~ s|^\Q$abs_srcdir_shaved\E([\.>\]])?|$srcdir_shaved$1|i) {
                    $depconv_cache{$line} = $dep;
                }
            }
            return ($objfile, $depconv_cache{$line})
                if defined $depconv_cache{$line};
            print STDERR "DEBUG[VMS C]: ignoring $objfile <- $line\n"
                if $debug;

            return undef;
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
        },
    'VC' =>
        sub {
            # For the moment, we only support Visual C on native Windows, or
            # compatible compilers.  With those, the flags /Zs /showIncludes
            # give us the necessary output to be able to create dependencies
            # that nmake (or any 'make' implementation) should be able to read,
            # with a bit of help.  The output we're interested in looks like
            # this (it always starts the same)
            #
            #   Note: including file: {whatever header file}
            #
            # Since there's no object file name at all in that information,
            # we must construct it ourselves.

            (my $objfile = shift) =~ s|\.d$|.obj|i;
            my $line = shift;

            # There are also other lines mixed in, for example compiler
            # warnings, so we simply discard anything that doesn't start with
            # the Note:

            if (/^Note: including file: */) {
                (my $tail = $') =~ s/\s*\R$//;

                # VC gives us absolute paths for all include files, so to
                # remove system header dependencies, we need to check that
186
                # they don't match $abs_srcdir or $abs_blddir.
187
                $tail = canonpath($tail);
188 189 190 191 192 193 194 195 196

                unless (defined $depconv_cache{$tail}) {
                    my $dep = $tail;
                    # Since we have already pre-populated the cache with
                    # mappings for generated headers, we only need to deal
                    # with the source tree.
                    if ($dep =~ s|^\Q$abs_srcdir\E\\|\$(SRCDIR)\\|i) {
                        $depconv_cache{$tail} = $dep;
                    }
197
                }
198 199 200 201
                return ($objfile, '"'.$depconv_cache{$tail}.'"')
                    if defined $depconv_cache{$tail};
                print STDERR "DEBUG[VC]: ignoring $objfile <- $tail\n"
                    if $debug;
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
            }

            return undef;
        },
);
my %continuations = (
    'gcc' => undef,
    'makedepend' => "\\",
    'VMS C' => "-",
    'VC' => "\\",
);

die "Producer unrecognised: $producer\n"
    unless exists $procedures{$producer} && exists $continuations{$producer};

my $procedure = $procedures{$producer};
my $continuation = $continuations{$producer};

my $buildfile_new = "$buildfile-$$";

my %collect = ();
if (defined $procedure) {
    foreach my $depfile (@depfiles) {
        open IDEP,$depfile or die "Trying to read $depfile: $!\n";
        while (<IDEP>) {
            s|\R$||;                # The better chomp
            my ($target, $deps) = $procedure->($depfile, $_);
            $collect{$target}->{$deps} = 1 if defined $target;
        }
        close IDEP;
    }
}

R
Richard Levitte 已提交
235 236 237 238 239 240 241 242 243 244
open IBF, $buildfile or die "Trying to read $buildfile: $!\n";
open OBF, '>', $buildfile_new or die "Trying to write $buildfile_new: $!\n";
while (<IBF>) {
    last if /^# DO NOT DELETE THIS LINE/;
    print OBF or die "$!\n";
}
close IBF;

print OBF "# DO NOT DELETE THIS LINE -- make depend depends on it.\n";

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
if (defined $procedure) {
    foreach my $target (sort keys %collect) {
        my $prefix = $target . ' :';
        my @deps = sort keys %{$collect{$target}};

        while (@deps) {
            my $buf = $prefix;
            $prefix = '';

            while (@deps && ($buf eq ''
                                 || length($buf) + length($deps[0]) <= 77)) {
                $buf .= ' ' . shift @deps;
            }
            $buf .= ' '.$continuation if @deps;

            print OBF $buf,"\n" or die "Trying to print: $!\n"
        }
    }
} else {
    foreach my $depfile (@depfiles) {
        open IDEP,$depfile or die "Trying to read $depfile: $!\n";
        while (<IDEP>) {
            print OBF or die "Trying to print: $!\n";
        }
        close IDEP;
R
Richard Levitte 已提交
270 271
    }
}
272

R
Richard Levitte 已提交
273 274 275 276 277 278
close OBF;

if (compare_text($buildfile_new, $buildfile) != 0) {
    rename $buildfile_new, $buildfile
        or die "Trying to rename $buildfile_new -> $buildfile: $!\n";
}
279 280 281 282 283 284 285 286

END {
    # On VMS, we want to remove all generations of this file, in case there
    # are more than one, so we loop.
    if (defined $buildfile_new) {
        while (unlink $buildfile_new) {}
    }
}