util/postprocess-makedepend.pl: make an effort to collect dependencies
[openssl.git] / util / postprocess-makedepend.pl
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(canonpath rel2abs);
16
17 my $abs_srcdir = rel2abs($config{sourcedir});
18 my $abs_blddir = rel2abs($config{builddir});
19
20 my $producer = shift @ARGV;
21 die "Producer not given\n" unless $producer;
22
23 my $procedure = {
24     'makedepend' =>
25         sub {
26             my $line = shift;
27
28             # makedepend, in its infinite wisdom, wants to have the object file
29             # in the same directory as the source file.  This doesn't work too
30             # well with out-of-source-tree builds, so we must resort to tricks
31             # to get things right.  The trick is to call makedepend with an
32             # extra suffix that contains the desired object file path, like
33             # this:
34             #
35             #   makedepend -f- -o"|dir/foo.o" -- $(CFLAGS) -- ../some/foo.c
36             #
37             # The result will look something like this:
38             #
39             #   ../somewhere/foo|dir/foo.o: deps...
40             #
41             # Which is easy to massage by removing everything up to the first |
42
43             # Remove everything up to the first |
44             $line =~ s/^.*\|//;
45             # Also, remove any dependency that starts with a /, because those
46             # are typically system headers
47             $line =~ s/\s+\/(\\.|\S)*//g;
48             # Finally, discard all empty lines or comment lines
49             return undef if $line =~ /:\s*$/ || $line =~ /^(#.*|\s*)$/;
50
51             my ($target, $deps) = $line =~ /^((?:\\.|[^:])*):(.*)/;
52             $deps =~ s/^\s+//;
53             $deps =~ s/\s+$//;
54             return ($target, $deps);
55         },
56     'VMS C' =>
57         sub {
58             my $line = shift;
59
60             # current versions of DEC / Compaq / HP / VSI C strips away all
61             # directory information from the object file, so we must insert it
62             # back. Just to be safe against future changes, we check that there
63             # really is no directory information.
64             my $directory = shift;
65
66             # The pattern for target and dependencies will always take this
67             # form:
68             #
69             #   target SPACE : SPACE deps
70             #
71             # This is so a volume delimiter (a : without any spaces around it)
72             # won't get mixed up with the target / deps delimiter.  We use this
73             # fact in the regexp below to make sure we do look at the target.
74             $line =~ s/^/$directory/ unless /^\S+[:>\]]\S+\s+:/;
75
76             # We know that VMS has system header files in text libraries,
77             # extension .TLB.  We also know that our header files aren't stored
78             # in text libraries.  Finally, we know that VMS C produces exactly
79             # one dependency per line, so we simply discard any line ending with
80             # .TLB.
81             return undef if /\.TLB\s*$/;
82
83             my ($target, $deps) = $line =~ /^(.*)\s:\s(.*)/;
84             $deps =~ s/^\s+//;
85             $deps =~ s/\s+$//;
86             return ($target, $deps);
87         },
88     'VC' =>
89         sub {
90             my $line = shift;
91             my $object = shift;
92
93             # For the moment, we only support Visual C on native Windows, or
94             # compatible compilers.  With those, the flags /Zs /showIncludes
95             # give us the necessary output to be able to create dependencies
96             # that nmake (or any 'make' implementation) should be able to read,
97             # with a bit of help.  The output we're interested in looks like
98             # this (it always starts the same)
99             #
100             #   Note: including file: {whatever header file}
101             #
102             # So all we really have to do is to is to replace the start of the
103             # line with an object file specification, given to us as an extra
104             # argument (passed from $ARGV[1]);
105             #
106             # There are also other lines mixed in, for example compiler
107             # warnings, so we simply discard anything that doesn't start with
108             # the Note:
109
110             if (/^Note: including file: */) {
111                 (my $tail = $') =~ s/\s*\R$//;
112
113                 # VC gives us absolute paths for all include files, so to
114                 # remove system header dependencies, we need to check that
115                 # they don't match $abs_srcdir or $abs_blddir
116                 $tail = canonpath($tail);
117                 if ($tail =~ m|^\Q$abs_srcdir\E|i
118                         || $tail =~ m|^\Q$abs_blddir\E|i) {
119                     return ($object, "\"$tail\"");
120                 }
121             }
122
123             return undef;
124         },
125 } -> {$producer};
126
127 die "Producer unrecognised: $producer\n" unless defined $procedure;
128
129 my %collect = ();
130 while (<STDIN>) {
131     s|\R$||;                    # The better chomp
132     my ($target, $deps) = $procedure->($_, @ARGV);
133     $collect{$target}->{$deps} = 1
134         if defined $target;
135 }
136
137 my $continuation = {
138     'makedepend' => "\\",
139     'VMS C' => "-",
140     'VC' => "\\",
141 } -> {$producer};
142
143 die "Producer unrecognised: $producer\n" unless defined $continuation;
144
145 foreach my $target (sort keys %collect) {
146     my $prefix = $target . ' :';
147     my @deps = sort keys %{$collect{$target}};
148
149     while (@deps) {
150         my $buf = $prefix;
151         $prefix = '';
152
153         while (@deps && ($buf eq '' || length($buf) + length($deps[0]) <= 77)) {
154             $buf .= ' ' . shift @deps;
155         }
156         $buf .= ' '.$continuation if @deps;
157
158         print $buf,"\n";
159     }
160 }