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:
parent
8e11a9b425
commit
8b6f73e37b
73
ChangeLog
73
ChangeLog
@ -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
6
FILES
@ -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
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20210110
|
||||
_MAKE_VERSION=20210206
|
||||
|
24
arch.c
24
arch.c
@ -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
|
||||
}
|
||||
|
||||
|
6
bmake.1
6
bmake.1
@ -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.
|
||||
|
@ -37,7 +37,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[1m-C [4m[22m/etc[24m.
|
||||
|
||||
[1m-D [4m[22mvariable[0m
|
||||
Define [4mvariable[24m to be 1, in the global context.
|
||||
Define [4mvariable[24m to be 1, in the global scope.
|
||||
|
||||
[1m-d [4m[22m[-]flags[0m
|
||||
Turn on debugging, and specify which portions of [1mbmake [22mare 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 [4mvariable[24m 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
63
buf.c
@ -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
25
buf.h
@ -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 */
|
||||
|
18
compat.c
18
compat.c
@ -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
645
cond.c
@ -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. */
|
||||
|
@ -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
36
configure
vendored
@ -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\\"
|
||||
|
||||
|
15
configure.in
15
configure.in
@ -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
454
dir.c
@ -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
10
dir.h
@ -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
16
enum.c
@ -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
33
enum.h
@ -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
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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
253
for.c
@ -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
13
hash.c
@ -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
260
job.c
@ -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
3
job.h
@ -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
17
lst.c
@ -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
4
lst.h
@ -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
316
main.c
@ -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
6
make.1
@ -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
154
make.c
@ -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
104
make.h
@ -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 */
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
87
meta.c
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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 != '=')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
|
58
nonints.h
58
nonints.h
@ -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
306
parse.c
@ -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
22
str.c
@ -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
66
suff.c
@ -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
30
targ.c
@ -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
10
trace.c
@ -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");
|
||||
|
6
trace.h
6
trace.h
@ -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.
|
||||
*/
|
||||
|
@ -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'
|
||||
|
1
unit-tests/cmdline-redirect-stdin.exp
Normal file
1
unit-tests/cmdline-redirect-stdin.exp
Normal file
@ -0,0 +1 @@
|
||||
exit status 0
|
34
unit-tests/cmdline-redirect-stdin.mk
Normal file
34
unit-tests/cmdline-redirect-stdin.mk
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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(...).
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "directive-error.mk" line 13: message
|
||||
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -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
|
||||
|
@ -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:)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@:;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,5 @@
|
||||
MAKELEVEL=1
|
||||
TMPDIR=TMPDIR
|
||||
UT_DOLLAR=This is $UT_FU
|
||||
UT_FOO=foobar is fubar
|
||||
UT_FU=fubar
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
2
unit-tests/jobs-empty-commands.exp
Normal file
2
unit-tests/jobs-empty-commands.exp
Normal file
@ -0,0 +1,2 @@
|
||||
action
|
||||
exit status 0
|
18
unit-tests/jobs-empty-commands.mk
Normal file
18
unit-tests/jobs-empty-commands.mk
Normal 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'
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
#
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
11
unit-tests/opt-no-action-touch.exp
Normal file
11
unit-tests/opt-no-action-touch.exp
Normal 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
|
48
unit-tests/opt-no-action-touch.mk
Normal file
48
unit-tests/opt-no-action-touch.mk
Normal 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; }
|
@ -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 $@.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:)
|
||||
|
@ -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
|
||||
|
||||
|
||||
#
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user