-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakeppreplay
executable file
·273 lines (249 loc) · 9.88 KB
/
makeppreplay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
156
157
158
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
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
235
236
237
238
239
240
241
242
243
244
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
270
271
272
273
#!/usr/bin/env perl
# $Id: makeppreplay,v 1.24 2016/09/12 20:34:11 pfeiffer Exp $
package Mpp;
use strict;
our $datadir;
BEGIN {
#@@setdatadir
#
# Find the location of our data directory that contains the auxiliary files.
# This is normally built into the program by install.pl, but if makepp hasn't
# been installed, then we look in the directory we were run from.
#
$datadir = $0; # Assume it's running from the same place that
# we're running from.
unless( $datadir =~ s@/[^/]+$@@ ) { # No path specified?
# See if we can find ourselves in the path.
foreach( split( /:/, $ENV{'PATH'} ), '.' ) {
# Add '.' to the path in case the user is
# running it with "perl makeppreplay" even if
# . is not in his path.
if( -d "$_/Mpp" ) { # Found something we need?
$datadir = $_;
last;
}
}
}
$datadir or die "makeppreplay: can't find library files\n";
$datadir = eval "use Cwd; cwd . '/$datadir'"
if $datadir =~ /^\./; # Make it absolute, if it's a relative path.
do "$datadir/Mpp/DB.pm" if exists $DB::{sh};
#@@
unshift @INC, $datadir;
}
use Mpp::Utils;
use Mpp::Text ();
use POSIX ();
use Mpp;
use Mpp::Event qw(wait_for);
our $progname;
my( $sed, $temporary );
use Mpp::File;
use Mpp::FileOpt;
use Mpp::Cmds;
use Mpp::Makefile;
use Mpp::Rule;
my @targets;
my $target_cwd = $CWD_INFO;
Mpp::Makefile::find_root_makefile_upwards $target_cwd;
# See if we find a RootMakeppfile from here, in case Perl code uses ROOT.
my $modules = '';
my %command_line_vars;
my $tmp;
sub load_build_info_file($) {
my $build_info = &Mpp::File::load_build_info_file
or return; # No build info -- don't create it by following modification.
unless( exists $build_info->{SIGNATURE} ) { # Out of date build info?
my $sig = &Mpp::File::signature; # But file exists.
my $count = keys %$build_info;
delete @{$build_info}{qw(FROM_REPOSITORY LINKED_TO_CACHE)} unless $sig;
while( my( $key, $value ) = each %$build_info ) {
delete $build_info->{$key} # We may recalculate some of these, but not necessarily all.
# So make sure not to save any outdated ones with new SIGNATURE.
if $key ne 'DEP_SIGS' && Mpp::Signature::is_content_based $value or
$key eq 'LINKED_TO_CACHE' && $_[0]{LSTAT}[Mpp::File::STAT_NLINK] == 1 or
# Could have more than one link, but none to cache -- how can we know?
$key eq 'FROM_REPOSITORY' && (readlink( &absolute_filename_nolink ) || '') ne $value;
}
if( $sig ) { # But file exists.
&Mpp::File::mark_build_info_for_update
if $count != keys %$build_info; # Found a significant change?
$build_info->{RESCAN} ||= 1; # If we save this, let makepp double check what we signed.
# keep value if it was already 2 from last run.
$build_info->{SIGNATURE} = $sig;
}
}
$build_info;
}
@ARGV = '.' unless @ARGV;
my $nothing_in_dir;
my $found_in_dir = 0;
eval {
while( @ARGV) {
Mpp::Text::getopts \%command_line_vars, 1,
['c', qr/root(?:[-_]?dir(?:ectory)?)?/, \$tmp, undef, sub {
Mpp::Makefile::find_root_makefile_upwards $target_cwd;
$target_cwd = $target_cwd->{ROOT}
or die "$0: No RootMakeppfile(.mk) found above `", absolute_filename( $target_cwd ), "'.\n";
# See if we find a RootMakeppfile from here.
chdir $target_cwd; # Switch to that directory.
}],
[qw(C directory), \$tmp, 1, sub {
$target_cwd = file_info $tmp, $target_cwd;
chdir $target_cwd; # Switch to that directory.
Mpp::Makefile::find_root_makefile_upwards $target_cwd;
# See if we find a RootMakeppfile from here.
}],
['I', qr/include(?:[-_]?dir)?/, \$tmp, 1, sub { unshift @INC, absolute_filename file_info $tmp }],
[qw(M module), \$tmp, 1, sub { $tmp =~ s/=(.*)/ qw($1)/ and $tmp =~ tr/,/ /; $modules .= "use $tmp;" }],
[qw(s sed), \$sed, 1],
[qw(t temporary), \$temporary],
@Mpp::common_opts;
@ARGV = '.' unless @ARGV || @targets;
my $finfo = file_info shift, $target_cwd;
if( is_dir $finfo ) {
Mpp::log RULE_ALL => $finfo
if $Mpp::log_level;
my $build_info_subdir = file_info relative_filename( $finfo ) . "/$Mpp::File::build_info_subdir";
$nothing_in_dir = absolute_filename $finfo;
unshift @ARGV, grep {
$_ = relative_filename $_;
s!$Mpp::File::build_info_subdir/!! &&
s!\.mk$!! &&
++$found_in_dir;
} Mpp::Glob::zglob_fileinfo '*.mk', $build_info_subdir;
next;
}
my $build_info = $finfo->{BUILD_INFO} = load_build_info_file $finfo;
if( $found_in_dir ) { # Not an explicit target?
$found_in_dir--;
next unless exists $build_info->{COMMAND}; # Not built by makepp
undef $nothing_in_dir;
} else {
die "$progname: No files built by makepp in `$nothing_in_dir'.\n"
if $nothing_in_dir;
die "$progname: Nothing known about `" . absolute_filename( $finfo ) . "'.\n"
unless defined $build_info;
die "$progname: File `" . absolute_filename( $finfo ) . "' not built by makepp.\n"
unless exists $build_info->{COMMAND};
}
(my $cmd = $build_info->{COMMAND}) =~ s/\A\|FAILED\|//;
$cmd =~ tr/\cC/\n/;
if( $sed ) {
my $ocmd = $cmd;
eval $sed for $cmd;
die "--sed: $@" if $@;
undef $finfo->{xSED} if $ocmd ne $cmd; # Cmd might now be changing other files. Treat this as a change outside of mpp.
}
(my $deps = $build_info->{SORTED_DEPS} || '') =~ tr/\cA/ /;
my $dinfo = file_info $build_info->{CWD}, $finfo->{'..'};
my $makefile = $dinfo->{MAKEINFO};
unless( $makefile ) {
$makefile = Mpp::Makefile::load $dinfo, $dinfo, \%command_line_vars, '', [], \%ENV;
if( $modules ) {
$makefile->cd; # Evaluate in the correct directory.
Mpp::Subs::eval_or_die $modules, $makefile, absolute_filename( $dinfo ) . ':0';
}
}
$makefile->{EXPORTS} ||= {};
my $target = relative_filename $finfo, $dinfo;
my $rule_cache = "$deps\01$cmd"; # Try to group targets that came from same rule.
if( $dinfo->{$rule_cache} ) {
Mpp::log RULE_EXTEND => $finfo, $dinfo->{$rule_cache}{TARGETS}
if $Mpp::log_level;
$dinfo->{$rule_cache}{TARGET_STRING} .= " $target";
push @{$dinfo->{$rule_cache}{TARGETS}}, $finfo;
} else {
Mpp::log RULE_NEW => $finfo
if $Mpp::log_level;
$dinfo->{$rule_cache} = new Mpp::Rule $target, $deps, $cmd, $makefile, Mpp::File::build_info_fname( $finfo ) . ':0';
push @targets, $finfo;
$dinfo->{$rule_cache}{TARGETS} = [$finfo];
@{$dinfo->{$rule_cache}{ENV_DEPS}}{split "\cA", $build_info->{ENV_DEPS}} = split "\cA", $build_info->{ENV_VALS}
if exists $build_info->{ENV_DEPS};
}
$finfo->{RULE} = $dinfo->{$rule_cache};
}
@Mpp::common_opts = ();
close DATA;
die "$progname: No files built by makepp in `$nothing_in_dir'.\n"
if $nothing_in_dir;
for my $target ( @targets ) { # Should use Mpp::build et al. here!
my $build_info = $target->{BUILD_INFO};
if( delete $build_info->{FROM_REPOSITORY} || delete $build_info->{LINKED_TO_CACHE} ) {
# If these survived load_build_info_file, the file is linked.
Mpp::log REMOVE => $target
if $Mpp::log_level;
Mpp::File::unlink $target;
}
my $rule = $target->{RULE};
my $n_files = @{$rule->{TARGETS}};
local $rule->{MAKEFILE}{EXPORTS} = $rule->{ENV_DEPS}
if exists $rule->{ENV_DEPS};
if( $Mpp::dry_run ) {
$rule->print_command( $rule->{COMMAND_STRING} );
} elsif( my $status = wait_for $rule->execute(
$rule->{COMMAND_STRING},
$rule->{TARGETS},
[map file_info( $_, $target->{'..'} ), split ' ', $rule->{DEPENDENCY_STRING}] ) ) {
warn 'error: Failed to build target', (@{$rule->{TARGETS}}>1 ? 's' : ''),
map( ' `'.absolute_filename( $_ )."'", @{$rule->{TARGETS}} ), " [$status]\n";
Mpp::log RULE_FAILED => $rule
if $Mpp::log_level;
$Mpp::error_found = $status
if $status =~ /^signal (?:$Mpp::int_signo|$Mpp::quit_signo)$/os;
$Mpp::error_found ||= $status # Remember the error status. This will
unless $Mpp::keep_going; # cause us to stop compilation as soon as
# possible.
$rule->{TARGET_STRING} =~ s/ /' `/g;
$Mpp::failed_count += $n_files;
unless( $temporary || $build_info->{COMMAND} =~ /\A\|FAILED\|/ ) {
for my $tinfo ( @{$rule->{TARGETS}} ) {
next if exists $tinfo->{xSED};
substr $tinfo->{BUILD_INFO}{COMMAND}, 0, 0, '|FAILED|';
Mpp::File::mark_build_info_for_update $tinfo;
}
&Mpp::File::update_build_infos;
}
last unless $Mpp::keep_going;
} else {
Mpp::log SUCCESS => $rule, $rule->{TARGETS}
if $Mpp::log_level;
$Mpp::n_files_changed += $n_files;
next if $temporary;
my $sig = $build_info->{SIG_METHOD_NAME}; # All targets have the same.
$sig &&= $rule->set_signature_class( $sig );
my $dep_sigs = ''; # Precalculate this for the targets loop
my $sep = '';
for my $dep ( split /\cA/, $build_info->{SORTED_DEPS} || '' ) {
$dep = path_file_info $dep, $rule->{MAKEFILE}{CWD};
$dep->{BUILD_INFO} ||= load_build_info_file $dep;
$dep_sigs .= $sep .
(($sig ? $sig->signature( $dep ) : Mpp::File::signature $dep) || '');
$sep = "\cA";
}
for my $tinfo ( @{$rule->{TARGETS}} ) {
next if exists $tinfo->{xSED};
$build_info = $tinfo->{BUILD_INFO};
delete $tinfo->{LSTAT};
$build_info->{SIGNATURE} = Mpp::File::signature $tinfo;
if( $tinfo->{LINK_DEREF} && $tinfo->{LSTAT}[Mpp::File::STAT_NLINK] == 1 ) {
# Assume nlink > 1 to mean action only created
# a link to an already existing symlink.
$build_info->{SYMLINK} = readlink absolute_filename $tinfo;
} else {
delete $build_info->{SYMLINK}; # Unlikely to have changed, but just in case.
}
$build_info->{DEP_SIGS} = $dep_sigs;
$build_info->{BUILD_SIGNATURE} = $sig ? $sig->signature( $tinfo ) : $build_info->{SIGNATURE};
$build_info->{RESCAN} = 2; # Let makepp double check what we built.
Mpp::File::mark_build_info_for_update $tinfo;
}
&Mpp::File::update_build_infos;
}
}
};
perform; # Use function w/o args only for final diagnostic.
__DATA__
[option ...] [VAR=value ...] target ...
Valid options are: