Import 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 17:51:11 -08:00
parent 8e11a9b425
commit 8b6f73e37b
127 changed files with 3162 additions and 2139 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>
* VERSION (_MAKE_VERSION): 20210110

6
FILES
View File

@ -83,6 +83,8 @@ unit-tests/cmd-errors.exp
unit-tests/cmd-errors.mk
unit-tests/cmd-interrupt.exp
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.mk
unit-tests/cmdline.exp
@ -397,6 +399,8 @@ unit-tests/job-flags.exp
unit-tests/job-flags.mk
unit-tests/job-output-long-lines.exp
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.mk
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-runflags.exp
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.mk
unit-tests/opt-query.exp

View File

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

24
arch.c
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
@ -147,7 +147,7 @@ struct ar_hdr {
#include "dir.h"
/* "@(#)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 ListNode ArchListNode;
@ -225,14 +225,14 @@ ArchFree(void *ap)
* Input:
* pp The start of the specification.
* 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:
* return TRUE if it was a valid specification.
* *pp Points to the first non-space after the archive spec.
*/
Boolean
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
{
char *cp; /* Pointer into line */
GNode *gn; /* New node */
@ -255,7 +255,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
Boolean isError;
/* 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);
/* TODO: handle errors */
isError = result.str == var_Error;
@ -272,7 +272,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
*cp++ = '\0';
if (expandLibName) {
char *expanded;
(void)Var_Subst(libName.str, ctxt,
(void)Var_Subst(libName.str, scope,
VARE_WANTRES | VARE_UNDEFERR, &expanded);
/* TODO: handle errors */
libName = MFStr_InitOwn(expanded);
@ -298,7 +298,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
Boolean isError;
const char *nested_p = cp;
(void)Var_Parse(&nested_p, ctxt,
(void)Var_Parse(&nested_p, scope,
VARE_WANTRES | VARE_UNDEFERR,
&result);
/* TODO: handle errors */
@ -355,7 +355,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
char *p;
char *unexpandedMemName = memName;
(void)Var_Subst(memName, ctxt,
(void)Var_Subst(memName, scope,
VARE_WANTRES | VARE_UNDEFERR,
&memName);
/* TODO: handle errors */
@ -379,7 +379,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
gn->type |= OP_ARCHV;
Lst_Append(gns, gn);
} else if (!Arch_ParseArchive(&p, gns, ctxt)) {
} else if (!Arch_ParseArchive(&p, gns, scope)) {
/* Error in nested call. */
free(fullName);
/* XXX: does unexpandedMemName leak? */
@ -390,7 +390,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
} else if (Dir_HasWildcards(memName)) {
StringList members = LST_INIT;
Dir_Expand(memName, &dirSearchPath, &members);
SearchPath_Expand(&dirSearchPath, memName, &members);
while (!Lst_IsEmpty(&members)) {
char *member = Lst_Dequeue(&members);
@ -1022,9 +1022,9 @@ Arch_FindLib(GNode *gn, SearchPath *path)
free(libName);
#ifdef LIBRARIES
Var_Set(TARGET, gn->name, gn);
Var_Set(gn, TARGET, gn->name);
#else
Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn);
Var_Set(gn, TARGET, GNode_Path(gn));
#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
.\" The Regents of the University of California. All rights reserved.
@ -99,7 +99,7 @@ is equivalent to
.It Fl D Ar variable
Define
.Ar variable
to be 1, in the global context.
to be 1, in the global scope.
.It Fl d Ar [-]flags
Turn on debugging, and specify which portions of
.Nm
@ -355,7 +355,7 @@ Do not build any targets.
Multiple instances of this option may be specified;
the variables will be printed one per line,
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.
By default, the raw variable contents (which may
include additional unexpanded variable references) are shown.

View File

@ -37,7 +37,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
-C /etc.
-D variable
Define variable to be 1, in the global context.
Define variable to be 1, in the global scope.
-d [-]flags
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
printed one per line, with a blank line for each null or unde-
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-
able references) are shown. If variable contains a `$' then the
value will be recursively expanded to its complete resultant text

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

63
buf.c
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.
@ -75,7 +75,7 @@
#include "make.h"
/* "@(#)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. */
void
@ -92,7 +92,7 @@ Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
size_t old_len = buf->len;
char *end;
if (__predict_false(old_len + bytes_len >= buf->cap)) {
if (old_len + bytes_len >= buf->cap) {
size_t minIncr = bytes_len + 16;
buf->cap += buf->cap > minIncr ? buf->cap : minIncr;
buf->data = bmake_realloc(buf->data, buf->cap);
@ -135,21 +135,6 @@ Buf_AddInt(Buffer *buf, int n)
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. */
void
Buf_Empty(Buffer *buf)
@ -175,22 +160,35 @@ Buf_Init(Buffer *buf)
}
/*
* Reset the buffer.
* If freeData is TRUE, the data from the buffer is freed as well.
* Otherwise it is kept and returned.
* Free the data from the buffer.
* Leave the buffer itself in an indeterminate state.
*/
char *
Buf_Destroy(Buffer *buf, Boolean freeData)
void
Buf_Done(Buffer *buf)
{
char *data = buf->data;
if (freeData) {
free(data);
data = NULL;
}
free(buf->data);
#ifdef CLEANUP
buf->cap = 0;
buf->len = 0;
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;
}
@ -200,22 +198,23 @@ Buf_Destroy(Buffer *buf, Boolean freeData)
#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,
* a new buffer will be allocated and the old one freed.
*/
char *
Buf_DestroyCompact(Buffer *buf)
Buf_DoneDataCompact(Buffer *buf)
{
#if BUF_COMPACT_LIMIT > 0
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
/* We trust realloc to be smart */
char *data = bmake_realloc(buf->data, buf->len + 1);
data[buf->len] = '\0'; /* XXX: unnecessary */
Buf_Destroy(buf, FALSE);
Buf_DoneData(buf);
return data;
}
#endif
return Buf_Destroy(buf, FALSE);
return Buf_DoneData(buf);
}

25
buf.h
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.
@ -81,16 +81,11 @@
/* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer {
size_t cap; /* Allocated size of the buffer, including the null */
size_t len; /* Number of bytes in buffer, excluding the null */
size_t cap; /* Allocated size of the buffer, including the '\0' */
size_t len; /* Number of bytes in buffer, excluding the '\0' */
char *data; /* The buffer itself (always null-terminated) */
} 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 *);
/* 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++;
char *end;
if (__predict_false(old_len + 1 >= buf->cap))
if (old_len + 1 >= buf->cap)
Buf_Expand(buf);
end = buf->data + old_len;
end[0] = byte;
end[1] = '\0';
}
MAKE_INLINE size_t
Buf_Len(const Buffer *buf)
{
return buf->len;
}
MAKE_INLINE Boolean
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_AddStr(Buffer *, const char *);
void Buf_AddInt(Buffer *, int);
char *Buf_GetAll(Buffer *, size_t *);
void Buf_Empty(Buffer *);
void Buf_Init(Buffer *);
void Buf_InitSize(Buffer *, size_t);
char *Buf_Destroy(Buffer *, Boolean);
char *Buf_DestroyCompact(Buffer *);
void Buf_Done(Buffer *);
char *Buf_DoneData(Buffer *);
char *Buf_DoneDataCompact(Buffer *);
#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.
@ -69,7 +69,7 @@
* SUCH DAMAGE.
*/
/*-
/*
* compat.c --
* The routines in this file implement the full-compatibility
* mode of PMake. Most of the special functionality of PMake
@ -99,7 +99,7 @@
#include "pathnames.h"
/* "@(#)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 pid_t compatChild;
@ -260,7 +260,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
/*
* Append the expanded command, to prevent 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
* 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.
*/
compatChild = cpid = vFork();
compatChild = cpid = vfork();
if (cpid < 0) {
Fatal("Could not fork");
}
@ -512,7 +512,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
}
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
@ -605,7 +605,7 @@ MakeOther(GNode *gn, GNode *pgn)
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) {
const char *target = GNode_VarTarget(gn);
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
Var_Set(pgn, IMPSRC, target != NULL ? target : "");
}
switch (gn->made) {
@ -752,6 +752,10 @@ Compat_Run(GNodeList *targs)
}
if (errorNode != NULL) {
if (DEBUG(GRAPH2))
Targ_PrintGraph(2);
else if (DEBUG(GRAPH3))
Targ_PrintGraph(3);
PrintOnError(errorNode, "\nStop.");
exit(1);
}

645
cond.c
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.
@ -95,53 +95,65 @@
#include "dir.h"
/* "@(#)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:
* E -> F || E
* E -> F
* F -> T && F
* F -> T
* T -> defined(variable)
* T -> make(target)
* T -> exists(file)
* T -> empty(varspec)
* T -> target(name)
* T -> commands(name)
* T -> symbol
* T -> $(varspec) op value
* T -> $(varspec) == "string"
* T -> $(varspec) != "string"
* T -> "string"
* T -> ( E )
* T -> ! T
* op -> == | != | > | < | >= | <=
* Or -> And '||' Or
* Or -> And
* And -> Term '&&' And
* And -> Term
* Term -> Function '(' Argument ')'
* Term -> Leaf Operator Leaf
* Term -> Leaf
* Term -> '(' Or ')'
* Term -> '!' Term
* Leaf -> "string"
* Leaf -> Number
* Leaf -> VariableExpression
* Leaf -> Symbol
* Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
*
* '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:
* TOK_AND for '&' or '&&'
* TOK_OR for '|' or '||'
* TOK_AND for '&&'
* TOK_OR for '||'
* TOK_NOT for '!'
* TOK_LPAREN for '('
* TOK_RPAREN for ')'
*
* Other terminal symbols are evaluated using either the default function or
* the function given in the terminal, they return either TOK_TRUE or
* 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 {
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
} Token;
typedef enum CondResult {
CR_FALSE, CR_TRUE, CR_ERROR
} CondResult;
typedef enum ComparisonOp {
LT, LE, GT, GE, EQ, NE
} ComparisonOp;
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 */
Token curr; /* Single push-back token used in parsing */
@ -154,11 +166,13 @@ typedef struct CondParser {
Boolean printedError;
} 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_min_depth = 0; /* depth at makefile open */
static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
/*
* Indicate when we should be strict about lhs of comparisons.
* 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.
*/
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)
{
const char *p = *pp;
@ -254,7 +268,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
? VARE_WANTRES | VARE_UNDEFERR
: VARE_NONE;
FStr nestedVal;
(void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal);
(void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal);
/* TODO: handle errors */
Buf_AddStr(&argBuf, nestedVal.str);
FStr_Done(&nestedVal);
@ -268,16 +282,15 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
p++;
}
*out_arg = Buf_GetAll(&argBuf, &argLen);
Buf_Destroy(&argBuf, FALSE);
argLen = argBuf.len;
*out_arg = Buf_DoneData(&argBuf);
cpp_skip_hspace(&p);
if (func != NULL && *p++ != ')') {
Parse_Error(PARSE_WARNING,
"Missing closing parenthesis for %s()",
func);
/* The PARSE_FATAL follows in CondEvalExpression. */
Parse_Error(PARSE_FATAL,
"Missing closing parenthesis for %s()", func);
par->printedError = TRUE;
return 0;
}
@ -290,7 +303,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
static Boolean
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;
FStr_Done(&value);
return result;
@ -390,7 +403,70 @@ is_separator(char ch)
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
* 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_freeIt.
*/
/* coverity:[+alloc : arg-*4] */
static void
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
FStr *out_str, Boolean *out_quoted)
{
Buffer buf;
FStr str;
Boolean atStart;
const char *nested_p;
Boolean quoted;
const char *start;
VarEvalFlags eflags;
VarParseResult parseResult;
Buf_Init(&buf);
str = FStr_InitRefer(NULL);
@ -430,12 +501,10 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
}
continue;
case '"':
if (quoted) {
par->p++; /* skip the closing quote */
goto got_str;
}
Buf_AddByte(&buf, par->p[0]); /* likely? */
par->p++;
if (quoted)
goto got_str; /* skip the closing quote */
Buf_AddByte(&buf, '"');
continue;
case ')': /* see is_separator */
case '!':
@ -450,47 +519,9 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
par->p++;
continue;
case '$':
/* 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, 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);
if (!CondParser_StringExpr(par,
start, doEval, quoted, &buf, &str))
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;
default:
if (strictLHS && !quoted && *start != '$' &&
@ -508,38 +539,17 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
}
}
got_str:
str = FStr_InitOwn(Buf_GetAll(&buf, NULL));
str = FStr_InitOwn(buf.data);
cleanup:
Buf_Destroy(&buf, FALSE);
Buf_DoneData(&buf);
*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
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);
return if_info->doNot ? !res : res;
Boolean res = par->evalBare(arglen, arg);
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
* whether a variable of the given name were defined. */
/* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
if (par->if_info->form[0] == '\0')
if (par->plain)
return value[0] != '\0';
/* For the other variants of .ifxxx ${...}, use its default function. */
return If_Eval(par->if_info, value, strlen(value));
return If_Eval(par, value, strlen(value));
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
static Token
EvalCompareNum(double lhs, const char *op, double rhs)
static Boolean
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]) {
case '!':
if (op[1] != '=') {
Parse_Error(PARSE_WARNING, "Unknown operator");
/* The PARSE_FATAL follows in CondEvalExpression. */
return TOK_ERROR;
}
return ToToken(lhs != rhs);
case '=':
if (op[1] != '=') {
Parse_Error(PARSE_WARNING, "Unknown operator");
/* The PARSE_FATAL follows in CondEvalExpression. */
return TOK_ERROR;
}
return ToToken(lhs == rhs);
case '<':
return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs);
case '>':
return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs);
switch (op) {
case LT:
return lhs < rhs;
case LE:
return lhs <= rhs;
case GT:
return lhs > rhs;
case GE:
return lhs >= rhs;
case NE:
return lhs != rhs;
default:
return lhs == rhs;
}
return TOK_ERROR;
}
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] == '=')) {
Parse_Error(PARSE_WARNING,
"String comparison operator "
"must be either == or !=");
/* The PARSE_FATAL follows in CondEvalExpression. */
if (op != EQ && op != NE) {
Parse_Error(PARSE_FATAL,
"String comparison operator must be either == or !=");
par->printedError = TRUE;
return TOK_ERROR;
}
DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op);
return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0));
DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n",
lhs, rhs, opname[op]);
return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0));
}
/* Evaluate a comparison, such as "${VAR} == 12345". */
static Token
EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
const char *rhs, Boolean rhsQuoted)
EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
ComparisonOp op, const char *rhs, Boolean rhsQuoted)
{
double left, right;
if (!rhsQuoted && !lhsQuoted)
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;
FStr lhs, rhs;
const char *op;
ComparisonOp op;
Boolean lhsQuoted, rhsQuoted;
/*
@ -654,18 +691,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par);
op = par->p;
switch (par->p[0]) {
case '!':
case '=':
case '<':
case '>':
if (par->p[1] == '=')
par->p += 2;
else
par->p++;
break;
default:
if (!CondParser_ComparisonOp(par, &op)) {
/* Unknown operator, compare against an empty string or 0. */
t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
goto done_lhs;
@ -674,9 +700,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_SkipWhitespace(par);
if (par->p[0] == '\0') {
Parse_Error(PARSE_WARNING,
"Missing right-hand-side of operator");
/* The PARSE_FATAL follows in CondEvalExpression. */
Parse_Error(PARSE_FATAL,
"Missing right-hand-side of operator '%s'", opname[op]);
par->printedError = TRUE;
goto done_lhs;
}
@ -689,7 +715,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
goto done_rhs;
}
t = EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
done_rhs:
FStr_Done(&rhs);
@ -704,8 +730,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
*/
/*ARGSUSED*/
static size_t
ParseEmptyArg(const char **pp, Boolean doEval,
const char *func MAKE_ATTR_UNUSED, char **out_arg)
ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
Boolean doEval, const char *func MAKE_ATTR_UNUSED,
char **out_arg)
{
FStr val;
size_t magic_res;
@ -714,7 +741,7 @@ ParseEmptyArg(const char **pp, Boolean doEval,
*out_arg = NULL;
(*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);
/* TODO: handle errors */
/* 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 {
const char *fn_name;
size_t fn_name_len;
size_t (*fn_parse)(const char **, Boolean, const char *,
char **);
size_t (*fn_parse)(CondParser *, const char **, Boolean,
const char *, char **);
Boolean (*fn_eval)(size_t, const char *);
} fns[] = {
{ "defined", 7, ParseFuncArg, FuncDefined },
@ -779,7 +806,7 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
if (*cp != '(')
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) {
par->p = cp;
*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
* as an expression.
*/
arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
cp1 = cp;
cpp_skip_whitespace(&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
* 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);
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
* or TOK_NOT and a term (not including the binary operators):
*
* T -> defined(variable) | make(target) | exists(file) | symbol
* T -> ! T | ( E )
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
* Term -> '(' Or ')'
* Term -> '!' Term
* Term -> Leaf Operator Leaf
* Term -> Leaf
*/
static Token
static CondResult
CondParser_Term(CondParser *par, Boolean doEval)
{
CondResult res;
Token t;
t = CondParser_Token(par, doEval);
if (t == TOK_TRUE)
return CR_TRUE;
if (t == TOK_FALSE)
return CR_FALSE;
if (t == TOK_EOF) {
/*
* If we reached the end of the expression, the expression
* is malformed...
*/
t = TOK_ERROR;
} else if (t == TOK_LPAREN) {
/*
* 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;
}
if (t == TOK_LPAREN) {
res = CondParser_Or(par, doEval);
if (res == CR_ERROR)
return CR_ERROR;
if (CondParser_Token(par, doEval) != TOK_RPAREN)
return CR_ERROR;
return res;
}
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?)
*
* F -> T && F | T
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR
* And -> Term '&&' And
* And -> Term
*/
static Token
CondParser_Factor(CondParser *par, Boolean doEval)
static CondResult
CondParser_And(CondParser *par, Boolean doEval)
{
Token l, o;
CondResult res;
Token op;
l = CondParser_Term(par, doEval);
if (l != TOK_ERROR) {
o = CondParser_Token(par, doEval);
res = CondParser_Term(par, doEval);
if (res == CR_ERROR)
return CR_ERROR;
if (o == TOK_AND) {
/*
* F -> T && F
*
* If T is TOK_FALSE, the whole thing will be
* TOK_FALSE, but we have to parse the r.h.s. anyway
* (to throw it away). If T is TOK_TRUE, the result
* 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);
}
op = CondParser_Token(par, doEval);
if (op == TOK_AND) {
if (res == CR_TRUE)
return CondParser_And(par, doEval);
if (CondParser_And(par, FALSE) == CR_ERROR)
return CR_ERROR;
return res;
}
return l;
CondParser_PushBack(par, op);
return res;
}
/*
* Main expression production.
*
* E -> F || E | F
*
* Results:
* TOK_TRUE, TOK_FALSE or TOK_ERROR.
* Or -> And '||' Or
* Or -> And
*/
static Token
CondParser_Expr(CondParser *par, Boolean doEval)
static CondResult
CondParser_Or(CondParser *par, Boolean doEval)
{
Token l, o;
CondResult res;
Token op;
l = CondParser_Factor(par, doEval);
if (l != TOK_ERROR) {
o = CondParser_Token(par, doEval);
res = CondParser_And(par, doEval);
if (res == CR_ERROR)
return CR_ERROR;
if (o == TOK_OR) {
/*
* E -> F || E
*
* A similar thing occurs for ||, except that here
* we make sure the l.h.s. is TOK_FALSE before we
* bother to evaluate the r.h.s. Once again, if l
* 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);
}
op = CondParser_Token(par, doEval);
if (op == TOK_OR) {
if (res == CR_FALSE)
return CondParser_Or(par, doEval);
if (CondParser_Or(par, FALSE) == CR_ERROR)
return CR_ERROR;
return res;
}
return l;
CondParser_PushBack(par, op);
return res;
}
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);
res = CondParser_Expr(par, TRUE);
if (res != TOK_FALSE && res != TOK_TRUE)
res = CondParser_Or(par, TRUE);
if (res == CR_ERROR)
return COND_INVALID;
if (CondParser_Token(par, FALSE) != TOK_EOF)
return COND_INVALID;
*value = res == TOK_TRUE;
*out_value = res == CR_TRUE;
return COND_PARSE;
}
@ -1066,7 +1058,8 @@ CondParser_Eval(CondParser *par, Boolean *value)
* (*value) is set to the boolean value of the condition
*/
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)
{
CondParser par;
@ -1076,12 +1069,14 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
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.curr = TOK_NONE;
par.printedError = FALSE;
rval = CondParser_Eval(&par, value);
rval = CondParser_Eval(&par, out_value);
if (rval == COND_INVALID && eprint && !par.printedError)
Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
@ -1096,7 +1091,8 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
CondEvalResult
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
@ -1106,6 +1102,43 @@ IsEndif(const char *p)
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:
*
@ -1159,7 +1192,9 @@ Cond_EvalLine(const char *line)
static enum IfState *cond_states = NULL;
static unsigned int cond_states_cap = 128;
const struct If *ifp;
Boolean plain;
Boolean (*evalBare)(size_t, const char *);
Boolean negate;
Boolean isElif;
Boolean value;
IfState state;
@ -1242,29 +1277,8 @@ Cond_EvalLine(const char *line)
return COND_INVALID; /* Not an ifxxx or elifxxx line */
}
/*
* Figure out what sort of conditional it is -- what its default
* 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 (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
return COND_INVALID;
if (isElif) {
if (cond_depth == cond_min_depth) {
@ -1308,7 +1322,8 @@ Cond_EvalLine(const char *line)
}
/* 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. */
/* Skip everything to matching .endif */
/* 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. */
#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. */
#undef size_t

36
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# 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>.
#
@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
PACKAGE_VERSION='20201112'
PACKAGE_STRING='bmake 20201112'
PACKAGE_VERSION='20210201'
PACKAGE_STRING='bmake 20210201'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
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.
# This message is too long to be a string in the A/UX 3.1 sh.
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]...
@ -1316,7 +1316,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of bmake 20201112:";;
short | recursive ) echo "Configuration of bmake 20210201:";;
esac
cat <<\_ACEOF
@ -1422,7 +1422,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
bmake configure 20201112
bmake configure 20210201
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -2002,7 +2002,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
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
$ $0 $@
@ -5322,6 +5322,24 @@ $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h
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_n "checking return type of signal handlers... " >&6; }
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
# values after options handling.
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
CONFIG_FILES = $CONFIG_FILES
@ -6728,7 +6746,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
bmake config.status 20201112
bmake config.status 20210201
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@ -1,11 +1,11 @@
dnl
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 Process this file with autoconf to produce a configure script
dnl
AC_PREREQ(2.50)
AC_INIT([bmake], [20201112], [sjg@NetBSD.org])
AC_INIT([bmake], [20210201], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@ -185,6 +185,17 @@ AC_DECL_SYS_SIGLIST
AC_HEADER_TIME
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.
AC_TYPE_SIGNAL
AC_FUNC_VFORK

454
dir.c
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.
@ -89,8 +89,9 @@
* Returns TRUE if the name given it needs to
* be wildcard-expanded.
*
* Dir_Expand Given a pattern and a path, return a Lst of names
* which match the pattern on the search path.
* SearchPath_Expand
* Expand a filename pattern to find all matching files
* from the search path.
*
* Dir_FindFile Searches for a file on a given search path.
* If it exists, the entire path is returned.
@ -105,7 +106,7 @@
* Update the modification time and path of a node with
* 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
* Given a search path and a command flag, create
@ -137,7 +138,7 @@
#include "job.h"
/* "@(#)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
@ -252,7 +253,7 @@ typedef enum CachedStatsFlags {
} CachedStatsFlags;
SearchPath dirSearchPath = LST_INIT; /* main search path */
SearchPath dirSearchPath = { LST_INIT }; /* main search path */
static OpenDirs openDirs; /* all cached directories */
@ -484,18 +485,18 @@ Dir_Init(void)
* Called by Dir_InitDir and whenever .CURDIR is assigned to.
*/
void
Dir_InitCur(const char *cdname)
Dir_InitCur(const char *newCurdir)
{
CachedDir *dir;
if (cdname == NULL)
if (newCurdir == NULL)
return;
/*
* Our build directory is not the same as our source directory.
* Keep this one around too.
*/
dir = Dir_AddDir(NULL, cdname);
dir = SearchPath_Add(NULL, newCurdir);
if (dir == NULL)
return;
@ -511,7 +512,7 @@ Dir_InitDot(void)
{
CachedDir *dir;
dir = Dir_AddDir(NULL, ".");
dir = SearchPath_Add(NULL, ".");
if (dir == NULL) {
Error("Cannot open `.' (%s)", strerror(errno));
exit(2); /* Not 1 so -q can distinguish error */
@ -548,37 +549,37 @@ Dir_SetPATH(void)
CachedDirListNode *ln;
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;
if (dir == dotLast) {
seenDotLast = TRUE;
Var_Append(".PATH", dotLast->name, VAR_GLOBAL);
Global_Append(".PATH", dotLast->name);
}
}
if (!seenDotLast) {
if (dot != NULL)
Var_Append(".PATH", dot->name, VAR_GLOBAL);
Global_Append(".PATH", dot->name);
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;
if (dir == dotLast)
continue;
if (dir == dot && seenDotLast)
continue;
Var_Append(".PATH", dir->name, VAR_GLOBAL);
Global_Append(".PATH", dir->name);
}
if (seenDotLast) {
if (dot != NULL)
Var_Append(".PATH", dot->name, VAR_GLOBAL);
Global_Append(".PATH", dot->name);
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
* 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,
* 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:
* pattern Pattern to look for
@ -667,7 +670,8 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
* directory cache.
*
* 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] != '.')
continue;
@ -797,7 +801,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
suffix, suffix_len);
if (contains_wildcard(file)) {
Dir_Expand(file, path, expansions);
SearchPath_Expand(path, file, expansions);
free(file);
} else {
Lst_Append(expansions, file);
@ -814,7 +818,7 @@ static void
DirExpandPath(const char *word, SearchPath *path, StringList *expansions)
{
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;
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
* directories on the given search path.
* The wildcard isn't in the first component.
* 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:
* word the word to expand
* path the directories in which to find the files
* pattern the pattern to expand
* expansions the list on which to place the results
*/
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(expansions != NULL);
DEBUG1(DIR, "Expanding \"%s\"... ", word);
DEBUG1(DIR, "Expanding \"%s\"... ", pattern);
cp = strchr(word, '{');
if (cp != NULL) {
DirExpandCurly(word, cp, path, expansions);
brace = strchr(pattern, '{');
if (brace != NULL) {
DirExpandCurly(pattern, brace, path, expansions);
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. */
DirMatchFiles(word, dot, expansions);
DirMatchFiles(pattern, dot, expansions);
/* Then the files in every other directory on the path. */
DirExpandPath(word, path, expansions);
DirExpandPath(pattern, path, expansions);
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. */
for (cp = word; *cp != '\0'; cp++)
if (*cp == '?' || *cp == '[' || *cp == '*')
/* Find the first wildcard in the pattern. */
for (wildcard = pattern; *wildcard != '\0'; wildcard++)
if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*')
break;
if (*cp == '\0') {
if (*wildcard == '\0') {
/*
* No directory component and no wildcard at all -- this
* should never happen as in such a simple case there is no
* need to expand anything.
*/
DirExpandPath(word, path, expansions);
DirExpandPath(pattern, path, expansions);
goto done;
}
/* Back up to the start of the component containing the wildcard. */
/* XXX: This handles '///' and '/' differently. */
while (cp > word && *cp != '/')
cp--;
wildcardComponent = wildcard;
while (wildcardComponent > pattern && *wildcardComponent != '/')
wildcardComponent--;
if (cp == word) {
if (wildcardComponent == pattern) {
/* The first component contains the wildcard. */
/* Start the search from the local directory */
DirExpandPath(word, path, expansions);
goto done;
}
{
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);
}
DirExpandPath(pattern, path, expansions);
} else {
SearchPath_ExpandMiddle(path, pattern, wildcardComponent,
expansions);
}
done:
@ -1051,6 +1064,115 @@ DirFindDot(const char *name, const char *base)
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.
*
@ -1085,8 +1207,8 @@ Dir_FindFile(const char *name, SearchPath *path)
return NULL;
}
if (path->first != NULL) {
CachedDir *dir = path->first->datum;
if (path->dirs.first != NULL) {
CachedDir *dir = path->dirs.first->datum;
if (dir == dotLast) {
seenDotLast = TRUE;
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
* 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.
* 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)
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;
if (dir == dotLast)
continue;
@ -1139,7 +1261,7 @@ Dir_FindFile(const char *name, SearchPath *path)
* 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
* /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
* component of the resulting name onto the search path (at the
* end).]
@ -1157,100 +1279,11 @@ Dir_FindFile(const char *name, SearchPath *path)
}
if (name[0] != '/') {
SearchPathNode *ln;
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;
}
if (FindFileRelative(path, seenDotLast, name, &file))
return 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;
}
} else {
if (FindFileAbsolute(path, seenDotLast, name, base, &file))
return file;
}
}
/*
@ -1281,7 +1314,7 @@ Dir_FindFile(const char *name, SearchPath *path)
base++;
}
prefix = bmake_strsedup(name, base - 1);
(void)Dir_AddDir(path, prefix);
(void)SearchPath_Add(path, prefix);
free(prefix);
bigmisses++;
@ -1511,7 +1544,7 @@ CacheNewDir(const char *name, SearchPath *path)
OpenDirs_Add(&openDirs, dir);
if (path != NULL)
Lst_Append(path, CachedDir_Ref(dir));
Lst_Append(&path->dirs, CachedDir_Ref(dir));
DEBUG1(DIR, "Caching %s done\n", name);
return dir;
@ -1536,28 +1569,28 @@ CacheNewDir(const char *name, SearchPath *path)
* Lst_Append and CachedDir_Ref.
*/
CachedDir *
Dir_AddDir(SearchPath *path, const char *name)
SearchPath_Add(SearchPath *path, const char *name)
{
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
SearchPathNode *ln;
/* 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;
if (strcmp(pathDir->name, name) == 0)
return pathDir;
}
Lst_Prepend(path, CachedDir_Ref(dotLast));
Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast));
}
if (path != NULL) {
/* XXX: Why is OpenDirs only checked if path != NULL? */
CachedDir *dir = OpenDirs_Find(&openDirs, name);
if (dir != NULL) {
if (Lst_FindDatum(path, dir) == NULL)
Lst_Append(path, CachedDir_Ref(dir));
if (Lst_FindDatum(&path->dirs, dir) == NULL)
Lst_Append(&path->dirs, CachedDir_Ref(dir));
return dir;
}
}
@ -1574,9 +1607,9 @@ Dir_CopyDirSearchPath(void)
{
SearchPath *path = SearchPath_New();
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;
Lst_Append(path, CachedDir_Ref(dir));
Lst_Append(&path->dirs, CachedDir_Ref(dir));
}
return path;
}
@ -1596,7 +1629,7 @@ Dir_CopyDirSearchPath(void)
* don't go well.
*/
char *
SearchPath_ToFlags(const char *flag, SearchPath *path)
SearchPath_ToFlags(SearchPath *path, const char *flag)
{
Buffer buf;
SearchPathNode *ln;
@ -1604,7 +1637,7 @@ SearchPath_ToFlags(const char *flag, SearchPath *path)
Buf_Init(&buf);
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;
Buf_AddStr(&buf, " ");
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. */
@ -1621,11 +1654,12 @@ SearchPath_Free(SearchPath *path)
{
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_Unref(dir);
}
Lst_Free(path);
Lst_Done(&path->dirs);
free(path);
}
/*
@ -1635,8 +1669,8 @@ SearchPath_Free(SearchPath *path)
void
SearchPath_Clear(SearchPath *path)
{
while (!Lst_IsEmpty(path)) {
CachedDir *dir = Lst_Dequeue(path);
while (!Lst_IsEmpty(&path->dirs)) {
CachedDir *dir = Lst_Dequeue(&path->dirs);
CachedDir_Unref(dir);
}
}
@ -1651,10 +1685,10 @@ SearchPath_AddAll(SearchPath *dst, SearchPath *src)
{
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;
if (Lst_FindDatum(dst, dir) == NULL)
Lst_Append(dst, CachedDir_Ref(dir));
if (Lst_FindDatum(&dst->dirs, dir) == NULL)
Lst_Append(&dst->dirs, CachedDir_Ref(dir));
}
}
@ -1685,11 +1719,11 @@ Dir_PrintDirectories(void)
}
void
SearchPath_Print(SearchPath *path)
SearchPath_Print(const SearchPath *path)
{
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;
debug_printf("%s ", dir->name);
}

10
dir.h
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.
@ -83,16 +83,16 @@ void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
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_FindHereOrAbove(const char *, const char *);
void Dir_UpdateMTime(GNode *, Boolean);
CachedDir *Dir_AddDir(SearchPath *, const char *);
char *SearchPath_ToFlags(const char *, SearchPath *);
CachedDir *SearchPath_Add(SearchPath *, const char *);
char *SearchPath_ToFlags(SearchPath *, const char *);
void SearchPath_Clear(SearchPath *);
void SearchPath_AddAll(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void);
void SearchPath_Print(SearchPath *);
void SearchPath_Print(const SearchPath *);
SearchPath *Dir_CopyDirSearchPath(void);
/* Stripped-down variant of struct stat. */

16
enum.c
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>
@ -29,7 +29,7 @@
#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
@ -78,15 +78,3 @@ Enum_FlagsToString(char *buf, size_t buf_size,
buf[0] = '\0';
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*/
}

33
enum.h
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>
@ -39,8 +39,9 @@ typedef struct EnumToStringSpec {
const char *es_name;
} 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. */
#define ENUM__SEP "|"
@ -100,14 +101,16 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \
{ 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. */
#define ENUM__FLAGS_RTTI(typnam, specs, joined) \
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
@ -174,28 +177,18 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
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
* 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__SPECS_2( \
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_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

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.
* 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.
* All rights reserved.
*
@ -39,7 +39,7 @@
#include <unistd.h>
#ifdef HAVE_FILEMON_H
# include <filemon.h>
# include <filemon.h>
#endif
#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.
* 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);
}
/* TODO: monitor fchdir as well */
/*ARGSUSED*/
static struct filemon_state *
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];
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)
return NULL;
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*/
static struct filemon_state *
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,

253
for.c
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.
@ -29,7 +29,7 @@
* SUCH DAMAGE.
*/
/*-
/*
* Handling of .for/.endfor loops in a makefile.
*
* For loops have the form:
@ -58,9 +58,8 @@
#include "make.h"
/* "@(#)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. */
typedef struct ForVar {
@ -68,10 +67,7 @@ typedef struct ForVar {
size_t nameLen;
} ForVar;
/*
* State of a for loop.
*/
typedef struct For {
typedef struct ForLoop {
Buffer body; /* Unexpanded body of the loop */
Vector /* of ForVar */ vars; /* Iteration variables */
Words items; /* Substitution items */
@ -81,22 +77,33 @@ typedef struct For {
* only ${V} and $(V). */
Boolean short_var;
unsigned int sub_next; /* Where to continue iterating */
} For;
} ForLoop;
static For *accumFor; /* Loop being accumulated */
static void
ForAddVar(For *f, const char *name, size_t len)
static ForLoop *accumFor; /* Loop being accumulated */
static int forLevel = 0; /* Nesting level */
static ForLoop *
ForLoop_New(void)
{
ForVar *var = Vector_Push(&f->vars);
var->name = bmake_strldup(name, len);
var->nameLen = len;
ForLoop *f = bmake_malloc(sizeof *f);
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
For_Free(For *f)
ForLoop_Free(ForLoop *f)
{
Buf_Destroy(&f->body, TRUE);
Buf_Done(&f->body);
while (f->vars.len > 0) {
ForVar *var = Vector_Pop(&f->vars);
@ -105,11 +112,89 @@ For_Free(For *f)
Vector_Done(&f->vars);
Words_Free(f->items);
Buf_Destroy(&f->curBody, TRUE);
Buf_Done(&f->curBody);
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
IsFor(const char *p)
{
@ -138,7 +223,7 @@ IsEndfor(const char *p)
int
For_Eval(const char *line)
{
For *f;
ForLoop *f;
const char *p;
p = line + 1; /* skip the '.' */
@ -153,113 +238,41 @@ For_Eval(const char *line)
}
p += 3;
/*
* we found a for loop, and now we are going to parse it.
*/
f = ForLoop_New();
f = bmake_malloc(sizeof *f);
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;
/* 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);
if (!ForLoop_ParseVarnames(f, &p)) {
ForLoop_Free(f);
return -1;
}
cpp_skip_whitespace(&p);
{
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} */
if (!ForLoop_ParseItems(f, p)) {
/* Continue parsing the .for loop, but don't iterate. */
f->items.len = 0;
}
{
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;
forLevel = 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.
*/
Boolean
For_Accum(const char *line)
{
const char *ptr = line;
const char *p = line;
if (*ptr == '.') {
ptr++;
cpp_skip_whitespace(&ptr);
if (*p == '.') {
p++;
cpp_skip_whitespace(&p);
if (IsEndfor(ptr)) {
if (IsEndfor(p)) {
DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0)
return FALSE;
} else if (IsFor(ptr)) {
} else if (IsFor(p)) {
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.
*/
static Boolean
NeedsEscapes(const char *word, char endc)
NeedsEscapes(const char *value, char endc)
{
const char *p;
for (p = word; *p != '\0'; p++) {
for (p = value; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
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".
*/
static void
SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc,
const char **inout_mark)
ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
char endc, const char **inout_mark)
{
size_t i;
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.
*/
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;
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
* defined, see unit-tests/varname-empty.mk for more details.
*
* The detection of substitutions of the loop control variable is naive.
* Many of the modifiers use \ to escape $ (not $) so it is possible
* The detection of substitutions of the loop control variables is naive.
* Many of the modifiers use '\' to escape '$' (not '$'), so it is possible
* to contrive a makefile where an unwanted substitution happens.
*/
static void
ForSubstBody(For *f)
ForLoop_SubstBody(ForLoop *f)
{
const char *p, *bodyEnd;
const char *mark; /* where the last replacement left off */
@ -449,10 +462,10 @@ ForSubstBody(For *f)
for (p = mark; (p = strchr(p, '$')) != NULL;) {
if (p[1] == '{' || p[1] == '(') {
p += 2;
SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')',
&mark);
ForLoop_SubstVarLong(f, &p, bodyEnd,
p[-1] == '{' ? '}' : ')', &mark);
} else if (p[1] != '\0') {
SubstVarShort(f, p + 1, &mark);
ForLoop_SubstVarShort(f, p + 1, &mark);
p += 2;
} else
break;
@ -468,15 +481,15 @@ ForSubstBody(For *f)
static char *
ForReadMore(void *v_arg, size_t *out_len)
{
For *f = v_arg;
ForLoop *f = v_arg;
if (f->sub_next == f->items.len) {
/* No more iterations */
For_Free(f);
ForLoop_Free(f);
return NULL;
}
ForSubstBody(f);
ForLoop_SubstBody(f);
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
f->sub_next += (unsigned int)f->vars.len;
@ -488,7 +501,7 @@ ForReadMore(void *v_arg, size_t *out_len)
void
For_Run(int lineno)
{
For *f = accumFor;
ForLoop *f = accumFor;
accumFor = NULL;
if (f->items.len == 0) {
@ -496,7 +509,7 @@ For_Run(int lineno)
* Nothing to expand - possibly due to an earlier syntax
* error.
*/
For_Free(f);
ForLoop_Free(f);
return;
}

13
hash.c
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.
@ -74,7 +74,7 @@
#include "make.h"
/* "@(#)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
@ -82,7 +82,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $");
*/
#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
hash(const char *key, size_t *out_keylen)
{
@ -142,7 +142,10 @@ HashTable_Init(HashTable *t)
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
HashTable_Done(HashTable *t)
{
@ -157,8 +160,8 @@ HashTable_Done(HashTable *t)
he = next;
}
}
free(t->buckets);
free(t->buckets);
#ifdef CLEANUP
t->buckets = NULL;
#endif

260
job.c
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.
@ -69,14 +69,14 @@
* SUCH DAMAGE.
*/
/*-
/*
* job.c --
* handle the creation etc. of our child processes.
*
* Interface:
* Job_Init Called to initialize this module. In addition,
* any commands attached to the .BEGIN target
* are executed before this function returns.
* the .BEGIN target is made including all of its
* dependencies before this function returns.
* Hence, the makefiles must have been parsed
* before this function is called.
*
@ -99,9 +99,9 @@
* a time given by the SEL_* constants, below,
* or until output is ready.
*
* Job_ParseShell Given the line following a .SHELL target, parse
* the line as a shell specification. Returns
* FALSE if the spec was incorrect.
* Job_ParseShell Given a special dependency line with target '.SHELL',
* define the shell that is used for the creation
* commands in jobs mode.
*
* Job_Finish Perform any final processing which needs doing.
* This includes the execution of any commands
@ -109,10 +109,9 @@
* target. It should only be called when the
* job table is empty.
*
* Job_AbortAll Abort all currently running jobs. It doesn't
* handle output or do anything for the jobs,
* just kills them. It should only be called in
* an emergency.
* Job_AbortAll Abort all currently running jobs. Do not handle
* output or do anything for the jobs, just kill them.
* Should only be called in an emergency.
*
* Job_CheckCommands
* Verify that the commands for a target are
@ -156,7 +155,7 @@
#include "trace.h"
/* "@(#)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
@ -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,
* errOn, errOff and runChkTmpl.
*
* If a shell doesn't have error control, echoTmpl becomes a printf template
* for echoing the command, should echoing be on; runIgnTmpl becomes
* another printf template for executing the command while ignoring the return
* In case a shell doesn't have error control, echoTmpl is a printf template
* for echoing the command, should echoing be on; runIgnTmpl is another
* printf template for executing the command while ignoring the return
* 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
* 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
* escape any '$ ` \ "' characters in the command string to avoid common
* problems with echo "%s\n" as a template.
* escape any '$ ` \ "' characters in the command string to avoid unwanted
* 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
* "echo" flag causes the shell to start echoing commands right away. The
@ -230,7 +229,7 @@ typedef struct Shell {
} Shell;
typedef struct CommandFlags {
/* Whether to echo the command before running it. */
/* Whether to echo the command before or instead of running it. */
Boolean echo;
/* Run the command even in -n or -N mode. */
@ -266,6 +265,7 @@ typedef enum AbortReason { /* why is the make aborting? */
ABORT_ERROR, /* Because of an error */
ABORT_INTERRUPT, /* Because it was interrupted */
ABORT_WAIT /* Waiting for jobs to finish */
/* XXX: "WAIT" is not a _reason_ for aborting, it's rather a status. */
} AbortReason;
static AbortReason aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
@ -420,7 +420,7 @@ static Shell *shell = &shells[DEFSHELL_INDEX];
const char *shellPath = NULL; /* full pathname of executable image */
const char *shellName = NULL; /* last component of shellPath */
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 */
@ -434,8 +434,8 @@ static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */
* the output channels of children
*/
static struct pollfd *fds = NULL;
static Job **allJobs = NULL;
static nfds_t nJobs = 0;
static Job **jobByFdIndex = NULL;
static nfds_t fdsLen = 0;
static void watchfd(Job *);
static void clearfd(Job *);
static Boolean readyfd(Job *);
@ -452,6 +452,7 @@ enum {
};
static sigset_t caught_signals; /* Set of signals we handle */
static volatile sig_atomic_t caught_sigchld;
static void JobDoOutput(Job *, Boolean);
static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD;
@ -616,6 +617,7 @@ JobCondPassSig(int signo)
static void
JobChildSig(int signo MAKE_ATTR_UNUSED)
{
caught_sigchld = 1;
while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 &&
errno == EAGAIN)
continue;
@ -1002,7 +1004,7 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
* Print all commands to the shell file that is later executed.
*
* 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.
*/
@ -1029,7 +1031,10 @@ JobPrintCommands(Job *job)
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
JobSaveCommands(Job *job)
{
@ -1038,9 +1043,11 @@ JobSaveCommands(Job *job)
for (ln = job->tailCmds; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum;
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
* 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);
/* TODO: handle errors */
Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd);
@ -1322,7 +1329,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
* .DEFAULT itself.
*/
Make_HandleUse(defaultNode, gn);
Var_Set(IMPSRC, GNode_VarTarget(gn), gn);
Var_Set(gn, IMPSRC, GNode_VarTarget(gn));
return TRUE;
}
@ -1404,7 +1411,7 @@ JobExec(Job *job, char **argv)
Var_ReexportVars();
cpid = vFork();
cpid = vfork();
if (cpid == -1)
Punt("Cannot vfork: %s", strerror(errno));
@ -1479,12 +1486,12 @@ JobExec(Job *job, char **argv)
#if defined(HAVE_SETPGID)
(void)setpgid(0, getpid());
#else
#if defined(HAVE_SETSID)
# if defined(HAVE_SETSID)
/* XXX: dsl - I'm sure this should be setpgrp()... */
(void)setsid();
#else
# else
(void)setpgrp(0, getpid());
#endif
# endif
#endif
(void)execv(shellPath, argv);
@ -1515,9 +1522,7 @@ JobExec(Job *job, char **argv)
job->cmdFILE = NULL;
}
/*
* Now the job is actually running, add it to the table.
*/
/* Now that the job is actually running, add it to the table. */
if (DEBUG(JOB)) {
debug_printf("JobExec(%s): pid %d added to jobs table\n",
job->node->name, job->pid);
@ -1572,15 +1577,14 @@ JobMakeArgv(Job *job, char **argv)
}
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
* are put. It is removed before the child shell is executed,
* unless DEBUG(SCRIPT) is set.
*/
char *tfile;
sigset_t mask;
char tfile[MAXPATHLEN];
int tfd; /* File descriptor to the temp file */
/*
@ -1592,22 +1596,13 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
DieHorribly();
}
JobSigLock(&mask);
tfd = mkTempFile(TMPPAT, &tfile);
if (!DEBUG(SCRIPT))
(void)eunlink(tfile);
JobSigUnlock(&mask);
tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile);
job->cmdFILE = fdopen(tfd, "w+");
if (job->cmdFILE == NULL)
Punt("Could not fdopen %s", tfile);
(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
if (useMeta) {
@ -1617,11 +1612,7 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run)
}
#endif
/* We can do all the commands at once. hooray for sanity */
if (!JobPrintCommands(job))
*out_run = FALSE;
free(tfile);
*out_run = JobPrintCommands(job);
}
/*
@ -1675,46 +1666,36 @@ JobStart(GNode *gn, Boolean special)
cmdsOK = Job_CheckCommands(gn, Error);
job->inPollfd = NULL;
/*
* If the -n flag wasn't given, we open up OUR (not the child's)
* temporary file to stuff commands in it. The thing is rd/wr so
* we don't need to reopen it to feed it to the shell. If the -n
* flag *was* given, we just set the file to be stdout. Cute, huh?
*/
if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
if (Lst_IsEmpty(&gn->commands)) {
job->cmdFILE = stdout;
run = FALSE;
} else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
(!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)) {
/*
* Not executing anything -- just print all the commands to
* stdout in one fell swoop. This will still set up
* job->tailCmds correctly.
* Just print all the commands to stdout in one fell swoop.
* This still sets up job->tailCmds correctly.
*/
SwitchOutputTo(gn);
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)
JobPrintCommands(job);
/* Don't execute the shell, thank you. */
run = FALSE;
(void)fflush(job->cmdFILE);
} 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);
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 (!run) {
@ -1955,11 +1936,11 @@ JobRun(GNode *targ)
* Running these jobs in compat mode also guarantees that these
* jobs do not overlap with other unrelated jobs.
*/
List *lst = Lst_New();
Lst_Append(lst, targ);
(void)Make_Run(lst);
Lst_Destroy(lst, NULL);
JobStart(targ, JOB_SPECIAL);
GNodeList lst = LST_INIT;
Lst_Append(&lst, targ);
(void)Make_Run(&lst);
Lst_Done(&lst);
JobStart(targ, TRUE);
while (jobTokensRunning != 0) {
Job_CatchOutput();
}
@ -1993,6 +1974,11 @@ Job_CatchChildren(void)
if (jobTokensRunning == 0)
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) {
DEBUG2(JOB, "Process %d exited/stopped status %x.\n",
pid, WAIT_STATUS(status));
@ -2068,7 +2054,7 @@ Job_CatchOutput(void)
/* The first fd in the list is the job token pipe */
do {
nready = poll(fds + 1 - wantToken, nJobs - 1 + wantToken,
nready = poll(fds + 1 - wantToken, fdsLen - 1 + wantToken,
POLL_MSEC);
} while (nready < 0 && errno == EINTR);
@ -2079,24 +2065,17 @@ Job_CatchOutput(void)
char token = 0;
ssize_t count;
count = read(childExitJob.inPipe, &token, 1);
switch (count) {
case 0:
Punt("unexpected eof on token pipe");
/*NOTREACHED*/
case -1:
Punt("token pipe read: %s", strerror(errno));
/*NOTREACHED*/
case 1:
if (count == 1) {
if (token == DO_JOB_RESUME[0])
/*
* Complete relay requested from our SIGCONT
* handler
*/
JobRestartJobs();
break;
default:
abort();
}
} else if (count == 0)
Punt("unexpected eof on token pipe");
else
Punt("token pipe read: %s", strerror(errno));
nready--;
}
@ -2104,10 +2083,10 @@ Job_CatchOutput(void)
if (nready == 0)
return;
for (i = npseudojobs * nfds_per_job(); i < nJobs; i++) {
for (i = npseudojobs * nfds_per_job(); i < fdsLen; i++) {
if (fds[i].revents == 0)
continue;
job = allJobs[i];
job = jobByFdIndex[i];
if (job->status == JOB_ST_RUNNING)
JobDoOutput(job, FALSE);
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
@ -2159,7 +2138,7 @@ Shell_Init(void)
if (shellPath == NULL)
InitShellNameAndPath();
Var_SetWithFlags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY);
Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY);
if (shell->errFlag == NULL)
shell->errFlag = "";
if (shell->echoFlag == NULL)
@ -2199,12 +2178,12 @@ Job_SetPrefix(void)
{
if (targPrefix != NULL) {
free(targPrefix);
} else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) {
Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL);
} else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) {
Global_Set(MAKE_JOB_PREFIX, "---");
}
(void)Var_Subst("${" MAKE_JOB_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &targPrefix);
SCOPE_GLOBAL, VARE_WANTRES, &targPrefix);
/* TODO: handle errors */
}
@ -2227,6 +2206,7 @@ Job_Init(void)
memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table);
job_table_end = job_table + opts.maxJobs;
wantToken = 0;
caught_sigchld = 0;
aborting = ABORT_NONE;
job_errors = 0;
@ -2254,13 +2234,13 @@ Job_Init(void)
JobCreatePipe(&childExitJob, 3);
/* Preallocate enough for the maximum number of jobs. */
fds = bmake_malloc(sizeof *fds *
(npseudojobs + (size_t)opts.maxJobs) *
nfds_per_job());
allJobs = bmake_malloc(sizeof *allJobs *
(npseudojobs + (size_t)opts.maxJobs) *
nfds_per_job());
{
/* Preallocate enough for the maximum number of jobs. */
size_t nfds = (npseudojobs + (size_t)opts.maxJobs) *
nfds_per_job();
fds = bmake_malloc(sizeof *fds * nfds);
jobByFdIndex = bmake_malloc(sizeof *jobByFdIndex * nfds);
}
/* These are permanent entries and take slots 0 and 1 */
watchfd(&tokenWaitJob);
@ -2394,7 +2374,7 @@ Job_ParseShell(char *line)
/* XXX: don't use line as an iterator variable */
pp_skip_whitespace(&line);
free(shellArgv);
free(shell_freeIt);
memset(&newShell, 0, sizeof newShell);
@ -2409,7 +2389,7 @@ Job_ParseShell(char *line)
Error("Unterminated quoted string [%s]", line);
return FALSE;
}
shellArgv = path;
shell_freeIt = path;
for (path = NULL, argv = words; argc != 0; argc--, argv++) {
char *arg = *argv;
@ -2620,7 +2600,7 @@ void
Job_End(void)
{
#ifdef CLEANUP
free(shellArgv);
free(shell_freeIt);
#endif
}
@ -2715,17 +2695,17 @@ watchfd(Job *job)
if (job->inPollfd != NULL)
Punt("Watching watched job");
fds[nJobs].fd = job->inPipe;
fds[nJobs].events = POLLIN;
allJobs[nJobs] = job;
job->inPollfd = &fds[nJobs];
nJobs++;
fds[fdsLen].fd = job->inPipe;
fds[fdsLen].events = POLLIN;
jobByFdIndex[fdsLen] = job;
job->inPollfd = &fds[fdsLen];
fdsLen++;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
fds[nJobs].fd = meta_job_fd(job);
fds[nJobs].events = fds[nJobs].fd == -1 ? 0 : POLLIN;
allJobs[nJobs] = job;
nJobs++;
fds[fdsLen].fd = meta_job_fd(job);
fds[fdsLen].events = fds[fdsLen].fd == -1 ? 0 : POLLIN;
jobByFdIndex[fdsLen] = job;
fdsLen++;
}
#endif
}
@ -2737,7 +2717,7 @@ clearfd(Job *job)
if (job->inPollfd == NULL)
Punt("Unwatching unwatched job");
i = (size_t)(job->inPollfd - fds);
nJobs--;
fdsLen--;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
/*
@ -2747,20 +2727,20 @@ clearfd(Job *job)
assert(nfds_per_job() == 2);
if (i % 2 != 0)
Punt("odd-numbered fd with meta");
nJobs--;
fdsLen--;
}
#endif
/*
* Move last job in table into hole made by dead job.
*/
if (nJobs != i) {
fds[i] = fds[nJobs];
allJobs[i] = allJobs[nJobs];
allJobs[i]->inPollfd = &fds[i];
if (fdsLen != i) {
fds[i] = fds[fdsLen];
jobByFdIndex[i] = jobByFdIndex[fdsLen];
jobByFdIndex[i]->inPollfd = &fds[i];
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
fds[i + 1] = fds[nJobs + 1];
allJobs[i + 1] = allJobs[nJobs + 1];
fds[i + 1] = fds[fdsLen + 1];
jobByFdIndex[i + 1] = jobByFdIndex[fdsLen + 1];
}
#endif
}
@ -2794,6 +2774,22 @@ JobTokenAdd(void)
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. */
void
Job_ServerStart(int max_tokens, int jp_0, int jp_1)
@ -2815,8 +2811,8 @@ Job_ServerStart(int max_tokens, int jp_0, int jp_1)
snprintf(jobarg, sizeof jobarg, "%d,%d",
tokenWaitJob.inPipe, tokenWaitJob.outPipe);
Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL);
Global_Append(MAKEFLAGS, "-J");
Global_Append(MAKEFLAGS, jobarg);
/*
* Preload the job pipe with one token per job, save the one
@ -2915,7 +2911,7 @@ Job_RunTarget(const char *target, const char *fname)
return FALSE;
if (fname != NULL)
Var_Set(ALLSRC, fname, gn);
Var_Set(gn, ALLSRC, fname);
JobRun(gn);
/* XXX: Replace with GNode_IsError(gn) */

3
job.h
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.
@ -205,5 +205,6 @@ void Job_ServerStart(int, int, int);
void Job_SetPrefix(void);
Boolean Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t);
int Job_TempFile(const char *, char *, size_t);
#endif /* MAKE_JOB_H */

17
lst.c
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
@ -34,7 +34,7 @@
#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 *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@ -89,17 +89,6 @@ Lst_Free(List *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. */
void
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.
*/
void *

4
lst.h
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.
@ -116,8 +116,6 @@ void Lst_Done(List *);
void Lst_DoneCall(List *, LstFreeProc);
/* Free the list, leaving the node data unmodified. */
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 }

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

6
make.1
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
.\" The Regents of the University of California. All rights reserved.
@ -99,7 +99,7 @@ is equivalent to
.It Fl D Ar variable
Define
.Ar variable
to be 1, in the global context.
to be 1, in the global scope.
.It Fl d Ar [-]flags
Turn on debugging, and specify which portions of
.Nm
@ -355,7 +355,7 @@ Do not build any targets.
Multiple instances of this option may be specified;
the variables will be printed one per line,
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.
By default, the raw variable contents (which may
include additional unexpanded variable references) are shown.

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

104
make.h
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
@ -72,7 +72,7 @@
* from: @(#)make.h 8.3 (Berkeley) 6/13/95
*/
/*-
/*
* make.h --
* The global definitions for pmake
*/
@ -350,25 +350,23 @@ typedef enum GNodeType {
typedef enum GNodeFlags {
GNF_NONE = 0,
/* this target needs to be (re)made */
REMAKE = 0x0001,
REMAKE = 1 << 0,
/* children of this target were made */
CHILDMADE = 0x0002,
CHILDMADE = 1 << 1,
/* children don't exist, and we pretend made */
FORCE = 0x0004,
FORCE = 1 << 2,
/* Set by Make_ProcessWait() */
DONE_WAIT = 0x0008,
DONE_WAIT = 1 << 3,
/* Build requested by .ORDER processing */
DONE_ORDER = 0x0010,
DONE_ORDER = 1 << 4,
/* Node created from .depend */
FROM_DEPEND = 0x0020,
FROM_DEPEND = 1 << 5,
/* We do it once only */
DONE_ALLSRC = 0x0040,
DONE_ALLSRC = 1 << 6,
/* Used by MakePrintStatus */
CYCLE = 0x1000,
CYCLE = 1 << 12,
/* Used by MakePrintStatus */
DONECYCLE = 0x2000,
/* Internal use only */
INTERNAL = 0x4000
DONECYCLE = 1 << 13
} GNodeFlags;
typedef struct List StringList;
@ -377,7 +375,9 @@ typedef struct ListNode StringListNode;
typedef struct List GNodeList;
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
@ -429,7 +429,10 @@ typedef struct GNode {
* this node, in the normal sense. */
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;
/* The "#n" suffix for this cohort, or "" for other nodes */
char cohort_num[8];
@ -442,11 +445,14 @@ typedef struct GNode {
/* Last time (sequence number) we tried to make this node */
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 $@, $<, $?.
*
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
* VAR_INTERNAL, which contain variables with arbitrary names. */
* Also used for the global variable scopes SCOPE_GLOBAL,
* SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with
* arbitrary names.
*/
HashTable /* of Var pointer */ vars;
/* 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
* by makefiles.
*/
extern GNode *VAR_INTERNAL;
/* Variables defined in a global context, e.g in the Makefile itself. */
extern GNode *VAR_GLOBAL;
extern GNode *SCOPE_INTERNAL;
/* Variables defined in a global scope, e.g in the makefile itself. */
extern GNode *SCOPE_GLOBAL;
/* 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
@ -532,8 +538,8 @@ extern SearchPath dirSearchPath;
/* Used for .include "...". */
extern SearchPath *parseIncPath;
/*
* Used for .include <...>, for the built-in sys.mk and makefiles from the
* command line arguments.
* Used for .include <...>, for the built-in sys.mk and for makefiles from
* the command line arguments.
*/
extern SearchPath *sysIncPath;
/* The default for sysIncPath. */
@ -543,18 +549,12 @@ extern SearchPath *defSysIncPath;
extern char curdir[];
/* The basename of the program name, suffixed with [n] for sub-makes. */
extern const char *progname;
extern int makelevel;
/* Name of the .depend makefile */
extern char *makeDependfile;
/* If we replaced environ, this will be non-NULL. */
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;
#define MAKEFLAGS ".MAKEFLAGS"
@ -606,7 +606,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
do { \
if (DEBUG(module)) \
debug_printf args; \
} while (/*CONSTCOND*/ 0)
} while (/*CONSTCOND*/FALSE)
#define DEBUG0(module, text) \
DEBUG_IMPL(module, ("%s", text))
@ -671,11 +671,13 @@ typedef struct CmdOpts {
/* -n: execute almost no commands from the targets */
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;
/* -r: raw mode, without loading the builtin rules. */
/* -r: raw mode, do not load the builtin rules. */
Boolean noBuiltins;
/* -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 */
Boolean parseWarnFatal;
/* -w: print Entering and Leaving for submakes */
/* -w: print 'Entering' and 'Leaving' for submakes */
Boolean enterFlag;
/* -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 Main_ExportMAKEFLAGS(Boolean);
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 *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
Boolean GNode_ShouldExecute(GNode *gn);
@ -765,19 +767,19 @@ GNode_IsError(const GNode *gn)
}
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 *
GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); }
GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); }
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 *
GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); }
GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); }
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 *
GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); }
GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
MAKE_INLINE const char *
GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#ifdef __GNUC__
#define UNCONST(ptr) ({ \
@ -851,18 +853,18 @@ pp_skip_hspace(char **pp)
}
#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)
# include <sys/cdefs.h>
# define MAKE_RCSID(id) __RCSID(id)
# include <sys/cdefs.h>
# define MAKE_RCSID(id) __RCSID(id)
#elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
# define MAKE_RCSID(id) static volatile char \
# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
# define MAKE_RCSID(id) static volatile char \
MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id
#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
# define MAKE_RCSID(id) static volatile char rcsid[] = id
# define MAKE_RCSID(id) static volatile char rcsid[] = id
#endif
#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.
* All rights reserved.
*
@ -30,7 +30,7 @@
#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

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.
* All rights reserved.
*

87
meta.c
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.
@ -144,7 +144,10 @@ meta_open_filemon(BuildMon *pbm)
* cwd causing getcwd to do a lot more work.
* 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) {
err(1, "Could not dup filemon output!");
}
@ -327,7 +330,7 @@ is_submake(const char *cmd, GNode *gn)
Boolean rc = FALSE;
if (p_make == NULL) {
p_make = Var_Value(".MAKE", gn).str;
p_make = Var_Value(gn, ".MAKE").str;
p_len = strlen(p_make);
}
cp = strchr(cmd, '$');
@ -407,7 +410,7 @@ printCMDs(GNode *gn, FILE *fp)
} \
return FALSE; \
} \
} while (/*CONSTCOND*/0)
} while (/*CONSTCOND*/FALSE)
/*
@ -483,7 +486,7 @@ meta_create(BuildMon *pbm, GNode *gn)
fp = NULL;
dname = Var_Value(".OBJDIR", gn);
dname = Var_Value(gn, ".OBJDIR");
tname = GNode_VarTarget(gn);
/* if this succeeds objdir_realpath is realpath of dname */
@ -538,8 +541,8 @@ meta_create(BuildMon *pbm, GNode *gn)
fprintf(fp, "-- command output --\n");
fflush(fp);
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
Global_Append(".MAKE.META.FILES", fname);
Global_Append(".MAKE.META.CREATED", fname);
gn->type |= OP_META; /* in case anyone wants to know */
if (metaSilent) {
@ -573,7 +576,7 @@ meta_init(void)
{
#ifdef USE_FILEMON
/* 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
}
@ -613,14 +616,15 @@ meta_mode_init(const char *make_mode)
get_mode_bf(metaMissing, "missing-meta=");
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
* prints the absolute path of the target.
* This works be cause :H will generate '.' if there is no /
* 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)
return;
@ -630,28 +634,28 @@ meta_mode_init(const char *make_mode)
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
/* TODO: handle errors */
str2Lst_Append(&metaBailiwick, metaBailiwickStr);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
Var_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
Global_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}");
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
/* TODO: handle errors */
str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
/*
* 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) {
metaIgnorePatterns = TRUE;
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) {
metaIgnoreFilter = TRUE;
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",
status, ignerr ? "(ignored)" : "");
}
if (gn != NULL) {
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
}
if (gn != NULL)
Global_Set(".ERROR_TARGET", GNode_Path(gn));
getcwd(cwd, sizeof cwd);
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL);
Global_Set(".ERROR_CWD", cwd);
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);
}
@ -816,7 +819,7 @@ meta_job_output(Job *job, char *cp, const char *nl)
char *cp2;
(void)Var_Subst("${" MAKE_META_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix);
/* TODO: handle errors */
if ((cp2 = strchr(meta_prefix, '$')) != NULL)
meta_prefix_len = (size_t)(cp2 - meta_prefix);
@ -993,7 +996,13 @@ meta_ignore(GNode *gn, const char *p)
const char *expr;
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}@}";
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
/* TODO: handle errors */
@ -1093,7 +1102,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (oodate)
return oodate; /* we're done */
dname = Var_Value(".OBJDIR", gn);
dname = Var_Value(gn, ".OBJDIR");
tname = GNode_VarTarget(gn);
/* 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 */
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
Global_Append(".MAKE.META.FILES", fname);
cmdNode = gn->commands.first;
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.
*
* 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')
* elsewhere we need to track that - but only for
* that process. If it forks ('F'), we initialize
@ -1221,18 +1230,18 @@ meta_oodate(GNode *gn, Boolean oodate)
if (lastpid > 0) {
/* We need to remember these. */
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
Var_Set(ldir_vname, latestdir, VAR_GLOBAL);
Global_SetExpand(lcwd_vname, lcwd);
Global_SetExpand(ldir_vname, latestdir);
}
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
lastpid = pid;
ldir = Var_Value(ldir_vname, VAR_GLOBAL);
ldir = Var_Value(SCOPE_GLOBAL, ldir_vname);
if (ldir.str != NULL) {
strlcpy(latestdir, ldir.str, sizeof latestdir);
FStr_Done(&ldir);
}
ldir = Var_Value(lcwd_vname, VAR_GLOBAL);
ldir = Var_Value(SCOPE_GLOBAL, lcwd_vname);
if (ldir.str != NULL) {
strlcpy(lcwd, ldir.str, sizeof lcwd);
FStr_Done(&ldir);
@ -1255,8 +1264,8 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Process according to record type. */
switch (buf[0]) {
case 'X': /* eXit */
Var_Delete(lcwd_vname, VAR_GLOBAL);
Var_Delete(ldir_vname, VAR_GLOBAL);
Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname);
Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname);
lastpid = 0; /* no need to save ldir_vname */
break;
@ -1268,9 +1277,9 @@ meta_oodate(GNode *gn, Boolean oodate)
child = atoi(p);
if (child > 0) {
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);
Var_Set(cldir, latestdir, VAR_GLOBAL);
Global_SetExpand(cldir, latestdir);
#ifdef DEBUG_META_MODE
if (DEBUG(META))
debug_printf(
@ -1286,8 +1295,8 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Update lcwd and latest directory. */
strlcpy(latestdir, p, sizeof latestdir);
strlcpy(lcwd, p, sizeof lcwd);
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
Var_Set(ldir_vname, lcwd, VAR_GLOBAL);
Global_SetExpand(lcwd_vname, lcwd);
Global_SetExpand(ldir_vname, lcwd);
#ifdef DEBUG_META_MODE
DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
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.
* All we can sanely do is set it to .ALLSRC.
*/
Var_Delete(OODATE, gn);
Var_Set(OODATE, GNode_VarAllsrc(gn), gn);
Var_Delete(gn, OODATE);
Var_Set(gn, OODATE, GNode_VarAllsrc(gn));
}
oodate_out:
@ -1699,7 +1708,7 @@ meta_compat_parent(pid_t child)
fflush(stdout);
buf[nread] = '\0';
meta_job_output(NULL, buf, "");
} while (/*CONSTCOND*/0);
} while (/*CONSTCOND*/FALSE);
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0)
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.
* All rights reserved.
*
@ -39,7 +39,7 @@
#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
@ -82,4 +82,3 @@ unsigned char _metachar[128] = {
/* x y z { | } ~ del */
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.
* All rights reserved.
*
@ -37,7 +37,7 @@ extern unsigned char _metachar[];
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
MAKE_INLINE int
MAKE_INLINE Boolean
needshell(const char *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>
* 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-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 empty(_DEP_TARGET_SPEC)
# if not, just use TARGET_SPEC
_DEP_TARGET_SPEC := ${TARGET_SPEC}
DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}}
_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC}
.if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != ""
# record that we've read dependfile for this
_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}:
@ -386,8 +386,10 @@ DIRDEP_LOADAVG_LAST = 0
# yes the expression here is a bit complicated,
# the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}}
# when we want to report.
# Note: expr(1) will exit 1 if the expression evaluates to 0
# hence the || true
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}`"
# we suppress SUBDIR when visiting the leaves

View File

@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# 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
#
@ -70,7 +70,7 @@
# sjg@crufty.net
#
MK_VERSION=20210101
MK_VERSION=20210130
OWNER=
GROUP=
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
#
@ -30,8 +30,11 @@ _dirdep ?= ${RELDIR}
CLEANFILES+= .dirdep
# this allows us to trace dependencies back to their src dir
.dirdep: .NOPATH
.dirdep: .NOPATH
.if !commands(.dirdep)
.dirdep:
@echo '${_dirdep}' > $@
.endif
.if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == ""
_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
* The Regents of the University of California. All rights reserved.
*
@ -34,7 +34,7 @@
* from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
*/
/*-
/*
* Copyright (c) 1989 by Berkeley Softworks
* All rights reserved.
*
@ -107,7 +107,11 @@ str_basename(const char *pathname)
MAKE_INLINE SearchPath *
SearchPath_New(void)
{ return Lst_New(); }
{
SearchPath *path = bmake_malloc(sizeof *path);
Lst_Init(&path->dirs);
return path;
}
void SearchPath_Free(SearchPath *);
@ -282,6 +286,7 @@ const char *Targ_FmtTime(time_t);
void Targ_PrintType(int);
void Targ_PrintGraph(int);
void Targ_Propagate(void);
const char *GNodeMade_Name(GNodeMade);
/* var.c */
void Var_Init(void);
@ -290,26 +295,28 @@ void Var_End(void);
typedef enum VarEvalFlags {
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
* is not set. */
* is not set.
*/
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,
/* 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
* multiple such assignments to be chained without accidentally
* expanding '$$file' to '$file' in the first assignment and
* 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,
/*
@ -369,15 +376,19 @@ typedef enum VarExportMode {
VEM_LITERAL
} VarExportMode;
void Var_DeleteVar(const char *, GNode *);
void Var_Delete(const char *, GNode *);
void Var_Delete(GNode *, const char *);
void Var_DeleteExpand(GNode *, const char *);
void Var_Undef(const char *);
void Var_Set(const char *, const char *, GNode *);
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags);
void Var_Append(const char *, const char *, GNode *);
Boolean Var_Exists(const char *, GNode *);
FStr Var_Value(const char *, GNode *);
const char *Var_ValueDirect(const char *, GNode *);
void Var_Set(GNode *, const char *, const char *);
void Var_SetExpand(GNode *, const char *, const char *);
void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags);
void Var_Append(GNode *, const char *, const char *);
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_Subst(const char *, GNode *, VarEvalFlags, char **);
void Var_Stats(void);
@ -387,6 +398,11 @@ void Var_Export(VarExportMode, const char *);
void Var_ExportVars(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 */
typedef void (*SignalProc)(int);
SignalProc bmake_signal(int, SignalProc);

306
parse.c
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
@ -84,7 +84,7 @@
* Parse_End Clean up the module
*
* 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
* assignment. Used by MainParseArgs to determine if
@ -124,7 +124,7 @@
#include "pathnames.h"
/* "@(#)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 */
@ -240,44 +240,11 @@ static int fatals = 0;
*/
/*
* The include chain of makefiles. At the bottom is the top-level makefile
* from the command line, and on top of that, there are the included files or
* .for loops, up to and including the current file.
* The include chain of makefiles. At index 0 is the top-level makefile from
* the command line, followed by the included files or .for loops, up to and
* including the current file.
*
* This data could be used to print stack traces on parse errors. As of
* 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
* See PrintStackTrace for how to interpret the data.
*/
static Vector /* of IFile */ includes;
@ -295,8 +262,8 @@ CurFile(void)
}
/* include paths */
SearchPath *parseIncPath; /* dirs for "..." includes */
SearchPath *sysIncPath; /* dirs for <...> includes */
SearchPath *parseIncPath; /* directories for "..." includes */
SearchPath *sysIncPath; /* directories for <...> includes */
SearchPath *defSysIncPath; /* default for sysIncPath */
/* parser tables */
@ -503,12 +470,61 @@ loadfile(const char *path, int fd)
{
struct loadedfile *lf = loadedfile_create(path,
buf.data, buf.len);
Buf_Destroy(&buf, FALSE);
Buf_DoneData(&buf);
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. */
static Boolean
@ -575,13 +591,13 @@ PrintLocation(FILE *f, const char *fname, size_t lineno)
/* Find out which makefile is the culprit.
* 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)
dir.str = ".";
if (dir.str[0] != '/')
dir.str = realpath(dir.str, dirbuf);
base = Var_Value(".PARSEFILE", VAR_GLOBAL);
base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE");
if (base.str == NULL)
base.str = str_basename(fname);
@ -609,13 +625,17 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
(void)fflush(f);
if (type == PARSE_INFO)
return;
if (type == PARSE_FATAL || opts.parseWarnFatal)
fatals++;
if (opts.parseWarnFatal && !fatal_warning_error_printed) {
goto print_stack_trace;
if (type == PARSE_WARNING && !opts.parseWarnFatal)
goto print_stack_trace;
fatals++;
if (type == PARSE_WARNING && !fatal_warning_error_printed) {
Error("parsing warnings being treated as errors");
fatal_warning_error_printed = TRUE;
}
print_stack_trace:
PrintStackTrace();
}
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.
*/
static void
@ -690,7 +710,7 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
return;
}
(void)Var_Subst(umsg, VAR_CMDLINE, VARE_WANTRES, &xmsg);
(void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg);
/* TODO: handle errors */
Parse_Error(level, "%s", xmsg);
@ -867,11 +887,9 @@ static void
ParseDependencySourceMain(const char *src)
{
/*
* In a line like ".MAIN: source1 source2", it means we need to add
* the sources of said target to the list of things to create.
*
* 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.
* In a line like ".MAIN: source1 source2", add all sources to the
* 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.
*
* See ParseDoDependencyTargetSpecial, branch SP_MAIN.
* 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
* employ that, if desired.
*/
Var_Append(".TARGETS", src, VAR_GLOBAL);
Global_Append(".TARGETS", src);
}
static void
@ -918,12 +936,11 @@ ParseDependencySourceOther(const char *src, GNodeType tOp,
GNode *gn;
/*
* If the source is not an attribute, we need to find/create
* a node for it. After that we can apply any operator to it
* from a special target or link it to its parents, as
* appropriate.
* The source is not an attribute, so find/create a node for it.
* After that, apply any operator to it from a special target or
* link it to its parents, as 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 'cohorts' list of the node) or all the cohorts are linked
* to all the targets.
@ -1013,7 +1030,7 @@ ParseErrorNoDependency(const char *lstart)
Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"",
(int)(dirend - dirstart), dirstart);
} else
Parse_Error(PARSE_FATAL, "Need an operator");
Parse_Error(PARSE_FATAL, "Invalid line type");
}
static void
@ -1040,7 +1057,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
const char *nested_p = cp;
FStr nested_val;
(void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_NONE,
(void)Var_Parse(&nested_p, SCOPE_CMDLINE, VARE_NONE,
&nested_val);
/* TODO: handle errors */
FStr_Done(&nested_val);
@ -1055,7 +1072,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart)
/* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */
static void
ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
const char *line, /* XXX: bad name */
const char *targetName,
SearchPathList **inout_paths)
{
switch (*inout_specType) {
@ -1077,7 +1094,7 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType,
case SP_STALE:
case SP_ERROR:
case SP_INTERRUPT: {
GNode *gn = Targ_GetNode(line);
GNode *gn = Targ_GetNode(targetName);
if (doing_depend)
ParseMark(gn);
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.
*/
static Boolean
ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */
ParseDoDependencyTargetPath(const char *suffixName,
SearchPathList **inout_paths)
{
SearchPath *path;
path = Suff_GetPath(&line[5]);
path = Suff_GetPath(suffixName);
if (path == NULL) {
Parse_Error(PARSE_FATAL,
"Suffix '%s' not defined (yet)", &line[5]);
"Suffix '%s' not defined (yet)", suffixName);
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.
*/
static Boolean
ParseDoDependencyTarget(const char *line, /* XXX: bad name */
ParseDoDependencyTarget(const char *targetName,
ParseSpecial *inout_specType,
GNodeType *out_tOp, SearchPathList **inout_paths)
{
int keywd;
if (!(line[0] == '.' && ch_isupper(line[1])))
if (!(targetName[0] == '.' && ch_isupper(targetName[1])))
return TRUE;
/*
* See if the target is a special target that must have it
* or its sources handled specially.
*/
keywd = ParseFindKeyword(line);
keywd = ParseFindKeyword(targetName);
if (keywd != -1) {
if (*inout_specType == SP_PATH &&
parseKeywords[keywd].spec != SP_PATH) {
@ -1167,22 +1184,21 @@ ParseDoDependencyTarget(const char *line, /* XXX: bad name */
*inout_specType = parseKeywords[keywd].spec;
*out_tOp = parseKeywords[keywd].op;
ParseDoDependencyTargetSpecial(inout_specType, line,
ParseDoDependencyTargetSpecial(inout_specType, targetName,
inout_paths);
} else if (strncmp(line, ".PATH", 5) == 0) {
} else if (strncmp(targetName, ".PATH", 5) == 0) {
*inout_specType = SP_PATH;
if (!ParseDoDependencyTargetPath(line, inout_paths))
if (!ParseDoDependencyTargetPath(targetName + 5, inout_paths))
return FALSE;
}
return TRUE;
}
static void
ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */
StringList *curTargs)
ParseDoDependencyTargetMundane(char *targetName, StringList *curTargs)
{
if (Dir_HasWildcards(line)) {
if (Dir_HasWildcards(targetName)) {
/*
* Targets are to be sought only in the current directory,
* 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();
Dir_Expand(line, emptyPath, curTargs);
SearchPath_Expand(emptyPath, targetName, curTargs);
SearchPath_Free(emptyPath);
} else {
@ -1199,7 +1215,7 @@ ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */
* No wildcards, but we want to avoid code duplication,
* so create a list with the word on it.
*/
Lst_Append(curTargs, line);
Lst_Append(curTargs, targetName);
}
/* Apply the targets. */
@ -1323,7 +1339,7 @@ ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths)
break;
#ifdef POSIX
case SP_POSIX:
Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
Global_Set("%POSIX", "1003.2");
break;
#endif
default:
@ -1337,7 +1353,7 @@ AddToPaths(const char *dir, SearchPathList *paths)
if (paths != NULL) {
SearchPathListNode *ln;
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,
* line should remain untouched.
*/
if (!Arch_ParseArchive(&tgt, targets, VAR_CMDLINE)) {
if (!Arch_ParseArchive(&tgt, targets, SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL,
"Error in archive specification: \"%s\"",
tgt);
@ -1545,7 +1561,8 @@ ParseDoDependencySourcesMundane(char *start, char *end,
if (*end == '(') {
GNodeList sources = LST_INIT;
if (!Arch_ParseArchive(&start, &sources, VAR_CMDLINE)) {
if (!Arch_ParseArchive(&start, &sources,
SCOPE_CMDLINE)) {
Parse_Error(PARSE_FATAL,
"Error in source archive spec \"%s\"",
start);
@ -1866,13 +1883,13 @@ Parse_IsVar(const char *p, VarAssign *out_var)
* Check for syntax errors such as unclosed expressions or unknown modifiers.
*/
static void
VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
{
if (opts.strict) {
if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
char *expandedValue;
(void)Var_Subst(uvalue, ctxt, VARE_NONE,
(void)Var_Subst(uvalue, scope, VARE_NONE,
&expandedValue);
/* TODO: handle errors */
free(expandedValue);
@ -1881,31 +1898,32 @@ VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
}
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)
{
const char *avalue;
char *evalue;
/*
* 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))
Var_Set(name, "", ctxt);
if (!Var_ExistsExpand(scope, name))
Var_SetExpand(scope, name, "");
(void)Var_Subst(uvalue, ctxt,
(void)Var_Subst(uvalue, scope,
VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue);
/* TODO: handle errors */
avalue = evalue;
Var_Set(name, avalue, ctxt);
Var_SetExpand(scope, name, evalue);
*out_avalue = (FStr){ avalue, evalue };
*out_avalue = FStr_InitOwn(evalue);
}
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 cmd;
@ -1915,14 +1933,14 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt,
cmd = FStr_InitRefer(uvalue);
if (strchr(cmd.str, '$') != NULL) {
char *expanded;
(void)Var_Subst(cmd.str, VAR_CMDLINE,
(void)Var_Subst(cmd.str, SCOPE_CMDLINE,
VARE_WANTRES | VARE_UNDEFERR, &expanded);
/* TODO: handle errors */
cmd = FStr_InitOwn(expanded);
}
cmdOut = Cmd_Exec(cmd.str, &errfmt);
Var_Set(name, cmdOut, ctxt);
Var_SetExpand(scope, name, cmdOut);
*out_avalue = FStr_InitOwn(cmdOut);
if (errfmt != NULL)
@ -1934,31 +1952,32 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt,
/*
* Perform a variable assignment.
*
* The actual value of the variable is returned in *out_avalue and
* *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ
* from the literal value.
* The actual value of the variable is returned in *out_TRUE_avalue.
* Especially for VAR_SUBST and VAR_SHELL this can differ from the literal
* value.
*
* Return whether the assignment was actually done. The assignment is only
* skipped if the operator is '?=' and the variable already exists.
* Return whether the assignment was actually performed, which is usually
* the case. It is only skipped if the operator is '?=' and the variable
* already exists.
*/
static Boolean
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);
if (op == VAR_APPEND)
Var_Append(name, uvalue, ctxt);
Var_AppendExpand(scope, name, uvalue);
else if (op == VAR_SUBST)
VarAssign_EvalSubst(name, uvalue, ctxt, &avalue);
VarAssign_EvalSubst(scope, name, uvalue, &avalue);
else if (op == VAR_SHELL)
VarAssign_EvalShell(name, uvalue, ctxt, &avalue);
VarAssign_EvalShell(name, uvalue, scope, &avalue);
else {
if (op == VAR_DEFAULT && Var_Exists(name, ctxt))
if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name))
return FALSE;
/* Normal assignment -- just do it. */
Var_Set(name, uvalue, ctxt);
Var_SetExpand(scope, name, uvalue);
}
*out_TRUE_avalue = avalue;
@ -1984,14 +2003,14 @@ VarAssignSpecial(const char *name, const char *avalue)
Var_ExportVars(avalue);
}
/* Perform the variable variable assignment in the given context. */
/* Perform the variable variable assignment in the given scope. */
void
Parse_DoVar(VarAssign *var, GNode *ctxt)
Parse_DoVar(VarAssign *var, GNode *scope)
{
FStr avalue; /* actual value (maybe expanded) */
VarCheckSyntax(var->op, var->value, ctxt);
if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, &avalue)) {
VarCheckSyntax(var->op, var->value, scope);
if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) {
VarAssignSpecial(var->varname, avalue.str);
FStr_Done(&avalue);
}
@ -2086,7 +2105,7 @@ ParseAddCmd(GNode *gn, char *cmd)
void
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.
*/
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;
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
*/
SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath
: sysIncPath;
SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs)
? defSysIncPath : sysIncPath;
fullname = Dir_FindFile(file, path);
}
@ -2206,12 +2225,12 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent)
}
static void
ParseDoInclude(char *line /* XXX: bad name */)
ParseDoInclude(char *directive)
{
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
Boolean silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
Boolean silent = directive[0] != 'i';
char *file = directive + (silent ? 8 : 7);
/* Skip to delimiter character so we know where to look */
pp_skip_hspace(&file);
@ -2248,10 +2267,10 @@ ParseDoInclude(char *line /* XXX: bad name */)
* Substitute for any variables in the filename before trying to
* find the file.
*/
(void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file);
(void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &file);
/* TODO: handle errors */
Parse_include_file(file, endc == '>', line[0] == 'd', silent);
IncludeFile(file, endc == '>', directive[0] == 'd', silent);
free(file);
}
@ -2275,8 +2294,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
basename = slash + 1;
}
Var_Set(dirvar, dirname, VAR_GLOBAL);
Var_Set(filevar, basename, VAR_GLOBAL);
Global_SetExpand(dirvar, dirname);
Global_SetExpand(filevar, basename);
DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
__func__, dirvar, dirname, filevar, basename);
@ -2314,8 +2333,8 @@ ParseSetParseFile(const char *filename)
SetFilenameVars(including,
".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE");
} else {
Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
Global_Delete(".INCLUDEDFROMDIR");
Global_Delete(".INCLUDEDFROMFILE");
}
}
@ -2352,7 +2371,7 @@ StrContainsWord(const char *str, const char *word)
static Boolean
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);
FStr_Done(&val);
return found;
@ -2369,7 +2388,7 @@ static void
ParseTrackInput(const char *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
* 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 */
if (*file == '\0') {
@ -2504,7 +2523,7 @@ ParseTraditionalInclude(char *line)
else
done = TRUE;
Parse_include_file(file, FALSE, FALSE, silent);
IncludeFile(file, FALSE, FALSE, silent);
}
out:
free(all_files);
@ -2536,7 +2555,7 @@ ParseGmakeExport(char *line)
/*
* 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 */
setenv(variable, value, 1);
@ -2581,16 +2600,16 @@ ParseEOF(void)
}
/* 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);
Vector_Pop(&includes);
if (includes.len == 0) {
/* We've run out of input */
Var_Delete(".PARSEDIR", VAR_GLOBAL);
Var_Delete(".PARSEFILE", VAR_GLOBAL);
Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL);
Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL);
Global_Delete(".PARSEDIR");
Global_Delete(".PARSEFILE");
Global_Delete(".INCLUDEDFROMDIR");
Global_Delete(".INCLUDEDFROMFILE");
return FALSE;
}
@ -2781,7 +2800,6 @@ ParseGetLine(GetLineMode mode)
char *firstBackslash;
char *firstComment;
/* Loop through blank lines and comment lines */
for (;;) {
ParseRawLineResult res = ParseRawLine(curFile,
&line, &line_end, &firstBackslash, &firstComment);
@ -3048,7 +3066,7 @@ ParseVarassign(const char *line)
return FALSE;
FinishDependencyGroup();
Parse_DoVar(&var, VAR_GLOBAL);
Parse_DoVar(&var, SCOPE_GLOBAL);
return TRUE;
}
@ -3130,7 +3148,7 @@ ParseDependency(char *line)
* It simply returns the special empty string var_Error,
* which cannot be detected in the result of Var_Subst. */
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 */
/* Need a fresh list for the target nodes */
@ -3274,13 +3292,11 @@ Parse_MainName(GNodeList *mainList)
if (mainNode == NULL)
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);
} else
Lst_Append(mainList, mainNode);
Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL);
Global_Append(".TARGETS", mainNode->name);
}
int

22
str.c
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
* The Regents of the University of California. All rights reserved.
*
@ -32,7 +32,7 @@
* SUCH DAMAGE.
*/
/*-
/*
* Copyright (c) 1989 by Berkeley Softworks
* All rights reserved.
*
@ -71,7 +71,7 @@
#include "make.h"
/* "@(#)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. */
char *
@ -156,7 +156,7 @@ Str_Words(const char *str, Boolean expand)
inquote = '\0';
word_start = words_buf;
word_end = words_buf;
for (str_p = str;; ++str_p) {
for (str_p = str;; str_p++) {
char ch = *str_p;
switch (ch) {
case '"':
@ -324,9 +324,15 @@ Str_Match(const char *str, const char *pat)
break;
return FALSE;
}
/* XXX: This naive comparison makes the parser
* for the pattern dependent on the actual of
* the string. This is unpredictable. */
/*
* XXX: This naive comparison makes the
* 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)
break;
if (pat[1] == '-') {

66
suff.c
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
@ -114,7 +114,7 @@
#include "dir.h"
/* "@(#)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 ListNode SuffixListNode;
@ -885,7 +885,7 @@ Suff_DoPaths(void)
for (ln = sufflist.first; ln != NULL; ln = ln->next) {
Suffix *suff = ln->datum;
if (!Lst_IsEmpty(suff->searchPath)) {
if (!Lst_IsEmpty(&suff->searchPath->dirs)) {
#ifdef INCLUDES
if (suff->flags & SUFF_INCLUDE)
SearchPath_AddAll(includesPath,
@ -902,12 +902,12 @@ Suff_DoPaths(void)
}
}
flags = SearchPath_ToFlags("-I", includesPath);
Var_Set(".INCLUDES", flags, VAR_GLOBAL);
flags = SearchPath_ToFlags(includesPath, "-I");
Global_Set(".INCLUDES", flags);
free(flags);
flags = SearchPath_ToFlags("-L", libsPath);
Var_Set(".LIBS", flags, VAR_GLOBAL);
flags = SearchPath_ToFlags(libsPath, "-L");
Global_Set(".LIBS", flags);
free(flags);
SearchPath_Free(includesPath);
@ -1176,14 +1176,14 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
GNode *tgn; /* Target GNode */
GNode *sgn; /* Source GNode */
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 */
tgn = targ->node;
prefLen = strlen(targ->prefix);
for (gln = tgn->children.first; gln != NULL; gln = gln->next) {
const char *cp;
const char *base;
sgn = gln->datum;
@ -1198,11 +1198,11 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
continue;
}
cp = str_basename(sgn->name);
if (strncmp(cp, targ->prefix, prefLen) != 0)
base = str_basename(sgn->name);
if (strncmp(base, targ->prefix, prefLen) != 0)
continue;
/* The node matches the prefix, see if it has a known suffix. */
suff = FindSuffixByName(cp + prefLen);
suff = FindSuffixByName(base + prefLen);
if (suff == NULL)
continue;
@ -1246,7 +1246,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* Expand the word along the chosen path
*/
Lst_Init(&expansions);
Dir_Expand(cgn->name, Suff_FindPath(cgn), &expansions);
SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
while (!Lst_IsEmpty(&expansions)) {
GNode *gn;
@ -1388,9 +1388,9 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
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,
* expanding variables in the parent's context.
* expanding variables in the parent's scope.
*/
char *p = cp;
(void)Arch_ParseArchive(&p, &members, pgn);
@ -1626,8 +1626,8 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
gn->unmade++;
/* Copy in the variables from the member node to this one. */
Var_Set(PREFIX, GNode_VarPrefix(mem), gn);
Var_Set(TARGET, GNode_VarTarget(mem), gn);
Var_Set(gn, PREFIX, GNode_VarPrefix(mem));
Var_Set(gn, TARGET, GNode_VarTarget(mem));
memSuff = mem->suffix;
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. */
Var_Set(MEMBER, name, gn);
Var_Set(ARCHIVE, gn->name, gn);
Var_Set(gn, MEMBER, name);
Var_Set(gn, ARCHIVE, gn->name);
/* 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
@ -1691,7 +1691,7 @@ FindDepsLib(GNode *gn)
Arch_FindLib(gn, suff->searchPath);
} else {
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
* the thing. .PREFIX is simply made empty.
*/
Var_Set(PREFIX, "", gn);
Var_Set(gn, PREFIX, "");
}
static void
@ -1776,7 +1776,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
if (gn->path == NULL)
return;
Var_Set(TARGET, gn->path, gn);
Var_Set(gn, TARGET, gn->path);
if (targ != NULL) {
/*
@ -1791,7 +1791,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
savec = gn->path[savep];
gn->path[savep] = '\0';
Var_Set(PREFIX, str_basename(gn->path), gn);
Var_Set(gn, PREFIX, str_basename(gn->path));
gn->path[savep] = savec;
} else {
@ -1800,7 +1800,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
* known 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(PREFIX, targ != NULL ? targ->prefix : gn->name, gn);
Var_Set(gn, TARGET, GNode_Path(gn));
Var_Set(gn, PREFIX, targ != NULL ? targ->prefix : gn->name);
/*
* 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.
*/
targ->node->type |= OP_DEPS_FOUND;
Var_Set(PREFIX, targ->prefix, targ->node);
Var_Set(TARGET, targ->node->name, targ->node);
Var_Set(targ->node, PREFIX, targ->prefix);
Var_Set(targ->node, TARGET, targ->node->name);
}
}
@ -2048,8 +2048,8 @@ FindDeps(GNode *gn, CandidateSearcher *cs)
gn->type |= OP_DEPS_FOUND;
/* Make sure we have these set, may get revised below. */
Var_Set(TARGET, GNode_Path(gn), gn);
Var_Set(PREFIX, gn->name, gn);
Var_Set(gn, TARGET, GNode_Path(gn));
Var_Set(gn, PREFIX, gn->name);
DEBUG1(SUFF, "SuffFindDeps \"%s\"\n", gn->name);
@ -2137,9 +2137,7 @@ Suffix_Print(Suffix *suff)
char flags_buf[SuffixFlags_ToStringSize];
debug_printf(" (%s)",
Enum_FlagsToString(flags_buf, sizeof flags_buf,
suff->flags,
SuffixFlags_ToStringSpecs));
SuffixFlags_ToString(flags_buf, suff->flags));
}
debug_printf("\n");

30
targ.c
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
@ -113,7 +113,7 @@
#include "dir.h"
/* "@(#)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
@ -243,14 +243,14 @@ GNode_Free(void *gnp)
* by this node.
*
* 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.
*
* XXX: The GNodes that are only used as variable contexts (VAR_CMD,
* VAR_GLOBAL, VAR_INTERNAL) are not freed at all (see Var_End, where
* they are not mentioned). These might be freed at all, if their
* variable values are indeed not used anywhere else (see Trace_Init
* for the only suspicious use).
* XXX: The GNodes that are only used as variable scopes (VAR_CMD,
* SCOPE_GLOBAL, SCOPE_INTERNAL) are not freed at all (see Var_End,
* where they are not mentioned). These might be freed at all, if
* their variable values are indeed not used anywhere else (see
* Trace_Init for the only suspicious use).
*/
HashTable_Done(&gn->vars);
@ -305,7 +305,7 @@ GNode *
Targ_NewInternalNode(const char *name)
{
GNode *gn = GNode_New(name);
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
Global_Append(".ALLTARGETS", name);
Lst_Append(&allTargets, gn);
DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
if (doing_depend)
@ -450,8 +450,8 @@ Targ_PrintType(int type)
}
}
static const char *
made_name(GNodeMade made)
const char *
GNodeMade_Name(GNodeMade made)
{
switch (made) {
case UNMADE: return "unmade";
@ -505,10 +505,10 @@ Targ_PrintNode(GNode *gn, int pass)
if (gn->mtime != 0) {
debug_printf("# last modified %s: %s\n",
Targ_FmtTime(gn->mtime),
made_name(gn->made));
GNodeMade_Name(gn->made));
} else if (gn->made != UNMADE) {
debug_printf("# nonexistent (maybe): %s\n",
made_name(gn->made));
GNodeMade_Name(gn->made));
} else
debug_printf("# unmade\n");
}
@ -577,10 +577,10 @@ Targ_PrintGraph(int pass)
PrintOnlySources();
debug_printf("#*** Global Variables:\n");
Var_Dump(VAR_GLOBAL);
Var_Dump(SCOPE_GLOBAL);
debug_printf("#*** Command-line Variables:\n");
Var_Dump(VAR_CMDLINE);
Var_Dump(SCOPE_CMDLINE);
debug_printf("\n");
Dir_PrintDirectories();

10
trace.c
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.
* All rights reserved.
*
@ -29,7 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
/*
* trace.c --
* handle logging of trace events generated by various parts of make.
*
@ -48,7 +48,7 @@
#include "job.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 pid_t trpid;
@ -71,7 +71,7 @@ Trace_Init(const char *pathname)
trpid = getpid();
/* XXX: This variable may get overwritten later, which
* would make trwd point to undefined behavior. */
curDir = Var_Value(".CURDIR", VAR_GLOBAL);
curDir = Var_Value(SCOPE_GLOBAL, ".CURDIR");
trwd = curDir.str;
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.
* All rights reserved.
*
@ -29,7 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
/*-
/*
* trace.h --
* 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)
#
@ -30,6 +30,8 @@
# src/tests/usr.bin/make/t_make.sh.
#
.MAIN: all
# we use these below but we might be an older make
.MAKE.OS?= ${uname -s:L:sh}
.MAKE.UID?= ${id -u:L:sh}
@ -45,6 +47,7 @@ TESTS+= cmd-errors-jobs
TESTS+= cmd-errors-lint
TESTS+= cmd-interrupt
TESTS+= cmdline
TESTS+= cmdline-redirect-stdin
TESTS+= cmdline-undefined
TESTS+= comment
TESTS+= compat-error
@ -200,6 +203,7 @@ TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
TESTS+= jobs-empty-commands
TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make
@ -256,6 +260,7 @@ TESTS+= opt-m-include-dir
TESTS+= opt-no-action
TESTS+= opt-no-action-at-all
TESTS+= opt-no-action-runflags
TESTS+= opt-no-action-touch
TESTS+= opt-query
TESTS+= opt-raw
TESTS+= opt-silent
@ -439,16 +444,10 @@ TESTS+= varquote
# escape-for-item.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.
# The base environment is -i PATH="$PATH".
ENV.depsrc-optional+= TZ=UTC
ENV.envfirst= FROM_ENV=value-from-env
ENV.objdir-writable+= RO_OBJDIR=${RO_OBJDIR}
ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=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. } \
-e '/^aa*--- job-b ---$$/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-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,pid [0-9][0-9]*,pid <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-several+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= \
-e 's,^${.SHELL:T}: [ 0-9:]*,,' \
-e 's,^${.SHELL:T}: ,,' \
-e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell}
SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
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-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.
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
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.
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,^\(MACHINE[_ARCH]* *=\) .*,\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.
STD_SED_CMDS.dj= \
@ -558,9 +563,34 @@ STD_SED_CMDS.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.config"
@ -603,7 +633,22 @@ LANG= C
_MKMSG_TEST= :
.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+= TMPDIR=${TMPDIR}
.if ${.MAKE.OS} == "NetBSD"
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 ,'
# replace anything after 'stopped in' with unit-tests
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g'
# strip ${.CURDIR}/ from the output
_SED_CMDS+= -e 's,${.CURDIR: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:
show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
show-objdir: TMPDIR/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5
makeobjdir-indirect:
show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
show-objdir: TMPDIR/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/
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.
TMPBASE?= /tmp/uid${.MAKE.UID}
TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}}
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
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 74: Malformed conditional (!(12345 === 12345))
make: Fatal errors encountered -- cannot continue

View File

@ -1,14 +1,10 @@
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: Malformed conditional (!(${:UINF} > 1e100))
make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or !=
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: Malformed conditional (${:UNaN} > NaN)
make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or !=
CondParser_Eval: !(${:UNaN} == NaN)
lhs = "NaN", rhs = "NaN", op = ==
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: Fatal errors encountered -- cannot continue
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 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 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: stopped in unit-tests
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.
@ -108,3 +108,31 @@
.if 12345.0 == "12345"
. 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
# 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: Malformed conditional (!defined(A B))
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 23: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 33: Missing closing parenthesis for defined()
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 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: Malformed conditional (!defined(A B))
make: "cond-func.mk" line 51: warning: 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 36: Missing closing parenthesis for defined()
make: "cond-func.mk" line 51: Missing closing parenthesis for defined()
make: "cond-func.mk" line 54: Missing closing parenthesis for 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 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 49: Not 1 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.
@ -55,5 +55,12 @@
. info Not word evaluates to false.
.endif
# A single exclamation mark is a parse error.
.if !
. error
.else
. error
.endif
all:
@:;

View File

@ -1,2 +1,6 @@
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.
@ -15,5 +15,22 @@
. error
.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:
@:;

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 1 0 => 0 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: stopped in unit-tests
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.
#
@ -61,8 +61,8 @@
# As soon as the parser sees the '$', it knows that the condition will
# be malformed. Therefore there is no point in evaluating it.
#
# As of 2020-09-11, that part of the condition is evaluated nevertheless,
# since CondParser_Expr just requests the next token, without restricting
# As of 2021-01-20, that part of the condition is evaluated nevertheless,
# since CondParser_Or just requests the next token, without restricting
# 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
# a correct condition (which in this case would be comparison operators,
@ -98,5 +98,39 @@
. 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:
@:;

View File

@ -18,12 +18,37 @@ lhs = "yes", rhs = "yes", op = !=
CondParser_Eval: ${UNDEF:Uundefined}!=undefined
lhs = "undefined", rhs = "undefined", op = !=
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
lhs = 12345.000000, rhs = 12345.000000, op = <1
lhs = 12345.000000, rhs = 12345.000000, op = <
CondParser_Eval: (${UNDEF:U0})||0
CondParser_Eval: ${:Uvar}&&name != "var&&name"
lhs = "var&&name", rhs = "var&&name", op = !=
CondParser_Eval: ${:Uvar}||name != "var||name"
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)
# in .if conditions.
@ -93,5 +93,100 @@
. error
.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:
@:;

View File

@ -1,8 +1,18 @@
make: "cond-token-string.mk" line 9: Unknown modifier 'Z'
make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 18: xvalue is not defined.
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")
make: "cond-token-string.mk" line 33: Expected.
make: "cond-token-string.mk" line 13: Unknown modifier 'Z'
make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 22: xvalue is not defined.
make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "")
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: stopped in unit-tests
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
@ -35,5 +39,44 @@
. error
.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:
@:;

View File

@ -16,7 +16,7 @@ Passed:
4 is not 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
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.
@ -79,7 +79,7 @@ single-word:
# Demonstrate debug logging for filename expansion, especially curly braces.
.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
# indeed made.
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
# immediately, unlike other "fatal" parse errors, which continue to parse
# until the end of the current top-level makefile.
#
# See also:
# opt-warnings-as-errors.mk
# TODO: Implementation
all:
@:;
# Before parse.c 1.532 from 2021-01-27, the ".error" issued an irrelevant
# message saying "parsing warnings being treated as errors".
.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(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
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""
Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
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)
Var_Parse: ${UT_VAR} 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 = !=
ParseReadLine (49): ': ${UT_VAR:N*}'
Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
@ -35,7 +35,7 @@ ParseReadLine (53): 'REF= defined'
Global:REF = defined
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
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""
Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
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)
Var_Parse: ${UT_VAR} 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 = !=
ParseReadLine (61): 'all:'
ParseDoDependency(all:)

View File

@ -27,25 +27,29 @@ make: "directive-for-escape.mk" line 41: value-with-modifier
For: end for 1
For: loop body:
. 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:
. info ${:U{{\}\}}
make: "directive-for-escape.mk" line 52: {{}}
make: "directive-for-escape.mk" line 55: {{}}
For: loop body:
. 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: loop body:
. info ${:U\$}
make: "directive-for-escape.mk" line 60: $
make: "directive-for-escape.mk" line 74: $
For: end for 1
For: loop body:
. 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: loop body:
. info ${:Ureplaced}
make: "directive-for-escape.mk" line 78: replaced
make: "directive-for-escape.mk" line 92: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@ -54,21 +58,18 @@ For: loop body:
. info . $$(i): $(:Uinner)
. info . $$(i:M*): $(:Uinner:M*)
. 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 . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
make: "directive-for-escape.mk" line 86: . $i: inner
make: "directive-for-escape.mk" line 87: . ${i}: inner
make: "directive-for-escape.mk" line 88: . ${i:M*}: inner
make: "directive-for-escape.mk" line 89: . $(i): inner
make: "directive-for-escape.mk" line 90: . $(i:M*): inner
make: "directive-for-escape.mk" line 91: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 92: . ${i\}}: inner}
make: "directive-for-escape.mk" line 93: . ${i2}: two
make: "directive-for-escape.mk" line 94: . ${i,}: comma
make: "directive-for-escape.mk" line 95: . adjacent: innerinnerinnerinner
make: no target to make.
make: stopped in unit-tests
exit status 2
make: "directive-for-escape.mk" line 100: . $i: inner
make: "directive-for-escape.mk" line 101: . ${i}: inner
make: "directive-for-escape.mk" line 102: . ${i:M*}: inner
make: "directive-for-escape.mk" line 103: . $(i): inner
make: "directive-for-escape.mk" line 104: . $(i:M*): inner
make: "directive-for-escape.mk" line 105: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 106: . ${i\}}: inner}
make: "directive-for-escape.mk" line 107: . ${i2}: two
make: "directive-for-escape.mk" line 108: . ${i,}: comma
make: "directive-for-escape.mk" line 109: . adjacent: innerinnerinnerinner
exit status 0

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.
# 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
.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
# represent dollar, lbrace, "UNDEF:U", backslash, dollar, backslash, dollar,
# space, nested braces, space, "end}".
# The value of VALUES is not meant to be a variable expression. Instead, it
# is meant to represent dollar, lbrace, "UNDEF:U", backslash, dollar,
# 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}
# XXX: Where does the '\$$\$$' get converted into a single '\$'?
.for i in ${VALUES}
. info $i
.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.
# 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
@ -89,8 +103,10 @@ i,= comma
. info . $$(i): $(i)
. info . $$(i:M*): $(i:M*)
. 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 . $${i,}: ${i,}
. info . adjacent: $i${i}${i:M*}$i
.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 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

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.
@ -14,5 +14,20 @@ DEFINED= defined
. error
.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:
@:;

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.
#
@ -6,7 +6,7 @@
# 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
# 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.
# 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.
@ -33,7 +33,7 @@ FROM_ENV?= default
. error ${FROM_ENV}
.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.
.undef FROM_ENV
.if ${FROM_ENV} != value-from-env

View File

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

View File

@ -1,11 +1,11 @@
#*** Input graph:
# all, made UNMADE, type OP_DEPENDS, flags none
# makeinfo, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# make-index, made 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-no-dot, made 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
# parentheses-dot, made 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
# all, unmade, type OP_DEPENDS, flags none
# makeinfo, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# make-index, 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, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none
# braces-no-dot-modifier, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# parentheses-dot, 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

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-sub.mk" line 4: sub-before-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 45: sub-after-for-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
# as described in the manual page.
@ -17,7 +17,7 @@
.endif
.for i in once
. if !defined(${.INCLUDEDFROMFILE})
. if !defined(.INCLUDEDFROMFILE)
. info main-before-for-ok
. else
. warning main-before-for-fail(${.INCLUDEDFROMFILE})
@ -33,7 +33,7 @@
.endif
.for i in once
. if !defined(${.INCLUDEDFROMFILE})
. if !defined(.INCLUDEDFROMFILE)
. info main-after-for-ok
. else
. 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"
.MAKEFLAGS: -dp
. info subsub-ok
.MAKEFLAGS: -d0
.else
. warning subsub-fail(${.INCLUDEDFROMFILE})
.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,
# using the -dL option.
# Demonstrates stricter checks that are only enabled in lint mode, using the
# option -dL.
# Ouch: as of 2020-08-03, make exits successfully even though the error
# message has been issued as PARSE_FATAL.
# Before main.c 1.421 from 2020-11-01, make exited successfully even though
# 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
# 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
OBJDIR/roobj
OBJDIR/roobj
TMPDIR/roobj
TMPDIR/roobj
exit status 0

View File

@ -1,12 +1,12 @@
#*** Input graph:
# all, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# made-target, made UNMADE, type OP_DEPENDS, flags none
# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none
# made-source, made UNMADE, type OP_DEPENDS, flags none
# unmade-target, made UNMADE, type OP_DEPENDS, flags none
# unmade-sources, made UNMADE, type none, flags none
# unmade-silent-source, made UNMADE, type OP_SILENT, flags none
# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none
# all, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# made-target, unmade, type OP_DEPENDS, flags none
# made-target-no-sources, unmade, type OP_DEPENDS, flags none
# made-source, unmade, type OP_DEPENDS, flags none
# unmade-target, unmade, type OP_DEPENDS, flags none
# unmade-sources, unmade, type none, flags none
# unmade-silent-source, unmade, type OP_SILENT, 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
# 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
# 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.
@ -9,12 +9,18 @@
.MAKEFLAGS: opt-touch-use
.MAKEFLAGS: opt-touch-make
# .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 $@.

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: "opt-warnings-as-errors.mk" line 8: warning: message 2
make: "opt-warnings-as-errors.mk" line 13: warning: message 2
parsing continues
make: Fatal errors encountered -- cannot continue
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.
#
# 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

View File

@ -18,8 +18,8 @@ defining transformation from `.a' to `.c'
inserting ".a" (1) at end of list
inserting ".c" (3) at end of list
# LinkSource: added child .a.c - ${.PREFIX}.dependency
# .a.c, made UNMADE, type OP_DEPENDS|OP_TRANSFORM, flags none
# ${.PREFIX}.dependency, made UNMADE, type none, flags none
# .a.c, unmade, type OP_DEPENDS|OP_TRANSFORM, flags none
# ${.PREFIX}.dependency, unmade, type none, flags none
ParseReadLine (23): '.DEFAULT:'
transformation .a.c complete
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}'
ParseDoDependency(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
# suff-main-several.{2,3,4}, made UNMADE, type none, flags none
# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none
# suff-main-several.{2,3,4}, unmade, type none, flags none
ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1'
ParseDoDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph:
# .1.2, made UNMADE, type OP_TRANSFORM, flags none
# .1.3, made UNMADE, type OP_TRANSFORM, flags none
# .1.4, made UNMADE, type OP_TRANSFORM, flags none
# next-main, made 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.{2,3,4}, made UNMADE, type none, flags none
# .1.2, unmade, type OP_TRANSFORM, flags none
# .1.3, unmade, type OP_TRANSFORM, flags none
# .1.4, unmade, type OP_TRANSFORM, flags none
# next-main, 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}, unmade, type none, flags none
#

View File

@ -1,5 +1,5 @@
#*** 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