make(1): fix assertion failure in -j mode with .END node

There had been two separate global variables for the .END node, and in
parallel mode, only the one in jobs.c was initialized.

The code in JobRun heads over to Compat_Make without calling Compat_Run
first, which left the variable ENDNode uninitialized.
This commit is contained in:
rillig 2020-09-23 03:06:38 +00:00
parent cf9ee22cf9
commit 84ce7c67b4
10 changed files with 108 additions and 43 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.925 2020/09/22 01:09:33 kamil Exp $
# $NetBSD: mi,v 1.926 2020/09/23 03:06:38 rillig Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -4670,6 +4670,8 @@
./usr/tests/usr.bin/make/unit-tests/deptgt-default.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-delete_on_error.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-delete_on_error.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-end-jobs.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-end-jobs.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-end.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-end.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-error.exp tests-usr.bin-tests compattestfile,atf

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.148 2020/09/22 20:19:46 rillig Exp $ */
/* $NetBSD: compat.c,v 1.149 2020/09/23 03:06:38 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -99,10 +99,9 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.148 2020/09/22 20:19:46 rillig Exp $");
MAKE_RCSID("$NetBSD: compat.c,v 1.149 2020/09/23 03:06:38 rillig Exp $");
static GNode *curTarg = NULL;
static GNode *ENDNode;
static void CompatInterrupt(int);
static pid_t compatChild;
static int compatSigno;
@ -226,10 +225,12 @@ Compat_RunCommand(char *cmdp, struct GNode *gn)
cmd = cmdStart;
LstNode_Set(cmdNode, cmdStart);
if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
assert(ENDNode != NULL);
Lst_Append(ENDNode->commands, cmdStart);
return 0;
if (gn->type & OP_SAVE_CMDS) {
GNode *endNode = Targ_GetEndNode();
if (gn != endNode) {
Lst_Append(endNode->commands, cmdStart);
return 0;
}
}
if (strcmp(cmdStart, "...") == 0) {
gn->type |= OP_SAVE_CMDS;
@ -682,8 +683,10 @@ Compat_Run(GNodeList *targs)
bmake_signal(SIGQUIT, CompatInterrupt);
}
ENDNode = Targ_FindNode(".END", TARG_CREATE);
ENDNode->type = OP_SPECIAL;
/* Create the .END node now, to keep the (debug) output of the
* counter.mk test the same as before 2020-09-23. This implementation
* detail probably doesn't matter though. */
(void)Targ_GetEndNode();
/*
* If the user has defined a .BEGIN target, execute the commands attached
* to it.
@ -732,7 +735,9 @@ Compat_Run(GNodeList *targs)
* If the user has defined a .END target, run its commands.
*/
if (errors == 0) {
Compat_Make(ENDNode, ENDNode);
GNode *endNode = Targ_GetEndNode();
Compat_Make(endNode, endNode);
/* XXX: Did you mean endNode->made instead of gn->made? */
if (gn->made == ERROR) {
PrintOnError(gn, "\nStop.");
exit(1);

View File

@ -1,4 +1,4 @@
/* $NetBSD: job.c,v 1.234 2020/09/22 20:19:46 rillig Exp $ */
/* $NetBSD: job.c,v 1.235 2020/09/23 03:06:38 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -140,7 +140,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: job.c,v 1.234 2020/09/22 20:19:46 rillig Exp $");
MAKE_RCSID("$NetBSD: job.c,v 1.235 2020/09/23 03:06:38 rillig Exp $");
# define STATIC static
@ -165,14 +165,6 @@ int jobTokensRunning = 0;
*/
#define FILENO(a) ((unsigned) fileno(a))
/*
* post-make command processing. The node postCommands is really just the
* .END target but we keep it around to avoid having to search for it
* all the time.
*/
static GNode *postCommands = NULL;
/* node containing commands to execute when
* everything else is done */
static int numCommands; /* The number of commands actually printed
* for a target. Should this number be
* 0, no shell will be executed. */
@ -907,7 +899,7 @@ JobSaveCommand(void *cmd, void *gn)
char *expanded_cmd;
(void)Var_Subst(cmd, (GNode *)gn, VARE_WANTRES, &expanded_cmd);
/* TODO: handle errors */
Lst_Append(postCommands->commands, expanded_cmd);
Lst_Append(Targ_GetEndNode()->commands, expanded_cmd);
return 0;
}
@ -2301,7 +2293,9 @@ Job_Init(void)
#undef ADDSIG
(void)Job_RunTarget(".BEGIN", NULL);
postCommands = Targ_FindNode(".END", TARG_CREATE);
/* Create the .END node now, even though no code in the unit tests
* depends on it. See also Targ_GetEndNode in Compat_Run. */
(void)Targ_GetEndNode();
}
static void JobSigReset(void)
@ -2601,29 +2595,20 @@ JobInterrupt(int runINTERRUPT, int signo)
exit(signo);
}
/*
*-----------------------------------------------------------------------
* Job_Finish --
* Do final processing such as the running of the commands
* attached to the .END target.
/* Do the final processing, i.e. run the commands attached to the .END target.
*
* Results:
* Number of errors reported.
*
* Side Effects:
* None.
*-----------------------------------------------------------------------
*/
int
Job_Finish(void)
{
if (postCommands != NULL &&
(!Lst_IsEmpty(postCommands->commands) ||
!Lst_IsEmpty(postCommands->children))) {
GNode *endNode = Targ_GetEndNode();
if (!Lst_IsEmpty(endNode->commands) || !Lst_IsEmpty(endNode->children)) {
if (errors) {
Error("Errors reported so .END ignored");
} else {
JobRun(postCommands);
JobRun(endNode);
}
}
return errors;

View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.144 2020/09/22 04:05:41 rillig Exp $ */
/* $NetBSD: make.h,v 1.145 2020/09/23 03:06:38 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -251,7 +251,9 @@ typedef enum {
/* Target has all the commands it should. Used when parsing to catch
* multiple commands for a target. */
OP_HAS_COMMANDS = 1 << 27,
/* Saving commands on .END (Compat) */
/* The special command "..." has been seen. All further commands from
* this node will be saved on the .END node instead, to be executed at
* the very end. */
OP_SAVE_CMDS = 1 << 26,
/* Already processed by Suff_FindDeps */
OP_DEPS_FOUND = 1 << 25,

View File

@ -1,4 +1,4 @@
/* $NetBSD: nonints.h,v 1.126 2020/09/22 20:19:46 rillig Exp $ */
/* $NetBSD: nonints.h,v 1.127 2020/09/23 03:06:38 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@ -174,6 +174,7 @@ void Targ_Stats(void);
GNodeList *Targ_List(void);
GNode *Targ_NewGN(const char *);
GNode *Targ_FindNode(const char *, int);
GNode *Targ_GetEndNode(void);
GNodeList *Targ_FindList(StringList *, int);
Boolean Targ_Ignore(GNode *);
Boolean Targ_Silent(GNode *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: targ.c,v 1.89 2020/09/22 04:05:41 rillig Exp $ */
/* $NetBSD: targ.c,v 1.90 2020/09/23 03:06:38 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -122,7 +122,7 @@
#include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: targ.c,v 1.89 2020/09/22 04:05:41 rillig Exp $");
MAKE_RCSID("$NetBSD: targ.c,v 1.90 2020/09/23 03:06:38 rillig Exp $");
static GNodeList *allTargets; /* the list of all targets found so far */
#ifdef CLEANUP
@ -283,6 +283,19 @@ Targ_FindNode(const char *name, int flags)
return gn;
}
/* Return the .END node, which contains the commands to be executed when
* everything else is done. */
GNode *Targ_GetEndNode(void)
{
/* Save the node locally to avoid having to search for it all the time. */
static GNode *endNode = NULL;
if (endNode == NULL) {
endNode = Targ_FindNode(".END", TARG_CREATE);
endNode->type = OP_SPECIAL;
}
return endNode;
}
/* Make a complete list of GNodes from the given list of names.
* If flags is TARG_CREATE, nodes will be created for all names in
* names which do not yet have graph nodes. If flags is TARG_NOCREATE,

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.145 2020/09/21 04:20:35 rillig Exp $
# $NetBSD: Makefile,v 1.146 2020/09/23 03:06:38 rillig Exp $
#
# Unit tests for make(1)
#
@ -102,6 +102,7 @@ TESTS+= deptgt-begin
TESTS+= deptgt-default
TESTS+= deptgt-delete_on_error
TESTS+= deptgt-end
TESTS+= deptgt-end-jobs
TESTS+= deptgt-error
TESTS+= deptgt-ignore
TESTS+= deptgt-interrupt

View File

@ -0,0 +1,8 @@
: .BEGIN '${VAR}'
--- all ---
: all '${VAR}'
: .END '${VAR}'
: .END '${VAR}' deferred
: .BEGIN 'Should not be expanded.' deferred
: all 'Should not be expanded.' deferred
exit status 0

View File

@ -0,0 +1,46 @@
# $NetBSD: deptgt-end-jobs.mk,v 1.1 2020/09/23 03:06:38 rillig Exp $
#
# Tests for the special target .END in dependency declarations,
# which is run after making the desired targets.
#
# This test is very similar to deptgt-end.mk, except for the -j option.
# This option enables parallel mode, in which the code from job.c partially
# replaces the code from compat.c.
#
# Before 2020-08-22, this test crashed with a null pointer dereference.
# Before 2020-09-23, this test crashed with an assertion failure.
.MAKEFLAGS: -j 8
VAR= Should not be expanded.
.BEGIN:
: $@ '$${VAR}'
...
: $@ '$${VAR}' deferred
# Oops: The deferred command must not be expanded twice.
# The Var_Subst in Compat_RunCommand looks suspicious.
# The Var_Subst in JobSaveCommand looks suspicious.
.END:
: $@ '$${VAR}'
...
: $@ '$${VAR}' deferred
all:
: $@ '$${VAR}'
...
: $@ '$${VAR}' deferred
# Oops: The deferred command must not be expanded twice.
# The Var_Subst in Compat_RunCommand looks suspicious.
# The Var_Subst in JobSaveCommand looks suspicious.
# The deferred commands are run in the order '.END .BEGIN all'.
# This may be unexpected at first since the natural order would be
# '.BEGIN all .END', but it is implemented correctly.
#
# At the point where the commands of a node with deferred commands are run,
# the deferred commands are appended to the commands of the .END node.
# This happens in Compat_RunCommand, and to prevent an endless loop, the
# deferred commands of the .END node itself are not appended to itself.
# Instead, the deferred commands of the .END node are run as if they were
# immediate commands.

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt-end.mk,v 1.4 2020/09/22 19:41:09 rillig Exp $
# $NetBSD: deptgt-end.mk,v 1.5 2020/09/23 03:06:38 rillig Exp $
#
# Tests for the special target .END in dependency declarations,
# which is run after making the desired targets.
@ -11,6 +11,7 @@ VAR= Should not be expanded.
: $@ '$${VAR}' deferred
# Oops: The deferred command must not be expanded twice.
# The Var_Subst in Compat_RunCommand looks suspicious.
# The Var_Subst in JobSaveCommand looks suspicious.
.END:
: $@ '$${VAR}'
@ -23,6 +24,7 @@ all:
: $@ '$${VAR}' deferred
# Oops: The deferred command must not be expanded twice.
# The Var_Subst in Compat_RunCommand looks suspicious.
# The Var_Subst in JobSaveCommand looks suspicious.
# The deferred commands are run in the order '.END .BEGIN all'.
# This may be unexpected at first since the natural order would be