#!/usr/bin/env perl
#
# Makepp version @VERSION@.
# $Id: makepp,v 1.223 2013/04/22 07:00:37 pfeiffer Exp $
# See @htmldir@/index.html for user documentation.
#

package Mpp;

use 5.008;
use strict;

use Config;
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 makepp" even if
				# . is not in his path.
      if( -d "$_/Mpp" ) {	# Found something we need?
	$datadir = $_;
	last;
      }
    }
  }
  $datadir or die "makepp: 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};

  if( $^O =~ /^MSWin/ && $] < 5.008007 ) { # IDENTICAL AS IN t/run_tests.pl
    # This is a very bad hack!  On earlier Win Active State "lstat 'file'; lstat _ or -l _" is broken.
    my $file = "$datadir/Mpp/File.pm";
    local $_ = "$file.broken";
    unless( -f ) {		# Already converted
      rename $file, $_;
      open my $in, '<', $_;
      open my $out, '>', $file;
      chmod 07777 & (stat)[2], $file;
      while( <$in> ) {
	s/\blstat\b/stat/g;
	s/-l _/0/g;
	print $out $_;
      }
    }
  }
#@@
  unshift @INC, $datadir;
}

our $progname;

use Mpp::Text;
$Mpp::Text::pod = 'makepp_command';
BEGIN { *MAKEPP = $Mpp::Text::N[1] } # Differentiate from makeppreplay
use POSIX ();
use Mpp;


our $error_found;
our $log_level;

use Mpp::File;
use Mpp::FileOpt;
use Mpp::Makefile;
use Mpp::Rule;
use Mpp::BuildCheck::exact_match;
use Mpp::Signature::md5;
use Mpp::Event qw(when_done wait_for read_wait);

our $original_cwd = $CWD_INFO;	# Remember the directory we started from.

delete $ENV{ROOT};		# This is our builtin variable.
our $MAKEFLAGS = delete $ENV{MAKEFLAGS};
our $MAKEPPFLAGS = delete $ENV{MAKEPPFLAGS};
our %global_ENV = %ENV;		# Make a copy of the environment, because
				# we're going to change the environment a lot.
%ENV = %global_ENV;		# Some (only Solaris?) versions hang when changing $0 before %ENV
our( $assume_unchanged_dir_flag, $dont_build_dir_flag, $dont_read_dir_flag,
  $sandbox_enabled_flag, $in_sandbox_dir_flag, $sandbox_warn_flag, $virtual_sandbox_flag );
				# This is set if we set ASSUME_UNCHANGED /
				# DONT_BUILD on any directory. Optimizations
				# can be made if they aren't set.

our $dry_run;			# Display commands but don't actually
				# execute them.

our $n_phony_messages = 0;	# Number of times we've had to warn about
				# phony targets.
#
# Default settings for various command line options:
#
our $environment_override = 0;	# Variables in environment do not override
				# variables in the makefile by default.
our $implicitly_load_makefiles = 1; # Load a makefile from every directory
				# that contains a dependency or is searched
				# by a wildcard.
our $implicit_load_makeppfile_only = 0;
				# Never implicitly load makefile and Makefile.
our $force_rescan = 0;		# Don't use cached scanner results
our $final_rule_only = 0;	# Unconditionally run only the top-level rules
our $nocache_scaninfo = 0;	# Don't save scanner results
our $rm_stale_files = 0;	# Ignore stale files, removing them if
				# necessary, instead of treating them as
				# source files.
our $no_path_executable_dependencies = 0;
				# Don't add implicit dependencies on executables
				# picked up from the command search path.
our @makepp_include_path = file_info $datadir, $original_cwd;
				# The default directories to search for
				# include files supplied with makepp.
our $remake_makefiles = 1;	# Default to making the makefiles automatically.
our $gullible = 0;		# Trust actions not to touch non-targets
our $stop_on_race = 0;		# Stop if a race is detected.
our $last_chance_rules;		# Flag allowing target pattern only rules for gmake compatibility.



#   my $handle = build($objinfo);

# Starts building the specified object.  When build() returns, the object
# may not have been built yet; build() returns a handle (see Mpp/Event.pm for
# details) which you can wait on if you want to wait until the build is
# actually done.  The usual idiom is

#   wait_for build($objinfo);

# If you do this, of course, you make it harder for makepp to build targets in
# parallel because the makepp process will not spawn any other build requests
# until this one is done.

# When any targets are built, the global variable $n_files_changed is
# updated.

sub build {
  exists $_[0]{BUILD_HANDLE} and return $_[0]{BUILD_HANDLE};
				# Don't start a rebuild if we're already
				# trying to build it, or if we've tried
				# before.
  return if $error_found;	# If we're quitting because of an error, don't
				# start any new builds (but still propagate
				# errors from existing builds).
#
# Find a rule for this object.
#
  my $oinfo = $_[0];		# Name the arguments.
  if( &Mpp::File::dont_read ) {
    print_error "Not building inexistent `" . &absolute_filename . "' because it's marked for dont-read";
    return 1;
  }
  if( &Mpp::File::dont_build ) {
    if( exists $_[0]{xPHONY} || &file_exists ) {
      Mpp::log BUILD_NOT => $oinfo
	if $log_level;
    } elsif( &Mpp::File::dont_build == 2 ) { # Outside of ROOT
      print_error "Not building inexistent `" . &absolute_filename . "' because it's marked for dont-build";
      return 1;
    } else {
      warn "Not building inexistent `" . &absolute_filename . "' because it's marked for dont-build\n";
    }
    return undef $_[0]{BUILD_HANDLE};
  }
  Mpp::log TRY => $oinfo
    if $log_level;

  # NOTE: We can't unlink the target just yet (even if it's from a repository),
  # because then we couldn't use its cached scanner info. Instead, that
  # happens just before (and only if) it's built.

  my $rule = Mpp::File::get_rule( $oinfo ) || # Do we already have a rule for it?

#
# Make up a dummy rule if there is no rule.  This makes it much easier to
# handle the case where there is a target with no action but some
# dependencies, like this:
#
#  all: my_program my_other_program
#
# This is quite common in makefiles.  Another more sinister idiom is like
# this:
#
#  y.tab.c: y.tab.h
#  y.tab.h: parse.y
#      yacc -d $<
#
# This despicable idiom is the only way to tell the traditional make that the
# yacc command produces both output files.  Not only is this ugly, but if the
# rule containing the action looks up-to-date but the side-effect is not, then
# the side-effect doesn't get updated.  This isn't easy to fix, because by the
# time we build the side effect file, the action rule could have already been
# checked.
#
      new Mpp::DefaultRule($oinfo);

  # By the time we're done getting the rule, the target might have already
  # been built, so just return its handle in that case.  Otherwise we'll try
  # to build it a second time after its rule has been nuked, and that won't
  # work.
  exists $oinfo->{BUILD_HANDLE} and return $oinfo->{BUILD_HANDLE};

  # If we find that the rule for this target is in the middle of being
  # processed, then just return as if the file were already built and hope
  # for the best.  (This is currently the best we can do with circular
  # dependencies.)
  return if exists $rule->{xSCANNING};

  # If the file still looks stale (now that we've tried to load its rule), and
  # we're not treating stale files as source files, then we need to error out
  # here, because something specifically requested a stale file.
  if( $rm_stale_files && &Mpp::File::is_stale ) {
    print_error 'Not building ', $oinfo, " because it's a stale generated file";
    &Mpp::File::unlink;
    ++$Mpp::failed_count;	# Wouldn't get counted, since there's no rule
    return 1;
  }

#
# If a makefile contains $(MAKE), then we turn off implicit loading of other
# makefiles, assuming that the makefile is constructed to load them manually.
# This is a bit tricky, since the implicit loading flag must be passed in a
# global variable. This local modification will not apply, however, to the
# routines activated by when_done.
#
  local $implicitly_load_makefiles if $implicitly_load_makefiles &&
    $rule->{MAKEFILE} && exists $rule->{MAKEFILE}{xRECURSIVE_MAKE};

  undef $oinfo->{BUILD_HANDLE}; # Indicate that we're trying to build.
				# This will be replaced later by a real
				# build handle.	 This must be done after
				# get_rule because we may attempt to load a
				# makefile in get_rule, and we don't want
				# to print out a warning message about
				# encountering the rule after we already tried
				# to build the file.
  local $rule->{xSCANNING};	# Ditto for other targets of this rule that
				# we might not know about yet.

  Mpp::log USE => $rule
    if $log_level;

  # In --final-rule-only mode, ignore the dependencies and implicit targets
  # of the rule unless the target is phony.
  my $dont_scan = $final_rule_only && !UNIVERSAL::isa( $rule, 'Mpp::DefaultRule' ) &&
    !exists $oinfo->{xPHONY};
  warn 'Ignoring dependencies of ', &absolute_filename,
    " because --final-rule-only is specified\n"
    if $dont_scan;

  my( $all_targets, $all_dependencies, $command_string, $env ) =
    $rule->find_all_targets_dependencies( $oinfo, $dont_scan );
				# Find out everything that's needed for this
				# command, and everything that it changes.

  Mpp::log DEPEND => $all_targets, $all_dependencies
    if $log_level;

#
# Build each of the dependencies:
#
  my @build_handles;
  if( exists $rule->{SCAN_FAILED} ) {
    # If we couldn't even figure out what the dependencies are, then
    # propagate error status instead of running the rule. But if -k is
    # specified, then build the dependencies that we already know about.
    die unless $rule->{SCAN_FAILED};
    push @build_handles, $rule->{SCAN_FAILED};
    my $reason = '';
    if( UNIVERSAL::isa $rule->{SCAN_FAILED}, 'Mpp::File' ) {
      my $finfo = $rule->{SCAN_FAILED};
      my( $handle, $next );
      while( $handle = $finfo->{BUILD_HANDLE} and
        exists $handle->{STATUS} and
        $next = $handle->{STATUS} and
        UNIVERSAL::isa $next, 'Mpp::File'
      ) {
	$finfo = $next;
      }
      $reason = ', because ' . absolute_filename( $finfo ) . ' failed to build';
    }
    print_error 'Not building ' . &absolute_filename .
      ' because the dependencies could not be computed' . $reason;
  }
  my $handle;
  if( @$all_dependencies and $Mpp::keep_going || ! exists $rule->{SCAN_FAILED} ) {
    local $Mpp::indent_level = $Mpp::indent_level + 2;
				# Temporarily change the indentation level
				# when building dependencies.
    foreach my $dep (@$all_dependencies) {
      next if exists $dep->{BUILD_HANDLE} && !defined $dep->{BUILD_HANDLE};
				# Don't wait for what's already been done.
      $handle = when_done build( $dep ), $Mpp::Text::N[0], ERROR =>
	$rm_stale_files ?
	  sub {
	    if( grep !(exists $_->{xPHONY} || file_exists $_), @$all_dependencies ) {
	      for my $finfo ( @$all_targets ) { # Not all deps exist: target is now stale.
		Mpp::log DEL_STALE => $finfo
		  if $log_level;
		Mpp::File::unlink $finfo;
		CORE::unlink Mpp::File::build_info_fname $finfo;
	      }
	    }
	    $dep;
	  } :
	  $dep;
      $handle and push @build_handles, $handle;
				# Don't bother saving it if it's blank.
    }
  }

  $handle = when_done @build_handles, \&build_dependencies_done,
    [$oinfo, $all_targets, $all_dependencies, $command_string, $rule, $env, $dont_scan]
  and exists $handle->{STATUS} && !$handle->{STATUS} and
    undef $handle;		# If the build operation already succeeded,
				# don't keep the handle around.
  foreach my $target (@$all_targets) { # Tag all targets with the handle
    $target->{BUILD_HANDLE} = $handle; # that builds them.
  }
#
# If we're not doing a parallel make, wait for the above to finish, because
# it's very confusing if makepp gets ahead of the shell commands.
#
  $Mpp::parallel_make or
    wait_for $oinfo->{BUILD_HANDLE};

  $oinfo->{BUILD_HANDLE};
}

#
# This subroutine is called when all the dependencies for a given target
# are finished building.  We make sure the target is up to date, or if it
# isn't, we set off the build.
# Arguments:
# a) A list of all the targets.
# b) A list of all the dependencies, in sorted order.
# c) The command string to execute.
# d) The rule object to execute.
#
# Note that indentation level in the log file is handled automatically by
# the when_done mechanism.
#
sub build_dependencies_done {
  my( $oinfo, $all_targets, $all_dependencies, $command_string, $rule, $env, $dont_scan ) = @_;

#
# This routine is only called if no errors were encountered in building the
# dependencies.
#
  return if $error_found; # If we're quitting because of an error,
				# don't start any new builds.  This can happen
				# if we're doing a parallel build and we got
				# an error in a different, unrelated target.

  for my $dep ( @$all_dependencies ) {
    Mpp::File::check_for_change $dep; # Restat each dependency.  This will slow down
				# the build, but it's important for accuracy,
				# because if a file has been edited or if
				# a command altered a file without specifying
				# it as a target, we won't know about it
				# otherwise, and thus the build info that
				# we store will be wrong.  (There's still a
				# time window where this can occur, but since
				# we restat just before we execute the
				# commands, it's much narrower.)
    warn 'Target ', &absolute_filename, ' depends on temporary file ',
      absolute_filename( $dep ), "\n"
      if exists $dep->{xTEMP};
  }

#
# Now check all the signatures of each target to see if it is up
# to date.
#
  my $is_recursive_make = defined $Mpp::Recursive::command && $command_string =~ /\brecursive_makepp\b/;
				# Note that the above will catch invocations
				# of the recursive_makepp program, and also
				# invocations of makepp when
				# --traditional-recursive-make is in effect.
  my $rebuild_needed;		# Assume we don't need to rebuild.
  my $dont_build;		# Assume no files marked with --dont-build.
  my $out_of_sandbox;		# Assume no files marked with --out-of-sandbox.

  my @targets_to_get_from_repository; # If we have to get any files from a
				# repository, this is where we store them.
  my @targets_to_get_from_build_cache; # Targets to get from a build cache.

  my $build_cwd = $rule->build_cwd;
  my $build_check_method = $rule->build_check_method;
  my $sig_method = $rule->signature_method; # Get which signature checking
				# algorithm we want to use.

  if( $is_recursive_make ) {
    $rebuild_needed = 1;	# Always reexecute this command.
    Mpp::log BUILD_RECURSIVE => $all_targets
      if $log_level;
  } elsif( $dont_scan ) {
    $rebuild_needed = 1;	# Always reexecute this command.
    Mpp::log BUILD_FINAL => $all_targets
      if $log_level;
  } else {
  target_loop:
    foreach my $target (@$all_targets) {
      last if $rebuild_needed && $dont_build;
      if( Mpp::File::dont_build $target ) {
	$dont_build = $target; # Remember not to clobber dont-build targets
	next target_loop; # Skip to next target now.
      }
      unless( Mpp::File::in_sandbox $target ) {
	$out_of_sandbox = $target;
	$dont_build = $target unless $sandbox_warn_flag;
				# Remember not to overstep sandbox
      }
      next target_loop if $rebuild_needed
	or exists $target->{xTEMP};
				# Temporary targets don't have to be up-to-date
      if( exists $target->{xPHONY} ) { # If this is a phony target, then we always rebuild.
	$rebuild_needed = 1;
	Mpp::log BUILD_PHONY => $target
	  if $log_level;
	next target_loop;	# Keep checking for dont_build.
      }

#
# Sometimes when we're using repositories, we may need to execute a rule
# but the output directory does not exist yet.  If the output directory
# does not exist, but it does exist in a repository, then go ahead and
# make it.  However, if the output directory does not exist in any
# repository, then don't make it, because it's possible that the
# command or the targets are erroneous, and we don't want to scatter
# junk directories around.
#
      Mpp::File::mkdir $target->{'..'} # Make it with parents.
	if exists $target->{'..'}{xIN_REPOSITORY} && !file_exists $target->{'..'};

      if( $build_check_method->build_check( $target, $all_dependencies,
					    $command_string, $build_cwd,
					    $sig_method, $env )) { # Need to rebuild?
	if( exists $target->{ALTERNATE_VERSIONS} ) { # Possibly copy from repository?
	  my $is_src_file = UNIVERSAL::isa $rule, 'Mpp::DefaultRule';
	  for( @{$target->{ALTERNATE_VERSIONS}} ) {
	    if( $rm_stale_files && $is_src_file && Mpp::File::was_built_by_makepp $_ ) {
	      Mpp::log REP_SKIP => $_
		if $log_level;
	      next;
	    }
	    Mpp::log REP_CHECK => $_
	      if $log_level;
	    if( $is_src_file ||
               !$build_check_method->build_check_from_build_info( $_, $all_dependencies, $command_string, $build_cwd, $sig_method, $env )) {
	      push @targets_to_get_from_repository, $target, $_;
				# Remember to copy this from repository.
	      next target_loop; # Move on to next target.
	    }
	  }
	}
	if( defined $Mpp::BuildCache::used and my $build_cache = !$Mpp::BuildCache::write_only && $rule->build_cache ) {
				# Do we have a build cache we can look in?
	  my $bc_key = $build_check_method->
	    build_cache_key( $target, $all_dependencies, $command_string,
			     $build_cwd, $sig_method, $env );
	  if( $bc_key ) {	# Got a valid key?
	    my $bc_entry = $build_cache->lookup_file($bc_key);
	    if( $bc_entry ) {	# Got a candidate entry in cache?
	      Mpp::log BC_FOUND => $target, $bc_key
		if $log_level;
	      push @targets_to_get_from_build_cache, $bc_entry, $target;
	      next target_loop; # Move on to next target.
	    } else {
	      Mpp::log BC_NONE => $target, $bc_key
		if $log_level;
	    }
	  } else {
	    Mpp::log BC_NO_KEY => $target
	      if $log_level;
	  }
	}
	$rebuild_needed = 1;	# We'll need to run the command.
	next target_loop;	# Keep checking for dont_build.
      }
    }
  }

  $rebuild_needed ||= Mpp::BuildCache::get( $rule, \@targets_to_get_from_build_cache )
    if @targets_to_get_from_build_cache;
    # At this point, it appears that we won't have to run the rule.
    # However, we could have a problem when we attempt to import from
    # the cache, and because there could be concurrent processes operating
    # on the build cache, we can't predict ahead of time whether such a
    # problem will occur.  The solution is to attempt the import first,
    # and if it fails then fall back to rebuilding.

  if( $rebuild_needed ) {
    if( $dont_build ) {
      # NOTE: This can trip even if the target(s) that we need are up to date
      # if some other target *not* marked for dont-build isn't up to date.
      # This usually isn't a problem in practice, and it appears nontrivial
      # to fix.
      print_error 'Not building ' . absolute_filename( $oinfo ) .
	' because ' . absolute_filename( $dont_build ) .
	' (a target of its rule) was marked for dont-build or out-of-sandbox';
      return 1;
    }
    warn 'Building ', absolute_filename( $out_of_sandbox ), ", which is outside the sandbox\n"
      if $out_of_sandbox;
				# Do we actually need to rebuild something?
    if( $is_recursive_make ) {	# If this is a recursive make invocation,
				# we'll have to reexecute a build
				# command for it, so don't inhibit that.
      foreach my $target (@$all_targets) {
	delete $target->{BUILD_HANDLE};
      }
    }

    unless( $is_recursive_make || $dry_run ) {
      # For the targets that are about to be built, flush out any potentially
      # stale stuff (repository links and build info) from the filesystem.
      foreach my $tinfo (@$all_targets) {
        ++$tinfo->{BUILDING} if $Mpp::virtual_sandbox_flag;
	if( grep $_, Mpp::File::build_info_string $tinfo, qw'FROM_REPOSITORY LINKED_TO_CACHE' or
	    !exists $tinfo->{xPHONY} && Mpp::File::check_for_change( $tinfo ), exists $tinfo->{xEXISTS} &&
	    (my $nw = !Mpp::File::is_writable_owner $tinfo) ) { # Compiler can't write?
	  Mpp::log REMOVE => $tinfo, $nw ? 'not writable' : 'repository or build cache import'
	    if $log_level;
	  Mpp::File::unlink $tinfo; # If it's from a repository, get rid of it.
				# It might be a bogus file from the past.
				# If it's from a build cache, delete it so
				# we don't corrupt the file in the build
				# cache.
	} elsif( exists $tinfo->{BUILD_INFO}{SYMLINK} ) {
	  $tinfo->{TEMP_BUILD_INFO} = delete $tinfo->{BUILD_INFO};
	  next;
	} elsif( exists $tinfo->{TEMP_BUILD_INFO} ) {
	  next;
	}
	Mpp::File::clear_build_info $tinfo;
      }
    }
    $Mpp::critical_sections++;
    # TBD: It would be better to mark all the targets as invalid first,
    # and then restore them when the command succeeded (and ideally restore
    # the ones that weren't touched if it fails).  That way, we won't get
    # confused if makepp stops very suddenly (e.g. kernel panic), even if
    # none of the targets' timestamps changed.
    my $execute_handle = $dry_run && !$is_recursive_make ?
      $rule->print_command( $command_string ) :
      $rule->execute( $command_string, $all_targets, $all_dependencies );
				# Start executing the command, or just print it
				# if -n is enabled (unless it's recursive make).
    shift;
    my $handle = when_done $execute_handle, \&build_target_done, \@_,
      ERROR => sub {
	$error_found = $_[0]
	  if $_[0] =~ /^signal (?:$Mpp::int_signo|$Mpp::quit_signo)$/os;
	$error_found ||= $_[0]	# Remember the error status.  This will cause us
	  unless $Mpp::keep_going; # to stop compilation as soon as possible.
	$Mpp::failed_count += @$all_targets;
	print_error "Failed to build target", (@$all_targets>1 ? 's' : ''),
	  map( ' `'.absolute_filename( $_ )."'", @$all_targets ), " [$_[0]]";
	Mpp::log RULE_FAILED => $rule
	  if $log_level;	# Sometimes it's hard to tell where the problem
				# was if -k was specified unless we mark it
				# in the log file.
  # TBD: We don't need to invalidate the build info of targets that we know
  # weren't touched by the command.  We can leverage the code that detects
  # when we need to retouch a file (also TBD) to accomplish this.
	foreach my $tinfo (@$all_targets) {
	  next if exists $tinfo->{xPHONY}; # Skip phony targets.
	  may_have_changed $tinfo;	# Invalidate cached info.
 				# We don't really need to blow away BUILD_INFO,
				# because we called clear_build_info earlier,
				# but that doesn't slow things down too much.
	  my $deref = dereference $tinfo;
	  may_have_changed $deref if $tinfo != $deref;
 				# Since we don't know whether the command
				# modified the link or its referent or both,
				# need to invalidate both.
	  unless( $is_recursive_make || $dry_run || ref($rule) eq 'Mpp::DefaultRule' ) {
				# Never store build information on recursive
				# makes.  They always need to be rerun every
				# time we run through the makefile.
	    Mpp::File::set_build_info_string $tinfo,
	      BUILD_SIGNATURE => 'FAILED',
	      COMMAND => "|FAILED|$command_string",
	      CWD => relative_filename $rule->{MAKEFILE}{CWD}, $tinfo->{'..'};
	  }
	}
	&Mpp::File::update_build_infos # Flush the build info to disk.
	  unless $dry_run;
	$Mpp::critical_sections--;
	propagate_pending_signals;
	return $_[0];		# Propagate the status.
      };

    return $handle;		# Not finished building until the rule has
				# finished executing.
  }

#
# We didn't actually need to rebuild anything.	We may have to copy or link
# a target from the repository, however.
#
  Mpp::Repository::get( splice @targets_to_get_from_repository, 0, 2 )
    and return 1			   # failed
    while @targets_to_get_from_repository; # Move or link the targets.

  Mpp::log UP_TO_DATE => $all_targets
    if $log_level;

  # We need to cache the scanner info even if the action wasn't executed,
  # because the existing build info may have been generated with
  # --nocache_scaninfo. Since Mpp::File::set_build_info is intelligent, this
  # shouldn't slow things down noticeably if the scan info didn't change.
  if( UNIVERSAL::isa $build_check_method, 'Mpp::BuildCheck::exact_match' ) {
    $rule->cache_scaninfo([
      grep {
	!exists $_->{xPHONY} &&
	(Mpp::File::build_info_string $_, 'CWD' or '') eq relative_filename $build_cwd, $_->{'..'}
      } @$all_targets
    ]);
  }

  foreach my $tinfo (@$all_targets) {
    Mpp::File::set_rule $tinfo, undef;	# Discard the rule to save memory.
  }
  undef;			# Success, and nothing to wait for.
}

#
# This subroutine is called when a rule's command has finished executing.
# We don't reach this subroutine if an error occurred while the rule was
# executing.
#
# Arguments:
# a) A list of all the targets.
# b) A list of all the dependencies, in sorted order.
# c) The indentation level in the log file.
# d) The command string to execute.
# e) The rule object to execute.
#
# Note that indentation level in the log file is handled automatically by
# the when_done mechanism.
#
#
sub build_target_done {
  my( $all_targets, $all_dependencies, $command_string, $rule, $env ) = @_;
  my $implicit_phony = 0;

  # TBD: We should probably do an eval on a per-target basis, because it's nice
  # to clean up after the rest of the targets after one fails to have its build
  # info straightened out.
  eval {
    my $sig_method = $rule->signature_method; # Get which signature checking
				# algorithm we want to use.
    my $build_cwd = $rule->build_cwd;
    my $is_recursive_make = defined $Mpp::Recursive::command && $command_string =~ /\brecursive_makepp\b/;
    my $build_cache = defined $Mpp::BuildCache::used && $rule->build_cache;
    my $build_check_method = $rule->build_check_method;

    Mpp::log SUCCESS => $rule, $all_targets
      if $log_level;
#
# Update the stored build information for each target:
#
    my @built_targets;
    my $include = $rule->{INCLUDE};
    my %uniq;
    if( $include ) {
      if( $rule->{INCLUDE_MD5} ? $rule->{INCLUDE_MD5} ne Mpp::Signature::md5::signature( $include ) : 1 ) {
				# If we have a sig, only reread if it changed.
	$uniq{sprintf '%x', $_} = $_ for @$all_dependencies;
	for my $tinfo (@$all_targets) {
	  delete $uniq{sprintf '%x', $_} for $rule->expand_additional_deps( $tinfo );
				# These may now be outdated.
	}
	Mpp::log LOAD_INCL => $include, $rule
	  if $Mpp::log_level;
	local $Mpp::Makefile::rule_include = 2;
	$rule->{MAKEFILE}->read_makefile( $include );
      } else {
	undef $include;		# No change, forget about it for this time.
      }
    }

    foreach my $tinfo (@$all_targets) {
      next if exists $tinfo->{xPHONY}; # Skip phony targets.
      if( $include ) {		# Do this for all targets, as we don't know which one was in the :include .d
	$uniq{sprintf '%x', $_} = $_ for $rule->expand_additional_deps( $tinfo );
      }
      may_have_changed $tinfo;	# Invalidate cached info.
				# We don't really need to blow away BUILD_INFO,
				# because we called clear_build_info earlier,
				# but that doesn't slow things down too much.
      my $deref = dereference $tinfo;
      may_have_changed $deref if $tinfo != $deref;
				# Since we don't know whether the command modified the link
				# or its referent or both, need to invalidate both.
      push @built_targets, $tinfo;
    }
    $all_dependencies = $rule->sorted_dependencies( values %uniq )
      if keys %uniq;
    $rule->cache_scaninfo(\@built_targets);
    my $sorted_dep_names = join "\cA", map relative_filename( $_, $build_cwd ), @$all_dependencies;
    my $sorted_dep_sigs = join "\cA", map { $sig_method->signature($_) || '' } @$all_dependencies;
    foreach my $tinfo (@built_targets) {
      unless( $is_recursive_make || $dry_run ) {
				# Never store build information on recursive
				# makes.  They always need to be rerun every
				# time we run through the makefile.
	if( my $tsig = $sig_method->signature( $tinfo )) { # File actually exists now:
				# Store the information on how we built it.
	  my @extra;
	  if( %$env ) {
	    my @deps = sort keys %$env;
	    @extra = (
	      ENV_DEPS => join( "\cA", @deps ), ENV_VALS => join( "\cA", @{$env}{@deps} )
	    );
	  }
	  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.
	    Mpp::log SYMLINK => $tinfo
	      if $log_level;
	    my $link = readlink absolute_filename $tinfo;
	    my $linkee = file_info $link, $tinfo->{'..'};
	    $tinfo->{LINK_DEREF} ||= $linkee;
	    if( grep $_ == $linkee, @$all_dependencies, @$all_targets ) {
	      # Don't use dereference, as that follows a link to a link.
	      push @extra, 'SYMLINK', $link;
	    } else {
	      warn '`' . absolute_filename_nolink( $tinfo ) . "' is a symbolic link to `$link',
  which is neither a dependency of rule `" . $rule->source . "',
  nor a target.  That means makepp can't guarantee a correct build of that file.
  Your rule should in essence follow one of these two schemes:
    link: linkee
	&ln --force --resolve \$(input) \$(output)
    linkee link:
	&echo Somehow produce linkee -o \$(output)
	&ln -fr \$(outputs)\n";
	    }
	  }
	  Mpp::File::set_build_info_string $tinfo,
	    SORTED_DEPS => $sorted_dep_names,
	    DEP_SIGS => $sorted_dep_sigs,
	    BUILD_SIGNATURE => $tsig,
	    COMMAND => $command_string,
	    CWD => relative_filename( $build_cwd, $tinfo->{'..'} ),
	    ARCH => ARCHITECTURE, # Tag the build with the architecture, too.
	    @extra;
	  if( $build_cache && !$Mpp::BuildCache::read_only and # Store this file in a build cache?
	      my $bc_key = $build_check_method->
		build_cache_key( $tinfo, $all_dependencies, $command_string, $build_cwd, $sig_method, $env )) {
	    # Store the file in cache.
	    unless( $build_cache->cache_file( $tinfo, $bc_key, \my $reason )) {
	      my $msg = 'Exporting of ' . absolute_filename( $tinfo ) . " to the build cache failed because $reason\n";
	      &$Mpp::BuildCache::error_hook( $msg ) if $Mpp::BuildCache::error_hook;
	      if( $reason =~ /\(OK\)$/ && !$stop_on_race ) {
		warn $msg."This might be due to concurrent access, so we'll just skip exporting this target.\n";
	      } else {
		die $msg;
	      }
	    }
	  }
	} elsif( !exists $tinfo->{xTEMP} ) {
	  my $mfile = $rule->{MAKEFILE} || $tinfo->{'..'}{MAKEINFO};
	  if( $mfile && $mfile->expand_variable( 'makepp_require_phony', $rule->source )) {
	    $implicit_phony++;	# DefaultRule has no MAKEFILE.
	  } elsif( UNIVERSAL::isa $rule, 'Mpp::DefaultRule' ) {
	    warn 'There is no rule to build inexistent ' . absolute_filename( $tinfo ) . "\n";
	  } elsif( $n_phony_messages++ ) { # Give a shorter message on other instances.
	    warn 'Target ' . absolute_filename( $tinfo ) . " is probably also phony.\n";
	  } else {
	    warn 'I attempted to build ' . absolute_filename( $tinfo ) . ',
  but after successfully executing the commands, the target does not exist.
  Perhaps it is a phony target?	 If so, the rule defining it should be modified
  like this:
    $(phony ', relative_filename($tinfo, $build_cwd), '): dependencies
	actions

  or you could also declare it as phony like this:
    .PHONY: ', relative_filename( $tinfo, $build_cwd ), "\n";
	  }
	  undef $tinfo->{xPHONY};
	  $tinfo->{SIGNATURE} = 9e99; # Give it a very recent time to
				# force rebuilding of everything that depends
				# on it even if -m target_newer is in effect.
	}
      }
      undef $tinfo->{xASSUME_NEW} if $dry_run; # If this is a dry run, the file
				# didn't actually change, but force any command
				# that depends on it to execute.
      undef $tinfo->{BUILD_HANDLE} unless $is_recursive_make;
				# No need to keep the handle around since
				# the build was successful.  Just leave a
				# flag that we tried to build.
      Mpp::File::set_rule( $tinfo, undef ); # Discard the rule to save memory.
    }

    my $real = ref($rule) ne 'Mpp::DefaultRule';
				# Don't count targets for which there was no rule.
    for( @$all_targets ) {
      if( exists $_->{xPHONY} ) { ++$Mpp::n_phony_targets_built }
      elsif( $real ) { ++$Mpp::n_files_changed }
    }
  };
  my $error = $@;

  &Mpp::File::update_build_infos # Flush the build info to disk.
    unless $dry_run;
  if( $implicit_phony ) {	# Why must this block come before $critical_sections--?
    warn $error if $error;
    return -2;
  }
  $Mpp::critical_sections --;
  propagate_pending_signals;

  die $error if $error;
  undef;			# Return a success code.
}


our( $stop, $loop );
sub maybe_stop {
  $stop = 1 if $loop and !defined $stop;
  if( $stop ) {
    $stop = 0;			# Only stop once per loop (in case of prebuild)
    print "\nmakepp: Stopped before building anything.  Kill it; or continue with",
      -t() ? " one of:\n\n\tfg\tbg" : ":\n\n",
      "\tkill -CONT $$\n\n";
    &flush_log;
    kill STOP => $$;
  }
}



# Find .makepprc upwards and add content to @ARGV
sub makepprc {
  return if grep /^-(?:[h?V]|-help|-version)$/, @ARGV; # Avoid loading expensive things like huge repository.
  my $finfo = Mpp::Subs::f_find_first_upwards '.makepprc', { CWD => $CWD_INFO }, 'startup', \1;
  return if !$finfo or exists $finfo->{xMAKEPPRC}; # Already read?
  return unless $finfo->{LSTAT}[Mpp::File::STAT_SIZE]; # Empty file is test dir boundary.
  open my $fh, absolute_filename $finfo or die "$0: cannot open `.makepprc'--$!\n";
  local $/;
  unshift @ARGV, $finfo->{'..'} == $CWD_INFO ?
    unquote_split_on_whitespace <$fh> :
    ('-C', $finfo->{'..'}, unquote_split_on_whitespace( <$fh> ), '-C', $CWD_INFO);
  close $fh;
  undef $finfo->{xMAKEPPRC};	# Mark as read
}

#   parse_command_line(@ARGV, \%environment_hash);

# Parses and executes the given command line.  Loads whatever makefiles are
# necessary and builds the appropriate targets, or at least starts off the
# build.  (It doesn't wait until the build is finished.)  Returns a list of
# build handles.

# The environment must be provided as a reference to a hash.  Any rules which
# are executed have the environment set to this value before the shell is
# invoked.

# This parser only accepts options which are valid during recursive makes or
# from the load_makefile command.  There are other options which are handled
# by the mainline makepp code which are not accepted here.

# parse_command_line assumes that the current directory is the proper directory
# for executing the command.

my @do_build;
our @ignore_opts;
if( $ENV{MAKEPP_IGNORE_OPTS} ) {
  my( @lx, @l, @sx, @s );
  for my $opt( split ' ', $ENV{MAKEPP_IGNORE_OPTS} ) {
    if( $opt =~ /^--(.+)=/ ) {
      push @lx, $1;
    } elsif( $opt =~ /^--(.+)/ ) {
      push @l, $1;
    } elsif( $opt =~ /^-(.)./ ) {
      push @sx, $1;
    } elsif( $opt =~ /^-(.)/ ) {
      push @s, $1;
    } else {
      die "\$MAKEPP_IGNORE_OPTS: '$opt' not understood\n";
    }
  }
  my $nop;
  local $" = '';
  if( @lx || @sx ) {
    my $lx = @lx ? join '|', @lx : 'DUMMY';
    $lx = qr/$lx/;
    my $sx = @sx > 1 ? qr/[@sx]/ : $sx[0];
    push @ignore_opts, [$sx, $lx, \$nop, 1];
  }
  if( @l || @s ) {
    my $l = @l ? join '|', @l : 'DUMMY';
    $l = qr/$l/;
    my $s = @s > 1 ? qr/[@s]/ : $s[0];
    push @ignore_opts, [$s, $l, \$nop];
  }
}

sub parse_command_line(\%@) {
  local $_;			# Don't mess up caller's $_.

  my $this_ENV = shift;		# First argument is the environment hash.
  my @targets;			# Targets we need to make.
  my @initial_makefiles;
  my @makefiles;
  my @include_path = @makepp_include_path;
				# Make a copy of the include path so we can
				# add to it.

#
# Command line variable settings affect all makefiles regardless of where they
# are specified on the command line.
#
  my %command_line_vars;

  my $root_makefile;
  my $target_cwd = $CWD_INFO;
  my $tmp;
  &makepprc;
  while( @ARGV ) {
    Mpp::Text::getopts \%command_line_vars, 1,
      @ignore_opts,
      @_,
      ['c', qr/root(?:[-_]?dir(?:ectory)?)?/, \$tmp, undef, sub {
	 $tmp = Mpp::Makefile::find_root_makefile_upwards $CWD_INFO
	   or $CWD_INFO->{ROOT}
	   or die "$0: No RootMakeppfile(.mk) found above `", absolute_filename( $CWD_INFO ), "'\n";
				# See if we find a RootMakeppfile from here.
	 if( $tmp ) {
	   $root_makefile ||= $tmp; # TODO: Load multiple RootMakeppfiles for targets from multiple trees.
	   $tmp = $tmp->{'..'};
	 } else {
	   $tmp = $CWD_INFO->{ROOT};
	 }
	 chdir $tmp;		# Switch to that directory.
	 $target_cwd = $tmp;	# It's now the directory relative to which
				# further targets are specified.
	 &makepprc;
       }],
      [qw(C directory), \$tmp, 1, sub {
	 if( ref $tmp ) {	# Called via makepprc?
	   chdir $tmp;		# Switch to that directory.
	   return;
	 }
	 $tmp = file_info $tmp;
	 Mpp::File::mkdir $tmp;	# Make sure the directory exists.
	 chdir $tmp;		# Switch to that directory.
	 $root_makefile ||= Mpp::Makefile::find_root_makefile_upwards $tmp;
				# See if we find a RootMakeppfile from here.
	 $target_cwd = $tmp;	# It's now the directory relative to which
				# further targets are specified.
	 &makepprc;
       }],

      [undef, qr/dump[-_]?makep*file/, \$tmp, 1,
       sub {
	 open my $fh, '>', $tmp or die "Failed to write $tmp--$!";
	 push @Mpp::close_fhs, $fh;
	 $CWD_INFO->{DUMP_MAKEFILE} = $fh;
       }],

      ['e', qr/env(?:ironment)?[-_]?overrides?/, \$environment_override],

      ['f', qr/(?:make)?file/, \$tmp, 1, sub {
				# Load a makefile without changing directory?
	 if( $tmp eq '-' ) {	# Loading expects a real file(_info) so make one.
	   $tmp = Mpp::Subs::f_mktemp '';
	   open my $fh, '>', $tmp;
	   local $/;		# Makefiles are small enough to slurp.
	   syswrite $fh, <>;
	   close $fh;
	 }
	 push @makefiles, [file_info( $tmp ), $CWD_INFO]; # Load makefile later
				# when we can specify MAKECMDGOALS.
       }],
      [qw(F makeppfile), \$tmp, 1, sub {
	 $tmp = file_info $tmp;
	 my $mdir = $tmp;	# Assume it is actually a directory.
	 Mpp::File::is_or_will_be_dir $tmp or $mdir = $tmp->{'..'};
				# Default directory is the directory the
				# makefile is in.
	 push @makefiles, [$tmp, $mdir]; # Load the makefile later.
	 Mpp::File::mkdir( $mdir ); # Make sure the directory exists, so we
				# can cd into it.
	 $root_makefile ||= Mpp::Makefile::find_root_makefile_upwards $mdir;
				# See if we find a RootMakeppfile from here.
	 $target_cwd = $mdir;	# It's now the directory relative to which
				# further targets are specified.
       }],

      ['I', qr/include(?:[-_]?dir)?/, \$tmp, 1, sub {
				# Specify more directories for the include
				# path?
	 push @include_path, file_info $tmp;
       }],

      [undef, qr/load[-_]?make(?:pp)?file/, \$tmp, 1, sub {
	 $tmp = file_info $tmp;
	 my $mdir = $tmp;	# Assume it is actually a directory.
	 Mpp::File::is_or_will_be_dir( $tmp ) or $mdir = $tmp->{'..'};
				# Default directory is the directory the makefile is in.
	 push @initial_makefiles, [$tmp, $mdir]; # Load the makefile later.
	 Mpp::File::mkdir( $mdir ); # Make sure the directory exists, so we can cd into it.
       }],

      ['o', qr/(?:assume[-_]?old|old[-_]?file)/, \$tmp, 1, sub {
	 $tmp = file_info $tmp;
	 $tmp->{ASSUME_UNCHANGED} = 1; # Don't pay any attention to this file.
	 $assume_unchanged_dir_flag = 1 if is_or_will_be_dir $tmp;
				# Remember that we have to check parent dirs
       }],

      ['r', qr/no[-_]?builtin[-_]?rules/, \$command_line_vars{makepp_no_builtin}],
				# Pretend this was given as a variable.
      [qw(R repository), \$tmp, 1, sub { Mpp::Subs::s_repository $tmp }],

      ['W', qr/(?:what[-_]?if|assume[-_]?new|new[-_]?file)/, \$tmp, 1, sub {
	 undef file_info( $tmp )->{xASSUME_NEW}; # Pretend this file has changed.
       }],

      @Mpp::common_opts;

    delete $command_line_vars{makepp_no_builtin} unless defined $command_line_vars{makepp_no_builtin};
				# Ref above brought this into existence.

    push @targets, file_info shift @ARGV, $target_cwd
      if @ARGV;
				# Evidently it is a real target.  This is a
				# hack, because getopts has no way of handling
				# normal args immediately.  So we operate in
				# strict mode, giving the 1st non-opt/non-var,
				# at the beginning of @ARGV.  Just one extra
				# call per target...
  }
  close DATA;			# Only needed for --help.
  @_ = ();			# Free lots of memory.

  unless( defined $MAKEFLAGS ) { # 1st call
    my $flags =
      ($environment_override ? 'e' : '') .
      ($Mpp::keep_going ? 'k' : '') .
      ($dry_run ? 'n' : '') .
      ($Mpp::quiet_flag ? 's' : '') .
      ($Mpp::log_level == 1 ? 'v' : '');
    $MAKEFLAGS = join ' ',
      ($flags ? $flags : ()),
      ($Mpp::implicitly_load_makefiles ? () : '--noimplicitload'),
      ($Mpp::log_level ? () : '--nolog'),
      ($Mpp::print_directory ? () : '--noprintdirectory');
			# TODO: what about -o -W
  }

  $root_makefile ||= Mpp::Makefile::find_root_makefile_upwards( $target_cwd );
  if( $root_makefile ) {
    unshift @initial_makefiles, [$root_makefile, $root_makefile->{'..'}];
    my $build_root = $root_makefile->{'..'};

    for( @do_build ) {
      my $saw_build_root;
      $_ = $_->{'..'} unless $_ == $Mpp::File::root;
      while( !exists $_->{DONT_BUILD} ) {
	$saw_build_root++ if $_ == $build_root;
	if( $_ == $Mpp::File::root ) {
	  if( $saw_build_root ) {
	    $build_root->{DONT_BUILD} = 1;
				# Found a do_build within build tree
				# but not within a dont_build dir.
	    warn "--do-build not overriding --dont-build implies not building anything else.\n";
	    @do_build = ();
	  }
	  last;
	}
	$_ = $_->{'..'};
      }
    }
    $dont_build_dir_flag = 1;
    exists $build_root->{DONT_BUILD} or
      undef $build_root->{DONT_BUILD};
    exists $Mpp::File::root->{DONT_BUILD} or
      $Mpp::File::root->{DONT_BUILD} = 2;
				# If we have a root, don't build anything
				# outside our build tree unless specifically
				# requested.  Use 2 to mean: implicitly set.
  }

  my $makecmdgoals = join(' ', map relative_filename( $_ ), @targets);
				# Format MAKECMDGOALS.

  if(@initial_makefiles) {
    my $cwd = $CWD_INFO;
    foreach (@initial_makefiles) {
      Mpp::Makefile::load($_->[0], $_->[1], \%command_line_vars, $makecmdgoals,
			\@include_path, $this_ENV); # Load the initial makefiles
    }
    chdir $cwd;
  }

#
# At this point we have a list of target fileinfo structures in @targets.
# Now load makefiles.  The procedure for finding a makefile is:
# 1) Use any explicitly specified on the command line.
# 2) If none, try to find one in the current directory.
# 3) If none and no RootMakeppfile, try all the directories containing targets.
#
  {
    last if @makefiles || $CWD_INFO->{MAKEINFO}; # last leaves this block

    my $finfo = Mpp::Makefile::find_makefile_in( $CWD_INFO );
				# Look in the current directory.

    $finfo and @makefiles = [$finfo, $CWD_INFO] and last;

    $root_makefile and last;

    for( @targets ) {
      $finfo = Mpp::Makefile::find_makefile_in( $_->{'..'} ) and
				# Did we find one in this directory?
	push @makefiles, [$finfo, $finfo->{'..'}];
    }

    @makefiles or @makefiles = [$CWD_INFO, $CWD_INFO];
				# Load the default makefile if we still haven't found one.
  }
  $Mpp::Recursive::depth =	# Avoid warning.
    $Mpp::Recursive::depth = $command_line_vars{recursive_makepp}
    if defined $Mpp::Recursive::traditional || defined $Mpp::Recursive::hybrid;

  foreach (@makefiles) {
    $_ = Mpp::Makefile::load($_->[0], $_->[1], \%command_line_vars, $makecmdgoals,
			\@include_path, $this_ENV); # Load all the makefiles, and store
				# the makeinfo structure back in @makefiles.
  }

  @targets or			# No targets specified?
    @targets = ($target_cwd->{MAKEINFO} || $makefiles[0] || $CWD_INFO)->{FIRST_TARGET} ||
				# Use the first one in the first makefile.  If
				# we had no makefile in cwd but a
				# RootMakeppfile, @makefiles is empty.  That's
				# why we fall back to $CWD_INFO, just so we
				# don't reference undef.
    die 'no targets specified and no default target in makefile `' .
      absolute_filename( ($target_cwd->{MAKEINFO} || $makefiles[0])->{MAKEFILE} || $CWD_INFO ) .
      "'\n";
  @targets;
}

###############################################################################
#
# Parse the top level command.	This is at the bottom of the file to force
# all the miscellaneous initialization stuff scattered in with the routines
# to be executed.
#

my @other_args;			# Arguments that are valid at places other
				# than the top level.

if( $MAKEPPFLAGS || $MAKEFLAGS ) {
  unshift @ARGV, unquote_split_on_whitespace $MAKEPPFLAGS || $MAKEFLAGS;
				# See if we're passed flags from a parent process.
  $ARGV[0] =~ /^-/ || $ARGV[0] =~ /=/ or substr $ARGV[0], 0, 0, '-';
				# If we're actually called from make, the first
				# argument might not have a minus sign.
				# However, don't put a minus sign in if it's
				# a variable assignment.
}
undef $MAKEFLAGS;		# 1st round of parsing shall set standard values.

unshift @ARGV, unquote_split_on_whitespace $ENV{_MAKEPPFLAGS}
  if $ENV{_MAKEPPFLAGS};

perform eval {
				# Now parse the command line, and handle the
				# targets.  Do fast exit without freeing all
				# variables.  Cleaning up the Mpp::File tree
				# can take a long time and there's no need to
				# do it.  (This saves 8% of the run time on a
				# Mpp::File tree with > 10^5 files, so it's not
				# a trivial savings.)
  my $tmp;
  parse_command_line %global_ENV,
    ['b', qr/build[-_]?cache/, \$tmp, 1, sub {
       Mpp::Subs::s_build_cache $tmp, undef, $progname, {global => 1};
     }],
    [undef, qr/build[-_]?check(?:[-_]?method)?/, \$tmp, 1, sub {
       Mpp::Subs::s_build_check $tmp, undef, $progname, {global => 1};
     }],

    [undef, qr/do[-_]?build/, \$tmp, 1, sub {
       $tmp = file_info $tmp;
       undef $tmp->{DONT_BUILD};
       push @do_build, $tmp;
     }],
    [undef, qr/do[-_]?read/, \$tmp, 1, sub {
       undef file_info( $tmp )->{DONT_READ};
     }],
    [undef, qr/dont[-_]?build/, \$tmp, 1, sub {
       $tmp = file_info $tmp;
       $tmp->{DONT_BUILD} = 1;
       $dont_build_dir_flag = 1 if is_or_will_be_dir $tmp;
				# Remember that we have to check parent dirs
     }],
    [undef, qr/dont[-_]?read/, \$tmp, 1, sub {
       $tmp = file_info $tmp;
       $tmp->{DONT_READ} = 1;
       $dont_read_dir_flag = 1 if is_or_will_be_dir $tmp;
				# Remember that we have to check parent dirs
     }],

    [undef, qr/final[-_]?rules?[-_]?only/, \$final_rule_only],
    [undef, qr/force[-_]?copy[-_]?from[-_]?bc/, eval '\$Mpp::BuildCache::force_copy'],
    [undef, qr/force[-_]?rescan/, \$force_rescan],

    [undef, 'gullible', \$gullible],

    [undef, qr/hybrid(?:[-_]?recurs(?:ion|ive|ive[-_]?make))?/, \$Mpp::Recursive::hybrid],

    [undef, qr/implicit[-_]load[-_]?[Mm]akeppfile[-_]?only/, \$implicit_load_makeppfile_only],
    [undef, qr/(?:in(?:side)?[-_]?)?sand[-_]?box/, \$tmp, 1, sub {
       $tmp = file_info $tmp;
       $tmp->{IN_SANDBOX} = 1;
       $sandbox_enabled_flag = 1; # default is now out-of-sandbox
       $in_sandbox_dir_flag = 1 if is_or_will_be_dir $tmp;
				# Remember that we have to check parent dirs
     }],

    [qw(j jobs), \$Mpp::Event::max_proc, 1, sub {
       $Mpp::Event::max_proc =~ /^\d+$/ or die "$progname: invalid argument to -j\n";
       if( is_windows > 0 ) {
	 $Mpp::Event::max_proc = 1;
	 warn "parallel make (-j) only supported on Cygwin or MinGW Perl\n";
	 return;
       }
       if( $Mpp::Event::max_proc != 1 ) { # More than one process?
	 $Mpp::parallel_make = 1;
				# Remember if we're doing a parallel make.
				# It's not sufficient to test $max_proc,
				# because if we are doing a recursive make,
				# we have to increment max_proc, and we don't
				# want to switch into parallel make mode.
	 require File::Copy;	# Needed for reemitting collected output.
				# Do it once here, so as to not recheck each time.
       }
     }],

    [undef, qr/last[-_]?chance[-_]?rules/, \$last_chance_rules],

    ['m', qr/signature(?:[-_]?method)?/, \$tmp, 1, sub {
       Mpp::Subs::s_signature $tmp, undef, $progname, {global => 1};
     }],

    [undef, qr/override[-_]?signature(?:[-_]?method)?/, \$tmp, 1, sub {
       Mpp::Subs::s_signature $tmp, undef, $progname, {global => 1, override => 1};
     }],

    [undef, qr/md5[-_]?(?:check[-_]?)?bc/, eval '\$Mpp::BuildCache::md5check'],

    [undef, qr/no[-_]?cache[-_]?scaninfos?/, \$nocache_scaninfo],
    [undef, qr/no[-_]?implicit[-_]?load/, \$implicitly_load_makefiles, undef, 0],
    [undef, qr/no[-_]?path[-_]?exe(?:cutable)?[-_]?dep(?:s|endency|endencies)?/, \$no_path_executable_dependencies],
    [undef, qr/no[-_]?populate[-_]?bc/, \$Mpp::BuildCache::read_only],
				# don't populate bc with targets, just read from it
    [undef, qr/no[-_]?remake[-_]?makefiles/, \$remake_makefiles, undef, 0],
				# Don't remake makefiles automatically?

    [undef, qr/out[-_]?of[-_]?sand[-_]?box/, \$tmp, 1, sub {
       $tmp = file_info $tmp;
       undef $tmp->{IN_SANDBOX};
       $in_sandbox_dir_flag = 1 if is_or_will_be_dir $tmp;
				# Remember that we have to check parent dirs
     }],

    [undef, qr/populate[-_]?bc[-_]?only/, \$Mpp::BuildCache::write_only],

    [undef, qr/re?m(?:ove)?[-_]?stale(?:[-_]?files)?/, \$rm_stale_files],

    [undef, qr/sand[-_]?box[-_]?warn(?:ing)?/, \$sandbox_warn_flag],
    [undef, qr/loop|stop(?:[-_]?before[-_]?build(?:ing)?|[-_]?after[-_]?load(?:ing|ed)?)?/, \$loop],
    [undef, qr/stop[-_]?(?:on[-_]?)?race/, \$stop_on_race],
    [undef, qr/symlink[-_]?in[-_]?rep(?:os(?:itory)?)?[-_]?as[-_]?file/, eval '\$Mpp::Repository::symlink_as_file'],

    [undef, qr/traditional(?:[-_]?recurs(?:ion|ive|ive[-_]?make))?/, \$Mpp::Recursive::traditional],

    [undef, qr/virtual[-_]?sandbox/, \$virtual_sandbox_flag];
};

__DATA__
[option ...] [VAR=value ...] [target ...]

Valid options are:
