| # -*-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; |