[SV 63856] Implement .WAIT on the command line

* src/main.c (handle_non_switch_argument): Return 1 if arg is .WAIT.
(decode_switches): Set wait_here for a goal that follows .WAIT.
* src/remake.c (update_goal_chain): Honor wait_here for a command
line goal.  Don't allow double-colon targets to continue if .WAIT is
given for one of them.
* tests/scripts/targets/WAIT: Add .WAIT tests.
This commit is contained in:
Dmitry Goncharov 2023-04-02 10:50:17 -04:00 committed by Paul Smith
parent 54b3202f8d
commit cd46baab90
3 changed files with 168 additions and 18 deletions

View File

@ -2975,15 +2975,17 @@ init_switches (void)
}
/* Non-option argument. It might be a variable definition. */
static void
/* Non-option argument. It might be a variable definition.
Returns 1 if the argument we read was .WAIT, else 0.
*/
static unsigned int
handle_non_switch_argument (const char *arg, enum variable_origin origin)
{
struct variable *v;
if (arg[0] == '-' && arg[1] == '\0')
/* Ignore plain '-' for compatibility. */
return;
return 0;
#if MK_OS_VMS
{
@ -3036,7 +3038,12 @@ handle_non_switch_argument (const char *arg, enum variable_origin origin)
Enter it as a file and add it to the dep chain of goals.
Check ARG[0] because if the top makefile resets MAKEOVERRIDES
then ARG points to an empty string in the submake. */
struct file *f = enter_file (strcache_add (expand_command_line_file (arg)));
struct file *f;
if (strcmp (arg, ".WAIT") == 0)
return 1;
f = enter_file (strcache_add (expand_command_line_file (arg)));
f->cmd_target = 1;
if (goals == 0)
@ -3077,6 +3084,7 @@ handle_non_switch_argument (const char *arg, enum variable_origin origin)
define_variable_cname ("MAKECMDGOALS", value, o_default, 0);
}
}
return 0;
}
/* Called if the makefile resets the MAKEFLAGS variable. */
@ -3098,6 +3106,7 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
struct command_switch *cs;
struct stringlist *sl;
int c;
unsigned int found_wait = 0;
/* getopt does most of the parsing for us.
First, get its vectors set up. */
@ -3120,15 +3129,22 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
if (c == EOF)
/* End of arguments, or "--" marker seen. */
break;
else if (c == 1)
/* An argument not starting with a dash. */
handle_non_switch_argument (coptarg, origin);
else if (c == '?')
/* Bad option. We will print a usage message and die later.
But continue to parse the other options so the user can
see all he did wrong. */
bad = 1;
else if (c == 1)
{
/* An argument not starting with a dash. */
const unsigned int prior_found_wait = found_wait;
found_wait = handle_non_switch_argument (coptarg, origin);
if (prior_found_wait && lastgoal)
/* If the argument before this was .WAIT, wait here. */
lastgoal->wait_here = 1;
}
else
/* An option starting with a dash. */
for (cs = switches; cs->c != '\0'; ++cs)
if (cs->c == c)
{
@ -3320,7 +3336,12 @@ decode_switches (int argc, const char **argv, enum variable_origin origin)
to be returned in order, this only happens when there is a "--"
argument to prevent later arguments from being options. */
while (optind < argc)
handle_non_switch_argument (argv[optind++], origin);
{
const int prior_found_wait = found_wait;
found_wait = handle_non_switch_argument (argv[optind++], origin);
if (prior_found_wait && lastgoal)
lastgoal->wait_here = 1;
}
if (bad && origin == o_command)
print_usage (bad);

View File

@ -119,6 +119,7 @@ update_goal_chain (struct goaldep *goaldeps)
unsigned long last_cmd_count = 0;
int t = touch_flag, q = question_flag, n = just_print_flag;
enum update_status status = us_none;
const unsigned int depth = rebuilding_makefiles ? 1 : 0;
/* Duplicate the chain so we can remove things from it. */
struct dep *goals_orig = copy_dep_chain ((struct dep *)goaldeps);
@ -137,6 +138,7 @@ update_goal_chain (struct goaldep *goaldeps)
while (goals != 0)
{
struct dep *gu, *g, *lastgoal;
int running = 0, wait = 0;
/* Start jobs that are waiting for the load to go down. */
@ -154,16 +156,14 @@ update_goal_chain (struct goaldep *goaldeps)
while (gu != 0)
{
/* Iterate over all double-colon entries for this file. */
struct file *file;
struct file *file, *dchead;
int stop = 0, any_not_updated = 0;
g = gu->shuf ? gu->shuf : gu;
goal_dep = g;
for (file = g->file->double_colon ? g->file->double_colon : g->file;
file != NULL;
file = file->prev)
dchead = g->file->double_colon ? g->file->double_colon : g->file;
for (file = dchead; file != NULL; file = file->prev)
{
unsigned int ocommands_started;
enum update_status fail;
@ -188,8 +188,24 @@ update_goal_chain (struct goaldep *goaldeps)
actually run. */
ocommands_started = commands_started;
fail = update_file (file, rebuilding_makefiles ? 1 : 0);
stop = 0;
/* In the case of double colon rules, only the recipe of the 1st
rule should be blocked by .WAIT. The recipes of all subsequent
rules for the same file will execute sequentially in order
after the 1st. */
wait = file == dchead && g->wait_here && running;
if (wait)
{
DBF (DB_VERBOSE, _(".WAIT is blocking '%s'.\n"));
break;
}
fail = update_file (file, depth);
check_renamed (file);
running |= (file->command_state == cs_running
|| file->command_state == cs_deps_running);
/* Set the goal's 'changed' flag if any commands were started
by calling update_file above. We check this flag below to
@ -197,7 +213,6 @@ update_goal_chain (struct goaldep *goaldeps)
if (commands_started > ocommands_started)
g->changed = 1;
stop = 0;
if ((fail || file->updated) && status < us_question)
{
/* We updated this goal. Update STATUS and decide whether
@ -249,6 +264,9 @@ update_goal_chain (struct goaldep *goaldeps)
/* Reset FILE since it is null at the end of the loop. */
file = g->file;
if (wait)
break;
if (stop || !any_not_updated)
{
/* If we have found nothing whatever to do for the goal,
@ -285,8 +303,9 @@ update_goal_chain (struct goaldep *goaldeps)
}
/* If we reached the end of the dependency graph update CONSIDERED
for the next pass. */
if (gu == 0)
for the next pass. In the case of waiting, increment CONSIDERED to
prevent the same file from getting pruned over and over again. */
if (gu == 0 || wait)
++considered;
}
@ -384,7 +403,8 @@ update_file (struct file *file, unsigned int depth)
if (f->command_state == cs_running
|| f->command_state == cs_deps_running)
/* Don't run other :: rules for this target until
this rule is finished. */
this rule is finished. Multiple recipes running in parallel and
updating the same target will corrupt the target. */
return us_success;
if (new > status)

View File

@ -56,6 +56,73 @@ pre2: ; @#HELPER# -q out $@
run_make_test(undef, '-j10 pre2', "pre2\n");
# sv 63856.
# .WAIT on the command line.
run_make_test(q!
pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
pre2: ; @#HELPER# -q out $@
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10 pre1 .WAIT pre2', "start-pre1\nend-pre1\npre2\n");
# Multiple consecutive .WAITs.
run_make_test(q!
all : pre1 .WAIT .WAIT .WAIT pre2
pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
pre2: ; @#HELPER# -q out $@
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10', "start-pre1\nend-pre1\npre2\n");
# First and last prerequsites are .WAIT.
run_make_test(q!
all : .WAIT pre1 .WAIT pre2 .WAIT
pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
pre2: ; @#HELPER# -q out $@
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10', "start-pre1\nend-pre1\npre2\n");
# All prerequisites are .WAITs.
run_make_test(q!
all : .WAIT .WAIT .WAIT
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10', "#MAKE#: Nothing to be done for 'all'.\n");
run_make_test(q!
all:
!,
'-j10 .WAIT', "#MAKE#: Nothing to be done for 'all'.\n");
# Wait between the duplicate goals.
run_make_test(q!
all: hello.tsk .WAIT hello.tsk
hello.tsk:; $(info $@)
!,
'-j10', "hello.tsk\n#MAKE#: Nothing to be done for 'all'.\n");
# Wait between the duplicate command line goals.
run_make_test(q!
hello.tsk:; $(info $@)
!,
'-j10 hello.tsk .WAIT hello.tsk', "hello.tsk\n#MAKE#: 'hello.tsk' is up to date.\n#MAKE#: 'hello.tsk' is up to date.\n");
# Ensure .WAIT doesn't wait between all targets
run_make_test(q!
@ -71,6 +138,20 @@ pre3: ; @#HELPER# -q wait TWO out $@ file THREE
unlink(qw(TWO THREE));
# Ensure .WAIT on the command line doesn't wait between all targets.
run_make_test(q!
pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
pre2: ; @#HELPER# -q out start-$@ file TWO wait THREE out end-$@
pre3: ; @#HELPER# -q wait TWO out $@ file THREE
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10 pre1 .WAIT pre2 pre3', "start-pre1\nend-pre1\nstart-pre2\npre3\nend-pre2\n");
unlink(qw(TWO THREE));
# Ensure .WAIT waits for ALL targets on the left before ANY targets on the right
run_make_test(q!
@ -88,6 +169,23 @@ post2: ; @#HELPER# -q file POST2 wait POST1 out $@
unlink(qw(PRE1 PRE2 POST1 POST2));
# Ensure .WAIT on the command line waits for ALL targets on the left before ANY
# targets on the right.
run_make_test(q!
pre1: ; @#HELPER# -q out start-$@ file PRE1 wait PRE2 sleep 1 out end-$@
pre2: ; @#HELPER# -q wait PRE1 out $@ file PRE2
post1: ; @#HELPER# -q wait POST2 out $@ file POST1
post2: ; @#HELPER# -q file POST2 wait POST1 out $@
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10 pre1 pre2 .WAIT post1 post2', "start-pre1\npre2\nend-pre1\npost1\npost2\n");
unlink(qw(PRE1 PRE2 POST1 POST2));
# See if .WAIT takes effect between different lists of prereqs
# In the current implementation, .WAIT waits only between two prerequisites
# in a given target. These same two targets might be run in a different
@ -180,6 +278,17 @@ pre2: ; @#HELPER# -q out $@
!,
'-j10 --shuffle=reverse', "start-pre1\nend-pre1\npre2\n");
# Ensure we don't shuffle if .WAIT is set on the command line.
run_make_test(q!
pre1: ; @#HELPER# -q out start-$@ sleep 1 out end-$@
pre2: ; @#HELPER# -q out $@
# This is just here so we don't fail with older versions of make
.WAIT:
!,
'-j10 --shuffle=reverse pre1 .WAIT pre2', "start-pre1\nend-pre1\npre2\n");
# Warn about invalid .WAIT definitions
run_make_test(q!