Merge bmake-20210206

Changes of interest

  o unit-tests: use private TMPDIR to avoid errors from other users
  o avoid strdup in mkTempFile
  o always use vfork
  o job.c: do not create empty shell files in jobs mode
    reduce unnecessary calls to waitpid
  o cond.c: fix debug output for comparison operators in conditionals
This commit is contained in:
Simon J. Gerraty 2021-02-10 22:01:59 -08:00
commit dba7b0ef92
130 changed files with 3231 additions and 2159 deletions

View File

@ -1,3 +1,76 @@
2021-02-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210206
Merge with NetBSD make, pick up
o unit-tests: use private TMPDIR to avoid errors from other users
2021-02-05 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210205
Merge with NetBSD make, pick up
o avoid strdup in mkTempFile
o always use vfork
o rename context and ctxt to scope
o rename some VAR constants to SCOPE
o Var_ functions, move the scope to the front
o use shortcut functions Global_Set and Global_Append
o add shortcut Global_Delete for deleting a global variable
o rename Var_Delete to Var_DeleteExpand, Var_DeleteVar to Var_Delete
o compat.c: when exiting due to an error, print graph information
o enum.c: remove overengineered Enum_ValueToString
o make.c: remove unused INTERNAL flag
remove unused return type of MakeBuildParent
o parse.c: replace parse error "Need an operator" with better message
o var.c: improve documentation about variable scopes
rename Var_ValueDirect to GNode_ValueDirect
rename old Var_SetWithFlags to Var_SetExpandWithFlags
merge SetVar into Var_SetWithFlags
split Var_Exists into plain Var_Exists and Var_ExistsExpand
split Var_Append into Var_Append and Var_AppendExpand
replace enum bit-set with bit-field
o unit-tests/var-op-shell: use kill rather than kill -14
which broke on darwin with recent update.
2021-02-01 Simon J Gerraty <sjg@beast.crufty.net>
* configure.in: check for sig_atomic_t and define it as 'int'
if missing.
* VERSION (_MAKE_VERSION): 20210201
Merge with NetBSD make, pick up
o use sig_atomic_t for caught_sigchld
2021-01-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210130
Merge with NetBSD make, pick up
o more unit tests
o convert SearchPath to struct
o split Buf_Destroy into Buf_Done and Buf_DoneData
o for.c: split For_Eval into separate functions
rename struct For to struct ForLoop
o job.c: do not create empty shell files in jobs mode
rename JobOpenTmpFile to JobWriteShellCommands
reduce unnecessary calls to waitpid
o parse.c: in -dp mode, print stack trace with each diagnostic
2021-01-23 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210123
Merge with NetBSD make, pick up
o rename Dir_Expand to SearchPath_Expand
o rename Dir_AddDir, reorder parameters of SearchPath_ToFlags
o cond.c: fix debug output for comparison operators in conditionals
o dir.c: split Dir_FindFile into separate functions
2021-01-20 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210120
Merge with NetBSD make, pick up
o fix some more lint nits
o refine some unit tests for portability
o cond.c: rework parsing
2021-01-10 Simon J Gerraty <sjg@beast.crufty.net> 2021-01-10 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210110 * VERSION (_MAKE_VERSION): 20210110

View File

@ -83,6 +83,8 @@ unit-tests/cmd-errors.exp
unit-tests/cmd-errors.mk unit-tests/cmd-errors.mk
unit-tests/cmd-interrupt.exp unit-tests/cmd-interrupt.exp
unit-tests/cmd-interrupt.mk unit-tests/cmd-interrupt.mk
unit-tests/cmdline-redirect-stdin.exp
unit-tests/cmdline-redirect-stdin.mk
unit-tests/cmdline-undefined.exp unit-tests/cmdline-undefined.exp
unit-tests/cmdline-undefined.mk unit-tests/cmdline-undefined.mk
unit-tests/cmdline.exp unit-tests/cmdline.exp
@ -397,6 +399,8 @@ unit-tests/job-flags.exp
unit-tests/job-flags.mk unit-tests/job-flags.mk
unit-tests/job-output-long-lines.exp unit-tests/job-output-long-lines.exp
unit-tests/job-output-long-lines.mk unit-tests/job-output-long-lines.mk
unit-tests/jobs-empty-commands.exp
unit-tests/jobs-empty-commands.mk
unit-tests/jobs-error-indirect.exp unit-tests/jobs-error-indirect.exp
unit-tests/jobs-error-indirect.mk unit-tests/jobs-error-indirect.mk
unit-tests/jobs-error-nested-make.exp unit-tests/jobs-error-nested-make.exp
@ -501,6 +505,8 @@ unit-tests/opt-no-action-at-all.exp
unit-tests/opt-no-action-at-all.mk unit-tests/opt-no-action-at-all.mk
unit-tests/opt-no-action-runflags.exp unit-tests/opt-no-action-runflags.exp
unit-tests/opt-no-action-runflags.mk unit-tests/opt-no-action-runflags.mk
unit-tests/opt-no-action-touch.exp
unit-tests/opt-no-action-touch.mk
unit-tests/opt-no-action.exp unit-tests/opt-no-action.exp
unit-tests/opt-no-action.mk unit-tests/opt-no-action.mk
unit-tests/opt-query.exp unit-tests/opt-query.exp

View File

@ -1,2 +1,2 @@
# keep this compatible with sh and make # keep this compatible with sh and make
_MAKE_VERSION=20210110 _MAKE_VERSION=20210206

View File

@ -1,4 +1,4 @@
/* $NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $ */ /* $NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -147,7 +147,7 @@ struct ar_hdr {
#include "dir.h" #include "dir.h"
/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ /* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $"); MAKE_RCSID("$NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $");
typedef struct List ArchList; typedef struct List ArchList;
typedef struct ListNode ArchListNode; typedef struct ListNode ArchListNode;
@ -225,14 +225,14 @@ ArchFree(void *ap)
* Input: * Input:
* pp The start of the specification. * pp The start of the specification.
* gns The list on which to place the nodes. * gns The list on which to place the nodes.
* ctxt The context in which to expand variables. * scope The scope in which to expand variables.
* *
* Output: * Output:
* return TRUE if it was a valid specification. * return TRUE if it was a valid specification.
* *pp Points to the first non-space after the archive spec. * *pp Points to the first non-space after the archive spec.
*/ */
Boolean Boolean
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
{ {
char *cp; /* Pointer into line */ char *cp; /* Pointer into line */
GNode *gn; /* New node */ GNode *gn; /* New node */
@ -255,7 +255,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
Boolean isError; Boolean isError;
/* XXX: is expanded twice: once here and once below */ /* XXX: is expanded twice: once here and once below */
(void)Var_Parse(&nested_p, ctxt, (void)Var_Parse(&nested_p, scope,
VARE_WANTRES | VARE_UNDEFERR, &result); VARE_WANTRES | VARE_UNDEFERR, &result);
/* TODO: handle errors */ /* TODO: handle errors */
isError = result.str == var_Error; isError = result.str == var_Error;
@ -272,7 +272,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
*cp++ = '\0'; *cp++ = '\0';
if (expandLibName) { if (expandLibName) {
char *expanded; char *expanded;
(void)Var_Subst(libName.str, ctxt, (void)Var_Subst(libName.str, scope,
VARE_WANTRES | VARE_UNDEFERR, &expanded); VARE_WANTRES | VARE_UNDEFERR, &expanded);
/* TODO: handle errors */ /* TODO: handle errors */
libName = MFStr_InitOwn(expanded); libName = MFStr_InitOwn(expanded);
@ -298,7 +298,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
Boolean isError; Boolean isError;
const char *nested_p = cp; const char *nested_p = cp;
(void)Var_Parse(&nested_p, ctxt, (void)Var_Parse(&nested_p, scope,
VARE_WANTRES | VARE_UNDEFERR, VARE_WANTRES | VARE_UNDEFERR,
&result); &result);
/* TODO: handle errors */ /* TODO: handle errors */
@ -355,7 +355,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
char *p; char *p;
char *unexpandedMemName = memName; char *unexpandedMemName = memName;
(void)Var_Subst(memName, ctxt, (void)Var_Subst(memName, scope,
VARE_WANTRES | VARE_UNDEFERR, VARE_WANTRES | VARE_UNDEFERR,
&memName); &memName);
/* TODO: handle errors */ /* TODO: handle errors */
@ -379,7 +379,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
gn->type |= OP_ARCHV; gn->type |= OP_ARCHV;
Lst_Append(gns, gn); Lst_Append(gns, gn);
} else if (!Arch_ParseArchive(&p, gns, ctxt)) { } else if (!Arch_ParseArchive(&p, gns, scope)) {
/* Error in nested call. */ /* Error in nested call. */
free(fullName); free(fullName);
/* XXX: does unexpandedMemName leak? */ /* XXX: does unexpandedMemName leak? */
@ -390,7 +390,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
} else if (Dir_HasWildcards(memName)) { } else if (Dir_HasWildcards(memName)) {
StringList members = LST_INIT; StringList members = LST_INIT;
Dir_Expand(memName, &dirSearchPath, &members); SearchPath_Expand(&dirSearchPath, memName, &members);
while (!Lst_IsEmpty(&members)) { while (!Lst_IsEmpty(&members)) {
char *member = Lst_Dequeue(&members); char *member = Lst_Dequeue(&members);
@ -1022,9 +1022,9 @@ Arch_FindLib(GNode *gn, SearchPath *path)
free(libName); free(libName);
#ifdef LIBRARIES #ifdef LIBRARIES
Var_Set(TARGET, gn->name, gn); Var_Set(gn, TARGET, gn->name);
#else #else
Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); Var_Set(gn, TARGET, GNode_Path(gn));
#endif #endif
} }

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $ .\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -99,7 +99,7 @@ is equivalent to
.It Fl D Ar variable .It Fl D Ar variable
Define Define
.Ar variable .Ar variable
to be 1, in the global context. to be 1, in the global scope.
.It Fl d Ar [-]flags .It Fl d Ar [-]flags
Turn on debugging, and specify which portions of Turn on debugging, and specify which portions of
.Nm .Nm
@ -355,7 +355,7 @@ Do not build any targets.
Multiple instances of this option may be specified; Multiple instances of this option may be specified;
the variables will be printed one per line, the variables will be printed one per line,
with a blank line for each null or undefined variable. with a blank line for each null or undefined variable.
The value printed is extracted from the global context after all The value printed is extracted from the global scope after all
makefiles have been read. makefiles have been read.
By default, the raw variable contents (which may By default, the raw variable contents (which may
include additional unexpanded variable references) are shown. include additional unexpanded variable references) are shown.

View File

@ -37,7 +37,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
-C /etc. -C /etc.
-D variable -D variable
Define variable to be 1, in the global context. Define variable to be 1, in the global scope.
-d [-]flags -d [-]flags
Turn on debugging, and specify which portions of bmake are to Turn on debugging, and specify which portions of bmake are to
@ -224,7 +224,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
instances of this option may be specified; the variables will be instances of this option may be specified; the variables will be
printed one per line, with a blank line for each null or unde- printed one per line, with a blank line for each null or unde-
fined variable. The value printed is extracted from the global fined variable. The value printed is extracted from the global
context after all makefiles have been read. By default, the raw scope after all makefiles have been read. By default, the raw
variable contents (which may include additional unexpanded vari- variable contents (which may include additional unexpanded vari-
able references) are shown. If variable contains a `$' then the able references) are shown. If variable contains a `$' then the
value will be recursively expanded to its complete resultant text value will be recursively expanded to its complete resultant text

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -75,7 +75,7 @@
#include "make.h" #include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $"); MAKE_RCSID("$NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $");
/* Make space in the buffer for adding at least 16 more bytes. */ /* Make space in the buffer for adding at least 16 more bytes. */
void void
@ -92,7 +92,7 @@ Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
size_t old_len = buf->len; size_t old_len = buf->len;
char *end; char *end;
if (__predict_false(old_len + bytes_len >= buf->cap)) { if (old_len + bytes_len >= buf->cap) {
size_t minIncr = bytes_len + 16; size_t minIncr = bytes_len + 16;
buf->cap += buf->cap > minIncr ? buf->cap : minIncr; buf->cap += buf->cap > minIncr ? buf->cap : minIncr;
buf->data = bmake_realloc(buf->data, buf->cap); buf->data = bmake_realloc(buf->data, buf->cap);
@ -135,21 +135,6 @@ Buf_AddInt(Buffer *buf, int n)
Buf_AddBytes(buf, str, len); Buf_AddBytes(buf, str, len);
} }
/*
* Get the data (usually a string) from the buffer.
* The returned data is valid until the next modifying operation
* on the buffer.
*
* Returns the data and optionally the length of the data.
*/
char *
Buf_GetAll(Buffer *buf, size_t *out_len)
{
if (out_len != NULL)
*out_len = buf->len;
return buf->data;
}
/* Mark the buffer as empty, so it can be filled with data again. */ /* Mark the buffer as empty, so it can be filled with data again. */
void void
Buf_Empty(Buffer *buf) Buf_Empty(Buffer *buf)
@ -175,22 +160,35 @@ Buf_Init(Buffer *buf)
} }
/* /*
* Reset the buffer. * Free the data from the buffer.
* If freeData is TRUE, the data from the buffer is freed as well. * Leave the buffer itself in an indeterminate state.
* Otherwise it is kept and returned.
*/ */
char * void
Buf_Destroy(Buffer *buf, Boolean freeData) Buf_Done(Buffer *buf)
{ {
char *data = buf->data; free(buf->data);
if (freeData) {
free(data);
data = NULL;
}
#ifdef CLEANUP
buf->cap = 0; buf->cap = 0;
buf->len = 0; buf->len = 0;
buf->data = NULL; buf->data = NULL;
#endif
}
/*
* Return the data from the buffer.
* Leave the buffer itself in an indeterminate state.
*/
char *
Buf_DoneData(Buffer *buf)
{
char *data = buf->data;
#ifdef CLEANUP
buf->cap = 0;
buf->len = 0;
buf->data = NULL;
#endif
return data; return data;
} }
@ -200,22 +198,23 @@ Buf_Destroy(Buffer *buf, Boolean freeData)
#endif #endif
/* /*
* Reset the buffer and return its data. * Return the data from the buffer.
* Leave the buffer itself in an indeterminate state.
* *
* If the buffer size is much greater than its content, * If the buffer size is much greater than its content,
* a new buffer will be allocated and the old one freed. * a new buffer will be allocated and the old one freed.
*/ */
char * char *
Buf_DestroyCompact(Buffer *buf) Buf_DoneDataCompact(Buffer *buf)
{ {
#if BUF_COMPACT_LIMIT > 0 #if BUF_COMPACT_LIMIT > 0
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) { if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
/* We trust realloc to be smart */ /* We trust realloc to be smart */
char *data = bmake_realloc(buf->data, buf->len + 1); char *data = bmake_realloc(buf->data, buf->len + 1);
data[buf->len] = '\0'; /* XXX: unnecessary */ data[buf->len] = '\0'; /* XXX: unnecessary */
Buf_Destroy(buf, FALSE); Buf_DoneData(buf);
return data; return data;
} }
#endif #endif
return Buf_Destroy(buf, FALSE); return Buf_DoneData(buf);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.h,v 1.38 2020/12/28 15:42:53 rillig Exp $ */ /* $NetBSD: buf.h,v 1.42 2021/01/30 21:25:10 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -81,16 +81,11 @@
/* An automatically growing null-terminated buffer of characters. */ /* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer { typedef struct Buffer {
size_t cap; /* Allocated size of the buffer, including the null */ size_t cap; /* Allocated size of the buffer, including the '\0' */
size_t len; /* Number of bytes in buffer, excluding the null */ size_t len; /* Number of bytes in buffer, excluding the '\0' */
char *data; /* The buffer itself (always null-terminated) */ char *data; /* The buffer itself (always null-terminated) */
} Buffer; } Buffer;
/* If we aren't on NetBSD, __predict_false() might not be defined. */
#ifndef __predict_false
#define __predict_false(x) (x)
#endif
void Buf_Expand(Buffer *); void Buf_Expand(Buffer *);
/* Buf_AddByte adds a single byte to a buffer. */ /* Buf_AddByte adds a single byte to a buffer. */
@ -99,19 +94,13 @@ Buf_AddByte(Buffer *buf, char byte)
{ {
size_t old_len = buf->len++; size_t old_len = buf->len++;
char *end; char *end;
if (__predict_false(old_len + 1 >= buf->cap)) if (old_len + 1 >= buf->cap)
Buf_Expand(buf); Buf_Expand(buf);
end = buf->data + old_len; end = buf->data + old_len;
end[0] = byte; end[0] = byte;
end[1] = '\0'; end[1] = '\0';
} }
MAKE_INLINE size_t
Buf_Len(const Buffer *buf)
{
return buf->len;
}
MAKE_INLINE Boolean MAKE_INLINE Boolean
Buf_EndsWith(const Buffer *buf, char ch) Buf_EndsWith(const Buffer *buf, char ch)
{ {
@ -122,11 +111,11 @@ void Buf_AddBytes(Buffer *, const char *, size_t);
void Buf_AddBytesBetween(Buffer *, const char *, const char *); void Buf_AddBytesBetween(Buffer *, const char *, const char *);
void Buf_AddStr(Buffer *, const char *); void Buf_AddStr(Buffer *, const char *);
void Buf_AddInt(Buffer *, int); void Buf_AddInt(Buffer *, int);
char *Buf_GetAll(Buffer *, size_t *);
void Buf_Empty(Buffer *); void Buf_Empty(Buffer *);
void Buf_Init(Buffer *); void Buf_Init(Buffer *);
void Buf_InitSize(Buffer *, size_t); void Buf_InitSize(Buffer *, size_t);
char *Buf_Destroy(Buffer *, Boolean); void Buf_Done(Buffer *);
char *Buf_DestroyCompact(Buffer *); char *Buf_DoneData(Buffer *);
char *Buf_DoneDataCompact(Buffer *);
#endif /* MAKE_BUF_H */ #endif /* MAKE_BUF_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,7 +69,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/*- /*
* compat.c -- * compat.c --
* The routines in this file implement the full-compatibility * The routines in this file implement the full-compatibility
* mode of PMake. Most of the special functionality of PMake * mode of PMake. Most of the special functionality of PMake
@ -99,7 +99,7 @@
#include "pathnames.h" #include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $");
static GNode *curTarg = NULL; static GNode *curTarg = NULL;
static pid_t compatChild; static pid_t compatChild;
@ -260,7 +260,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
/* /*
* Append the expanded command, to prevent the * Append the expanded command, to prevent the
* local variables from being interpreted in the * local variables from being interpreted in the
* context of the .END node. * scope of the .END node.
* *
* A probably unintended side effect of this is that * A probably unintended side effect of this is that
* the expanded command will be expanded again in the * the expanded command will be expanded again in the
@ -360,7 +360,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
/* /*
* Fork and execute the single command. If the fork fails, we abort. * Fork and execute the single command. If the fork fails, we abort.
*/ */
compatChild = cpid = vFork(); compatChild = cpid = vfork();
if (cpid < 0) { if (cpid < 0) {
Fatal("Could not fork"); Fatal("Could not fork");
} }
@ -512,7 +512,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
} }
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
Var_Set(IMPSRC, GNode_VarTarget(gn), pgn); Var_Set(pgn, IMPSRC, GNode_VarTarget(gn));
/* /*
* All the children were made ok. Now youngestChild->mtime contains the * All the children were made ok. Now youngestChild->mtime contains the
@ -605,7 +605,7 @@ MakeOther(GNode *gn, GNode *pgn)
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) { if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) {
const char *target = GNode_VarTarget(gn); const char *target = GNode_VarTarget(gn);
Var_Set(IMPSRC, target != NULL ? target : "", pgn); Var_Set(pgn, IMPSRC, target != NULL ? target : "");
} }
switch (gn->made) { switch (gn->made) {
@ -752,6 +752,10 @@ Compat_Run(GNodeList *targs)
} }
if (errorNode != NULL) { if (errorNode != NULL) {
if (DEBUG(GRAPH2))
Targ_PrintGraph(2);
else if (DEBUG(GRAPH3))
Targ_PrintGraph(3);
PrintOnError(errorNode, "\nStop."); PrintOnError(errorNode, "\nStop.");
exit(1); exit(1);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -95,53 +95,65 @@
#include "dir.h" #include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $");
/* /*
* The parsing of conditional expressions is based on this grammar: * The parsing of conditional expressions is based on this grammar:
* E -> F || E * Or -> And '||' Or
* E -> F * Or -> And
* F -> T && F * And -> Term '&&' And
* F -> T * And -> Term
* T -> defined(variable) * Term -> Function '(' Argument ')'
* T -> make(target) * Term -> Leaf Operator Leaf
* T -> exists(file) * Term -> Leaf
* T -> empty(varspec) * Term -> '(' Or ')'
* T -> target(name) * Term -> '!' Term
* T -> commands(name) * Leaf -> "string"
* T -> symbol * Leaf -> Number
* T -> $(varspec) op value * Leaf -> VariableExpression
* T -> $(varspec) == "string" * Leaf -> Symbol
* T -> $(varspec) != "string" * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
* T -> "string"
* T -> ( E )
* T -> ! T
* op -> == | != | > | < | >= | <=
* *
* 'symbol' is some other symbol to which the default function is applied. * 'Symbol' is an unquoted string literal to which the default function is
* applied.
* *
* The tokens are scanned by CondToken, which returns: * The tokens are scanned by CondToken, which returns:
* TOK_AND for '&' or '&&' * TOK_AND for '&&'
* TOK_OR for '|' or '||' * TOK_OR for '||'
* TOK_NOT for '!' * TOK_NOT for '!'
* TOK_LPAREN for '(' * TOK_LPAREN for '('
* TOK_RPAREN for ')' * TOK_RPAREN for ')'
*
* Other terminal symbols are evaluated using either the default function or * Other terminal symbols are evaluated using either the default function or
* the function given in the terminal, they return either TOK_TRUE or * the function given in the terminal, they return either TOK_TRUE or
* TOK_FALSE. * TOK_FALSE.
*
* TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons.
*
* All non-terminal functions (CondParser_Expr, CondParser_Factor and
* CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error.
*/ */
typedef enum Token { typedef enum Token {
TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
} Token; } Token;
typedef enum CondResult {
CR_FALSE, CR_TRUE, CR_ERROR
} CondResult;
typedef enum ComparisonOp {
LT, LE, GT, GE, EQ, NE
} ComparisonOp;
typedef struct CondParser { typedef struct CondParser {
const struct If *if_info; /* Info for current statement */
/*
* The plain '.if ${VAR}' evaluates to true if the value of the
* expression has length > 0. The other '.if' variants delegate
* to evalBare instead.
*/
Boolean plain;
/* The function to apply on unquoted bare words. */
Boolean (*evalBare)(size_t, const char *);
Boolean negateEvalBare;
const char *p; /* The remaining condition to parse */ const char *p; /* The remaining condition to parse */
Token curr; /* Single push-back token used in parsing */ Token curr; /* Single push-back token used in parsing */
@ -154,11 +166,13 @@ typedef struct CondParser {
Boolean printedError; Boolean printedError;
} CondParser; } CondParser;
static Token CondParser_Expr(CondParser *par, Boolean); static CondResult CondParser_Or(CondParser *par, Boolean);
static unsigned int cond_depth = 0; /* current .if nesting level */ static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */ static unsigned int cond_min_depth = 0; /* depth at makefile open */
static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
/* /*
* Indicate when we should be strict about lhs of comparisons. * Indicate when we should be strict about lhs of comparisons.
* In strict mode, the lhs must be a variable expression or a string literal * In strict mode, the lhs must be a variable expression or a string literal
@ -214,7 +228,7 @@ CondParser_SkipWhitespace(CondParser *par)
* Return the length of the argument, or 0 on error. * Return the length of the argument, or 0 on error.
*/ */
static size_t static size_t
ParseFuncArg(const char **pp, Boolean doEval, const char *func, ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
char **out_arg) char **out_arg)
{ {
const char *p = *pp; const char *p = *pp;
@ -254,7 +268,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
? VARE_WANTRES | VARE_UNDEFERR ? VARE_WANTRES | VARE_UNDEFERR
: VARE_NONE; : VARE_NONE;
FStr nestedVal; FStr nestedVal;
(void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal); (void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal);
/* TODO: handle errors */ /* TODO: handle errors */
Buf_AddStr(&argBuf, nestedVal.str); Buf_AddStr(&argBuf, nestedVal.str);
FStr_Done(&nestedVal); FStr_Done(&nestedVal);
@ -268,16 +282,15 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
p++; p++;
} }
*out_arg = Buf_GetAll(&argBuf, &argLen); argLen = argBuf.len;
Buf_Destroy(&argBuf, FALSE); *out_arg = Buf_DoneData(&argBuf);
cpp_skip_hspace(&p); cpp_skip_hspace(&p);
if (func != NULL && *p++ != ')') { if (func != NULL && *p++ != ')') {
Parse_Error(PARSE_WARNING, Parse_Error(PARSE_FATAL,
"Missing closing parenthesis for %s()", "Missing closing parenthesis for %s()", func);
func); par->printedError = TRUE;
/* The PARSE_FATAL follows in CondEvalExpression. */
return 0; return 0;
} }
@ -290,7 +303,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
static Boolean static Boolean
FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg) FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{ {
FStr value = Var_Value(arg, VAR_CMDLINE); FStr value = Var_Value(SCOPE_CMDLINE, arg);
Boolean result = value.str != NULL; Boolean result = value.str != NULL;
FStr_Done(&value); FStr_Done(&value);
return result; return result;
@ -390,7 +403,70 @@ is_separator(char ch)
return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL; return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
} }
/*- /*
* In a quoted or unquoted string literal or a number, parse a variable
* expression.
*
* Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
*/
static Boolean
CondParser_StringExpr(CondParser *par, const char *start,
Boolean const doEval, Boolean const quoted,
Buffer *buf, FStr *const inout_str)
{
VarEvalFlags eflags;
const char *nested_p;
Boolean atStart;
VarParseResult parseResult;
/* if we are in quotes, an undefined variable is ok */
eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR
: doEval ? VARE_WANTRES
: VARE_NONE;
nested_p = par->p;
atStart = nested_p == start;
parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, eflags, inout_str);
/* TODO: handle errors */
if (inout_str->str == var_Error) {
if (parseResult == VPR_ERR) {
/*
* FIXME: Even if an error occurs, there is no
* guarantee that it is reported.
*
* See cond-token-plain.mk $$$$$$$$.
*/
par->printedError = TRUE;
}
/*
* XXX: Can there be any situation in which a returned
* var_Error requires freeIt?
*/
FStr_Done(inout_str);
/*
* Even if !doEval, we still report syntax errors, which is
* what getting var_Error back with !doEval means.
*/
*inout_str = FStr_InitRefer(NULL);
return FALSE;
}
par->p = nested_p;
/*
* If the '$' started the string literal (which means no quotes), and
* the variable expression is followed by a space, looks like a
* comparison operator or is the end of the expression, we are done.
*/
if (atStart && is_separator(par->p[0]))
return FALSE;
Buf_AddStr(buf, inout_str->str);
FStr_Done(inout_str);
*inout_str = FStr_InitRefer(NULL); /* not finished yet */
return TRUE;
}
/*
* Parse a string from a variable reference or an optionally quoted * Parse a string from a variable reference or an optionally quoted
* string. This is called for the lhs and rhs of string comparisons. * string. This is called for the lhs and rhs of string comparisons.
* *
@ -399,19 +475,14 @@ is_separator(char ch)
* Sets out_quoted if the string was quoted. * Sets out_quoted if the string was quoted.
* Sets out_freeIt. * Sets out_freeIt.
*/ */
/* coverity:[+alloc : arg-*4] */
static void static void
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
FStr *out_str, Boolean *out_quoted) FStr *out_str, Boolean *out_quoted)
{ {
Buffer buf; Buffer buf;
FStr str; FStr str;
Boolean atStart;
const char *nested_p;
Boolean quoted; Boolean quoted;
const char *start; const char *start;
VarEvalFlags eflags;
VarParseResult parseResult;
Buf_Init(&buf); Buf_Init(&buf);
str = FStr_InitRefer(NULL); str = FStr_InitRefer(NULL);
@ -430,12 +501,10 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
} }
continue; continue;
case '"': case '"':
if (quoted) {
par->p++; /* skip the closing quote */
goto got_str;
}
Buf_AddByte(&buf, par->p[0]); /* likely? */
par->p++; par->p++;
if (quoted)
goto got_str; /* skip the closing quote */
Buf_AddByte(&buf, '"');
continue; continue;
case ')': /* see is_separator */ case ')': /* see is_separator */
case '!': case '!':
@ -450,47 +519,9 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
par->p++; par->p++;
continue; continue;
case '$': case '$':
/* if we are in quotes, an undefined variable is ok */ if (!CondParser_StringExpr(par,
eflags = start, doEval, quoted, &buf, &str))
doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
doEval ? VARE_WANTRES :
VARE_NONE;
nested_p = par->p;
atStart = nested_p == start;
parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags,
&str);
/* TODO: handle errors */
if (str.str == var_Error) {
if (parseResult == VPR_ERR)
par->printedError = TRUE;
/*
* XXX: Can there be any situation in which
* a returned var_Error requires freeIt?
*/
FStr_Done(&str);
/*
* Even if !doEval, we still report syntax
* errors, which is what getting var_Error
* back with !doEval means.
*/
str = FStr_InitRefer(NULL);
goto cleanup; goto cleanup;
}
par->p = nested_p;
/*
* If the '$' started the string literal (which means
* no quotes), and the variable expression is followed
* by a space, looks like a comparison operator or is
* the end of the expression, we are done.
*/
if (atStart && is_separator(par->p[0]))
goto cleanup;
Buf_AddStr(&buf, str.str);
FStr_Done(&str);
str = FStr_InitRefer(NULL); /* not finished yet */
continue; continue;
default: default:
if (strictLHS && !quoted && *start != '$' && if (strictLHS && !quoted && *start != '$' &&
@ -508,38 +539,17 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
} }
} }
got_str: got_str:
str = FStr_InitOwn(Buf_GetAll(&buf, NULL)); str = FStr_InitOwn(buf.data);
cleanup: cleanup:
Buf_Destroy(&buf, FALSE); Buf_DoneData(&buf);
*out_str = str; *out_str = str;
} }
struct If {
const char *form; /* Form of if */
size_t formlen; /* Length of form */
Boolean doNot; /* TRUE if default function should be negated */
/* The default function to apply on unquoted bare words. */
Boolean (*defProc)(size_t, const char *);
};
/* The different forms of .if directives. */
static const struct If ifs[] = {
{ "def", 3, FALSE, FuncDefined },
{ "ndef", 4, TRUE, FuncDefined },
{ "make", 4, FALSE, FuncMake },
{ "nmake", 5, TRUE, FuncMake },
{ "", 0, FALSE, FuncDefined },
{ NULL, 0, FALSE, NULL }
};
enum {
PLAIN_IF_INDEX = 4
};
static Boolean static Boolean
If_Eval(const struct If *if_info, const char *arg, size_t arglen) If_Eval(const CondParser *par, const char *arg, size_t arglen)
{ {
Boolean res = if_info->defProc(arglen, arg); Boolean res = par->evalBare(arglen, arg);
return if_info->doNot ? !res : res; return par->negateEvalBare ? !res : res;
} }
/* /*
@ -563,69 +573,96 @@ EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
* the evaluation function from that .if variant, which would test * the evaluation function from that .if variant, which would test
* whether a variable of the given name were defined. */ * whether a variable of the given name were defined. */
/* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */ /* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
if (par->if_info->form[0] == '\0') if (par->plain)
return value[0] != '\0'; return value[0] != '\0';
/* For the other variants of .ifxxx ${...}, use its default function. */ return If_Eval(par, value, strlen(value));
return If_Eval(par->if_info, value, strlen(value));
} }
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
static Token static Boolean
EvalCompareNum(double lhs, const char *op, double rhs) EvalCompareNum(double lhs, ComparisonOp op, double rhs)
{ {
DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op); DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
switch (op[0]) { switch (op) {
case '!': case LT:
if (op[1] != '=') { return lhs < rhs;
Parse_Error(PARSE_WARNING, "Unknown operator"); case LE:
/* The PARSE_FATAL follows in CondEvalExpression. */ return lhs <= rhs;
return TOK_ERROR; case GT:
} return lhs > rhs;
return ToToken(lhs != rhs); case GE:
case '=': return lhs >= rhs;
if (op[1] != '=') { case NE:
Parse_Error(PARSE_WARNING, "Unknown operator"); return lhs != rhs;
/* The PARSE_FATAL follows in CondEvalExpression. */ default:
return TOK_ERROR; return lhs == rhs;
}
return ToToken(lhs == rhs);
case '<':
return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs);
case '>':
return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs);
} }
return TOK_ERROR;
} }
static Token static Token
EvalCompareStr(const char *lhs, const char *op, const char *rhs) EvalCompareStr(CondParser *par, const char *lhs,
ComparisonOp op, const char *rhs)
{ {
if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) { if (op != EQ && op != NE) {
Parse_Error(PARSE_WARNING, Parse_Error(PARSE_FATAL,
"String comparison operator " "String comparison operator must be either == or !=");
"must be either == or !="); par->printedError = TRUE;
/* The PARSE_FATAL follows in CondEvalExpression. */
return TOK_ERROR; return TOK_ERROR;
} }
DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op); DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0)); lhs, rhs, opname[op]);
return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
} }
/* Evaluate a comparison, such as "${VAR} == 12345". */ /* Evaluate a comparison, such as "${VAR} == 12345". */
static Token static Token
EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op, EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
const char *rhs, Boolean rhsQuoted) ComparisonOp op, const char *rhs, Boolean rhsQuoted)
{ {
double left, right; double left, right;
if (!rhsQuoted && !lhsQuoted) if (!rhsQuoted && !lhsQuoted)
if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right))
return EvalCompareNum(left, op, right); return ToToken(EvalCompareNum(left, op, right));
return EvalCompareStr(lhs, op, rhs); return EvalCompareStr(par, lhs, op, rhs);
}
static Boolean
CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
{
const char *p = par->p;
if (p[0] == '<' && p[1] == '=') {
*out_op = LE;
goto length_2;
} else if (p[0] == '<') {
*out_op = LT;
goto length_1;
} else if (p[0] == '>' && p[1] == '=') {
*out_op = GE;
goto length_2;
} else if (p[0] == '>') {
*out_op = GT;
goto length_1;
} else if (p[0] == '=' && p[1] == '=') {
*out_op = EQ;
goto length_2;
} else if (p[0] == '!' && p[1] == '=') {
*out_op = NE;
goto length_2;
}
return FALSE;
length_2:
par->p = p + 2;
return TRUE;
length_1:
par->p = p + 1;
return TRUE;
} }
/* /*
@ -641,7 +678,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
{ {
Token t = TOK_ERROR; Token t = TOK_ERROR;
FStr lhs, rhs; FStr lhs, rhs;
const char *op; ComparisonOp op;
Boolean lhsQuoted, rhsQuoted; Boolean lhsQuoted, rhsQuoted;
/* /*
@ -654,18 +691,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par); CondParser_SkipWhitespace(par);
op = par->p; if (!CondParser_ComparisonOp(par, &op)) {
switch (par->p[0]) {
case '!':
case '=':
case '<':
case '>':
if (par->p[1] == '=')
par->p += 2;
else
par->p++;
break;
default:
/* Unknown operator, compare against an empty string or 0. */ /* Unknown operator, compare against an empty string or 0. */
t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted)); t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
goto done_lhs; goto done_lhs;
@ -674,9 +700,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par); CondParser_SkipWhitespace(par);
if (par->p[0] == '\0') { if (par->p[0] == '\0') {
Parse_Error(PARSE_WARNING, Parse_Error(PARSE_FATAL,
"Missing right-hand-side of operator"); "Missing right-hand-side of operator '%s'", opname[op]);
/* The PARSE_FATAL follows in CondEvalExpression. */ par->printedError = TRUE;
goto done_lhs; goto done_lhs;
} }
@ -689,7 +715,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
goto done_rhs; goto done_rhs;
} }
t = EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
done_rhs: done_rhs:
FStr_Done(&rhs); FStr_Done(&rhs);
@ -704,8 +730,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
*/ */
/*ARGSUSED*/ /*ARGSUSED*/
static size_t static size_t
ParseEmptyArg(const char **pp, Boolean doEval, ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
const char *func MAKE_ATTR_UNUSED, char **out_arg) Boolean doEval, const char *func MAKE_ATTR_UNUSED,
char **out_arg)
{ {
FStr val; FStr val;
size_t magic_res; size_t magic_res;
@ -714,7 +741,7 @@ ParseEmptyArg(const char **pp, Boolean doEval,
*out_arg = NULL; *out_arg = NULL;
(*pp)--; /* Make (*pp)[1] point to the '('. */ (*pp)--; /* Make (*pp)[1] point to the '('. */
(void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE, (void)Var_Parse(pp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
&val); &val);
/* TODO: handle errors */ /* TODO: handle errors */
/* If successful, *pp points beyond the closing ')' now. */ /* If successful, *pp points beyond the closing ')' now. */
@ -753,8 +780,8 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
static const struct fn_def { static const struct fn_def {
const char *fn_name; const char *fn_name;
size_t fn_name_len; size_t fn_name_len;
size_t (*fn_parse)(const char **, Boolean, const char *, size_t (*fn_parse)(CondParser *, const char **, Boolean,
char **); const char *, char **);
Boolean (*fn_eval)(size_t, const char *); Boolean (*fn_eval)(size_t, const char *);
} fns[] = { } fns[] = {
{ "defined", 7, ParseFuncArg, FuncDefined }, { "defined", 7, ParseFuncArg, FuncDefined },
@ -779,7 +806,7 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
if (*cp != '(') if (*cp != '(')
break; break;
arglen = fn->fn_parse(&cp, doEval, fn->fn_name, &arg); arglen = fn->fn_parse(par, &cp, doEval, fn->fn_name, &arg);
if (arglen == 0 || arglen == (size_t)-1) { if (arglen == 0 || arglen == (size_t)-1) {
par->p = cp; par->p = cp;
*out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR; *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
@ -825,7 +852,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* syntax would be invalid if we did "defined(a)" - so instead treat * syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression. * as an expression.
*/ */
arglen = ParseFuncArg(&cp, doEval, NULL, &arg); arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
cp1 = cp; cp1 = cp;
cpp_skip_whitespace(&cp1); cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!') if (*cp1 == '=' || *cp1 == '!')
@ -838,7 +865,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* after .if must have been taken literally, so the argument cannot * after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion. * be empty - even if it contained a variable expansion.
*/ */
t = ToToken(!doEval || If_Eval(par->if_info, arg, arglen)); t = ToToken(!doEval || If_Eval(par, arg, arglen));
free(arg); free(arg);
return t; return t;
} }
@ -909,148 +936,113 @@ CondParser_Token(CondParser *par, Boolean doEval)
} }
/* /*
* Parse a single term in the expression. This consists of a terminal symbol * Term -> '(' Or ')'
* or TOK_NOT and a term (not including the binary operators): * Term -> '!' Term
* * Term -> Leaf Operator Leaf
* T -> defined(variable) | make(target) | exists(file) | symbol * Term -> Leaf
* T -> ! T | ( E )
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
*/ */
static Token static CondResult
CondParser_Term(CondParser *par, Boolean doEval) CondParser_Term(CondParser *par, Boolean doEval)
{ {
CondResult res;
Token t; Token t;
t = CondParser_Token(par, doEval); t = CondParser_Token(par, doEval);
if (t == TOK_TRUE)
return CR_TRUE;
if (t == TOK_FALSE)
return CR_FALSE;
if (t == TOK_EOF) { if (t == TOK_LPAREN) {
/* res = CondParser_Or(par, doEval);
* If we reached the end of the expression, the expression if (res == CR_ERROR)
* is malformed... return CR_ERROR;
*/ if (CondParser_Token(par, doEval) != TOK_RPAREN)
t = TOK_ERROR; return CR_ERROR;
} else if (t == TOK_LPAREN) { return res;
/*
* T -> ( E )
*/
t = CondParser_Expr(par, doEval);
if (t != TOK_ERROR) {
if (CondParser_Token(par, doEval) != TOK_RPAREN) {
t = TOK_ERROR;
}
}
} else if (t == TOK_NOT) {
t = CondParser_Term(par, doEval);
if (t == TOK_TRUE) {
t = TOK_FALSE;
} else if (t == TOK_FALSE) {
t = TOK_TRUE;
}
} }
return t;
if (t == TOK_NOT) {
res = CondParser_Term(par, doEval);
if (res == CR_TRUE)
res = CR_FALSE;
else if (res == CR_FALSE)
res = CR_TRUE;
return res;
}
return CR_ERROR;
} }
/* /*
* Parse a conjunctive factor (nice name, wot?) * And -> Term '&&' And
* * And -> Term
* F -> T && F | T
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR
*/ */
static Token static CondResult
CondParser_Factor(CondParser *par, Boolean doEval) CondParser_And(CondParser *par, Boolean doEval)
{ {
Token l, o; CondResult res;
Token op;
l = CondParser_Term(par, doEval); res = CondParser_Term(par, doEval);
if (l != TOK_ERROR) { if (res == CR_ERROR)
o = CondParser_Token(par, doEval); return CR_ERROR;
if (o == TOK_AND) { op = CondParser_Token(par, doEval);
/* if (op == TOK_AND) {
* F -> T && F if (res == CR_TRUE)
* return CondParser_And(par, doEval);
* If T is TOK_FALSE, the whole thing will be if (CondParser_And(par, FALSE) == CR_ERROR)
* TOK_FALSE, but we have to parse the r.h.s. anyway return CR_ERROR;
* (to throw it away). If T is TOK_TRUE, the result return res;
* is the r.h.s., be it a TOK_ERROR or not.
*/
if (l == TOK_TRUE) {
l = CondParser_Factor(par, doEval);
} else {
(void)CondParser_Factor(par, FALSE);
}
} else {
/*
* F -> T
*/
CondParser_PushBack(par, o);
}
} }
return l;
CondParser_PushBack(par, op);
return res;
} }
/* /*
* Main expression production. * Or -> And '||' Or
* * Or -> And
* E -> F || E | F
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
*/ */
static Token static CondResult
CondParser_Expr(CondParser *par, Boolean doEval) CondParser_Or(CondParser *par, Boolean doEval)
{ {
Token l, o; CondResult res;
Token op;
l = CondParser_Factor(par, doEval); res = CondParser_And(par, doEval);
if (l != TOK_ERROR) { if (res == CR_ERROR)
o = CondParser_Token(par, doEval); return CR_ERROR;
if (o == TOK_OR) { op = CondParser_Token(par, doEval);
/* if (op == TOK_OR) {
* E -> F || E if (res == CR_FALSE)
* return CondParser_Or(par, doEval);
* A similar thing occurs for ||, except that here if (CondParser_Or(par, FALSE) == CR_ERROR)
* we make sure the l.h.s. is TOK_FALSE before we return CR_ERROR;
* bother to evaluate the r.h.s. Once again, if l return res;
* is TOK_FALSE, the result is the r.h.s. and once
* again if l is TOK_TRUE, we parse the r.h.s. to
* throw it away.
*/
if (l == TOK_FALSE) {
l = CondParser_Expr(par, doEval);
} else {
(void)CondParser_Expr(par, FALSE);
}
} else {
/*
* E -> F
*/
CondParser_PushBack(par, o);
}
} }
return l;
CondParser_PushBack(par, op);
return res;
} }
static CondEvalResult static CondEvalResult
CondParser_Eval(CondParser *par, Boolean *value) CondParser_Eval(CondParser *par, Boolean *out_value)
{ {
Token res; CondResult res;
DEBUG1(COND, "CondParser_Eval: %s\n", par->p); DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
res = CondParser_Expr(par, TRUE); res = CondParser_Or(par, TRUE);
if (res != TOK_FALSE && res != TOK_TRUE) if (res == CR_ERROR)
return COND_INVALID; return COND_INVALID;
if (CondParser_Token(par, FALSE) != TOK_EOF) if (CondParser_Token(par, FALSE) != TOK_EOF)
return COND_INVALID; return COND_INVALID;
*value = res == TOK_TRUE; *out_value = res == CR_TRUE;
return COND_PARSE; return COND_PARSE;
} }
@ -1066,7 +1058,8 @@ CondParser_Eval(CondParser *par, Boolean *value)
* (*value) is set to the boolean value of the condition * (*value) is set to the boolean value of the condition
*/ */
static CondEvalResult static CondEvalResult
CondEvalExpression(const struct If *info, const char *cond, Boolean *value, CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
Boolean (*evalBare)(size_t, const char *), Boolean negate,
Boolean eprint, Boolean strictLHS) Boolean eprint, Boolean strictLHS)
{ {
CondParser par; CondParser par;
@ -1076,12 +1069,14 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
cpp_skip_hspace(&cond); cpp_skip_hspace(&cond);
par.if_info = info != NULL ? info : ifs + PLAIN_IF_INDEX; par.plain = plain;
par.evalBare = evalBare;
par.negateEvalBare = negate;
par.p = cond; par.p = cond;
par.curr = TOK_NONE; par.curr = TOK_NONE;
par.printedError = FALSE; par.printedError = FALSE;
rval = CondParser_Eval(&par, value); rval = CondParser_Eval(&par, out_value);
if (rval == COND_INVALID && eprint && !par.printedError) if (rval == COND_INVALID && eprint && !par.printedError)
Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
@ -1096,7 +1091,8 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
CondEvalResult CondEvalResult
Cond_EvalCondition(const char *cond, Boolean *out_value) Cond_EvalCondition(const char *cond, Boolean *out_value)
{ {
return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE); return CondEvalExpression(cond, out_value, TRUE,
FuncDefined, FALSE, FALSE, FALSE);
} }
static Boolean static Boolean
@ -1106,6 +1102,43 @@ IsEndif(const char *p)
p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
} }
static Boolean
DetermineKindOfConditional(const char **pp, Boolean *out_plain,
Boolean (**out_evalBare)(size_t, const char *),
Boolean *out_negate)
{
const char *p = *pp;
p += 2;
*out_plain = FALSE;
*out_evalBare = FuncDefined;
*out_negate = FALSE;
if (*p == 'n') {
p++;
*out_negate = TRUE;
}
if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
p += 3;
} else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */
p += 4;
*out_evalBare = FuncMake;
} else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
*out_plain = TRUE;
} else {
/*
* TODO: Add error message about unknown directive,
* since there is no other known directive that starts
* with 'el' or 'if'.
*
* Example: .elifx 123
*/
return FALSE;
}
*pp = p;
return TRUE;
}
/* /*
* Evaluate the conditional directive in the line, which is one of: * Evaluate the conditional directive in the line, which is one of:
* *
@ -1159,7 +1192,9 @@ Cond_EvalLine(const char *line)
static enum IfState *cond_states = NULL; static enum IfState *cond_states = NULL;
static unsigned int cond_states_cap = 128; static unsigned int cond_states_cap = 128;
const struct If *ifp; Boolean plain;
Boolean (*evalBare)(size_t, const char *);
Boolean negate;
Boolean isElif; Boolean isElif;
Boolean value; Boolean value;
IfState state; IfState state;
@ -1242,29 +1277,8 @@ Cond_EvalLine(const char *line)
return COND_INVALID; /* Not an ifxxx or elifxxx line */ return COND_INVALID; /* Not an ifxxx or elifxxx line */
} }
/* if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
* Figure out what sort of conditional it is -- what its default return COND_INVALID;
* function is, etc. -- by looking in the table of valid "ifs"
*/
p += 2;
for (ifp = ifs;; ifp++) {
if (ifp->form == NULL) {
/*
* TODO: Add error message about unknown directive,
* since there is no other known directive that starts
* with 'el' or 'if'.
*
* Example: .elifx 123
*/
return COND_INVALID;
}
if (is_token(p, ifp->form, ifp->formlen)) {
p += ifp->formlen;
break;
}
}
/* Now we know what sort of 'if' it is... */
if (isElif) { if (isElif) {
if (cond_depth == cond_min_depth) { if (cond_depth == cond_min_depth) {
@ -1308,7 +1322,8 @@ Cond_EvalLine(const char *line)
} }
/* And evaluate the conditional expression */ /* And evaluate the conditional expression */
if (CondEvalExpression(ifp, p, &value, TRUE, TRUE) == COND_INVALID) { if (CondEvalExpression(p, &value, plain, evalBare, negate,
TRUE, TRUE) == COND_INVALID) {
/* Syntax error in conditional, error message already output. */ /* Syntax error in conditional, error message already output. */
/* Skip everything to matching .endif */ /* Skip everything to matching .endif */
/* XXX: An extra '.else' is not detected in this case. */ /* XXX: An extra '.else' is not detected in this case. */

View File

@ -333,6 +333,9 @@
/* Define to `int' if <sys/types.h> does not define. */ /* Define to `int' if <sys/types.h> does not define. */
#undef pid_t #undef pid_t
/* type that signal handlers can safely frob */
#undef sig_atomic_t
/* Define to `unsigned int' if <sys/types.h> does not define. */ /* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t #undef size_t

View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for bmake 20201112. # Generated by GNU Autoconf 2.69 for bmake 20210201.
# #
# Report bugs to <sjg@NetBSD.org>. # Report bugs to <sjg@NetBSD.org>.
# #
@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='bmake' PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake' PACKAGE_TARNAME='bmake'
PACKAGE_VERSION='20201112' PACKAGE_VERSION='20210201'
PACKAGE_STRING='bmake 20201112' PACKAGE_STRING='bmake 20210201'
PACKAGE_BUGREPORT='sjg@NetBSD.org' PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL='' PACKAGE_URL=''
@ -1255,7 +1255,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures bmake 20201112 to adapt to many kinds of systems. \`configure' configures bmake 20210201 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1316,7 +1316,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of bmake 20201112:";; short | recursive ) echo "Configuration of bmake 20210201:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1422,7 +1422,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
bmake configure 20201112 bmake configure 20210201
generated by GNU Autoconf 2.69 generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc. Copyright (C) 2012 Free Software Foundation, Inc.
@ -2002,7 +2002,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by bmake $as_me 20201112, which was It was created by bmake $as_me 20210201, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@ $ $0 $@
@ -5322,6 +5322,24 @@ $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h
fi fi
ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" "
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <signal.h>
"
if test "x$ac_cv_type_sig_atomic_t" = xyes; then :
else
$as_echo "#define sig_atomic_t int" >>confdefs.h
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
$as_echo_n "checking return type of signal handlers... " >&6; } $as_echo_n "checking return type of signal handlers... " >&6; }
if ${ac_cv_type_signal+:} false; then : if ${ac_cv_type_signal+:} false; then :
@ -6666,7 +6684,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by bmake $as_me 20201112, which was This file was extended by bmake $as_me 20210201, which was
generated by GNU Autoconf 2.69. Invocation command line was generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -6728,7 +6746,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\ ac_cs_version="\\
bmake config.status 20201112 bmake config.status 20210201
configured by $0, generated by GNU Autoconf 2.69, configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -1,11 +1,11 @@
dnl dnl
dnl RCSid: dnl RCSid:
dnl $Id: configure.in,v 1.69 2020/11/14 07:40:43 sjg Exp $ dnl $Id: configure.in,v 1.70 2021/02/01 18:29:26 sjg Exp $
dnl dnl
dnl Process this file with autoconf to produce a configure script dnl Process this file with autoconf to produce a configure script
dnl dnl
AC_PREREQ(2.50) AC_PREREQ(2.50)
AC_INIT([bmake], [20201112], [sjg@NetBSD.org]) AC_INIT([bmake], [20210201], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h) AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute dnl make srcdir absolute
@ -185,6 +185,17 @@ AC_DECL_SYS_SIGLIST
AC_HEADER_TIME AC_HEADER_TIME
AC_STRUCT_TM AC_STRUCT_TM
dnl we need sig_atomic_t
AH_TEMPLATE([sig_atomic_t],[type that signal handlers can safely frob])
AC_CHECK_TYPE(sig_atomic_t,,[
AC_DEFINE([sig_atomic_t],[int],)
],[
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <signal.h>
])
dnl Checks for library functions. dnl Checks for library functions.
AC_TYPE_SIGNAL AC_TYPE_SIGNAL
AC_FUNC_VFORK AC_FUNC_VFORK

View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -89,8 +89,9 @@
* Returns TRUE if the name given it needs to * Returns TRUE if the name given it needs to
* be wildcard-expanded. * be wildcard-expanded.
* *
* Dir_Expand Given a pattern and a path, return a Lst of names * SearchPath_Expand
* which match the pattern on the search path. * Expand a filename pattern to find all matching files
* from the search path.
* *
* Dir_FindFile Searches for a file on a given search path. * Dir_FindFile Searches for a file on a given search path.
* If it exists, the entire path is returned. * If it exists, the entire path is returned.
@ -105,7 +106,7 @@
* Update the modification time and path of a node with * Update the modification time and path of a node with
* data from the file corresponding to the node. * data from the file corresponding to the node.
* *
* Dir_AddDir Add a directory to a search path. * SearchPath_Add Add a directory to a search path.
* *
* SearchPath_ToFlags * SearchPath_ToFlags
* Given a search path and a command flag, create * Given a search path and a command flag, create
@ -137,7 +138,7 @@
#include "job.h" #include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $");
/* /*
* A search path is a list of CachedDir structures. A CachedDir has in it the * A search path is a list of CachedDir structures. A CachedDir has in it the
@ -252,7 +253,7 @@ typedef enum CachedStatsFlags {
} CachedStatsFlags; } CachedStatsFlags;
SearchPath dirSearchPath = LST_INIT; /* main search path */ SearchPath dirSearchPath = { LST_INIT }; /* main search path */
static OpenDirs openDirs; /* all cached directories */ static OpenDirs openDirs; /* all cached directories */
@ -484,18 +485,18 @@ Dir_Init(void)
* Called by Dir_InitDir and whenever .CURDIR is assigned to. * Called by Dir_InitDir and whenever .CURDIR is assigned to.
*/ */
void void
Dir_InitCur(const char *cdname) Dir_InitCur(const char *newCurdir)
{ {
CachedDir *dir; CachedDir *dir;
if (cdname == NULL) if (newCurdir == NULL)
return; return;
/* /*
* Our build directory is not the same as our source directory. * Our build directory is not the same as our source directory.
* Keep this one around too. * Keep this one around too.
*/ */
dir = Dir_AddDir(NULL, cdname); dir = SearchPath_Add(NULL, newCurdir);
if (dir == NULL) if (dir == NULL)
return; return;
@ -511,7 +512,7 @@ Dir_InitDot(void)
{ {
CachedDir *dir; CachedDir *dir;
dir = Dir_AddDir(NULL, "."); dir = SearchPath_Add(NULL, ".");
if (dir == NULL) { if (dir == NULL) {
Error("Cannot open `.' (%s)", strerror(errno)); Error("Cannot open `.' (%s)", strerror(errno));
exit(2); /* Not 1 so -q can distinguish error */ exit(2); /* Not 1 so -q can distinguish error */
@ -548,37 +549,37 @@ Dir_SetPATH(void)
CachedDirListNode *ln; CachedDirListNode *ln;
Boolean seenDotLast = FALSE; /* true if we should search '.' last */ Boolean seenDotLast = FALSE; /* true if we should search '.' last */
Var_Delete(".PATH", VAR_GLOBAL); Global_Delete(".PATH");
if ((ln = dirSearchPath.first) != NULL) { if ((ln = dirSearchPath.dirs.first) != NULL) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
if (dir == dotLast) { if (dir == dotLast) {
seenDotLast = TRUE; seenDotLast = TRUE;
Var_Append(".PATH", dotLast->name, VAR_GLOBAL); Global_Append(".PATH", dotLast->name);
} }
} }
if (!seenDotLast) { if (!seenDotLast) {
if (dot != NULL) if (dot != NULL)
Var_Append(".PATH", dot->name, VAR_GLOBAL); Global_Append(".PATH", dot->name);
if (cur != NULL) if (cur != NULL)
Var_Append(".PATH", cur->name, VAR_GLOBAL); Global_Append(".PATH", cur->name);
} }
for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
if (dir == dotLast) if (dir == dotLast)
continue; continue;
if (dir == dot && seenDotLast) if (dir == dot && seenDotLast)
continue; continue;
Var_Append(".PATH", dir->name, VAR_GLOBAL); Global_Append(".PATH", dir->name);
} }
if (seenDotLast) { if (seenDotLast) {
if (dot != NULL) if (dot != NULL)
Var_Append(".PATH", dot->name, VAR_GLOBAL); Global_Append(".PATH", dot->name);
if (cur != NULL) if (cur != NULL)
Var_Append(".PATH", cur->name, VAR_GLOBAL); Global_Append(".PATH", cur->name);
} }
} }
@ -633,7 +634,9 @@ Dir_HasWildcards(const char *name)
* This is incomplete -- wildcards are only expanded in the final path * This is incomplete -- wildcards are only expanded in the final path
* component, but not in directories like src/lib*c/file*.c, but it * component, but not in directories like src/lib*c/file*.c, but it
* will do for now (now being 1993 until at least 2020). To expand these, * will do for now (now being 1993 until at least 2020). To expand these,
* use the ':sh' variable modifier such as in ${:!echo src/lib*c/file*.c!}. * delegate the work to the shell, using the '!=' variable assignment
* operator, the ':sh' variable modifier or the ':!...!' variable modifier,
* such as in ${:!echo src/lib*c/file*.c!}.
* *
* Input: * Input:
* pattern Pattern to look for * pattern Pattern to look for
@ -667,7 +670,8 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
* directory cache. * directory cache.
* *
* This means that the pattern '[a-z.]*' does not find * This means that the pattern '[a-z.]*' does not find
* '.file', which is consistent with bash, NetBSD sh and csh. * '.file', which is consistent with NetBSD sh, NetBSD ksh,
* bash, dash, csh and probably many other shells as well.
*/ */
if (base[0] == '.' && pattern[0] != '.') if (base[0] == '.' && pattern[0] != '.')
continue; continue;
@ -797,7 +801,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
suffix, suffix_len); suffix, suffix_len);
if (contains_wildcard(file)) { if (contains_wildcard(file)) {
Dir_Expand(file, path, expansions); SearchPath_Expand(path, file, expansions);
free(file); free(file);
} else { } else {
Lst_Append(expansions, file); Lst_Append(expansions, file);
@ -814,7 +818,7 @@ static void
DirExpandPath(const char *word, SearchPath *path, StringList *expansions) DirExpandPath(const char *word, SearchPath *path, StringList *expansions)
{ {
SearchPathNode *ln; SearchPathNode *ln;
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
DirMatchFiles(word, dir, expansions); DirMatchFiles(word, dir, expansions);
} }
@ -834,112 +838,121 @@ PrintExpansions(StringList *expansions)
} }
/* /*
* Expand the given word into a list of words by globbing it, looking in the * The wildcard isn't in the first component.
* directories on the given search path. * Find all the components up to the one with the wildcard.
*/
static void
SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
const char *wildcardComponent, StringList *expansions)
{
char *prefix, *dirpath, *end;
SearchPath *partPath;
prefix = bmake_strsedup(pattern, wildcardComponent + 1);
/*
* XXX: Check the "the directory is added to the path" part.
* It is probably surprising that the directory before a
* wildcard gets added to the path.
*/
/*
* XXX: Only the first match of the prefix in the path is
* taken, any others are ignored. The expectation may be
* that the pattern is expanded in the whole path.
*/
dirpath = Dir_FindFile(prefix, path);
free(prefix);
/*
* dirpath is null if can't find the leading component
*
* XXX: Dir_FindFile won't find internal components. i.e. if the
* path contains ../Etc/Object and we're looking for Etc, it won't
* be found. Ah well. Probably not important.
*
* XXX: Check whether the above comment is still true.
*/
if (dirpath == NULL)
return;
end = &dirpath[strlen(dirpath) - 1];
/* XXX: What about multiple trailing slashes? */
if (*end == '/')
*end = '\0';
partPath = SearchPath_New();
(void)SearchPath_Add(partPath, dirpath);
DirExpandPath(wildcardComponent + 1, partPath, expansions);
SearchPath_Free(partPath);
}
/*
* Expand the given pattern into a list of existing filenames by globbing it,
* looking in each directory from the search path.
* *
* Input: * Input:
* word the word to expand
* path the directories in which to find the files * path the directories in which to find the files
* pattern the pattern to expand
* expansions the list on which to place the results * expansions the list on which to place the results
*/ */
void void
Dir_Expand(const char *word, SearchPath *path, StringList *expansions) SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
{ {
const char *cp; const char *brace, *slash, *wildcard, *wildcardComponent;
assert(path != NULL); assert(path != NULL);
assert(expansions != NULL); assert(expansions != NULL);
DEBUG1(DIR, "Expanding \"%s\"... ", word); DEBUG1(DIR, "Expanding \"%s\"... ", pattern);
cp = strchr(word, '{'); brace = strchr(pattern, '{');
if (cp != NULL) { if (brace != NULL) {
DirExpandCurly(word, cp, path, expansions); DirExpandCurly(pattern, brace, path, expansions);
goto done; goto done;
} }
/* At this point, the word does not contain '{'. */ /* At this point, the pattern does not contain '{'. */
slash = strchr(pattern, '/');
if (slash == NULL) {
/* The pattern has no directory component. */
cp = strchr(word, '/');
if (cp == NULL) {
/* The word has no directory component. */
/* First the files in dot. */ /* First the files in dot. */
DirMatchFiles(word, dot, expansions); DirMatchFiles(pattern, dot, expansions);
/* Then the files in every other directory on the path. */ /* Then the files in every other directory on the path. */
DirExpandPath(word, path, expansions); DirExpandPath(pattern, path, expansions);
goto done; goto done;
} }
/* At this point, the word has a directory component. */ /* At this point, the pattern has a directory component. */
/* Find the first wildcard in the word. */ /* Find the first wildcard in the pattern. */
for (cp = word; *cp != '\0'; cp++) for (wildcard = pattern; *wildcard != '\0'; wildcard++)
if (*cp == '?' || *cp == '[' || *cp == '*') if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*')
break; break;
if (*cp == '\0') { if (*wildcard == '\0') {
/* /*
* No directory component and no wildcard at all -- this * No directory component and no wildcard at all -- this
* should never happen as in such a simple case there is no * should never happen as in such a simple case there is no
* need to expand anything. * need to expand anything.
*/ */
DirExpandPath(word, path, expansions); DirExpandPath(pattern, path, expansions);
goto done; goto done;
} }
/* Back up to the start of the component containing the wildcard. */ /* Back up to the start of the component containing the wildcard. */
/* XXX: This handles '///' and '/' differently. */ /* XXX: This handles '///' and '/' differently. */
while (cp > word && *cp != '/') wildcardComponent = wildcard;
cp--; while (wildcardComponent > pattern && *wildcardComponent != '/')
wildcardComponent--;
if (cp == word) { if (wildcardComponent == pattern) {
/* The first component contains the wildcard. */ /* The first component contains the wildcard. */
/* Start the search from the local directory */ /* Start the search from the local directory */
DirExpandPath(word, path, expansions); DirExpandPath(pattern, path, expansions);
goto done; } else {
} SearchPath_ExpandMiddle(path, pattern, wildcardComponent,
expansions);
{
char *prefix = bmake_strsedup(word, cp + 1);
/*
* The wildcard isn't in the first component.
* Find all the components up to the one with the wildcard.
*/
/*
* XXX: Check the "the directory is added to the path" part.
* It is probably surprising that the directory before a
* wildcard gets added to the path.
*/
/*
* XXX: Only the first match of the prefix in the path is
* taken, any others are ignored. The expectation may be
* that the pattern is expanded in the whole path.
*/
char *dirpath = Dir_FindFile(prefix, path);
free(prefix);
/*
* dirpath is null if can't find the leading component
* XXX: Dir_FindFile won't find internal components.
* i.e. if the path contains ../Etc/Object and we're
* looking for Etc, it won't be found. Ah well.
* Probably not important.
* XXX: Check whether the above comment is still true.
*/
if (dirpath != NULL) {
SearchPath *partPath;
char *end = &dirpath[strlen(dirpath) - 1];
/* XXX: What about multiple trailing slashes? */
if (*end == '/')
*end = '\0';
partPath = SearchPath_New();
(void)Dir_AddDir(partPath, dirpath);
DirExpandPath(cp + 1, partPath, expansions);
SearchPath_Free(partPath);
}
} }
done: done:
@ -1051,6 +1064,115 @@ DirFindDot(const char *name, const char *base)
return NULL; return NULL;
} }
static Boolean
FindFileRelative(SearchPath *path, Boolean seenDotLast,
const char *name, char **out_file)
{
SearchPathNode *ln;
Boolean checkedDot = FALSE;
char *file;
DEBUG0(DIR, " Trying subdirectories...\n");
if (!seenDotLast) {
if (dot != NULL) {
checkedDot = TRUE;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
goto found;
}
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
if (dir == dotLast)
continue;
if (dir == dot) {
if (checkedDot)
continue;
checkedDot = TRUE;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
goto found;
}
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = TRUE;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
goto found;
}
if (checkedDot) {
/*
* Already checked by the given name, since . was in
* the path, so no point in proceeding.
*/
DEBUG0(DIR, " Checked . already, returning NULL\n");
file = NULL;
goto found;
}
return FALSE;
found:
*out_file = file;
return TRUE;
}
static Boolean
FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
const char *const name, const char *const base,
char **out_file)
{
char *file;
SearchPathNode *ln;
/*
* For absolute names, compare directory path prefix against
* the the directory path of each member on the search path
* for an exact match. If we have an exact match on any member
* of the search path, use the cached contents of that member
* to lookup the final file component. If that lookup fails we
* can safely assume that the file does not exist at all.
* This is signified by DirLookupAbs() returning an empty
* string.
*/
DEBUG0(DIR, " Trying exact path matches...\n");
if (!seenDotLast && cur != NULL &&
((file = DirLookupAbs(cur, name, base)) != NULL))
goto found;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
if (dir == dotLast)
continue;
if ((file = DirLookupAbs(dir, name, base)) != NULL)
goto found;
}
if (seenDotLast && cur != NULL &&
((file = DirLookupAbs(cur, name, base)) != NULL))
goto found;
return FALSE;
found:
if (file[0] == '\0') {
free(file);
file = NULL;
}
*out_file = file;
return TRUE;
}
/* /*
* Find the file with the given name along the given search path. * Find the file with the given name along the given search path.
* *
@ -1085,8 +1207,8 @@ Dir_FindFile(const char *name, SearchPath *path)
return NULL; return NULL;
} }
if (path->first != NULL) { if (path->dirs.first != NULL) {
CachedDir *dir = path->first->datum; CachedDir *dir = path->dirs.first->datum;
if (dir == dotLast) { if (dir == dotLast) {
seenDotLast = TRUE; seenDotLast = TRUE;
DEBUG0(DIR, "[dot last]..."); DEBUG0(DIR, "[dot last]...");
@ -1105,7 +1227,7 @@ Dir_FindFile(const char *name, SearchPath *path)
/* /*
* We look through all the directories on the path seeking one * We look through all the directories on the path seeking one
* which contains the final component of the given name. If * which contains the final component of the given name. If
* such a beast is found, we concatenate the directory name * such a file is found, we concatenate the directory name
* and the final component and return the resulting string. * and the final component and return the resulting string.
* If we don't find any such thing, we go on to phase two. * If we don't find any such thing, we go on to phase two.
* *
@ -1119,7 +1241,7 @@ Dir_FindFile(const char *name, SearchPath *path)
if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)
return file; return file;
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
if (dir == dotLast) if (dir == dotLast)
continue; continue;
@ -1139,7 +1261,7 @@ Dir_FindFile(const char *name, SearchPath *path)
* path. (eg. /usr/include and sys/types.h. The above search would * path. (eg. /usr/include and sys/types.h. The above search would
* fail to turn up types.h in /usr/include, but it *is* in * fail to turn up types.h in /usr/include, but it *is* in
* /usr/include/sys/types.h). * /usr/include/sys/types.h).
* [ This no longer applies: If we find such a beast, we assume there * [ This no longer applies: If we find such a file, we assume there
* will be more (what else can we assume?) and add all but the last * will be more (what else can we assume?) and add all but the last
* component of the resulting name onto the search path (at the * component of the resulting name onto the search path (at the
* end).] * end).]
@ -1157,100 +1279,11 @@ Dir_FindFile(const char *name, SearchPath *path)
} }
if (name[0] != '/') { if (name[0] != '/') {
SearchPathNode *ln; if (FindFileRelative(path, seenDotLast, name, &file))
Boolean checkedDot = FALSE;
DEBUG0(DIR, " Trying subdirectories...\n");
if (!seenDotLast) {
if (dot != NULL) {
checkedDot = TRUE;
if ((file = DirLookupSubdir(dot, name)) != NULL)
return file;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
return file;
}
for (ln = path->first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
if (dir == dotLast)
continue;
if (dir == dot) {
if (checkedDot)
continue;
checkedDot = TRUE;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
return file;
}
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = TRUE;
if ((file = DirLookupSubdir(dot, name)) != NULL)
return file;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
return file;
}
if (checkedDot) {
/*
* Already checked by the given name, since . was in
* the path, so no point in proceeding.
*/
DEBUG0(DIR, " Checked . already, returning NULL\n");
return NULL;
}
} else { /* name[0] == '/' */
SearchPathNode *ln;
/*
* For absolute names, compare directory path prefix against
* the the directory path of each member on the search path
* for an exact match. If we have an exact match on any member
* of the search path, use the cached contents of that member
* to lookup the final file component. If that lookup fails we
* can safely assume that the file does not exist at all.
* This is signified by DirLookupAbs() returning an empty
* string.
*/
DEBUG0(DIR, " Trying exact path matches...\n");
if (!seenDotLast && cur != NULL &&
((file = DirLookupAbs(cur, name, base)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
}
return file; return file;
} } else {
if (FindFileAbsolute(path, seenDotLast, name, base, &file))
for (ln = path->first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
if (dir == dotLast)
continue;
if ((file = DirLookupAbs(dir, name, base)) != NULL) {
if (file[0] == '\0') {
free(file);
return NULL;
}
return file;
}
}
if (seenDotLast && cur != NULL &&
((file = DirLookupAbs(cur, name, base)) != NULL)) {
if (file[0] == '\0') {
free(file);
return NULL;
}
return file; return file;
}
} }
/* /*
@ -1281,7 +1314,7 @@ Dir_FindFile(const char *name, SearchPath *path)
base++; base++;
} }
prefix = bmake_strsedup(name, base - 1); prefix = bmake_strsedup(name, base - 1);
(void)Dir_AddDir(path, prefix); (void)SearchPath_Add(path, prefix);
free(prefix); free(prefix);
bigmisses++; bigmisses++;
@ -1511,7 +1544,7 @@ CacheNewDir(const char *name, SearchPath *path)
OpenDirs_Add(&openDirs, dir); OpenDirs_Add(&openDirs, dir);
if (path != NULL) if (path != NULL)
Lst_Append(path, CachedDir_Ref(dir)); Lst_Append(&path->dirs, CachedDir_Ref(dir));
DEBUG1(DIR, "Caching %s done\n", name); DEBUG1(DIR, "Caching %s done\n", name);
return dir; return dir;
@ -1536,28 +1569,28 @@ CacheNewDir(const char *name, SearchPath *path)
* Lst_Append and CachedDir_Ref. * Lst_Append and CachedDir_Ref.
*/ */
CachedDir * CachedDir *
Dir_AddDir(SearchPath *path, const char *name) SearchPath_Add(SearchPath *path, const char *name)
{ {
if (path != NULL && strcmp(name, ".DOTLAST") == 0) { if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
SearchPathNode *ln; SearchPathNode *ln;
/* XXX: Linear search gets slow with thousands of entries. */ /* XXX: Linear search gets slow with thousands of entries. */
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *pathDir = ln->datum; CachedDir *pathDir = ln->datum;
if (strcmp(pathDir->name, name) == 0) if (strcmp(pathDir->name, name) == 0)
return pathDir; return pathDir;
} }
Lst_Prepend(path, CachedDir_Ref(dotLast)); Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast));
} }
if (path != NULL) { if (path != NULL) {
/* XXX: Why is OpenDirs only checked if path != NULL? */ /* XXX: Why is OpenDirs only checked if path != NULL? */
CachedDir *dir = OpenDirs_Find(&openDirs, name); CachedDir *dir = OpenDirs_Find(&openDirs, name);
if (dir != NULL) { if (dir != NULL) {
if (Lst_FindDatum(path, dir) == NULL) if (Lst_FindDatum(&path->dirs, dir) == NULL)
Lst_Append(path, CachedDir_Ref(dir)); Lst_Append(&path->dirs, CachedDir_Ref(dir));
return dir; return dir;
} }
} }
@ -1574,9 +1607,9 @@ Dir_CopyDirSearchPath(void)
{ {
SearchPath *path = SearchPath_New(); SearchPath *path = SearchPath_New();
SearchPathNode *ln; SearchPathNode *ln;
for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
Lst_Append(path, CachedDir_Ref(dir)); Lst_Append(&path->dirs, CachedDir_Ref(dir));
} }
return path; return path;
} }
@ -1596,7 +1629,7 @@ Dir_CopyDirSearchPath(void)
* don't go well. * don't go well.
*/ */
char * char *
SearchPath_ToFlags(const char *flag, SearchPath *path) SearchPath_ToFlags(SearchPath *path, const char *flag)
{ {
Buffer buf; Buffer buf;
SearchPathNode *ln; SearchPathNode *ln;
@ -1604,7 +1637,7 @@ SearchPath_ToFlags(const char *flag, SearchPath *path)
Buf_Init(&buf); Buf_Init(&buf);
if (path != NULL) { if (path != NULL) {
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
Buf_AddStr(&buf, " "); Buf_AddStr(&buf, " ");
Buf_AddStr(&buf, flag); Buf_AddStr(&buf, flag);
@ -1612,7 +1645,7 @@ SearchPath_ToFlags(const char *flag, SearchPath *path)
} }
} }
return Buf_Destroy(&buf, FALSE); return Buf_DoneData(&buf);
} }
/* Free the search path and all directories mentioned in it. */ /* Free the search path and all directories mentioned in it. */
@ -1621,11 +1654,12 @@ SearchPath_Free(SearchPath *path)
{ {
SearchPathNode *ln; SearchPathNode *ln;
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
CachedDir_Unref(dir); CachedDir_Unref(dir);
} }
Lst_Free(path); Lst_Done(&path->dirs);
free(path);
} }
/* /*
@ -1635,8 +1669,8 @@ SearchPath_Free(SearchPath *path)
void void
SearchPath_Clear(SearchPath *path) SearchPath_Clear(SearchPath *path)
{ {
while (!Lst_IsEmpty(path)) { while (!Lst_IsEmpty(&path->dirs)) {
CachedDir *dir = Lst_Dequeue(path); CachedDir *dir = Lst_Dequeue(&path->dirs);
CachedDir_Unref(dir); CachedDir_Unref(dir);
} }
} }
@ -1651,10 +1685,10 @@ SearchPath_AddAll(SearchPath *dst, SearchPath *src)
{ {
SearchPathNode *ln; SearchPathNode *ln;
for (ln = src->first; ln != NULL; ln = ln->next) { for (ln = src->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum; CachedDir *dir = ln->datum;
if (Lst_FindDatum(dst, dir) == NULL) if (Lst_FindDatum(&dst->dirs, dir) == NULL)
Lst_Append(dst, CachedDir_Ref(dir)); Lst_Append(&dst->dirs, CachedDir_Ref(dir));
} }
} }
@ -1685,11 +1719,11 @@ Dir_PrintDirectories(void)
} }
void void
SearchPath_Print(SearchPath *path) SearchPath_Print(const SearchPath *path)
{ {
SearchPathNode *ln; SearchPathNode *ln;
for (ln = path->first; ln != NULL; ln = ln->next) { for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
const CachedDir *dir = ln->datum; const CachedDir *dir = ln->datum;
debug_printf("%s ", dir->name); debug_printf("%s ", dir->name);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.h,v 1.40 2020/12/01 19:28:32 rillig Exp $ */ /* $NetBSD: dir.h,v 1.43 2021/02/05 05:48:19 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -83,16 +83,16 @@ void Dir_InitDot(void);
void Dir_End(void); void Dir_End(void);
void Dir_SetPATH(void); void Dir_SetPATH(void);
Boolean Dir_HasWildcards(const char *); Boolean Dir_HasWildcards(const char *);
void Dir_Expand(const char *, SearchPath *, StringList *); void SearchPath_Expand(SearchPath *, const char *, StringList *);
char *Dir_FindFile(const char *, SearchPath *); char *Dir_FindFile(const char *, SearchPath *);
char *Dir_FindHereOrAbove(const char *, const char *); char *Dir_FindHereOrAbove(const char *, const char *);
void Dir_UpdateMTime(GNode *, Boolean); void Dir_UpdateMTime(GNode *, Boolean);
CachedDir *Dir_AddDir(SearchPath *, const char *); CachedDir *SearchPath_Add(SearchPath *, const char *);
char *SearchPath_ToFlags(const char *, SearchPath *); char *SearchPath_ToFlags(SearchPath *, const char *);
void SearchPath_Clear(SearchPath *); void SearchPath_Clear(SearchPath *);
void SearchPath_AddAll(SearchPath *, SearchPath *); void SearchPath_AddAll(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void); void Dir_PrintDirectories(void);
void SearchPath_Print(SearchPath *); void SearchPath_Print(const SearchPath *);
SearchPath *Dir_CopyDirSearchPath(void); SearchPath *Dir_CopyDirSearchPath(void);
/* Stripped-down variant of struct stat. */ /* Stripped-down variant of struct stat. */

View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $ */ /* $NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $ */
/* /*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -29,7 +29,7 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $"); MAKE_RCSID("$NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $");
/* /*
* Convert a bitset into a string representation, showing the names of the * Convert a bitset into a string representation, showing the names of the
@ -78,15 +78,3 @@ Enum_FlagsToString(char *buf, size_t buf_size,
buf[0] = '\0'; buf[0] = '\0';
return buf_start; return buf_start;
} }
/* Convert a fixed-value enum into a string representation. */
const char *
Enum_ValueToString(int value, const EnumToStringSpec *spec)
{
for (; spec->es_name[0] != '\0'; spec++) {
if (value == spec->es_value)
return spec->es_name;
}
abort(/* unknown enum value */);
/*NOTREACHED*/
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.h,v 1.14 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: enum.h,v 1.18 2021/02/02 21:26:51 rillig Exp $ */
/* /*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -39,8 +39,9 @@ typedef struct EnumToStringSpec {
const char *es_name; const char *es_name;
} EnumToStringSpec; } EnumToStringSpec;
const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *); const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
const char *Enum_ValueToString(int, const EnumToStringSpec *);
/* For Enum_FlagsToString, the separator between flags. */ /* For Enum_FlagsToString, the separator between flags. */
#define ENUM__SEP "|" #define ENUM__SEP "|"
@ -100,14 +101,16 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \ #define ENUM__SPECS_5(part1, part2, part3, part4, part5) \
{ part1, part2, part3, part4, part5, { 0, "" } } { part1, part2, part3, part4, part5, { 0, "" } }
/* Declare the necessary data structures for calling Enum_ValueToString. */
#define ENUM__VALUE_RTTI(typnam, specs) \
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs
/* Declare the necessary data structures for calling Enum_FlagsToString. */ /* Declare the necessary data structures for calling Enum_FlagsToString. */
#define ENUM__FLAGS_RTTI(typnam, specs, joined) \ #define ENUM__FLAGS_RTTI(typnam, specs, joined) \
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
enum { typnam ## _ ## ToStringSize = sizeof joined } enum { typnam ## _ ## ToStringSize = sizeof (joined) }; \
MAKE_INLINE const char *typnam ## _ToString(char *buf, typnam value) \
{ return Enum_FlagsToString(buf, typnam ## _ ## ToStringSize, \
value, typnam ## _ ## ToStringSpecs); \
} \
extern void enum_flags_rtti_dummy(void)
/* /*
* Declare the necessary data structures for calling Enum_FlagsToString * Declare the necessary data structures for calling Enum_FlagsToString
@ -174,28 +177,18 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_4(v1, v2, v3, v4), \ ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_4(v5, v6, v7, v8))) ENUM__JOIN_STR_4(v5, v6, v7, v8)))
/*
* Declare the necessary data structures for calling Enum_ValueToString
* for an enum with 8 constants.
*/
#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
ENUM__VALUE_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_4(v1, v2, v3, v4), \
ENUM__SPEC_4(v5, v6, v7, v8)))
/* /*
* Declare the necessary data structures for calling Enum_FlagsToString * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 10 flags. * for an enum with 9 flags.
*/ */
#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ #define ENUM_FLAGS_RTTI_9(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \ ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \
ENUM__SPEC_2(v9, v10)), \ ENUM__SPEC_1(v9)), \
ENUM__JOIN_2( \ ENUM__JOIN_2( \
ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \
ENUM__JOIN_STR_2(v9, v10))) ENUM__JOIN_STR_1(v9)))
/* /*
* Declare the necessary data structures for calling Enum_FlagsToString * Declare the necessary data structures for calling Enum_FlagsToString

View File

@ -1,6 +1,6 @@
/* $NetBSD: filemon.h,v 1.4 2020/11/29 09:27:40 rillig Exp $ */ /* $NetBSD: filemon.h,v 1.5 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2019 The NetBSD Foundation, Inc. * Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *

View File

@ -1,6 +1,6 @@
/* $NetBSD: filemon_dev.c,v 1.6 2020/11/29 09:27:40 rillig Exp $ */ /* $NetBSD: filemon_dev.c,v 1.8 2021/02/01 21:09:25 rillig Exp $ */
/*- /*
* Copyright (c) 2020 The NetBSD Foundation, Inc. * Copyright (c) 2020 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -39,7 +39,7 @@
#include <unistd.h> #include <unistd.h>
#ifdef HAVE_FILEMON_H #ifdef HAVE_FILEMON_H
# include <filemon.h> # include <filemon.h>
#endif #endif
#ifndef _PATH_FILEMON #ifndef _PATH_FILEMON

View File

@ -1,6 +1,6 @@
/* $NetBSD: filemon_ktrace.c,v 1.12 2021/01/10 23:59:53 rillig Exp $ */ /* $NetBSD: filemon_ktrace.c,v 1.14 2021/02/01 21:34:41 rillig Exp $ */
/*- /*
* Copyright (c) 2019 The NetBSD Foundation, Inc. * Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -761,6 +761,8 @@ filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
return syscall_enter(key, call, 1, &show_chdir); return syscall_enter(key, call, 1, &show_chdir);
} }
/* TODO: monitor fchdir as well */
/*ARGSUSED*/ /*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_execve(struct filemon *F, const struct filemon_key *key, filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
@ -832,6 +834,11 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
const register_t *args = (const void *)&call[1]; const register_t *args = (const void *)&call[1];
int flags, fd; int flags, fd;
/*
* XXX: In the .meta log, the base directory is missing, which makes
* all references to relative pathnames useless.
*/
if (call->ktr_argsize < 3) if (call->ktr_argsize < 3)
return NULL; return NULL;
fd = (int)args[0]; fd = (int)args[0];
@ -860,6 +867,8 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
} }
} }
/* TODO: monitor the other *at syscalls as well, not only openat. */
/*ARGSUSED*/ /*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key, filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,

View File

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $ */
/* /*
* Copyright (c) 1992, The Regents of the University of California. * Copyright (c) 1992, The Regents of the University of California.
@ -29,7 +29,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/*- /*
* Handling of .for/.endfor loops in a makefile. * Handling of .for/.endfor loops in a makefile.
* *
* For loops have the form: * For loops have the form:
@ -58,9 +58,8 @@
#include "make.h" #include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $");
static int forLevel = 0; /* Nesting level */
/* One of the variables to the left of the "in" in a .for loop. */ /* One of the variables to the left of the "in" in a .for loop. */
typedef struct ForVar { typedef struct ForVar {
@ -68,10 +67,7 @@ typedef struct ForVar {
size_t nameLen; size_t nameLen;
} ForVar; } ForVar;
/* typedef struct ForLoop {
* State of a for loop.
*/
typedef struct For {
Buffer body; /* Unexpanded body of the loop */ Buffer body; /* Unexpanded body of the loop */
Vector /* of ForVar */ vars; /* Iteration variables */ Vector /* of ForVar */ vars; /* Iteration variables */
Words items; /* Substitution items */ Words items; /* Substitution items */
@ -81,22 +77,33 @@ typedef struct For {
* only ${V} and $(V). */ * only ${V} and $(V). */
Boolean short_var; Boolean short_var;
unsigned int sub_next; /* Where to continue iterating */ unsigned int sub_next; /* Where to continue iterating */
} For; } ForLoop;
static For *accumFor; /* Loop being accumulated */
static void static ForLoop *accumFor; /* Loop being accumulated */
ForAddVar(For *f, const char *name, size_t len) static int forLevel = 0; /* Nesting level */
static ForLoop *
ForLoop_New(void)
{ {
ForVar *var = Vector_Push(&f->vars); ForLoop *f = bmake_malloc(sizeof *f);
var->name = bmake_strldup(name, len);
var->nameLen = len; Buf_Init(&f->body);
Vector_Init(&f->vars, sizeof(ForVar));
f->items.words = NULL;
f->items.freeIt = NULL;
Buf_Init(&f->curBody);
f->short_var = FALSE;
f->sub_next = 0;
return f;
} }
static void static void
For_Free(For *f) ForLoop_Free(ForLoop *f)
{ {
Buf_Destroy(&f->body, TRUE); Buf_Done(&f->body);
while (f->vars.len > 0) { while (f->vars.len > 0) {
ForVar *var = Vector_Pop(&f->vars); ForVar *var = Vector_Pop(&f->vars);
@ -105,11 +112,89 @@ For_Free(For *f)
Vector_Done(&f->vars); Vector_Done(&f->vars);
Words_Free(f->items); Words_Free(f->items);
Buf_Destroy(&f->curBody, TRUE); Buf_Done(&f->curBody);
free(f); free(f);
} }
static void
ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
{
ForVar *var = Vector_Push(&f->vars);
var->name = bmake_strldup(name, len);
var->nameLen = len;
}
static Boolean
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
for (;;) {
size_t len;
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
return FALSE;
}
/*
* XXX: This allows arbitrary variable names;
* see directive-for.mk.
*/
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
continue;
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
break;
}
if (len == 1)
f->short_var = TRUE;
ForLoop_AddVar(f, p, len);
p += len;
}
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
return FALSE;
}
*pp = p;
return TRUE;
}
static Boolean
ForLoop_ParseItems(ForLoop *f, const char *p)
{
char *items;
cpp_skip_whitespace(&p);
if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
Parse_Error(PARSE_FATAL, "Error in .for loop items");
return FALSE;
}
f->items = Str_Words(items, FALSE);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
f->items.len = 0; /* .for var in ${:U} */
if (f->items.len != 0 && f->items.len % f->vars.len != 0) {
Parse_Error(PARSE_FATAL,
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
(unsigned)f->items.len, (unsigned)f->vars.len);
return FALSE;
}
return TRUE;
}
static Boolean static Boolean
IsFor(const char *p) IsFor(const char *p)
{ {
@ -138,7 +223,7 @@ IsEndfor(const char *p)
int int
For_Eval(const char *line) For_Eval(const char *line)
{ {
For *f; ForLoop *f;
const char *p; const char *p;
p = line + 1; /* skip the '.' */ p = line + 1; /* skip the '.' */
@ -153,113 +238,41 @@ For_Eval(const char *line)
} }
p += 3; p += 3;
/* f = ForLoop_New();
* we found a for loop, and now we are going to parse it.
*/
f = bmake_malloc(sizeof *f); if (!ForLoop_ParseVarnames(f, &p)) {
Buf_Init(&f->body); ForLoop_Free(f);
Vector_Init(&f->vars, sizeof(ForVar));
f->items.words = NULL;
f->items.freeIt = NULL;
Buf_Init(&f->curBody);
f->short_var = FALSE;
f->sub_next = 0;
/* Grab the variables. Terminate on "in". */
for (;;) {
size_t len;
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
For_Free(f);
return -1;
}
/*
* XXX: This allows arbitrary variable names;
* see directive-for.mk.
*/
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
continue;
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
break;
}
if (len == 1)
f->short_var = TRUE;
ForAddVar(f, p, len);
p += len;
}
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
For_Free(f);
return -1; return -1;
} }
cpp_skip_whitespace(&p); if (!ForLoop_ParseItems(f, p)) {
/* Continue parsing the .for loop, but don't iterate. */
{ f->items.len = 0;
char *items;
if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
Parse_Error(PARSE_FATAL, "Error in .for loop items");
f->items.len = 0;
goto done;
}
f->items = Str_Words(items, FALSE);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
f->items.len = 0; /* .for var in ${:U} */
} }
{
size_t nitems, nvars;
if ((nitems = f->items.len) > 0 &&
nitems % (nvars = f->vars.len) != 0) {
Parse_Error(PARSE_FATAL,
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
(unsigned)nitems, (unsigned)nvars);
/*
* Return 'success' so that the body of the .for loop
* is accumulated.
* Remove all items so that the loop doesn't iterate.
*/
f->items.len = 0;
}
}
done:
accumFor = f; accumFor = f;
forLevel = 1; forLevel = 1;
return 1; return 1;
} }
/* /*
* Add another line to a .for loop. * Add another line to the .for loop that is being built up.
* Returns FALSE when the matching .endfor is reached. * Returns FALSE when the matching .endfor is reached.
*/ */
Boolean Boolean
For_Accum(const char *line) For_Accum(const char *line)
{ {
const char *ptr = line; const char *p = line;
if (*ptr == '.') { if (*p == '.') {
ptr++; p++;
cpp_skip_whitespace(&ptr); cpp_skip_whitespace(&p);
if (IsEndfor(ptr)) { if (IsEndfor(p)) {
DEBUG1(FOR, "For: end for %d\n", forLevel); DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0) if (--forLevel <= 0)
return FALSE; return FALSE;
} else if (IsFor(ptr)) { } else if (IsFor(p)) {
forLevel++; forLevel++;
DEBUG1(FOR, "For: new loop %d\n", forLevel); DEBUG1(FOR, "For: new loop %d\n", forLevel);
} }
@ -307,11 +320,11 @@ for_var_len(const char *var)
* that characters that break this syntax must be backslash-escaped. * that characters that break this syntax must be backslash-escaped.
*/ */
static Boolean static Boolean
NeedsEscapes(const char *word, char endc) NeedsEscapes(const char *value, char endc)
{ {
const char *p; const char *p;
for (p = word; *p != '\0'; p++) { for (p = value; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
return TRUE; return TRUE;
} }
@ -356,8 +369,8 @@ Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
*/ */
static void static void
SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
const char **inout_mark) char endc, const char **inout_mark)
{ {
size_t i; size_t i;
const char *p = *pp; const char *p = *pp;
@ -397,7 +410,7 @@ SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc,
* variable expressions like $i with their ${:U...} expansion. * variable expressions like $i with their ${:U...} expansion.
*/ */
static void static void
SubstVarShort(For *f, const char *p, const char **inout_mark) ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
{ {
const char ch = *p; const char ch = *p;
ForVar *vars; ForVar *vars;
@ -432,12 +445,12 @@ SubstVarShort(For *f, const char *p, const char **inout_mark)
* This code assumes that the variable with the empty name will never be * This code assumes that the variable with the empty name will never be
* defined, see unit-tests/varname-empty.mk for more details. * defined, see unit-tests/varname-empty.mk for more details.
* *
* The detection of substitutions of the loop control variable is naive. * The detection of substitutions of the loop control variables is naive.
* Many of the modifiers use \ to escape $ (not $) so it is possible * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible
* to contrive a makefile where an unwanted substitution happens. * to contrive a makefile where an unwanted substitution happens.
*/ */
static void static void
ForSubstBody(For *f) ForLoop_SubstBody(ForLoop *f)
{ {
const char *p, *bodyEnd; const char *p, *bodyEnd;
const char *mark; /* where the last replacement left off */ const char *mark; /* where the last replacement left off */
@ -449,10 +462,10 @@ ForSubstBody(For *f)
for (p = mark; (p = strchr(p, '$')) != NULL;) { for (p = mark; (p = strchr(p, '$')) != NULL;) {
if (p[1] == '{' || p[1] == '(') { if (p[1] == '{' || p[1] == '(') {
p += 2; p += 2;
SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')', ForLoop_SubstVarLong(f, &p, bodyEnd,
&mark); p[-1] == '{' ? '}' : ')', &mark);
} else if (p[1] != '\0') { } else if (p[1] != '\0') {
SubstVarShort(f, p + 1, &mark); ForLoop_SubstVarShort(f, p + 1, &mark);
p += 2; p += 2;
} else } else
break; break;
@ -468,15 +481,15 @@ ForSubstBody(For *f)
static char * static char *
ForReadMore(void *v_arg, size_t *out_len) ForReadMore(void *v_arg, size_t *out_len)
{ {
For *f = v_arg; ForLoop *f = v_arg;
if (f->sub_next == f->items.len) { if (f->sub_next == f->items.len) {
/* No more iterations */ /* No more iterations */
For_Free(f); ForLoop_Free(f);
return NULL; return NULL;
} }
ForSubstBody(f); ForLoop_SubstBody(f);
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
f->sub_next += (unsigned int)f->vars.len; f->sub_next += (unsigned int)f->vars.len;
@ -488,7 +501,7 @@ ForReadMore(void *v_arg, size_t *out_len)
void void
For_Run(int lineno) For_Run(int lineno)
{ {
For *f = accumFor; ForLoop *f = accumFor;
accumFor = NULL; accumFor = NULL;
if (f->items.len == 0) { if (f->items.len == 0) {
@ -496,7 +509,7 @@ For_Run(int lineno)
* Nothing to expand - possibly due to an earlier syntax * Nothing to expand - possibly due to an earlier syntax
* error. * error.
*/ */
For_Free(f); ForLoop_Free(f);
return; return;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -74,7 +74,7 @@
#include "make.h" #include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $"); MAKE_RCSID("$NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $");
/* /*
* The ratio of # entries to # buckets at which we rebuild the table to * The ratio of # entries to # buckets at which we rebuild the table to
@ -82,7 +82,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $");
*/ */
#define rebuildLimit 3 #define rebuildLimit 3
/* This hash function matches Gosling's emacs and java.lang.String. */ /* This hash function matches Gosling's Emacs and java.lang.String. */
static unsigned int static unsigned int
hash(const char *key, size_t *out_keylen) hash(const char *key, size_t *out_keylen)
{ {
@ -142,7 +142,10 @@ HashTable_Init(HashTable *t)
t->maxchain = 0; t->maxchain = 0;
} }
/* Remove everything from the hash table and frees up the memory. */ /*
* Remove everything from the hash table and free up the memory for the keys
* of the hash table, but not for the values associated to these keys.
*/
void void
HashTable_Done(HashTable *t) HashTable_Done(HashTable *t)
{ {
@ -157,8 +160,8 @@ HashTable_Done(HashTable *t)
he = next; he = next;
} }
} }
free(t->buckets);
free(t->buckets);
#ifdef CLEANUP #ifdef CLEANUP
t->buckets = NULL; t->buckets = NULL;
#endif #endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $ */ /* $NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,14 +69,14 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/*- /*
* job.c -- * job.c --
* handle the creation etc. of our child processes. * handle the creation etc. of our child processes.
* *
* Interface: * Interface:
* Job_Init Called to initialize this module. In addition, * Job_Init Called to initialize this module. In addition,
* any commands attached to the .BEGIN target * the .BEGIN target is made including all of its
* are executed before this function returns. * dependencies before this function returns.
* Hence, the makefiles must have been parsed * Hence, the makefiles must have been parsed
* before this function is called. * before this function is called.
* *
@ -99,9 +99,9 @@
* a time given by the SEL_* constants, below, * a time given by the SEL_* constants, below,
* or until output is ready. * or until output is ready.
* *
* Job_ParseShell Given the line following a .SHELL target, parse * Job_ParseShell Given a special dependency line with target '.SHELL',
* the line as a shell specification. Returns * define the shell that is used for the creation
* FALSE if the spec was incorrect. * commands in jobs mode.
* *
* Job_Finish Perform any final processing which needs doing. * Job_Finish Perform any final processing which needs doing.
* This includes the execution of any commands * This includes the execution of any commands
@ -109,10 +109,9 @@
* target. It should only be called when the * target. It should only be called when the
* job table is empty. * job table is empty.
* *
* Job_AbortAll Abort all currently running jobs. It doesn't * Job_AbortAll Abort all currently running jobs. Do not handle
* handle output or do anything for the jobs, * output or do anything for the jobs, just kill them.
* just kills them. It should only be called in * Should only be called in an emergency.
* an emergency.
* *
* Job_CheckCommands * Job_CheckCommands
* Verify that the commands for a target are * Verify that the commands for a target are
@ -156,7 +155,7 @@
#include "trace.h" #include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $"); MAKE_RCSID("$NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $");
/* /*
* A shell defines how the commands are run. All commands for a target are * A shell defines how the commands are run. All commands for a target are
@ -177,15 +176,15 @@ MAKE_RCSID("$NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $");
* The error checking for individual commands is controlled using hasErrCtl, * The error checking for individual commands is controlled using hasErrCtl,
* errOn, errOff and runChkTmpl. * errOn, errOff and runChkTmpl.
* *
* If a shell doesn't have error control, echoTmpl becomes a printf template * In case a shell doesn't have error control, echoTmpl is a printf template
* for echoing the command, should echoing be on; runIgnTmpl becomes * for echoing the command, should echoing be on; runIgnTmpl is another
* another printf template for executing the command while ignoring the return * printf template for executing the command while ignoring the return
* status. Finally runChkTmpl is a printf template for running the command and * status. Finally runChkTmpl is a printf template for running the command and
* causing the shell to exit on error. If any of these strings are empty when * causing the shell to exit on error. If any of these strings are empty when
* hasErrCtl is FALSE, the command will be executed anyway as is, and if it * hasErrCtl is FALSE, the command will be executed anyway as is, and if it
* causes an error, so be it. Any templates set up to echo the command will * causes an error, so be it. Any templates set up to echo the command will
* escape any '$ ` \ "' characters in the command string to avoid common * escape any '$ ` \ "' characters in the command string to avoid unwanted
* problems with echo "%s\n" as a template. * shell code injection, the escaped command is safe to use in double quotes.
* *
* The command-line flags "echo" and "exit" also control the behavior. The * The command-line flags "echo" and "exit" also control the behavior. The
* "echo" flag causes the shell to start echoing commands right away. The * "echo" flag causes the shell to start echoing commands right away. The
@ -230,7 +229,7 @@ typedef struct Shell {
} Shell; } Shell;
typedef struct CommandFlags { typedef struct CommandFlags {
/* Whether to echo the command before running it. */ /* Whether to echo the command before or instead of running it. */
Boolean echo; Boolean echo;
/* Run the command even in -n or -N mode. */ /* Run the command even in -n or -N mode. */
@ -282,6 +281,7 @@ typedef enum AbortReason { /* why is the make aborting? */
ABORT_ERROR, /* Because of an error */ ABORT_ERROR, /* Because of an error */
ABORT_INTERRUPT, /* Because it was interrupted */ ABORT_INTERRUPT, /* Because it was interrupted */
ABORT_WAIT /* Waiting for jobs to finish */ ABORT_WAIT /* Waiting for jobs to finish */
/* XXX: "WAIT" is not a _reason_ for aborting, it's rather a status. */
} AbortReason; } AbortReason;
static AbortReason aborting = ABORT_NONE; static AbortReason aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ #define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
@ -436,7 +436,7 @@ static Shell *shell = &shells[DEFSHELL_INDEX];
const char *shellPath = NULL; /* full pathname of executable image */ const char *shellPath = NULL; /* full pathname of executable image */
const char *shellName = NULL; /* last component of shellPath */ const char *shellName = NULL; /* last component of shellPath */
char *shellErrFlag = NULL; char *shellErrFlag = NULL;
static char *shellArgv = NULL; /* Custom shell args */ static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
static Job *job_table; /* The structures that describe them */ static Job *job_table; /* The structures that describe them */
@ -450,8 +450,8 @@ static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */
* the output channels of children * the output channels of children
*/ */
static struct pollfd *fds = NULL; static struct pollfd *fds = NULL;
static Job **allJobs = NULL; static Job **jobByFdIndex = NULL;
static nfds_t nJobs = 0; static nfds_t fdsLen = 0;
static void watchfd(Job *); static void watchfd(Job *);
static void clearfd(Job *); static void clearfd(Job *);
static Boolean readyfd(Job *); static Boolean readyfd(Job *);
@ -468,6 +468,7 @@ enum {
}; };
static sigset_t caught_signals; /* Set of signals we handle */ static sigset_t caught_signals; /* Set of signals we handle */
static volatile sig_atomic_t caught_sigchld;
static void JobDoOutput(Job *, Boolean); static void JobDoOutput(Job *, Boolean);
static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD; static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD;
@ -632,6 +633,7 @@ JobCondPassSig(int signo)
static void static void
JobChildSig(int signo MAKE_ATTR_UNUSED) JobChildSig(int signo MAKE_ATTR_UNUSED)
{ {
caught_sigchld = 1;
while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 &&
errno == EAGAIN) errno == EAGAIN)
continue; continue;
@ -1018,7 +1020,7 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
* Print all commands to the shell file that is later executed. * Print all commands to the shell file that is later executed.
* *
* The special command "..." stops printing and saves the remaining commands * The special command "..." stops printing and saves the remaining commands
* to be executed later. * to be executed later, when the target '.END' is made.
* *
* Return whether at least one command was written to the shell file. * Return whether at least one command was written to the shell file.
*/ */
@ -1045,7 +1047,10 @@ JobPrintCommands(Job *job)
return seen; return seen;
} }
/* Save the delayed commands, to be executed when everything else is done. */ /*
* Save the delayed commands (those after '...'), to be executed later in
* the '.END' node, when everything else is done.
*/
static void static void
JobSaveCommands(Job *job) JobSaveCommands(Job *job)
{ {
@ -1054,9 +1059,11 @@ JobSaveCommands(Job *job)
for (ln = job->tailCmds; ln != NULL; ln = ln->next) { for (ln = job->tailCmds; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum; const char *cmd = ln->datum;
char *expanded_cmd; char *expanded_cmd;
/* XXX: This Var_Subst is only intended to expand the dynamic /*
* XXX: This Var_Subst is only intended to expand the dynamic
* variables such as .TARGET, .IMPSRC. It is not intended to * variables such as .TARGET, .IMPSRC. It is not intended to
* expand the other variables as well; see deptgt-end.mk. */ * expand the other variables as well; see deptgt-end.mk.
*/
(void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd); (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
/* TODO: handle errors */ /* TODO: handle errors */
Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd); Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd);
@ -1338,7 +1345,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
* .DEFAULT itself. * .DEFAULT itself.
*/ */
Make_HandleUse(defaultNode, gn); Make_HandleUse(defaultNode, gn);
Var_Set(IMPSRC, GNode_VarTarget(gn), gn); Var_Set(gn, IMPSRC, GNode_VarTarget(gn));
return TRUE; return TRUE;
} }
@ -1420,7 +1427,7 @@ JobExec(Job *job, char **argv)
Var_ReexportVars(); Var_ReexportVars();
cpid = vFork(); cpid = vfork();
if (cpid == -1) if (cpid == -1)
Punt("Cannot vfork: %s", strerror(errno)); Punt("Cannot vfork: %s", strerror(errno));
@ -1496,12 +1503,12 @@ JobExec(Job *job, char **argv)
#if defined(HAVE_SETPGID) #if defined(HAVE_SETPGID)
(void)setpgid(0, getpid()); (void)setpgid(0, getpid());
#else #else
#if defined(HAVE_SETSID) # if defined(HAVE_SETSID)
/* XXX: dsl - I'm sure this should be setpgrp()... */ /* XXX: dsl - I'm sure this should be setpgrp()... */
(void)setsid(); (void)setsid();
#else # else
(void)setpgrp(0, getpid()); (void)setpgrp(0, getpid());
#endif # endif
#endif #endif
(void)execv(shellPath, argv); (void)execv(shellPath, argv);
@ -1532,9 +1539,7 @@ JobExec(Job *job, char **argv)
job->cmdFILE = NULL; job->cmdFILE = NULL;
} }
/* /* Now that the job is actually running, add it to the table. */
* Now the job is actually running, add it to the table.
*/
if (DEBUG(JOB)) { if (DEBUG(JOB)) {
debug_printf("JobExec(%s): pid %d added to jobs table\n", debug_printf("JobExec(%s): pid %d added to jobs table\n",
job->node->name, job->pid); job->node->name, job->pid);
@ -1589,15 +1594,14 @@ JobMakeArgv(Job *job, char **argv)
} }
static void static void
JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run) JobWriteShellCommands(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
{ {
/* /*
* tfile is the name of a file into which all shell commands * tfile is the name of a file into which all shell commands
* are put. It is removed before the child shell is executed, * are put. It is removed before the child shell is executed,
* unless DEBUG(SCRIPT) is set. * unless DEBUG(SCRIPT) is set.
*/ */
char *tfile; char tfile[MAXPATHLEN];
sigset_t mask;
int tfd; /* File descriptor to the temp file */ int tfd; /* File descriptor to the temp file */
/* /*
@ -1609,22 +1613,13 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
DieHorribly(); DieHorribly();
} }
JobSigLock(&mask); tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile);
tfd = mkTempFile(TMPPAT, &tfile);
if (!DEBUG(SCRIPT))
(void)eunlink(tfile);
JobSigUnlock(&mask);
job->cmdFILE = fdopen(tfd, "w+"); job->cmdFILE = fdopen(tfd, "w+");
if (job->cmdFILE == NULL) if (job->cmdFILE == NULL)
Punt("Could not fdopen %s", tfile); Punt("Could not fdopen %s", tfile);
(void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC); (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
/*
* Send the commands to the command file, flush all its
* buffers then rewind and remove the thing.
*/
*out_run = TRUE;
#ifdef USE_META #ifdef USE_META
if (useMeta) { if (useMeta) {
@ -1634,11 +1629,7 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
} }
#endif #endif
/* We can do all the commands at once. hooray for sanity */ *out_run = JobPrintCommands(job);
if (!JobPrintCommands(job))
*out_run = FALSE;
free(tfile);
} }
/* /*
@ -1692,46 +1683,36 @@ JobStart(GNode *gn, Boolean special)
cmdsOK = Job_CheckCommands(gn, Error); cmdsOK = Job_CheckCommands(gn, Error);
job->inPollfd = NULL; job->inPollfd = NULL;
/*
* If the -n flag wasn't given, we open up OUR (not the child's) if (Lst_IsEmpty(&gn->commands)) {
* temporary file to stuff commands in it. The thing is rd/wr so job->cmdFILE = stdout;
* we don't need to reopen it to feed it to the shell. If the -n run = FALSE;
* flag *was* given, we just set the file to be stdout. Cute, huh? } else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
*/
if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
(!opts.noExecute && !opts.touchFlag)) { (!opts.noExecute && !opts.touchFlag)) {
JobOpenTmpFile(job, gn, cmdsOK, &run); /*
* The above condition looks very similar to
* GNode_ShouldExecute but is subtly different. It prevents
* that .MAKE targets are touched since these are usually
* virtual targets.
*/
JobWriteShellCommands(job, gn, cmdsOK, &run);
(void)fflush(job->cmdFILE);
} else if (!GNode_ShouldExecute(gn)) { } else if (!GNode_ShouldExecute(gn)) {
/* /*
* Not executing anything -- just print all the commands to * Just print all the commands to stdout in one fell swoop.
* stdout in one fell swoop. This will still set up * This still sets up job->tailCmds correctly.
* job->tailCmds correctly.
*/ */
SwitchOutputTo(gn); SwitchOutputTo(gn);
job->cmdFILE = stdout; job->cmdFILE = stdout;
/*
* Only print the commands if they're ok, but don't die if
* they're not -- just let the user know they're bad and
* keep going. It doesn't do any harm in this case and may
* do some good.
*/
if (cmdsOK) if (cmdsOK)
JobPrintCommands(job); JobPrintCommands(job);
/* Don't execute the shell, thank you. */
run = FALSE; run = FALSE;
(void)fflush(job->cmdFILE);
} else { } else {
/*
* Just touch the target and note that no shell should be
* executed. Set cmdFILE to stdout to make life easier.
* Check the commands, too, but don't die if they're no
* good -- it does no harm to keep working up the graph.
*/
job->cmdFILE = stdout;
Job_Touch(gn, job->echo); Job_Touch(gn, job->echo);
run = FALSE; run = FALSE;
} }
/* Just in case it isn't already... */
(void)fflush(job->cmdFILE);
/* If we're not supposed to execute a shell, don't. */ /* If we're not supposed to execute a shell, don't. */
if (!run) { if (!run) {
@ -1972,11 +1953,11 @@ JobRun(GNode *targ)
* Running these jobs in compat mode also guarantees that these * Running these jobs in compat mode also guarantees that these
* jobs do not overlap with other unrelated jobs. * jobs do not overlap with other unrelated jobs.
*/ */
List *lst = Lst_New(); GNodeList lst = LST_INIT;
Lst_Append(lst, targ); Lst_Append(&lst, targ);
(void)Make_Run(lst); (void)Make_Run(&lst);
Lst_Destroy(lst, NULL); Lst_Done(&lst);
JobStart(targ, JOB_SPECIAL); JobStart(targ, TRUE);
while (jobTokensRunning != 0) { while (jobTokensRunning != 0) {
Job_CatchOutput(); Job_CatchOutput();
} }
@ -2010,6 +1991,11 @@ Job_CatchChildren(void)
if (jobTokensRunning == 0) if (jobTokensRunning == 0)
return; return;
/* Have we received SIGCHLD since last call? */
if (caught_sigchld == 0)
return;
caught_sigchld = 0;
while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) { while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) {
DEBUG2(JOB, "Process %d exited/stopped status %x.\n", DEBUG2(JOB, "Process %d exited/stopped status %x.\n",
pid, WAIT_STATUS(status)); pid, WAIT_STATUS(status));
@ -2085,7 +2071,7 @@ Job_CatchOutput(void)
/* The first fd in the list is the job token pipe */ /* The first fd in the list is the job token pipe */
do { do {
nready = poll(fds + 1 - wantToken, nJobs - 1 + wantToken, nready = poll(fds + 1 - wantToken, fdsLen - 1 + wantToken,
POLL_MSEC); POLL_MSEC);
} while (nready < 0 && errno == EINTR); } while (nready < 0 && errno == EINTR);
@ -2096,24 +2082,17 @@ Job_CatchOutput(void)
char token = 0; char token = 0;
ssize_t count; ssize_t count;
count = read(childExitJob.inPipe, &token, 1); count = read(childExitJob.inPipe, &token, 1);
switch (count) { if (count == 1) {
case 0:
Punt("unexpected eof on token pipe");
/*NOTREACHED*/
case -1:
Punt("token pipe read: %s", strerror(errno));
/*NOTREACHED*/
case 1:
if (token == DO_JOB_RESUME[0]) if (token == DO_JOB_RESUME[0])
/* /*
* Complete relay requested from our SIGCONT * Complete relay requested from our SIGCONT
* handler * handler
*/ */
JobRestartJobs(); JobRestartJobs();
break; } else if (count == 0)
default: Punt("unexpected eof on token pipe");
abort(); else
} Punt("token pipe read: %s", strerror(errno));
nready--; nready--;
} }
@ -2121,10 +2100,10 @@ Job_CatchOutput(void)
if (nready == 0) if (nready == 0)
return; return;
for (i = npseudojobs * nfds_per_job(); i < nJobs; i++) { for (i = npseudojobs * nfds_per_job(); i < fdsLen; i++) {
if (fds[i].revents == 0) if (fds[i].revents == 0)
continue; continue;
job = allJobs[i]; job = jobByFdIndex[i];
if (job->status == JOB_ST_RUNNING) if (job->status == JOB_ST_RUNNING)
JobDoOutput(job, FALSE); JobDoOutput(job, FALSE);
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
@ -2176,7 +2155,7 @@ Shell_Init(void)
if (shellPath == NULL) if (shellPath == NULL)
InitShellNameAndPath(); InitShellNameAndPath();
Var_SetWithFlags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY); Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY);
if (shell->errFlag == NULL) if (shell->errFlag == NULL)
shell->errFlag = ""; shell->errFlag = "";
if (shell->echoFlag == NULL) if (shell->echoFlag == NULL)
@ -2216,12 +2195,12 @@ Job_SetPrefix(void)
{ {
if (targPrefix != NULL) { if (targPrefix != NULL) {
free(targPrefix); free(targPrefix);
} else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { } else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) {
Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL); Global_Set(MAKE_JOB_PREFIX, "---");
} }
(void)Var_Subst("${" MAKE_JOB_PREFIX "}", (void)Var_Subst("${" MAKE_JOB_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &targPrefix); SCOPE_GLOBAL, VARE_WANTRES, &targPrefix);
/* TODO: handle errors */ /* TODO: handle errors */
} }
@ -2244,6 +2223,7 @@ Job_Init(void)
memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table); memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table);
job_table_end = job_table + opts.maxJobs; job_table_end = job_table + opts.maxJobs;
wantToken = 0; wantToken = 0;
caught_sigchld = 0;
aborting = ABORT_NONE; aborting = ABORT_NONE;
job_errors = 0; job_errors = 0;
@ -2276,13 +2256,13 @@ Job_Init(void)
JobCreatePipe(&childExitJob, 3); JobCreatePipe(&childExitJob, 3);
/* Preallocate enough for the maximum number of jobs. */ {
fds = bmake_malloc(sizeof *fds * /* Preallocate enough for the maximum number of jobs. */
(npseudojobs + (size_t)opts.maxJobs) * size_t nfds = (npseudojobs + (size_t)opts.maxJobs) *
nfds_per_job()); nfds_per_job();
allJobs = bmake_malloc(sizeof *allJobs * fds = bmake_malloc(sizeof *fds * nfds);
(npseudojobs + (size_t)opts.maxJobs) * jobByFdIndex = bmake_malloc(sizeof *jobByFdIndex * nfds);
nfds_per_job()); }
/* These are permanent entries and take slots 0 and 1 */ /* These are permanent entries and take slots 0 and 1 */
watchfd(&tokenWaitJob); watchfd(&tokenWaitJob);
@ -2416,7 +2396,7 @@ Job_ParseShell(char *line)
/* XXX: don't use line as an iterator variable */ /* XXX: don't use line as an iterator variable */
pp_skip_whitespace(&line); pp_skip_whitespace(&line);
free(shellArgv); free(shell_freeIt);
memset(&newShell, 0, sizeof newShell); memset(&newShell, 0, sizeof newShell);
@ -2431,7 +2411,7 @@ Job_ParseShell(char *line)
Error("Unterminated quoted string [%s]", line); Error("Unterminated quoted string [%s]", line);
return FALSE; return FALSE;
} }
shellArgv = path; shell_freeIt = path;
for (path = NULL, argv = words; argc != 0; argc--, argv++) { for (path = NULL, argv = words; argc != 0; argc--, argv++) {
char *arg = *argv; char *arg = *argv;
@ -2642,7 +2622,7 @@ void
Job_End(void) Job_End(void)
{ {
#ifdef CLEANUP #ifdef CLEANUP
free(shellArgv); free(shell_freeIt);
#endif #endif
} }
@ -2737,17 +2717,17 @@ watchfd(Job *job)
if (job->inPollfd != NULL) if (job->inPollfd != NULL)
Punt("Watching watched job"); Punt("Watching watched job");
fds[nJobs].fd = job->inPipe; fds[fdsLen].fd = job->inPipe;
fds[nJobs].events = POLLIN; fds[fdsLen].events = POLLIN;
allJobs[nJobs] = job; jobByFdIndex[fdsLen] = job;
job->inPollfd = &fds[nJobs]; job->inPollfd = &fds[fdsLen];
nJobs++; fdsLen++;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) { if (useMeta) {
fds[nJobs].fd = meta_job_fd(job); fds[fdsLen].fd = meta_job_fd(job);
fds[nJobs].events = fds[nJobs].fd == -1 ? 0 : POLLIN; fds[fdsLen].events = fds[fdsLen].fd == -1 ? 0 : POLLIN;
allJobs[nJobs] = job; jobByFdIndex[fdsLen] = job;
nJobs++; fdsLen++;
} }
#endif #endif
} }
@ -2759,7 +2739,7 @@ clearfd(Job *job)
if (job->inPollfd == NULL) if (job->inPollfd == NULL)
Punt("Unwatching unwatched job"); Punt("Unwatching unwatched job");
i = (size_t)(job->inPollfd - fds); i = (size_t)(job->inPollfd - fds);
nJobs--; fdsLen--;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) { if (useMeta) {
/* /*
@ -2769,20 +2749,20 @@ clearfd(Job *job)
assert(nfds_per_job() == 2); assert(nfds_per_job() == 2);
if (i % 2 != 0) if (i % 2 != 0)
Punt("odd-numbered fd with meta"); Punt("odd-numbered fd with meta");
nJobs--; fdsLen--;
} }
#endif #endif
/* /*
* Move last job in table into hole made by dead job. * Move last job in table into hole made by dead job.
*/ */
if (nJobs != i) { if (fdsLen != i) {
fds[i] = fds[nJobs]; fds[i] = fds[fdsLen];
allJobs[i] = allJobs[nJobs]; jobByFdIndex[i] = jobByFdIndex[fdsLen];
allJobs[i]->inPollfd = &fds[i]; jobByFdIndex[i]->inPollfd = &fds[i];
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) { if (useMeta) {
fds[i + 1] = fds[nJobs + 1]; fds[i + 1] = fds[fdsLen + 1];
allJobs[i + 1] = allJobs[nJobs + 1]; jobByFdIndex[i + 1] = jobByFdIndex[fdsLen + 1];
} }
#endif #endif
} }
@ -2822,6 +2802,22 @@ JobTokenAdd(void)
continue; continue;
} }
/* Get a temp file */
int
Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz)
{
int fd;
sigset_t mask;
JobSigLock(&mask);
fd = mkTempFile(pattern, tfile, tfile_sz);
if (tfile != NULL && !DEBUG(SCRIPT))
unlink(tfile);
JobSigUnlock(&mask);
return fd;
}
/* Prep the job token pipe in the root make process. */ /* Prep the job token pipe in the root make process. */
void void
Job_ServerStart(int max_tokens, int jp_0, int jp_1) Job_ServerStart(int max_tokens, int jp_0, int jp_1)
@ -2843,8 +2839,8 @@ Job_ServerStart(int max_tokens, int jp_0, int jp_1)
snprintf(jobarg, sizeof jobarg, "%d,%d", snprintf(jobarg, sizeof jobarg, "%d,%d",
tokenWaitJob.inPipe, tokenWaitJob.outPipe); tokenWaitJob.inPipe, tokenWaitJob.outPipe);
Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-J");
Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); Global_Append(MAKEFLAGS, jobarg);
/* /*
* Preload the job pipe with one token per job, save the one * Preload the job pipe with one token per job, save the one
@ -2943,7 +2939,7 @@ Job_RunTarget(const char *target, const char *fname)
return FALSE; return FALSE;
if (fname != NULL) if (fname != NULL)
Var_Set(ALLSRC, fname, gn); Var_Set(gn, ALLSRC, fname);
JobRun(gn); JobRun(gn);
/* XXX: Replace with GNode_IsError(gn) */ /* XXX: Replace with GNode_IsError(gn) */

View File

@ -1,4 +1,4 @@
/* $NetBSD: job.h,v 1.71 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: job.h,v 1.72 2021/02/05 19:19:17 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -205,5 +205,6 @@ void Job_ServerStart(int, int, int);
void Job_SetPrefix(void); void Job_SetPrefix(void);
Boolean Job_RunTarget(const char *, const char *); Boolean Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t); void Job_FlagsToString(const Job *, char *, size_t);
int Job_TempFile(const char *, char *, size_t);
#endif /* MAKE_JOB_H */ #endif /* MAKE_JOB_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -34,7 +34,7 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $"); MAKE_RCSID("$NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $");
static ListNode * static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum) LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@ -89,17 +89,6 @@ Lst_Free(List *list)
free(list); free(list);
} }
/*
* Destroy a list and free all its resources. The freeProc is called with the
* datum from each node in turn before the node is freed.
*/
void
Lst_Destroy(List *list, LstFreeProc freeProc)
{
Lst_DoneCall(list, freeProc);
free(list);
}
/* Insert a new node with the datum before the given node. */ /* Insert a new node with the datum before the given node. */
void void
Lst_InsertBefore(List *list, ListNode *ln, void *datum) Lst_InsertBefore(List *list, ListNode *ln, void *datum)
@ -287,7 +276,7 @@ Vector_Push(Vector *v)
} }
/* /*
* Return the pointer to the last item in the vector. * Remove the last item from the vector, return the pointer to it.
* The returned data is valid until the next modifying operation. * The returned data is valid until the next modifying operation.
*/ */
void * void *

View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.h,v 1.95 2021/01/03 21:12:03 rillig Exp $ */ /* $NetBSD: lst.h,v 1.96 2021/02/01 18:55:15 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -116,8 +116,6 @@ void Lst_Done(List *);
void Lst_DoneCall(List *, LstFreeProc); void Lst_DoneCall(List *, LstFreeProc);
/* Free the list, leaving the node data unmodified. */ /* Free the list, leaving the node data unmodified. */
void Lst_Free(List *); void Lst_Free(List *);
/* Free the list, freeing the node data using the given function. */
void Lst_Destroy(List *, LstFreeProc);
#define LST_INIT { NULL, NULL } #define LST_INIT { NULL, NULL }

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $ */ /* $NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -73,18 +73,19 @@
* *
* Utility functions defined in this file: * Utility functions defined in this file:
* *
* Main_ParseArgLine Parse and process command line arguments from * Main_ParseArgLine
* a single string. Used to implement the * Parse and process command line arguments from a
* special targets .MFLAGS and .MAKEFLAGS. * single string. Used to implement the special targets
* .MFLAGS and .MAKEFLAGS.
* *
* Error Print a tagged error message. * Error Print a tagged error message.
* *
* Fatal Print an error message and exit. * Fatal Print an error message and exit.
* *
* Punt Abort all jobs and exit with a message. * Punt Abort all jobs and exit with a message.
* *
* Finish Finish things up by printing the number of * Finish Finish things up by printing the number of errors
* errors which occurred, and exit. * that occurred, and exit.
*/ */
#include <sys/types.h> #include <sys/types.h>
@ -110,7 +111,7 @@
#include "trace.h" #include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $"); MAKE_RCSID("$NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $");
#if defined(MAKE_NATIVE) && !defined(lint) #if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. " "The Regents of the University of California. "
@ -200,7 +201,7 @@ usage(void)
} }
static void static void
parse_debug_option_F(const char *modules) MainParseArgDebugFile(const char *arg)
{ {
const char *mode; const char *mode;
size_t len; size_t len;
@ -209,24 +210,24 @@ parse_debug_option_F(const char *modules)
if (opts.debug_file != stdout && opts.debug_file != stderr) if (opts.debug_file != stdout && opts.debug_file != stderr)
fclose(opts.debug_file); fclose(opts.debug_file);
if (*modules == '+') { if (*arg == '+') {
modules++; arg++;
mode = "a"; mode = "a";
} else } else
mode = "w"; mode = "w";
if (strcmp(modules, "stdout") == 0) { if (strcmp(arg, "stdout") == 0) {
opts.debug_file = stdout; opts.debug_file = stdout;
return; return;
} }
if (strcmp(modules, "stderr") == 0) { if (strcmp(arg, "stderr") == 0) {
opts.debug_file = stderr; opts.debug_file = stderr;
return; return;
} }
len = strlen(modules); len = strlen(arg);
fname = bmake_malloc(len + 20); fname = bmake_malloc(len + 20);
memcpy(fname, modules, len + 1); memcpy(fname, arg, len + 1);
/* Let the filename be modified by the pid */ /* Let the filename be modified by the pid */
if (strcmp(fname + len - 3, ".%d") == 0) if (strcmp(fname + len - 3, ".%d") == 0)
@ -242,12 +243,12 @@ parse_debug_option_F(const char *modules)
} }
static void static void
parse_debug_options(const char *argvalue) MainParseArgDebug(const char *argvalue)
{ {
const char *modules; const char *modules;
DebugFlags debug = opts.debug; DebugFlags debug = opts.debug;
for (modules = argvalue; *modules != '\0'; ++modules) { for (modules = argvalue; *modules != '\0'; modules++) {
switch (*modules) { switch (*modules) {
case '0': /* undocumented, only intended for tests */ case '0': /* undocumented, only intended for tests */
debug = DEBUG_NONE; debug = DEBUG_NONE;
@ -325,7 +326,7 @@ parse_debug_options(const char *argvalue)
debug |= DEBUG_SHELL; debug |= DEBUG_SHELL;
break; break;
case 'F': case 'F':
parse_debug_option_F(modules + 1); MainParseArgDebugFile(modules + 1);
goto debug_setbuf; goto debug_setbuf;
default: default:
(void)fprintf(stderr, (void)fprintf(stderr,
@ -348,11 +349,9 @@ parse_debug_options(const char *argvalue)
} }
} }
/* /* Is path relative, or does it contain any relative component "." or ".."? */
* does path contain any relative components
*/
static Boolean static Boolean
is_relpath(const char *path) IsRelativePath(const char *path)
{ {
const char *cp; const char *cp;
@ -383,7 +382,7 @@ MainParseArgChdir(const char *argvalue)
(void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
exit(2); exit(2);
} }
if (!is_relpath(argvalue) && if (!IsRelativePath(argvalue) &&
stat(argvalue, &sa) != -1 && stat(argvalue, &sa) != -1 &&
stat(curdir, &sb) != -1 && stat(curdir, &sb) != -1 &&
sa.st_ino == sb.st_ino && sa.st_ino == sb.st_ino &&
@ -414,8 +413,8 @@ MainParseArgJobsInternal(const char *argvalue)
jp_1 = -1; jp_1 = -1;
opts.compatMake = TRUE; opts.compatMake = TRUE;
} else { } else {
Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-J");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
} }
} }
@ -432,9 +431,9 @@ MainParseArgJobs(const char *argvalue)
progname); progname);
exit(2); /* Not 1 so -q can distinguish error */ exit(2); /* Not 1 so -q can distinguish error */
} }
Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-j");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL); Global_Set(".MAKE.JOBS", argvalue);
maxJobTokens = opts.maxJobs; maxJobTokens = opts.maxJobs;
} }
@ -446,13 +445,13 @@ MainParseArgSysInc(const char *argvalue)
char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4);
if (found_path == NULL) if (found_path == NULL)
return; return;
(void)Dir_AddDir(sysIncPath, found_path); (void)SearchPath_Add(sysIncPath, found_path);
free(found_path); free(found_path);
} else { } else {
(void)Dir_AddDir(sysIncPath, argvalue); (void)SearchPath_Add(sysIncPath, argvalue);
} }
Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-m");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
} }
static Boolean static Boolean
@ -463,22 +462,22 @@ MainParseArg(char c, const char *argvalue)
break; break;
case 'B': case 'B':
opts.compatMake = TRUE; opts.compatMake = TRUE;
Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-B");
Var_Set(MAKE_MODE, "compat", VAR_GLOBAL); Global_Set(MAKE_MODE, "compat");
break; break;
case 'C': case 'C':
MainParseArgChdir(argvalue); MainParseArgChdir(argvalue);
break; break;
case 'D': case 'D':
if (argvalue[0] == '\0') return FALSE; if (argvalue[0] == '\0') return FALSE;
Var_Set(argvalue, "1", VAR_GLOBAL); Global_SetExpand(argvalue, "1");
Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-D");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
break; break;
case 'I': case 'I':
Parse_AddIncludeDir(argvalue); Parse_AddIncludeDir(argvalue);
Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-I");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
break; break;
case 'J': case 'J':
MainParseArgJobsInternal(argvalue); MainParseArgJobsInternal(argvalue);
@ -486,24 +485,24 @@ MainParseArg(char c, const char *argvalue)
case 'N': case 'N':
opts.noExecute = TRUE; opts.noExecute = TRUE;
opts.noRecursiveExecute = TRUE; opts.noRecursiveExecute = TRUE;
Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-N");
break; break;
case 'S': case 'S':
opts.keepgoing = FALSE; opts.keepgoing = FALSE;
Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-S");
break; break;
case 'T': case 'T':
tracefile = bmake_strdup(argvalue); tracefile = bmake_strdup(argvalue);
Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-T");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
break; break;
case 'V': case 'V':
case 'v': case 'v':
opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED;
Lst_Append(&opts.variables, bmake_strdup(argvalue)); Lst_Append(&opts.variables, bmake_strdup(argvalue));
/* XXX: Why always -V? */ /* XXX: Why always -V? */
Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-V");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
break; break;
case 'W': case 'W':
opts.parseWarnFatal = TRUE; opts.parseWarnFatal = TRUE;
@ -511,35 +510,35 @@ MainParseArg(char c, const char *argvalue)
break; break;
case 'X': case 'X':
opts.varNoExportEnv = TRUE; opts.varNoExportEnv = TRUE;
Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-X");
break; break;
case 'd': case 'd':
/* If '-d-opts' don't pass to children */ /* If '-d-opts' don't pass to children */
if (argvalue[0] == '-') if (argvalue[0] == '-')
argvalue++; argvalue++;
else { else {
Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-d");
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Global_Append(MAKEFLAGS, argvalue);
} }
parse_debug_options(argvalue); MainParseArgDebug(argvalue);
break; break;
case 'e': case 'e':
opts.checkEnvFirst = TRUE; opts.checkEnvFirst = TRUE;
Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-e");
break; break;
case 'f': case 'f':
Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
break; break;
case 'i': case 'i':
opts.ignoreErrors = TRUE; opts.ignoreErrors = TRUE;
Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-i");
break; break;
case 'j': case 'j':
MainParseArgJobs(argvalue); MainParseArgJobs(argvalue);
break; break;
case 'k': case 'k':
opts.keepgoing = TRUE; opts.keepgoing = TRUE;
Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-k");
break; break;
case 'm': case 'm':
MainParseArgSysInc(argvalue); MainParseArgSysInc(argvalue);
@ -547,28 +546,28 @@ MainParseArg(char c, const char *argvalue)
break; break;
case 'n': case 'n':
opts.noExecute = TRUE; opts.noExecute = TRUE;
Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-n");
break; break;
case 'q': case 'q':
opts.queryFlag = TRUE; opts.queryFlag = TRUE;
/* Kind of nonsensical, wot? */ /* Kind of nonsensical, wot? */
Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-q");
break; break;
case 'r': case 'r':
opts.noBuiltins = TRUE; opts.noBuiltins = TRUE;
Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-r");
break; break;
case 's': case 's':
opts.beSilent = TRUE; opts.beSilent = TRUE;
Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-s");
break; break;
case 't': case 't':
opts.touchFlag = TRUE; opts.touchFlag = TRUE;
Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-t");
break; break;
case 'w': case 'w':
opts.enterFlag = TRUE; opts.enterFlag = TRUE;
Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-w");
break; break;
default: default:
case '?': case '?':
@ -657,10 +656,10 @@ MainParseArgs(int argc, char **argv)
* perform them if so. Else take them to be targets and stuff them * perform them if so. Else take them to be targets and stuff them
* on the end of the "create" list. * on the end of the "create" list.
*/ */
for (; argc > 1; ++argv, --argc) { for (; argc > 1; argv++, argc--) {
VarAssign var; VarAssign var;
if (Parse_IsVar(argv[1], &var)) { if (Parse_IsVar(argv[1], &var)) {
Parse_DoVar(&var, VAR_CMDLINE); Parse_DoVar(&var, SCOPE_CMDLINE);
} else { } else {
if (argv[1][0] == '\0') if (argv[1][0] == '\0')
Punt("illegal (null) argument."); Punt("illegal (null) argument.");
@ -692,7 +691,7 @@ Main_ParseArgLine(const char *line)
if (line == NULL) if (line == NULL)
return; return;
/* XXX: don't use line as an iterator variable */ /* XXX: don't use line as an iterator variable */
for (; *line == ' '; ++line) for (; *line == ' '; line++)
continue; continue;
if (line[0] == '\0') if (line[0] == '\0')
return; return;
@ -712,7 +711,7 @@ Main_ParseArgLine(const char *line)
} }
#endif #endif
{ {
FStr argv0 = Var_Value(".MAKE", VAR_GLOBAL); FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE");
buf = str_concat3(argv0.str, " ", line); buf = str_concat3(argv0.str, " ", line);
FStr_Done(&argv0); FStr_Done(&argv0);
} }
@ -756,7 +755,7 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
progname, path, strerror(errno)); progname, path, strerror(errno));
} else { } else {
snprintf(objdir, sizeof objdir, "%s", path); snprintf(objdir, sizeof objdir, "%s", path);
Var_Set(".OBJDIR", objdir, VAR_GLOBAL); Global_Set(".OBJDIR", objdir);
setenv("PWD", objdir, 1); setenv("PWD", objdir, 1);
Dir_InitDot(); Dir_InitDot();
purge_relative_cached_realpaths(); purge_relative_cached_realpaths();
@ -772,7 +771,7 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
static Boolean static Boolean
SetVarObjdir(Boolean writable, const char *var, const char *suffix) SetVarObjdir(Boolean writable, const char *var, const char *suffix)
{ {
FStr path = Var_Value(var, VAR_CMDLINE); FStr path = Var_Value(SCOPE_CMDLINE, var);
FStr xpath; FStr xpath;
if (path.str == NULL || path.str[0] == '\0') { if (path.str == NULL || path.str[0] == '\0') {
@ -784,7 +783,7 @@ SetVarObjdir(Boolean writable, const char *var, const char *suffix)
xpath = FStr_InitRefer(path.str); xpath = FStr_InitRefer(path.str);
if (strchr(path.str, '$') != 0) { if (strchr(path.str, '$') != 0) {
char *expanded; char *expanded;
(void)Var_Subst(path.str, VAR_GLOBAL, VARE_WANTRES, &expanded); (void)Var_Subst(path.str, SCOPE_GLOBAL, VARE_WANTRES, &expanded);
/* TODO: handle errors */ /* TODO: handle errors */
xpath = FStr_InitOwn(expanded); xpath = FStr_InitOwn(expanded);
} }
@ -835,28 +834,23 @@ siginfo(int signo MAKE_ATTR_UNUSED)
static void static void
MakeMode(void) MakeMode(void)
{ {
FStr mode = FStr_InitRefer(NULL); char *mode;
if (mode.str == NULL) { (void)Var_Subst("${" MAKE_MODE ":tl}", SCOPE_GLOBAL, VARE_WANTRES, &mode);
char *expanded; /* TODO: handle errors */
(void)Var_Subst("${" MAKE_MODE ":tl}",
VAR_GLOBAL, VARE_WANTRES, &expanded);
/* TODO: handle errors */
mode = FStr_InitOwn(expanded);
}
if (mode.str[0] != '\0') { if (mode[0] != '\0') {
if (strstr(mode.str, "compat") != NULL) { if (strstr(mode, "compat") != NULL) {
opts.compatMake = TRUE; opts.compatMake = TRUE;
forceJobs = FALSE; forceJobs = FALSE;
} }
#if USE_META #if USE_META
if (strstr(mode.str, "meta") != NULL) if (strstr(mode, "meta") != NULL)
meta_mode_init(mode.str); meta_mode_init(mode);
#endif #endif
} }
FStr_Done(&mode); free(mode);
} }
static void static void
@ -864,7 +858,7 @@ PrintVar(const char *varname, Boolean expandVars)
{ {
if (strchr(varname, '$') != NULL) { if (strchr(varname, '$') != NULL) {
char *evalue; char *evalue;
(void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue); (void)Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES, &evalue);
/* TODO: handle errors */ /* TODO: handle errors */
printf("%s\n", evalue); printf("%s\n", evalue);
bmake_free(evalue); bmake_free(evalue);
@ -872,14 +866,14 @@ PrintVar(const char *varname, Boolean expandVars)
} else if (expandVars) { } else if (expandVars) {
char *expr = str_concat3("${", varname, "}"); char *expr = str_concat3("${", varname, "}");
char *evalue; char *evalue;
(void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &evalue); (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &evalue);
/* TODO: handle errors */ /* TODO: handle errors */
free(expr); free(expr);
printf("%s\n", evalue); printf("%s\n", evalue);
bmake_free(evalue); bmake_free(evalue);
} else { } else {
FStr value = Var_Value(varname, VAR_GLOBAL); FStr value = Var_Value(SCOPE_GLOBAL, varname);
printf("%s\n", value.str != NULL ? value.str : ""); printf("%s\n", value.str != NULL ? value.str : "");
FStr_Done(&value); FStr_Done(&value);
} }
@ -899,7 +893,7 @@ GetBooleanVar(const char *varname, Boolean fallback)
char *value; char *value;
Boolean res; Boolean res;
(void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &value); (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value);
/* TODO: handle errors */ /* TODO: handle errors */
res = ParseBoolean(value, fallback); res = ParseBoolean(value, fallback);
free(value); free(value);
@ -966,7 +960,7 @@ runTargets(void)
Compat_Run(&targs); Compat_Run(&targs);
outOfDate = FALSE; outOfDate = FALSE;
} }
Lst_Done(&targs); /* Don't free the nodes. */ Lst_Done(&targs); /* Don't free the targets themselves. */
return outOfDate; return outOfDate;
} }
@ -981,13 +975,13 @@ InitVarTargets(void)
StringListNode *ln; StringListNode *ln;
if (Lst_IsEmpty(&opts.create)) { if (Lst_IsEmpty(&opts.create)) {
Var_Set(".TARGETS", "", VAR_GLOBAL); Global_Set(".TARGETS", "");
return; return;
} }
for (ln = opts.create.first; ln != NULL; ln = ln->next) { for (ln = opts.create.first; ln != NULL; ln = ln->next) {
char *name = ln->datum; const char *name = ln->datum;
Var_Append(".TARGETS", name, VAR_GLOBAL); Global_Append(".TARGETS", name);
} }
} }
@ -1001,7 +995,7 @@ InitRandom(void)
} }
static const char * static const char *
InitVarMachine(const struct utsname *utsname) InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED)
{ {
#ifdef FORCE_MACHINE #ifdef FORCE_MACHINE
return FORCE_MACHINE; return FORCE_MACHINE;
@ -1062,6 +1056,9 @@ InitVarMachineArch(void)
* All this code is so that we know where we are when we start up * All this code is so that we know where we are when we start up
* on a different machine with pmake. * on a different machine with pmake.
* *
* XXX: Make no longer has "local" and "remote" mode. Is this code still
* necessary?
*
* Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX
* since the value of curdir can vary depending on how we got * since the value of curdir can vary depending on how we got
* here. Ie sitting at a shell prompt (shell that provides $PWD) * here. Ie sitting at a shell prompt (shell that provides $PWD)
@ -1081,13 +1078,13 @@ HandlePWD(const struct stat *curdir_st)
if (ignorePWD || (pwd = getenv("PWD")) == NULL) if (ignorePWD || (pwd = getenv("PWD")) == NULL)
return; return;
prefix = Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE); prefix = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX");
if (prefix.str != NULL) { if (prefix.str != NULL) {
FStr_Done(&prefix); FStr_Done(&prefix);
return; return;
} }
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE); makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR");
if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL)
goto ignore_pwd; goto ignore_pwd;
@ -1146,7 +1143,7 @@ CmdOpts_Init(void)
{ {
opts.compatMake = FALSE; opts.compatMake = FALSE;
opts.debug = DEBUG_NONE; opts.debug = DEBUG_NONE;
/* opts.debug_file has been initialized earlier */ /* opts.debug_file has already been initialized earlier */
opts.strict = FALSE; opts.strict = FALSE;
opts.debugVflag = FALSE; opts.debugVflag = FALSE;
opts.checkEnvFirst = FALSE; opts.checkEnvFirst = FALSE;
@ -1189,8 +1186,8 @@ InitVarMake(const char *argv0)
make = abspath; make = abspath;
} }
Var_Set("MAKE", make, VAR_GLOBAL); Global_Set("MAKE", make);
Var_Set(".MAKE", make, VAR_GLOBAL); Global_Set(".MAKE", make);
} }
/* /*
@ -1223,11 +1220,11 @@ InitDefSysIncPath(char *syspath)
if (strncmp(start, ".../", 4) == 0) { if (strncmp(start, ".../", 4) == 0) {
char *dir = Dir_FindHereOrAbove(curdir, start + 4); char *dir = Dir_FindHereOrAbove(curdir, start + 4);
if (dir != NULL) { if (dir != NULL) {
(void)Dir_AddDir(defSysIncPath, dir); (void)SearchPath_Add(defSysIncPath, dir);
free(dir); free(dir);
} }
} else { } else {
(void)Dir_AddDir(defSysIncPath, start); (void)SearchPath_Add(defSysIncPath, start);
} }
} }
@ -1239,25 +1236,26 @@ static void
ReadBuiltinRules(void) ReadBuiltinRules(void)
{ {
StringListNode *ln; StringListNode *ln;
StringList sysMkPath = LST_INIT; StringList sysMkFiles = LST_INIT;
Dir_Expand(_PATH_DEFSYSMK, SearchPath_Expand(
Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath, Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath,
&sysMkPath); _PATH_DEFSYSMK,
if (Lst_IsEmpty(&sysMkPath)) &sysMkFiles);
if (Lst_IsEmpty(&sysMkFiles))
Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK);
for (ln = sysMkPath.first; ln != NULL; ln = ln->next) for (ln = sysMkFiles.first; ln != NULL; ln = ln->next)
if (ReadMakefile(ln->datum) == 0) if (ReadMakefile(ln->datum) == 0)
break; break;
if (ln == NULL) if (ln == NULL)
Fatal("%s: cannot open %s.", Fatal("%s: cannot open %s.",
progname, (const char *)sysMkPath.first->datum); progname, (const char *)sysMkFiles.first->datum);
/* Free the list but not the actual filenames since these may still /* Free the list nodes but not the actual filenames since these may
* be used in GNodes. */ * still be used in GNodes. */
Lst_Done(&sysMkPath); Lst_Done(&sysMkFiles);
} }
static void static void
@ -1267,10 +1265,10 @@ InitMaxJobs(void)
int n; int n;
if (forceJobs || opts.compatMake || if (forceJobs || opts.compatMake ||
!Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS"))
return; return;
(void)Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES, &value); (void)Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES, &value);
/* TODO: handle errors */ /* TODO: handle errors */
n = (int)strtol(value, NULL, 0); n = (int)strtol(value, NULL, 0);
if (n < 1) { if (n < 1) {
@ -1282,8 +1280,8 @@ InitMaxJobs(void)
} }
if (n != opts.maxJobs) { if (n != opts.maxJobs) {
Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Global_Append(MAKEFLAGS, "-j");
Var_Append(MAKEFLAGS, value, VAR_GLOBAL); Global_Append(MAKEFLAGS, value);
} }
opts.maxJobs = n; opts.maxJobs = n;
@ -1302,10 +1300,10 @@ static void
InitVpath(void) InitVpath(void)
{ {
char *vpath, savec, *path; char *vpath, savec, *path;
if (!Var_Exists("VPATH", VAR_CMDLINE)) if (!Var_Exists(SCOPE_CMDLINE, "VPATH"))
return; return;
(void)Var_Subst("${VPATH}", VAR_CMDLINE, VARE_WANTRES, &vpath); (void)Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES, &vpath);
/* TODO: handle errors */ /* TODO: handle errors */
path = vpath; path = vpath;
do { do {
@ -1317,7 +1315,7 @@ InitVpath(void)
savec = *cp; savec = *cp;
*cp = '\0'; *cp = '\0';
/* Add directory to search path */ /* Add directory to search path */
(void)Dir_AddDir(&dirSearchPath, path); (void)SearchPath_Add(&dirSearchPath, path);
*cp = savec; *cp = savec;
path = cp + 1; path = cp + 1;
} while (savec == ':'); } while (savec == ':');
@ -1343,7 +1341,7 @@ ReadFirstDefaultMakefile(void)
char *prefs; char *prefs;
(void)Var_Subst("${" MAKE_MAKEFILE_PREFERENCE "}", (void)Var_Subst("${" MAKE_MAKEFILE_PREFERENCE "}",
VAR_CMDLINE, VARE_WANTRES, &prefs); SCOPE_CMDLINE, VARE_WANTRES, &prefs);
/* TODO: handle errors */ /* TODO: handle errors */
/* XXX: This should use a local list instead of opts.makefiles /* XXX: This should use a local list instead of opts.makefiles
@ -1412,21 +1410,21 @@ main_Init(int argc, char **argv)
*/ */
Targ_Init(); Targ_Init();
Var_Init(); Var_Init();
Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL); Global_Set(".MAKE.OS", utsname.sysname);
Var_Set("MACHINE", machine, VAR_GLOBAL); Global_Set("MACHINE", machine);
Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); Global_Set("MACHINE_ARCH", machine_arch);
#ifdef MAKE_VERSION #ifdef MAKE_VERSION
Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL); Global_Set("MAKE_VERSION", MAKE_VERSION);
#endif #endif
Var_Set(".newline", "\n", VAR_GLOBAL); /* handy for :@ loops */ Global_Set(".newline", "\n"); /* handy for :@ loops */
/* /*
* This is the traditional preference for makefiles. * This is the traditional preference for makefiles.
*/ */
#ifndef MAKEFILE_PREFERENCE_LIST #ifndef MAKEFILE_PREFERENCE_LIST
# define MAKEFILE_PREFERENCE_LIST "makefile Makefile" # define MAKEFILE_PREFERENCE_LIST "makefile Makefile"
#endif #endif
Var_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, VAR_GLOBAL); Global_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST);
Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); Global_Set(MAKE_DEPENDFILE, ".depend");
CmdOpts_Init(); CmdOpts_Init();
allPrecious = FALSE; /* Remove targets when interrupted */ allPrecious = FALSE; /* Remove targets when interrupted */
@ -1450,12 +1448,12 @@ main_Init(int argc, char **argv)
*/ */
Parse_Init(); Parse_Init();
InitVarMake(argv[0]); InitVarMake(argv[0]);
Var_Set(MAKEFLAGS, "", VAR_GLOBAL); Global_Set(MAKEFLAGS, "");
Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL); Global_Set(MAKEOVERRIDES, "");
Var_Set("MFLAGS", "", VAR_GLOBAL); Global_Set("MFLAGS", "");
Var_Set(".ALLTARGETS", "", VAR_GLOBAL); Global_Set(".ALLTARGETS", "");
/* some makefiles need to know this */ /* some makefiles need to know this */
Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMDLINE); Var_Set(SCOPE_CMDLINE, MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV);
/* Set some other useful variables. */ /* Set some other useful variables. */
{ {
@ -1465,15 +1463,15 @@ main_Init(int argc, char **argv)
if (makelevel < 0) if (makelevel < 0)
makelevel = 0; makelevel = 0;
snprintf(tmp, sizeof tmp, "%d", makelevel); snprintf(tmp, sizeof tmp, "%d", makelevel);
Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL); Global_Set(MAKE_LEVEL, tmp);
snprintf(tmp, sizeof tmp, "%u", myPid); snprintf(tmp, sizeof tmp, "%u", myPid);
Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); Global_Set(".MAKE.PID", tmp);
snprintf(tmp, sizeof tmp, "%u", getppid()); snprintf(tmp, sizeof tmp, "%u", getppid());
Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); Global_Set(".MAKE.PPID", tmp);
snprintf(tmp, sizeof tmp, "%u", getuid()); snprintf(tmp, sizeof tmp, "%u", getuid());
Var_Set(".MAKE.UID", tmp, VAR_GLOBAL); Global_Set(".MAKE.UID", tmp);
snprintf(tmp, sizeof tmp, "%u", getgid()); snprintf(tmp, sizeof tmp, "%u", getgid());
Var_Set(".MAKE.GID", tmp, VAR_GLOBAL); Global_Set(".MAKE.GID", tmp);
} }
if (makelevel > 0) { if (makelevel > 0) {
char pn[1024]; char pn[1024];
@ -1528,7 +1526,7 @@ main_Init(int argc, char **argv)
#ifndef NO_PWD_OVERRIDE #ifndef NO_PWD_OVERRIDE
HandlePWD(&sa); HandlePWD(&sa);
#endif #endif
Var_Set(".CURDIR", curdir, VAR_GLOBAL); Global_Set(".CURDIR", curdir);
InitObjdir(machine, machine_arch); InitObjdir(machine, machine_arch);
@ -1574,7 +1572,7 @@ main_PrepareMaking(void)
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
if (!opts.noBuiltins || opts.printVars == PVM_NONE) { if (!opts.noBuiltins || opts.printVars == PVM_NONE) {
(void)Var_Subst("${.MAKE.DEPENDFILE}", (void)Var_Subst("${.MAKE.DEPENDFILE}",
VAR_CMDLINE, VARE_WANTRES, &makeDependfile); SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile);
if (makeDependfile[0] != '\0') { if (makeDependfile[0] != '\0') {
/* TODO: handle errors */ /* TODO: handle errors */
doing_depend = TRUE; doing_depend = TRUE;
@ -1589,8 +1587,8 @@ main_PrepareMaking(void)
MakeMode(); MakeMode();
{ {
FStr makeflags = Var_Value(MAKEFLAGS, VAR_GLOBAL); FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS);
Var_Append("MFLAGS", makeflags.str, VAR_GLOBAL); Global_Append("MFLAGS", makeflags.str);
FStr_Done(&makeflags); FStr_Done(&makeflags);
} }
@ -1720,7 +1718,7 @@ ReadMakefile(const char *fname)
if (strcmp(fname, "-") == 0) { if (strcmp(fname, "-") == 0) {
Parse_File(NULL /*stdin*/, -1); Parse_File(NULL /*stdin*/, -1);
Var_Set("MAKEFILE", "", VAR_INTERNAL); Var_Set(SCOPE_INTERNAL, "MAKEFILE", "");
} else { } else {
/* if we've chdir'd, rebuild the path name */ /* if we've chdir'd, rebuild the path name */
if (strcmp(curdir, objdir) != 0 && *fname != '/') { if (strcmp(curdir, objdir) != 0 && *fname != '/') {
@ -1747,7 +1745,7 @@ ReadMakefile(const char *fname)
/* look in -I and system include directories. */ /* look in -I and system include directories. */
name = Dir_FindFile(fname, parseIncPath); name = Dir_FindFile(fname, parseIncPath);
if (name == NULL) { if (name == NULL) {
SearchPath *sysInc = Lst_IsEmpty(sysIncPath) SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs)
? defSysIncPath : sysIncPath; ? defSysIncPath : sysIncPath;
name = Dir_FindFile(fname, sysInc); name = Dir_FindFile(fname, sysInc);
} }
@ -1764,14 +1762,14 @@ ReadMakefile(const char *fname)
*/ */
found: found:
if (!doing_depend) if (!doing_depend)
Var_Set("MAKEFILE", fname, VAR_INTERNAL); Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname);
Parse_File(fname, fd); Parse_File(fname, fd);
} }
free(path); free(path);
return 0; return 0;
} }
/*- /*
* Cmd_Exec -- * Cmd_Exec --
* Execute the command in cmd, and return the output of that command * Execute the command in cmd, and return the output of that command
* in a string. In the output, newlines are replaced with spaces. * in a string. In the output, newlines are replaced with spaces.
@ -1824,7 +1822,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
/* /*
* Fork * Fork
*/ */
switch (cpid = vFork()) { switch (cpid = vfork()) {
case 0: case 0:
(void)close(pipefds[0]); /* Close input side of pipe */ (void)close(pipefds[0]); /* Close input side of pipe */
@ -1866,8 +1864,8 @@ Cmd_Exec(const char *cmd, const char **errfmt)
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
JobReapChild(pid, status, FALSE); JobReapChild(pid, status, FALSE);
res_len = Buf_Len(&buf); res_len = buf.len;
res = Buf_Destroy(&buf, FALSE); res = Buf_DoneData(&buf);
if (savederr != 0) if (savederr != 0)
*errfmt = "Couldn't read shell's output for \"%s\""; *errfmt = "Couldn't read shell's output for \"%s\"";
@ -2051,9 +2049,9 @@ execDie(const char *af, const char *av)
Buf_AddStr(&buf, strerror(errno)); Buf_AddStr(&buf, strerror(errno));
Buf_AddStr(&buf, ")\n"); Buf_AddStr(&buf, ")\n");
write_all(STDERR_FILENO, Buf_GetAll(&buf, NULL), Buf_Len(&buf)); write_all(STDERR_FILENO, buf.data, buf.len);
Buf_Destroy(&buf, TRUE); Buf_Done(&buf);
_exit(1); _exit(1);
} }
@ -2122,7 +2120,7 @@ shouldDieQuietly(GNode *gn, int bf)
else else
quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0; quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0;
} }
return quietly; return quietly != 0;
} }
static void static void
@ -2133,15 +2131,15 @@ SetErrorVars(GNode *gn)
/* /*
* We can print this even if there is no .ERROR target. * We can print this even if there is no .ERROR target.
*/ */
Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); Global_Set(".ERROR_TARGET", gn->name);
Var_Delete(".ERROR_CMD", VAR_GLOBAL); Global_Delete(".ERROR_CMD");
for (ln = gn->commands.first; ln != NULL; ln = ln->next) { for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum; const char *cmd = ln->datum;
if (cmd == NULL) if (cmd == NULL)
break; break;
Var_Append(".ERROR_CMD", cmd, VAR_GLOBAL); Global_Append(".ERROR_CMD", cmd);
} }
} }
@ -2176,7 +2174,7 @@ PrintOnError(GNode *gn, const char *msg)
{ {
char *errorVarsValues; char *errorVarsValues;
(void)Var_Subst("${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", (void)Var_Subst("${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}",
VAR_GLOBAL, VARE_WANTRES, &errorVarsValues); SCOPE_GLOBAL, VARE_WANTRES, &errorVarsValues);
/* TODO: handle errors */ /* TODO: handle errors */
printf("%s", errorVarsValues); printf("%s", errorVarsValues);
free(errorVarsValues); free(errorVarsValues);
@ -2206,7 +2204,7 @@ Main_ExportMAKEFLAGS(Boolean first)
once = FALSE; once = FALSE;
expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
(void)Var_Subst(expr, VAR_CMDLINE, VARE_WANTRES, &s); (void)Var_Subst(expr, SCOPE_CMDLINE, VARE_WANTRES, &s);
/* TODO: handle errors */ /* TODO: handle errors */
if (s[0] != '\0') { if (s[0] != '\0') {
#ifdef POSIX #ifdef POSIX
@ -2228,7 +2226,7 @@ getTmpdir(void)
/* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */ /* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */
(void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/",
VAR_GLOBAL, VARE_WANTRES, &tmpdir); SCOPE_GLOBAL, VARE_WANTRES, &tmpdir);
/* TODO: handle errors */ /* TODO: handle errors */
if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) {
@ -2244,27 +2242,29 @@ getTmpdir(void)
* Otherwise unlink the file once open. * Otherwise unlink the file once open.
*/ */
int int
mkTempFile(const char *pattern, char **out_fname) mkTempFile(const char *pattern, char *tfile, size_t tfile_sz)
{ {
static char *tmpdir = NULL; static char *tmpdir = NULL;
char tfile[MAXPATHLEN]; char tbuf[MAXPATHLEN];
int fd; int fd;
if (pattern == NULL) if (pattern == NULL)
pattern = TMPPAT; pattern = TMPPAT;
if (tmpdir == NULL) if (tmpdir == NULL)
tmpdir = getTmpdir(); tmpdir = getTmpdir();
if (tfile == NULL) {
tfile = tbuf;
tfile_sz = sizeof tbuf;
}
if (pattern[0] == '/') { if (pattern[0] == '/') {
snprintf(tfile, sizeof tfile, "%s", pattern); snprintf(tfile, tfile_sz, "%s", pattern);
} else { } else {
snprintf(tfile, sizeof tfile, "%s%s", tmpdir, pattern); snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern);
} }
if ((fd = mkstemp(tfile)) < 0) if ((fd = mkstemp(tfile)) < 0)
Punt("Could not create temporary file %s: %s", tfile, Punt("Could not create temporary file %s: %s", tfile,
strerror(errno)); strerror(errno));
if (out_fname != NULL) { if (tfile == tbuf) {
*out_fname = bmake_strdup(tfile);
} else {
unlink(tfile); /* we just want the descriptor */ unlink(tfile); /* we just want the descriptor */
} }
return fd; return fd;

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $ .\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -99,7 +99,7 @@ is equivalent to
.It Fl D Ar variable .It Fl D Ar variable
Define Define
.Ar variable .Ar variable
to be 1, in the global context. to be 1, in the global scope.
.It Fl d Ar [-]flags .It Fl d Ar [-]flags
Turn on debugging, and specify which portions of Turn on debugging, and specify which portions of
.Nm .Nm
@ -355,7 +355,7 @@ Do not build any targets.
Multiple instances of this option may be specified; Multiple instances of this option may be specified;
the variables will be printed one per line, the variables will be printed one per line,
with a blank line for each null or undefined variable. with a blank line for each null or undefined variable.
The value printed is extracted from the global context after all The value printed is extracted from the global scope after all
makefiles have been read. makefiles have been read.
By default, the raw variable contents (which may By default, the raw variable contents (which may
include additional unexpanded variable references) are shown. include additional unexpanded variable references) are shown.

View File

@ -1,4 +1,4 @@
/* $NetBSD: make.c,v 1.234 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -78,8 +78,8 @@
* Make_Update After a target is made, update all its parents. * Make_Update After a target is made, update all its parents.
* Perform various bookkeeping chores like the updating * Perform various bookkeeping chores like the updating
* of the youngestChild field of the parent, filling * of the youngestChild field of the parent, filling
* of the IMPSRC context variable, etc. Place the parent * of the IMPSRC variable, etc. Place the parent on the
* on the toBeMade queue if it should be. * toBeMade queue if it should be.
* *
* GNode_UpdateYoungestChild * GNode_UpdateYoungestChild
* Update the node's youngestChild field based on the * Update the node's youngestChild field based on the
@ -103,7 +103,7 @@
#include "job.h" #include "job.h"
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: make.c,v 1.234 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $");
/* Sequence # to detect recursion. */ /* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1; static unsigned int checked_seqno = 1;
@ -137,10 +137,6 @@ make_abort(GNode *gn, int lineno)
abort(); abort();
} }
ENUM_VALUE_RTTI_8(GNodeMade,
UNMADE, DEFERRED, REQUESTED, BEINGMADE,
MADE, UPTODATE, ERROR, ABORTED);
ENUM_FLAGS_RTTI_31(GNodeType, ENUM_FLAGS_RTTI_31(GNodeType,
OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP, OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP,
/* OP_OPMASK is omitted since it combines other flags */ /* OP_OPMASK is omitted since it combines other flags */
@ -152,10 +148,10 @@ ENUM_FLAGS_RTTI_31(GNodeType,
OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV, OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV,
OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK); OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK);
ENUM_FLAGS_RTTI_10(GNodeFlags, ENUM_FLAGS_RTTI_9(GNodeFlags,
REMAKE, CHILDMADE, FORCE, DONE_WAIT, REMAKE, CHILDMADE, FORCE, DONE_WAIT,
DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE, DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE,
DONECYCLE, INTERNAL); DONECYCLE);
void void
GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
@ -164,13 +160,11 @@ GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
char type_buf[GNodeType_ToStringSize]; char type_buf[GNodeType_ToStringSize];
char flags_buf[GNodeFlags_ToStringSize]; char flags_buf[GNodeFlags_ToStringSize];
fprintf(f, "%smade %s, type %s, flags %s%s", fprintf(f, "%s%s, type %s, flags %s%s",
prefix, prefix,
Enum_ValueToString(gn->made, GNodeMade_ToStringSpecs), GNodeMade_Name(gn->made),
Enum_FlagsToString(type_buf, sizeof type_buf, GNodeType_ToString(type_buf, gn->type),
gn->type, GNodeType_ToStringSpecs), GNodeFlags_ToString(flags_buf, gn->flags),
Enum_FlagsToString(flags_buf, sizeof flags_buf,
gn->flags, GNodeFlags_ToStringSpecs),
suffix); suffix);
} }
@ -574,9 +568,9 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname)
for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) { for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) {
GNode *pgn = ln->datum; GNode *pgn = ln->datum;
if (pgn->flags & REMAKE) { if (pgn->flags & REMAKE) {
Var_Set(IMPSRC, cname, pgn); Var_Set(pgn, IMPSRC, cname);
if (cpref != NULL) if (cpref != NULL)
Var_Set(PREFIX, cpref, pgn); Var_Set(pgn, PREFIX, cpref);
} }
} }
} }
@ -601,7 +595,7 @@ IsWaitingForOrder(GNode *gn)
return FALSE; return FALSE;
} }
static int MakeBuildParent(GNode *, GNodeListNode *); static void MakeBuildParent(GNode *, GNodeListNode *);
static void static void
ScheduleOrderSuccessors(GNode *gn) ScheduleOrderSuccessors(GNode *gn)
@ -610,8 +604,7 @@ ScheduleOrderSuccessors(GNode *gn)
GNodeListNode *ln; GNodeListNode *ln;
for (ln = gn->order_succ.first; ln != NULL; ln = ln->next) for (ln = gn->order_succ.first; ln != NULL; ln = ln->next)
if (MakeBuildParent(ln->datum, toBeMadeNext) != 0) MakeBuildParent(ln->datum, toBeMadeNext);
break;
} }
/* /*
@ -810,50 +803,53 @@ UnmarkChildren(GNode *gn)
static void static void
MakeAddAllSrc(GNode *cgn, GNode *pgn) MakeAddAllSrc(GNode *cgn, GNode *pgn)
{ {
const char *child, *allsrc;
if (cgn->type & OP_MARK) if (cgn->type & OP_MARK)
return; return;
cgn->type |= OP_MARK; cgn->type |= OP_MARK;
if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE | OP_INVISIBLE))) { if (cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE | OP_INVISIBLE))
const char *child, *allsrc; return;
if (cgn->type & OP_ARCHV) if (cgn->type & OP_ARCHV)
child = GNode_VarMember(cgn); child = GNode_VarMember(cgn);
else else
child = GNode_Path(cgn); child = GNode_Path(cgn);
if (cgn->type & OP_JOIN) {
allsrc = GNode_VarAllsrc(cgn); if (cgn->type & OP_JOIN)
} else { allsrc = GNode_VarAllsrc(cgn);
allsrc = child; else
} allsrc = child;
if (allsrc != NULL)
Var_Append(ALLSRC, allsrc, pgn); if (allsrc != NULL)
if (pgn->type & OP_JOIN) { Var_Append(pgn, ALLSRC, allsrc);
if (cgn->made == MADE) {
Var_Append(OODATE, child, pgn); if (pgn->type & OP_JOIN) {
} if (cgn->made == MADE)
} else if ((pgn->mtime < cgn->mtime) || Var_Append(pgn, OODATE, child);
(cgn->mtime >= now && cgn->made == MADE)) {
/* } else if ((pgn->mtime < cgn->mtime) ||
* It goes in the OODATE variable if the parent is (cgn->mtime >= now && cgn->made == MADE)) {
* younger than the child or if the child has been /*
* modified more recently than the start of the make. * It goes in the OODATE variable if the parent is
* This is to keep pmake from getting confused if * younger than the child or if the child has been
* something else updates the parent after the make * modified more recently than the start of the make.
* starts (shouldn't happen, I know, but sometimes it * This is to keep pmake from getting confused if
* does). In such a case, if we've updated the child, * something else updates the parent after the make
* the parent is likely to have a modification time * starts (shouldn't happen, I know, but sometimes it
* later than that of the child and anything that * does). In such a case, if we've updated the child,
* relies on the OODATE variable will be hosed. * the parent is likely to have a modification time
* * later than that of the child and anything that
* XXX: This will cause all made children to go in * relies on the OODATE variable will be hosed.
* the OODATE variable, even if they're not touched, *
* if RECHECK isn't defined, since cgn->mtime is set * XXX: This will cause all made children to go in
* to now in Make_Update. According to some people, * the OODATE variable, even if they're not touched,
* this is good... * if RECHECK isn't defined, since cgn->mtime is set
*/ * to now in Make_Update. According to some people,
Var_Append(OODATE, child, pgn); * this is good...
} */
Var_Append(pgn, OODATE, child);
} }
} }
@ -883,17 +879,17 @@ Make_DoAllVar(GNode *gn)
for (ln = gn->children.first; ln != NULL; ln = ln->next) for (ln = gn->children.first; ln != NULL; ln = ln->next)
MakeAddAllSrc(ln->datum, gn); MakeAddAllSrc(ln->datum, gn);
if (!Var_Exists(OODATE, gn)) if (!Var_Exists(gn, OODATE))
Var_Set(OODATE, "", gn); Var_Set(gn, OODATE, "");
if (!Var_Exists(ALLSRC, gn)) if (!Var_Exists(gn, ALLSRC))
Var_Set(ALLSRC, "", gn); Var_Set(gn, ALLSRC, "");
if (gn->type & OP_JOIN) if (gn->type & OP_JOIN)
Var_Set(TARGET, GNode_VarAllsrc(gn), gn); Var_Set(gn, TARGET, GNode_VarAllsrc(gn));
gn->flags |= DONE_ALLSRC; gn->flags |= DONE_ALLSRC;
} }
static int static Boolean
MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
{ {
@ -903,13 +899,13 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
GNode_FprintDetails(opts.debug_file, "", cn, "\n"); GNode_FprintDetails(opts.debug_file, "", cn, "\n");
} }
if (GNode_IsReady(cn)) if (GNode_IsReady(cn))
return 0; return FALSE;
/* If this node is on the RHS of a .ORDER, check LHSs. */ /* If this node is on the RHS of a .ORDER, check LHSs. */
if (IsWaitingForOrder(cn)) { if (IsWaitingForOrder(cn)) {
/* Can't build this (or anything else in this child list) yet */ /* Can't build this (or anything else in this child list) yet */
cn->made = DEFERRED; cn->made = DEFERRED;
return 0; /* but keep looking */ return FALSE; /* but keep looking */
} }
DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n", DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n",
@ -925,7 +921,7 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
ListNode *ln; ListNode *ln;
for (ln = cn->cohorts.first; ln != NULL; ln = ln->next) for (ln = cn->cohorts.first; ln != NULL; ln = ln->next)
if (MakeBuildChild(ln->datum, toBeMadeNext) != 0) if (MakeBuildChild(ln->datum, toBeMadeNext))
break; break;
} }
@ -937,18 +933,16 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
} }
/* When a .ORDER LHS node completes, we do this on each RHS. */ /* When a .ORDER LHS node completes, we do this on each RHS. */
static int static void
MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext) MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext)
{ {
if (pn->made != DEFERRED) if (pn->made != DEFERRED)
return 0; return;
if (MakeBuildChild(pn, toBeMadeNext) == 0) { if (!MakeBuildChild(pn, toBeMadeNext)) {
/* When this node is built, reschedule its parents. */ /* When this node is built, reschedule its parents. */
pn->flags |= DONE_ORDER; pn->flags |= DONE_ORDER;
} }
return 0;
} }
static void static void
@ -958,7 +952,7 @@ MakeChildren(GNode *gn)
GNodeListNode *ln; GNodeListNode *ln;
for (ln = gn->children.first; ln != NULL; ln = ln->next) for (ln = gn->children.first; ln != NULL; ln = ln->next)
if (MakeBuildChild(ln->datum, toBeMadeNext) != 0) if (MakeBuildChild(ln->datum, toBeMadeNext))
break; break;
} }
@ -1038,9 +1032,9 @@ MakeStartJobs(void)
if (gn->type & OP_JOIN) { if (gn->type & OP_JOIN) {
/* /*
* Even for an up-to-date .JOIN node, we * Even for an up-to-date .JOIN node, we
* need it to have its context variables so * need it to have its local variables so
* references to it get the correct value * references to it get the correct value
* for .TARGET when building up the context * for .TARGET when building up the local
* variables of its parent(s)... * variables of its parent(s)...
*/ */
Make_DoAllVar(gn); Make_DoAllVar(gn);
@ -1243,14 +1237,14 @@ Make_ExpandUse(GNodeList *targs)
continue; continue;
*eoa = '\0'; *eoa = '\0';
*eon = '\0'; *eon = '\0';
Var_Set(MEMBER, eoa + 1, gn); Var_Set(gn, MEMBER, eoa + 1);
Var_Set(ARCHIVE, gn->name, gn); Var_Set(gn, ARCHIVE, gn->name);
*eoa = '('; *eoa = '(';
*eon = ')'; *eon = ')';
} }
Dir_UpdateMTime(gn, FALSE); Dir_UpdateMTime(gn, FALSE);
Var_Set(TARGET, GNode_Path(gn), gn); Var_Set(gn, TARGET, GNode_Path(gn));
UnmarkChildren(gn); UnmarkChildren(gn);
HandleUseNodes(gn); HandleUseNodes(gn);

View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.242 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: make.h,v 1.256 2021/02/05 19:19:17 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -72,7 +72,7 @@
* from: @(#)make.h 8.3 (Berkeley) 6/13/95 * from: @(#)make.h 8.3 (Berkeley) 6/13/95
*/ */
/*- /*
* make.h -- * make.h --
* The global definitions for pmake * The global definitions for pmake
*/ */
@ -350,25 +350,23 @@ typedef enum GNodeType {
typedef enum GNodeFlags { typedef enum GNodeFlags {
GNF_NONE = 0, GNF_NONE = 0,
/* this target needs to be (re)made */ /* this target needs to be (re)made */
REMAKE = 0x0001, REMAKE = 1 << 0,
/* children of this target were made */ /* children of this target were made */
CHILDMADE = 0x0002, CHILDMADE = 1 << 1,
/* children don't exist, and we pretend made */ /* children don't exist, and we pretend made */
FORCE = 0x0004, FORCE = 1 << 2,
/* Set by Make_ProcessWait() */ /* Set by Make_ProcessWait() */
DONE_WAIT = 0x0008, DONE_WAIT = 1 << 3,
/* Build requested by .ORDER processing */ /* Build requested by .ORDER processing */
DONE_ORDER = 0x0010, DONE_ORDER = 1 << 4,
/* Node created from .depend */ /* Node created from .depend */
FROM_DEPEND = 0x0020, FROM_DEPEND = 1 << 5,
/* We do it once only */ /* We do it once only */
DONE_ALLSRC = 0x0040, DONE_ALLSRC = 1 << 6,
/* Used by MakePrintStatus */ /* Used by MakePrintStatus */
CYCLE = 0x1000, CYCLE = 1 << 12,
/* Used by MakePrintStatus */ /* Used by MakePrintStatus */
DONECYCLE = 0x2000, DONECYCLE = 1 << 13
/* Internal use only */
INTERNAL = 0x4000
} GNodeFlags; } GNodeFlags;
typedef struct List StringList; typedef struct List StringList;
@ -377,7 +375,9 @@ typedef struct ListNode StringListNode;
typedef struct List GNodeList; typedef struct List GNodeList;
typedef struct ListNode GNodeListNode; typedef struct ListNode GNodeListNode;
typedef struct List /* of CachedDir */ SearchPath; typedef struct SearchPath {
List /* of CachedDir */ dirs;
} SearchPath;
/* /*
* A graph node represents a target that can possibly be made, including its * A graph node represents a target that can possibly be made, including its
@ -429,7 +429,10 @@ typedef struct GNode {
* this node, in the normal sense. */ * this node, in the normal sense. */
GNodeList order_succ; GNodeList order_succ;
/* Other nodes of the same name, for the '::' dependency operator. */ /*
* Other nodes of the same name, for targets that were defined using
* the '::' dependency operator (OP_DOUBLEDEP).
*/
GNodeList cohorts; GNodeList cohorts;
/* The "#n" suffix for this cohort, or "" for other nodes */ /* The "#n" suffix for this cohort, or "" for other nodes */
char cohort_num[8]; char cohort_num[8];
@ -442,11 +445,14 @@ typedef struct GNode {
/* Last time (sequence number) we tried to make this node */ /* Last time (sequence number) we tried to make this node */
unsigned int checked_seqno; unsigned int checked_seqno;
/* The "local" variables that are specific to this target and this /*
* The "local" variables that are specific to this target and this
* target only, such as $@, $<, $?. * target only, such as $@, $<, $?.
* *
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE, * Also used for the global variable scopes SCOPE_GLOBAL,
* VAR_INTERNAL, which contain variables with arbitrary names. */ * SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with
* arbitrary names.
*/
HashTable /* of Var pointer */ vars; HashTable /* of Var pointer */ vars;
/* The commands to be given to a shell to create this target. */ /* The commands to be given to a shell to create this target. */
@ -509,11 +515,11 @@ extern GNode *defaultNode;
* Variables defined internally by make which should not override those set * Variables defined internally by make which should not override those set
* by makefiles. * by makefiles.
*/ */
extern GNode *VAR_INTERNAL; extern GNode *SCOPE_INTERNAL;
/* Variables defined in a global context, e.g in the Makefile itself. */ /* Variables defined in a global scope, e.g in the makefile itself. */
extern GNode *VAR_GLOBAL; extern GNode *SCOPE_GLOBAL;
/* Variables defined on the command line. */ /* Variables defined on the command line. */
extern GNode *VAR_CMDLINE; extern GNode *SCOPE_CMDLINE;
/* /*
* Value returned by Var_Parse when an error is encountered. It actually * Value returned by Var_Parse when an error is encountered. It actually
@ -532,8 +538,8 @@ extern SearchPath dirSearchPath;
/* Used for .include "...". */ /* Used for .include "...". */
extern SearchPath *parseIncPath; extern SearchPath *parseIncPath;
/* /*
* Used for .include <...>, for the built-in sys.mk and makefiles from the * Used for .include <...>, for the built-in sys.mk and for makefiles from
* command line arguments. * the command line arguments.
*/ */
extern SearchPath *sysIncPath; extern SearchPath *sysIncPath;
/* The default for sysIncPath. */ /* The default for sysIncPath. */
@ -543,18 +549,12 @@ extern SearchPath *defSysIncPath;
extern char curdir[]; extern char curdir[];
/* The basename of the program name, suffixed with [n] for sub-makes. */ /* The basename of the program name, suffixed with [n] for sub-makes. */
extern const char *progname; extern const char *progname;
extern int makelevel;
/* Name of the .depend makefile */ /* Name of the .depend makefile */
extern char *makeDependfile; extern char *makeDependfile;
/* If we replaced environ, this will be non-NULL. */ /* If we replaced environ, this will be non-NULL. */
extern char **savedEnv; extern char **savedEnv;
extern int makelevel;
/*
* We cannot vfork() in a child of vfork().
* Most systems do not enforce this but some do.
*/
#define vFork() ((getpid() == myPid) ? vfork() : fork())
extern pid_t myPid; extern pid_t myPid;
#define MAKEFLAGS ".MAKEFLAGS" #define MAKEFLAGS ".MAKEFLAGS"
@ -606,7 +606,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
do { \ do { \
if (DEBUG(module)) \ if (DEBUG(module)) \
debug_printf args; \ debug_printf args; \
} while (/*CONSTCOND*/ 0) } while (/*CONSTCOND*/FALSE)
#define DEBUG0(module, text) \ #define DEBUG0(module, text) \
DEBUG_IMPL(module, ("%s", text)) DEBUG_IMPL(module, ("%s", text))
@ -671,11 +671,13 @@ typedef struct CmdOpts {
/* -n: execute almost no commands from the targets */ /* -n: execute almost no commands from the targets */
Boolean noExecute; Boolean noExecute;
/* -q: if true, we aren't supposed to really make anything, just see /*
* if the targets are out-of-date */ * -q: if true, do not really make anything, just see if the targets
* are out-of-date
*/
Boolean queryFlag; Boolean queryFlag;
/* -r: raw mode, without loading the builtin rules. */ /* -r: raw mode, do not load the builtin rules. */
Boolean noBuiltins; Boolean noBuiltins;
/* -s: don't echo the shell commands before executing them */ /* -s: don't echo the shell commands before executing them */
@ -693,7 +695,7 @@ typedef struct CmdOpts {
/* -W: if true, makefile parsing warnings are treated as errors */ /* -W: if true, makefile parsing warnings are treated as errors */
Boolean parseWarnFatal; Boolean parseWarnFatal;
/* -w: print Entering and Leaving for submakes */ /* -w: print 'Entering' and 'Leaving' for submakes */
Boolean enterFlag; Boolean enterFlag;
/* -X: if true, do not export variables set on the command line to the /* -X: if true, do not export variables set on the command line to the
@ -722,7 +724,7 @@ Boolean shouldDieQuietly(GNode *, int);
void PrintOnError(GNode *, const char *); void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(Boolean); void Main_ExportMAKEFLAGS(Boolean);
Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
int mkTempFile(const char *, char **); int mkTempFile(const char *, char *, size_t);
int str2Lst_Append(StringList *, char *); int str2Lst_Append(StringList *, char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
Boolean GNode_ShouldExecute(GNode *gn); Boolean GNode_ShouldExecute(GNode *gn);
@ -765,19 +767,19 @@ GNode_IsError(const GNode *gn)
} }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); } GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); } GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarAllsrc(GNode *gn) { return Var_ValueDirect(ALLSRC, gn); } GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); } GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarPrefix(GNode *gn) { return Var_ValueDirect(PREFIX, gn); } GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); } GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); } GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#ifdef __GNUC__ #ifdef __GNUC__
#define UNCONST(ptr) ({ \ #define UNCONST(ptr) ({ \
@ -851,18 +853,18 @@ pp_skip_hspace(char **pp)
} }
#if defined(lint) #if defined(lint)
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) # define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
#elif defined(MAKE_NATIVE) #elif defined(MAKE_NATIVE)
# include <sys/cdefs.h> # include <sys/cdefs.h>
# define MAKE_RCSID(id) __RCSID(id) # define MAKE_RCSID(id) __RCSID(id)
#elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
# define MAKE_RCSID(id) static volatile char \ # define MAKE_RCSID(id) static volatile char \
MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id
#elif defined(MAKE_ALL_IN_ONE) #elif defined(MAKE_ALL_IN_ONE)
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) # define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
#else #else
# define MAKE_RCSID(id) static volatile char rcsid[] = id # define MAKE_RCSID(id) static volatile char rcsid[] = id
#endif #endif
#endif /* MAKE_MAKE_H */ #endif /* MAKE_MAKE_H */

View File

@ -1,6 +1,6 @@
/* $NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $ */ /* $NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2009 The NetBSD Foundation, Inc. * Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -30,7 +30,7 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $"); MAKE_RCSID("$NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $");
#ifndef USE_EMALLOC #ifndef USE_EMALLOC

View File

@ -1,6 +1,6 @@
/* $NetBSD: make_malloc.h,v 1.15 2020/12/30 10:03:16 rillig Exp $ */ /* $NetBSD: make_malloc.h,v 1.16 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2009 The NetBSD Foundation, Inc. * Copyright (c) 2009 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *

View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.168 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: meta.c,v 1.177 2021/02/05 19:19:17 sjg Exp $ */
/* /*
* Implement 'meta' mode. * Implement 'meta' mode.
@ -144,7 +144,10 @@ meta_open_filemon(BuildMon *pbm)
* cwd causing getcwd to do a lot more work. * cwd causing getcwd to do a lot more work.
* We only care about the descriptor. * We only care about the descriptor.
*/ */
pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); if (!opts.compatMake)
pbm->mon_fd = Job_TempFile("filemon.XXXXXX", NULL, 0);
else
pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0);
if ((dupfd = dup(pbm->mon_fd)) == -1) { if ((dupfd = dup(pbm->mon_fd)) == -1) {
err(1, "Could not dup filemon output!"); err(1, "Could not dup filemon output!");
} }
@ -327,7 +330,7 @@ is_submake(const char *cmd, GNode *gn)
Boolean rc = FALSE; Boolean rc = FALSE;
if (p_make == NULL) { if (p_make == NULL) {
p_make = Var_Value(".MAKE", gn).str; p_make = Var_Value(gn, ".MAKE").str;
p_len = strlen(p_make); p_len = strlen(p_make);
} }
cp = strchr(cmd, '$'); cp = strchr(cmd, '$');
@ -407,7 +410,7 @@ printCMDs(GNode *gn, FILE *fp)
} \ } \
return FALSE; \ return FALSE; \
} \ } \
} while (/*CONSTCOND*/0) } while (/*CONSTCOND*/FALSE)
/* /*
@ -483,7 +486,7 @@ meta_create(BuildMon *pbm, GNode *gn)
fp = NULL; fp = NULL;
dname = Var_Value(".OBJDIR", gn); dname = Var_Value(gn, ".OBJDIR");
tname = GNode_VarTarget(gn); tname = GNode_VarTarget(gn);
/* if this succeeds objdir_realpath is realpath of dname */ /* if this succeeds objdir_realpath is realpath of dname */
@ -538,8 +541,8 @@ meta_create(BuildMon *pbm, GNode *gn)
fprintf(fp, "-- command output --\n"); fprintf(fp, "-- command output --\n");
fflush(fp); fflush(fp);
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Global_Append(".MAKE.META.FILES", fname);
Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); Global_Append(".MAKE.META.CREATED", fname);
gn->type |= OP_META; /* in case anyone wants to know */ gn->type |= OP_META; /* in case anyone wants to know */
if (metaSilent) { if (metaSilent) {
@ -573,7 +576,7 @@ meta_init(void)
{ {
#ifdef USE_FILEMON #ifdef USE_FILEMON
/* this allows makefiles to test if we have filemon support */ /* this allows makefiles to test if we have filemon support */
Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL); Global_Set(".MAKE.PATH_FILEMON", filemon_path());
#endif #endif
} }
@ -613,14 +616,15 @@ meta_mode_init(const char *make_mode)
get_mode_bf(metaMissing, "missing-meta="); get_mode_bf(metaMissing, "missing-meta=");
get_mode_bf(metaSilent, "silent="); get_mode_bf(metaSilent, "silent=");
} }
if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { if (metaVerbose && !Var_Exists(SCOPE_GLOBAL, MAKE_META_PREFIX)) {
/* /*
* The default value for MAKE_META_PREFIX * The default value for MAKE_META_PREFIX
* prints the absolute path of the target. * prints the absolute path of the target.
* This works be cause :H will generate '.' if there is no / * This works be cause :H will generate '.' if there is no /
* and :tA will resolve that to cwd. * and :tA will resolve that to cwd.
*/ */
Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL); Global_Set(MAKE_META_PREFIX,
"Building ${.TARGET:H:tA}/${.TARGET:T}");
} }
if (once) if (once)
return; return;
@ -630,28 +634,28 @@ meta_mode_init(const char *make_mode)
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK} * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/ */
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr); SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
/* TODO: handle errors */ /* TODO: handle errors */
str2Lst_Append(&metaBailiwick, metaBailiwickStr); str2Lst_Append(&metaBailiwick, metaBailiwickStr);
/* /*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/ */
Var_Append(MAKE_META_IGNORE_PATHS, Global_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}");
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
/* TODO: handle errors */ /* TODO: handle errors */
str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr); str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
/* /*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
*/ */
value = Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL); value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
if (value.str != NULL) { if (value.str != NULL) {
metaIgnorePatterns = TRUE; metaIgnorePatterns = TRUE;
FStr_Done(&value); FStr_Done(&value);
} }
value = Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL); value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
if (value.str != NULL) { if (value.str != NULL) {
metaIgnoreFilter = TRUE; metaIgnoreFilter = TRUE;
FStr_Done(&value); FStr_Done(&value);
@ -786,13 +790,12 @@ meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status)
fprintf(pbm->mfp, "\n*** Error code %d%s\n", fprintf(pbm->mfp, "\n*** Error code %d%s\n",
status, ignerr ? "(ignored)" : ""); status, ignerr ? "(ignored)" : "");
} }
if (gn != NULL) { if (gn != NULL)
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL); Global_Set(".ERROR_TARGET", GNode_Path(gn));
}
getcwd(cwd, sizeof cwd); getcwd(cwd, sizeof cwd);
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL); Global_Set(".ERROR_CWD", cwd);
if (pbm->meta_fname[0] != '\0') { if (pbm->meta_fname[0] != '\0') {
Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL); Global_Set(".ERROR_META_FILE", pbm->meta_fname);
} }
meta_job_finish(job); meta_job_finish(job);
} }
@ -816,7 +819,7 @@ meta_job_output(Job *job, char *cp, const char *nl)
char *cp2; char *cp2;
(void)Var_Subst("${" MAKE_META_PREFIX "}", (void)Var_Subst("${" MAKE_META_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &meta_prefix); SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix);
/* TODO: handle errors */ /* TODO: handle errors */
if ((cp2 = strchr(meta_prefix, '$')) != NULL) if ((cp2 = strchr(meta_prefix, '$')) != NULL)
meta_prefix_len = (size_t)(cp2 - meta_prefix); meta_prefix_len = (size_t)(cp2 - meta_prefix);
@ -993,7 +996,13 @@ meta_ignore(GNode *gn, const char *p)
const char *expr; const char *expr;
char *pm; char *pm;
Var_Set(".p.", p, gn); /*
* XXX: This variable is set on a target GNode but is not one of
* the usual local variables. It should be deleted afterwards.
* Ideally it would not be created in the first place, just like
* in a .for loop.
*/
Var_Set(gn, ".p.", p);
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm); (void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
/* TODO: handle errors */ /* TODO: handle errors */
@ -1093,7 +1102,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (oodate) if (oodate)
return oodate; /* we're done */ return oodate; /* we're done */
dname = Var_Value(".OBJDIR", gn); dname = Var_Value(gn, ".OBJDIR");
tname = GNode_VarTarget(gn); tname = GNode_VarTarget(gn);
/* if this succeeds fname3 is realpath of dname */ /* if this succeeds fname3 is realpath of dname */
@ -1146,7 +1155,7 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
/* we want to track all the .meta we read */ /* we want to track all the .meta we read */
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Global_Append(".MAKE.META.FILES", fname);
cmdNode = gn->commands.first; cmdNode = gn->commands.first;
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
@ -1198,7 +1207,7 @@ meta_oodate(GNode *gn, Boolean oodate)
/* /*
* We need to track pathnames per-process. * We need to track pathnames per-process.
* *
* Each process run by make, starts off in the 'CWD' * Each process run by make starts off in the 'CWD'
* recorded in the .meta file, if it chdirs ('C') * recorded in the .meta file, if it chdirs ('C')
* elsewhere we need to track that - but only for * elsewhere we need to track that - but only for
* that process. If it forks ('F'), we initialize * that process. If it forks ('F'), we initialize
@ -1221,18 +1230,18 @@ meta_oodate(GNode *gn, Boolean oodate)
if (lastpid > 0) { if (lastpid > 0) {
/* We need to remember these. */ /* We need to remember these. */
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); Global_SetExpand(lcwd_vname, lcwd);
Var_Set(ldir_vname, latestdir, VAR_GLOBAL); Global_SetExpand(ldir_vname, latestdir);
} }
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid); snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid); snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
lastpid = pid; lastpid = pid;
ldir = Var_Value(ldir_vname, VAR_GLOBAL); ldir = Var_Value(SCOPE_GLOBAL, ldir_vname);
if (ldir.str != NULL) { if (ldir.str != NULL) {
strlcpy(latestdir, ldir.str, sizeof latestdir); strlcpy(latestdir, ldir.str, sizeof latestdir);
FStr_Done(&ldir); FStr_Done(&ldir);
} }
ldir = Var_Value(lcwd_vname, VAR_GLOBAL); ldir = Var_Value(SCOPE_GLOBAL, lcwd_vname);
if (ldir.str != NULL) { if (ldir.str != NULL) {
strlcpy(lcwd, ldir.str, sizeof lcwd); strlcpy(lcwd, ldir.str, sizeof lcwd);
FStr_Done(&ldir); FStr_Done(&ldir);
@ -1255,8 +1264,8 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Process according to record type. */ /* Process according to record type. */
switch (buf[0]) { switch (buf[0]) {
case 'X': /* eXit */ case 'X': /* eXit */
Var_Delete(lcwd_vname, VAR_GLOBAL); Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname);
Var_Delete(ldir_vname, VAR_GLOBAL); Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname);
lastpid = 0; /* no need to save ldir_vname */ lastpid = 0; /* no need to save ldir_vname */
break; break;
@ -1268,9 +1277,9 @@ meta_oodate(GNode *gn, Boolean oodate)
child = atoi(p); child = atoi(p);
if (child > 0) { if (child > 0) {
snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child); snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child);
Var_Set(cldir, lcwd, VAR_GLOBAL); Global_SetExpand(cldir, lcwd);
snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child); snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child);
Var_Set(cldir, latestdir, VAR_GLOBAL); Global_SetExpand(cldir, latestdir);
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
if (DEBUG(META)) if (DEBUG(META))
debug_printf( debug_printf(
@ -1286,8 +1295,8 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Update lcwd and latest directory. */ /* Update lcwd and latest directory. */
strlcpy(latestdir, p, sizeof latestdir); strlcpy(latestdir, p, sizeof latestdir);
strlcpy(lcwd, p, sizeof lcwd); strlcpy(lcwd, p, sizeof lcwd);
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); Global_SetExpand(lcwd_vname, lcwd);
Var_Set(ldir_vname, lcwd, VAR_GLOBAL); Global_SetExpand(ldir_vname, lcwd);
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n", DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
fname, lineno, cwd, lcwd); fname, lineno, cwd, lcwd);
@ -1605,8 +1614,8 @@ meta_oodate(GNode *gn, Boolean oodate)
* We have decided it is oodate, so .OODATE needs to be set. * We have decided it is oodate, so .OODATE needs to be set.
* All we can sanely do is set it to .ALLSRC. * All we can sanely do is set it to .ALLSRC.
*/ */
Var_Delete(OODATE, gn); Var_Delete(gn, OODATE);
Var_Set(OODATE, GNode_VarAllsrc(gn), gn); Var_Set(gn, OODATE, GNode_VarAllsrc(gn));
} }
oodate_out: oodate_out:
@ -1699,7 +1708,7 @@ meta_compat_parent(pid_t child)
fflush(stdout); fflush(stdout);
buf[nread] = '\0'; buf[nread] = '\0';
meta_job_output(NULL, buf, ""); meta_job_output(NULL, buf, "");
} while (/*CONSTCOND*/0); } while (/*CONSTCOND*/FALSE);
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) { if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0) if (meta_job_event(NULL) <= 0)
metafd = -1; metafd = -1;

View File

@ -1,6 +1,6 @@
/* $NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $ */ /* $NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2015 The NetBSD Foundation, Inc. * Copyright (c) 2015 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -39,7 +39,7 @@
#include "metachar.h" #include "metachar.h"
MAKE_RCSID("$NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $"); MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $");
/* /*
* The following array is used to make a fast determination of which * The following array is used to make a fast determination of which
@ -82,4 +82,3 @@ unsigned char _metachar[128] = {
/* x y z { | } ~ del */ /* x y z { | } ~ del */
0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
}; };

View File

@ -1,6 +1,6 @@
/* $NetBSD: metachar.h,v 1.13 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: metachar.h,v 1.15 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2015 The NetBSD Foundation, Inc. * Copyright (c) 2015 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -37,7 +37,7 @@ extern unsigned char _metachar[];
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0) #define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
MAKE_INLINE int MAKE_INLINE Boolean
needshell(const char *cmd) needshell(const char *cmd)
{ {
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=') while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')

View File

@ -1,3 +1,12 @@
2021-01-30 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210130
* dirdeps.mk: expr 2 - 1 - 1 exits with a bad status
we need to guard against this in DIRDEP_LOADAVG_REPORT.
* dirdeps.mk: restore respect for TARGET_MACHINE
2021-01-06 Simon J Gerraty <sjg@beast.crufty.net> 2021-01-06 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210101 * install-mk (MK_VERSION): 20210101

View File

@ -1,4 +1,4 @@
# $Id: dirdeps.mk,v 1.131 2021/01/07 00:57:51 sjg Exp $ # $Id: dirdeps.mk,v 1.133 2021/01/31 04:39:22 sjg Exp $
# Copyright (c) 2010-2021, Simon J. Gerraty # Copyright (c) 2010-2021, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc. # Copyright (c) 2010-2018, Juniper Networks, Inc.
@ -265,8 +265,8 @@ N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
# if we were included recursively _DEP_TARGET_SPEC should be valid. # if we were included recursively _DEP_TARGET_SPEC should be valid.
.if empty(_DEP_TARGET_SPEC) .if empty(_DEP_TARGET_SPEC)
# if not, just use TARGET_SPEC DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}}
_DEP_TARGET_SPEC := ${TARGET_SPEC} _DEP_TARGET_SPEC := ${DEP_TARGET_SPEC}
.if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" .if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != ""
# record that we've read dependfile for this # record that we've read dependfile for this
_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: _dirdeps_checked.${_CURDIR}.${TARGET_SPEC}:
@ -386,8 +386,10 @@ DIRDEP_LOADAVG_LAST = 0
# yes the expression here is a bit complicated, # yes the expression here is a bit complicated,
# the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}} # the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}}
# when we want to report. # when we want to report.
# Note: expr(1) will exit 1 if the expression evaluates to 0
# hence the || true
DIRDEP_LOADAVG_REPORT = \ DIRDEP_LOADAVG_REPORT = \
test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST}:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \
echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`" echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`"
# we suppress SUBDIR when visiting the leaves # we suppress SUBDIR when visiting the leaves

View File

@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net> # Simon J. Gerraty <sjg@crufty.net>
# RCSid: # RCSid:
# $Id: install-mk,v 1.190 2021/01/07 00:58:42 sjg Exp $ # $Id: install-mk,v 1.191 2021/01/30 23:16:42 sjg Exp $
# #
# @(#) Copyright (c) 1994 Simon J. Gerraty # @(#) Copyright (c) 1994 Simon J. Gerraty
# #
@ -70,7 +70,7 @@
# sjg@crufty.net # sjg@crufty.net
# #
MK_VERSION=20210101 MK_VERSION=20210130
OWNER= OWNER=
GROUP= GROUP=
MODE=444 MODE=444

View File

@ -1,4 +1,4 @@
# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $ # $Id: meta.stage.mk,v 1.61 2021/01/31 04:43:12 sjg Exp $
# #
# @(#) Copyright (c) 2011-2017, Simon J. Gerraty # @(#) Copyright (c) 2011-2017, Simon J. Gerraty
# #
@ -30,8 +30,11 @@ _dirdep ?= ${RELDIR}
CLEANFILES+= .dirdep CLEANFILES+= .dirdep
# this allows us to trace dependencies back to their src dir # this allows us to trace dependencies back to their src dir
.dirdep: .NOPATH .dirdep: .NOPATH
.if !commands(.dirdep)
.dirdep:
@echo '${_dirdep}' > $@ @echo '${_dirdep}' > $@
.endif
.if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == "" .if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == ""
_stage_file_basename = `basename $$f` _stage_file_basename = `basename $$f`

View File

@ -1,6 +1,6 @@
/* $NetBSD: nonints.h,v 1.186 2020/12/28 00:46:24 rillig Exp $ */ /* $NetBSD: nonints.h,v 1.202 2021/02/05 05:15:12 rillig Exp $ */
/*- /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
* *
@ -34,7 +34,7 @@
* from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
*/ */
/*- /*
* Copyright (c) 1989 by Berkeley Softworks * Copyright (c) 1989 by Berkeley Softworks
* All rights reserved. * All rights reserved.
* *
@ -107,7 +107,11 @@ str_basename(const char *pathname)
MAKE_INLINE SearchPath * MAKE_INLINE SearchPath *
SearchPath_New(void) SearchPath_New(void)
{ return Lst_New(); } {
SearchPath *path = bmake_malloc(sizeof *path);
Lst_Init(&path->dirs);
return path;
}
void SearchPath_Free(SearchPath *); void SearchPath_Free(SearchPath *);
@ -282,6 +286,7 @@ const char *Targ_FmtTime(time_t);
void Targ_PrintType(int); void Targ_PrintType(int);
void Targ_PrintGraph(int); void Targ_PrintGraph(int);
void Targ_Propagate(void); void Targ_Propagate(void);
const char *GNodeMade_Name(GNodeMade);
/* var.c */ /* var.c */
void Var_Init(void); void Var_Init(void);
@ -290,26 +295,28 @@ void Var_End(void);
typedef enum VarEvalFlags { typedef enum VarEvalFlags {
VARE_NONE = 0, VARE_NONE = 0,
/* Expand and evaluate variables during parsing. /*
* Expand and evaluate variables during parsing.
* *
* TODO: Document what Var_Parse and Var_Subst return when this flag * TODO: Document what Var_Parse and Var_Subst return when this flag
* is not set. */ * is not set.
*/
VARE_WANTRES = 1 << 0, VARE_WANTRES = 1 << 0,
/* Treat undefined variables as errors. /*
* Must only be used in combination with VARE_WANTRES. */ * Treat undefined variables as errors.
* Must only be used in combination with VARE_WANTRES.
*/
VARE_UNDEFERR = 1 << 1, VARE_UNDEFERR = 1 << 1,
/* Keep '$$' as '$$' instead of reducing it to a single '$'. /*
* Keep '$$' as '$$' instead of reducing it to a single '$'.
* *
* Used in variable assignments using the ':=' operator. It allows * Used in variable assignments using the ':=' operator. It allows
* multiple such assignments to be chained without accidentally * multiple such assignments to be chained without accidentally
* expanding '$$file' to '$file' in the first assignment and * expanding '$$file' to '$file' in the first assignment and
* interpreting it as '${f}' followed by 'ile' in the next assignment. * interpreting it as '${f}' followed by 'ile' in the next assignment.
* */
* See also preserveUndefined, which preserves subexpressions that are
* based on undefined variables; maybe that can be converted to a flag
* as well. */
VARE_KEEP_DOLLAR = 1 << 2, VARE_KEEP_DOLLAR = 1 << 2,
/* /*
@ -369,15 +376,19 @@ typedef enum VarExportMode {
VEM_LITERAL VEM_LITERAL
} VarExportMode; } VarExportMode;
void Var_DeleteVar(const char *, GNode *); void Var_Delete(GNode *, const char *);
void Var_Delete(const char *, GNode *); void Var_DeleteExpand(GNode *, const char *);
void Var_Undef(const char *); void Var_Undef(const char *);
void Var_Set(const char *, const char *, GNode *); void Var_Set(GNode *, const char *, const char *);
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags); void Var_SetExpand(GNode *, const char *, const char *);
void Var_Append(const char *, const char *, GNode *); void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
Boolean Var_Exists(const char *, GNode *); void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags);
FStr Var_Value(const char *, GNode *); void Var_Append(GNode *, const char *, const char *);
const char *Var_ValueDirect(const char *, GNode *); void Var_AppendExpand(GNode *, const char *, const char *);
Boolean Var_Exists(GNode *, const char *);
Boolean Var_ExistsExpand(GNode *, const char *);
FStr Var_Value(GNode *, const char *);
const char *GNode_ValueDirect(GNode *, const char *);
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *); VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *);
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **); VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
void Var_Stats(void); void Var_Stats(void);
@ -387,6 +398,11 @@ void Var_Export(VarExportMode, const char *);
void Var_ExportVars(const char *); void Var_ExportVars(const char *);
void Var_UnExport(Boolean, const char *); void Var_UnExport(Boolean, const char *);
void Global_Set(const char *, const char *);
void Global_SetExpand(const char *, const char *);
void Global_Append(const char *, const char *);
void Global_Delete(const char *);
/* util.c */ /* util.c */
typedef void (*SignalProc)(int); typedef void (*SignalProc)(int);
SignalProc bmake_signal(int, SignalProc); SignalProc bmake_signal(int, SignalProc);

View File

@ -1,4 +1,4 @@
/* $NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -84,7 +84,7 @@
* Parse_End Clean up the module * Parse_End Clean up the module
* *
* Parse_File Parse a top-level makefile. Included files are * Parse_File Parse a top-level makefile. Included files are
* handled by Parse_include_file though. * handled by IncludeFile instead.
* *
* Parse_IsVar Return TRUE if the given line is a variable * Parse_IsVar Return TRUE if the given line is a variable
* assignment. Used by MainParseArgs to determine if * assignment. Used by MainParseArgs to determine if
@ -124,7 +124,7 @@
#include "pathnames.h" #include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $");
/* types and constants */ /* types and constants */
@ -240,44 +240,11 @@ static int fatals = 0;
*/ */
/* /*
* The include chain of makefiles. At the bottom is the top-level makefile * The include chain of makefiles. At index 0 is the top-level makefile from
* from the command line, and on top of that, there are the included files or * the command line, followed by the included files or .for loops, up to and
* .for loops, up to and including the current file. * including the current file.
* *
* This data could be used to print stack traces on parse errors. As of * See PrintStackTrace for how to interpret the data.
* 2020-09-14, this is not done though. It seems quite simple to print the
* tuples (fname:lineno:fromForLoop), from top to bottom. This simple idea is
* made complicated by the fact that the .for loops also use this stack for
* storing information.
*
* The lineno fields of the IFiles with fromForLoop == TRUE look confusing,
* which is demonstrated by the test 'include-main.mk'. They seem sorted
* backwards since they tell the number of completely parsed lines, which for
* a .for loop is right after the terminating .endfor. To compensate for this
* confusion, there is another field first_lineno pointing at the start of the
* .for loop, 1-based for human consumption.
*
* To make the stack trace intuitive, the entry below the first .for loop must
* be ignored completely since neither its lineno nor its first_lineno is
* useful. Instead, the topmost of each chain of .for loop needs to be
* printed twice, once with its first_lineno and once with its lineno.
*
* As of 2020-10-28, using the above rules, the stack trace for the .info line
* in include-subsub.mk would be:
*
* includes[5]: include-subsub.mk:4
* (lineno, from an .include)
* includes[4]: include-sub.mk:32
* (lineno, from a .for loop below an .include)
* includes[4]: include-sub.mk:31
* (first_lineno, from a .for loop, lineno == 32)
* includes[3]: include-sub.mk:30
* (first_lineno, from a .for loop, lineno == 33)
* includes[2]: include-sub.mk:29
* (first_lineno, from a .for loop, lineno == 34)
* includes[1]: include-sub.mk:35
* (not printed since it is below a .for loop)
* includes[0]: include-main.mk:27
*/ */
static Vector /* of IFile */ includes; static Vector /* of IFile */ includes;
@ -295,8 +262,8 @@ CurFile(void)
} }
/* include paths */ /* include paths */
SearchPath *parseIncPath; /* dirs for "..." includes */ SearchPath *parseIncPath; /* directories for "..." includes */
SearchPath *sysIncPath; /* dirs for <...> includes */ SearchPath *sysIncPath; /* directories for <...> includes */
SearchPath *defSysIncPath; /* default for sysIncPath */ SearchPath *defSysIncPath; /* default for sysIncPath */
/* parser tables */ /* parser tables */
@ -503,12 +470,61 @@ loadfile(const char *path, int fd)
{ {
struct loadedfile *lf = loadedfile_create(path, struct loadedfile *lf = loadedfile_create(path,
buf.data, buf.len); buf.data, buf.len);
Buf_Destroy(&buf, FALSE); Buf_DoneData(&buf);
return lf; return lf;
} }
} }
/* old code */ static void
PrintStackTrace(void)
{
const IFile *entries;
size_t i, n;
if (!(DEBUG(PARSE)))
return;
entries = GetInclude(0);
n = includes.len;
if (n == 0)
return;
n--; /* This entry is already in the diagnostic. */
/*
* For the IFiles with fromForLoop, lineno seems to be sorted
* backwards. This is because lineno is the number of completely
* parsed lines, which for a .for loop is right after the
* corresponding .endfor. The intuitive line number comes from
* first_lineno instead, which points at the start of the .for loop.
*
* To make the stack trace intuitive, the entry below each chain of
* .for loop entries must be ignored completely since neither its
* lineno nor its first_lineno is useful. Instead, the topmost of
* each chain of .for loop entries needs to be printed twice, once
* with its first_lineno and once with its lineno.
*/
for (i = n; i-- > 0;) {
const IFile *entry = entries + i;
const char *fname = entry->fname;
Boolean printLineno;
char dirbuf[MAXPATHLEN + 1];
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
fname = realpath(fname, dirbuf);
printLineno = !entry->fromForLoop;
if (i + 1 < n && entries[i + 1].fromForLoop == printLineno)
printLineno = entry->fromForLoop;
if (printLineno)
debug_printf("\tin .include from %s:%d\n",
fname, entry->lineno);
if (entry->fromForLoop)
debug_printf("\tin .for loop from %s:%d\n",
fname, entry->first_lineno);
}
}
/* Check if the current character is escaped on the current line. */ /* Check if the current character is escaped on the current line. */
static Boolean static Boolean
@ -575,13 +591,13 @@ PrintLocation(FILE *f, const char *fname, size_t lineno)
/* Find out which makefile is the culprit. /* Find out which makefile is the culprit.
* We try ${.PARSEDIR} and apply realpath(3) if not absolute. */ * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */
dir = Var_Value(".PARSEDIR", VAR_GLOBAL); dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR");
if (dir.str == NULL) if (dir.str == NULL)
dir.str = "."; dir.str = ".";
if (dir.str[0] != '/') if (dir.str[0] != '/')
dir.str = realpath(dir.str, dirbuf); dir.str = realpath(dir.str, dirbuf);
base = Var_Value(".PARSEFILE", VAR_GLOBAL); base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE");
if (base.str == NULL) if (base.str == NULL)
base.str = str_basename(fname); base.str = str_basename(fname);
@ -609,13 +625,17 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
(void)fflush(f); (void)fflush(f);
if (type == PARSE_INFO) if (type == PARSE_INFO)
return; goto print_stack_trace;
if (type == PARSE_FATAL || opts.parseWarnFatal) if (type == PARSE_WARNING && !opts.parseWarnFatal)
fatals++; goto print_stack_trace;
if (opts.parseWarnFatal && !fatal_warning_error_printed) { fatals++;
if (type == PARSE_WARNING && !fatal_warning_error_printed) {
Error("parsing warnings being treated as errors"); Error("parsing warnings being treated as errors");
fatal_warning_error_printed = TRUE; fatal_warning_error_printed = TRUE;
} }
print_stack_trace:
PrintStackTrace();
} }
static void static void
@ -676,7 +696,7 @@ Parse_Error(ParseErrorLevel type, const char *fmt, ...)
/* /*
* Parse and handle a .info, .warning or .error directive. * Parse and handle an .info, .warning or .error directive.
* For an .error directive, immediately exit. * For an .error directive, immediately exit.
*/ */
static void static void
@ -690,7 +710,7 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
return; return;
} }
(void)Var_Subst(umsg, VAR_CMDLINE, VARE_WANTRES, &xmsg); (void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg);
/* TODO: handle errors */ /* TODO: handle errors */
Parse_Error(level, "%s", xmsg); Parse_Error(level, "%s", xmsg);
@ -867,11 +887,9 @@ static void
ParseDependencySourceMain(const char *src) ParseDependencySourceMain(const char *src)
{ {
/* /*
* In a line like ".MAIN: source1 source2", it means we need to add * In a line like ".MAIN: source1 source2", add all sources to the
* the sources of said target to the list of things to create. * list of things to create, but only if the user didn't specify a
* * target on the command line and .MAIN occurs for the first time.
* Note that this will only be invoked if the user didn't specify a
* target on the command line and the .MAIN occurs for the first time.
* *
* See ParseDoDependencyTargetSpecial, branch SP_MAIN. * See ParseDoDependencyTargetSpecial, branch SP_MAIN.
* See unit-tests/cond-func-make-main.mk. * See unit-tests/cond-func-make-main.mk.
@ -881,7 +899,7 @@ ParseDependencySourceMain(const char *src)
* Add the name to the .TARGETS variable as well, so the user can * Add the name to the .TARGETS variable as well, so the user can
* employ that, if desired. * employ that, if desired.
*/ */
Var_Append(".TARGETS", src, VAR_GLOBAL); Global_Append(".TARGETS", src);
} }
static void static void
@ -918,12 +936,11 @@ ParseDependencySourceOther(const char *src, GNodeType tOp,
GNode *gn; GNode *gn;
/* /*
* If the source is not an attribute, we need to find/create * The source is not an attribute, so find/create a node for it.
* a node for it. After that we can apply any operator to it * After that, apply any operator to it from a special target or
* from a special target or link it to its parents, as * link it to its parents, as appropriate.
* appropriate.
* *
* In the case of a source that was the object of a :: operator, * In the case of a source that was the object of a '::' operator,
* the attribute is applied to all of its instances (as kept in * the attribute is applied to all of its instances (as kept in
* the 'cohorts' list of the node) or all the cohorts are linked * the 'cohorts' list of the node) or all the cohorts are linked
* to all the targets. * to all the targets.
@ -1013,7 +1030,7 @@ ParseErrorNoDependency(const char *lstart)
Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
(int)(dirend - dirstart), dirstart); (int)(dirend - dirstart), dirstart);
} else } else
Parse_Error(PARSE_FATAL, "Need an operator"); Parse_Error(PARSE_FATAL, "Invalid line type");
} }
static void static void
@ -1040,7 +1057,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
const char *nested_p = cp; const char *nested_p = cp;
FStr nested_val; FStr nested_val;
(void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_NONE, (void)Var_Parse(&nested_p, SCOPE_CMDLINE, VARE_NONE,
&nested_val); &nested_val);
/* TODO: handle errors */ /* TODO: handle errors */
FStr_Done(&nested_val); FStr_Done(&nested_val);
@ -1055,7 +1072,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
/* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */ /* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */
static void static void
ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType, ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
const char *line, /* XXX: bad name */ const char *targetName,
SearchPathList **inout_paths) SearchPathList **inout_paths)
{ {
switch (*inout_specType) { switch (*inout_specType) {
@ -1077,7 +1094,7 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
case SP_STALE: case SP_STALE:
case SP_ERROR: case SP_ERROR:
case SP_INTERRUPT: { case SP_INTERRUPT: {
GNode *gn = Targ_GetNode(line); GNode *gn = Targ_GetNode(targetName);
if (doing_depend) if (doing_depend)
ParseMark(gn); ParseMark(gn);
gn->type |= OP_NOTMAIN | OP_SPECIAL; gn->type |= OP_NOTMAIN | OP_SPECIAL;
@ -1120,15 +1137,15 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
* Call on the suffix module to give us a path to modify. * Call on the suffix module to give us a path to modify.
*/ */
static Boolean static Boolean
ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */ ParseDoDependencyTargetPath(const char *suffixName,
SearchPathList **inout_paths) SearchPathList **inout_paths)
{ {
SearchPath *path; SearchPath *path;
path = Suff_GetPath(&line[5]); path = Suff_GetPath(suffixName);
if (path == NULL) { if (path == NULL) {
Parse_Error(PARSE_FATAL, Parse_Error(PARSE_FATAL,
"Suffix '%s' not defined (yet)", &line[5]); "Suffix '%s' not defined (yet)", suffixName);
return FALSE; return FALSE;
} }
@ -1143,20 +1160,20 @@ ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */
* See if it's a special target and if so set specType to match it. * See if it's a special target and if so set specType to match it.
*/ */
static Boolean static Boolean
ParseDoDependencyTarget(const char *line, /* XXX: bad name */ ParseDoDependencyTarget(const char *targetName,
ParseSpecial *inout_specType, ParseSpecial *inout_specType,
GNodeType *out_tOp, SearchPathList **inout_paths) GNodeType *out_tOp, SearchPathList **inout_paths)
{ {
int keywd; int keywd;
if (!(line[0] == '.' && ch_isupper(line[1]))) if (!(targetName[0] == '.' && ch_isupper(targetName[1])))
return TRUE; return TRUE;
/* /*
* See if the target is a special target that must have it * See if the target is a special target that must have it
* or its sources handled specially. * or its sources handled specially.
*/ */
keywd = ParseFindKeyword(line); keywd = ParseFindKeyword(targetName);
if (keywd != -1) { if (keywd != -1) {
if (*inout_specType == SP_PATH && if (*inout_specType == SP_PATH &&
parseKeywords[keywd].spec != SP_PATH) { parseKeywords[keywd].spec != SP_PATH) {
@ -1167,22 +1184,21 @@ ParseDoDependencyTarget(const char *line, /* XXX: bad name */
*inout_specType = parseKeywords[keywd].spec; *inout_specType = parseKeywords[keywd].spec;
*out_tOp = parseKeywords[keywd].op; *out_tOp = parseKeywords[keywd].op;
ParseDoDependencyTargetSpecial(inout_specType, line, ParseDoDependencyTargetSpecial(inout_specType, targetName,
inout_paths); inout_paths);
} else if (strncmp(line, ".PATH", 5) == 0) { } else if (strncmp(targetName, ".PATH", 5) == 0) {
*inout_specType = SP_PATH; *inout_specType = SP_PATH;
if (!ParseDoDependencyTargetPath(line, inout_paths)) if (!ParseDoDependencyTargetPath(targetName + 5, inout_paths))
return FALSE; return FALSE;
} }
return TRUE; return TRUE;
} }
static void static void
ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */ ParseDoDependencyTargetMundane(char *targetName, StringList *curTargs)
StringList *curTargs)
{ {
if (Dir_HasWildcards(line)) { if (Dir_HasWildcards(targetName)) {
/* /*
* Targets are to be sought only in the current directory, * Targets are to be sought only in the current directory,
* so create an empty path for the thing. Note we need to * so create an empty path for the thing. Note we need to
@ -1191,7 +1207,7 @@ ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */
*/ */
SearchPath *emptyPath = SearchPath_New(); SearchPath *emptyPath = SearchPath_New();
Dir_Expand(line, emptyPath, curTargs); SearchPath_Expand(emptyPath, targetName, curTargs);
SearchPath_Free(emptyPath); SearchPath_Free(emptyPath);
} else { } else {
@ -1199,7 +1215,7 @@ ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */
* No wildcards, but we want to avoid code duplication, * No wildcards, but we want to avoid code duplication,
* so create a list with the word on it. * so create a list with the word on it.
*/ */
Lst_Append(curTargs, line); Lst_Append(curTargs, targetName);
} }
/* Apply the targets. */ /* Apply the targets. */
@ -1323,7 +1339,7 @@ ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
break; break;
#ifdef POSIX #ifdef POSIX
case SP_POSIX: case SP_POSIX:
Var_Set("%POSIX", "1003.2", VAR_GLOBAL); Global_Set("%POSIX", "1003.2");
break; break;
#endif #endif
default: default:
@ -1337,7 +1353,7 @@ AddToPaths(const char *dir, SearchPathList *paths)
if (paths != NULL) { if (paths != NULL) {
SearchPathListNode *ln; SearchPathListNode *ln;
for (ln = paths->first; ln != NULL; ln = ln->next) for (ln = paths->first; ln != NULL; ln = ln->next)
(void)Dir_AddDir(ln->datum, dir); (void)SearchPath_Add(ln->datum, dir);
} }
} }
@ -1440,7 +1456,7 @@ ParseDoDependencyTargets(char **inout_cp,
* there was an error in the specification. On error, * there was an error in the specification. On error,
* line should remain untouched. * line should remain untouched.
*/ */
if (!Arch_ParseArchive(&tgt, targets, VAR_CMDLINE)) { if (!Arch_ParseArchive(&tgt, targets, SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL, Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"", "Error in archive specification: \"%s\"",
tgt); tgt);
@ -1545,7 +1561,8 @@ ParseDoDependencySourcesMundane(char *start, char *end,
if (*end == '(') { if (*end == '(') {
GNodeList sources = LST_INIT; GNodeList sources = LST_INIT;
if (!Arch_ParseArchive(&start, &sources, VAR_CMDLINE)) { if (!Arch_ParseArchive(&start, &sources,
SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL, Parse_Error(PARSE_FATAL,
"Error in source archive spec \"%s\"", "Error in source archive spec \"%s\"",
start); start);
@ -1866,13 +1883,13 @@ Parse_IsVar(const char *p, VarAssign *out_var)
* Check for syntax errors such as unclosed expressions or unknown modifiers. * Check for syntax errors such as unclosed expressions or unknown modifiers.
*/ */
static void static void
VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt) VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
{ {
if (opts.strict) { if (opts.strict) {
if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) { if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
char *expandedValue; char *expandedValue;
(void)Var_Subst(uvalue, ctxt, VARE_NONE, (void)Var_Subst(uvalue, scope, VARE_NONE,
&expandedValue); &expandedValue);
/* TODO: handle errors */ /* TODO: handle errors */
free(expandedValue); free(expandedValue);
@ -1881,31 +1898,32 @@ VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
} }
static void static void
VarAssign_EvalSubst(const char *name, const char *uvalue, GNode *ctxt, VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
FStr *out_avalue) FStr *out_avalue)
{ {
const char *avalue;
char *evalue; char *evalue;
/* /*
* make sure that we set the variable the first time to nothing * make sure that we set the variable the first time to nothing
* so that it gets substituted! * so that it gets substituted.
*
* TODO: Add a test that demonstrates why this code is needed,
* apart from making the debug log longer.
*/ */
if (!Var_Exists(name, ctxt)) if (!Var_ExistsExpand(scope, name))
Var_Set(name, "", ctxt); Var_SetExpand(scope, name, "");
(void)Var_Subst(uvalue, ctxt, (void)Var_Subst(uvalue, scope,
VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue); VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue);
/* TODO: handle errors */ /* TODO: handle errors */
avalue = evalue; Var_SetExpand(scope, name, evalue);
Var_Set(name, avalue, ctxt);
*out_avalue = (FStr){ avalue, evalue }; *out_avalue = FStr_InitOwn(evalue);
} }
static void static void
VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope,
FStr *out_avalue) FStr *out_avalue)
{ {
FStr cmd; FStr cmd;
@ -1915,14 +1933,14 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt,
cmd = FStr_InitRefer(uvalue); cmd = FStr_InitRefer(uvalue);
if (strchr(cmd.str, '$') != NULL) { if (strchr(cmd.str, '$') != NULL) {
char *expanded; char *expanded;
(void)Var_Subst(cmd.str, VAR_CMDLINE, (void)Var_Subst(cmd.str, SCOPE_CMDLINE,
VARE_WANTRES | VARE_UNDEFERR, &expanded); VARE_WANTRES | VARE_UNDEFERR, &expanded);
/* TODO: handle errors */ /* TODO: handle errors */
cmd = FStr_InitOwn(expanded); cmd = FStr_InitOwn(expanded);
} }
cmdOut = Cmd_Exec(cmd.str, &errfmt); cmdOut = Cmd_Exec(cmd.str, &errfmt);
Var_Set(name, cmdOut, ctxt); Var_SetExpand(scope, name, cmdOut);
*out_avalue = FStr_InitOwn(cmdOut); *out_avalue = FStr_InitOwn(cmdOut);
if (errfmt != NULL) if (errfmt != NULL)
@ -1934,31 +1952,32 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt,
/* /*
* Perform a variable assignment. * Perform a variable assignment.
* *
* The actual value of the variable is returned in *out_avalue and * The actual value of the variable is returned in *out_TRUE_avalue.
* *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal
* from the literal value. * value.
* *
* Return whether the assignment was actually done. The assignment is only * Return whether the assignment was actually performed, which is usually
* skipped if the operator is '?=' and the variable already exists. * the case. It is only skipped if the operator is '?=' and the variable
* already exists.
*/ */
static Boolean static Boolean
VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue,
GNode *ctxt, FStr *out_TRUE_avalue) GNode *scope, FStr *out_TRUE_avalue)
{ {
FStr avalue = FStr_InitRefer(uvalue); FStr avalue = FStr_InitRefer(uvalue);
if (op == VAR_APPEND) if (op == VAR_APPEND)
Var_Append(name, uvalue, ctxt); Var_AppendExpand(scope, name, uvalue);
else if (op == VAR_SUBST) else if (op == VAR_SUBST)
VarAssign_EvalSubst(name, uvalue, ctxt, &avalue); VarAssign_EvalSubst(scope, name, uvalue, &avalue);
else if (op == VAR_SHELL) else if (op == VAR_SHELL)
VarAssign_EvalShell(name, uvalue, ctxt, &avalue); VarAssign_EvalShell(name, uvalue, scope, &avalue);
else { else {
if (op == VAR_DEFAULT && Var_Exists(name, ctxt)) if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name))
return FALSE; return FALSE;
/* Normal assignment -- just do it. */ /* Normal assignment -- just do it. */
Var_Set(name, uvalue, ctxt); Var_SetExpand(scope, name, uvalue);
} }
*out_TRUE_avalue = avalue; *out_TRUE_avalue = avalue;
@ -1984,14 +2003,14 @@ VarAssignSpecial(const char *name, const char *avalue)
Var_ExportVars(avalue); Var_ExportVars(avalue);
} }
/* Perform the variable variable assignment in the given context. */ /* Perform the variable variable assignment in the given scope. */
void void
Parse_DoVar(VarAssign *var, GNode *ctxt) Parse_DoVar(VarAssign *var, GNode *scope)
{ {
FStr avalue; /* actual value (maybe expanded) */ FStr avalue; /* actual value (maybe expanded) */
VarCheckSyntax(var->op, var->value, ctxt); VarCheckSyntax(var->op, var->value, scope);
if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, &avalue)) { if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) {
VarAssignSpecial(var->varname, avalue.str); VarAssignSpecial(var->varname, avalue.str);
FStr_Done(&avalue); FStr_Done(&avalue);
} }
@ -2086,7 +2105,7 @@ ParseAddCmd(GNode *gn, char *cmd)
void void
Parse_AddIncludeDir(const char *dir) Parse_AddIncludeDir(const char *dir)
{ {
(void)Dir_AddDir(parseIncPath, dir); (void)SearchPath_Add(parseIncPath, dir);
} }
/* /*
@ -2100,7 +2119,7 @@ Parse_AddIncludeDir(const char *dir)
* line options. * line options.
*/ */
static void static void
Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent) IncludeFile(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
{ {
struct loadedfile *lf; struct loadedfile *lf;
char *fullname; /* full pathname of file */ char *fullname; /* full pathname of file */
@ -2175,8 +2194,8 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
/* /*
* Look for it on the system path * Look for it on the system path
*/ */
SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)
: sysIncPath; ? defSysIncPath : sysIncPath;
fullname = Dir_FindFile(file, path); fullname = Dir_FindFile(file, path);
} }
@ -2206,12 +2225,12 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
} }
static void static void
ParseDoInclude(char *line /* XXX: bad name */) ParseDoInclude(char *directive)
{ {
char endc; /* the character which ends the file spec */ char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */ char *cp; /* current position in file spec */
Boolean silent = line[0] != 'i'; Boolean silent = directive[0] != 'i';
char *file = line + (silent ? 8 : 7); char *file = directive + (silent ? 8 : 7);
/* Skip to delimiter character so we know where to look */ /* Skip to delimiter character so we know where to look */
pp_skip_hspace(&file); pp_skip_hspace(&file);
@ -2248,10 +2267,10 @@ ParseDoInclude(char *line /* XXX: bad name */)
* Substitute for any variables in the filename before trying to * Substitute for any variables in the filename before trying to
* find the file. * find the file.
*/ */
(void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file); (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &file);
/* TODO: handle errors */ /* TODO: handle errors */
Parse_include_file(file, endc == '>', line[0] == 'd', silent); IncludeFile(file, endc == '>', directive[0] == 'd', silent);
free(file); free(file);
} }
@ -2275,8 +2294,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
basename = slash + 1; basename = slash + 1;
} }
Var_Set(dirvar, dirname, VAR_GLOBAL); Global_SetExpand(dirvar, dirname);
Var_Set(filevar, basename, VAR_GLOBAL); Global_SetExpand(filevar, basename);
DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n", DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
__func__, dirvar, dirname, filevar, basename); __func__, dirvar, dirname, filevar, basename);
@ -2314,8 +2333,8 @@ ParseSetParseFile(const char *filename)
SetFilenameVars(including, SetFilenameVars(including,
".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE");
} else { } else {
Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); Global_Delete(".INCLUDEDFROMDIR");
Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); Global_Delete(".INCLUDEDFROMFILE");
} }
} }
@ -2352,7 +2371,7 @@ StrContainsWord(const char *str, const char *word)
static Boolean static Boolean
VarContainsWord(const char *varname, const char *word) VarContainsWord(const char *varname, const char *word)
{ {
FStr val = Var_Value(varname, VAR_GLOBAL); FStr val = Var_Value(SCOPE_GLOBAL, varname);
Boolean found = val.str != NULL && StrContainsWord(val.str, word); Boolean found = val.str != NULL && StrContainsWord(val.str, word);
FStr_Done(&val); FStr_Done(&val);
return found; return found;
@ -2369,7 +2388,7 @@ static void
ParseTrackInput(const char *name) ParseTrackInput(const char *name)
{ {
if (!VarContainsWord(MAKE_MAKEFILES, name)) if (!VarContainsWord(MAKE_MAKEFILES, name))
Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL); Global_Append(MAKE_MAKEFILES, name);
} }
@ -2486,7 +2505,7 @@ ParseTraditionalInclude(char *line)
* Substitute for any variables in the file name before trying to * Substitute for any variables in the file name before trying to
* find the thing. * find the thing.
*/ */
(void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files); (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files);
/* TODO: handle errors */ /* TODO: handle errors */
if (*file == '\0') { if (*file == '\0') {
@ -2504,7 +2523,7 @@ ParseTraditionalInclude(char *line)
else else
done = TRUE; done = TRUE;
Parse_include_file(file, FALSE, FALSE, silent); IncludeFile(file, FALSE, FALSE, silent);
} }
out: out:
free(all_files); free(all_files);
@ -2536,7 +2555,7 @@ ParseGmakeExport(char *line)
/* /*
* Expand the value before putting it in the environment. * Expand the value before putting it in the environment.
*/ */
(void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value); (void)Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES, &value);
/* TODO: handle errors */ /* TODO: handle errors */
setenv(variable, value, 1); setenv(variable, value, 1);
@ -2581,16 +2600,16 @@ ParseEOF(void)
} }
/* Dispose of curFile info */ /* Dispose of curFile info */
/* Leak curFile->fname because all the gnodes have pointers to it. */ /* Leak curFile->fname because all the GNodes have pointers to it. */
free(curFile->buf_freeIt); free(curFile->buf_freeIt);
Vector_Pop(&includes); Vector_Pop(&includes);
if (includes.len == 0) { if (includes.len == 0) {
/* We've run out of input */ /* We've run out of input */
Var_Delete(".PARSEDIR", VAR_GLOBAL); Global_Delete(".PARSEDIR");
Var_Delete(".PARSEFILE", VAR_GLOBAL); Global_Delete(".PARSEFILE");
Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); Global_Delete(".INCLUDEDFROMDIR");
Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); Global_Delete(".INCLUDEDFROMFILE");
return FALSE; return FALSE;
} }
@ -2781,7 +2800,6 @@ ParseGetLine(GetLineMode mode)
char *firstBackslash; char *firstBackslash;
char *firstComment; char *firstComment;
/* Loop through blank lines and comment lines */
for (;;) { for (;;) {
ParseRawLineResult res = ParseRawLine(curFile, ParseRawLineResult res = ParseRawLine(curFile,
&line, &line_end, &firstBackslash, &firstComment); &line, &line_end, &firstBackslash, &firstComment);
@ -3048,7 +3066,7 @@ ParseVarassign(const char *line)
return FALSE; return FALSE;
FinishDependencyGroup(); FinishDependencyGroup();
Parse_DoVar(&var, VAR_GLOBAL); Parse_DoVar(&var, SCOPE_GLOBAL);
return TRUE; return TRUE;
} }
@ -3130,7 +3148,7 @@ ParseDependency(char *line)
* It simply returns the special empty string var_Error, * It simply returns the special empty string var_Error,
* which cannot be detected in the result of Var_Subst. */ * which cannot be detected in the result of Var_Subst. */
eflags = opts.strict ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR; eflags = opts.strict ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR;
(void)Var_Subst(line, VAR_CMDLINE, eflags, &expanded_line); (void)Var_Subst(line, SCOPE_CMDLINE, eflags, &expanded_line);
/* TODO: handle errors */ /* TODO: handle errors */
/* Need a fresh list for the target nodes */ /* Need a fresh list for the target nodes */
@ -3274,13 +3292,11 @@ Parse_MainName(GNodeList *mainList)
if (mainNode == NULL) if (mainNode == NULL)
Punt("no target to make."); Punt("no target to make.");
if (mainNode->type & OP_DOUBLEDEP) { Lst_Append(mainList, mainNode);
Lst_Append(mainList, mainNode); if (mainNode->type & OP_DOUBLEDEP)
Lst_AppendAll(mainList, &mainNode->cohorts); Lst_AppendAll(mainList, &mainNode->cohorts);
} else
Lst_Append(mainList, mainNode);
Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); Global_Append(".TARGETS", mainNode->name);
} }
int int

View File

@ -1,6 +1,6 @@
/* $NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $ */ /* $NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $ */
/*- /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
* The Regents of the University of California. All rights reserved. * The Regents of the University of California. All rights reserved.
* *
@ -32,7 +32,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/*- /*
* Copyright (c) 1989 by Berkeley Softworks * Copyright (c) 1989 by Berkeley Softworks
* All rights reserved. * All rights reserved.
* *
@ -71,7 +71,7 @@
#include "make.h" #include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */ /* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
MAKE_RCSID("$NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $"); MAKE_RCSID("$NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */ /* Return the concatenation of s1 and s2, freshly allocated. */
char * char *
@ -156,7 +156,7 @@ Str_Words(const char *str, Boolean expand)
inquote = '\0'; inquote = '\0';
word_start = words_buf; word_start = words_buf;
word_end = words_buf; word_end = words_buf;
for (str_p = str;; ++str_p) { for (str_p = str;; str_p++) {
char ch = *str_p; char ch = *str_p;
switch (ch) { switch (ch) {
case '"': case '"':
@ -324,9 +324,15 @@ Str_Match(const char *str, const char *pat)
break; break;
return FALSE; return FALSE;
} }
/* XXX: This naive comparison makes the parser /*
* for the pattern dependent on the actual of * XXX: This naive comparison makes the
* the string. This is unpredictable. */ * control flow of the pattern parser
* dependent on the actual value of the
* string. This is unpredictable. It may be
* though that the code only looks wrong but
* actually all code paths result in the same
* behavior. This needs further tests.
*/
if (*pat == *str) if (*pat == *str)
break; break;
if (pat[1] == '-') { if (pat[1] == '-') {

View File

@ -1,4 +1,4 @@
/* $NetBSD: suff.c,v 1.335 2021/01/10 21:20:46 rillig Exp $ */ /* $NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -114,7 +114,7 @@
#include "dir.h" #include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */ /* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
MAKE_RCSID("$NetBSD: suff.c,v 1.335 2021/01/10 21:20:46 rillig Exp $"); MAKE_RCSID("$NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $");
typedef List SuffixList; typedef List SuffixList;
typedef ListNode SuffixListNode; typedef ListNode SuffixListNode;
@ -885,7 +885,7 @@ Suff_DoPaths(void)
for (ln = sufflist.first; ln != NULL; ln = ln->next) { for (ln = sufflist.first; ln != NULL; ln = ln->next) {
Suffix *suff = ln->datum; Suffix *suff = ln->datum;
if (!Lst_IsEmpty(suff->searchPath)) { if (!Lst_IsEmpty(&suff->searchPath->dirs)) {
#ifdef INCLUDES #ifdef INCLUDES
if (suff->flags & SUFF_INCLUDE) if (suff->flags & SUFF_INCLUDE)
SearchPath_AddAll(includesPath, SearchPath_AddAll(includesPath,
@ -902,12 +902,12 @@ Suff_DoPaths(void)
} }
} }
flags = SearchPath_ToFlags("-I", includesPath); flags = SearchPath_ToFlags(includesPath, "-I");
Var_Set(".INCLUDES", flags, VAR_GLOBAL); Global_Set(".INCLUDES", flags);
free(flags); free(flags);
flags = SearchPath_ToFlags("-L", libsPath); flags = SearchPath_ToFlags(libsPath, "-L");
Var_Set(".LIBS", flags, VAR_GLOBAL); Global_Set(".LIBS", flags);
free(flags); free(flags);
SearchPath_Free(includesPath); SearchPath_Free(includesPath);
@ -1176,14 +1176,14 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
GNode *tgn; /* Target GNode */ GNode *tgn; /* Target GNode */
GNode *sgn; /* Source GNode */ GNode *sgn; /* Source GNode */
size_t prefLen; /* The length of the defined prefix */ size_t prefLen; /* The length of the defined prefix */
Suffix *suff; /* Suffix on matching beastie */ Suffix *suff; /* Suffix of the matching candidate */
Candidate *ret; /* Return value */ Candidate *ret; /* Return value */
tgn = targ->node; tgn = targ->node;
prefLen = strlen(targ->prefix); prefLen = strlen(targ->prefix);
for (gln = tgn->children.first; gln != NULL; gln = gln->next) { for (gln = tgn->children.first; gln != NULL; gln = gln->next) {
const char *cp; const char *base;
sgn = gln->datum; sgn = gln->datum;
@ -1198,11 +1198,11 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
continue; continue;
} }
cp = str_basename(sgn->name); base = str_basename(sgn->name);
if (strncmp(cp, targ->prefix, prefLen) != 0) if (strncmp(base, targ->prefix, prefLen) != 0)
continue; continue;
/* The node matches the prefix, see if it has a known suffix. */ /* The node matches the prefix, see if it has a known suffix. */
suff = FindSuffixByName(cp + prefLen); suff = FindSuffixByName(base + prefLen);
if (suff == NULL) if (suff == NULL)
continue; continue;
@ -1246,7 +1246,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* Expand the word along the chosen path * Expand the word along the chosen path
*/ */
Lst_Init(&expansions); Lst_Init(&expansions);
Dir_Expand(cgn->name, Suff_FindPath(cgn), &expansions); SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
while (!Lst_IsEmpty(&expansions)) { while (!Lst_IsEmpty(&expansions)) {
GNode *gn; GNode *gn;
@ -1388,9 +1388,9 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
if (cgn->type & OP_ARCHV) { if (cgn->type & OP_ARCHV) {
/* /*
* Node was an archive(member) target, so we want to * Node was an 'archive(member)' target, so
* call on the Arch module to find the nodes for us, * call on the Arch module to find the nodes for us,
* expanding variables in the parent's context. * expanding variables in the parent's scope.
*/ */
char *p = cp; char *p = cp;
(void)Arch_ParseArchive(&p, &members, pgn); (void)Arch_ParseArchive(&p, &members, pgn);
@ -1626,8 +1626,8 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
gn->unmade++; gn->unmade++;
/* Copy in the variables from the member node to this one. */ /* Copy in the variables from the member node to this one. */
Var_Set(PREFIX, GNode_VarPrefix(mem), gn); Var_Set(gn, PREFIX, GNode_VarPrefix(mem));
Var_Set(TARGET, GNode_VarTarget(mem), gn); Var_Set(gn, TARGET, GNode_VarTarget(mem));
memSuff = mem->suffix; memSuff = mem->suffix;
if (memSuff == NULL) { /* Didn't know what it was. */ if (memSuff == NULL) { /* Didn't know what it was. */
@ -1637,10 +1637,10 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
/* Set the other two local variables required for this target. */ /* Set the other two local variables required for this target. */
Var_Set(MEMBER, name, gn); Var_Set(gn, MEMBER, name);
Var_Set(ARCHIVE, gn->name, gn); Var_Set(gn, ARCHIVE, gn->name);
/* Set $@ for compatibility with other makes. */ /* Set $@ for compatibility with other makes. */
Var_Set(TARGET, gn->name, gn); Var_Set(gn, TARGET, gn->name);
/* /*
* Now we've got the important local variables set, expand any sources * Now we've got the important local variables set, expand any sources
@ -1691,7 +1691,7 @@ FindDepsLib(GNode *gn)
Arch_FindLib(gn, suff->searchPath); Arch_FindLib(gn, suff->searchPath);
} else { } else {
Suffix_Unassign(&gn->suffix); Suffix_Unassign(&gn->suffix);
Var_Set(TARGET, gn->name, gn); Var_Set(gn, TARGET, gn->name);
} }
/* /*
@ -1699,7 +1699,7 @@ FindDepsLib(GNode *gn)
* filesystem conventions, we don't set the regular variables for * filesystem conventions, we don't set the regular variables for
* the thing. .PREFIX is simply made empty. * the thing. .PREFIX is simply made empty.
*/ */
Var_Set(PREFIX, "", gn); Var_Set(gn, PREFIX, "");
} }
static void static void
@ -1776,7 +1776,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
if (gn->path == NULL) if (gn->path == NULL)
return; return;
Var_Set(TARGET, gn->path, gn); Var_Set(gn, TARGET, gn->path);
if (targ != NULL) { if (targ != NULL) {
/* /*
@ -1791,7 +1791,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
savec = gn->path[savep]; savec = gn->path[savep];
gn->path[savep] = '\0'; gn->path[savep] = '\0';
Var_Set(PREFIX, str_basename(gn->path), gn); Var_Set(gn, PREFIX, str_basename(gn->path));
gn->path[savep] = savec; gn->path[savep] = savec;
} else { } else {
@ -1800,7 +1800,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
* known suffix. * known suffix.
*/ */
Suffix_Unassign(&gn->suffix); Suffix_Unassign(&gn->suffix);
Var_Set(PREFIX, str_basename(gn->path), gn); Var_Set(gn, PREFIX, str_basename(gn->path));
} }
} }
@ -1890,8 +1890,8 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
} }
} }
Var_Set(TARGET, GNode_Path(gn), gn); Var_Set(gn, TARGET, GNode_Path(gn));
Var_Set(PREFIX, targ != NULL ? targ->prefix : gn->name, gn); Var_Set(gn, PREFIX, targ != NULL ? targ->prefix : gn->name);
/* /*
* Now we've got the important local variables set, expand any sources * Now we've got the important local variables set, expand any sources
@ -1981,8 +1981,8 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
* we need to do is set the standard variables. * we need to do is set the standard variables.
*/ */
targ->node->type |= OP_DEPS_FOUND; targ->node->type |= OP_DEPS_FOUND;
Var_Set(PREFIX, targ->prefix, targ->node); Var_Set(targ->node, PREFIX, targ->prefix);
Var_Set(TARGET, targ->node->name, targ->node); Var_Set(targ->node, TARGET, targ->node->name);
} }
} }
@ -2048,8 +2048,8 @@ FindDeps(GNode *gn, CandidateSearcher *cs)
gn->type |= OP_DEPS_FOUND; gn->type |= OP_DEPS_FOUND;
/* Make sure we have these set, may get revised below. */ /* Make sure we have these set, may get revised below. */
Var_Set(TARGET, GNode_Path(gn), gn); Var_Set(gn, TARGET, GNode_Path(gn));
Var_Set(PREFIX, gn->name, gn); Var_Set(gn, PREFIX, gn->name);
DEBUG1(SUFF, "SuffFindDeps \"%s\"\n", gn->name); DEBUG1(SUFF, "SuffFindDeps \"%s\"\n", gn->name);
@ -2137,9 +2137,7 @@ Suffix_Print(Suffix *suff)
char flags_buf[SuffixFlags_ToStringSize]; char flags_buf[SuffixFlags_ToStringSize];
debug_printf(" (%s)", debug_printf(" (%s)",
Enum_FlagsToString(flags_buf, sizeof flags_buf, SuffixFlags_ToString(flags_buf, suff->flags));
suff->flags,
SuffixFlags_ToStringSpecs));
} }
debug_printf("\n"); debug_printf("\n");

View File

@ -1,4 +1,4 @@
/* $NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $ */ /* $NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -113,7 +113,7 @@
#include "dir.h" #include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $"); MAKE_RCSID("$NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $");
/* /*
* All target nodes that appeared on the left-hand side of one of the * All target nodes that appeared on the left-hand side of one of the
@ -243,14 +243,14 @@ GNode_Free(void *gnp)
* by this node. * by this node.
* *
* XXX: For the nodes that represent targets or sources (and not * XXX: For the nodes that represent targets or sources (and not
* VAR_GLOBAL), it should be safe to free the variables as well, * SCOPE_GLOBAL), it should be safe to free the variables as well,
* since each node manages the memory for all its variables itself. * since each node manages the memory for all its variables itself.
* *
* XXX: The GNodes that are only used as variable contexts (VAR_CMD, * XXX: The GNodes that are only used as variable scopes (VAR_CMD,
* VAR_GLOBAL, VAR_INTERNAL) are not freed at all (see Var_End, where * SCOPE_GLOBAL, SCOPE_INTERNAL) are not freed at all (see Var_End,
* they are not mentioned). These might be freed at all, if their * where they are not mentioned). These might be freed at all, if
* variable values are indeed not used anywhere else (see Trace_Init * their variable values are indeed not used anywhere else (see
* for the only suspicious use). * Trace_Init for the only suspicious use).
*/ */
HashTable_Done(&gn->vars); HashTable_Done(&gn->vars);
@ -305,7 +305,7 @@ GNode *
Targ_NewInternalNode(const char *name) Targ_NewInternalNode(const char *name)
{ {
GNode *gn = GNode_New(name); GNode *gn = GNode_New(name);
Var_Append(".ALLTARGETS", name, VAR_GLOBAL); Global_Append(".ALLTARGETS", name);
Lst_Append(&allTargets, gn); Lst_Append(&allTargets, gn);
DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name); DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
if (doing_depend) if (doing_depend)
@ -450,8 +450,8 @@ Targ_PrintType(int type)
} }
} }
static const char * const char *
made_name(GNodeMade made) GNodeMade_Name(GNodeMade made)
{ {
switch (made) { switch (made) {
case UNMADE: return "unmade"; case UNMADE: return "unmade";
@ -505,10 +505,10 @@ Targ_PrintNode(GNode *gn, int pass)
if (gn->mtime != 0) { if (gn->mtime != 0) {
debug_printf("# last modified %s: %s\n", debug_printf("# last modified %s: %s\n",
Targ_FmtTime(gn->mtime), Targ_FmtTime(gn->mtime),
made_name(gn->made)); GNodeMade_Name(gn->made));
} else if (gn->made != UNMADE) { } else if (gn->made != UNMADE) {
debug_printf("# nonexistent (maybe): %s\n", debug_printf("# nonexistent (maybe): %s\n",
made_name(gn->made)); GNodeMade_Name(gn->made));
} else } else
debug_printf("# unmade\n"); debug_printf("# unmade\n");
} }
@ -577,10 +577,10 @@ Targ_PrintGraph(int pass)
PrintOnlySources(); PrintOnlySources();
debug_printf("#*** Global Variables:\n"); debug_printf("#*** Global Variables:\n");
Var_Dump(VAR_GLOBAL); Var_Dump(SCOPE_GLOBAL);
debug_printf("#*** Command-line Variables:\n"); debug_printf("#*** Command-line Variables:\n");
Var_Dump(VAR_CMDLINE); Var_Dump(SCOPE_CMDLINE);
debug_printf("\n"); debug_printf("\n");
Dir_PrintDirectories(); Dir_PrintDirectories();

View File

@ -1,6 +1,6 @@
/* $NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $ */ /* $NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $ */
/*- /*
* Copyright (c) 2000 The NetBSD Foundation, Inc. * Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -29,7 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*- /*
* trace.c -- * trace.c --
* handle logging of trace events generated by various parts of make. * handle logging of trace events generated by various parts of make.
* *
@ -48,7 +48,7 @@
#include "job.h" #include "job.h"
#include "trace.h" #include "trace.h"
MAKE_RCSID("$NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $"); MAKE_RCSID("$NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $");
static FILE *trfile; static FILE *trfile;
static pid_t trpid; static pid_t trpid;
@ -71,7 +71,7 @@ Trace_Init(const char *pathname)
trpid = getpid(); trpid = getpid();
/* XXX: This variable may get overwritten later, which /* XXX: This variable may get overwritten later, which
* would make trwd point to undefined behavior. */ * would make trwd point to undefined behavior. */
curDir = Var_Value(".CURDIR", VAR_GLOBAL); curDir = Var_Value(SCOPE_GLOBAL, ".CURDIR");
trwd = curDir.str; trwd = curDir.str;
trfile = fopen(pathname, "a"); trfile = fopen(pathname, "a");

View File

@ -1,6 +1,6 @@
/* $NetBSD: trace.h,v 1.5 2020/11/28 08:41:53 rillig Exp $ */ /* $NetBSD: trace.h,v 1.6 2021/01/19 20:51:46 rillig Exp $ */
/*- /*
* Copyright (c) 2000 The NetBSD Foundation, Inc. * Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved. * All rights reserved.
* *
@ -29,7 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
/*- /*
* trace.h -- * trace.h --
* Definitions pertaining to the tracing of jobs in parallel mode. * Definitions pertaining to the tracing of jobs in parallel mode.
*/ */

View File

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.138 2021/01/01 22:55:09 sjg Exp $ # $Id: Makefile,v 1.143 2021/02/06 18:31:30 sjg Exp $
# #
# $NetBSD: Makefile,v 1.260 2020/12/31 03:05:12 rillig Exp $ # $NetBSD: Makefile,v 1.269 2021/02/06 18:26:03 sjg Exp $
# #
# Unit tests for make(1) # Unit tests for make(1)
# #
@ -30,6 +30,8 @@
# src/tests/usr.bin/make/t_make.sh. # src/tests/usr.bin/make/t_make.sh.
# #
.MAIN: all
# we use these below but we might be an older make # we use these below but we might be an older make
.MAKE.OS?= ${uname -s:L:sh} .MAKE.OS?= ${uname -s:L:sh}
.MAKE.UID?= ${id -u:L:sh} .MAKE.UID?= ${id -u:L:sh}
@ -45,6 +47,7 @@ TESTS+= cmd-errors-jobs
TESTS+= cmd-errors-lint TESTS+= cmd-errors-lint
TESTS+= cmd-interrupt TESTS+= cmd-interrupt
TESTS+= cmdline TESTS+= cmdline
TESTS+= cmdline-redirect-stdin
TESTS+= cmdline-undefined TESTS+= cmdline-undefined
TESTS+= comment TESTS+= comment
TESTS+= compat-error TESTS+= compat-error
@ -200,6 +203,7 @@ TESTS+= impsrc
TESTS+= include-main TESTS+= include-main
TESTS+= job-flags TESTS+= job-flags
#TESTS+= job-output-long-lines #TESTS+= job-output-long-lines
TESTS+= jobs-empty-commands
TESTS+= jobs-error-indirect TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make TESTS+= jobs-error-nested-make
@ -256,6 +260,7 @@ TESTS+= opt-m-include-dir
TESTS+= opt-no-action TESTS+= opt-no-action
TESTS+= opt-no-action-at-all TESTS+= opt-no-action-at-all
TESTS+= opt-no-action-runflags TESTS+= opt-no-action-runflags
TESTS+= opt-no-action-touch
TESTS+= opt-query TESTS+= opt-query
TESTS+= opt-raw TESTS+= opt-raw
TESTS+= opt-silent TESTS+= opt-silent
@ -439,16 +444,10 @@ TESTS+= varquote
# escape-for-item.mk # escape-for-item.mk
# posix-*.mk (see posix.mk and posix1.mk) # posix-*.mk (see posix.mk and posix1.mk)
.if ${.OBJDIR} != ${.CURDIR}
RO_OBJDIR:= ${.OBJDIR}/roobj
.else
RO_OBJDIR:= ${TMPDIR:U/tmp}/roobj
.endif
# Additional environment variables for some of the tests. # Additional environment variables for some of the tests.
# The base environment is -i PATH="$PATH". # The base environment is -i PATH="$PATH".
ENV.depsrc-optional+= TZ=UTC ENV.depsrc-optional+= TZ=UTC
ENV.envfirst= FROM_ENV=value-from-env ENV.envfirst= FROM_ENV=value-from-env
ENV.objdir-writable+= RO_OBJDIR=${RO_OBJDIR}
ENV.varmisc= FROM_ENV=env ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_BEFORE=env
ENV.varmisc+= FROM_ENV_AFTER=env ENV.varmisc+= FROM_ENV_AFTER=env
@ -485,8 +484,9 @@ SED_CMDS.job-output-long-lines= \
${:D marker should always be at the beginning of the line. } \ ${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \ -e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d' -e '/^bb*--- job-a ---$$/d'
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1} SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2}
SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3}
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),' SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,' SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,' SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
@ -506,11 +506,9 @@ SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= \ SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell}
-e 's,^${.SHELL:T}: [ 0-9:]*,,' \ SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
-e 's,^${.SHELL:T}: ,,' \ SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
-e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= \ SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),' -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,' SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
@ -518,11 +516,13 @@ SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<norm
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g' SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g' SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g' SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
SED_CMDS.varname-empty= -e 's,${.CURDIR},<curdir>,g'
SED_CMDS.varname-empty+= -e '/\.PARSEDIR/d'
SED_CMDS.varname-empty+= -e '/\.SHELL/d'
# Some tests need an additional round of postprocessing. # Some tests need an additional round of postprocessing.
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
# Some tests reuse other tests, which makes them unnecessarily fragile. # Some tests reuse other tests, which makes them unnecessarily fragile.
export-all.rawout: export.mk export-all.rawout: export.mk
@ -542,6 +542,11 @@ STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,' STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,' STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1}
STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 <timestamp>,'
STD_SED_CMDS.dg3= ${STD_SED_CMDS.dg2}
# Omit details such as process IDs from the output of the -dj option. # Omit details such as process IDs from the output of the -dj option.
STD_SED_CMDS.dj= \ STD_SED_CMDS.dj= \
@ -558,9 +563,34 @@ STD_SED_CMDS.hide-from-output= \
-e 's,hide-from-output ,,' \ -e 's,hide-from-output ,,' \
-e 's,hide-from-output,,' -e 's,hide-from-output,,'
# End of the configuration helpers section. # Normalize the output for error messages from the shell.
#
# $shell -c '...'
# NetBSD sh ...: not found
# NetBSD ksh ksh: ...: not found
# bash 5.0.18 bash: ...: command not found
# bash 5.1.0 bash: line 1: ...: command not found
# dash dash: 1: ...: not found
#
# $shell -c '< /nonexistent'
# NetBSD sh sh: cannot open /nonexistent: no such file
# NetBSD ksh ksh: cannot open /nonexistent: No such file or directory
# bash 5.0.18 bash: /nonexistent: No such file or directory
# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory
# dash dash: 1: cannot open /nonexistent: No such file
#
# echo '< /nonexistent' | $shell
# NetBSD sh sh: cannot open /nonexistent: no such file
# NetBSD ksh ksh: <stdin>[1]: cannot open /nonexistent: No such file or directory
# bash 5.0.18 bash: line 1: /nonexistent: No such file or directory
# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory
# dash dash: 1: cannot open /nonexistent: No such file
#
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,'
.MAIN: all # End of the configuration helpers section.
.-include "Makefile.inc" .-include "Makefile.inc"
.-include "Makefile.config" .-include "Makefile.config"
@ -603,7 +633,22 @@ LANG= C
_MKMSG_TEST= : _MKMSG_TEST= :
.endif .endif
# for many tests we need a TMPDIR that will not collide
# with other users.
.if ${.OBJDIR} != ${.CURDIR}
# easy
TMPDIR:= ${.OBJDIR}/tmp
.else
TMPDIR:= ${TMPDIR:U/tmp}/uid${.MAKE.UID}
.endif
# make sure it exists
.if !exist(${TMPDIR})
x!= echo; mkdir -p ${TMPDIR}
.endif
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
MAKE_TEST_ENV+= TMPDIR=${TMPDIR}
.if ${.MAKE.OS} == "NetBSD" .if ${.MAKE.OS} == "NetBSD"
LIMIT_RESOURCES?= ulimit -v 200000 LIMIT_RESOURCES?= ulimit -v 200000
@ -637,6 +682,7 @@ _SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,'
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
# replace anything after 'stopped in' with unit-tests # replace anything after 'stopped in' with unit-tests
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g'
# strip ${.CURDIR}/ from the output # strip ${.CURDIR}/ from the output
_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'

View File

@ -0,0 +1 @@
exit status 0

View File

@ -0,0 +1,34 @@
# $NetBSD: cmdline-redirect-stdin.mk,v 1.1 2021/02/01 20:31:41 rillig Exp $
#
# Demonstrate that the '!=' assignment operator can read individual lines
# from make's stdin.
#
# This edge case is an implementation detail that has no practical
# application.
all: .PHONY
@printf '%s\n' "first line" "second line" \
| ${MAKE} -f ${MAKEFILE} read-lines
.if make(read-lines)
line1!= read line; echo "$$line"
line2!= read line; echo "$$line"
.if ${line1} != "first line"
. error line1="${line1}"
.elif ${line2} == ""
# If this branch is ever reached, the shell from the assignment to line1
# probably buffers its input. Most shells use unbuffered stdin, and this
# is actually specified by POSIX, which says that "The read utility shall
# read a single line from standard input". This is the reason why the shell
# reads its input byte by byte, which makes it terribly slow for practical
# applications.
. error The shell's read command does not read a single line.
.elif ${line2} != "second line"
. error line2="${line2}"
.endif
read-lines: .PHONY
.endif

View File

@ -1,5 +1,5 @@
makeobjdir-direct: makeobjdir-direct:
show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 show-objdir: TMPDIR/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
makeobjdir-indirect: makeobjdir-indirect:
show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/ show-objdir: TMPDIR/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
exit status 0 exit status 0

View File

@ -1,8 +1,8 @@
# $NetBSD: cmdline.mk,v 1.2 2020/11/15 14:07:53 rillig Exp $ # $NetBSD: cmdline.mk,v 1.3 2021/02/06 18:26:03 sjg Exp $
# #
# Tests for command line parsing and related special variables. # Tests for command line parsing and related special variables.
TMPBASE?= /tmp/uid${.MAKE.UID} TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}}
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r

View File

@ -1,4 +1,3 @@
make: "cond-cmp-numeric-eq.mk" line 67: warning: Unknown operator
make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345)) make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345))
make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345)) make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345))
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue

View File

@ -1,14 +1,10 @@
CondParser_Eval: !(${:UINF} > 1e100) CondParser_Eval: !(${:UINF} > 1e100)
make: "cond-cmp-numeric.mk" line 11: warning: String comparison operator must be either == or != make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or !=
make: "cond-cmp-numeric.mk" line 11: Malformed conditional (!(${:UINF} > 1e100))
CondParser_Eval: ${:UNaN} > NaN CondParser_Eval: ${:UNaN} > NaN
make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be either == or != make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or !=
make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN)
CondParser_Eval: !(${:UNaN} == NaN) CondParser_Eval: !(${:UNaN} == NaN)
lhs = "NaN", rhs = "NaN", op = == lhs = "NaN", rhs = "NaN", op = ==
CondParser_Eval: 123 ! 123 CondParser_Eval: 123 ! 123
lhs = 123.000000, rhs = 123.000000, op = !
make: "cond-cmp-numeric.mk" line 34: warning: Unknown operator
make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests

View File

@ -1,8 +1,11 @@
make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing") make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
make: "cond-cmp-string.mk" line 49: warning: String comparison operator must be either == or !=
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value")) make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value")) make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
make: "cond-cmp-string.mk" line 113: String comparison operator must be either == or !=
make: "cond-cmp-string.mk" line 120: String comparison operator must be either == or !=
make: "cond-cmp-string.mk" line 127: String comparison operator must be either == or !=
make: "cond-cmp-string.mk" line 134: String comparison operator must be either == or !=
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-cmp-string.mk,v 1.13 2020/11/15 14:07:53 rillig Exp $ # $NetBSD: cond-cmp-string.mk,v 1.14 2021/01/19 19:54:57 rillig Exp $
# #
# Tests for string comparisons in .if conditions. # Tests for string comparisons in .if conditions.
@ -108,3 +108,31 @@
.if 12345.0 == "12345" .if 12345.0 == "12345"
. error . error
.endif .endif
# Strings cannot be compared relationally, only for equality.
.if "string" < "string"
. error
.else
. error
.endif
# Strings cannot be compared relationally, only for equality.
.if "string" <= "string"
. error
.else
. error
.endif
# Strings cannot be compared relationally, only for equality.
.if "string" > "string"
. error
.else
. error
.endif
# Strings cannot be compared relationally, only for equality.
.if "string" >= "string"
. error
.else
. error
.endif

View File

@ -1,7 +1,5 @@
make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined() make: "cond-func-defined.mk" line 23: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B)) make: "cond-func-defined.mk" line 33: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 33: warning: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 33: Malformed conditional (defined(DEF)
make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are
make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable
make: "cond-func-defined.mk" line 47: involved, even if it feels like it. make: "cond-func-defined.mk" line 47: involved, even if it feels like it.

View File

@ -1,9 +1,6 @@
make: "cond-func.mk" line 36: warning: Missing closing parenthesis for defined() make: "cond-func.mk" line 36: Missing closing parenthesis for defined()
make: "cond-func.mk" line 36: Malformed conditional (!defined(A B)) make: "cond-func.mk" line 51: Missing closing parenthesis for defined()
make: "cond-func.mk" line 51: warning: Missing closing parenthesis for defined() make: "cond-func.mk" line 54: Missing closing parenthesis for defined()
make: "cond-func.mk" line 51: Malformed conditional (!defined(A&B))
make: "cond-func.mk" line 54: warning: Missing closing parenthesis for defined()
make: "cond-func.mk" line 54: Malformed conditional (!defined(A|B))
make: "cond-func.mk" line 94: The empty variable is never defined. make: "cond-func.mk" line 94: The empty variable is never defined.
make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).

View File

@ -3,4 +3,7 @@ make: "cond-op-not.mk" line 37: Not space evaluates to false.
make: "cond-op-not.mk" line 41: Not 0 evaluates to true. make: "cond-op-not.mk" line 41: Not 0 evaluates to true.
make: "cond-op-not.mk" line 49: Not 1 evaluates to false. make: "cond-op-not.mk" line 49: Not 1 evaluates to false.
make: "cond-op-not.mk" line 55: Not word evaluates to false. make: "cond-op-not.mk" line 55: Not word evaluates to false.
exit status 0 make: "cond-op-not.mk" line 59: Malformed conditional (!)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op-not.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $ # $NetBSD: cond-op-not.mk,v 1.7 2021/01/19 17:49:13 rillig Exp $
# #
# Tests for the ! operator in .if conditions, which negates its argument. # Tests for the ! operator in .if conditions, which negates its argument.
@ -55,5 +55,12 @@
. info Not word evaluates to false. . info Not word evaluates to false.
.endif .endif
# A single exclamation mark is a parse error.
.if !
. error
.else
. error
.endif
all: all:
@:; @:;

View File

@ -1,2 +1,6 @@
make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112. make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112.
exit status 0 make: "cond-op-parentheses.mk" line 19: Malformed conditional (()
make: "cond-op-parentheses.mk" line 29: Malformed conditional ())
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op-parentheses.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $ # $NetBSD: cond-op-parentheses.mk,v 1.4 2021/01/19 17:49:13 rillig Exp $
# #
# Tests for parentheses in .if conditions. # Tests for parentheses in .if conditions.
@ -15,5 +15,22 @@
. error . error
.endif .endif
# An unbalanced opening parenthesis is a parse error.
.if (
. error
.else
. error
.endif
# An unbalanced closing parenthesis is a parse error.
#
# As of 2021-01-19, CondParser_Term returned TOK_RPAREN even though this
# function promised to only ever return TOK_TRUE, TOK_FALSE or TOK_ERROR.
.if )
. error
.else
. error
.endif
all: all:
@:; @:;

View File

@ -11,6 +11,10 @@ make: "cond-op.mk" line 93: 1 0 0 => 0 1 1
make: "cond-op.mk" line 93: 1 0 1 => 1 1 1 make: "cond-op.mk" line 93: 1 0 1 => 1 1 1
make: "cond-op.mk" line 93: 1 1 0 => 0 1 1 make: "cond-op.mk" line 93: 1 1 0 => 0 1 1
make: "cond-op.mk" line 93: 1 1 1 => 1 1 1 make: "cond-op.mk" line 93: 1 1 1 => 1 1 1
make: "cond-op.mk" line 104: Malformed conditional (1 &&)
make: "cond-op.mk" line 112: Malformed conditional (0 &&)
make: "cond-op.mk" line 120: Malformed conditional (1 ||)
make: "cond-op.mk" line 129: Malformed conditional (0 ||)
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op.mk,v 1.10 2020/11/15 14:58:14 rillig Exp $ # $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 rillig Exp $
# #
# Tests for operators like &&, ||, ! in .if conditions. # Tests for operators like &&, ||, ! in .if conditions.
# #
@ -61,8 +61,8 @@
# As soon as the parser sees the '$', it knows that the condition will # As soon as the parser sees the '$', it knows that the condition will
# be malformed. Therefore there is no point in evaluating it. # be malformed. Therefore there is no point in evaluating it.
# #
# As of 2020-09-11, that part of the condition is evaluated nevertheless, # As of 2021-01-20, that part of the condition is evaluated nevertheless,
# since CondParser_Expr just requests the next token, without restricting # since CondParser_Or just requests the next token, without restricting
# the token to the expected tokens. If the parser were to restrict the # the token to the expected tokens. If the parser were to restrict the
# valid follow tokens for the token "0" to those that can actually produce # valid follow tokens for the token "0" to those that can actually produce
# a correct condition (which in this case would be comparison operators, # a correct condition (which in this case would be comparison operators,
@ -98,5 +98,39 @@
. endfor . endfor
.endfor .endfor
# This condition is obviously malformed. It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '&&' evaluated to true.
.if 1 &&
. error
.else
. error
.endif
# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
.if 0 &&
. error
.else
. error
.endif
# This obviously malformed condition was not detected as such before cond.c
# 1.238 from 2021-01-19.
.if 1 ||
. error
.else
. error
.endif
# This condition is obviously malformed. It is properly detected and also
# was properly detected before 2021-01-19, but only because the left hand
# side of the '||' evaluated to false.
.if 0 ||
. error
.else
. error
.endif
all: all:
@:; @:;

View File

@ -18,12 +18,37 @@ lhs = "yes", rhs = "yes", op = !=
CondParser_Eval: ${UNDEF:Uundefined}!=undefined CondParser_Eval: ${UNDEF:Uundefined}!=undefined
lhs = "undefined", rhs = "undefined", op = != lhs = "undefined", rhs = "undefined", op = !=
CondParser_Eval: ${UNDEF:U12345}>12345 CondParser_Eval: ${UNDEF:U12345}>12345
lhs = 12345.000000, rhs = 12345.000000, op = >1 lhs = 12345.000000, rhs = 12345.000000, op = >
CondParser_Eval: ${UNDEF:U12345}<12345 CondParser_Eval: ${UNDEF:U12345}<12345
lhs = 12345.000000, rhs = 12345.000000, op = <1 lhs = 12345.000000, rhs = 12345.000000, op = <
CondParser_Eval: (${UNDEF:U0})||0 CondParser_Eval: (${UNDEF:U0})||0
CondParser_Eval: ${:Uvar}&&name != "var&&name" CondParser_Eval: ${:Uvar}&&name != "var&&name"
lhs = "var&&name", rhs = "var&&name", op = != lhs = "var&&name", rhs = "var&&name", op = !=
CondParser_Eval: ${:Uvar}||name != "var||name" CondParser_Eval: ${:Uvar}||name != "var||name"
lhs = "var||name", rhs = "var||name", op = != lhs = "var||name", rhs = "var||name", op = !=
exit status 0 CondParser_Eval: bare
make: "cond-token-plain.mk" line 102: A bare word is treated like defined(...), and the variable 'bare' is not defined.
CondParser_Eval: VAR
make: "cond-token-plain.mk" line 107: A bare word is treated like defined(...).
CondParser_Eval: V${:UA}R
make: "cond-token-plain.mk" line 114: ok
CondParser_Eval: V${UNDEF}AR
make: "cond-token-plain.mk" line 122: Undefined variables in bare words expand to an empty string.
CondParser_Eval: 0${:Ux00}
make: "cond-token-plain.mk" line 130: Numbers can be composed from literals and variable expressions.
CondParser_Eval: 0${:Ux01}
make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions.
CondParser_Eval: "" ==
make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '=='
CondParser_Eval: == ""
make: "cond-token-plain.mk" line 148: Malformed conditional (== "")
CondParser_Eval: \\
make: "cond-token-plain.mk" line 163: The variable '\\' is not defined.
CondParser_Eval: \\
make: "cond-token-plain.mk" line 168: Now the variable '\\' is defined.
CondParser_Eval: "unquoted\"quoted" != unquoted"quoted
lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = !=
CondParser_Eval: $$$$$$$$ != ""
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-plain.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $ # $NetBSD: cond-token-plain.mk,v 1.10 2021/01/21 14:08:09 rillig Exp $
# #
# Tests for plain tokens (that is, string literals without quotes) # Tests for plain tokens (that is, string literals without quotes)
# in .if conditions. # in .if conditions.
@ -93,5 +93,100 @@
. error . error
.endif .endif
# A bare word may appear alone in a condition, without any comparison
# operator. It is implicitly converted into defined(bare).
.if bare
. error
.else
. info A bare word is treated like defined(...), and the variable $\
'bare' is not defined.
.endif
VAR= defined
.if VAR
. info A bare word is treated like defined(...).
.else
. error
.endif
# Bare words may be intermixed with variable expressions.
.if V${:UA}R
. info ok
.else
. error
.endif
# In bare words, even undefined variables are allowed. Without the bare
# words, undefined variables are not allowed. That feels inconsistent.
.if V${UNDEF}AR
. info Undefined variables in bare words expand to an empty string.
.else
. error
.endif
.if 0${:Ux00}
. error
.else
. info Numbers can be composed from literals and variable expressions.
.endif
.if 0${:Ux01}
. info Numbers can be composed from literals and variable expressions.
.else
. error
.endif
# If the right-hand side is missing, it's a parse error.
.if "" ==
. error
.else
. error
.endif
# If the left-hand side is missing, it's a parse error as well, but without
# a specific error message.
.if == ""
. error
.else
. error
.endif
# The '\\' is not a line continuation. Neither is it an unquoted string
# literal. Instead, it is parsed as a function argument (ParseFuncArg),
# and in that context, the backslash is just an ordinary character. The
# function argument thus stays '\\' (2 backslashes). This string is passed
# to FuncDefined, and since there is no variable named '\\', the condition
# evaluates to false.
.if \\
. error
.else
. info The variable '\\' is not defined.
.endif
${:U\\\\}= backslash
.if \\
. info Now the variable '\\' is defined.
.else
. error
.endif
# Anything that doesn't start with a double quote is considered a "bare word".
# Strangely, a bare word may contain double quotes inside. Nobody should ever
# depend on this since it may well be unintended. See CondParser_String.
.if "unquoted\"quoted" != unquoted"quoted
. error
.endif
# FIXME: In CondParser_String, Var_Parse returns var_Error without a
# corresponding error message.
.if $$$$$$$$ != ""
. error
.else
. error
.endif
# See cond-token-string.mk for similar tests where the condition is enclosed
# in "quotes".
all: all:
@:; @:;

View File

@ -1,8 +1,18 @@
make: "cond-token-string.mk" line 9: Unknown modifier 'Z' make: "cond-token-string.mk" line 13: Unknown modifier 'Z'
make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}") make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 18: xvalue is not defined. make: "cond-token-string.mk" line 22: xvalue is not defined.
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "") make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "")
make: "cond-token-string.mk" line 33: Expected. make: "cond-token-string.mk" line 37: Expected.
CondParser_Eval: "UNDEF"
make: "cond-token-string.mk" line 46: The string literal "UNDEF" is not empty.
CondParser_Eval: " "
make: "cond-token-string.mk" line 55: The string literal " " is not empty, even though it consists of whitespace only.
CondParser_Eval: "${UNDEF}"
make: "cond-token-string.mk" line 64: An undefined variable in quotes expands to an empty string, which then evaluates to false.
CondParser_Eval: "${:Uvalue}"
make: "cond-token-string.mk" line 68: A nonempty variable expression evaluates to true.
CondParser_Eval: "${:U}"
make: "cond-token-string.mk" line 76: An empty variable evaluates to false.
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,6 +1,10 @@
# $NetBSD: cond-token-string.mk,v 1.3 2020/11/10 22:23:37 rillig Exp $ # $NetBSD: cond-token-string.mk,v 1.4 2021/01/21 00:38:28 rillig Exp $
# #
# Tests for quoted and unquoted string literals in .if conditions. # Tests for quoted string literals in .if conditions.
#
# See also:
# cond-token-plain.mk
# Covers string literals without quotes (called "bare words").
# TODO: Implementation # TODO: Implementation
@ -35,5 +39,44 @@
. error . error
.endif .endif
.MAKEFLAGS: -dc
# A string in quotes is checked whether it is not empty.
.if "UNDEF"
. info The string literal "UNDEF" is not empty.
.else
. error
.endif
# A space is not empty as well.
# This differs from many other places where whitespace is trimmed.
.if " "
. info The string literal " " is not empty, even though it consists of $\
whitespace only.
.else
. error
.endif
.if "${UNDEF}"
. error
.else
. info An undefined variable in quotes expands to an empty string, which $\
then evaluates to false.
.endif
.if "${:Uvalue}"
. info A nonempty variable expression evaluates to true.
.else
. error
.endif
.if "${:U}"
. error
.else
. info An empty variable evaluates to false.
.endif
.MAKEFLAGS: -d0
all: all:
@:; @:;

View File

@ -16,7 +16,7 @@ Passed:
4 is not prime 4 is not prime
5 is prime 5 is prime
make: warning: String comparison operator must be either == or != make: String comparison operator must be either == or !=
make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
OK OK

View File

@ -1,4 +1,4 @@
# $NetBSD: dir.mk,v 1.8 2020/11/03 18:42:33 rillig Exp $ # $NetBSD: dir.mk,v 1.9 2021/01/23 10:48:49 rillig Exp $
# #
# Tests for dir.c. # Tests for dir.c.
@ -79,7 +79,7 @@ single-word:
# Demonstrate debug logging for filename expansion, especially curly braces. # Demonstrate debug logging for filename expansion, especially curly braces.
.MAKEFLAGS: -dd .MAKEFLAGS: -dd
# The below line does not call Dir_Expand yet. # The below line does not call SearchPath_Expand yet.
# It is expanded only when necessary, that is, when the 'debug' target is # It is expanded only when necessary, that is, when the 'debug' target is
# indeed made. # indeed made.
debug: {{thi,fou}r,fif}twen debug: {{thi,fou}r,fif}twen

View File

@ -1 +1,4 @@
exit status 0 make: "directive-error.mk" line 13: message
make: stopped in unit-tests
exit status 1

View File

@ -1,10 +1,13 @@
# $NetBSD: directive-error.mk,v 1.3 2020/12/13 01:07:54 rillig Exp $ # $NetBSD: directive-error.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $
# #
# Tests for the .error directive, which prints an error message and exits # Tests for the .error directive, which prints an error message and exits
# immediately, unlike other "fatal" parse errors, which continue to parse # immediately, unlike other "fatal" parse errors, which continue to parse
# until the end of the current top-level makefile. # until the end of the current top-level makefile.
#
# See also:
# opt-warnings-as-errors.mk
# TODO: Implementation # Before parse.c 1.532 from 2021-01-27, the ".error" issued an irrelevant
# message saying "parsing warnings being treated as errors".
all: .MAKEFLAGS: -W
@:; .error message

View File

@ -12,7 +12,7 @@ Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXP
ParseDoDependency(: ) ParseDoDependency(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>" CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
Modifier part: "echo "$UT_VAR"" Modifier part: "echo "$UT_VAR""
Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none) Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
@ -21,7 +21,7 @@ Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none) Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
Var_Parse: ${UT_VAR} with VARE_WANTRES Var_Parse: ${UT_VAR} with VARE_WANTRES
Var_Parse: ${REF}> with VARE_WANTRES Var_Parse: ${REF}> with VARE_WANTRES
Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
lhs = "<>", rhs = "<>", op = != lhs = "<>", rhs = "<>", op = !=
ParseReadLine (49): ': ${UT_VAR:N*}' ParseReadLine (49): ': ${UT_VAR:N*}'
Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
@ -35,7 +35,7 @@ ParseReadLine (53): 'REF= defined'
Global:REF = defined Global:REF = defined
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>" CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" with VARE_UNDEFERR|VARE_WANTRES
Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
Modifier part: "echo "$UT_VAR"" Modifier part: "echo "$UT_VAR""
Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none) Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none)
@ -44,7 +44,7 @@ Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none)
Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none) Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none)
Var_Parse: ${UT_VAR} with VARE_WANTRES Var_Parse: ${UT_VAR} with VARE_WANTRES
Var_Parse: ${REF}> with VARE_WANTRES Var_Parse: ${REF}> with VARE_WANTRES
Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) Result of ${:!echo "\$UT_VAR"!} is "<defined>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF)
lhs = "<defined>", rhs = "<defined>", op = != lhs = "<defined>", rhs = "<defined>", op = !=
ParseReadLine (61): 'all:' ParseReadLine (61): 'all:'
ParseDoDependency(all:) ParseDoDependency(all:)

View File

@ -27,25 +27,29 @@ make: "directive-for-escape.mk" line 41: value-with-modifier
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U\${UNDEF\:U\\$\\$} . info ${:U\${UNDEF\:U\\$\\$}
make: "directive-for-escape.mk" line 52: ${UNDEF:U\$ make: "directive-for-escape.mk" line 55: ${UNDEF:U\$
For: loop body: For: loop body:
. info ${:U{{\}\}} . info ${:U{{\}\}}
make: "directive-for-escape.mk" line 52: {{}} make: "directive-for-escape.mk" line 55: {{}}
For: loop body: For: loop body:
. info ${:Uend\}} . info ${:Uend\}}
make: "directive-for-escape.mk" line 52: end} make: "directive-for-escape.mk" line 55: end}
For: end for 1
For: loop body:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
make: "directive-for-escape.mk" line 66: begin<fallback>end
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U\$} . info ${:U\$}
make: "directive-for-escape.mk" line 60: $ make: "directive-for-escape.mk" line 74: $
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${NUMBERS} ${:Ureplaced} . info ${NUMBERS} ${:Ureplaced}
make: "directive-for-escape.mk" line 68: one two three replaced make: "directive-for-escape.mk" line 82: one two three replaced
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:Ureplaced} . info ${:Ureplaced}
make: "directive-for-escape.mk" line 78: replaced make: "directive-for-escape.mk" line 92: replaced
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info . $$i: ${:Uinner} . info . $$i: ${:Uinner}
@ -54,21 +58,18 @@ For: loop body:
. info . $$(i): $(:Uinner) . info . $$(i): $(:Uinner)
. info . $$(i:M*): $(:Uinner:M*) . info . $$(i:M*): $(:Uinner:M*)
. info . $${i$${:U}}: ${i${:U}} . info . $${i$${:U}}: ${i${:U}}
. info . $${i\}}: ${:Uinner\}} # XXX: unclear why SubstVarLong needs this . info . $${i\}}: ${:Uinner\}} # XXX: unclear why ForLoop_SubstVarLong needs this
. info . $${i2}: ${i2} . info . $${i2}: ${i2}
. info . $${i,}: ${i,} . info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
make: "directive-for-escape.mk" line 86: . $i: inner make: "directive-for-escape.mk" line 100: . $i: inner
make: "directive-for-escape.mk" line 87: . ${i}: inner make: "directive-for-escape.mk" line 101: . ${i}: inner
make: "directive-for-escape.mk" line 88: . ${i:M*}: inner make: "directive-for-escape.mk" line 102: . ${i:M*}: inner
make: "directive-for-escape.mk" line 89: . $(i): inner make: "directive-for-escape.mk" line 103: . $(i): inner
make: "directive-for-escape.mk" line 90: . $(i:M*): inner make: "directive-for-escape.mk" line 104: . $(i:M*): inner
make: "directive-for-escape.mk" line 91: . ${i${:U}}: outer make: "directive-for-escape.mk" line 105: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 92: . ${i\}}: inner} make: "directive-for-escape.mk" line 106: . ${i\}}: inner}
make: "directive-for-escape.mk" line 93: . ${i2}: two make: "directive-for-escape.mk" line 107: . ${i2}: two
make: "directive-for-escape.mk" line 94: . ${i,}: comma make: "directive-for-escape.mk" line 108: . ${i,}: comma
make: "directive-for-escape.mk" line 95: . adjacent: innerinnerinnerinner make: "directive-for-escape.mk" line 109: . adjacent: innerinnerinnerinner
make: no target to make. exit status 0
make: stopped in unit-tests
exit status 2

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-escape.mk,v 1.3 2020/12/31 14:26:37 rillig Exp $ # $NetBSD: directive-for-escape.mk,v 1.6 2021/01/25 19:05:39 rillig Exp $
# #
# Test escaping of special characters in the iteration values of a .for loop. # Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this # These values get expanded later using the :U variable modifier, and this
@ -41,17 +41,31 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
. info $i . info $i
.endfor .endfor
# Cover the code for nested '{}' in for_var_len. # Try to cover the code for nested '{}' in for_var_len, without success.
# #
# The value of VALUES is not a variable expression. Instead, it is meant to # The value of VALUES is not meant to be a variable expression. Instead, it
# represent dollar, lbrace, "UNDEF:U", backslash, dollar, backslash, dollar, # is meant to represent dollar, lbrace, "UNDEF:U", backslash, dollar,
# space, nested braces, space, "end}". # backslash, dollar, space, nested braces, space, "end}".
#
# The .for loop splits ${VALUES} into 3 words, at the space characters, since
# these are not escaped.
VALUES= $${UNDEF:U\$$\$$ {{}} end} VALUES= $${UNDEF:U\$$\$$ {{}} end}
# XXX: Where does the '\$$\$$' get converted into a single '\$'? # XXX: Where does the '\$$\$$' get converted into a single '\$'?
.for i in ${VALUES} .for i in ${VALUES}
. info $i . info $i
.endfor .endfor
# Second try to cover the code for nested '{}' in for_var_len.
#
# XXX: It is wrong that for_var_len requires the braces to be balanced.
# Each variable modifier has its own inconsistent way of parsing nested
# variable expressions, braces and parentheses. The only sensible thing
# to do is therefore to let Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES}
. info $i
.endfor
# A single trailing dollar doesn't happen in practice. # A single trailing dollar doesn't happen in practice.
# The dollar sign is correctly passed through to the body of the .for loop. # The dollar sign is correctly passed through to the body of the .for loop.
# There, it is expanded by the .info directive, but even there a trailing # There, it is expanded by the .info directive, but even there a trailing
@ -89,8 +103,10 @@ i,= comma
. info . $$(i): $(i) . info . $$(i): $(i)
. info . $$(i:M*): $(i:M*) . info . $$(i:M*): $(i:M*)
. info . $${i$${:U}}: ${i${:U}} . info . $${i$${:U}}: ${i${:U}}
. info . $${i\}}: ${i\}} # XXX: unclear why SubstVarLong needs this . info . $${i\}}: ${i\}} # XXX: unclear why ForLoop_SubstVarLong needs this
. info . $${i2}: ${i2} . info . $${i2}: ${i2}
. info . $${i,}: ${i,} . info . $${i,}: ${i,}
. info . adjacent: $i${i}${i:M*}$i . info . adjacent: $i${i}${i:M*}$i
.endfor .endfor
all:

View File

@ -1,2 +1,4 @@
make: "directive-ifdef.mk" line 12: Function calls in .ifdef are possible. make: "directive-ifdef.mk" line 12: Function calls in .ifdef are possible.
make: "directive-ifdef.mk" line 23: String literals are tested for emptiness.
make: "directive-ifdef.mk" line 27: String literals are tested for emptiness. Whitespace is non-empty.
exit status 0 exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-ifdef.mk,v 1.3 2020/11/08 22:38:28 rillig Exp $ # $NetBSD: directive-ifdef.mk,v 1.4 2021/01/21 23:03:41 rillig Exp $
# #
# Tests for the .ifdef directive. # Tests for the .ifdef directive.
@ -14,5 +14,20 @@ DEFINED= defined
. error . error
.endif .endif
# String literals are handled the same in all variants of the .if directive.
# They evaluate to true if they are not empty. Whitespace counts as non-empty
# as well.
.ifdef ""
. error
.else
. info String literals are tested for emptiness.
.endif
.ifdef " "
. info String literals are tested for emptiness. Whitespace is non-empty.
.else
. error
.endif
all: all:
@:; @:;

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-include-fatal.mk,v 1.2 2020/09/13 10:20:11 rillig Exp $ # $NetBSD: directive-include-fatal.mk,v 1.3 2021/02/01 22:16:57 rillig Exp $
# #
# Test for the .include directive combined with fatal errors. # Test for the .include directive combined with fatal errors.
# #
@ -6,7 +6,7 @@
# suspicious, as if it were possible to suppress fatal errors by including # suspicious, as if it were possible to suppress fatal errors by including
# another file. It was a false alarm though, since Parse_File only handles # another file. It was a false alarm though, since Parse_File only handles
# the top-level makefiles from the command line. Any included files are # the top-level makefiles from the command line. Any included files are
# handled by Parse_include_file instead, and that function does not reset # handled by IncludeFile instead, and that function does not reset
# the "fatals" counter. # the "fatals" counter.
# Using an undefined variable in a condition generates a fatal error. # Using an undefined variable in a condition generates a fatal error.

View File

@ -1,4 +1,4 @@
# $NetBSD: envfirst.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ # $NetBSD: envfirst.mk,v 1.5 2021/02/04 21:42:47 rillig Exp $
# #
# The -e option makes environment variables stronger than global variables. # The -e option makes environment variables stronger than global variables.
@ -33,7 +33,7 @@ FROM_ENV?= default
. error ${FROM_ENV} . error ${FROM_ENV}
.endif .endif
# Even .undef doesn't work since it only affects the global context, # Even .undef doesn't work since it only affects the global scope,
# which is independent from the environment variables. # which is independent from the environment variables.
.undef FROM_ENV .undef FROM_ENV
.if ${FROM_ENV} != value-from-env .if ${FROM_ENV} != value-from-env

View File

@ -1,4 +1,5 @@
MAKELEVEL=1 MAKELEVEL=1
TMPDIR=TMPDIR
UT_DOLLAR=This is $UT_FU UT_DOLLAR=This is $UT_FU
UT_FOO=foobar is fubar UT_FOO=foobar is fubar
UT_FU=fubar UT_FU=fubar

View File

@ -1,11 +1,11 @@
#*** Input graph: #*** Input graph:
# all, made UNMADE, type OP_DEPENDS, flags none # all, unmade, type OP_DEPENDS, flags none
# makeinfo, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # makeinfo, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# make-index, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none # make-index, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
# braces-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none # braces-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
# braces-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none # braces-no-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
# braces-no-dot-modifier, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # braces-no-dot-modifier, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# parentheses-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none # parentheses-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
# parentheses-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none # parentheses-no-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
exit status 0 exit status 0

View File

@ -2,7 +2,14 @@ make: "include-main.mk" line 14: main-before-ok
make: "include-main.mk" line 21: main-before-for-ok make: "include-main.mk" line 21: main-before-for-ok
make: "include-sub.mk" line 4: sub-before-ok make: "include-sub.mk" line 4: sub-before-ok
make: "include-sub.mk" line 14: sub-before-for-ok make: "include-sub.mk" line 14: sub-before-for-ok
make: "include-subsub.mk" line 4: subsub-ok ParseReadLine (5): '. info subsub-ok'
make: "include-subsub.mk" line 5: subsub-ok
in .for loop from include-sub.mk:31
in .for loop from include-sub.mk:30
in .for loop from include-sub.mk:29
in .include from include-main.mk:27
ParseReadLine (6): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0)
make: "include-sub.mk" line 38: sub-after-ok make: "include-sub.mk" line 38: sub-after-ok
make: "include-sub.mk" line 45: sub-after-for-ok make: "include-sub.mk" line 45: sub-after-for-ok
make: "include-main.mk" line 30: main-after-ok make: "include-main.mk" line 30: main-after-ok

View File

@ -1,4 +1,4 @@
# $NetBSD: include-main.mk,v 1.5 2020/09/05 18:18:05 rillig Exp $ # $NetBSD: include-main.mk,v 1.6 2021/01/22 00:44:55 rillig Exp $
# #
# Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave # Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave
# as described in the manual page. # as described in the manual page.
@ -17,7 +17,7 @@
.endif .endif
.for i in once .for i in once
. if !defined(${.INCLUDEDFROMFILE}) . if !defined(.INCLUDEDFROMFILE)
. info main-before-for-ok . info main-before-for-ok
. else . else
. warning main-before-for-fail(${.INCLUDEDFROMFILE}) . warning main-before-for-fail(${.INCLUDEDFROMFILE})
@ -33,7 +33,7 @@
.endif .endif
.for i in once .for i in once
. if !defined(${.INCLUDEDFROMFILE}) . if !defined(.INCLUDEDFROMFILE)
. info main-after-for-ok . info main-after-for-ok
. else . else
. warning main-after-for-fail(${.INCLUDEDFROMFILE}) . warning main-after-for-fail(${.INCLUDEDFROMFILE})

View File

@ -1,7 +1,9 @@
# $NetBSD: include-subsub.mk,v 1.3 2020/09/05 18:13:47 rillig Exp $ # $NetBSD: include-subsub.mk,v 1.4 2021/01/26 23:44:56 rillig Exp $
.if ${.INCLUDEDFROMFILE} == "include-sub.mk" .if ${.INCLUDEDFROMFILE} == "include-sub.mk"
.MAKEFLAGS: -dp
. info subsub-ok . info subsub-ok
.MAKEFLAGS: -d0
.else .else
. warning subsub-fail(${.INCLUDEDFROMFILE}) . warning subsub-fail(${.INCLUDEDFROMFILE})
.endif .endif

View File

@ -0,0 +1,2 @@
action
exit status 0

View File

@ -0,0 +1,18 @@
# $NetBSD: jobs-empty-commands.mk,v 1.2 2021/01/30 12:46:38 rillig Exp $
#
# In jobs mode, the shell commands for creating a target are written to a
# temporary file first, which is then run by the shell. In chains of
# dependencies, these files would end up empty. Since job.c 1.399 from
# 2021-01-29, these empty files are no longer created.
#
# https://mail-index.netbsd.org/current-users/2021/01/26/msg040215.html
.MAKEFLAGS: -j1
#.MAKEFLAGS: -dn # to see the created temporary files
all: .PHONY step-1
.for i i_plus_1 in ${:U:range=100:@i@$i $i@:[2..199]}
step-$i: .PHONY step-${i_plus_1}
.endfor
step-100: .PHONY
@echo 'action'

View File

@ -1,10 +1,13 @@
# $NetBSD: lint.mk,v 1.3 2020/09/15 16:22:04 rillig Exp $ # $NetBSD: lint.mk,v 1.4 2021/01/30 13:50:18 rillig Exp $
# #
# Demonstrates stricter checks that are only enabled in the lint mode, # Demonstrates stricter checks that are only enabled in lint mode, using the
# using the -dL option. # option -dL.
# Ouch: as of 2020-08-03, make exits successfully even though the error # Before main.c 1.421 from 2020-11-01, make exited successfully even though
# message has been issued as PARSE_FATAL. # the error message had been issued as PARSE_FATAL. This was because back
# then, make checked for parse errors only after parsing each top-level
# makefile, in Parse_File. After that, when expanding variable expressions
# in shell commands, the parse errors were not checked again.
# Ouch: as of 2020-08-03, the variable is malformed and parsing stops # Ouch: as of 2020-08-03, the variable is malformed and parsing stops
# for a moment, but is continued after the wrongly-guessed end of the # for a moment, but is continued after the wrongly-guessed end of the

View File

@ -1,5 +1,5 @@
make warning: OBJDIR/roobj: Permission denied. make warning: TMPDIR/roobj: Permission denied.
/tmp /tmp
OBJDIR/roobj TMPDIR/roobj
OBJDIR/roobj TMPDIR/roobj
exit status 0 exit status 0

View File

@ -1,12 +1,12 @@
#*** Input graph: #*** Input graph:
# all, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # all, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# made-target, made UNMADE, type OP_DEPENDS, flags none # made-target, unmade, type OP_DEPENDS, flags none
# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none # made-target-no-sources, unmade, type OP_DEPENDS, flags none
# made-source, made UNMADE, type OP_DEPENDS, flags none # made-source, unmade, type OP_DEPENDS, flags none
# unmade-target, made UNMADE, type OP_DEPENDS, flags none # unmade-target, unmade, type OP_DEPENDS, flags none
# unmade-sources, made UNMADE, type none, flags none # unmade-sources, unmade, type none, flags none
# unmade-silent-source, made UNMADE, type OP_SILENT, flags none # unmade-silent-source, unmade, type OP_SILENT, flags none
# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none # unmade-target-no-sources, unmade, type OP_DEPENDS, flags none
# #

View File

@ -1 +1,91 @@
exit status 0 : 'Making made-target.'
false
*** Error code 1 (continuing)
false
*** Error code 1 (continuing)
`all' not remade because of errors.
#*** Input graph:
# made-target, made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# *** MAIN TARGET ***
# No unmade children
# last modified <timestamp>: made
# parents: all
made-target :
(null)
# error-target, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# No unmade children
# nonexistent (maybe): error when made
# parents: all
error-target :
(null)
# aborted-target, aborted, type OP_DEPENDS|OP_PHONY|OP_DEPS_FOUND|OP_MARK, flags none
# aborted-target-dependency, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# No unmade children
# nonexistent (maybe): error when made
# parents: aborted-target
aborted-target-dependency:
(null)
# all, aborted, type OP_DEPENDS|OP_DEPS_FOUND, flags CHILDMADE|FORCE
#
# 3 unmade children
# nonexistent (maybe): aborted
all : made-target error-target aborted-target
# .END, unmade, type OP_SPECIAL, flags none
#
# Files that are only sources:
# .END [.END]
#*** Global Variables:
.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END
.CURDIR = <curdir>
.INCLUDES =
.LIBS =
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
.MAKE.LEVEL = <details omitted>
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
.MAKE.PID = <details omitted>
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g2
.MAKEOVERRIDES =
.OBJDIR = <curdir>
.PATH = . <curdir>
.TARGETS = all
.newline =
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g2
#*** Command-line Variables:
.MAKE.LEVEL.ENV = MAKELEVEL
.SHELL = <details omitted>
#*** Directory Cache:
# Stats: 0 hits 4 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
# 1 0 .
#*** Suffixes:
#*** Transformations:
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -1,9 +1,23 @@
# $NetBSD: opt-debug-graph2.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $ # $NetBSD: opt-debug-graph2.mk,v 1.3 2021/02/02 17:47:56 rillig Exp $
# #
# Tests for the -dg2 command line option, which prints the input # Tests for the -dg2 command line option, which prints the input
# graph after making everything, or before exiting on error. # graph after making everything, or before exiting on error.
#
# Before compat.c 1.222 from 2021-02-02, there was no debug output despite
# the error.
# TODO: Implementation .MAKEFLAGS: -dg2
all: .MAIN: all
@:;
made-target: .PHONY
: 'Making $@.'
error-target: .PHONY
false
aborted-target: .PHONY aborted-target-dependency
aborted-target-dependency: .PHONY
false
all: made-target error-target aborted-target

View File

@ -1 +1,91 @@
exit status 0 : 'Making made-target.'
false
*** Error code 1 (continuing)
false
*** Error code 1 (continuing)
`all' not remade because of errors.
#*** Input graph:
# made-target, made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# *** MAIN TARGET ***
# No unmade children
# last modified <timestamp>: made
# parents: all
made-target :
(null)
# error-target, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# No unmade children
# nonexistent (maybe): error when made
# parents: all
error-target :
(null)
# aborted-target, aborted, type OP_DEPENDS|OP_PHONY|OP_DEPS_FOUND|OP_MARK, flags none
# aborted-target-dependency, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC
#
# No unmade children
# nonexistent (maybe): error when made
# parents: aborted-target
aborted-target-dependency:
(null)
# all, aborted, type OP_DEPENDS|OP_DEPS_FOUND, flags CHILDMADE|FORCE
#
# 3 unmade children
# nonexistent (maybe): aborted
all : made-target error-target aborted-target
# .END, unmade, type OP_SPECIAL, flags none
#
# Files that are only sources:
# .END [.END]
#*** Global Variables:
.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END
.CURDIR = <curdir>
.INCLUDES =
.LIBS =
.MAKE = <details omitted>
.MAKE.DEPENDFILE = <details omitted>
.MAKE.GID = <details omitted>
.MAKE.LEVEL = <details omitted>
.MAKE.MAKEFILES = <details omitted>
.MAKE.MAKEFILE_PREFERENCE = <details omitted>
.MAKE.OS = <details omitted>
.MAKE.PID = <details omitted>
.MAKE.PPID = <details omitted>
.MAKE.UID = <details omitted>
.MAKEFLAGS = -r -k -d g3
.MAKEOVERRIDES =
.OBJDIR = <curdir>
.PATH = . <curdir>
.TARGETS = all
.newline =
MACHINE = <details omitted>
MACHINE_ARCH = <details omitted>
MAKE = <details omitted>
MFLAGS = -r -k -d g3
#*** Command-line Variables:
.MAKE.LEVEL.ENV = MAKELEVEL
.SHELL = <details omitted>
#*** Directory Cache:
# Stats: 0 hits 4 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
# 1 0 .
#*** Suffixes:
#*** Transformations:
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -1,9 +1,23 @@
# $NetBSD: opt-debug-graph3.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $ # $NetBSD: opt-debug-graph3.mk,v 1.3 2021/02/02 17:47:56 rillig Exp $
# #
# Tests for the -dg3 command line option, which prints the input # Tests for the -dg3 command line option, which prints the input
# graph before exiting on error. # graph before exiting on error.
#
# Before compat.c 1.222 from 2021-02-02, there was no debug output despite
# the error.
# TODO: Implementation .MAKEFLAGS: -dg3
all: .MAIN: all
@:;
made-target: .PHONY
: 'Making $@.'
error-target: .PHONY
false
aborted-target: .PHONY aborted-target-dependency
aborted-target-dependency: .PHONY
false
all: made-target error-target aborted-target

View File

@ -0,0 +1,11 @@
echo ": Making opt-touch-phony."
{ : Making opt-touch-phony.
} || exit $?
echo 'Making opt-touch-make.'
Making opt-touch-make.
echo ": Making opt-touch-regular."
{ : Making opt-touch-regular.
} || exit $?
`opt-touch-join' is up to date.
`opt-touch-use' is up to date.
exit status 0

View File

@ -0,0 +1,48 @@
# $NetBSD: opt-no-action-touch.mk,v 1.1 2021/01/30 12:46:38 rillig Exp $
#
# Tests for combining the command line options -n (no action) and -t (touch).
# This combination is unusual and probably doesn't ever happen in practice,
# but still make needs to behave as expected. The option -n is stronger than
# -t, so instead of being touched, the commands of the targets are printed.
#
# See also:
# opt-touch-jobs.mk contains the same test without the option -n.
.MAKEFLAGS: -j1 -n -t
.MAKEFLAGS: opt-touch-phony
.MAKEFLAGS: opt-touch-join
.MAKEFLAGS: opt-touch-use
.MAKEFLAGS: opt-touch-make
.MAKEFLAGS: opt-touch-regular
# .PHONY targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-phony: .PHONY
: Making $@.
# .JOIN targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-join: .JOIN
: Making $@.
# .USE targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-use: .USE
: Making use of $@.
# The attribute .MAKE is stronger than the command line option -n. Therefore
# this target is run as usual. It is not prefixed by '@', therefore it is
# printed before being run.
opt-touch-make: .MAKE
echo 'Making $@.'
# Since the option -n is stronger than the option -t, this target is not
# touched either. Without the -n, it would be touched.
opt-touch-regular:
: Making $@.
# Since none of the above targets are actually touched, the following command
# does not output anything.
.END:
@files=$$(ls opt-touch-* 2>/dev/null | grep -v -e '\.'); \
[ -z "$$files" ] || { echo "created files: $$files" 1>&2; exit 1; }

View File

@ -1,4 +1,4 @@
# $NetBSD: opt-touch-jobs.mk,v 1.1 2020/11/14 15:35:20 rillig Exp $ # $NetBSD: opt-touch-jobs.mk,v 1.2 2021/01/30 12:14:08 rillig Exp $
# #
# Tests for the -t command line option in jobs mode. # Tests for the -t command line option in jobs mode.
@ -9,12 +9,18 @@
.MAKEFLAGS: opt-touch-use .MAKEFLAGS: opt-touch-use
.MAKEFLAGS: opt-touch-make .MAKEFLAGS: opt-touch-make
# .PHONY targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-phony: .PHONY opt-touch-phony: .PHONY
: Making $@. : Making $@.
# .JOIN targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-join: .JOIN opt-touch-join: .JOIN
: Making $@. : Making $@.
# .USE targets are not touched since they do not represent actual files.
# See Job_Touch.
opt-touch-use: .USE opt-touch-use: .USE
: Making use of $@. : Making use of $@.

View File

@ -1,6 +1,6 @@
make: "opt-warnings-as-errors.mk" line 7: warning: message 1 make: "opt-warnings-as-errors.mk" line 12: warning: message 1
make: parsing warnings being treated as errors make: parsing warnings being treated as errors
make: "opt-warnings-as-errors.mk" line 8: warning: message 2 make: "opt-warnings-as-errors.mk" line 13: warning: message 2
parsing continues parsing continues
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests

View File

@ -1,6 +1,11 @@
# $NetBSD: opt-warnings-as-errors.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ # $NetBSD: opt-warnings-as-errors.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $
# #
# Tests for the -W command line option, which turns warnings into errors. # Tests for the -W command line option, which turns warnings into errors.
#
# Even in -W mode, a .warning is not completely equivalent to an .error.
# First, the word "warning" is still printed, and second, parsing continues
# after a failed warning, whereas it would stop immediately at the first
# .error.
.MAKEFLAGS: -W .MAKEFLAGS: -W

View File

@ -18,8 +18,8 @@ defining transformation from `.a' to `.c'
inserting ".a" (1) at end of list inserting ".a" (1) at end of list
inserting ".c" (3) at end of list inserting ".c" (3) at end of list
# LinkSource: added child .a.c - ${.PREFIX}.dependency # LinkSource: added child .a.c - ${.PREFIX}.dependency
# .a.c, made UNMADE, type OP_DEPENDS|OP_TRANSFORM, flags none # .a.c, unmade, type OP_DEPENDS|OP_TRANSFORM, flags none
# ${.PREFIX}.dependency, made UNMADE, type none, flags none # ${.PREFIX}.dependency, unmade, type none, flags none
ParseReadLine (23): '.DEFAULT:' ParseReadLine (23): '.DEFAULT:'
transformation .a.c complete transformation .a.c complete
ParseDoDependency(.DEFAULT:) ParseDoDependency(.DEFAULT:)

View File

@ -59,17 +59,17 @@ ParseReadLine (39): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}' ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}'
ParseDoDependency(next-main: suff-main-several.{2,3,4}) ParseDoDependency(next-main: suff-main-several.{2,3,4})
# LinkSource: added child next-main - suff-main-several.{2,3,4} # LinkSource: added child next-main - suff-main-several.{2,3,4}
# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, made UNMADE, type none, flags none # suff-main-several.{2,3,4}, unmade, type none, flags none
ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1' ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
ParseDoDependency(.MAKEFLAGS: -d0 -dg1) ParseDoDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph: #*** Input graph:
# .1.2, made UNMADE, type OP_TRANSFORM, flags none # .1.2, unmade, type OP_TRANSFORM, flags none
# .1.3, made UNMADE, type OP_TRANSFORM, flags none # .1.3, unmade, type OP_TRANSFORM, flags none
# .1.4, made UNMADE, type OP_TRANSFORM, flags none # .1.4, unmade, type OP_TRANSFORM, flags none
# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.1, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none # suff-main-several.1, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, made UNMADE, type none, flags none # suff-main-several.{2,3,4}, unmade, type none, flags none
# #

View File

@ -1,5 +1,5 @@
#*** Input graph: #*** Input graph:
# all, made UNMADE, type OP_DEPENDS, flags none # all, unmade, type OP_DEPENDS, flags none
# #

Some files were not shown because too many files have changed in this diff Show More