1#!/usr/bin/perl -w 2# $Id: makeppreplay,v 1.15 2012/02/07 22:26:15 pfeiffer Exp $ 3 4package Mpp; 5 6use strict; 7 8our $datadir; 9BEGIN { 10 our $VERSION = '@VERSION@'; 11 12#@@setdatadir 13# 14# Find the location of our data directory that contains the auxiliary files. 15# This is normally built into the program by install.pl, but if makepp hasn't 16# been installed, then we look in the directory we were run from. 17# 18 $datadir = $0; # Assume it's running from the same place that 19 # we're running from. 20 unless( $datadir =~ s@/[^/]+$@@ ) { # No path specified? 21 # See if we can find ourselves in the path. 22 foreach( split( /:/, $ENV{'PATH'} ), '.' ) { 23 # Add '.' to the path in case the user is 24 # running it with "perl makepp" even if 25 # . is not in his path. 26 if( -d "$_/Mpp" ) { # Found something we need? 27 $datadir = $_; 28 last; 29 } 30 } 31 } 32 $datadir or die "makeppreplay: can't find library files\n"; 33 34 $datadir = eval "use Cwd; cwd . '/$datadir'" 35 if $datadir =~ /^\./; # Make it absolute, if it's a relative path. 36#@@ 37 unshift @INC, $datadir; 38} 39 40 41use Mpp::Utils; 42use Mpp::Text (); 43use POSIX (); 44use Mpp; 45 46use Mpp::Event qw(wait_for); 47 48#@@useoldmodules 49#@@ 50 51our $progname; 52 53my $temporary; 54 55use Mpp::File; 56use Mpp::FileOpt; 57use Mpp::Cmds; 58use Mpp::Makefile; 59use Mpp::Rule; 60 61my @targets; 62my $target_cwd = $CWD_INFO; 63Mpp::Makefile::find_root_makefile_upwards $target_cwd; 64 # See if we find a RootMakeppfile from here, in case Perl code uses ROOT. 65my $modules = ''; 66 67my %command_line_vars; 68my $tmp; 69 70sub load_build_info_file($) { 71 my $build_info = &Mpp::File::load_build_info_file 72 or return; # No build info -- don't create it by following modification. 73 unless( exists $build_info->{SIGNATURE} ) { # Out of date build info? 74 my $sig = &Mpp::File::signature; # But file exists. 75 my $count = keys %$build_info; 76 delete @{$build_info}{qw(FROM_REPOSITORY LINKED_TO_CACHE)} unless $sig; 77 while( my( $key, $value ) = each %$build_info ) { 78 delete $build_info->{$key} # We may recalculate some of these, but not necessarily all. 79 # So make sure not to save any outdated ones with new SIGNATURE. 80 if $key ne 'DEP_SIGS' && Mpp::Signature::is_content_based $value or 81 $key eq 'LINKED_TO_CACHE' && $_[0]{LSTAT}[Mpp::File::STAT_NLINK] == 1 or 82 # Could have more than one link, but none to cache -- how can we know? 83 $key eq 'FROM_REPOSITORY' && (readlink( &absolute_filename_nolink ) || '') ne $value; 84 } 85 if( $sig ) { # But file exists. 86 &Mpp::File::mark_build_info_for_update 87 if $count != keys %$build_info; # Found a significant change? 88 $build_info->{RESCAN} ||= 1; # If we save this, let makepp double check what we signed. 89 # keep value if it was already 2 from last run. 90 $build_info->{SIGNATURE} = $sig; 91 } 92 } 93 $build_info; 94} 95 96@ARGV = '.' unless @ARGV; 97my $nothing_in_dir; 98my $found_in_dir = 0; 99perform eval { 100 while( @ARGV) { 101 Mpp::Text::getopts \%command_line_vars, 1, 102 ['c', qr/root(?:[-_]?dir(?:ectory)?)?/, \$tmp, undef, sub { 103 Mpp::Makefile::find_root_makefile_upwards $target_cwd; 104 $target_cwd = $target_cwd->{ROOT} 105 or die "$0: No RootMakeppfile(.mk) found above `", absolute_filename( $target_cwd ), "'.\n"; 106 # See if we find a RootMakeppfile from here. 107 chdir $target_cwd; # Switch to that directory. 108 }], 109 [qw(C directory), \$tmp, 1, sub { 110 $target_cwd = file_info $tmp, $target_cwd; 111 chdir $target_cwd; # Switch to that directory. 112 Mpp::Makefile::find_root_makefile_upwards $target_cwd; 113 # See if we find a RootMakeppfile from here. 114 }], 115 ['I', qr/include(?:[-_]?dir)?/, \$tmp, 1, sub { unshift @INC, absolute_filename file_info $tmp }], 116 [qw(M module), \$tmp, 1, sub { $tmp =~ s/=(.*)/ qw($1)/ and $tmp =~ tr/,/ /; $modules .= "use $tmp;" }], 117 [qw(t temporary), \$temporary], 118 119 @Mpp::common_opts; 120 121 @ARGV = '.' unless @ARGV || @targets; 122 my $finfo = file_info shift, $target_cwd; 123 if( is_dir $finfo ) { 124 Mpp::log RULE_ALL => $finfo 125 if $Mpp::log_level; 126 my $build_info_subdir = file_info relative_filename( $finfo ) . "/$Mpp::File::build_info_subdir"; 127 $nothing_in_dir = absolute_filename $finfo; 128 unshift @ARGV, grep { 129 $_ = relative_filename $_; 130 s!$Mpp::File::build_info_subdir/!! && 131 s!\.mk$!! && 132 ++$found_in_dir; 133 } Mpp::Glob::zglob_fileinfo '*.mk', $build_info_subdir; 134 next; 135 } 136 137 my $build_info = $finfo->{BUILD_INFO} = load_build_info_file $finfo; 138 if( $found_in_dir ) { # Not an explicit target? 139 $found_in_dir--; 140 next unless exists $build_info->{COMMAND}; # Not built by makepp 141 undef $nothing_in_dir; 142 } else { 143 die "$progname: No files built by makepp in `$nothing_in_dir'.\n" 144 if $nothing_in_dir; 145 die "$progname: Nothing known about `" . absolute_filename( $finfo ) . "'.\n" 146 unless defined $build_info; 147 die "$progname: File `" . absolute_filename( $finfo ) . "' not built by makepp.\n" 148 unless exists $build_info->{COMMAND}; 149 } 150 151 (my $cmd = $build_info->{COMMAND}) =~ s/\A\|FAILED\|//; 152 $cmd =~ tr/\cC/\n/; 153 (my $deps = $build_info->{SORTED_DEPS} || '') =~ tr/\cA/ /; 154 155 my $dinfo = file_info $build_info->{CWD}, $finfo->{'..'}; 156 my $makefile = $dinfo->{MAKEINFO}; 157 unless( $makefile ) { 158 $makefile = Mpp::Makefile::load $dinfo, $dinfo, \%command_line_vars, '', [], \%ENV; 159 if( $modules ) { 160 $makefile->cd; # Evaluate in the correct directory. 161 Mpp::Subs::eval_or_die $modules, $makefile, absolute_filename( $dinfo ) . ':0'; 162 } 163 } 164 $makefile->{EXPORTS} ||= {}; 165 166 my $target = relative_filename $finfo, $dinfo; 167 my $rule_cache = "$deps\01$cmd"; # Try to group targets that came from same rule. 168 if( $dinfo->{$rule_cache} ) { 169 Mpp::log RULE_EXTEND => $finfo, $dinfo->{$rule_cache}{TARGETS} 170 if $Mpp::log_level; 171 $dinfo->{$rule_cache}{TARGET_STRING} .= " $target"; 172 push @{$dinfo->{$rule_cache}{TARGETS}}, $finfo; 173 } else { 174 Mpp::log RULE_NEW => $finfo 175 if $Mpp::log_level; 176 $dinfo->{$rule_cache} = new Mpp::Rule $target, $deps, $cmd, $makefile, Mpp::File::build_info_fname( $finfo ) . ':0'; 177 push @targets, $finfo; 178 $dinfo->{$rule_cache}{TARGETS} = [$finfo]; 179 @{$dinfo->{$rule_cache}{ENV_DEPS}}{split "\cA", $build_info->{ENV_DEPS}} = split "\cA", $build_info->{ENV_VALS} 180 if exists $build_info->{ENV_DEPS}; 181 } 182 $finfo->{RULE} = $dinfo->{$rule_cache}; 183 } 184 @Mpp::common_opts = (); 185 close DATA; 186 die "$progname: No files built by makepp in `$nothing_in_dir'.\n" 187 if $nothing_in_dir; 188 189 for my $target ( @targets ) { # Should use Mpp::build et al. here! 190 my $build_info = $target->{BUILD_INFO}; 191 if( delete $build_info->{FROM_REPOSITORY} || delete $build_info->{LINKED_TO_CACHE} ) { 192 # If these survived load_build_info_file, the file is linked. 193 Mpp::log REMOVE => $target 194 if $Mpp::log_level; 195 Mpp::File::unlink $target; 196 } 197 my $rule = $target->{RULE}; 198 my $n_files = @{$rule->{TARGETS}}; 199 local $rule->{MAKEFILE}{EXPORTS} = $rule->{ENV_DEPS} 200 if exists $rule->{ENV_DEPS}; 201 if( $Mpp::dry_run ) { 202 $rule->print_command( $rule->{COMMAND_STRING} ); 203 } elsif( my $status = wait_for $rule->execute( 204 $rule->{COMMAND_STRING}, 205 $rule->{TARGETS}, 206 [map file_info( $_, $target->{'..'} ), split ' ', $rule->{DEPENDENCY_STRING}] ) ) { 207 print_error "Failed to build target", (@{$rule->{TARGETS}}>1 ? 's' : ''), 208 map( ' `'.absolute_filename( $_ )."'", @{$rule->{TARGETS}} ), " [$status]"; 209 Mpp::log RULE_FAILED => $rule 210 if $Mpp::log_level; 211 $Mpp::error_found = $status 212 if $status =~ /^signal (?:$Mpp::int_signo|$Mpp::quit_signo)$/os; 213 $Mpp::error_found ||= $status # Remember the error status. This will 214 unless $Mpp::keep_going; # cause us to stop compilation as soon as 215 # possible. 216 $rule->{TARGET_STRING} =~ s/ /' `/g; 217 $Mpp::failed_count += $n_files; 218 unless( $temporary || $build_info->{COMMAND} =~ /\A\|FAILED\|/ ) { 219 for my $tinfo ( @{$rule->{TARGETS}} ) { 220 substr $tinfo->{BUILD_INFO}{COMMAND}, 0, 0, '|FAILED|'; 221 Mpp::File::mark_build_info_for_update $tinfo; 222 } 223 &Mpp::File::update_build_infos; 224 } 225 last unless $Mpp::keep_going; 226 } else { 227 Mpp::log SUCCESS => $rule, $rule->{TARGETS} 228 if $Mpp::log_level; 229 $Mpp::n_files_changed += $n_files; 230 next if $temporary; 231 my $sig = $build_info->{SIG_METHOD_NAME}; # All targets have the same. 232 $sig &&= $rule->set_signature_class( $sig ); 233 my $dep_sigs = ''; # Precalculate this for the targets loop 234 my $sep = ''; 235 for my $dep ( split /\cA/, $build_info->{SORTED_DEPS} || '' ) { 236 $dep = path_file_info $dep, $rule->{MAKEFILE}{CWD}; 237 $dep->{BUILD_INFO} ||= load_build_info_file $dep; 238 $dep_sigs .= $sep . 239 (($sig ? $sig->signature( $dep ) : Mpp::File::signature $dep) || ''); 240 $sep = "\cA"; 241 } 242 for my $tinfo ( @{$rule->{TARGETS}} ) { 243 $build_info = $tinfo->{BUILD_INFO}; 244 delete $tinfo->{LSTAT}; 245 $build_info->{SIGNATURE} = Mpp::File::signature $tinfo; 246 247 if( $tinfo->{LINK_DEREF} && $tinfo->{LSTAT}[Mpp::File::STAT_NLINK] == 1 ) { 248 # Assume nlink > 1 to mean action only created 249 # a link to an already existing symlink. 250 $build_info->{SYMLINK} = readlink absolute_filename $tinfo; 251 } else { 252 delete $build_info->{SYMLINK}; # Unlikely to have changed, but just in case. 253 } 254 $build_info->{DEP_SIGS} = $dep_sigs; 255 $build_info->{BUILD_SIGNATURE} = $sig ? $sig->signature( $tinfo ) : $build_info->{SIGNATURE}; 256 $build_info->{RESCAN} = 2; # Let makepp double check what we built. 257 Mpp::File::mark_build_info_for_update $tinfo; 258 } 259 &Mpp::File::update_build_infos; 260 } 261 } 262}; 263 264__DATA__ 265Usage: makeppreplay [-options] [VAR=value] targets 266 267Valid options are: 268 269-A filename, --args-file=filename, --arguments-file=filename 270 Read the file and parse it as possibly quoted whitespace- and/or 271 newline-separated options. 272-C directory, --directory=directory 273 Cd to the given directory before loading the makefile and trying to 274 build the targets. 275-c, --root-dir, --root-directory 276 Cd up to the directory containing a RootMakeppfile. 277-I directory, --include=directory, --include-dir=directory 278 Add directory to Perl load path @INC. 279-?, -h, --help 280 Print out a brief summary of the options. 281-k, --keep-going 282 Build as many files as possible, even if some of them have errors. 283-M module[=arg,...], --module=module[=arg,...] 284 Load module and import any functions it exports. 285-n, --dry-run, --just-print, --recon 286 Print out commands without actually executing them. 287--no-print-directory 288 Turn off the entering or leaving directory messages. 289-t, --temporary 290 Makeppreplay modifies the build info of all files it touched and of all 291 dependencies it found modified. 292-V, --version 293 Print out the version number. 294--no-warn 295 Don't print any warning messages. 296 297Look at @htmldir@/makeppreplay.html for wrapper details, 298or at http://makepp.sourceforge.net/@BASEVERSION@/makeppreplay.html 299or type "man makeppreplay". 300