add-depends.pl 9.3 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 17
use File::Compare qw(compare_text);

18 19 20 21
# 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;

22
my $debug = $ENV{ADD_DEPENDS_DEBUG};
R
Richard Levitte 已提交
23
my $buildfile = $config{build_file};
24 25
my $build_mtime = (stat($buildfile))[9];
my $rebuild = 0;
R
Richard Levitte 已提交
26
my $depext = $target{dep_extension} || ".d";
27
my @depfiles =
28
    sort
29 30 31 32 33 34 35 36
    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 已提交
37 38 39 40
    map { (my $x = $_) =~ s|\.o$|$depext|; $x; }
    grep { $unified_info{sources}->{$_}->[0] =~ /\.cc?$/ }
    keys %{$unified_info{sources}};

41 42 43 44
exit 0 unless $rebuild;

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

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

48 49 50
my $abs_srcdir = rel2abs($config{sourcedir});
my $abs_blddir = rel2abs($config{builddir});

51 52 53 54 55 56 57 58 59 60 61 62 63 64
# 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 =
    map { lc catfile($abs_blddir, $_) => $_ }
    keys %{$unified_info{generate}};
65 66 67 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 100 101 102 103 104 105 106 107 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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

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 {
            # 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+$//;
            return ($objfile, $line);
        },
    '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
156 157 158 159 160 161 162 163 164 165 166
                # they don't match $abs_srcdir or $abs_blddir.
                $tail = lc canonpath($tail);

                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;
                    }
167
                }
168 169 170 171
                return ($objfile, '"'.$depconv_cache{$tail}.'"')
                    if defined $depconv_cache{$tail};
                print STDERR "DEBUG[VC]: ignoring $objfile <- $tail\n"
                    if $debug;
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
            }

            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 已提交
205 206 207 208 209 210 211 212 213 214
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";

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
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 已提交
240 241
    }
}
242

R
Richard Levitte 已提交
243 244 245 246 247 248
close OBF;

if (compare_text($buildfile_new, $buildfile) != 0) {
    rename $buildfile_new, $buildfile
        or die "Trying to rename $buildfile_new -> $buildfile: $!\n";
}
249 250 251 252 253 254 255 256

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) {}
    }
}