#                                                                    -*-perl-*-

use warnings;

my $description = "Test appending to variables of various origins.";

# sv 64822, sv 36486.
# Test various combinations of appends in the presence and absence of command
# line definitions, env overrides and makefile overrides.
# Test the following statements.
# A target or pattern specific definition or append is available at build time
# only.
# A definition with a makefile override can only be appended to with another
# override.
# A definition with a makefile override can only be redefined with another
# override.
# A target or pattern specific definition takes precedence over a global
# definition for that target.
# A global variable is immune to a target or pattern specific definition or
# append.
# A target or pattern specific definition is immune to another target or pattern
# specific definition or append.
# A prerequisite inherits a target or pattern specific value from its target.
# "phobos" inherits the value of "hello" from "mars".

my @goals = ('phobos', 'mars');
my @specificities = ('', 's: ', '%: ');
my @inits = ('', 'hello=file', 'hello:=file', 'override hello=file');
my @global_append = ('', 'hello+=red');
my @blue_override = ('', 'override ');
my @yellow_override = ('', 'override ');
my @cmdline = ('', 'hello=cmd', 'hello:=cmd hello+=cmd2');
my @env_ovr = ('', '-e');
my @envs = ('', 'env');

for my $goal (@goals) {
 for my $spec (@specificities) {
  for my $init (@inits) {
   for my $ga (@global_append) {
    for my $bovr (@blue_override) {
     for my $yovr (@yellow_override) {
      for my $dashe (@env_ovr) {
       for my $env (@envs) {
        for my $cmd (@cmdline) {

        if ($env) {
            $ENV{'hello'} = 'env';
        } else {
            delete $ENV{'hello'};
        }

        my $parse_time_answer = '';
        my $build_time_answer = '';

        # For goal "phobos" target is "", "phobos: " and "phobo%: ".
        # For goal "mars" target is "", "mars: " and "mar%: ".
        my $target = '';
        if ($spec) {
            $target = $goal;
            chop($target);
            $target .= $spec;
        }

        if ($init =~ 'override') {
            $build_time_answer = 'file';
        } elsif ($cmd) {
            $build_time_answer = $cmd =~ 'cmd2' ? 'cmd cmd2' : 'cmd';
        } elsif ($dashe and $env) {
            $build_time_answer = 'env';
        } elsif ($init and !$target and $ga) {
            $build_time_answer = 'file red';
        } elsif ($init) {
            $build_time_answer = 'file';
        } elsif ($env and $ga) {
            $build_time_answer = 'env red';
        } elsif ($env) {
            $build_time_answer = 'env';
        } elsif ($ga) {
            $build_time_answer = 'red';
        }

        if ($bovr and $yovr) {
            $build_time_answer .= ' ' if ($build_time_answer);
            $build_time_answer .= 'blue yellow';
        } elsif ($bovr) {
            $build_time_answer .= ' ' if ($build_time_answer);
            $build_time_answer .= 'blue';
        } elsif ($yovr and !($init =~ 'override') and !$cmd and !($dashe and $env)) {
            $build_time_answer .= ' ' if ($build_time_answer);
            $build_time_answer .= 'blue yellow';
        } elsif ($yovr) {
            $build_time_answer .= ' ' if ($build_time_answer);
            $build_time_answer .= 'yellow';
        } elsif ($init =~ 'override') {
        } elsif ($cmd) {
        } elsif ($dashe and $env) {
        } else {
            $build_time_answer .= ' ' if ($build_time_answer);
            $build_time_answer .= 'blue yellow';
        }


        if ($cmd and $target) {
            $parse_time_answer = $cmd =~ 'cmd2' ? 'cmd cmd2' : 'cmd';
        } elsif ($env and !$dashe and $target and $ga) {
            $parse_time_answer = 'env red';
        } elsif ($env and $target) {
            $parse_time_answer = 'env';
        } elsif ($target and $ga) {
            $parse_time_answer = 'red';
        } elsif ($target) {
            $parse_time_answer = '';
        } else {
            $parse_time_answer = $build_time_answer;
        }

        my $i = $init ? "$target$init" : '';

        # moon and satur% specific settings test that target and pattern
        # settings specific to one target do not affect another target.

        my $answer = $goal eq "mars" ?
            "$parse_time_answer\n$build_time_answer\n" # From parse time and from "phobos" recipe.
          . "$build_time_answer\n#MAKE#: 'mars' is up to date.\n" :  # From "mars" recipe.
            "$parse_time_answer\n$build_time_answer\n#MAKE#: 'phobos' is up to date.\n";


        run_make_test("
# goal = $goal
# init = $init
# target = $target
# ga = $ga
# bovr = $bovr
# yovr = $yovr
# cmd = $cmd
# env = $env
# dashe = $dashe

moon: hello=1
moon: override hello+=2

$i
$ga
$target${bovr}hello+=blue
$target${yovr}hello+=yellow

satur%: override hello:=3
satur%: hello+=4

\$(info \$(hello))
phobos:; \$(info \$(hello))
mars: phobos; \$(info \$(hello))
saturn:; \$(info \$(hello))
", "$dashe $cmd $goal", "$answer");
        }
       }
      }
     }
    }
   }
  }
 }
}

# Preferably, we would want to run the tests below for all the combinations,
# generated by the loop above. However, that causes the test to take a lot of
# time.

# The fix for sv 64822 went to recursively_expand_for_file.
# There are three code paths that lead to recursively_expand_for_file.
#  - export of a variable to target environment.
#  - expansion of a substitution reference.
#  - other expansions of a variable that was appended to.
# Test target env, substitution reference in parse and build modes.;
# Also test a mix of pattern and target specific definitions and appends.

run_make_test(q!
al%: hello=file
al%: hello+=one
all: hello+=two
$(info $(hello))
all:; $(info $(hello))
!, "", "\nfile one two\n#MAKE#: 'all' is up to date.\n");

run_make_test(q!
hello=file
al%: hello+=one
all: hello+=two
$(info $(hello:X=Y))
all:; $(info $(hello:X=Y))
!, "", "file\nfile one two\n#MAKE#: 'all' is up to date.\n");

run_make_test(q!
hello=file
al%: hello+=one
all: hello+=two
export hello
$(info $(shell echo $$hello))
all:; $(info $(shell echo $$hello))
!, "", "file\nfile one two\n#MAKE#: 'all' is up to date.\n");

# "phobos" inherits the value of "hello" from "mars". On top of that there are
# also "phobos" specific appends.
for my $goal (@goals) {
my $answer = $goal eq "mars" ?
    "\nminit mone mtwo pone ptwo\n" # From parse time and from "phobos" recipe.
  . "minit mone mtwo\n#MAKE#: 'mars' is up to date.\n" :  # From "mars" recipe.
    "\npone ptwo\n#MAKE#: 'phobos' is up to date.\n"; # From parse time and from "phobos" recipe.

run_make_test("
mar%: hello=minit
mar%: hello+=mone
mars: hello+=mtwo
phobo%: hello+=pone
phobos: hello+=ptwo
\$(info \$(hello))
phobos:; \$(info \$(hello))
mars: phobos; \$(info \$(hello))
", "$goal", "$answer");
}

# This test is similar to the one above. The difference is that here there is a
# pattern-specific definition of "hello" that matches "phobos".
run_make_test(q!
mar%: hello:=minit
mar%: hello+=mone
mars: hello+=mtwo
phobo%: hello:=pinit
phobo%: hello+=pone
phobos: hello+=ptwo
$(info $(hello))
phobos:; $(info $(hello))
mars: phobos; $(info $(hello))
!, 'phobos', "\npinit pone ptwo\n#MAKE#: 'phobos' is up to date.\n");

# Test pattern and target specific appends to a global variable that has origin override.
# sv 36486.
my @ops = ('=', '+=', ':=');
@inits = ('', 'override ', 'al%: override ');
@specificities = ('', 'all: ', 'al%: ');
for my $init (@inits) {
 for my $spec (@specificities) {
  for my $op (@ops) {

  my $build_time_answer = '';
  if ($init =~ ':' and $op eq '+=' and !$spec) {
      # This is the case where global variable obtains value 'one two three' at
      # parse time and later at build time a pattern or target specific
      # 'hello+=file' appends 'file'.
      # e.g.
      # al%: override hello+=file
      # hello+=one
      # hello+=two
      # hello+=three
      $build_time_answer = 'one two three file';
  } elsif ($init =~ 'override') {
      $build_time_answer = 'file';
  } else {
      $build_time_answer = 'file one two three';
  }

  my $parse_time_answer = $init =~ ':' ? '' : 'file';
  if (!$spec and ($init ne 'override ')) {
      $parse_time_answer .= ' ' if $parse_time_answer;
      $parse_time_answer .= 'one two three';
  }

  run_make_test("
${init}hello${op}file
${spec}hello+=one
${spec}hello+=two
${spec}hello+=three
\$(info \$(hello))
all:; \$(info \$(hello))
", "", "$parse_time_answer\n$build_time_answer\n#MAKE#: 'all' is up to date.\n");
  }
 }
}

1;
