Import bmake-20210621

Lots more unit tests and code cleanup

Relevant changes from ChangeLog

	o job.c: Print -de error information when running multiple jobs
	o var.c: only report error for unmatched regex subexpression
	when linting (-dL) since we cannot tell when an unmatched
	subexpression is an expected result.
	reduce memory allocations in the modifiers ':D' and ':U'
	reduce memory allocation and strlen calls in modifier ':from=to'
	in the ':Q' modifier, only allocate memory if necessary
	improve performance for LazyBuf
	reduce debug logging and memory allocation for ${:U...}
	reduce verbosity of the -dv debug logging for standard cases
	fix double varname expansion in the variable modifier '::='
	o var.c: avoid evaluating many modifiers in parse only mode
	in strict mode (-dL) many variable references are parsed twice,
	the first time just to report parse errors early, so we want to
	avoid side effects and wasted effort to the extent possible.
This commit is contained in:
Simon J. Gerraty 2021-06-25 11:16:24 -07:00
parent 8b6f73e37b
commit ee914ef902
144 changed files with 5491 additions and 3540 deletions

113
ChangeLog
View File

@ -1,3 +1,116 @@
2021-06-21 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210621
Merge with NetBSD make, pick up
o var.c: only report error for unmatched regex subexpression
when linting (-dL) since we cannot tell when an unmatched
subexpression is an expected result.
o move unmatched regex subexpression tests to
varmod-subst-regex.mk and enable strict (lint) mode
2021-06-16 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210616
Merge with NetBSD make, pick up
o more unit tests
o cond.c: rename If_Eval to EvalBare
improve function names for parsing conditions
o job.c: fix error handling of targets that cannot be made
o var.c: uncompress code in ApplyModifier_Unique
2021-05-18 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210518
Merge with NetBSD make, pick up
o fix unit-tests/opt-chdir to cope with /nonexistent existing.
o job.c: Print -de error information when running multiple jobs
2021-04-20 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210420
Merge with NetBSD make, pick up
o use C99 bool type
o convert VarEvalFlags back into an enum
o cond.c: do not complain when skipping the condition 'no >= 10'
o hash.c: avoid allocating memory for simple variable names
o job.c: use distinct wording for writing to the shell commands file
remove type name for the abort status in job handling
rename PrintOutput to PrintFilteredOutput to avoid confusion
o main.c: avoid double slash in name of temporary directory
o var.c: use straight quotes for error 'Bad conditional expression'
reduce memory allocations in the modifiers ':D' and ':U'
rename members of ModifyWord_LoopArgs
clean up pattern flags for the modifiers ':S' and ':C'
reduce memory allocation and strlen calls in modifier ':from=to'
in the ':Q' modifier, only allocate memory if necessary
improve performance for LazyBuf
remove redundant parameter from ParseVarnameLong
migrate ParseModifierPart to use Substring
avoid unnecessary calls to strlen when evaluating modifiers
migrate ModifyWord functions to use Substring
migrate handling of the modifier ':S,from,to,' to Substring
reduce debug logging and memory allocation for ${:U...}
reduce verbosity of the -dv debug logging for standard cases
clean up debug logging for ':M' and ':N'
disallow '$' in the variable name of the modifier ':@'
simplify access to the name of an expression during evaluation
2021-03-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210330
Merge with NetBSD make, pick up
o replace enum bit-field with struct bit-field for VarEvalFlags
o rename VARE_NONE to VARE_PARSE_ONLY
o var.c: rename ApplyModifiersState to ModChain
fix double varname expansion in the variable modifier '::='
change debug log for variable evaluation flags to lowercase
2021-03-14 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210314
Merge with NetBSD make, pick up
o var.c: avoid evaluating many modifiers in parse only mode
in strict mode (-dL) many variable references are parsed twice,
the first time just to report parse errors early, so we want to
avoid side effects and wasted effort to the extent possible.
2021-02-26 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210226
Merge with NetBSD make, pick up
o remove freestanding freeIt variables
link via FStr
o var.c: restructure code in ParseVarname to target human readers
improve error message for;
bad modifier in variable expression
unclosed modifier
unknown modifier
remove redundant parameter of ApplySingleModifier
explain non-obvious code around indirect variable modifiers
quote ':S' in error message about missing delimiter
extract ParseModifier_Match into separate function
add context information to error message about ':range' modifier
add quotes around variable name in an error message
reorder code in ModifyWords
use more common parameter order for VarSelectWords
make ModifyWord_Subst a little easier to understand
do not expand variable name from the command line twice
extract ExistsInCmdline from Var_SetWithFlags
save a hash map lookup when defining a cmdline variable
clean up VarAdd, Var_Delete, Var_ReexportVars
use bit-shift expressions for VarFlags constants
rename constants for VarFlags
rename ExprDefined constants for debug logging
rename ExprStatus to ExprDefined
split parameters for evaluating variable expressions
reduce redundant code around ModifyWords
print error about failed shell command before overwriting variable
clean up ValidShortVarname, ParseVarnameShort
rename VarExprStatus to ExprStatus
add functions for assigning the value of an expression
rename ApplyModifiersState_Define to Expr_Define
condense the code for parsing :S and :C modifiers
2021-02-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210206

13
FILES
View File

@ -63,6 +63,7 @@ realpath.c
setenv.c
sigcompat.c
str.c
str.h
stresep.c
strlcpy.c
suff.c
@ -399,6 +400,10 @@ 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/job-output-null.exp
unit-tests/job-output-null.mk
unit-tests/jobs-empty-commands-error.exp
unit-tests/jobs-empty-commands-error.mk
unit-tests/jobs-empty-commands.exp
unit-tests/jobs-empty-commands.mk
unit-tests/jobs-error-indirect.exp
@ -439,6 +444,8 @@ unit-tests/opt-debug-curdir.exp
unit-tests/opt-debug-curdir.mk
unit-tests/opt-debug-dir.exp
unit-tests/opt-debug-dir.mk
unit-tests/opt-debug-errors-jobs.exp
unit-tests/opt-debug-errors-jobs.mk
unit-tests/opt-debug-errors.exp
unit-tests/opt-debug-errors.mk
unit-tests/opt-debug-file.exp
@ -627,6 +634,8 @@ unit-tests/var-class-local.exp
unit-tests/var-class-local.mk
unit-tests/var-class.exp
unit-tests/var-class.mk
unit-tests/var-eval-short.exp
unit-tests/var-eval-short.mk
unit-tests/var-op-append.exp
unit-tests/var-op-append.mk
unit-tests/var-op-assign.exp
@ -675,6 +684,8 @@ unit-tests/varmod-l-name-to-value.exp
unit-tests/varmod-l-name-to-value.mk
unit-tests/varmod-localtime.exp
unit-tests/varmod-localtime.mk
unit-tests/varmod-loop-varname.exp
unit-tests/varmod-loop-varname.mk
unit-tests/varmod-loop.exp
unit-tests/varmod-loop.mk
unit-tests/varmod-match-escape.exp
@ -709,6 +720,8 @@ unit-tests/varmod-subst-regex.exp
unit-tests/varmod-subst-regex.mk
unit-tests/varmod-subst.exp
unit-tests/varmod-subst.mk
unit-tests/varmod-sun-shell.exp
unit-tests/varmod-sun-shell.mk
unit-tests/varmod-sysv.exp
unit-tests/varmod-sysv.mk
unit-tests/varmod-tail.exp

View File

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

104
arch.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 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.197 2021/02/05 05:15:12 rillig Exp $");
MAKE_RCSID("$NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@ -216,6 +216,19 @@ ArchFree(void *ap)
}
#endif
/* Return "archive(member)". */
static char *
FullName(const char *archive, const char *member)
{
size_t len1 = strlen(archive);
size_t len3 = strlen(member);
char *result = bmake_malloc(len1 + 1 + len3 + 1 + 1);
memcpy(result, archive, len1);
memcpy(result + len1, "(", 1);
memcpy(result + len1 + 1, member, len3);
memcpy(result + len1 + 1 + len3, ")", 1 + 1);
return result;
}
/*
* Parse an archive specification such as "archive.a(member1 member2.${EXT})",
@ -228,10 +241,10 @@ ArchFree(void *ap)
* scope The scope in which to expand variables.
*
* Output:
* return TRUE if it was a valid specification.
* return True if it was a valid specification.
* *pp Points to the first non-space after the archive spec.
*/
Boolean
bool
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
{
char *cp; /* Pointer into line */
@ -239,12 +252,12 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
MFStr libName; /* Library-part of specification */
char *memName; /* Member-part of specification */
char saveChar; /* Ending delimiter of member-name */
Boolean expandLibName; /* Whether the parsed libName contains
bool expandLibName; /* Whether the parsed libName contains
* variable expressions that need to be
* expanded */
libName = MFStr_InitRefer(*pp);
expandLibName = FALSE;
expandLibName = false;
for (cp = libName.str; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
@ -252,18 +265,18 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* XXX: This code can probably be shortened. */
const char *nested_p = cp;
FStr result;
Boolean isError;
bool isError;
/* XXX: is expanded twice: once here and once below */
(void)Var_Parse(&nested_p, scope,
VARE_WANTRES | VARE_UNDEFERR, &result);
VARE_UNDEFERR, &result);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
if (isError)
return FALSE;
return false;
expandLibName = TRUE;
expandLibName = true;
cp += nested_p - cp;
} else
cp++;
@ -272,8 +285,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
*cp++ = '\0';
if (expandLibName) {
char *expanded;
(void)Var_Subst(libName.str, scope,
VARE_WANTRES | VARE_UNDEFERR, &expanded);
(void)Var_Subst(libName.str, scope, VARE_UNDEFERR, &expanded);
/* TODO: handle errors */
libName = MFStr_InitOwn(expanded);
}
@ -285,7 +297,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* place and skip to the end of it (either white-space or
* a close paren).
*/
Boolean doSubst = FALSE;
bool doSubst = false;
pp_skip_whitespace(&cp);
@ -295,20 +307,19 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* Expand nested variable expressions. */
/* XXX: This code can probably be shortened. */
FStr result;
Boolean isError;
bool isError;
const char *nested_p = cp;
(void)Var_Parse(&nested_p, scope,
VARE_WANTRES | VARE_UNDEFERR,
&result);
VARE_UNDEFERR, &result);
/* TODO: handle errors */
isError = result.str == var_Error;
FStr_Done(&result);
if (isError)
return FALSE;
return false;
doSubst = TRUE;
doSubst = true;
cp += nested_p - cp;
} else {
cp++;
@ -325,7 +336,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Parse_Error(PARSE_FATAL,
"No closing parenthesis "
"in archive specification");
return FALSE;
return false;
}
/*
@ -355,16 +366,15 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
char *p;
char *unexpandedMemName = memName;
(void)Var_Subst(memName, scope,
VARE_WANTRES | VARE_UNDEFERR,
&memName);
(void)Var_Subst(memName, scope, VARE_UNDEFERR,
&memName);
/* TODO: handle errors */
/*
* Now form an archive spec and recurse to deal with
* nested variables and multi-word variable values.
*/
fullName = str_concat4(libName.str, "(", memName, ")");
fullName = FullName(libName.str, memName);
p = fullName;
if (strchr(memName, '$') != NULL &&
@ -383,7 +393,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* Error in nested call. */
free(fullName);
/* XXX: does unexpandedMemName leak? */
return FALSE;
return false;
}
free(fullName);
/* XXX: does unexpandedMemName leak? */
@ -394,8 +404,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
while (!Lst_IsEmpty(&members)) {
char *member = Lst_Dequeue(&members);
char *fullname = str_concat4(libName.str, "(",
member, ")");
char *fullname = FullName(libName.str, member);
free(member);
gn = Targ_GetNode(fullname);
@ -407,8 +416,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Lst_Done(&members);
} else {
char *fullname = str_concat4(libName.str, "(", memName,
")");
char *fullname = FullName(libName.str, memName);
gn = Targ_GetNode(fullname);
free(fullname);
@ -434,7 +442,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/* We promised that pp would be set up at the next non-space. */
pp_skip_whitespace(&cp);
*pp = cp;
return TRUE;
return true;
}
/*
@ -444,7 +452,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* Input:
* archive Path to the archive
* member Name of member; only its basename is used.
* addToCache TRUE if archive should be cached if not already so.
* addToCache True if archive should be cached if not already so.
*
* Results:
* The ar_hdr for the member, or NULL.
@ -452,7 +460,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
* See ArchFindMember for an almost identical copy of this code.
*/
static struct ar_hdr *
ArchStatMember(const char *archive, const char *member, Boolean addToCache)
ArchStatMember(const char *archive, const char *member, bool addToCache)
{
#define AR_MAX_NAME_LEN (sizeof arh.ar_name - 1)
FILE *arch;
@ -713,7 +721,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
#endif
static Boolean
static bool
ArchiveMember_HasName(const struct ar_hdr *hdr,
const char *name, size_t namelen)
{
@ -721,22 +729,22 @@ ArchiveMember_HasName(const struct ar_hdr *hdr,
const char *ar_name = hdr->AR_NAME;
if (strncmp(ar_name, name, namelen) != 0)
return FALSE;
return false;
if (namelen >= ar_name_len)
return namelen == ar_name_len;
/* hdr->AR_NAME is space-padded to the right. */
if (ar_name[namelen] == ' ')
return TRUE;
return true;
/* In archives created by GNU binutils 2.27, the member names end with
* a slash. */
if (ar_name[namelen] == '/' &&
(namelen == ar_name_len || ar_name[namelen + 1] == ' '))
return TRUE;
return true;
return FALSE;
return false;
}
/*
@ -951,7 +959,7 @@ Arch_UpdateMTime(GNode *gn)
{
struct ar_hdr *arh;
arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), true);
if (arh != NULL)
gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10);
else
@ -1058,26 +1066,26 @@ Arch_FindLib(GNode *gn, SearchPath *path)
* since this is used by 'ar' rules that affect the data contents of the
* archive, not by ranlib rules, which affect the TOC.
*/
Boolean
bool
Arch_LibOODate(GNode *gn)
{
Boolean oodate;
bool oodate;
if (gn->type & OP_PHONY) {
oodate = TRUE;
oodate = true;
} else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) {
oodate = FALSE;
oodate = false;
} else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
(gn->mtime > now) ||
(gn->youngestChild != NULL &&
gn->mtime < gn->youngestChild->mtime)) {
oodate = TRUE;
oodate = true;
} else {
#ifdef RANLIBMAG
struct ar_hdr *arh; /* Header for __.SYMDEF */
int modTimeTOC; /* The table-of-contents' mod time */
arh = ArchStatMember(gn->path, RANLIBMAG, FALSE);
arh = ArchStatMember(gn->path, RANLIBMAG, false);
if (arh != NULL) {
modTimeTOC = (int)strtol(arh->ar_date, NULL, 10);
@ -1094,10 +1102,10 @@ Arch_LibOODate(GNode *gn)
*/
if (DEBUG(ARCH) || DEBUG(MAKE))
debug_printf("no toc...");
oodate = TRUE;
oodate = true;
}
#else
oodate = FALSE;
oodate = false;
#endif
}
return oodate;
@ -1119,7 +1127,7 @@ Arch_End(void)
#endif
}
Boolean
bool
Arch_IsLib(GNode *gn)
{
static const char armag[] = "!<arch>\n";
@ -1127,11 +1135,11 @@ Arch_IsLib(GNode *gn)
int fd;
if ((fd = open(gn->path, O_RDONLY)) == -1)
return FALSE;
return false;
if (read(fd, buf, sizeof buf) != sizeof buf) {
(void)close(fd);
return FALSE;
return false;
}
(void)close(fd);

4
buf.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.h,v 1.42 2021/01/30 21:25:10 rillig Exp $ */
/* $NetBSD: buf.h,v 1.43 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -101,7 +101,7 @@ Buf_AddByte(Buffer *buf, char byte)
end[1] = '\0';
}
MAKE_INLINE Boolean
MAKE_INLINE bool
Buf_EndsWith(const Buffer *buf, char ch)
{
return buf->len > 0 && buf->data[buf->len - 1] == ch;

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -99,7 +99,7 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $");
MAKE_RCSID("$NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
@ -164,7 +164,7 @@ CompatInterrupt(int signo)
}
static void
DebugFailedTarget(const char *cmd, GNode *gn)
DebugFailedTarget(const char *cmd, const GNode *gn)
{
const char *p = cmd;
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
@ -184,7 +184,7 @@ DebugFailedTarget(const char *cmd, GNode *gn)
debug_printf("\n");
}
static Boolean
static bool
UseShell(const char *cmd MAKE_ATTR_UNUSED)
{
#if !defined(MAKE_NATIVE)
@ -194,7 +194,7 @@ UseShell(const char *cmd MAKE_ATTR_UNUSED)
* behaviour. Or perhaps the shell has been replaced with something
* that does extra logging, and that should not be bypassed.
*/
return TRUE;
return true;
#else
/*
* Search for meta characters in the command. If there are no meta
@ -227,22 +227,22 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
{
char *cmdStart; /* Start of expanded command */
char *bp;
Boolean silent; /* Don't print command */
Boolean doIt; /* Execute even if -n */
volatile Boolean errCheck; /* Check errors */
bool silent; /* Don't print command */
bool doIt; /* Execute even if -n */
volatile bool errCheck; /* Check errors */
WAIT_T reason; /* Reason for child's death */
WAIT_T status; /* Description of child's death */
pid_t cpid; /* Child actually found */
pid_t retstat; /* Result of wait */
const char **volatile av; /* Argument vector for thing to exec */
char **volatile mav; /* Copy of the argument vector for freeing */
Boolean useShell; /* TRUE if command should be executed
bool useShell; /* True if command should be executed
* using a shell */
const char *volatile cmd = cmdp;
silent = (gn->type & OP_SILENT) != 0;
errCheck = !(gn->type & OP_IGNORE);
doIt = FALSE;
doIt = false;
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
/* TODO: handle errors */
@ -281,9 +281,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
if (*cmd == '@')
silent = !DEBUG(LOUD);
else if (*cmd == '-')
errCheck = FALSE;
errCheck = false;
else if (*cmd == '+') {
doIt = TRUE;
doIt = true;
if (shellName == NULL) /* we came here from jobs */
Shell_Init();
} else
@ -343,7 +343,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
* command into words to form an argument vector we can
* execute.
*/
Words words = Str_Words(cmd, FALSE);
Words words = Str_Words(cmd, false);
mav = words.words;
bp = words.freeIt;
av = (void *)mav;
@ -392,7 +392,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
*/
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
JobReapChild(retstat, reason, FALSE); /* not ours? */
JobReapChild(retstat, reason, false); /* not ours? */
if (retstat == -1 && errno != EINTR) {
break;
}
@ -425,7 +425,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
if (errCheck) {
#ifdef USE_META
if (useMeta) {
meta_job_error(NULL, gn, FALSE, status);
meta_job_error(NULL, gn, false, status);
}
#endif
gn->made = ERROR;
@ -483,7 +483,7 @@ MakeNodes(GNodeList *gnodes, GNode *pgn)
}
}
static Boolean
static bool
MakeUnmade(GNode *gn, GNode *pgn)
{
@ -493,7 +493,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* First mark ourselves to be made, then apply whatever transformations
* the suffix module thinks are necessary. Once that's done, we can
* descend and make all our children. If any of them has an error
* but the -k flag was given, our 'make' field will be set to FALSE
* but the -k flag was given, our 'make' field will be set to false
* again. This is our signal to not attempt to do anything but abort
* our parent as well.
*/
@ -508,7 +508,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
if (!(gn->flags & REMAKE)) {
gn->made = ABORTED;
pgn->flags &= ~(unsigned)REMAKE;
return FALSE;
return false;
}
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
@ -524,7 +524,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
if (!GNode_IsOODate(gn)) {
gn->made = UPTODATE;
DEBUG0(MAKE, "up-to-date.\n");
return FALSE;
return false;
}
/*
@ -539,7 +539,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* We need to be re-made.
* Ensure that $? (.OODATE) and $> (.ALLSRC) are both set.
*/
Make_DoAllVar(gn);
GNode_SetLocalVars(gn);
/*
* Alter our type to tell if errors should be ignored or things
@ -596,7 +596,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
PrintOnError(gn, "\nStop.");
exit(1);
}
return TRUE;
return true;
}
static void

279
cond.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -95,7 +95,7 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $");
MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $");
/*
* The parsing of conditional expressions is based on this grammar:
@ -148,11 +148,11 @@ typedef struct CondParser {
* expression has length > 0. The other '.if' variants delegate
* to evalBare instead.
*/
Boolean plain;
bool plain;
/* The function to apply on unquoted bare words. */
Boolean (*evalBare)(size_t, const char *);
Boolean negateEvalBare;
bool (*evalBare)(size_t, const char *);
bool negateEvalBare;
const char *p; /* The remaining condition to parse */
Token curr; /* Single push-back token used in parsing */
@ -163,10 +163,10 @@ typedef struct CondParser {
* specific one, therefore it makes sense to suppress the standard
* "Malformed conditional" message.
*/
Boolean printedError;
bool printedError;
} CondParser;
static CondResult CondParser_Or(CondParser *par, Boolean);
static CondResult CondParser_Or(CondParser *par, bool);
static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */
@ -178,21 +178,21 @@ static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
* In strict mode, the lhs must be a variable expression or a string literal
* in quotes. In non-strict mode it may also be an unquoted string literal.
*
* TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
* FALSE when CondEvalExpression is called from ApplyModifier_IfElse
* True when CondEvalExpression is called from Cond_EvalLine (.if etc).
* False when CondEvalExpression is called from ApplyModifier_IfElse
* since lhs is already expanded, and at that point we cannot tell if
* it was a variable reference or not.
*/
static Boolean lhsStrict;
static bool lhsStrict;
static Boolean
static bool
is_token(const char *str, const char *tok, size_t len)
{
return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
}
static Token
ToToken(Boolean cond)
ToToken(bool cond)
{
return cond ? TOK_TRUE : TOK_FALSE;
}
@ -228,7 +228,7 @@ CondParser_SkipWhitespace(CondParser *par)
* Return the length of the argument, or 0 on error.
*/
static size_t
ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
char **out_arg)
{
const char *p = *pp;
@ -264,11 +264,11 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
* so we don't need to do it. Nor do we return an
* error, though perhaps we should.
*/
VarEvalFlags eflags = doEval
? VARE_WANTRES | VARE_UNDEFERR
: VARE_NONE;
VarEvalMode emode = doEval
? VARE_UNDEFERR
: VARE_PARSE_ONLY;
FStr nestedVal;
(void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal);
(void)Var_Parse(&p, SCOPE_CMDLINE, emode, &nestedVal);
/* TODO: handle errors */
Buf_AddStr(&argBuf, nestedVal.str);
FStr_Done(&nestedVal);
@ -290,7 +290,7 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
if (func != NULL && *p++ != ')') {
Parse_Error(PARSE_FATAL,
"Missing closing parenthesis for %s()", func);
par->printedError = TRUE;
par->printedError = true;
return 0;
}
@ -300,34 +300,34 @@ ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func,
/* Test whether the given variable is defined. */
/*ARGSUSED*/
static Boolean
static bool
FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
FStr value = Var_Value(SCOPE_CMDLINE, arg);
Boolean result = value.str != NULL;
bool result = value.str != NULL;
FStr_Done(&value);
return result;
}
/* See if the given target is being made. */
/*ARGSUSED*/
static Boolean
static bool
FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
StringListNode *ln;
for (ln = opts.create.first; ln != NULL; ln = ln->next)
if (Str_Match(ln->datum, arg))
return TRUE;
return FALSE;
return true;
return false;
}
/* See if the given file exists. */
/*ARGSUSED*/
static Boolean
static bool
FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
Boolean result;
bool result;
char *path;
path = Dir_FindFile(arg, &dirSearchPath);
@ -340,7 +340,7 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
/* See if the given node exists and is an actual target. */
/*ARGSUSED*/
static Boolean
static bool
FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
GNode *gn = Targ_FindNode(arg);
@ -352,7 +352,7 @@ FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
* associated with it.
*/
/*ARGSUSED*/
static Boolean
static bool
FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
{
GNode *gn = Targ_FindNode(arg);
@ -365,10 +365,10 @@ FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
* then we try a floating point conversion instead.
*
* Results:
* Returns TRUE if the conversion succeeded.
* Returns true if the conversion succeeded.
* Sets 'out_value' to the converted number.
*/
static Boolean
static bool
TryParseNumber(const char *str, double *out_value)
{
char *end;
@ -378,29 +378,30 @@ TryParseNumber(const char *str, double *out_value)
errno = 0;
if (str[0] == '\0') { /* XXX: why is an empty string a number? */
*out_value = 0.0;
return TRUE;
return true;
}
ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
if (*end == '\0' && errno != ERANGE) {
*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
return TRUE;
return true;
}
if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
return FALSE; /* skip the expensive strtod call */
return false; /* skip the expensive strtod call */
dbl_val = strtod(str, &end);
if (*end != '\0')
return FALSE;
return false;
*out_value = dbl_val;
return TRUE;
return true;
}
static Boolean
static bool
is_separator(char ch)
{
return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL;
return ch == '\0' || ch_isspace(ch) || ch == '!' || ch == '=' ||
ch == '>' || ch == '<' || ch == ')' /* but not '(' */;
}
/*
@ -409,24 +410,24 @@ is_separator(char ch)
*
* Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX}
*/
static Boolean
static bool
CondParser_StringExpr(CondParser *par, const char *start,
Boolean const doEval, Boolean const quoted,
bool const doEval, bool const quoted,
Buffer *buf, FStr *const inout_str)
{
VarEvalFlags eflags;
VarEvalMode emode;
const char *nested_p;
Boolean atStart;
bool atStart;
VarParseResult parseResult;
/* if we are in quotes, an undefined variable is ok */
eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR
emode = doEval && !quoted ? VARE_UNDEFERR
: doEval ? VARE_WANTRES
: VARE_NONE;
: VARE_PARSE_ONLY;
nested_p = par->p;
atStart = nested_p == start;
parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, eflags, inout_str);
parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
/* TODO: handle errors */
if (inout_str->str == var_Error) {
if (parseResult == VPR_ERR) {
@ -436,11 +437,11 @@ CondParser_StringExpr(CondParser *par, const char *start,
*
* See cond-token-plain.mk $$$$$$$$.
*/
par->printedError = TRUE;
par->printedError = true;
}
/*
* XXX: Can there be any situation in which a returned
* var_Error requires freeIt?
* var_Error needs to be freed?
*/
FStr_Done(inout_str);
/*
@ -448,7 +449,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
* what getting var_Error back with !doEval means.
*/
*inout_str = FStr_InitRefer(NULL);
return FALSE;
return false;
}
par->p = nested_p;
@ -458,30 +459,30 @@ CondParser_StringExpr(CondParser *par, const char *start,
* comparison operator or is the end of the expression, we are done.
*/
if (atStart && is_separator(par->p[0]))
return FALSE;
return false;
Buf_AddStr(buf, inout_str->str);
FStr_Done(inout_str);
*inout_str = FStr_InitRefer(NULL); /* not finished yet */
return TRUE;
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.
* Parse a string from a variable expression or an optionally quoted
* string. This is called for the left-hand and right-hand sides of
* comparisons.
*
* Results:
* Returns the string, absent any quotes, or NULL on error.
* Sets out_quoted if the string was quoted.
* Sets out_freeIt.
* Sets out_quoted if the leaf was a quoted string literal.
*/
static void
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
FStr *out_str, Boolean *out_quoted)
CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
FStr *out_str, bool *out_quoted)
{
Buffer buf;
FStr str;
Boolean quoted;
bool quoted;
const char *start;
Buf_Init(&buf);
@ -541,14 +542,14 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
got_str:
str = FStr_InitOwn(buf.data);
cleanup:
Buf_DoneData(&buf);
Buf_DoneData(&buf); /* XXX: memory leak on failure? */
*out_str = str;
}
static Boolean
If_Eval(const CondParser *par, const char *arg, size_t arglen)
static bool
EvalBare(const CondParser *par, const char *arg, size_t arglen)
{
Boolean res = par->evalBare(arglen, arg);
bool res = par->evalBare(arglen, arg);
return par->negateEvalBare ? !res : res;
}
@ -556,8 +557,8 @@ If_Eval(const CondParser *par, const char *arg, size_t arglen)
* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
* ".if 0".
*/
static Boolean
EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
static bool
EvalNotEmpty(CondParser *par, const char *value, bool quoted)
{
double num;
@ -576,11 +577,11 @@ EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
if (par->plain)
return value[0] != '\0';
return If_Eval(par, value, strlen(value));
return EvalBare(par, value, strlen(value));
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
static Boolean
static bool
EvalCompareNum(double lhs, ComparisonOp op, double rhs)
{
DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]);
@ -608,7 +609,7 @@ EvalCompareStr(CondParser *par, const char *lhs,
if (op != EQ && op != NE) {
Parse_Error(PARSE_FATAL,
"String comparison operator must be either == or !=");
par->printedError = TRUE;
par->printedError = true;
return TOK_ERROR;
}
@ -619,8 +620,8 @@ EvalCompareStr(CondParser *par, const char *lhs,
/* Evaluate a comparison, such as "${VAR} == 12345". */
static Token
EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
ComparisonOp op, const char *rhs, Boolean rhsQuoted)
EvalCompare(CondParser *par, const char *lhs, bool lhsQuoted,
ComparisonOp op, const char *rhs, bool rhsQuoted)
{
double left, right;
@ -631,7 +632,7 @@ EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted,
return EvalCompareStr(par, lhs, op, rhs);
}
static Boolean
static bool
CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
{
const char *p = par->p;
@ -655,14 +656,14 @@ CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
*out_op = NE;
goto length_2;
}
return FALSE;
return false;
length_2:
par->p = p + 2;
return TRUE;
return true;
length_1:
par->p = p + 1;
return TRUE;
return true;
}
/*
@ -674,18 +675,18 @@ CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
* ${VAR:U0} < 12345
*/
static Token
CondParser_Comparison(CondParser *par, Boolean doEval)
CondParser_Comparison(CondParser *par, bool doEval)
{
Token t = TOK_ERROR;
FStr lhs, rhs;
ComparisonOp op;
Boolean lhsQuoted, rhsQuoted;
bool lhsQuoted, rhsQuoted;
/*
* Parse the variable spec and skip over it, saving its
* value in lhs.
*/
CondParser_String(par, doEval, lhsStrict, &lhs, &lhsQuoted);
CondParser_Leaf(par, doEval, lhsStrict, &lhs, &lhsQuoted);
if (lhs.str == NULL)
goto done_lhs;
@ -702,11 +703,11 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
if (par->p[0] == '\0') {
Parse_Error(PARSE_FATAL,
"Missing right-hand-side of operator '%s'", opname[op]);
par->printedError = TRUE;
par->printedError = true;
goto done_lhs;
}
CondParser_String(par, doEval, FALSE, &rhs, &rhsQuoted);
CondParser_Leaf(par, doEval, false, &rhs, &rhsQuoted);
if (rhs.str == NULL)
goto done_rhs;
@ -731,7 +732,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
/*ARGSUSED*/
static size_t
ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
Boolean doEval, const char *func MAKE_ATTR_UNUSED,
bool doEval, const char *func MAKE_ATTR_UNUSED,
char **out_arg)
{
FStr val;
@ -741,8 +742,8 @@ ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
*out_arg = NULL;
(*pp)--; /* Make (*pp)[1] point to the '('. */
(void)Var_Parse(pp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
&val);
(void)Var_Parse(pp, SCOPE_CMDLINE,
doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
/* TODO: handle errors */
/* If successful, *pp points beyond the closing ')' now. */
@ -767,22 +768,23 @@ ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
}
/*ARGSUSED*/
static Boolean
static bool
FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
{
/* Magic values ahead, see ParseEmptyArg. */
return arglen == 1;
}
static Boolean
CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
/* Parse a function call expression, such as 'defined(${file})'. */
static bool
CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
{
static const struct fn_def {
const char *fn_name;
size_t fn_name_len;
size_t (*fn_parse)(CondParser *, const char **, Boolean,
size_t (*fn_parse)(CondParser *, const char **, bool,
const char *, char **);
Boolean (*fn_eval)(size_t, const char *);
bool (*fn_eval)(size_t, const char *);
} fns[] = {
{ "defined", 7, ParseFuncArg, FuncDefined },
{ "make", 4, ParseFuncArg, FuncMake },
@ -810,25 +812,25 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
if (arglen == 0 || arglen == (size_t)-1) {
par->p = cp;
*out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
return TRUE;
return true;
}
/* Evaluate the argument using the required function. */
*out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
free(arg);
par->p = cp;
return TRUE;
return true;
}
return FALSE;
return false;
}
/*
* Parse a function call, a number, a variable expression or a string
* literal.
* Parse a comparison such as '${VAR} == "value"', or a simple leaf without
* operator, which is a number, a variable expression or a string literal.
*/
static Token
CondParser_LeafToken(CondParser *par, Boolean doEval)
CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
{
Token t;
char *arg = NULL;
@ -836,9 +838,6 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
const char *cp;
const char *cp1;
if (CondParser_Func(par, doEval, &t))
return t;
/* Push anything numeric through the compare expression */
cp = par->p;
if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
@ -852,10 +851,14 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
/*
* XXX: Is it possible to have a variable expression evaluated twice
* at this point?
*/
arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
cp1 = cp;
cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!')
if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
return CondParser_Comparison(par, doEval);
par->p = cp;
@ -865,14 +868,14 @@ 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, arg, arglen));
t = ToToken(!doEval || EvalBare(par, arg, arglen));
free(arg);
return t;
}
/* Return the next token or comparison result from the parser. */
static Token
CondParser_Token(CondParser *par, Boolean doEval)
CondParser_Token(CondParser *par, bool doEval)
{
Token t;
@ -900,7 +903,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
par->p++;
else if (opts.strict) {
Parse_Error(PARSE_FATAL, "Unknown operator '|'");
par->printedError = TRUE;
par->printedError = true;
return TOK_ERROR;
}
return TOK_OR;
@ -911,7 +914,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
par->p++;
else if (opts.strict) {
Parse_Error(PARSE_FATAL, "Unknown operator '&'");
par->printedError = TRUE;
par->printedError = true;
return TOK_ERROR;
}
return TOK_AND;
@ -931,7 +934,9 @@ CondParser_Token(CondParser *par, Boolean doEval)
return CondParser_Comparison(par, doEval);
default:
return CondParser_LeafToken(par, doEval);
if (CondParser_FuncCall(par, doEval, &t))
return t;
return CondParser_ComparisonOrLeaf(par, doEval);
}
}
@ -942,7 +947,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
* Term -> Leaf
*/
static CondResult
CondParser_Term(CondParser *par, Boolean doEval)
CondParser_Term(CondParser *par, bool doEval)
{
CondResult res;
Token t;
@ -979,7 +984,7 @@ CondParser_Term(CondParser *par, Boolean doEval)
* And -> Term
*/
static CondResult
CondParser_And(CondParser *par, Boolean doEval)
CondParser_And(CondParser *par, bool doEval)
{
CondResult res;
Token op;
@ -992,7 +997,7 @@ CondParser_And(CondParser *par, Boolean doEval)
if (op == TOK_AND) {
if (res == CR_TRUE)
return CondParser_And(par, doEval);
if (CondParser_And(par, FALSE) == CR_ERROR)
if (CondParser_And(par, false) == CR_ERROR)
return CR_ERROR;
return res;
}
@ -1006,7 +1011,7 @@ CondParser_And(CondParser *par, Boolean doEval)
* Or -> And
*/
static CondResult
CondParser_Or(CondParser *par, Boolean doEval)
CondParser_Or(CondParser *par, bool doEval)
{
CondResult res;
Token op;
@ -1019,7 +1024,7 @@ CondParser_Or(CondParser *par, Boolean doEval)
if (op == TOK_OR) {
if (res == CR_FALSE)
return CondParser_Or(par, doEval);
if (CondParser_Or(par, FALSE) == CR_ERROR)
if (CondParser_Or(par, false) == CR_ERROR)
return CR_ERROR;
return res;
}
@ -1029,17 +1034,17 @@ CondParser_Or(CondParser *par, Boolean doEval)
}
static CondEvalResult
CondParser_Eval(CondParser *par, Boolean *out_value)
CondParser_Eval(CondParser *par, bool *out_value)
{
CondResult res;
DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
res = CondParser_Or(par, TRUE);
res = CondParser_Or(par, true);
if (res == CR_ERROR)
return COND_INVALID;
if (CondParser_Token(par, FALSE) != TOK_EOF)
if (CondParser_Token(par, false) != TOK_EOF)
return COND_INVALID;
*out_value = res == CR_TRUE;
@ -1058,9 +1063,9 @@ CondParser_Eval(CondParser *par, Boolean *out_value)
* (*value) is set to the boolean value of the condition
*/
static CondEvalResult
CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
Boolean (*evalBare)(size_t, const char *), Boolean negate,
Boolean eprint, Boolean strictLHS)
CondEvalExpression(const char *cond, bool *out_value, bool plain,
bool (*evalBare)(size_t, const char *), bool negate,
bool eprint, bool strictLHS)
{
CondParser par;
CondEvalResult rval;
@ -1074,7 +1079,7 @@ CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
par.negateEvalBare = negate;
par.p = cond;
par.curr = TOK_NONE;
par.printedError = FALSE;
par.printedError = false;
rval = CondParser_Eval(&par, out_value);
@ -1089,33 +1094,33 @@ CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain,
* ${"${VAR}" == value:?yes:no}.
*/
CondEvalResult
Cond_EvalCondition(const char *cond, Boolean *out_value)
Cond_EvalCondition(const char *cond, bool *out_value)
{
return CondEvalExpression(cond, out_value, TRUE,
FuncDefined, FALSE, FALSE, FALSE);
return CondEvalExpression(cond, out_value, true,
FuncDefined, false, false, false);
}
static Boolean
static bool
IsEndif(const char *p)
{
return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
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)
static bool
DetermineKindOfConditional(const char **pp, bool *out_plain,
bool (**out_evalBare)(size_t, const char *),
bool *out_negate)
{
const char *p = *pp;
p += 2;
*out_plain = FALSE;
*out_plain = false;
*out_evalBare = FuncDefined;
*out_negate = FALSE;
*out_negate = false;
if (*p == 'n') {
p++;
*out_negate = TRUE;
*out_negate = true;
}
if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
p += 3;
@ -1123,7 +1128,7 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
p += 4;
*out_evalBare = FuncMake;
} else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
*out_plain = TRUE;
*out_plain = true;
} else {
/*
* TODO: Add error message about unknown directive,
@ -1132,11 +1137,11 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
*
* Example: .elifx 123
*/
return FALSE;
return false;
}
*pp = p;
return TRUE;
return true;
}
/*
@ -1161,9 +1166,9 @@ DetermineKindOfConditional(const char **pp, Boolean *out_plain,
*
* Results:
* COND_PARSE to continue parsing the lines that follow the
* conditional (when <cond> evaluates to TRUE)
* conditional (when <cond> evaluates to true)
* COND_SKIP to skip the lines after the conditional
* (when <cond> evaluates to FALSE, or when a previous
* (when <cond> evaluates to false, or when a previous
* branch has already been taken)
* COND_INVALID if the conditional was not valid, either because of
* a syntax error or because some variable was undefined
@ -1174,17 +1179,17 @@ Cond_EvalLine(const char *line)
{
typedef enum IfState {
/* None of the previous <cond> evaluated to TRUE. */
/* None of the previous <cond> evaluated to true. */
IFS_INITIAL = 0,
/* The previous <cond> evaluated to TRUE.
/* The previous <cond> evaluated to true.
* The lines following this condition are interpreted. */
IFS_ACTIVE = 1 << 0,
/* The previous directive was an '.else'. */
IFS_SEEN_ELSE = 1 << 1,
/* One of the previous <cond> evaluated to TRUE. */
/* One of the previous <cond> evaluated to true. */
IFS_WAS_ACTIVE = 1 << 2
} IfState;
@ -1192,11 +1197,11 @@ Cond_EvalLine(const char *line)
static enum IfState *cond_states = NULL;
static unsigned int cond_states_cap = 128;
Boolean plain;
Boolean (*evalBare)(size_t, const char *);
Boolean negate;
Boolean isElif;
Boolean value;
bool plain;
bool (*evalBare)(size_t, const char *);
bool negate;
bool isElif;
bool value;
IfState state;
const char *p = line;
@ -1265,9 +1270,9 @@ Cond_EvalLine(const char *line)
return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
}
/* Assume for now it is an elif */
isElif = TRUE;
isElif = true;
} else
isElif = FALSE;
isElif = false;
if (p[0] != 'i' || p[1] != 'f') {
/*
@ -1323,7 +1328,7 @@ Cond_EvalLine(const char *line)
/* And evaluate the conditional expression */
if (CondEvalExpression(p, &value, plain, evalBare, negate,
TRUE, TRUE) == COND_INVALID) {
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. */

62
dir.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $ */
/* $NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -86,7 +86,7 @@
* Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
*
* Dir_HasWildcards
* Returns TRUE if the name given it needs to
* Returns true if the name given it needs to
* be wildcard-expanded.
*
* SearchPath_Expand
@ -138,7 +138,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $");
MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@ -217,7 +217,7 @@ struct CachedDir {
* and "./." are different.
*
* Not sure what happens when .CURDIR is assigned a new value; see
* Parse_DoVar.
* Parse_Var.
*/
char *name;
@ -547,14 +547,14 @@ void
Dir_SetPATH(void)
{
CachedDirListNode *ln;
Boolean seenDotLast = FALSE; /* true if we should search '.' last */
bool seenDotLast = false; /* true if we should search '.' last */
Global_Delete(".PATH");
if ((ln = dirSearchPath.dirs.first) != NULL) {
CachedDir *dir = ln->datum;
if (dir == dotLast) {
seenDotLast = TRUE;
seenDotLast = true;
Global_Append(".PATH", dotLast->name);
}
}
@ -591,34 +591,34 @@ Dir_SetPATH(void)
* that make(1) should be expanding patterns, because then you have to set a
* mechanism for escaping the expansion!
*
* Return TRUE if the word should be expanded, FALSE otherwise.
* Return true if the word should be expanded, false otherwise.
*/
Boolean
bool
Dir_HasWildcards(const char *name)
{
const char *p;
Boolean wild = FALSE;
bool wild = false;
int braces = 0, brackets = 0;
for (p = name; *p != '\0'; p++) {
switch (*p) {
case '{':
braces++;
wild = TRUE;
wild = true;
break;
case '}':
braces--;
break;
case '[':
brackets++;
wild = TRUE;
wild = true;
break;
case ']':
brackets--;
break;
case '?':
case '*':
wild = TRUE;
wild = true;
break;
default:
break;
@ -647,7 +647,7 @@ static void
DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
{
const char *dirName = dir->name;
Boolean isDot = dirName[0] == '.' && dirName[1] == '\0';
bool isDot = dirName[0] == '.' && dirName[1] == '\0';
HashIter hi;
/*
@ -725,7 +725,7 @@ separator_comma(const char *p)
return p;
}
static Boolean
static bool
contains_wildcard(const char *p)
{
for (; *p != '\0'; p++) {
@ -734,10 +734,10 @@ contains_wildcard(const char *p)
case '?':
case '{':
case '[':
return TRUE;
return true;
}
}
return FALSE;
return false;
}
static char *
@ -1064,19 +1064,19 @@ DirFindDot(const char *name, const char *base)
return NULL;
}
static Boolean
FindFileRelative(SearchPath *path, Boolean seenDotLast,
static bool
FindFileRelative(SearchPath *path, bool seenDotLast,
const char *name, char **out_file)
{
SearchPathNode *ln;
Boolean checkedDot = FALSE;
bool checkedDot = false;
char *file;
DEBUG0(DIR, " Trying subdirectories...\n");
if (!seenDotLast) {
if (dot != NULL) {
checkedDot = TRUE;
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
@ -1092,7 +1092,7 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
if (dir == dot) {
if (checkedDot)
continue;
checkedDot = TRUE;
checkedDot = true;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
goto found;
@ -1100,7 +1100,7 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = TRUE;
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
}
@ -1119,15 +1119,15 @@ FindFileRelative(SearchPath *path, Boolean seenDotLast,
goto found;
}
return FALSE;
return false;
found:
*out_file = file;
return TRUE;
return true;
}
static Boolean
FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
static bool
FindFileAbsolute(SearchPath *path, bool const seenDotLast,
const char *const name, const char *const base,
char **out_file)
{
@ -1162,7 +1162,7 @@ FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
((file = DirLookupAbs(cur, name, base)) != NULL))
goto found;
return FALSE;
return false;
found:
if (file[0] == '\0') {
@ -1170,7 +1170,7 @@ FindFileAbsolute(SearchPath *path, Boolean const seenDotLast,
file = NULL;
}
*out_file = file;
return TRUE;
return true;
}
/*
@ -1194,7 +1194,7 @@ char *
Dir_FindFile(const char *name, SearchPath *path)
{
char *file; /* the current filename to check */
Boolean seenDotLast = FALSE; /* true if we should search dot last */
bool seenDotLast = false; /* true if we should search dot last */
struct cached_stat cst; /* Buffer for stat, if necessary */
const char *trailing_dot = ".";
const char *base = str_basename(name);
@ -1210,7 +1210,7 @@ Dir_FindFile(const char *name, SearchPath *path)
if (path->dirs.first != NULL) {
CachedDir *dir = path->dirs.first->datum;
if (dir == dotLast) {
seenDotLast = TRUE;
seenDotLast = true;
DEBUG0(DIR, "[dot last]...");
}
}
@ -1471,7 +1471,7 @@ ResolveFullName(GNode *gn)
* The found file is stored in gn->path, unless the node already had a path.
*/
void
Dir_UpdateMTime(GNode *gn, Boolean recheck)
Dir_UpdateMTime(GNode *gn, bool recheck)
{
char *fullName;
struct cached_stat cst;

6
dir.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.h,v 1.43 2021/02/05 05:48:19 rillig Exp $ */
/* $NetBSD: dir.h,v 1.44 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -82,11 +82,11 @@ void Dir_InitCur(const char *);
void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
Boolean Dir_HasWildcards(const char *);
bool Dir_HasWildcards(const char *);
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);
void Dir_UpdateMTime(GNode *, bool);
CachedDir *SearchPath_Add(SearchPath *, const char *);
char *SearchPath_ToFlags(SearchPath *, const char *);
void SearchPath_Clear(SearchPath *);

62
enum.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.h,v 1.18 2021/02/02 21:26:51 rillig Exp $ */
/* $NetBSD: enum.h,v 1.19 2021/03/15 16:00:05 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -112,19 +112,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
} \
extern void enum_flags_rtti_dummy(void)
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 2 flags.
*/
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_1(v1), \
ENUM__SPEC_1(v2)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_1(v1), \
ENUM__JOIN_STR_1(v2)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 3 flags.
@ -138,19 +125,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_1(v3)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 4 flags.
*/
#define ENUM_FLAGS_RTTI_4(typnam, v1, v2, v3, v4) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_2(v1, v2), \
ENUM__SPEC_2(v3, v4)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_2(v3, v4)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 6 flags.
@ -164,19 +138,6 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_2(v5, v6)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 8 flags.
*/
#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_4(v1, v2, v3, v4), \
ENUM__SPEC_4(v5, v6, v7, v8)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_4(v5, v6, v7, v8)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 9 flags.
@ -215,25 +176,4 @@ const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
ENUM__JOIN_STR_2(v29, v30), \
ENUM__JOIN_STR_1(v31)))
/*
* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 32 flags.
*/
#define ENUM_FLAGS_RTTI_32(typnam, \
v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16, \
v17, v18, v19, v20, v21, v22, v23, v24, \
v25, v26, v27, v28, v29, v30, v31, v32) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16), \
ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \
v25, v26, v27, v28, v29, v30, v31, v32)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16), \
ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \
v25, v26, v27, v28, v29, v30, v31, v32)))
#endif

46
for.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $ */
/* $NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $");
MAKE_RCSID("$NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $");
/* One of the variables to the left of the "in" in a .for loop. */
@ -75,7 +75,7 @@ typedef struct ForLoop {
/* Is any of the names 1 character long? If so, when the variable values
* are substituted, the parser must handle $V expressions as well, not
* only ${V} and $(V). */
Boolean short_var;
bool short_var;
unsigned int sub_next; /* Where to continue iterating */
} ForLoop;
@ -94,7 +94,7 @@ ForLoop_New(void)
f->items.words = NULL;
f->items.freeIt = NULL;
Buf_Init(&f->curBody);
f->short_var = FALSE;
f->short_var = false;
f->sub_next = 0;
return f;
@ -125,7 +125,7 @@ ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
var->nameLen = len;
}
static Boolean
static bool
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
@ -136,7 +136,7 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
return FALSE;
return false;
}
/*
@ -151,7 +151,7 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
break;
}
if (len == 1)
f->short_var = TRUE;
f->short_var = true;
ForLoop_AddVar(f, p, len);
p += len;
@ -159,14 +159,14 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
return FALSE;
return false;
}
*pp = p;
return TRUE;
return true;
}
static Boolean
static bool
ForLoop_ParseItems(ForLoop *f, const char *p)
{
char *items;
@ -175,10 +175,10 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
Parse_Error(PARSE_FATAL, "Error in .for loop items");
return FALSE;
return false;
}
f->items = Str_Words(items, FALSE);
f->items = Str_Words(items, false);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
@ -189,19 +189,19 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
(unsigned)f->items.len, (unsigned)f->vars.len);
return FALSE;
return false;
}
return TRUE;
return true;
}
static Boolean
static bool
IsFor(const char *p)
{
return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
}
static Boolean
static bool
IsEndfor(const char *p)
{
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
@ -257,9 +257,9 @@ For_Eval(const char *line)
/*
* Add another line to the .for loop that is being built up.
* Returns FALSE when the matching .endfor is reached.
* Returns false when the matching .endfor is reached.
*/
Boolean
bool
For_Accum(const char *line)
{
const char *p = line;
@ -271,7 +271,7 @@ For_Accum(const char *line)
if (IsEndfor(p)) {
DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0)
return FALSE;
return false;
} else if (IsFor(p)) {
forLevel++;
DEBUG1(FOR, "For: new loop %d\n", forLevel);
@ -280,7 +280,7 @@ For_Accum(const char *line)
Buf_AddStr(&accumFor->body, line);
Buf_AddByte(&accumFor->body, '\n');
return TRUE;
return true;
}
@ -319,16 +319,16 @@ for_var_len(const char *var)
* The .for loop substitutes the items as ${:U<value>...}, which means
* that characters that break this syntax must be backslash-escaped.
*/
static Boolean
static bool
NeedsEscapes(const char *value, char endc)
{
const char *p;
for (p = value; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
return TRUE;
return true;
}
return FALSE;
return false;
}
/*

68
hash.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $ */
/* $NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 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.61 2021/02/01 17:32:10 rillig Exp $");
MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@ -84,7 +84,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $");
/* This hash function matches Gosling's Emacs and java.lang.String. */
static unsigned int
hash(const char *key, size_t *out_keylen)
Hash_String(const char *key, size_t *out_keylen)
{
unsigned int h;
const char *p;
@ -98,10 +98,17 @@ hash(const char *key, size_t *out_keylen)
return h;
}
/* This hash function matches Gosling's Emacs and java.lang.String. */
unsigned int
Hash_Hash(const char *key)
Hash_Substring(Substring key)
{
return hash(key, NULL);
unsigned int h;
const char *p;
h = 0;
for (p = key.start; p != key.end; p++)
h = 31 * h + (unsigned char)*p;
return h;
}
static HashEntry *
@ -126,6 +133,41 @@ HashTable_Find(HashTable *t, unsigned int h, const char *key)
return e;
}
static bool
HashEntry_KeyEquals(const HashEntry *he, Substring key)
{
const char *heKey, *p;
heKey = he->key;
for (p = key.start; p != key.end; p++, heKey++)
if (*p != *heKey || *heKey == '\0')
return false;
return *heKey == '\0';
}
static HashEntry *
HashTable_FindEntryBySubstring(HashTable *t, Substring key, unsigned int h)
{
HashEntry *e;
unsigned int chainlen = 0;
#ifdef DEBUG_HASH_LOOKUP
DEBUG4(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h,
(int)Substring_Length(key), key.start);
#endif
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
chainlen++;
if (e->key_hash == h && HashEntry_KeyEquals(e, key))
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
return e;
}
/* Set up the hash table. */
void
HashTable_Init(HashTable *t)
@ -171,7 +213,7 @@ HashTable_Done(HashTable *t)
HashEntry *
HashTable_FindEntry(HashTable *t, const char *key)
{
unsigned int h = hash(key, NULL);
unsigned int h = Hash_String(key, NULL);
return HashTable_Find(t, h, key);
}
@ -188,9 +230,9 @@ HashTable_FindValue(HashTable *t, const char *key)
* or return NULL.
*/
void *
HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
{
HashEntry *he = HashTable_Find(t, h, key);
HashEntry *he = HashTable_FindEntryBySubstring(t, key, h);
return he != NULL ? he->value : NULL;
}
@ -227,7 +269,7 @@ HashTable_Enlarge(HashTable *t)
t->bucketsMask = newMask;
t->buckets = newBuckets;
DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
__func__, t, t->bucketsSize, t->numEntries, t->maxchain);
__func__, (void *)t, t->bucketsSize, t->numEntries, t->maxchain);
t->maxchain = 0;
}
@ -236,15 +278,15 @@ HashTable_Enlarge(HashTable *t)
* Return in out_isNew whether a new entry has been created.
*/
HashEntry *
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
{
size_t keylen;
unsigned int h = hash(key, &keylen);
unsigned int h = Hash_String(key, &keylen);
HashEntry *he = HashTable_Find(t, h, key);
if (he != NULL) {
if (out_isNew != NULL)
*out_isNew = FALSE;
*out_isNew = false;
return he;
}
@ -261,7 +303,7 @@ HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
t->numEntries++;
if (out_isNew != NULL)
*out_isNew = TRUE;
*out_isNew = true;
return he;
}

14
hash.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.h,v 1.38 2020/12/15 01:23:55 rillig Exp $ */
/* $NetBSD: hash.h,v 1.40 2021/04/11 12:46:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -124,9 +124,9 @@ void HashTable_Init(HashTable *);
void HashTable_Done(HashTable *);
HashEntry *HashTable_FindEntry(HashTable *, const char *);
void *HashTable_FindValue(HashTable *, const char *);
unsigned int Hash_Hash(const char *);
void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
unsigned int Hash_Substring(Substring);
void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int);
HashEntry *HashTable_CreateEntry(HashTable *, const char *, bool *);
HashEntry *HashTable_Set(HashTable *, const char *, void *);
void HashTable_DeleteEntry(HashTable *, HashEntry *);
void HashTable_DebugStats(HashTable *, const char *);
@ -146,16 +146,16 @@ HashSet_Done(HashSet *set)
HashTable_Done(&set->tbl);
}
MAKE_INLINE Boolean
MAKE_INLINE bool
HashSet_Add(HashSet *set, const char *key)
{
Boolean isNew;
bool isNew;
(void)HashTable_CreateEntry(&set->tbl, key, &isNew);
return isNew;
}
MAKE_INLINE Boolean
MAKE_INLINE bool
HashSet_Contains(HashSet *set, const char *key)
{
return HashTable_FindEntry(&set->tbl, key) != NULL;

View File

@ -68,19 +68,24 @@ VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'`
rm -f *~
mkdir -p ../tmp
# new files are handled automatically
# but we need to rm if needed
# FILES are kept sorted so we can determine what was added and deleted
# but we need to take care dealing with re-ordering
(${GIT} diff FILES | sed -n '/^[+-][^+-]/p'; \
${GIT} diff mk/FILES | sed -n '/^[+-][^+-]/s,.,&mk/,p' ) > $TF.diffs
grep '^+' $TF.diffs | sed 's,^.,,' | sort > $TF.adds
grep '^-' $TF.diffs | sed 's,^.,,' | sort > $TF.rms
comm -13 $TF.adds $TF.rms > $TF.rm
if [ -z "$ECHO" ]; then
# new files are handled automatically
# but we need to rm if needed
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
test -s $TF.rm && xargs rm -f < $TF.rm
$GIT add -A
$GIT diff --staged | tee ../tmp/bmake-import.diff
echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh
echo "After you commit, run $here/../tmp/bmake-post.sh"
else
# FILES is kept sorted so we can determine what was added and deleted
$GIT diff FILES | sed -n '/^+[^+]/s,^+,,p' > $TF.add
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
comm -23 $TF.adds $TF.rms > $TF.add
test -s $TF.rm && { echo Removing:; cat $TF.rm; }
test -s $TF.add && { echo Adding:; cat $TF.add; }
$GIT diff

423
job.c

File diff suppressed because it is too large Load Diff

20
job.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: job.h,v 1.72 2021/02/05 19:19:17 sjg Exp $ */
/* $NetBSD: job.h,v 1.73 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -157,14 +157,14 @@ typedef struct Job {
JobStatus status;
Boolean suspended;
bool suspended;
/* Ignore non-zero exits */
Boolean ignerr;
bool ignerr;
/* Output the command before or instead of running it. */
Boolean echo;
bool echo;
/* Target is a special one. */
Boolean special;
bool special;
int inPipe; /* Pipe for reading output from job */
int outPipe; /* Pipe for writing control commands */
@ -188,22 +188,22 @@ extern int jobTokensRunning; /* tokens currently "out" */
void Shell_Init(void);
const char *Shell_GetNewline(void);
void Job_Touch(GNode *, Boolean);
Boolean Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
void Job_Touch(GNode *, bool);
bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
void Job_CatchChildren(void);
void Job_CatchOutput(void);
void Job_Make(GNode *);
void Job_Init(void);
Boolean Job_ParseShell(char *);
bool Job_ParseShell(char *);
int Job_Finish(void);
void Job_End(void);
void Job_Wait(void);
void Job_AbortAll(void);
void Job_TokenReturn(void);
Boolean Job_TokenWithdraw(void);
bool Job_TokenWithdraw(void);
void Job_ServerStart(int, int, int);
void Job_SetPrefix(void);
Boolean Job_RunTarget(const char *, const char *);
bool Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t);
int Job_TempFile(const char *, char *, size_t);

10
lst.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $ */
/* $NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -34,7 +34,7 @@
#include "make.h"
MAKE_RCSID("$NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $");
MAKE_RCSID("$NetBSD: lst.c,v 1.105 2021/03/15 16:45:30 rillig Exp $");
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@ -205,7 +205,7 @@ Lst_FindDatum(List *list, const void *datum)
/*
* Move all nodes from src to the end of dst.
* The source list becomes empty but is not freed.
* The source list becomes indeterminate.
*/
void
Lst_MoveAll(List *dst, List *src)
@ -219,6 +219,10 @@ Lst_MoveAll(List *dst, List *src)
dst->last = src->last;
}
#ifdef CLEANUP
src->first = NULL;
src->last = NULL;
#endif
}
/* Copy the element data from src to the start of dst. */

12
lst.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.h,v 1.96 2021/02/01 18:55:15 rillig Exp $ */
/* $NetBSD: lst.h,v 1.98 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -123,15 +123,17 @@ void Lst_Free(List *);
MAKE_INLINE void
Lst_Init(List *list)
{
list->first = NULL;
list->last = NULL;
list->first = NULL;
list->last = NULL;
}
/* Get information about a list */
MAKE_INLINE Boolean
MAKE_INLINE bool
Lst_IsEmpty(List *list)
{ return list->first == NULL; }
{
return list->first == NULL;
}
/* Find the first node that contains the given datum, or NULL. */
ListNode *Lst_FindDatum(List *, const void *);

248
main.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $ */
/* $NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -111,7 +111,7 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $");
MAKE_RCSID("$NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
@ -125,20 +125,20 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
CmdOpts opts;
time_t now; /* Time at start of make */
GNode *defaultNode; /* .DEFAULT node */
Boolean allPrecious; /* .PRECIOUS given on line by itself */
Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
bool allPrecious; /* .PRECIOUS given on line by itself */
bool deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */
Boolean enterFlagObj; /* -w and objdir != srcdir */
bool enterFlagObj; /* -w and objdir != srcdir */
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
Boolean doing_depend; /* Set while reading .depend */
static Boolean jobsRunning; /* TRUE if the jobs might be running */
bool doing_depend; /* Set while reading .depend */
static bool jobsRunning; /* true if the jobs might be running */
static const char *tracefile;
static int ReadMakefile(const char *);
static void purge_relative_cached_realpaths(void);
static Boolean ignorePWD; /* if we use -C, PWD is meaningless */
static bool ignorePWD; /* if we use -C, PWD is meaningless */
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
char curdir[MAXPATHLEN + 1]; /* Startup directory */
const char *progname;
@ -146,7 +146,7 @@ char *makeDependfile;
pid_t myPid;
int makelevel;
Boolean forceJobs = FALSE;
bool forceJobs = false;
static int main_errors = 0;
static HashTable cached_realpaths;
@ -293,7 +293,7 @@ MainParseArgDebug(const char *argvalue)
debug |= DEBUG_JOB;
break;
case 'L':
opts.strict = TRUE;
opts.strict = true;
break;
case 'l':
debug |= DEBUG_LOUD;
@ -317,7 +317,7 @@ MainParseArgDebug(const char *argvalue)
debug |= DEBUG_TARG;
break;
case 'V':
opts.debugVflag = TRUE;
opts.debugVflag = true;
break;
case 'v':
debug |= DEBUG_VAR;
@ -350,22 +350,22 @@ MainParseArgDebug(const char *argvalue)
}
/* Is path relative, or does it contain any relative component "." or ".."? */
static Boolean
static bool
IsRelativePath(const char *path)
{
const char *cp;
const char *p;
if (path[0] != '/')
return TRUE;
cp = path;
while ((cp = strstr(cp, "/.")) != NULL) {
cp += 2;
if (*cp == '.')
cp++;
if (cp[0] == '/' || cp[0] == '\0')
return TRUE;
return true;
p = path;
while ((p = strstr(p, "/.")) != NULL) {
p += 2;
if (*p == '.')
p++;
if (*p == '/' || *p == '\0')
return true;
}
return FALSE;
return false;
}
static void
@ -388,7 +388,7 @@ MainParseArgChdir(const char *argvalue)
sa.st_ino == sb.st_ino &&
sa.st_dev == sb.st_dev)
strncpy(curdir, argvalue, MAXPATHLEN);
ignorePWD = TRUE;
ignorePWD = true;
}
static void
@ -411,7 +411,7 @@ MainParseArgJobsInternal(const char *argvalue)
#endif
jp_0 = -1;
jp_1 = -1;
opts.compatMake = TRUE;
opts.compatMake = true;
} else {
Global_Append(MAKEFLAGS, "-J");
Global_Append(MAKEFLAGS, argvalue);
@ -423,7 +423,7 @@ MainParseArgJobs(const char *argvalue)
{
char *p;
forceJobs = TRUE;
forceJobs = true;
opts.maxJobs = (int)strtol(argvalue, &p, 0);
if (*p != '\0' || opts.maxJobs < 1) {
(void)fprintf(stderr,
@ -454,14 +454,14 @@ MainParseArgSysInc(const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
}
static Boolean
static bool
MainParseArg(char c, const char *argvalue)
{
switch (c) {
case '\0':
break;
case 'B':
opts.compatMake = TRUE;
opts.compatMake = true;
Global_Append(MAKEFLAGS, "-B");
Global_Set(MAKE_MODE, "compat");
break;
@ -469,7 +469,7 @@ MainParseArg(char c, const char *argvalue)
MainParseArgChdir(argvalue);
break;
case 'D':
if (argvalue[0] == '\0') return FALSE;
if (argvalue[0] == '\0') return false;
Global_SetExpand(argvalue, "1");
Global_Append(MAKEFLAGS, "-D");
Global_Append(MAKEFLAGS, argvalue);
@ -483,12 +483,12 @@ MainParseArg(char c, const char *argvalue)
MainParseArgJobsInternal(argvalue);
break;
case 'N':
opts.noExecute = TRUE;
opts.noRecursiveExecute = TRUE;
opts.noExecute = true;
opts.noRecursiveExecute = true;
Global_Append(MAKEFLAGS, "-N");
break;
case 'S':
opts.keepgoing = FALSE;
opts.keepgoing = false;
Global_Append(MAKEFLAGS, "-S");
break;
case 'T':
@ -505,11 +505,11 @@ MainParseArg(char c, const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
break;
case 'W':
opts.parseWarnFatal = TRUE;
opts.parseWarnFatal = true;
/* XXX: why no Var_Append? */
break;
case 'X':
opts.varNoExportEnv = TRUE;
opts.varNoExportEnv = true;
Global_Append(MAKEFLAGS, "-X");
break;
case 'd':
@ -523,21 +523,21 @@ MainParseArg(char c, const char *argvalue)
MainParseArgDebug(argvalue);
break;
case 'e':
opts.checkEnvFirst = TRUE;
opts.checkEnvFirst = true;
Global_Append(MAKEFLAGS, "-e");
break;
case 'f':
Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
break;
case 'i':
opts.ignoreErrors = TRUE;
opts.ignoreErrors = true;
Global_Append(MAKEFLAGS, "-i");
break;
case 'j':
MainParseArgJobs(argvalue);
break;
case 'k':
opts.keepgoing = TRUE;
opts.keepgoing = true;
Global_Append(MAKEFLAGS, "-k");
break;
case 'm':
@ -545,35 +545,35 @@ MainParseArg(char c, const char *argvalue)
/* XXX: why no Var_Append? */
break;
case 'n':
opts.noExecute = TRUE;
opts.noExecute = true;
Global_Append(MAKEFLAGS, "-n");
break;
case 'q':
opts.queryFlag = TRUE;
opts.queryFlag = true;
/* Kind of nonsensical, wot? */
Global_Append(MAKEFLAGS, "-q");
break;
case 'r':
opts.noBuiltins = TRUE;
opts.noBuiltins = true;
Global_Append(MAKEFLAGS, "-r");
break;
case 's':
opts.beSilent = TRUE;
opts.beSilent = true;
Global_Append(MAKEFLAGS, "-s");
break;
case 't':
opts.touchFlag = TRUE;
opts.touchFlag = true;
Global_Append(MAKEFLAGS, "-t");
break;
case 'w':
opts.enterFlag = TRUE;
opts.enterFlag = true;
Global_Append(MAKEFLAGS, "-w");
break;
default:
case '?':
usage();
}
return TRUE;
return true;
}
/*
@ -592,13 +592,13 @@ MainParseArgs(int argc, char **argv)
int arginc;
char *argvalue;
char *optscan;
Boolean inOption, dashDash = FALSE;
bool inOption, dashDash = false;
const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w";
/* Can't actually use getopt(3) because rescanning is not portable */
rearg:
inOption = FALSE;
inOption = false;
optscan = NULL;
while (argc > 1) {
const char *optspec;
@ -610,20 +610,20 @@ MainParseArgs(int argc, char **argv)
if (c == '\0') {
argv++;
argc--;
inOption = FALSE;
inOption = false;
continue;
}
} else {
if (c != '-' || dashDash)
break;
inOption = TRUE;
inOption = true;
c = *optscan++;
}
/* '-' found at some earlier point */
optspec = strchr(optspecs, c);
if (c != '\0' && optspec != NULL && optspec[1] == ':') {
/* -<something> found, and <something> should have an arg */
inOption = FALSE;
inOption = false;
arginc = 1;
argvalue = optscan;
if (*argvalue == '\0') {
@ -638,10 +638,10 @@ MainParseArgs(int argc, char **argv)
switch (c) {
case '\0':
arginc = 1;
inOption = FALSE;
inOption = false;
break;
case '-':
dashDash = TRUE;
dashDash = true;
break;
default:
if (!MainParseArg(c, argvalue))
@ -659,7 +659,7 @@ MainParseArgs(int argc, char **argv)
for (; argc > 1; argv++, argc--) {
VarAssign var;
if (Parse_IsVar(argv[1], &var)) {
Parse_DoVar(&var, SCOPE_CMDLINE);
Parse_Var(&var, SCOPE_CMDLINE);
} else {
if (argv[1][0] == '\0')
Punt("illegal (null) argument.");
@ -716,7 +716,7 @@ Main_ParseArgLine(const char *line)
FStr_Done(&argv0);
}
words = Str_Words(buf, TRUE);
words = Str_Words(buf, true);
if (words.words == NULL) {
Error("Unterminated quoted string [%s]", buf);
free(buf);
@ -728,14 +728,14 @@ Main_ParseArgLine(const char *line)
Words_Free(words);
}
Boolean
Main_SetObjdir(Boolean writable, const char *fmt, ...)
bool
Main_SetObjdir(bool writable, const char *fmt, ...)
{
struct stat sb;
char *path;
char buf[MAXPATHLEN + 1];
char buf2[MAXPATHLEN + 1];
Boolean rc = FALSE;
bool rc = false;
va_list ap;
va_start(ap, fmt);
@ -759,24 +759,24 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
setenv("PWD", objdir, 1);
Dir_InitDot();
purge_relative_cached_realpaths();
rc = TRUE;
rc = true;
if (opts.enterFlag && strcmp(objdir, curdir) != 0)
enterFlagObj = TRUE;
enterFlagObj = true;
}
}
return rc;
}
static Boolean
SetVarObjdir(Boolean writable, const char *var, const char *suffix)
static bool
SetVarObjdir(bool writable, const char *var, const char *suffix)
{
FStr path = Var_Value(SCOPE_CMDLINE, var);
FStr xpath;
if (path.str == NULL || path.str[0] == '\0') {
FStr_Done(&path);
return FALSE;
return false;
}
/* expand variable substitutions */
@ -792,7 +792,7 @@ SetVarObjdir(Boolean writable, const char *var, const char *suffix)
FStr_Done(&xpath);
FStr_Done(&path);
return TRUE;
return true;
}
/*
@ -841,8 +841,8 @@ MakeMode(void)
if (mode[0] != '\0') {
if (strstr(mode, "compat") != NULL) {
opts.compatMake = TRUE;
forceJobs = FALSE;
opts.compatMake = true;
forceJobs = false;
}
#if USE_META
if (strstr(mode, "meta") != NULL)
@ -854,7 +854,7 @@ MakeMode(void)
}
static void
PrintVar(const char *varname, Boolean expandVars)
PrintVar(const char *varname, bool expandVars)
{
if (strchr(varname, '$') != NULL) {
char *evalue;
@ -880,24 +880,22 @@ PrintVar(const char *varname, Boolean expandVars)
}
/*
* Return a Boolean based on a variable.
* Return a bool based on a variable.
*
* If the knob is not set, return the fallback.
* If set, anything that looks or smells like "No", "False", "Off", "0", etc.
* is FALSE, otherwise TRUE.
* is false, otherwise true.
*/
Boolean
GetBooleanVar(const char *varname, Boolean fallback)
bool
GetBooleanExpr(const char *expr, bool fallback)
{
char *expr = str_concat3("${", varname, ":U}");
char *value;
Boolean res;
bool res;
(void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value);
/* TODO: handle errors */
res = ParseBoolean(value, fallback);
free(value);
free(expr);
return res;
}
@ -905,14 +903,15 @@ static void
doPrintVars(void)
{
StringListNode *ln;
Boolean expandVars;
bool expandVars;
if (opts.printVars == PVM_EXPANDED)
expandVars = TRUE;
expandVars = true;
else if (opts.debugVflag)
expandVars = FALSE;
expandVars = false;
else
expandVars = GetBooleanVar(".MAKE.EXPAND_VARIABLES", FALSE);
expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}",
false);
for (ln = opts.variables.first; ln != NULL; ln = ln->next) {
const char *varname = ln->datum;
@ -920,11 +919,11 @@ doPrintVars(void)
}
}
static Boolean
static bool
runTargets(void)
{
GNodeList targs = LST_INIT; /* target nodes to create */
Boolean outOfDate; /* FALSE if all targets up to date */
bool outOfDate; /* false if all targets up to date */
/*
* Have now read the entire graph and need to make a list of
@ -947,7 +946,7 @@ runTargets(void)
*/
if (!opts.queryFlag) {
Job_Init();
jobsRunning = TRUE;
jobsRunning = true;
}
/* Traverse the graph, checking on all the targets */
@ -958,7 +957,7 @@ runTargets(void)
* targets as well as initializing the module.
*/
Compat_Run(&targs);
outOfDate = FALSE;
outOfDate = false;
}
Lst_Done(&targs); /* Don't free the targets themselves. */
return outOfDate;
@ -1110,11 +1109,11 @@ HandlePWD(const struct stat *curdir_st)
static void
InitObjdir(const char *machine, const char *machine_arch)
{
Boolean writable;
bool writable;
Dir_InitCur(curdir);
writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE);
(void)Main_SetObjdir(FALSE, "%s", curdir);
writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true);
(void)Main_SetObjdir(false, "%s", curdir);
if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) &&
!SetVarObjdir(writable, "MAKEOBJDIR", "") &&
@ -1141,27 +1140,27 @@ UnlimitFiles(void)
static void
CmdOpts_Init(void)
{
opts.compatMake = FALSE;
opts.compatMake = false;
opts.debug = DEBUG_NONE;
/* opts.debug_file has already been initialized earlier */
opts.strict = FALSE;
opts.debugVflag = FALSE;
opts.checkEnvFirst = FALSE;
opts.strict = false;
opts.debugVflag = false;
opts.checkEnvFirst = false;
Lst_Init(&opts.makefiles);
opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */
opts.ignoreErrors = false; /* Pay attention to non-zero returns */
opts.maxJobs = 1;
opts.keepgoing = FALSE; /* Stop on error */
opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
opts.noExecute = FALSE; /* Execute all commands */
opts.queryFlag = FALSE;
opts.noBuiltins = FALSE; /* Read the built-in rules */
opts.beSilent = FALSE; /* Print commands as executed */
opts.touchFlag = FALSE;
opts.keepgoing = false; /* Stop on error */
opts.noRecursiveExecute = false; /* Execute all .MAKE targets */
opts.noExecute = false; /* Execute all commands */
opts.queryFlag = false;
opts.noBuiltins = false; /* Read the built-in rules */
opts.beSilent = false; /* Print commands as executed */
opts.touchFlag = false;
opts.printVars = PVM_NONE;
Lst_Init(&opts.variables);
opts.parseWarnFatal = FALSE;
opts.enterFlag = FALSE;
opts.varNoExportEnv = FALSE;
opts.parseWarnFatal = false;
opts.enterFlag = false;
opts.varNoExportEnv = false;
Lst_Init(&opts.create);
}
@ -1286,7 +1285,7 @@ InitMaxJobs(void)
opts.maxJobs = n;
maxJobTokens = opts.maxJobs;
forceJobs = TRUE;
forceJobs = true;
free(value);
}
@ -1427,12 +1426,12 @@ main_Init(int argc, char **argv)
Global_Set(MAKE_DEPENDFILE, ".depend");
CmdOpts_Init();
allPrecious = FALSE; /* Remove targets when interrupted */
deleteOnError = FALSE; /* Historical default behavior */
jobsRunning = FALSE;
allPrecious = false; /* Remove targets when interrupted */
deleteOnError = false; /* Historical default behavior */
jobsRunning = false;
maxJobTokens = opts.maxJobs;
ignorePWD = FALSE;
ignorePWD = false;
/*
* Initialize the parsing, directory and variable modules to prepare
@ -1575,9 +1574,9 @@ main_PrepareMaking(void)
SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile);
if (makeDependfile[0] != '\0') {
/* TODO: handle errors */
doing_depend = TRUE;
doing_depend = true;
(void)ReadMakefile(makeDependfile);
doing_depend = FALSE;
doing_depend = false;
}
}
@ -1599,7 +1598,7 @@ main_PrepareMaking(void)
* turn compatibility on.
*/
if (!opts.compatMake && !forceJobs)
opts.compatMake = TRUE;
opts.compatMake = true;
if (!opts.compatMake)
Job_ServerStart(maxJobTokens, jp_0, jp_1);
@ -1607,7 +1606,7 @@ main_PrepareMaking(void)
jp_0, jp_1, opts.maxJobs, maxJobTokens, opts.compatMake ? 1 : 0);
if (opts.printVars == PVM_NONE)
Main_ExportMAKEFLAGS(TRUE); /* initial export */
Main_ExportMAKEFLAGS(true); /* initial export */
InitVpath();
@ -1615,7 +1614,7 @@ main_PrepareMaking(void)
* Now that all search paths have been read for suffixes et al, it's
* time to add the default search path to their lists...
*/
Suff_DoPaths();
Suff_ExtendPaths();
/*
* Propagate attributes through :: dependency lists.
@ -1632,13 +1631,13 @@ main_PrepareMaking(void)
* If the -v or -V options are given, print variables instead.
* Return whether any of the targets is out-of-date.
*/
static Boolean
static bool
main_Run(void)
{
if (opts.printVars != PVM_NONE) {
/* print the values of any variables requested by the user */
doPrintVars();
return FALSE;
return false;
} else {
return runTargets();
}
@ -1684,7 +1683,7 @@ main_CleanUp(void)
/* Determine the exit code. */
static int
main_Exit(Boolean outOfDate)
main_Exit(bool outOfDate)
{
if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0))
return 2; /* Not 1 so -q can distinguish error */
@ -1694,7 +1693,7 @@ main_Exit(Boolean outOfDate)
int
main(int argc, char **argv)
{
Boolean outOfDate;
bool outOfDate;
main_Init(argc, argv);
main_ReadFiles();
@ -1862,7 +1861,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
/* Wait for the process to exit. */
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
JobReapChild(pid, status, FALSE);
JobReapChild(pid, status, false);
res_len = buf.len;
res = Buf_DoneData(&buf);
@ -2107,13 +2106,14 @@ cached_realpath(const char *pathname, char *resolved)
* Return true if we should die without noise.
* For example our failing child was a sub-make or failure happened elsewhere.
*/
Boolean
bool
shouldDieQuietly(GNode *gn, int bf)
{
static int quietly = -1;
if (quietly < 0) {
if (DEBUG(JOB) || !GetBooleanVar(".MAKE.DIE_QUIETLY", TRUE))
if (DEBUG(JOB) ||
!GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true))
quietly = 0;
else if (bf >= 0)
quietly = bf;
@ -2193,15 +2193,15 @@ PrintOnError(GNode *gn, const char *msg)
}
void
Main_ExportMAKEFLAGS(Boolean first)
Main_ExportMAKEFLAGS(bool first)
{
static Boolean once = TRUE;
static bool once = true;
const char *expr;
char *s;
if (once != first)
return;
once = FALSE;
once = false;
expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}";
(void)Var_Subst(expr, SCOPE_CMDLINE, VARE_WANTRES, &s);
@ -2225,7 +2225,7 @@ getTmpdir(void)
return tmpdir;
/* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */
(void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/",
(void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/",
SCOPE_GLOBAL, VARE_WANTRES, &tmpdir);
/* TODO: handle errors */
@ -2272,18 +2272,18 @@ mkTempFile(const char *pattern, char *tfile, size_t tfile_sz)
/*
* Convert a string representation of a boolean into a boolean value.
* Anything that looks like "No", "False", "Off", "0" etc. is FALSE,
* the empty string is the fallback, everything else is TRUE.
* Anything that looks like "No", "False", "Off", "0" etc. is false,
* the empty string is the fallback, everything else is true.
*/
Boolean
ParseBoolean(const char *s, Boolean fallback)
bool
ParseBoolean(const char *s, bool fallback)
{
char ch = ch_tolower(s[0]);
if (ch == '\0')
return fallback;
if (ch == '0' || ch == 'f' || ch == 'n')
return FALSE;
return false;
if (ch == 'o')
return ch_tolower(s[1]) != 'f';
return TRUE;
return true;
}

91
make.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -72,7 +72,7 @@
* Examination of targets and their suitability for creation.
*
* Interface:
* Make_Run Initialize things for the module. Returns TRUE if
* Make_Run Initialize things for the module. Returns true if
* work was (or would have been) done.
*
* Make_Update After a target is made, update all its parents.
@ -85,7 +85,8 @@
* Update the node's youngestChild field based on the
* child's modification time.
*
* Make_DoAllVar Set up the various local variables for a
* GNode_SetLocalVars
* Set up the various local variables for a
* target, including the .ALLSRC variable, making
* sure that any variable that needs to exist
* at the very least has the empty value.
@ -103,7 +104,7 @@
#include "job.h"
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $");
MAKE_RCSID("$NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@ -168,7 +169,7 @@ GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
suffix);
}
Boolean
bool
GNode_ShouldExecute(GNode *gn)
{
return !((gn->type & OP_MAKE)
@ -184,7 +185,7 @@ GNode_UpdateYoungestChild(GNode *gn, GNode *cgn)
gn->youngestChild = cgn;
}
static Boolean
static bool
IsOODateRegular(GNode *gn)
{
/* These rules are inherited from the original Make. */
@ -193,22 +194,22 @@ IsOODateRegular(GNode *gn)
if (gn->mtime < gn->youngestChild->mtime) {
DEBUG1(MAKE, "modified before source \"%s\"...",
GNode_Path(gn->youngestChild));
return TRUE;
return true;
}
return FALSE;
return false;
}
if (gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) {
DEBUG0(MAKE, "nonexistent and no sources...");
return TRUE;
return true;
}
if (gn->type & OP_DOUBLEDEP) {
DEBUG0(MAKE, ":: operator and no sources...");
return TRUE;
return true;
}
return FALSE;
return false;
}
/*
@ -223,17 +224,17 @@ IsOODateRegular(GNode *gn)
* The mtime field of the node and the youngestChild field of its parents
* may be changed.
*/
Boolean
bool
GNode_IsOODate(GNode *gn)
{
Boolean oodate;
bool oodate;
/*
* Certain types of targets needn't even be sought as their datedness
* doesn't depend on their modification time...
*/
if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) {
Dir_UpdateMTime(gn, TRUE);
Dir_UpdateMTime(gn, true);
if (DEBUG(MAKE)) {
if (gn->mtime != 0)
debug_printf("modified %s...",
@ -267,7 +268,7 @@ GNode_IsOODate(GNode *gn)
* no matter *what*.
*/
DEBUG0(MAKE, ".USE node...");
oodate = FALSE;
oodate = false;
} else if ((gn->type & OP_LIB) && (gn->mtime == 0 || Arch_IsLib(gn))) {
DEBUG0(MAKE, "library...");
@ -302,9 +303,9 @@ GNode_IsOODate(GNode *gn)
debug_printf(".EXEC node...");
}
}
oodate = TRUE;
oodate = true;
} else if (IsOODateRegular(gn)) {
oodate = TRUE;
oodate = true;
} else {
/*
* When a nonexistent child with no sources
@ -351,7 +352,7 @@ PretendAllChildrenAreMade(GNode *pgn)
GNode *cgn = ln->datum;
/* This may also update cgn->path. */
Dir_UpdateMTime(cgn, FALSE);
Dir_UpdateMTime(cgn, false);
GNode_UpdateYoungestChild(pgn, cgn);
pgn->unmade--;
}
@ -443,7 +444,7 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
static void
MakeHandleUse(GNode *cgn, GNode *pgn, GNodeListNode *ln)
{
Boolean unmarked;
bool unmarked;
unmarked = !(cgn->type & OP_MARK);
cgn->type |= OP_MARK;
@ -485,7 +486,7 @@ Make_Recheck(GNode *gn)
{
time_t mtime;
Dir_UpdateMTime(gn, TRUE);
Dir_UpdateMTime(gn, true);
mtime = gn->mtime;
#ifndef RECHECK
@ -576,7 +577,7 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname)
}
/* See if a .ORDER rule stops us from building this node. */
static Boolean
static bool
IsWaitingForOrder(GNode *gn)
{
GNodeListNode *ln;
@ -590,9 +591,9 @@ IsWaitingForOrder(GNode *gn)
DEBUG2(MAKE,
"IsWaitingForOrder: Waiting for .ORDER node \"%s%s\"\n",
ogn->name, ogn->cohort_num);
return TRUE;
return true;
}
return FALSE;
return false;
}
static void MakeBuildParent(GNode *, GNodeListNode *);
@ -868,7 +869,7 @@ MakeAddAllSrc(GNode *cgn, GNode *pgn)
* match its ALLSRC variable.
*/
void
Make_DoAllVar(GNode *gn)
GNode_SetLocalVars(GNode *gn)
{
GNodeListNode *ln;
@ -889,7 +890,7 @@ Make_DoAllVar(GNode *gn)
gn->flags |= DONE_ALLSRC;
}
static Boolean
static bool
MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
{
@ -899,13 +900,13 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
GNode_FprintDetails(opts.debug_file, "", cn, "\n");
}
if (GNode_IsReady(cn))
return FALSE;
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 FALSE; /* but keep looking */
return false; /* but keep looking */
}
DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n",
@ -961,13 +962,13 @@ MakeChildren(GNode *gn)
*
* If the -q option was given, no job will be started,
* but as soon as an out-of-date target is found, this function
* returns TRUE. In all other cases, this function returns FALSE.
* returns true. In all other cases, this function returns false.
*/
static Boolean
static bool
MakeStartJobs(void)
{
GNode *gn;
Boolean have_token = FALSE;
bool have_token = false;
while (!Lst_IsEmpty(&toBeMade)) {
/*
@ -976,7 +977,7 @@ MakeStartJobs(void)
*/
if (!have_token && !Job_TokenWithdraw())
break;
have_token = TRUE;
have_token = true;
gn = Lst_Dequeue(&toBeMade);
DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num);
@ -1022,10 +1023,10 @@ MakeStartJobs(void)
if (GNode_IsOODate(gn)) {
DEBUG0(MAKE, "out-of-date\n");
if (opts.queryFlag)
return TRUE;
Make_DoAllVar(gn);
return true;
GNode_SetLocalVars(gn);
Job_Make(gn);
have_token = FALSE;
have_token = false;
} else {
DEBUG0(MAKE, "up-to-date\n");
gn->made = UPTODATE;
@ -1037,7 +1038,7 @@ MakeStartJobs(void)
* for .TARGET when building up the local
* variables of its parent(s)...
*/
Make_DoAllVar(gn);
GNode_SetLocalVars(gn);
}
Make_Update(gn);
}
@ -1046,7 +1047,7 @@ MakeStartJobs(void)
if (have_token)
Job_TokenReturn();
return FALSE;
return false;
}
/* Print the status of a .ORDER node. */
@ -1081,7 +1082,7 @@ static void MakePrintStatusList(GNodeList *, int *);
* Print the status of a top-level node, viz. it being up-to-date already
* or not created due to an error in a lower level.
*/
static Boolean
static bool
MakePrintStatus(GNode *gn, int *errors)
{
if (gn->flags & DONECYCLE) {
@ -1089,7 +1090,7 @@ MakePrintStatus(GNode *gn, int *errors)
* We've completely processed this node before, don't do
* it again.
*/
return FALSE;
return false;
}
if (gn->unmade == 0) {
@ -1128,7 +1129,7 @@ MakePrintStatus(GNode *gn, int *errors)
gn->name, gn->cohort_num);
break;
}
return FALSE;
return false;
}
DEBUG3(MAKE, "MakePrintStatus: %s%s has %d unmade children\n",
@ -1143,7 +1144,7 @@ MakePrintStatus(GNode *gn, int *errors)
MakePrintStatusList(&gn->children, errors);
/* Mark that this node needn't be processed again */
gn->flags |= DONECYCLE;
return FALSE;
return false;
}
/* Only output the error once per node */
@ -1151,11 +1152,11 @@ MakePrintStatus(GNode *gn, int *errors)
Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num);
if ((*errors)++ > 100)
/* Abandon the whole error report */
return TRUE;
return true;
/* Reporting for our children will give the rest of the loop */
MakePrintStatusList(&gn->children, errors);
return FALSE;
return false;
}
static void
@ -1243,7 +1244,7 @@ Make_ExpandUse(GNodeList *targs)
*eon = ')';
}
Dir_UpdateMTime(gn, FALSE);
Dir_UpdateMTime(gn, false);
Var_Set(gn, TARGET, GNode_Path(gn));
UnmarkChildren(gn);
HandleUseNodes(gn);
@ -1364,14 +1365,14 @@ Make_ProcessWait(GNodeList *targs)
* targs the initial list of targets
*
* Results:
* TRUE if work was done. FALSE otherwise.
* True if work was done, false otherwise.
*
* Side Effects:
* The make field of all nodes involved in the creation of the given
* targets is set to 1. The toBeMade list is set to contain all the
* 'leaves' of these subgraphs.
*/
Boolean
bool
Make_Run(GNodeList *targs)
{
int errors; /* Number of errors the Job module reports */

129
make.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.256 2021/02/05 19:19:17 sjg Exp $ */
/* $NetBSD: make.h,v 1.263 2021/06/21 10:33:11 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -136,55 +136,30 @@
#endif
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
#define MAKE_STATIC static MAKE_ATTR_UNUSED
/*
* A boolean type is defined as an integer, not an enum, for historic reasons.
* The only allowed values are the constants TRUE and FALSE (1 and 0).
*/
#if defined(lint) || defined(USE_C99_BOOLEAN)
#if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN)
#include <stdbool.h>
typedef bool Boolean;
#define FALSE false
#define TRUE true
#elif defined(USE_DOUBLE_BOOLEAN)
/* During development, to find type mismatches in function declarations. */
typedef double Boolean;
#define TRUE 1.0
#define FALSE 0.0
#elif defined(USE_UCHAR_BOOLEAN)
/*
* During development, to find code that depends on the exact value of TRUE or
* that stores other values in Boolean variables.
*/
typedef unsigned char Boolean;
#define TRUE ((unsigned char)0xFF)
#define FALSE ((unsigned char)0x00)
#elif defined(USE_CHAR_BOOLEAN)
/*
* During development, to find code that uses a boolean as array index, via
* -Wchar-subscripts.
*/
typedef char Boolean;
#define TRUE ((char)-1)
#define FALSE ((char)0x00)
#elif defined(USE_ENUM_BOOLEAN)
typedef enum Boolean { FALSE, TRUE } Boolean;
#else
typedef int Boolean;
#ifndef TRUE
#define TRUE 1
#ifndef bool
typedef unsigned int Boolean;
#define bool Boolean
#endif
#ifndef FALSE
#define FALSE 0
#ifndef true
#define true 1
#endif
#ifndef false
#define false 0
#endif
#endif
#include "lst.h"
#include "enum.h"
#include "make_malloc.h"
#include "str.h"
#include "hash.h"
#include "make-conf.h"
#include "buf.h"
#include "make_malloc.h"
/*
* some vendors don't have this --sjg
@ -247,6 +222,8 @@ typedef enum GNodeMade {
* should be made.
*
* Some of the OP_ constants can be combined, others cannot.
*
* See the tests depsrc-*.mk and deptgt-*.mk.
*/
typedef enum GNodeType {
OP_NONE = 0,
@ -503,11 +480,11 @@ typedef enum CondEvalResult {
*/
/* True if every target is precious */
extern Boolean allPrecious;
extern bool allPrecious;
/* True if failed targets should be deleted */
extern Boolean deleteOnError;
/* TRUE while processing .depend */
extern Boolean doing_depend;
extern bool deleteOnError;
/* true while processing .depend */
extern bool doing_depend;
/* .DEFAULT rule */
extern GNode *defaultNode;
@ -606,7 +583,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
do { \
if (DEBUG(module)) \
debug_printf args; \
} while (/*CONSTCOND*/FALSE)
} while (/*CONSTCOND*/false)
#define DEBUG0(module, text) \
DEBUG_IMPL(module, ("%s", text))
@ -630,7 +607,7 @@ typedef enum PrintVarsMode {
/* Command line options */
typedef struct CmdOpts {
/* -B: whether we are make compatible */
Boolean compatMake;
bool compatMake;
/* -d: debug control: There is one bit per module. It is up to the
* module what debug information to print. */
@ -643,19 +620,19 @@ typedef struct CmdOpts {
*
* Runs make in strict mode, with additional checks and better error
* handling. */
Boolean strict;
bool strict;
/* -dV: for the -V option, print unexpanded variable values */
Boolean debugVflag;
bool debugVflag;
/* -e: check environment variables before global variables */
Boolean checkEnvFirst;
bool checkEnvFirst;
/* -f: the makefiles to read */
StringList makefiles;
/* -i: if true, ignore all errors from shell commands */
Boolean ignoreErrors;
bool ignoreErrors;
/* -j: the maximum number of jobs that can run in parallel;
* this is coordinated with the submakes */
@ -663,29 +640,29 @@ typedef struct CmdOpts {
/* -k: if true and an error occurs while making a node, continue
* making nodes that do not depend on the erroneous node */
Boolean keepgoing;
bool keepgoing;
/* -N: execute no commands from the targets */
Boolean noRecursiveExecute;
bool noRecursiveExecute;
/* -n: execute almost no commands from the targets */
Boolean noExecute;
bool noExecute;
/*
* -q: if true, do not really make anything, just see if the targets
* are out-of-date
*/
Boolean queryFlag;
bool queryFlag;
/* -r: raw mode, do not load the builtin rules. */
Boolean noBuiltins;
bool noBuiltins;
/* -s: don't echo the shell commands before executing them */
Boolean beSilent;
bool beSilent;
/* -t: touch the targets if they are out-of-date, but don't actually
* make them */
Boolean touchFlag;
bool touchFlag;
/* -[Vv]: print expanded or unexpanded selected variables */
PrintVarsMode printVars;
@ -693,14 +670,14 @@ typedef struct CmdOpts {
StringList variables;
/* -W: if true, makefile parsing warnings are treated as errors */
Boolean parseWarnFatal;
bool parseWarnFatal;
/* -w: print 'Entering' and 'Leaving' for submakes */
Boolean enterFlag;
bool enterFlag;
/* -X: if true, do not export variables set on the command line to the
* environment. */
Boolean varNoExportEnv;
bool varNoExportEnv;
/* The target names specified on the command line.
* Used to resolve .if make(...) statements. */
@ -713,24 +690,24 @@ extern CmdOpts opts;
#include "nonints.h"
void GNode_UpdateYoungestChild(GNode *, GNode *);
Boolean GNode_IsOODate(GNode *);
bool GNode_IsOODate(GNode *);
void Make_ExpandUse(GNodeList *);
time_t Make_Recheck(GNode *);
void Make_HandleUse(GNode *, GNode *);
void Make_Update(GNode *);
void Make_DoAllVar(GNode *);
Boolean Make_Run(GNodeList *);
Boolean shouldDieQuietly(GNode *, int);
void GNode_SetLocalVars(GNode *);
bool Make_Run(GNodeList *);
bool shouldDieQuietly(GNode *, int);
void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(Boolean);
Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
void Main_ExportMAKEFLAGS(bool);
bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
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);
bool GNode_ShouldExecute(GNode *gn);
/* See if the node was seen on the left-hand side of a dependency operator. */
MAKE_INLINE Boolean
MAKE_INLINE bool
GNode_IsTarget(const GNode *gn)
{
return (gn->type & OP_OPMASK) != 0;
@ -742,25 +719,25 @@ GNode_Path(const GNode *gn)
return gn->path != NULL ? gn->path : gn->name;
}
MAKE_INLINE Boolean
MAKE_INLINE bool
GNode_IsWaitingFor(const GNode *gn)
{
return (gn->flags & REMAKE) && gn->made <= REQUESTED;
}
MAKE_INLINE Boolean
MAKE_INLINE bool
GNode_IsReady(const GNode *gn)
{
return gn->made > DEFERRED;
}
MAKE_INLINE Boolean
MAKE_INLINE bool
GNode_IsDone(const GNode *gn)
{
return gn->made >= MADE;
}
MAKE_INLINE Boolean
MAKE_INLINE bool
GNode_IsError(const GNode *gn)
{
return gn->made == ERROR || gn->made == ABORTED;
@ -781,7 +758,7 @@ GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
MAKE_INLINE const char *
GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#ifdef __GNUC__
#if defined(__GNUC__) && __STDC_VERSION__ >= 199901L
#define UNCONST(ptr) ({ \
union __unconst { \
const void *__cp; \
@ -809,15 +786,15 @@ GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
#define KILLPG(pid, sig) killpg((pid), (sig))
#endif
MAKE_INLINE Boolean
MAKE_INLINE bool
ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
MAKE_INLINE Boolean
MAKE_INLINE bool
ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
MAKE_INLINE Boolean
MAKE_INLINE bool
ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
MAKE_INLINE Boolean
MAKE_INLINE bool
ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
MAKE_INLINE Boolean
MAKE_INLINE bool
ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
MAKE_INLINE char
ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }

192
meta.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.177 2021/02/05 19:19:17 sjg Exp $ */
/* $NetBSD: meta.c,v 1.181 2021/04/04 10:05:08 rillig Exp $ */
/*
* Implement 'meta' mode.
@ -70,20 +70,20 @@ static char *metaIgnorePathsStr; /* string storage for the list */
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
#endif
Boolean useMeta = FALSE;
static Boolean useFilemon = FALSE;
static Boolean writeMeta = FALSE;
static Boolean metaMissing = FALSE; /* oodate if missing */
static Boolean filemonMissing = FALSE; /* oodate if missing */
static Boolean metaEnv = FALSE; /* don't save env unless asked */
static Boolean metaVerbose = FALSE;
static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
bool useMeta = false;
static bool useFilemon = false;
static bool writeMeta = false;
static bool metaMissing = false; /* oodate if missing */
static bool filemonMissing = false; /* oodate if missing */
static bool metaEnv = false; /* don't save env unless asked */
static bool metaVerbose = false;
static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */
static bool metaIgnorePatterns = false; /* do we need to do pattern matches */
static bool metaIgnoreFilter = false; /* do we have more complex filtering? */
static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
static bool metaSilent = false; /* if we have a .meta be SILENT */
extern Boolean forceJobs;
extern bool forceJobs;
extern char **environ;
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
@ -133,7 +133,7 @@ meta_open_filemon(BuildMon *pbm)
pbm->filemon = filemon_open();
if (pbm->filemon == NULL) {
useFilemon = FALSE;
useFilemon = false;
warn("Could not open filemon %s", filemon_path());
return;
}
@ -319,7 +319,7 @@ meta_name(char *mname, size_t mnamelen,
* Return true if running ${.MAKE}
* Bypassed if target is flagged .MAKE
*/
static Boolean
static bool
is_submake(const char *cmd, GNode *gn)
{
static const char *p_make = NULL;
@ -327,7 +327,7 @@ is_submake(const char *cmd, GNode *gn)
char *mp = NULL;
char *cp;
char *cp2;
Boolean rc = FALSE;
bool rc = false;
if (p_make == NULL) {
p_make = Var_Value(gn, ".MAKE").str;
@ -346,7 +346,7 @@ is_submake(const char *cmd, GNode *gn)
case ' ':
case '\t':
case '\n':
rc = TRUE;
rc = true;
break;
}
if (cp2 > cmd && rc) {
@ -356,7 +356,7 @@ is_submake(const char *cmd, GNode *gn)
case '\n':
break;
default:
rc = FALSE; /* no match */
rc = false; /* no match */
break;
}
}
@ -365,29 +365,31 @@ is_submake(const char *cmd, GNode *gn)
return rc;
}
static Boolean
static bool
any_is_submake(GNode *gn)
{
StringListNode *ln;
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
if (is_submake(ln->datum, gn))
return TRUE;
return FALSE;
return true;
return false;
}
static void
printCMD(const char *cmd, FILE *fp, GNode *gn)
printCMD(const char *ucmd, FILE *fp, GNode *gn)
{
char *cmd_freeIt = NULL;
FStr xcmd = FStr_InitRefer(ucmd);
if (strchr(cmd, '$') != NULL) {
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmd_freeIt);
if (strchr(ucmd, '$') != NULL) {
char *expanded;
(void)Var_Subst(ucmd, gn, VARE_WANTRES, &expanded);
/* TODO: handle errors */
cmd = cmd_freeIt;
xcmd = FStr_InitOwn(expanded);
}
fprintf(fp, "CMD %s\n", cmd);
free(cmd_freeIt);
fprintf(fp, "CMD %s\n", xcmd.str);
FStr_Done(&xcmd);
}
static void
@ -408,17 +410,17 @@ printCMDs(GNode *gn, FILE *fp)
debug_printf("Skipping meta for %s: .%s\n", \
gn->name, __STRING(_type)); \
} \
return FALSE; \
return false; \
} \
} while (/*CONSTCOND*/FALSE)
} while (/*CONSTCOND*/false)
/*
* Do we need/want a .meta file ?
*/
static Boolean
static bool
meta_needed(GNode *gn, const char *dname,
char *objdir_realpath, Boolean verbose)
char *objdir_realpath, bool verbose)
{
struct cached_stat cst;
@ -440,13 +442,13 @@ meta_needed(GNode *gn, const char *dname,
if (Lst_IsEmpty(&gn->commands)) {
if (verbose)
debug_printf("Skipping meta for %s: no commands\n", gn->name);
return FALSE;
return false;
}
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
/* OP_SUBMAKE is a bit too aggressive */
if (any_is_submake(gn)) {
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
return FALSE;
return false;
}
}
@ -454,7 +456,7 @@ meta_needed(GNode *gn, const char *dname,
if (cached_stat(dname, &cst) != 0) {
if (verbose)
debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
return FALSE;
return false;
}
/* make sure these are canonical */
@ -466,9 +468,9 @@ meta_needed(GNode *gn, const char *dname,
if (verbose)
debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
gn->name);
return FALSE;
return false;
}
return TRUE;
return true;
}
@ -490,7 +492,7 @@ meta_create(BuildMon *pbm, GNode *gn)
tname = GNode_VarTarget(gn);
/* if this succeeds objdir_realpath is realpath of dname */
if (!meta_needed(gn, dname.str, objdir_realpath, TRUE))
if (!meta_needed(gn, dname.str, objdir_realpath, true))
goto out;
dname.str = objdir_realpath;
@ -554,7 +556,7 @@ meta_create(BuildMon *pbm, GNode *gn)
return fp;
}
static Boolean
static bool
boolValue(char *s)
{
switch(*s) {
@ -563,9 +565,9 @@ boolValue(char *s)
case 'n':
case 'F':
case 'f':
return FALSE;
return false;
}
return TRUE;
return true;
}
/*
@ -591,25 +593,25 @@ meta_init(void)
void
meta_mode_init(const char *make_mode)
{
static Boolean once = FALSE;
static bool once = false;
char *cp;
FStr value;
useMeta = TRUE;
useFilemon = TRUE;
writeMeta = TRUE;
useMeta = true;
useFilemon = true;
writeMeta = true;
if (make_mode != NULL) {
if (strstr(make_mode, "env") != NULL)
metaEnv = TRUE;
metaEnv = true;
if (strstr(make_mode, "verb") != NULL)
metaVerbose = TRUE;
metaVerbose = true;
if (strstr(make_mode, "read") != NULL)
writeMeta = FALSE;
writeMeta = false;
if (strstr(make_mode, "nofilemon") != NULL)
useFilemon = FALSE;
useFilemon = false;
if (strstr(make_mode, "ignore-cmd") != NULL)
metaIgnoreCMDs = TRUE;
metaIgnoreCMDs = true;
if (useFilemon)
get_mode_bf(filemonMissing, "missing-filemon=");
get_mode_bf(metaCurdirOk, "curdirok=");
@ -628,7 +630,7 @@ meta_mode_init(const char *make_mode)
}
if (once)
return;
once = TRUE;
once = true;
memset(&Mybm, 0, sizeof Mybm);
/*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
@ -652,12 +654,12 @@ meta_mode_init(const char *make_mode)
*/
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
if (value.str != NULL) {
metaIgnorePatterns = TRUE;
metaIgnorePatterns = true;
FStr_Done(&value);
}
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
if (value.str != NULL) {
metaIgnoreFilter = TRUE;
metaIgnoreFilter = true;
FStr_Done(&value);
}
}
@ -774,7 +776,7 @@ meta_job_event(Job *job)
}
void
meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status)
meta_job_error(Job *job, GNode *gn, bool ignerr, int status)
{
char cwd[MAXPATHLEN];
BuildMon *pbm;
@ -944,7 +946,7 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
return 0;
}
static Boolean
static bool
prefix_match(const char *prefix, const char *path)
{
size_t n = strlen(prefix);
@ -952,35 +954,35 @@ prefix_match(const char *prefix, const char *path)
return strncmp(path, prefix, n) == 0;
}
static Boolean
static bool
has_any_prefix(const char *path, StringList *prefixes)
{
StringListNode *ln;
for (ln = prefixes->first; ln != NULL; ln = ln->next)
if (prefix_match(ln->datum, path))
return TRUE;
return FALSE;
return true;
return false;
}
/* See if the path equals prefix or starts with "prefix/". */
static Boolean
static bool
path_starts_with(const char *path, const char *prefix)
{
size_t n = strlen(prefix);
if (strncmp(path, prefix, n) != 0)
return FALSE;
return false;
return path[n] == '\0' || path[n] == '/';
}
static Boolean
static bool
meta_ignore(GNode *gn, const char *p)
{
char fname[MAXPATHLEN];
if (p == NULL)
return TRUE;
return true;
if (*p == '/') {
cached_realpath(p, fname); /* clean it up */
@ -988,7 +990,7 @@ meta_ignore(GNode *gn, const char *p)
#ifdef DEBUG_META_MODE
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
#endif
return TRUE;
return true;
}
}
@ -1011,7 +1013,7 @@ meta_ignore(GNode *gn, const char *p)
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
#endif
free(pm);
return TRUE;
return true;
}
free(pm);
}
@ -1030,11 +1032,11 @@ meta_ignore(GNode *gn, const char *p)
DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
#endif
free(fm);
return TRUE;
return true;
}
free(fm);
}
return FALSE;
return false;
}
/*
@ -1048,11 +1050,11 @@ meta_ignore(GNode *gn, const char *p)
/*
* It is possible that a .meta file is corrupted,
* if we detect this we want to reproduce it.
* Setting oodate TRUE will have that effect.
* Setting oodate true will have that effect.
*/
#define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
warnx("%s: %d: malformed", fname, lineno); \
oodate = TRUE; \
oodate = true; \
continue; \
}
@ -1074,8 +1076,8 @@ append_if_new(StringList *list, const char *str)
Lst_Append(list, bmake_strdup(str));
}
Boolean
meta_oodate(GNode *gn, Boolean oodate)
bool
meta_oodate(GNode *gn, bool oodate)
{
static char *tmpdir = NULL;
static char cwd[MAXPATHLEN];
@ -1095,9 +1097,9 @@ meta_oodate(GNode *gn, Boolean oodate)
static size_t cwdlen = 0;
static size_t tmplen = 0;
FILE *fp;
Boolean needOODATE = FALSE;
bool needOODATE = false;
StringList missingFiles;
Boolean have_filemon = FALSE;
bool have_filemon = false;
if (oodate)
return oodate; /* we're done */
@ -1106,7 +1108,7 @@ meta_oodate(GNode *gn, Boolean oodate)
tname = GNode_VarTarget(gn);
/* if this succeeds fname3 is realpath of dname */
if (!meta_needed(gn, dname.str, fname3, FALSE))
if (!meta_needed(gn, dname.str, fname3, false))
goto oodate_out;
dname.str = fname3;
@ -1118,7 +1120,7 @@ meta_oodate(GNode *gn, Boolean oodate)
* requires that all variables are set in the same way that they
* would be if the target needs to be re-built.
*/
Make_DoAllVar(gn);
GNode_SetLocalVars(gn);
meta_name(fname, sizeof fname, dname.str, tname, dname.str);
@ -1164,7 +1166,7 @@ meta_oodate(GNode *gn, Boolean oodate)
buf[x - 1] = '\0';
else {
warnx("%s: %d: line truncated at %u", fname, lineno, x);
oodate = TRUE;
oodate = true;
break;
}
link_src = NULL;
@ -1172,11 +1174,11 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Find the start of the build monitor section. */
if (!have_filemon) {
if (strncmp(buf, "-- filemon", 10) == 0) {
have_filemon = TRUE;
have_filemon = true;
continue;
}
if (strncmp(buf, "# buildmon", 10) == 0) {
have_filemon = TRUE;
have_filemon = true;
continue;
}
}
@ -1424,7 +1426,7 @@ meta_oodate(GNode *gn, Boolean oodate)
char *sdirs[4];
char **sdp;
int sdx = 0;
Boolean found = FALSE;
bool found = false;
if (*p == '/') {
sdirs[sdx++] = p; /* done */
@ -1455,7 +1457,7 @@ meta_oodate(GNode *gn, Boolean oodate)
fname, lineno, *sdp);
#endif
if (cached_stat(*sdp, &cst) == 0) {
found = TRUE;
found = true;
p = *sdp;
}
}
@ -1468,7 +1470,7 @@ meta_oodate(GNode *gn, Boolean oodate)
cst.cst_mtime > gn->mtime) {
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
fname, lineno, p);
oodate = TRUE;
oodate = true;
} else if (S_ISDIR(cst.cst_mode)) {
/* Update the latest directory. */
cached_realpath(p, latestdir);
@ -1500,25 +1502,25 @@ meta_oodate(GNode *gn, Boolean oodate)
if (cmdNode == NULL) {
DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
fname, lineno);
oodate = TRUE;
oodate = true;
} else {
const char *cp;
char *cmd = cmdNode->datum;
Boolean hasOODATE = FALSE;
bool hasOODATE = false;
if (strstr(cmd, "$?") != NULL)
hasOODATE = TRUE;
hasOODATE = true;
else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
/* check for $[{(].OODATE[:)}] */
if (cp > cmd + 2 && cp[-2] == '$')
hasOODATE = TRUE;
hasOODATE = true;
}
if (hasOODATE) {
needOODATE = TRUE;
needOODATE = true;
DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
fname, lineno);
}
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
(void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd);
/* TODO: handle errors */
if ((cp = strchr(cmd, '\n')) != NULL) {
@ -1553,7 +1555,7 @@ meta_oodate(GNode *gn, Boolean oodate)
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
fname, lineno, p, cmd);
if (!metaIgnoreCMDs)
oodate = TRUE;
oodate = true;
}
free(cmd);
cmdNode = cmdNode->next;
@ -1566,13 +1568,13 @@ meta_oodate(GNode *gn, Boolean oodate)
if (!oodate && cmdNode != NULL) {
DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
fname, lineno);
oodate = TRUE;
oodate = true;
}
CHECK_VALID_META(p);
if (strcmp(p, cwd) != 0) {
DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
fname, lineno, p, curdir);
oodate = TRUE;
oodate = true;
}
}
}
@ -1581,11 +1583,11 @@ meta_oodate(GNode *gn, Boolean oodate)
if (!Lst_IsEmpty(&missingFiles)) {
DEBUG2(META, "%s: missing files: %s...\n",
fname, (char *)missingFiles.first->datum);
oodate = TRUE;
oodate = true;
}
if (!oodate && !have_filemon && filemonMissing) {
DEBUG1(META, "%s: missing filemon data\n", fname);
oodate = TRUE;
oodate = true;
}
} else {
if (writeMeta && (metaMissing || (gn->type & OP_META))) {
@ -1600,8 +1602,8 @@ meta_oodate(GNode *gn, Boolean oodate)
}
if (cp == NULL) {
DEBUG1(META, "%s: required but missing\n", fname);
oodate = TRUE;
needOODATE = TRUE; /* assume the worst */
oodate = true;
needOODATE = true; /* assume the worst */
}
}
}
@ -1708,7 +1710,7 @@ meta_compat_parent(pid_t child)
fflush(stdout);
buf[nread] = '\0';
meta_job_output(NULL, buf, "");
} while (/*CONSTCOND*/FALSE);
} while (/*CONSTCOND*/false);
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0)
metafd = -1;

8
meta.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.h,v 1.9 2020/12/10 20:49:11 rillig Exp $ */
/* $NetBSD: meta.h,v 1.10 2021/04/03 11:08:40 rillig Exp $ */
/*
* Things needed for 'meta' mode.
@ -48,13 +48,13 @@ void meta_job_child(struct Job *);
void meta_job_parent(struct Job *, pid_t);
int meta_job_fd(struct Job *);
int meta_job_event(struct Job *);
void meta_job_error(struct Job *, GNode *, Boolean, int);
void meta_job_error(struct Job *, GNode *, bool, int);
void meta_job_output(struct Job *, char *, const char *);
int meta_cmd_finish(void *);
int meta_job_finish(struct Job *);
Boolean meta_oodate(GNode *, Boolean);
bool meta_oodate(GNode *, bool);
void meta_compat_start(void);
void meta_compat_child(void);
void meta_compat_parent(pid_t);
extern Boolean useMeta;
extern bool useMeta;

View File

@ -1,4 +1,4 @@
/* $NetBSD: metachar.h,v 1.15 2021/01/19 20:51:46 rillig Exp $ */
/* $NetBSD: metachar.h,v 1.16 2021/04/03 11:08:40 rillig Exp $ */
/*
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@ extern unsigned char _metachar[];
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
MAKE_INLINE Boolean
MAKE_INLINE bool
needshell(const char *cmd)
{
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')

View File

@ -1,3 +1,38 @@
2021-06-16 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210616
* dirdeps.mk: when using .MAKE.DEPENDFILE_PREFERENCE to find
depend files to read, anchor MACHINE at , or end of string
to avoid prefix match.
2021-05-04 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210504
* dirdeps.mk: re-implement ALL_MACHINES support to better
cater for local complexities, when ONLY_TARGET_SPEC_LIST
is not set. local.dirdeps.mk can set
DIRDEPS_ALL_MACHINES_FILTER and/or
DIRDEPS_ALL_MACHINES_FILTER_XTRAS to filter the results we get
from listing all existing Makefile.depend.*
2021-04-20 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210420
* dirdeps.mk: revert previous - not always safe.
2021-03-20 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210321
* dirdeps.mk: when generating dirdeps.cache
we only need to hook the initial DIRDEPS to the
dirdeps target. That and any _build_xtra_dirs (like tests which
should not be hooked directly to the dependency graph - to avoid
cycles)
2021-01-30 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210130

View File

@ -1,4 +1,4 @@
# $Id: dirdeps.mk,v 1.133 2021/01/31 04:39:22 sjg Exp $
# $Id: dirdeps.mk,v 1.140 2021/06/20 23:42:38 sjg Exp $
# Copyright (c) 2010-2021, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
@ -409,23 +409,72 @@ _DIRDEP_USE: .USE .MAKE
done
.ifdef ALL_MACHINES
# this is how you limit it to only the machines we have been built for
# previously.
.if empty(ONLY_TARGET_SPEC_LIST) && empty(ONLY_MACHINE_LIST)
.if !empty(ALL_MACHINE_LIST)
# ALL_MACHINE_LIST is the list of all legal machines - ignore anything else
_machine_list != cd ${_CURDIR} && 'ls' -1 ${ALL_MACHINE_LIST:O:u:@m@${.MAKE.DEPENDFILE:T:R}.$m@} 2> /dev/null; echo
.else
_machine_list != 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.* 2> /dev/null; echo
# we start with everything
_machine_list != echo; 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}* 2> /dev/null
# some things we know we want to ignore
DIRDEPS_TARGETS_SKIP_LIST += \
*~ \
*.bak \
*.inc \
*.old \
*.options \
*.orig \
*.rej \
# first trim things we know we want to skip
# and provide canonical form
_machine_list := ${_machine_list:${DIRDEPS_TARGETS_SKIP_LIST:${M_ListToSkip}}:T:E}
# cater for local complexities
# local.dirdeps.mk can set
# DIRDEPS_ALL_MACHINES_FILTER and
# DIRDEPS_ALL_MACHINES_FILTER_XTRAS for final tweaks
.if !empty(ALL_TARGET_SPEC_LIST)
.if ${_debug_reldir}
.info ALL_TARGET_SPEC_LIST=${ALL_TARGET_SPEC_LIST}
.endif
_only_machines := ${_machine_list:${NIgnoreFiles:UN*.bak}:E:O:u}
DIRDEPS_ALL_MACHINES_FILTER += \
@x@$${ALL_TARGET_SPEC_LIST:@s@$${x:M$$s}@}@
.elif !empty(ALL_MACHINE_LIST)
.if ${_debug_reldir}
.info ALL_MACHINE_LIST=${ALL_MACHINE_LIST}
.endif
.if ${TARGET_SPEC_VARS:[#]} > 1
# the space below can result in extraneous ':'
DIRDEPS_ALL_MACHINES_FILTER += \
@x@$${ALL_MACHINE_LIST:@m@$${x:M$$m,*} $${x:M$$m}@}@
.else
DIRDEPS_ALL_MACHINES_FILTER += \
@x@$${ALL_MACHINE_LIST:@m@$${x:M$$m}@}@
.endif
.endif
# add local XTRAS - default to something benign
DIRDEPS_ALL_MACHINES_FILTER += \
${DIRDEPS_ALL_MACHINES_FILTER_XTRAS:UNbak}
.if ${_debug_reldir}
.info _machine_list=${_machine_list}
.info DIRDEPS_ALL_MACHINES_FILTER=${DIRDEPS_ALL_MACHINES_FILTER}
.endif
_only_machines := ${_machine_list:${DIRDEPS_ALL_MACHINES_FILTER:ts:}:S,:, ,g}
.else
_only_machines := ${ONLY_TARGET_SPEC_LIST:U} ${ONLY_MACHINE_LIST:U}
.endif
.if empty(_only_machines)
# we must be boot-strapping
_only_machines := ${TARGET_MACHINE:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}}
_only_machines := ${TARGET_MACHINE:U${ALL_TARGET_SPEC_LIST:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}}}
.endif
# cleanup the result
_only_machines := ${_only_machines:O:u}
.if ${_debug_reldir}
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: ALL_MACHINES _only_machines=${_only_machines}
.endif
.else # ! ALL_MACHINES
@ -452,6 +501,10 @@ _only_machines := ${_only_machines:${NOT_TARGET_SPEC_LIST:${M_ListToSkip}}}
# clean up
_only_machines := ${_only_machines:O:u}
.if ${_debug_reldir}
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _only_machines=${_only_machines}
.endif
# make sure we have a starting place?
DIRDEPS ?= ${RELDIR}
.endif # target
@ -697,6 +750,7 @@ _cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m}
.export _cache_deps
x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
.endif
# anything in _build_xtra_dirs is hooked to dirdeps: only
x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \
@ -735,7 +789,7 @@ DEP_MACHINE := ${_DEP_MACHINE}
# Warning: there is an assumption here that MACHINE is always
# the first entry in TARGET_SPEC_VARS.
# If TARGET_SPEC and MACHINE are insufficient, you have a problem.
_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:S;${MACHINE};${d:E:C/,.*//};:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]}
_m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:C;${MACHINE}((,.+)?)$;${d:E:C/,.*//}\1;:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]}
.if !empty(_m)
# M_dep_qual_fixes isn't geared to Makefile.depend
_qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}}
@ -801,6 +855,7 @@ _reldir_failed: .NOMETA
.endif
# bootstrapping new dependencies made easy?
.if !target(bootstrap-empty)
.if !target(bootstrap) && (make(bootstrap) || \
make(bootstrap-this) || \
make(bootstrap-recurse) || \
@ -856,3 +911,4 @@ bootstrap-empty: .NOTMAIN .NOMETA
@{ echo DIRDEPS=; echo ".include <dirdeps.mk>"; } > ${_want}
.endif
.endif

View File

@ -1,4 +1,4 @@
# $Id: dpadd.mk,v 1.28 2020/08/19 17:51:53 sjg Exp $
# $Id: dpadd.mk,v 1.29 2021/04/20 02:30:44 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
#
@ -211,8 +211,9 @@ OBJ_${__lib:T:R} ?= ${__lib:H:S,${OBJTOP},${RELOBJTOP},}
# If INCLUDES_libfoo is not set, then we'll use ${SRC_libfoo}/h if it exists,
# else just ${SRC_libfoo}.
#
INCLUDES_${__lib:T:R}?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h:${SRC_${__lib:T:R}}}
.if !empty(SRC_${__lib:T:R})
INCLUDES_${__lib:T:R} ?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h:${SRC_${__lib:T:R}}}
.endif
.endfor
# even for staged libs we sometimes

View File

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

View File

@ -1,4 +1,4 @@
# $Id: meta.autodep.mk,v 1.53 2020/11/08 05:47:56 sjg Exp $
# $Id: meta.autodep.mk,v 1.54 2021/03/06 17:03:18 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@ -298,16 +298,20 @@ start_utc := ${now_utc}
meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \
created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}}
.if !target(_reldir_finish)
#.END: _reldir_finish
.if target(gendirdeps)
_reldir_finish: gendirdeps
.endif
_reldir_finish: .NOMETA
@echo "${TIME_STAMP} Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
.endif
.if !target(_reldir_failed)
#.ERROR: _reldir_failed
_reldir_failed: .NOMETA
@echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}"
.endif
.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0
.END: _reldir_finish

View File

@ -37,7 +37,7 @@
"""
RCSid:
$Id: meta2deps.py,v 1.34 2020/10/02 03:11:17 sjg Exp $
$Id: meta2deps.py,v 1.38 2021/06/17 05:20:08 sjg Exp $
Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@ -68,12 +68,6 @@
import os, re, sys
def getv(dict, key, d=None):
"""Lookup key in dict and return value or the supplied default."""
if key in dict:
return dict[key]
return d
def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
"""
Return an absolute path, resolving via cwd or last_dir if needed.
@ -152,7 +146,10 @@ def abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
return path
def sort_unique(list, cmp=None, key=None, reverse=False):
list.sort(cmp, key, reverse)
if sys.version_info[0] == 2:
list.sort(cmp, key, reverse)
else:
list.sort(reverse=reverse)
nl = []
le = None
for e in list:
@ -224,22 +221,22 @@ def __init__(self, name, conf={}):
"""
self.name = name
self.debug = getv(conf, 'debug', 0)
self.debug_out = getv(conf, 'debug_out', sys.stderr)
self.debug = conf.get('debug', 0)
self.debug_out = conf.get('debug_out', sys.stderr)
self.machine = getv(conf, 'MACHINE', '')
self.machine_arch = getv(conf, 'MACHINE_ARCH', '')
self.target_spec = getv(conf, 'TARGET_SPEC', '')
self.curdir = getv(conf, 'CURDIR')
self.reldir = getv(conf, 'RELDIR')
self.dpdeps = getv(conf, 'DPDEPS')
self.machine = conf.get('MACHINE', '')
self.machine_arch = conf.get('MACHINE_ARCH', '')
self.target_spec = conf.get('TARGET_SPEC', '')
self.curdir = conf.get('CURDIR')
self.reldir = conf.get('RELDIR')
self.dpdeps = conf.get('DPDEPS')
self.line = 0
if not self.conf:
# some of the steps below we want to do only once
self.conf = conf
self.host_target = getv(conf, 'HOST_TARGET')
for srctop in getv(conf, 'SRCTOPS', []):
self.host_target = conf.get('HOST_TARGET')
for srctop in conf.get('SRCTOPS', []):
if srctop[-1] != '/':
srctop += '/'
if not srctop in self.srctops:
@ -256,7 +253,7 @@ def __init__(self, name, conf={}):
if self.target_spec:
trim_list += add_trims(self.target_spec)
for objroot in getv(conf, 'OBJROOTS', []):
for objroot in conf.get('OBJROOTS', []):
for e in trim_list:
if objroot.endswith(e):
# this is not what we want - fix it
@ -276,7 +273,7 @@ def __init__(self, name, conf={}):
self.srctops.sort(reverse=True)
self.objroots.sort(reverse=True)
self.excludes = getv(conf, 'EXCLUDES', [])
self.excludes = conf.get('EXCLUDES', [])
if self.debug:
print("host_target=", self.host_target, file=self.debug_out)
@ -468,8 +465,8 @@ def parse(self, name=None, file=None):
if pid != last_pid:
if last_pid:
pid_last_dir[last_pid] = self.last_dir
cwd = getv(pid_cwd, pid, self.cwd)
self.last_dir = getv(pid_last_dir, pid, self.cwd)
cwd = pid_cwd.get(pid, self.cwd)
self.last_dir = pid_last_dir.get(pid, self.cwd)
last_pid = pid
# process operations
@ -557,7 +554,10 @@ def parse_path(self, path, cwd, op=None, w=[]):
# to the src dir, we may need to add dependencies for each
rdir = dir
dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out)
rdir = os.path.realpath(dir)
if dir:
rdir = os.path.realpath(dir)
else:
dir = rdir
if rdir == dir:
rdir = None
# now put path back together
@ -725,7 +725,7 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
for a in eaten:
args.remove(a)
debug_out = getv(conf, 'debug_out', sys.stderr)
debug_out = conf.get('debug_out', sys.stderr)
if debug:
print("config:", file=debug_out)
@ -752,9 +752,9 @@ def main(argv, klass=MetaFile, xopts='', xoptf=None):
print(m.src_dirdeps('\nsrc:'))
dpdeps = getv(conf, 'DPDEPS')
dpdeps = conf.get('DPDEPS')
if dpdeps:
m.file_depends(open(dpdeps, 'wb'))
m.file_depends(open(dpdeps, 'w'))
return m

View File

@ -1,4 +1,4 @@
# $Id: rst2htm.mk,v 1.11 2020/08/19 17:51:53 sjg Exp $
# $Id: rst2htm.mk,v 1.12 2021/05/26 04:20:31 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@ -21,7 +21,9 @@ TXTSRCS != 'ls' -1t ${.CURDIR}/*.txt ${.CURDIR}/*.rst 2>/dev/null; echo
.endif
RSTSRCS ?= ${TXTSRCS}
HTMFILES ?= ${RSTSRCS:R:T:O:u:%=%.htm}
RST2HTML ?= rst2html.py
# can be empty, 4 or 5
HTML_VERSION ?=
RST2HTML ?= rst2html${HTML_VERSION}.py
RST2PDF ?= rst2pdf
RST2S5 ?= rst2s5.py
# the following will run RST2S5 if the target name contains the word 'slides'

153
nonints.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: nonints.h,v 1.202 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: nonints.h,v 1.213 2021/04/11 13:35:56 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -76,14 +76,14 @@
void Arch_Init(void);
void Arch_End(void);
Boolean Arch_ParseArchive(char **, GNodeList *, GNode *);
bool Arch_ParseArchive(char **, GNodeList *, GNode *);
void Arch_Touch(GNode *);
void Arch_TouchLib(GNode *);
void Arch_UpdateMTime(GNode *gn);
void Arch_UpdateMemberMTime(GNode *gn);
void Arch_FindLib(GNode *, SearchPath *);
Boolean Arch_LibOODate(GNode *);
Boolean Arch_IsLib(GNode *);
bool Arch_LibOODate(GNode *);
bool Arch_IsLib(GNode *);
/* compat.c */
int Compat_RunCommand(const char *, GNode *, StringListNode *);
@ -91,7 +91,7 @@ void Compat_Run(GNodeList *);
void Compat_Make(GNode *, GNode *);
/* cond.c */
CondEvalResult Cond_EvalCondition(const char *, Boolean *);
CondEvalResult Cond_EvalCondition(const char *, bool *);
CondEvalResult Cond_EvalLine(const char *);
void Cond_restore_depth(unsigned int);
unsigned int Cond_save_depth(void);
@ -117,16 +117,16 @@ void SearchPath_Free(SearchPath *);
/* for.c */
int For_Eval(const char *);
Boolean For_Accum(const char *);
bool For_Accum(const char *);
void For_Run(int);
/* job.c */
#ifdef WAIT_T
void JobReapChild(pid_t, WAIT_T, Boolean);
void JobReapChild(pid_t, WAIT_T, bool);
#endif
/* main.c */
Boolean GetBooleanVar(const char *, Boolean);
bool GetBooleanExpr(const char *, bool);
void Main_ParseArgLine(const char *);
char *Cmd_Exec(const char *, const char **);
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
@ -137,7 +137,7 @@ void Finish(int) MAKE_ATTR_DEAD;
int eunlink(const char *);
void execDie(const char *, const char *);
char *getTmpdir(void);
Boolean ParseBoolean(const char *, Boolean);
bool ParseBoolean(const char *, bool);
char *cached_realpath(const char *, char *);
/* parse.c */
@ -161,86 +161,14 @@ typedef struct VarAssign {
typedef char *(*ReadMoreProc)(void *, size_t *);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
Boolean Parse_IsVar(const char *, VarAssign *out_var);
void Parse_DoVar(VarAssign *, GNode *);
bool Parse_IsVar(const char *, VarAssign *out_var);
void Parse_Var(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
void Parse_MainName(GNodeList *);
int Parse_GetFatals(void);
/* str.c */
/* A read-only string that may need to be freed after use. */
typedef struct FStr {
const char *str;
void *freeIt;
} FStr;
/* A modifiable string that may need to be freed after use. */
typedef struct MFStr {
char *str;
void *freeIt;
} MFStr;
typedef struct Words {
char **words;
size_t len;
void *freeIt;
} Words;
/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
FStr_InitOwn(char *str)
{
return (FStr){ str, str };
}
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
return (FStr){ str, NULL };
}
MAKE_INLINE void
FStr_Done(FStr *fstr)
{
free(fstr->freeIt);
}
/* Return a string that is the sole owner of str. */
MAKE_INLINE MFStr
MFStr_InitOwn(char *str)
{
return (MFStr){ str, str };
}
/* Return a string that refers to the shared str. */
MAKE_INLINE MFStr
MFStr_InitRefer(char *str)
{
return (MFStr){ str, NULL };
}
MAKE_INLINE void
MFStr_Done(MFStr *mfstr)
{
free(mfstr->freeIt);
}
Words Str_Words(const char *, Boolean);
MAKE_INLINE void
Words_Free(Words w)
{
free(w.words);
free(w.freeIt);
}
char *str_concat2(const char *, const char *);
char *str_concat3(const char *, const char *, const char *);
char *str_concat4(const char *, const char *, const char *, const char *);
Boolean Str_Match(const char *, const char *);
#ifndef HAVE_STRLCPY
/* strlcpy.c */
@ -252,12 +180,12 @@ void Suff_Init(void);
void Suff_End(void);
void Suff_ClearSuffixes(void);
Boolean Suff_IsTransform(const char *);
bool Suff_IsTransform(const char *);
GNode *Suff_AddTransform(const char *);
void Suff_EndTransform(GNode *);
void Suff_AddSuffix(const char *, GNode **);
SearchPath *Suff_GetPath(const char *);
void Suff_DoPaths(void);
void Suff_ExtendPaths(void);
void Suff_AddInclude(const char *);
void Suff_AddLib(const char *);
void Suff_FindDeps(GNode *);
@ -277,7 +205,7 @@ GNode *Targ_GetNode(const char *);
GNode *Targ_NewInternalNode(const char *);
GNode *Targ_GetEndNode(void);
void Targ_FindList(GNodeList *, StringList *);
Boolean Targ_Precious(const GNode *);
bool Targ_Precious(const GNode *);
void Targ_SetMain(GNode *);
void Targ_PrintCmds(GNode *);
void Targ_PrintNode(GNode *, int);
@ -292,36 +220,40 @@ const char *GNodeMade_Name(GNodeMade);
void Var_Init(void);
void Var_End(void);
typedef enum VarEvalFlags {
VARE_NONE = 0,
typedef enum VarEvalMode {
/*
* Expand and evaluate variables during parsing.
* Only parse the expression but don't evaluate any part of it.
*
* TODO: Document what Var_Parse and Var_Subst return when this flag
* is not set.
* TODO: Document what Var_Parse and Var_Subst return in this mode.
* As of 2021-03-15, they return unspecified, inconsistent results.
*/
VARE_WANTRES = 1 << 0,
VARE_PARSE_ONLY,
/* Parse and evaluate the expression. */
VARE_WANTRES,
/*
* Treat undefined variables as errors.
* Must only be used in combination with VARE_WANTRES.
* Parse and evaluate the expression. It is an error if a
* subexpression evaluates to undefined.
*/
VARE_UNDEFERR = 1 << 1,
VARE_UNDEFERR,
/*
* Keep '$$' as '$$' instead of reducing it to a single '$'.
* Parse and evaluate the expression. Keep '$$' as '$$' instead of
* reducing it to a single '$'. Subexpressions that evaluate to
* undefined expand to an empty string.
*
* 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.
*/
VARE_KEEP_DOLLAR = 1 << 2,
VARE_EVAL_KEEP_DOLLAR,
/*
* Keep undefined variables as-is instead of expanding them to an
* empty string.
* Parse and evaluate the expression. Keep undefined variables as-is
* instead of expanding them to an empty string.
*
* Example for a ':=' assignment:
* CFLAGS = $(.INCLUDES)
@ -330,8 +262,14 @@ typedef enum VarEvalFlags {
* # way) is still undefined, the updated CFLAGS becomes
* # "-I.. $(.INCLUDES)".
*/
VARE_KEEP_UNDEF = 1 << 3
} VarEvalFlags;
VARE_EVAL_KEEP_UNDEF,
/*
* Parse and evaluate the expression. Keep '$$' as '$$' and preserve
* undefined subexpressions.
*/
VARE_KEEP_DOLLAR_UNDEF
} VarEvalMode;
typedef enum VarSetFlags {
VAR_SET_NONE = 0,
@ -361,7 +299,8 @@ typedef enum VarParseResult {
* Some callers handle this case differently, so return this
* information to them, for now.
*
* TODO: Replace this with a new flag VARE_KEEP_UNDEFINED.
* TODO: Instead of having this special return value, rather ensure
* that VARE_EVAL_KEEP_UNDEF is processed properly.
*/
VPR_UNDEF
@ -385,18 +324,18 @@ 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 *);
bool Var_Exists(GNode *, const char *);
bool 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 **);
VarParseResult Var_Parse(const char **, GNode *, VarEvalMode, FStr *);
VarParseResult Var_Subst(const char *, GNode *, VarEvalMode, char **);
void Var_Stats(void);
void Var_Dump(GNode *);
void Var_ReexportVars(void);
void Var_Export(VarExportMode, const char *);
void Var_ExportVars(const char *);
void Var_UnExport(Boolean, const char *);
void Var_UnExport(bool, const char *);
void Global_Set(const char *, const char *);
void Global_SetExpand(const char *, const char *);

512
parse.c

File diff suppressed because it is too large Load Diff

101
str.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $ */
/* $NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -71,7 +71,7 @@
#include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
MAKE_RCSID("$NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $");
MAKE_RCSID("$NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
@ -99,39 +99,23 @@ str_concat3(const char *s1, const char *s2, const char *s3)
return result;
}
/* Return the concatenation of s1, s2, s3 and s4, freshly allocated. */
char *
str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
{
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
size_t len3 = strlen(s3);
size_t len4 = strlen(s4);
char *result = bmake_malloc(len1 + len2 + len3 + len4 + 1);
memcpy(result, s1, len1);
memcpy(result + len1, s2, len2);
memcpy(result + len1 + len2, s3, len3);
memcpy(result + len1 + len2 + len3, s4, len4 + 1);
return result;
}
/*
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account.
*
* If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
* If expand is true, quotes are removed and escape sequences such as \r, \t,
* etc... are expanded. In this case, return NULL on parse errors.
*
* Returns the fractured words, which must be freed later using Words_Free,
* unless the returned Words.words was NULL.
*/
Words
Str_Words(const char *str, Boolean expand)
SubstringWords
Substring_Words(const char *str, bool expand)
{
size_t str_len;
char *words_buf;
size_t words_cap;
char **words;
Substring *words;
size_t words_len;
char inquote;
char *word_start;
@ -146,7 +130,7 @@ Str_Words(const char *str, Boolean expand)
words_buf = bmake_malloc(str_len + 1);
words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
words = bmake_malloc((words_cap + 1) * sizeof(char *));
words = bmake_malloc((words_cap + 1) * sizeof(words[0]));
/*
* copy the string; at the same time, parse backslashes,
@ -205,17 +189,24 @@ Str_Words(const char *str, Boolean expand)
*word_end++ = '\0';
if (words_len == words_cap) {
size_t new_size;
words_cap *= 2; /* ramp up fast */
new_size = (words_cap + 1) * sizeof(char *);
words_cap *= 2;
new_size = (words_cap + 1) * sizeof(words[0]);
words = bmake_realloc(words, new_size);
}
words[words_len++] = word_start;
words[words_len++] =
Substring_Init(word_start, word_end - 1);
word_start = NULL;
if (ch == '\n' || ch == '\0') {
if (expand && inquote != '\0') {
SubstringWords res;
free(words);
free(words_buf);
return (Words){ NULL, 0, NULL };
res.words = NULL;
res.len = 0;
res.freeIt = NULL;
return res;
}
goto done;
}
@ -262,8 +253,40 @@ Str_Words(const char *str, Boolean expand)
*word_end++ = ch;
}
done:
words[words_len] = NULL; /* useful for argv */
return (Words){ words, words_len, words_buf };
words[words_len] = Substring_Init(NULL, NULL); /* useful for argv */
{
SubstringWords result;
result.words = words;
result.len = words_len;
result.freeIt = words_buf;
return result;
}
}
Words
Str_Words(const char *str, bool expand)
{
SubstringWords swords;
Words words;
size_t i;
swords = Substring_Words(str, expand);
if (swords.words == NULL) {
words.words = NULL;
words.len = 0;
words.freeIt = NULL;
return words;
}
words.words = bmake_malloc((swords.len + 1) * sizeof(words.words[0]));
words.len = swords.len;
words.freeIt = swords.freeIt;
for (i = 0; i < swords.len + 1; i++)
words.words[i] = UNCONST(swords.words[i].start);
free(swords.words);
return words;
}
/*
@ -272,7 +295,7 @@ Str_Words(const char *str, Boolean expand)
*
* XXX: this function does not detect or report malformed patterns.
*/
Boolean
bool
Str_Match(const char *str, const char *pat)
{
for (;;) {
@ -284,7 +307,7 @@ Str_Match(const char *str, const char *pat)
if (*pat == '\0')
return *str == '\0';
if (*str == '\0' && *pat != '*')
return FALSE;
return false;
/*
* A '*' in the pattern matches any substring. We handle this
@ -295,13 +318,13 @@ Str_Match(const char *str, const char *pat)
while (*pat == '*')
pat++;
if (*pat == '\0')
return TRUE;
return true;
while (*str != '\0') {
if (Str_Match(str, pat))
return TRUE;
return true;
str++;
}
return FALSE;
return false;
}
/* A '?' in the pattern matches any single character. */
@ -315,14 +338,14 @@ Str_Match(const char *str, const char *pat)
* character lists, the backslash is an ordinary character.
*/
if (*pat == '[') {
Boolean neg = pat[1] == '^';
bool neg = pat[1] == '^';
pat += neg ? 2 : 1;
for (;;) {
if (*pat == ']' || *pat == '\0') {
if (neg)
break;
return FALSE;
return false;
}
/*
* XXX: This naive comparison makes the
@ -347,7 +370,7 @@ Str_Match(const char *str, const char *pat)
pat++;
}
if (neg && *pat != ']' && *pat != '\0')
return FALSE;
return false;
while (*pat != ']' && *pat != '\0')
pat++;
if (*pat == '\0')
@ -362,11 +385,11 @@ Str_Match(const char *str, const char *pat)
if (*pat == '\\') {
pat++;
if (*pat == '\0')
return FALSE;
return false;
}
if (*pat != *str)
return FALSE;
return false;
thisCharOK:
pat++;

366
str.h Normal file
View File

@ -0,0 +1,366 @@
/* $NetBSD: str.h,v 1.9 2021/05/30 21:16:54 rillig Exp $ */
/*
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Memory-efficient string handling.
*/
/* A read-only string that may need to be freed after use. */
typedef struct FStr {
const char *str;
void *freeIt;
} FStr;
/* A modifiable string that may need to be freed after use. */
typedef struct MFStr {
char *str;
void *freeIt;
} MFStr;
/* A read-only range of a character array, NOT null-terminated. */
typedef struct Substring {
const char *start;
const char *end;
} Substring;
/*
* Builds a string, only allocating memory if the string is different from the
* expected string.
*/
typedef struct LazyBuf {
char *data;
size_t len;
size_t cap;
const char *expected;
void *freeIt;
} LazyBuf;
/* The result of splitting a string into words. */
typedef struct Words {
char **words;
size_t len;
void *freeIt;
} Words;
/* The result of splitting a string into words. */
typedef struct SubstringWords {
Substring *words;
size_t len;
void *freeIt;
} SubstringWords;
MAKE_INLINE FStr
FStr_Init(const char *str, void *freeIt)
{
FStr fstr;
fstr.str = str;
fstr.freeIt = freeIt;
return fstr;
}
/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
FStr_InitOwn(char *str)
{
return FStr_Init(str, str);
}
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
return FStr_Init(str, NULL);
}
MAKE_INLINE void
FStr_Done(FStr *fstr)
{
free(fstr->freeIt);
#ifdef CLEANUP
fstr->str = NULL;
fstr->freeIt = NULL;
#endif
}
MAKE_INLINE MFStr
MFStr_Init(char *str, void *freeIt)
{
MFStr mfstr;
mfstr.str = str;
mfstr.freeIt = freeIt;
return mfstr;
}
/* Return a string that is the sole owner of str. */
MAKE_INLINE MFStr
MFStr_InitOwn(char *str)
{
return MFStr_Init(str, str);
}
/* Return a string that refers to the shared str. */
MAKE_INLINE MFStr
MFStr_InitRefer(char *str)
{
return MFStr_Init(str, NULL);
}
MAKE_INLINE void
MFStr_Done(MFStr *mfstr)
{
free(mfstr->freeIt);
#ifdef CLEANUP
mfstr->str = NULL;
mfstr->freeIt = NULL;
#endif
}
MAKE_STATIC Substring
Substring_Init(const char *start, const char *end)
{
Substring sub;
sub.start = start;
sub.end = end;
return sub;
}
MAKE_INLINE Substring
Substring_InitStr(const char *str)
{
return Substring_Init(str, str + strlen(str));
}
MAKE_STATIC size_t
Substring_Length(Substring sub)
{
return (size_t)(sub.end - sub.start);
}
MAKE_STATIC bool
Substring_IsEmpty(Substring sub)
{
return sub.start == sub.end;
}
MAKE_INLINE bool
Substring_Equals(Substring sub, const char *str)
{
size_t len = strlen(str);
return Substring_Length(sub) == len &&
memcmp(sub.start, str, len) == 0;
}
MAKE_STATIC Substring
Substring_Sub(Substring sub, size_t start, size_t end)
{
assert(start <= Substring_Length(sub));
assert(end <= Substring_Length(sub));
return Substring_Init(sub.start + start, sub.start + end);
}
MAKE_STATIC bool
Substring_HasPrefix(Substring sub, Substring prefix)
{
return Substring_Length(sub) >= Substring_Length(prefix) &&
memcmp(sub.start, prefix.start, Substring_Length(prefix)) == 0;
}
MAKE_STATIC bool
Substring_HasSuffix(Substring sub, Substring suffix)
{
size_t suffixLen = Substring_Length(suffix);
return Substring_Length(sub) >= suffixLen &&
memcmp(sub.end - suffixLen, suffix.start, suffixLen) == 0;
}
/* Returns an independent, null-terminated copy of the substring. */
MAKE_STATIC FStr
Substring_Str(Substring sub)
{
if (Substring_IsEmpty(sub))
return FStr_InitRefer("");
return FStr_InitOwn(bmake_strsedup(sub.start, sub.end));
}
MAKE_STATIC const char *
Substring_SkipFirst(Substring sub, char ch)
{
const char *p;
for (p = sub.start; p != sub.end; p++)
if (*p == ch)
return p + 1;
return sub.start;
}
MAKE_STATIC const char *
Substring_LastIndex(Substring sub, char ch)
{
const char *p;
for (p = sub.end; p != sub.start; p--)
if (p[-1] == ch)
return p - 1;
return NULL;
}
MAKE_STATIC Substring
Substring_Dirname(Substring pathname)
{
const char *p;
for (p = pathname.end; p != pathname.start; p--)
if (p[-1] == '/')
return Substring_Init(pathname.start, p - 1);
return Substring_InitStr(".");
}
MAKE_STATIC Substring
Substring_Basename(Substring pathname)
{
const char *p;
for (p = pathname.end; p != pathname.start; p--)
if (p[-1] == '/')
return Substring_Init(p, pathname.end);
return pathname;
}
MAKE_STATIC void
LazyBuf_Init(LazyBuf *buf, const char *expected)
{
buf->data = NULL;
buf->len = 0;
buf->cap = 0;
buf->expected = expected;
buf->freeIt = NULL;
}
MAKE_INLINE void
LazyBuf_Done(LazyBuf *buf)
{
free(buf->freeIt);
}
MAKE_STATIC void
LazyBuf_Add(LazyBuf *buf, char ch)
{
if (buf->data != NULL) {
if (buf->len == buf->cap) {
buf->cap *= 2;
buf->data = bmake_realloc(buf->data, buf->cap);
}
buf->data[buf->len++] = ch;
} else if (ch == buf->expected[buf->len]) {
buf->len++;
return;
} else {
buf->cap = buf->len + 16;
buf->data = bmake_malloc(buf->cap);
memcpy(buf->data, buf->expected, buf->len);
buf->data[buf->len++] = ch;
}
}
MAKE_STATIC void
LazyBuf_AddStr(LazyBuf *buf, const char *str)
{
const char *p;
for (p = str; *p != '\0'; p++)
LazyBuf_Add(buf, *p);
}
MAKE_STATIC void
LazyBuf_AddBytesBetween(LazyBuf *buf, const char *start, const char *end)
{
const char *p;
for (p = start; p != end; p++)
LazyBuf_Add(buf, *p);
}
MAKE_INLINE void
LazyBuf_AddSubstring(LazyBuf *buf, Substring sub)
{
LazyBuf_AddBytesBetween(buf, sub.start, sub.end);
}
MAKE_STATIC Substring
LazyBuf_Get(const LazyBuf *buf)
{
const char *start = buf->data != NULL ? buf->data : buf->expected;
return Substring_Init(start, start + buf->len);
}
MAKE_STATIC FStr
LazyBuf_DoneGet(LazyBuf *buf)
{
if (buf->data != NULL) {
LazyBuf_Add(buf, '\0');
return FStr_InitOwn(buf->data);
}
return Substring_Str(LazyBuf_Get(buf));
}
Words Str_Words(const char *, bool);
MAKE_INLINE void
Words_Free(Words w)
{
free(w.words);
free(w.freeIt);
}
SubstringWords Substring_Words(const char *, bool);
MAKE_INLINE void
SubstringWords_Free(SubstringWords w)
{
free(w.words);
free(w.freeIt);
}
char *str_concat2(const char *, const char *);
char *str_concat3(const char *, const char *, const char *);
bool Str_Match(const char *, const char *);

71
suff.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $ */
/* $NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -77,7 +77,8 @@
*
* Suff_End Clean up the module.
*
* Suff_DoPaths Extend the search path of each suffix to include the
* Suff_ExtendPaths
* Extend the search path of each suffix to include the
* default search path.
*
* Suff_ClearSuffixes
@ -114,7 +115,7 @@
#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
MAKE_RCSID("$NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $");
MAKE_RCSID("$NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@ -328,7 +329,7 @@ Suffix_TrimSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
suff->name, suff->nameLen);
}
static Boolean
static bool
Suffix_IsSuffix(const Suffix *suff, size_t nameLen, const char *nameEnd)
{
return Suffix_TrimSuffix(suff, nameLen, nameEnd) != NULL;
@ -509,9 +510,9 @@ Suff_ClearSuffixes(void)
* suffixes (the source ".c" and the target ".o"). If there are no such
* suffixes, try a single-suffix transformation as well.
*
* Return TRUE if the string is a valid transformation.
* Return true if the string is a valid transformation.
*/
static Boolean
static bool
ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
{
SuffixListNode *ln;
@ -536,7 +537,7 @@ ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
if (targ != NULL) {
*out_src = src;
*out_targ = targ;
return TRUE;
return true;
}
}
}
@ -554,17 +555,17 @@ ParseTransform(const char *str, Suffix **out_src, Suffix **out_targ)
*/
*out_src = single;
*out_targ = nullSuff;
return TRUE;
return true;
}
return FALSE;
return false;
}
/*
* Return TRUE if the given string is a transformation rule, that is, a
* Return true if the given string is a transformation rule, that is, a
* concatenation of two known suffixes such as ".c.o" or a single suffix
* such as ".o".
*/
Boolean
bool
Suff_IsTransform(const char *str)
{
Suffix *src, *targ;
@ -616,7 +617,7 @@ Suff_AddTransform(const char *name)
{
/* TODO: Avoid the redundant parsing here. */
Boolean ok = ParseTransform(name, &srcSuff, &targSuff);
bool ok = ParseTransform(name, &srcSuff, &targSuff);
assert(ok);
(void)ok;
}
@ -725,11 +726,11 @@ RebuildGraph(GNode *transform, Suffix *suff)
* becomes the main target.
*
* Results:
* TRUE iff a new main target has been selected.
* true iff a new main target has been selected.
*/
static Boolean
static bool
UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
Boolean *inout_removedMain)
bool *inout_removedMain)
{
Suffix *srcSuff, *targSuff;
char *ptr;
@ -740,20 +741,20 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
*inout_main = target;
Targ_SetMain(target);
/*
* XXX: Why could it be a good idea to return TRUE here?
* XXX: Why could it be a good idea to return true here?
* The main task of this function is to turn ordinary nodes
* into transformations, no matter whether or not a new .MAIN
* node has been found.
*/
/*
* XXX: Even when changing this to FALSE, none of the existing
* XXX: Even when changing this to false, none of the existing
* unit tests fails.
*/
return TRUE;
return true;
}
if (target->type == OP_TRANSFORM)
return FALSE;
return false;
/*
* XXX: What about a transformation ".cpp.c"? If ".c" is added as
@ -762,7 +763,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
*/
ptr = strstr(target->name, suff->name);
if (ptr == NULL)
return FALSE;
return false;
/*
* XXX: In suff-rebuild.mk, in the line '.SUFFIXES: .c .b .a', this
@ -773,14 +774,14 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
* amounts of memory.
*/
if (ptr == target->name)
return FALSE;
return false;
if (ParseTransform(target->name, &srcSuff, &targSuff)) {
if (*inout_main == target) {
DEBUG1(MAKE,
"Setting main node from \"%s\" back to null\n",
target->name);
*inout_removedMain = TRUE;
*inout_removedMain = true;
*inout_main = NULL;
Targ_SetMain(NULL);
}
@ -795,7 +796,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
srcSuff->name, targSuff->name);
Relate(srcSuff, targSuff);
}
return FALSE;
return false;
}
/*
@ -808,7 +809,7 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
static void
UpdateTargets(GNode **inout_main, Suffix *suff)
{
Boolean removedMain = FALSE;
bool removedMain = false;
GNodeListNode *ln;
for (ln = Targ_List()->first; ln != NULL; ln = ln->next) {
@ -876,7 +877,7 @@ Suff_GetPath(const char *sname)
* ".LIBS" and the flag is '-L'.
*/
void
Suff_DoPaths(void)
Suff_ExtendPaths(void)
{
SuffixListNode *ln;
char *flags;
@ -1061,7 +1062,7 @@ CandidateList_AddCandidatesFor(CandidateList *list, Candidate *cand)
* Free the first candidate in the list that is not referenced anymore.
* Return whether a candidate was removed.
*/
static Boolean
static bool
RemoveCandidate(CandidateList *srcs)
{
CandidateListNode *ln;
@ -1097,7 +1098,7 @@ RemoveCandidate(CandidateList *srcs)
Lst_Remove(srcs, ln);
free(src->file);
free(src);
return TRUE;
return true;
}
#ifdef DEBUG_SRC
else {
@ -1108,7 +1109,7 @@ RemoveCandidate(CandidateList *srcs)
#endif
}
return FALSE;
return false;
}
/* Find the first existing file/target in srcs. */
@ -1282,7 +1283,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* add those nodes to the members list.
*
* Unfortunately, we can't use Str_Words because it doesn't understand about
* variable specifications with spaces in them.
* variable expressions with spaces in them.
*/
static void
ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
@ -1309,7 +1310,7 @@ ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
const char *nested_p = cp;
FStr junk;
(void)Var_Parse(&nested_p, pgn, VARE_NONE, &junk);
(void)Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY, &junk);
/* TODO: handle errors */
if (junk.str == var_Error) {
Parse_Error(PARSE_FATAL,
@ -1380,7 +1381,7 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
}
DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
(void)Var_Subst(cgn->name, pgn, VARE_WANTRES | VARE_UNDEFERR, &cp);
(void)Var_Subst(cgn->name, pgn, VARE_UNDEFERR, &cp);
/* TODO: handle errors */
{
@ -1494,9 +1495,9 @@ Suff_FindPath(GNode *gn)
* the sources for the transformation rule.
*
* Results:
* TRUE if successful, FALSE if not.
* true if successful, false if not.
*/
static Boolean
static bool
ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
{
GNodeListNode *ln;
@ -1515,7 +1516,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
/* This can happen when linking an OP_MEMBER and OP_ARCHV node. */
if (gn == NULL)
return FALSE;
return false;
DEBUG3(SUFF, "\tapplying %s -> %s to \"%s\"\n",
ssuff->name, tsuff->name, tgn->name);
@ -1540,7 +1541,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
*/
Lst_Append(&sgn->implicitParents, tgn);
return TRUE;
return true;
}
/*

14
targ.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $ */
/* $NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -93,7 +93,7 @@
* Targ_FindList Given a list of names, find nodes for all
* of them, creating them as necessary.
*
* Targ_Precious Return TRUE if the target is precious and
* Targ_Precious Return true if the target is precious and
* should not be removed if we are interrupted.
*
* Targ_Propagate Propagate information between related nodes.
@ -113,7 +113,7 @@
#include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $");
MAKE_RCSID("$NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $");
/*
* All target nodes that appeared on the left-hand side of one of the
@ -246,7 +246,7 @@ GNode_Free(void *gnp)
* 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 scopes (VAR_CMD,
* XXX: The GNodes that are only used as variable scopes (SCOPE_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
@ -283,7 +283,7 @@ Targ_FindNode(const char *name)
GNode *
Targ_GetNode(const char *name)
{
Boolean isNew;
bool isNew;
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
if (!isNew)
return HashEntry_Get(he);
@ -347,7 +347,7 @@ Targ_FindList(GNodeList *gns, StringList *names)
}
/* See if the given target is precious. */
Boolean
bool
Targ_Precious(const GNode *gn)
{
/* XXX: Why are '::' targets precious? */
@ -410,7 +410,7 @@ Targ_FmtTime(time_t tm)
static char buf[128];
struct tm *parts = localtime(&tm);
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
(void)strftime(buf, sizeof buf, "%H:%M:%S %b %d, %Y", parts);
return buf;
}

View File

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.143 2021/02/06 18:31:30 sjg Exp $
# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $
#
# $NetBSD: Makefile,v 1.269 2021/02/06 18:26:03 sjg Exp $
# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $
#
# Unit tests for make(1)
#
@ -203,7 +203,9 @@ TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
TESTS+= job-output-null
TESTS+= jobs-empty-commands
TESTS+= jobs-empty-commands-error
TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make
@ -228,6 +230,7 @@ TESTS+= opt-debug-curdir
TESTS+= opt-debug-cond
TESTS+= opt-debug-dir
TESTS+= opt-debug-errors
TESTS+= opt-debug-errors-jobs
TESTS+= opt-debug-file
TESTS+= opt-debug-for
TESTS+= opt-debug-graph1
@ -321,6 +324,7 @@ TESTS+= var-class-env
TESTS+= var-class-global
TESTS+= var-class-local
TESTS+= var-class-local-legacy
TESTS+= var-eval-short
TESTS+= var-op
TESTS+= var-op-append
TESTS+= var-op-assign
@ -347,6 +351,7 @@ TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime
TESTS+= varmod-loop
TESTS+= varmod-loop-varname
TESTS+= varmod-match
TESTS+= varmod-match-escape
TESTS+= varmod-no-match
@ -363,6 +368,7 @@ TESTS+= varmod-select-words
TESTS+= varmod-shell
TESTS+= varmod-subst
TESTS+= varmod-subst-regex
TESTS+= varmod-sun-shell
TESTS+= varmod-sysv
TESTS+= varmod-tail
TESTS+= varmod-to-abs
@ -484,6 +490,7 @@ 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.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,'
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}
@ -494,11 +501,12 @@ SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
# The "-q" may be there or not, see jobs.c, variable shells.
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
SED_CMDS.opt-debug-lint+= ${STD_SED_CMDS.regex}
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
# For Compat_RunCommand, useShell == FALSE.
# For Compat_RunCommand, useShell == false.
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
# For Compat_RunCommand, useShell == TRUE.
# For Compat_RunCommand, useShell == true.
SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
@ -509,8 +517,7 @@ SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
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.varmod-subst-regex+= ${STD_SED_CMDS.regex}
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
@ -590,6 +597,11 @@ 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}: ,,'
# The actual error messages for a failed regcomp or regexec differ between the
# implementations.
STD_SED_CMDS.regex= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
# End of the configuration helpers section.
.-include "Makefile.inc"
@ -639,8 +651,10 @@ _MKMSG_TEST= :
.if ${.OBJDIR} != ${.CURDIR}
# easy
TMPDIR:= ${.OBJDIR}/tmp
.elif defined(TMPDIR)
TMPDIR:= ${TMPDIR}/uid${.MAKE.UID}
.else
TMPDIR:= ${TMPDIR:U/tmp}/uid${.MAKE.UID}
TMPDIR:= /tmp/uid${.MAKE.UID}
.endif
# make sure it exists
.if !exist(${TMPDIR})

View File

@ -1,4 +1,4 @@
# $NetBSD: archive.mk,v 1.11 2020/11/15 14:07:53 rillig Exp $
# $NetBSD: archive.mk,v 1.12 2021/04/09 14:42:00 christos Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
@ -8,11 +8,11 @@
# several other tests.
ARCHIVE= libprog.a
FILES= archive.mk modmisc.mk varmisc.mk
FILES= archive.mk archive-suffix.mk modmisc.mk ternary.mk varmisc.mk
all:
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
@cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
@cd ${MAKEFILE:H} && cp ${FILES} ${.CURDIR}
.endif
# The following targets create and remove files. The filesystem cache in
# dir.c would probably not handle this correctly, therefore each of the

View File

@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier 'Z'
make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0

View File

@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier 'Z'
make: Unknown modifier "Z"
: unknown-modifier
: end
exit status 2

View File

@ -3,7 +3,7 @@ make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier 'Z'
make: Unknown modifier "Z"
: unknown-modifier eol
: end eol
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
@ -42,7 +42,7 @@ WORD= word
.endif
# The :U modifier modifies expressions based on undefined variables
# (VAR_JUNK) by adding the VAR_KEEP flag, which marks the expression
# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression
# as "being interesting enough to be further processed".
#
.if empty(UNDEF:S,^$,value,W:Ufallback)
@ -93,8 +93,8 @@ WORD= word
# neither leading nor trailing spaces are trimmed in the argument of the
# function. If the spaces were trimmed, the variable name would be "" and
# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse
# without VARE_UNDEFERR, the value of the undefined variable is returned as
# an empty string.
# without VARE_UNDEFERR, the value of the undefined variable is
# returned as an empty string.
${:U }= space
.if empty( )
. error
@ -168,7 +168,7 @@ ${:U WORD }= variable name with spaces
# parsing it, this unrealistic variable name should have done no harm.
#
# The variable expression was expanded though, and this was wrong. The
# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
# expansion was done without VARE_WANTRES (called VARF_WANTRES back
# then) though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
# $NetBSD: cond-func-make-main.mk,v 1.2 2021/04/04 10:13:09 rillig Exp $
#
# Test how accurately the make() function in .if conditions reflects
# what is actually made.
@ -33,7 +33,7 @@ first-main-target:
# the line. This implies that several main targets can be set at the name
# time, but they have to be in the same dependency group.
#
# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
# See ParseDependencyTargetSpecial, branch SP_MAIN.
.MAIN: dot-main-target-1a dot-main-target-1b
.if !make(dot-main-target-1a)
@ -47,7 +47,7 @@ dot-main-target-{1,2}{a,b}:
: Making ${.TARGET}.
# At this point, the list of targets to be made (opts.create) is not empty
# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
# anymore. ParseDependencyTargetSpecial therefore treats the .MAIN as if
# it were an ordinary target. Since .MAIN is not listed as a dependency
# anywhere, it is not made.
.if target(.MAIN)

View File

@ -1,4 +1,4 @@
make: Bad conditional expression ` != "no"' in != "no"?:
make: Bad conditional expression ' != "no"' in ' != "no"?:'
yes
no
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@ -13,8 +13,11 @@
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
# expressions, undefined variables were allowed.
#
# See also:
# var-eval-short.mk, for short-circuited variable modifiers
# The && operator.
# The && operator:
.if 0 && ${echo "unexpected and" 1>&2 :L:sh}
.endif
@ -86,7 +89,7 @@ VAR= # empty again, for the following tests
. warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
.endif
# The || operator.
# The || operator:
.if 1 || ${echo "unexpected or" 1>&2 :L:sh}
.endif
@ -208,9 +211,4 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
. error
.endif
# TODO: Test each modifier to make sure it is skipped when it is irrelevant
# for the result. Since this test is already quite long, do that in another
# test.
all:
@:;:

View File

@ -1,4 +1,4 @@
make: "cond-token-string.mk" line 13: Unknown modifier 'Z'
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} == "")

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-var.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
# $NetBSD: cond-token-var.mk,v 1.6 2021/04/25 21:05:38 rillig Exp $
#
# Tests for variable expressions in .if conditions.
#
@ -46,3 +46,24 @@ DEF= defined
# Since the expression is defined now, it doesn't generate any parse error.
.if ${UNDEF:U}
.endif
# If the value of the variable expression is a number, it is compared against
# zero.
.if ${:U0}
. error
.endif
.if !${:U1}
. error
.endif
# If the value of the variable expression is not a number, any non-empty
# value evaluates to true, even if there is only whitespace.
.if ${:U}
. error
.endif
.if !${:U }
. error
.endif
.if !${:Uanything}
. error
.endif

View File

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

View File

@ -1,4 +1,4 @@
# $NetBSD: counter-append.mk,v 1.4 2020/10/17 16:57:17 rillig Exp $
# $NetBSD: counter-append.mk,v 1.5 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::+= variable modifier.
@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::+=a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}

View File

@ -1,4 +1,4 @@
# $NetBSD: counter.mk,v 1.5 2020/10/17 16:57:17 rillig Exp $
# $NetBSD: counter.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Demonstrates how to let make count the number of times a variable
# is actually accessed, using the ::= variable modifier.
@ -15,7 +15,7 @@ COUNTER= # zero
NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]}
# This variable is first set to empty and then expanded.
# See parse.c, function Parse_DoVar, keyword "!Var_Exists".
# See parse.c, function Parse_Var, keyword "!Var_Exists".
A:= ${NEXT}
B:= ${NEXT}
C:= ${NEXT}

View File

@ -1,4 +1,4 @@
# $NetBSD: dep-var.mk,v 1.5 2020/09/13 20:04:26 rillig Exp $
# $NetBSD: dep-var.mk,v 1.6 2021/04/04 10:13:09 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
@ -79,7 +79,7 @@ all: $$$$)
# undefined.
#
# Since 2020-09-13, this generates a parse error in lint mode (-dL), but not
# in normal mode since ParseDoDependency does not handle any errors after
# in normal mode since ParseDependency does not handle any errors after
# calling Var_Parse.
undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
@echo ${.TARGET:Q}

View File

@ -1,10 +1,10 @@
Global:delete DOLLAR (not found)
Command:DOLLAR = $$$$
Global:.MAKEOVERRIDES = VAR DOLLAR
Command: DOLLAR = $$$$
Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"
Var_Parse: ${DOLLAR} != "\$\$" with VARE_UNDEFERR|VARE_WANTRES
Var_Parse: ${DOLLAR} != "\$\$" (eval-defined)
lhs = "$$", rhs = "$$", op = !=
Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
Global:.MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d
Global: .MAKEFLAGS = -r -k -D VAR -D VAR -d cv -d 0
make: Unterminated quoted string [make VAR=initial UNBALANCED=']
exit status 0

View File

@ -1 +1,4 @@
: 'Making two out of one.'
: 'Making three out of two.'
: 'Making all out of three.'
exit status 0

View File

@ -1,8 +1,18 @@
# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: deptgt-order.mk,v 1.3 2021/06/17 15:25:33 rillig Exp $
#
# Tests for the special target .ORDER in dependency declarations.
# TODO: Implementation
all one two three: .PHONY
all:
@:;
two: one
: 'Making $@ out of $>.'
three: two
: 'Making $@ out of $>.'
# This .ORDER creates a circular dependency since 'three' depends on 'one'
# but 'one' is supposed to be built after 'three'.
.ORDER: three one
# XXX: The circular dependency should be detected here.
all: three
: 'Making $@ out of $>.'

View File

@ -1,14 +1,14 @@
make: "deptgt.mk" line 10: warning: Extra target ignored
make: "deptgt.mk" line 28: Unassociated shell command ": command3 # parse error, since targets == NULL"
ParseReadLine (34): '${:U}: empty-source'
ParseDoDependency(: empty-source)
ParseDependency(: empty-source)
ParseReadLine (35): ' : command for empty targets list'
ParseReadLine (36): ': empty-source'
ParseDoDependency(: empty-source)
ParseDependency(: empty-source)
ParseReadLine (37): ' : command for empty targets list'
ParseReadLine (38): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0)
make: "deptgt.mk" line 46: Unknown modifier 'Z'
ParseDependency(.MAKEFLAGS: -d0)
make: "deptgt.mk" line 46: Unknown modifier "Z"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
# $NetBSD: deptgt.mk,v 1.11 2021/04/04 10:13:09 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@ -12,7 +12,7 @@
# The following lines demonstrate how 'targets' is set and reset during
# parsing of dependencies. To see it in action, set breakpoints in:
#
# ParseDoDependency at the beginning
# ParseDependency at the beginning
# FinishDependencyGroup at "targets = NULL"
# Parse_File at "Lst_Free(targets)"
# Parse_File at "targets = Lst_New()"

View File

@ -1,56 +1,56 @@
ParseReadLine (21): 'UT_VAR= <${REF}>'
Global:UT_VAR = <${REF}>
Global: UT_VAR = <${REF}>
ParseReadLine (28): '.export UT_VAR'
Global:.MAKE.EXPORTED = UT_VAR
Global: .MAKE.EXPORTED = UT_VAR
ParseReadLine (32): ': ${UT_VAR:N*}'
Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
Pattern[UT_VAR] for [<>] is [*]
Var_Parse: ${UT_VAR:N*} (eval-defined)
Var_Parse: ${REF}> (eval-defined)
Evaluating modifier ${UT_VAR:N...} on value "<>"
Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
ParseDoDependency(: )
Result of ${UT_VAR:N*} is ""
ParseDependency(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES
Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF)
Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" (eval-defined)
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
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)
Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
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, VES_DEF)
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
Var_Parse: ${UT_VAR} (eval)
Var_Parse: ${REF}> (eval)
Result of ${:!echo "\$UT_VAR"!} is "<>" (eval-defined, defined)
lhs = "<>", rhs = "<>", op = !=
ParseReadLine (49): ': ${UT_VAR:N*}'
Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES
Var_Parse: ${REF}> with VARE_UNDEFERR|VARE_WANTRES
Applying ${UT_VAR:N...} to "<>" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
Pattern[UT_VAR] for [<>] is [*]
ParseReadLine (50): ': ${UT_VAR:N*}'
Var_Parse: ${UT_VAR:N*} (eval-defined)
Var_Parse: ${REF}> (eval-defined)
Evaluating modifier ${UT_VAR:N...} on value "<>"
Pattern for ':N' is "*"
ModifyWords: split "<>" into 1 words
Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXPORT, none)
ParseDoDependency(: )
ParseReadLine (53): 'REF= defined'
Global:REF = defined
Result of ${UT_VAR:N*} is ""
ParseDependency(: )
ParseReadLine (54): '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, VES_UNDEF)
Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" (eval-defined)
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
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)
Result of ${.MAKE.EXPORTED:O} is "UT_VAR" (VARE_WANTRES, none, none)
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, VES_DEF)
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_VAR"
Result of ${.MAKE.EXPORTED:u} is "UT_VAR"
Var_Parse: ${UT_VAR} (eval)
Var_Parse: ${REF}> (eval)
Result of ${:!echo "\$UT_VAR"!} is "<defined>" (eval-defined, defined)
lhs = "<defined>", rhs = "<defined>", op = !=
ParseReadLine (61): 'all:'
ParseDoDependency(all:)
Global:.ALLTARGETS = all
ParseReadLine (62): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0)
Global:.MAKEFLAGS = -r -k -d cpv -d
Global:.MAKEFLAGS = -r -k -d cpv -d 0
ParseReadLine (62): 'all:'
ParseDependency(all:)
Global: .ALLTARGETS = all
ParseReadLine (63): '.MAKEFLAGS: -d0'
ParseDependency(.MAKEFLAGS: -d0)
Global: .MAKEFLAGS = -r -k -d cpv -d
Global: .MAKEFLAGS = -r -k -d cpv -d 0
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-export-impl.mk,v 1.1 2020/12/29 01:45:06 rillig Exp $
# $NetBSD: directive-export-impl.mk,v 1.3 2021/04/03 23:08:30 rillig Exp $
#
# Test for the implementation of exporting variables to child processes.
# This involves marking variables for export, actually exporting them,
@ -8,8 +8,8 @@
# Var_Export
# ExportVar
# VarExportedMode (global)
# VAR_EXPORTED (per variable)
# VAR_REEXPORT (per variable)
# VarFlags.exported (per variable)
# VarFlags.reexport (per variable)
# VarExportMode (per call of Var_Export and ExportVar)
: ${:U:sh} # side effect: initialize .SHELL
@ -22,13 +22,13 @@ UT_VAR= <${REF}>
# At this point, ExportVar("UT_VAR", VEM_PLAIN) is called. Since the
# variable value refers to another variable, ExportVar does not actually
# export the variable but only marks it as VAR_EXPORTED and VAR_REEXPORT.
# After that, ExportVars registers the variable name in .MAKE.EXPORTED.
# That's all for now.
# export the variable but only marks it as VarFlags.exported and
# VarFlags.reexport. After that, ExportVars registers the variable name in
# .MAKE.EXPORTED. That's all for now.
.export UT_VAR
# Evaluating this expression shows the variable flags in the debug log,
# which are VAR_EXPORTED|VAR_REEXPORT.
# The following expression has both flags 'exported' and 'reexport' set.
# These flags do not show up anywhere, not even in the debug log.
: ${UT_VAR:N*}
# At the last moment before actually forking off the child process for the
@ -43,9 +43,10 @@ UT_VAR= <${REF}>
. error
.endif
# Evaluating this expression shows the variable flags in the debug log,
# which are still VAR_EXPORTED|VAR_REEXPORT, which means that the variable
# is still marked as being re-exported for each child process.
# The following expression still has 'exported' and 'reexport' set.
# These flags do not show up anywhere though, not even in the debug log.
# These flags means that the variable is still marked as being re-exported
# for each child process.
: ${UT_VAR:N*}
# Now the referenced variable gets defined. This does not influence anything

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-export.mk,v 1.6 2020/12/13 01:07:54 rillig Exp $
# $NetBSD: directive-export.mk,v 1.8 2021/02/16 19:01:18 rillig Exp $
#
# Tests for the .export directive.
#
@ -28,8 +28,17 @@ VAR= value $$ ${INDIRECT}
. error
.endif
# No argument means to export all variables.
# No syntactical argument means to export all variables.
.export
# An empty argument means no additional variables to export.
.export ${:U}
# Trigger the "This isn't going to end well" in ExportVarEnv.
EMPTY_SHELL= ${:sh}
.export EMPTY_SHELL # only marked for export at this point
_!= :;: # Force the variable to be actually exported.
all:
@:;

View File

@ -13,7 +13,7 @@ make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for subst
make: "directive-for-errors.mk" line 64: missing `in' in for
make: "directive-for-errors.mk" line 66: warning: Should not be reached.
make: "directive-for-errors.mk" line 67: for-less endfor
make: "directive-for-errors.mk" line 73: Unknown modifier 'Z'
make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-errors.mk,v 1.1 2020/12/31 03:05:12 rillig Exp $
# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $
#
# Tests for error handling in .for loops.
@ -13,8 +13,8 @@
# XXX: The error message is misleading though. As of 2020-12-31, it says
# "Unknown directive "for"", but that directive is actually known. This is
# because ForEval does not detect the .for loop as such, so parsing
# continues in ParseLine > ParseDependency > ParseDoDependency >
# ParseDoDependencyTargets > ParseErrorNoDependency, and there the directive
# continues in ParseLine > ParseDependencyLine > ParseDependency >
# ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently.
.for/i in 1 2 3
. warning ${i}

View File

@ -1,12 +1,12 @@
For: end for 1
For: loop body:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable specification (expecting '}') for "" (value "!"") modifier U
make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
For: loop body:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable specification (expecting '}') for "" (value "!"\\") modifier U
make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 29: !"\\
For: end for 1
For: loop body:
@ -37,19 +37,19 @@ 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
make: "directive-for-escape.mk" line 67: begin<fallback>end
For: end for 1
For: loop body:
. info ${:U\$}
make: "directive-for-escape.mk" line 74: $
make: "directive-for-escape.mk" line 75: $
For: end for 1
For: loop body:
. info ${NUMBERS} ${:Ureplaced}
make: "directive-for-escape.mk" line 82: one two three replaced
make: "directive-for-escape.mk" line 83: one two three replaced
For: end for 1
For: loop body:
. info ${:Ureplaced}
make: "directive-for-escape.mk" line 92: replaced
make: "directive-for-escape.mk" line 93: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@ -62,14 +62,14 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
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
make: "directive-for-escape.mk" line 101: . $i: inner
make: "directive-for-escape.mk" line 102: . ${i}: inner
make: "directive-for-escape.mk" line 103: . ${i:M*}: inner
make: "directive-for-escape.mk" line 104: . $(i): inner
make: "directive-for-escape.mk" line 105: . $(i:M*): inner
make: "directive-for-escape.mk" line 106: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 107: . ${i\}}: inner}
make: "directive-for-escape.mk" line 108: . ${i2}: two
make: "directive-for-escape.mk" line 109: . ${i,}: comma
make: "directive-for-escape.mk" line 110: . adjacent: innerinnerinnerinner
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-escape.mk,v 1.6 2021/01/25 19:05:39 rillig Exp $
# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 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
@ -7,8 +7,8 @@
.MAKEFLAGS: -df
# Even though the .for loops takes quotes into account when splitting the
# string into words, the quotes don't need to be balances, as of 2020-12-31.
# Even though the .for loops take quotes into account when splitting the
# string into words, the quotes don't need to be balanced, as of 2020-12-31.
# This could be considered a bug.
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
@ -33,7 +33,7 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
#
# XXX: It is unexpected that the variable V gets expanded in the loop body.
# The double '$$' should prevent exactly this. Probably nobody was
# adventurous enough to use literal dollar signs in the values for a .for
# adventurous enough to use literal dollar signs in the values of a .for
# loop.
V= value
VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
@ -43,14 +43,14 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
# Try to cover the code for nested '{}' in for_var_len, without success.
#
# 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 value of the variable VALUES is not meant to be a variable expression.
# Instead, it is meant to represent literal text, the only escaping mechanism
# being that each '$' is written as '$$'.
#
# 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 '\$'?
# XXX: Where in the code does the '\$\$' get converted into a single '\$'?
.for i in ${VALUES}
. info $i
.endfor
@ -59,8 +59,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end}
#
# 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.
# variable expressions, braces and parentheses. (Compare ':M', ':S', and
# ':D' for details.) 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

View File

@ -16,7 +16,7 @@ make: "directive-for.mk" line 140: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{
make: "directive-for.mk" line 148: outer value value
make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
make: "directive-for.mk" line 154: Unknown modifier 'Z'
make: "directive-for.mk" line 154: Unknown modifier "Z"
make: "directive-for.mk" line 155: XXX: Not reached word1
make: "directive-for.mk" line 155: XXX: Not reached word3
make: Fatal errors encountered -- cannot continue

View File

@ -1,5 +1,6 @@
make: "directive-undef.mk" line 29: The .undef directive requires an argument
make: "directive-undef.mk" line 86: Unknown modifier 'Z'
make: "directive-undef.mk" line 86: Unknown modifier "Z"
make: "directive-undef.mk" line 103: warning: UT_EXPORTED is still listed in .MAKE.EXPORTED even though spaceit is not exported anymore.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-undef.mk,v 1.9 2020/12/22 20:10:21 rillig Exp $
# $NetBSD: directive-undef.mk,v 1.10 2021/02/16 18:02:19 rillig Exp $
#
# Tests for the .undef directive.
#
@ -86,5 +86,22 @@ ${DOLLAR}= dollar
.undef ${VARNAMES:L:Z}
UT_EXPORTED= exported-value
.export UT_EXPORTED
.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "exported-value"
. error
.endif
.if !${.MAKE.EXPORTED:MUT_EXPORTED}
. error
.endif
.undef UT_EXPORTED # XXX: does not update .MAKE.EXPORTED
.if ${:!echo "\${UT_EXPORTED:-not-exported}"!} != "not-exported"
. error
.endif
.if ${.MAKE.EXPORTED:MUT_EXPORTED}
. warning UT_EXPORTED is still listed in .MAKE.EXPORTED even though $\
it is not exported anymore.
.endif
all:
@:;

View File

@ -1,18 +1,18 @@
make: "directive-unexport-env.mk" line 13: Unknown directive "unexport-en"
make: "directive-unexport-env.mk" line 15: Unknown directive "unexport-environment"
Global:UT_EXPORTED = value
Global:UT_UNEXPORTED = value
Global:.MAKE.EXPORTED = UT_EXPORTED
Global: UT_EXPORTED = value
Global: UT_UNEXPORTED = value
Global: .MAKE.EXPORTED = UT_EXPORTED
make: "directive-unexport-env.mk" line 21: The directive .unexport-env does not take arguments
Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES
Applying ${.MAKE.EXPORTED:O} to "UT_EXPORTED" (VARE_WANTRES, none, none)
Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED" (VARE_WANTRES, none, none)
Applying ${.MAKE.EXPORTED:u} to "UT_EXPORTED" (VARE_WANTRES, none, none)
Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED" (VARE_WANTRES, none, none)
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_EXPORTED"
Result of ${.MAKE.EXPORTED:O} is "UT_EXPORTED"
Evaluating modifier ${.MAKE.EXPORTED:u} on value "UT_EXPORTED"
Result of ${.MAKE.EXPORTED:u} is "UT_EXPORTED"
Unexporting "UT_EXPORTED"
Global:delete .MAKE.EXPORTED
Global:.MAKEFLAGS = -r -k -d v -d
Global:.MAKEFLAGS = -r -k -d v -d 0
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -2,11 +2,11 @@ make: "directive.mk" line 9: Unknown directive "indented"
make: "directive.mk" line 10: Unknown directive "indented"
make: "directive.mk" line 11: Unknown directive "indented"
make: "directive.mk" line 15: Unknown directive "info"
Global:.info =
Global:.info = value
Global: .info =
Global: .info = value
make: "directive.mk" line 26: := value
Global:.MAKEFLAGS = -r -k -d v -d
Global:.MAKEFLAGS = -r -k -d v -d 0
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -9,7 +9,7 @@ make: "include-subsub.mk" line 5: subsub-ok
in .for loop from include-sub.mk:29
in .include from include-main.mk:27
ParseReadLine (6): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0)
ParseDependency(.MAKEFLAGS: -d0)
make: "include-sub.mk" line 38: sub-after-ok
make: "include-sub.mk" line 45: sub-after-for-ok
make: "include-main.mk" line 30: main-after-ok

View File

@ -0,0 +1,4 @@
hello
hello
hello world without newline, hello world without newline, hello world without newline.
exit status 0

View File

@ -0,0 +1,32 @@
# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $
#
# Test how null bytes in the output of a command are handled. Make processes
# them using null-terminated strings, which may cut off some of the output.
#
# As of 2021-04-15, make handles null bytes from the child process
# inconsistently. It's an edge case though since typically the child
# processes output text.
.MAKEFLAGS: -j1 # force jobs mode
all: .PHONY
# The null byte from the command output is kept as-is.
# See CollectOutput, which looks like it intended to replace these
# null bytes with simple spaces.
@printf 'hello\0world%s\n' ''
# Give the parent process a chance to see the above output, but not
# yet the output from the next printf command.
@sleep 1
# All null bytes from the command output are kept as-is.
@printf 'hello\0world%s\n' '' '' '' '' '' ''
@sleep 1
# The null bytes are replaced with spaces since they are not followed
# by a newline.
#
# The three null bytes in a row test whether this output is
# compressed to a single space like in DebugFailedTarget. It isn't.
@printf 'hello\0world\0without\0\0\0newline%s' ', ' ', ' '.'

View File

@ -0,0 +1,5 @@
: 'Making existing-target out of nothing.'
make: don't know how to make nonexistent-target (continuing)
make: stopped in unit-tests
exit status 2

View File

@ -0,0 +1,19 @@
# $NetBSD: jobs-empty-commands-error.mk,v 1.1 2021/06/16 09:39:48 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.
#
# After 2021-01-29, before job.c 1.435 2021-06-16, targets that could not be
# made led to longer error messages than necessary.
.MAKEFLAGS: -j1
all: existing-target
existing-target:
: 'Making $@ out of nothing.'
all: nonexistent-target
: 'Not reached'

View File

@ -1,143 +1,136 @@
mod-unknown-direct:
want: Unknown modifier 'Z'
make: Unknown modifier 'Z'
make: Unknown modifier "Z"
VAR:Z=before--after
mod-unknown-indirect:
want: Unknown modifier 'Z'
make: Unknown modifier 'Z'
make: Unknown modifier "Z"
VAR:Z=before-inner}-after
unclosed-direct:
want: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S
want: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
make: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
VAR:S,V,v,=Thevariable
unclosed-indirect:
want: Unclosed variable specification after complex modifier (expecting '}') for VAR
make: Unclosed variable specification after complex modifier (expecting '}') for VAR
want: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
make: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
VAR:S,V,v,=Thevariable
unfinished-indirect:
want: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
VAR:S,V,v=
unfinished-loop:
want: Unfinished modifier for UNDEF ('@' missing)
make: Unfinished modifier for UNDEF ('@' missing)
make: Unfinished modifier for "UNDEF" ('@' missing)
want: Unfinished modifier for UNDEF ('@' missing)
make: Unfinished modifier for UNDEF ('@' missing)
make: Unfinished modifier for "UNDEF" ('@' missing)
1 2 3
loop-close:
make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @
make: Unclosed variable expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
1}... 2}... 3}...
1}... 2}... 3}...
words:
want: Unfinished modifier for UNDEF (']' missing)
make: Unfinished modifier for UNDEF (']' missing)
make: Unfinished modifier for "UNDEF" (']' missing)
want: Unfinished modifier for UNDEF (']' missing)
make: Unfinished modifier for UNDEF (']' missing)
make: Unfinished modifier for "UNDEF" (']' missing)
13=
make: Bad modifier `:[123451234512345123451234512345]' for UNDEF
make: Bad modifier ":[123451234512345123451234512345]" for variable "UNDEF"
12345=S,^ok,:S,^3ok,}
exclam:
want: Unfinished modifier for VARNAME ('!' missing)
make: Unfinished modifier for VARNAME ('!' missing)
make: Unfinished modifier for "VARNAME" ('!' missing)
want: Unfinished modifier for ! ('!' missing)
make: Unfinished modifier for ! ('!' missing)
make: Unfinished modifier for "!" ('!' missing)
mod-subst-delimiter:
make: Missing delimiter for :S modifier
make: Missing delimiter for modifier ':S'
1:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
2:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
3:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
5:
make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S
make: Unclosed variable expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
mod-regex-delimiter:
make: Missing delimiter for :C modifier
1:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
2:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
3:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for VAR (',' missing)
make: Unfinished modifier for "VAR" (',' missing)
5:
make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C
make: Unclosed variable expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
mod-regex-undefined-subexpression:
one one 2 3 5 8 one3 2one 34
make: No match for subexpression \2
make: No match for subexpression \2
make: No match for subexpression \1
make: No match for subexpression \2
make: No match for subexpression \1
()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34
mod-ts-parse:
112358132134
15152535558513521534
make: Bad modifier `:ts\65oct' for FIB
make: Bad modifier ":ts\65oct" for variable "FIB"
65oct}
make: Bad modifier `:tsxy' for FIB
make: Bad modifier ":ts\65oct" for variable ""
65oct}
make: Bad modifier ":tsxy" for variable "FIB"
xy}
mod-t-parse:
make: Bad modifier `:t' for FIB
make: Bad modifier ":t" for variable "FIB"
make: Bad modifier `:txy' for FIB
make: Bad modifier ":txy" for variable "FIB"
y}
make: Bad modifier `:t' for FIB
make: Bad modifier ":t" for variable "FIB"
make: Bad modifier `:t' for FIB
make: Bad modifier ":t" for variable "FIB"
M*}
mod-ifelse-parse:
make: Unfinished modifier for FIB (':' missing)
make: Unfinished modifier for "FIB" (':' missing)
make: Unfinished modifier for FIB (':' missing)
make: Unfinished modifier for "FIB" (':' missing)
make: Unfinished modifier for FIB ('}' missing)
make: Unfinished modifier for "FIB" ('}' missing)
make: Unfinished modifier for FIB ('}' missing)
make: Unfinished modifier for "FIB" ('}' missing)
then
mod-remember-parse:
1 1 2 3 5 8 13 21 34
make: Unknown modifier '_'
make: Unknown modifier "__"
mod-sysv-parse:
make: Unknown modifier '3'
make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
make: Unknown modifier "3"
make: Unclosed variable expression, expecting '}' for modifier "3" of variable "FIB" with value ""
make: Unknown modifier '3'
make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
make: Unknown modifier "3="
make: Unclosed variable expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
make: Unknown modifier '3'
make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3
make: Unknown modifier "3=x3"
make: Unclosed variable expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
1 1 2 x3 5 8 1x3 21 34

View File

@ -1,4 +1,4 @@
# $NetBSD: moderrs.mk,v 1.25 2020/11/15 20:20:58 rillig Exp $
# $NetBSD: moderrs.mk,v 1.30 2021/06/21 08:28:37 rillig Exp $
#
# various modifier error tests
@ -19,7 +19,6 @@ all: words
all: exclam
all: mod-subst-delimiter
all: mod-regex-delimiter
all: mod-regex-undefined-subexpression
all: mod-ts-parse
all: mod-t-parse
all: mod-ifelse-parse
@ -35,11 +34,11 @@ mod-unknown-indirect: print-header print-footer
@echo 'VAR:${MOD_UNKN}=before-${VAR:${MOD_UNKN}:inner}-after'
unclosed-direct: print-header print-footer
@echo 'want: Unclosed variable specification (expecting $'}$') for "VAR" (value "Thevariable") modifier S'
@echo 'want: Unclosed variable expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
@echo VAR:S,V,v,=${VAR:S,V,v,
unclosed-indirect: print-header print-footer
@echo 'want: Unclosed variable specification after complex modifier (expecting $'}$') for VAR'
@echo 'want: Unclosed variable expression after indirect modifier, expecting $'}$' for variable "VAR"'
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
unfinished-indirect: print-header print-footer
@ -119,27 +118,11 @@ mod-regex-delimiter: print-header print-footer
@echo 6: ${VAR:C,from,to,
@echo 7: ${VAR:C,from,to,}
# In regular expressions with alternatives, not all capturing groups are
# always set; some may be missing. Warn about these.
#
# Since there is no way to turn off this warning, the combination of
# alternative matches and capturing groups is seldom used, if at all.
#
# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added
# for treating undefined capturing groups as empty, but that would create a
# syntactical ambiguity since the :S and :C modifiers are open-ended (see
# mod-subst-chain). Luckily the modifier :U does not make sense after :C,
# therefore this case does not happen in practice.
# The sub-modifier for the :S and :C modifiers would have to be chosen
# wisely, to not create ambiguities while parsing.
mod-regex-undefined-subexpression: print-header print-footer
@echo ${FIB:C,1(.*),one\1,} # all ok
@echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression
mod-ts-parse: print-header print-footer
@echo ${FIB:ts}
@echo ${FIB:ts\65} # octal 065 == U+0035 == '5'
@echo ${FIB:ts\65oct} # bad modifier
@echo ${:U${FIB}:ts\65oct} # bad modifier, variable name is ""
@echo ${FIB:tsxy} # modifier too long
mod-t-parse: print-header print-footer

View File

@ -1,6 +1,6 @@
make: Bad modifier `:tx' for LIST
make: Bad modifier ":tx" for variable "LIST"
LIST:tx="}"
make: Bad modifier `:ts\X' for LIST
make: Bad modifier ":ts\X" for variable "LIST"
LIST:ts/x:tu="\X:tu}"
FU_mod-ts="a/b/cool"
FU_mod-ts:ts:T="cool" == cool?

View File

@ -1,4 +1,4 @@
make: Bad modifier `:[]' for LIST
make: Bad modifier ":[]" for variable "LIST"
LIST:[]="" is an error
LIST:[0]="one two three four five six"
LIST:[0x0]="one two three four five six"
@ -37,17 +37,17 @@ REALLYSPACE=" "
REALLYSPACE:[1]="" == "" ?
REALLYSPACE:[*]:[1]=" " == " " ?
LIST:[1]="one"
make: Bad modifier `:[1.]' for LIST
make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
make: Bad modifier `:[1].' for LIST
make: Bad modifier ":[1]." for variable "LIST"
LIST:[1].="}" is an error
LIST:[2]="two"
LIST:[6]="six"
LIST:[7]=""
LIST:[999]=""
make: Bad modifier `:[-]' for LIST
make: Bad modifier ":[-]" for variable "LIST"
LIST:[-]="" is an error
make: Bad modifier `:[--]' for LIST
make: Bad modifier ":[--]" for variable "LIST"
LIST:[--]="" is an error
LIST:[-1]="six"
LIST:[-2]="five"
@ -67,20 +67,22 @@ LIST:[*]:C/ /,/:[2]=""
LIST:[*]:C/ /,/:[*]:[2]=""
LIST:[*]:C/ /,/:[@]:[2]="three"
LONGLIST:[012..0x12]="10 11 12 13 14 15 16 17 18"
make: Bad modifier `:[1.]' for LIST
make: Bad modifier ":[1.]" for variable "LIST"
LIST:[1.]="" is an error
make: Bad modifier `:[1..]' for LIST
make: Bad modifier ":[1..]" for variable "LIST"
LIST:[1..]="" is an error
make: Bad modifier ":[1.. ]" for variable "LIST"
LIST:[1.. ]="" is an error
LIST:[1..1]="one"
make: Bad modifier `:[1..1.]' for LIST
make: Bad modifier ":[1..1.]" for variable "LIST"
LIST:[1..1.]="" is an error
LIST:[1..2]="one two"
LIST:[2..1]="two one"
LIST:[3..-2]="three four five"
LIST:[-4..4]="three four"
make: Bad modifier `:[0..1]' for LIST
make: Bad modifier ":[0..1]" for variable "LIST"
LIST:[0..1]="" is an error
make: Bad modifier `:[-1..0]' for LIST
make: Bad modifier ":[-1..0]" for variable "LIST"
LIST:[-1..0]="" is an error
LIST:[-1..1]="six five four three two one"
LIST:[0..0]="one two three four five six"
@ -95,7 +97,7 @@ LIST:[${ONE}]="one"
LIST:[${MINUSONE}]="six"
LIST:[${STAR}]="one two three four five six"
LIST:[${AT}]="one two three four five six"
make: Bad modifier `:[${EMPTY' for LIST
make: Bad modifier ":[${EMPTY" for variable "LIST"
LIST:[${EMPTY}]="" is an error
LIST:[${LONGLIST:[21]:S/2//}]="one"
LIST:[${LIST:[#]}]="six"

View File

@ -1,4 +1,4 @@
# $NetBSD: modword.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
# $NetBSD: modword.mk,v 1.6 2021/03/14 16:00:07 rillig Exp $
#
# Test behaviour of new :[] modifier
# TODO: When was this modifier new?
@ -99,6 +99,7 @@ mod-squarebrackets-n:
mod-squarebrackets-start-end:
@echo 'LIST:[1.]="${LIST:[1.]}" is an error'
@echo 'LIST:[1..]="${LIST:[1..]}" is an error'
@echo 'LIST:[1.. ]="${LIST:[1.. ]}" is an error'
@echo 'LIST:[1..1]="${LIST:[1..1]}"'
@echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error'
@echo 'LIST:[1..2]="${LIST:[1..2]}"'

View File

@ -1,4 +1,4 @@
# $NetBSD: opt-chdir.mk,v 1.5 2020/11/15 05:43:56 sjg Exp $
# $NetBSD: opt-chdir.mk,v 1.6 2021/05/18 17:05:45 sjg Exp $
#
# Tests for the -C command line option, which changes the directory at the
# beginning.
@ -23,5 +23,7 @@ chdir-root: .PHONY .IGNORE
@MAKE_OBJDIR_CHECK_WRITABLE=no ${MAKE} -C / -V 'cwd: $${.CURDIR}'
# Trying to change to a nonexistent directory exits immediately.
# Note: just because the whole point of /nonexistent is that it should
# not exist - doesn't mean it doesn't.
chdir-nonexistent: .PHONY .IGNORE
@${MAKE} -C /nonexistent
@${MAKE} -C /nonexistent.${.MAKE.PID}

View File

@ -0,0 +1,48 @@
echo '3 spaces'; false
3 spaces
*** Failed target: fail-spaces
*** Failed commands:
echo '3 spaces'; false
*** [fail-spaces] Error code 1
make: stopped in unit-tests
echo \ indented; false
indented
*** Failed target: fail-escaped-space
*** Failed commands:
echo \ indented; false
*** [fail-escaped-space] Error code 1
make: stopped in unit-tests
echo 'line1
line2'; false
line1
line2
*** Failed target: fail-newline
*** Failed commands:
echo 'line1${.newline}line2'; false
*** [fail-newline] Error code 1
make: stopped in unit-tests
echo 'line1 line2'; false
line1 line2
*** Failed target: fail-multiline
*** Failed commands:
echo 'line1 line2'; false
*** [fail-multiline] Error code 1
make: stopped in unit-tests
echo 'word1' 'word2'; false
word1 word2
*** Failed target: fail-multiline-intention
*** Failed commands:
echo 'word1' 'word2'; false
*** [fail-multiline-intention] Error code 1
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,36 @@
# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $
#
# Tests for the -de command line option, which adds debug logging for
# failed commands and targets; since 2021-04-27 also in jobs mode.
.MAKEFLAGS: -de -j1
all: fail-spaces
all: fail-escaped-space
all: fail-newline
all: fail-multiline
all: fail-multiline-intention
fail-spaces:
echo '3 spaces'; false
fail-escaped-space:
echo \ indented; false
fail-newline:
echo 'line1${.newline}line2'; false
# The line continuations in multiline commands are turned into an ordinary
# space before the command is actually run.
fail-multiline:
echo 'line1\
line2'; false
# It is a common style to align the continuation backslashes at the right
# of the lines, usually at column 73. All spaces before the continuation
# backslash are preserved and are usually outside a shell word and thus
# irrelevant. Since "usually" is not "always", these space characters are
# not merged into a single space.
fail-multiline-intention:
echo 'word1' \
'word2'; false

View File

@ -2,7 +2,7 @@ make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
make: "opt-debug-lint.mk" line 69: Unknown modifier '$'
make: "opt-debug-lint.mk" line 69: Unknown modifier "${"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: opt-debug-lint.mk,v 1.12 2020/12/20 19:10:53 rillig Exp $
# $NetBSD: opt-debug-lint.mk,v 1.14 2021/03/14 10:57:12 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
# to catch common mistakes, such as unclosed variable expressions.
@ -77,5 +77,19 @@ ${UNDEF}: ${UNDEF}
. error
.endif
all:
@:;
# In lint mode, the whole variable text is evaluated to check for unclosed
# expressions and unknown operators. During this check, the subexpression
# '${:U2}' is not expanded, instead it is copied verbatim into the regular
# expression, leading to '.*=.{1,${:U2}}$'.
#
# Before var.c 1.856 from 2021-03-14, this regular expression was then
# compiled even though that was not necessary for checking the syntax at the
# level of variable expressions. The unexpanded '$' then resulted in a wrong
# error message.
#
# This only happened in lint mode since in default mode the early check for
# unclosed expressions and unknown modifiers is skipped.
#
# See VarCheckSyntax, ApplyModifier_Regex.
#
VARMOD_REGEX= ${:UA=111 B=222 C=33:C/.*=.{1,${:U2}}$//g}

View File

@ -1,4 +1,4 @@
Global:VAR = value
Global:.MAKEFLAGS = -r -k -d v -d
Global:.MAKEFLAGS = -r -k -d v -d 0
Global: VAR = value
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: opt-file.mk,v 1.11 2020/12/22 08:57:23 rillig Exp $
# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the -f command line option.
@ -28,10 +28,10 @@ all: file-containing-null-byte
# ParseReadLine (1): 'VAR=value\<A5><A5><A5><A5><A5><A5>'
# Global:VAR = value\<A5><A5><A5><A5><A5><A5>value\<A5><A5><A5><A5><A5><A5>
# ParseReadLine (2): 'alue\<A5><A5><A5><A5><A5><A5>'
# ParseDoDependency(alue\<A5><A5><A5><A5><A5><A5>)
# ParseDependency(alue\<A5><A5><A5><A5><A5><A5>)
# make-2014.01.01.00.00.00: "(stdin)" line 2: Need an operator
# ParseReadLine (3): '<A5><A5><A5>ZZZZZZZZZZZZZZZZ'
# ParseDoDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
# ParseDependency(<A5><A5><A5>ZZZZZZZZZZZZZZZZ)
#
file-ending-in-backslash: .PHONY
@printf '%s' 'VAR=value\' \

View File

@ -1,4 +1,4 @@
# $NetBSD: opt-jobs-no-action.mk,v 1.8 2020/12/10 23:54:41 rillig Exp $
# $NetBSD: opt-jobs-no-action.mk,v 1.9 2021/04/04 09:58:51 rillig Exp $
#
# Tests for the combination of the options -j and -n, which prints the
# commands instead of actually running them.
@ -23,7 +23,7 @@
# this is handled by the [0] != '\0' checks in Job_ParseShell.
# The '\#' is handled by ParseGetLine.
# The '\n' is handled by Str_Words in Job_ParseShell.
# The '$$' is handled by Var_Subst in ParseDependency.
# The '$$' is handled by Var_Subst in ParseDependencyLine.
.SHELL: \
name=sh \
path=${.SHELL} \

View File

@ -1,12 +1,12 @@
# $NetBSD: recursive.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $
# $NetBSD: recursive.mk,v 1.5 2021/03/15 12:15:03 rillig Exp $
#
# In -dL mode, a variable may get expanded before it makes sense.
# This would stop make from doing anything since the "recursive" error
# is fatal and exits immediately.
#
# The purpose of evaluating that variable early was just to detect
# whether there are unclosed variables. It might be enough to parse the
# variable value without VARE_WANTRES for that purpose.
# whether there are unclosed variables. The variable value is therefore
# parsed with VARE_PARSE_ONLY for that purpose.
#
# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
# GNU Automake.
@ -36,4 +36,3 @@ MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE}
UNCLOSED= $(MISSING_PAREN
UNCLOSED= ${MISSING_BRACE
UNCLOSED= ${MISSING_BRACE_INDIRECT}

View File

@ -1,4 +1,4 @@
# $NetBSD: sh-jobs.mk,v 1.3 2020/12/11 01:06:10 rillig Exp $
# $NetBSD: sh-jobs.mk,v 1.4 2021/04/16 16:49:27 rillig Exp $
#
# Tests for the "run in jobs mode" part of the "Shell Commands" section
# from the manual page.
@ -14,14 +14,14 @@ all: .PHONY comment .WAIT comment-with-followup-line .WAIT no-comment
# would lead to a syntax error in the generated shell file, at least for
# bash and dash, but not for NetBSD sh and ksh.
#
# See JobPrintCommand, cmdTemplate, runIgnTmpl
# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment: .PHONY
@# comment
# If a shell command starts with a comment character after stripping the
# leading '@', it is run in ignore-errors mode.
#
# See JobPrintCommand, cmdTemplate, runIgnTmpl
# See JobWriteCommand, cmdTemplate, runIgnTmpl
comment-with-followup-line: .PHONY
@# comment${.newline}echo '$@: This is printed.'; false
@true
@ -29,7 +29,7 @@ comment-with-followup-line: .PHONY
# Without the comment, the commands are run in the default mode, which checks
# the exit status of every makefile line.
#
# See JobPrintCommand, cmdTemplate, runChkTmpl
# See JobWriteCommand, cmdTemplate, runChkTmpl
no-comment: .PHONY
@echo '$@: This is printed.'; false
@true

View File

@ -1,4 +1,4 @@
# $NetBSD: shell-csh.mk,v 1.7 2020/12/13 02:09:55 sjg Exp $
# $NetBSD: shell-csh.mk,v 1.8 2021/04/04 09:58:51 rillig Exp $
#
# Tests for using a C shell for running the commands.
@ -12,7 +12,7 @@ CSH!= which csh 2> /dev/null || true
.endif
# In parallel mode, the shell->noPrint command is filtered from
# the output, rather naively (in JobOutput).
# the output, rather naively (in PrintOutput).
#
# Until 2020-10-03, the output in parallel mode was garbled because
# the definition of the csh had been wrong since 1993 at least.

View File

@ -1,19 +1,19 @@
ParseReadLine (9): '.SUFFIXES:'
ParseDoDependency(.SUFFIXES:)
ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (11): '.SUFFIXES: .a .b .c'
ParseDoDependency(.SUFFIXES: .a .b .c)
ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (17): '.a.b:'
ParseDoDependency(.a.b:)
ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (21): '.a.c: ${.PREFIX}.dependency'
deleting incomplete transformation from `.a' to `.b'
ParseDoDependency(.a.c: ${.PREFIX}.dependency)
ParseDependency(.a.c: ${.PREFIX}.dependency)
defining transformation from `.a' to `.c'
inserting ".a" (1) at end of list
inserting ".c" (3) at end of list
@ -22,7 +22,7 @@ inserting ".c" (3) at end of list
# ${.PREFIX}.dependency, unmade, type none, flags none
ParseReadLine (23): '.DEFAULT:'
transformation .a.c complete
ParseDoDependency(.DEFAULT:)
ParseDependency(.DEFAULT:)
ParseReadLine (24): ' : Making ${.TARGET} from ${.IMPSRC} all ${.ALLSRC} by default.'
transformation .DEFAULT complete
Wildcard expanding "all"...

View File

@ -1,12 +1,12 @@
ParseReadLine (8): '.1.2 .1.3 .1.4:'
ParseDoDependency(.1.2 .1.3 .1.4:)
ParseDependency(.1.2 .1.3 .1.4:)
Setting main node to ".1.2"
ParseReadLine (9): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (14): 'next-main:'
ParseDoDependency(next-main:)
ParseDependency(next-main:)
ParseReadLine (15): ' : Making ${.TARGET}'
ParseReadLine (19): '.SUFFIXES: .1 .2 .3 .4'
ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Setting main node from ".1.2" back to null
@ -27,42 +27,42 @@ inserting ".1" (1) at end of list
inserting ".4" (4) at end of list
Setting main node to "next-main"
ParseReadLine (24): '.SUFFIXES:'
ParseDoDependency(.SUFFIXES:)
ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (32): '.SUFFIXES: .4 .3 .2 .1'
ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (33): '.SUFFIXES:'
ParseDoDependency(.SUFFIXES:)
ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (34): '.SUFFIXES: .1 .2 .3 .4'
ParseDoDependency(.SUFFIXES: .1 .2 .3 .4)
ParseDependency(.SUFFIXES: .1 .2 .3 .4)
Adding suffix ".1"
Adding suffix ".2"
Adding suffix ".3"
Adding suffix ".4"
ParseReadLine (35): '.SUFFIXES:'
ParseDoDependency(.SUFFIXES:)
ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (36): '.SUFFIXES: .4 .3 .2 .1'
ParseDoDependency(.SUFFIXES: .4 .3 .2 .1)
ParseDependency(.SUFFIXES: .4 .3 .2 .1)
Adding suffix ".4"
Adding suffix ".3"
Adding suffix ".2"
Adding suffix ".1"
ParseReadLine (38): 'suff-main-several.1:'
ParseDoDependency(suff-main-several.1:)
ParseDependency(suff-main-several.1:)
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})
ParseDependency(next-main: suff-main-several.{2,3,4})
# LinkSource: added child next-main - suff-main-several.{2,3,4}
# 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)
ParseDependency(.MAKEFLAGS: -d0 -dg1)
#*** Input graph:
# .1.2, unmade, type OP_TRANSFORM, flags none
# .1.3, unmade, type OP_TRANSFORM, flags none

View File

@ -1,38 +1,38 @@
ParseReadLine (10): '.SUFFIXES:'
ParseDoDependency(.SUFFIXES:)
ParseDependency(.SUFFIXES:)
Clearing all suffixes
ParseReadLine (12): '.SUFFIXES: .a .b .c'
ParseDoDependency(.SUFFIXES: .a .b .c)
ParseDependency(.SUFFIXES: .a .b .c)
Adding suffix ".a"
Adding suffix ".b"
Adding suffix ".c"
ParseReadLine (14): 'suff-rebuild-example.a:'
ParseDoDependency(suff-rebuild-example.a:)
ParseDependency(suff-rebuild-example.a:)
Adding "suff-rebuild-example.a" to all targets.
ParseReadLine (15): ' : Making ${.TARGET} out of nothing.'
ParseReadLine (17): '.a.b:'
ParseDoDependency(.a.b:)
ParseDependency(.a.b:)
defining transformation from `.a' to `.b'
inserting ".a" (1) at end of list
inserting ".b" (2) at end of list
ParseReadLine (18): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (19): '.b.c:'
transformation .a.b complete
ParseDoDependency(.b.c:)
ParseDependency(.b.c:)
defining transformation from `.b' to `.c'
inserting ".b" (2) at end of list
inserting ".c" (3) at end of list
ParseReadLine (20): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (21): '.c:'
transformation .b.c complete
ParseDoDependency(.c:)
ParseDependency(.c:)
defining transformation from `.c' to `'
inserting ".c" (3) at end of list
inserting "" (0) at end of list
ParseReadLine (22): ' : Making ${.TARGET} from ${.IMPSRC}.'
ParseReadLine (44): '.SUFFIXES: .c .b .a'
transformation .c complete
ParseDoDependency(.SUFFIXES: .c .b .a)
ParseDependency(.SUFFIXES: .c .b .a)
Adding ".END" to all targets.
Wildcard expanding "all"...
SuffFindDeps "all"

View File

@ -1 +1,4 @@
make: "var-class-cmdline.mk" line 67: global
make: "var-class-cmdline.mk" line 76: makeflags
makeflags
exit status 0

View File

@ -1,8 +1,80 @@
# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: var-class-cmdline.mk,v 1.5 2021/02/23 21:59:31 rillig Exp $
#
# Tests for variables specified on the command line.
#
# Variables that are specified on the command line override those from the
# global scope.
#
# For performance reasons, the actual implementation is more complex than the
# above single-sentence rule, in order to avoid unnecessary lookups in scopes,
# which before var.c 1.586 from 2020-10-25 calculated the hash value of the
# variable name once for each lookup. Instead, when looking up the value of
# a variable, the search often starts in the global scope since that is where
# most of the variables are stored. This conflicts with the statement that
# variables from the cmdline scope override global variables, since after the
# common case of finding a variable in the global scope, another lookup would
# be needed in the cmdline scope to ensure that there is no overriding
# variable there.
#
# Instead of this costly lookup scheme, make implements it in a different
# way:
#
# Whenever a global variable is created, this creation is ignored if
# there is a cmdline variable of the same name.
#
# Whenever a cmdline variable is created, any global variable of the
# same name is deleted.
#
# Whenever a global variable is deleted, nothing special happens.
#
# Deleting a cmdline variable is not possible.
#
# These 4 rules provide the guarantee that whenever a global variable exists,
# there cannot be a cmdline variable of the same name. Therefore, after
# finding a variable in the global scope, no additional lookup is needed in
# the cmdline scope.
#
# The above ruleset provides the same guarantees as the simple rule "cmdline
# overrides global". Due to an implementation mistake, the actual behavior
# was not entirely equivalent to the simple rule though. The mistake was
# that when a cmdline variable with '$$' in its name was added, a global
# variable was deleted, but not with the exact same name as the cmdline
# variable. Instead, the name of the global variable was expanded one more
# time than the name of the cmdline variable. For variable names that didn't
# have a '$$' in their name, it was implemented correctly all the time.
#
# The bug was added in var.c 1.183 on 2013-07-16, when Var_Set called
# Var_Delete to delete the global variable. Just two months earlier, in var.c
# 1.174 from 2013-05-18, Var_Delete had started to expand the variable name.
# Together, these two changes made the variable name be expanded twice in a
# row. This bug was fixed in var.c 1.835 from 2021-02-22.
#
# Another bug was the wrong assumption that "deleting a cmdline variable is
# not possible". Deleting such a variable has been possible since var.c 1.204
# from 2016-02-19, when the variable modifier ':@' started to delete the
# temporary loop variable after finishing the loop. It was probably not
# intended back then that a side effect of this seemingly simple change was
# that both global and cmdline variables could now be undefined at will as a
# side effect of evaluating a variable expression. As of 2021-02-23, this is
# still possible.
#
# Most cmdline variables are set at the very beginning, when parsing the
# command line arguments. Using the special target '.MAKEFLAGS', it is
# possible to set cmdline variables at any later time.
# TODO: Implementation
# A normal global variable, without any cmdline variable nearby.
VAR= global
.info ${VAR}
all:
@:;
# The global variable is "overridden" by simply deleting it and then
# installing the cmdline variable instead. Since there is no obvious way to
# undefine a cmdline variable, there is no need to remember the old value
# of the global variable could become visible again.
#
# See varmod-loop.mk for a non-obvious way to undefine a cmdline variable.
.MAKEFLAGS: VAR=makeflags
.info ${VAR}
# If Var_SetWithFlags should ever forget to delete the global variable,
# the below line would print "global" instead of the current "makeflags".
.MAKEFLAGS: -V VAR

View File

@ -0,0 +1,29 @@
make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar.
make: "var-eval-short.mk" line 41: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
make: "var-eval-short.mk" line 79: Invalid time value: ${FAIL}}
make: "var-eval-short.mk" line 79: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
make: "var-eval-short.mk" line 93: Invalid time value: ${FAIL}}
make: "var-eval-short.mk" line 93: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...}
Modifier part: "${FAIL}then"
Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
ParseReadLine (158): 'DEFINED= defined'
Global: DEFINED = defined
CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${DEFINED:L}
Result of ${DEFINED:L} is "defined" (parse-only, regular)
Parsing modifier ${DEFINED:?...}
Modifier part: "${FAIL}then"
Modifier part: "${FAIL}else"
Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
ParseReadLine (161): '.MAKEFLAGS: -d0'
ParseDependency(.MAKEFLAGS: -d0)
Global: .MAKEFLAGS = -r -k -d cpv -d
Global: .MAKEFLAGS = -r -k -d cpv -d 0
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,163 @@
# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $
#
# Tests for each variable modifier to ensure that they only do the minimum
# necessary computations. If the result of the expression is not needed, they
# should only parse the modifier but not actually evaluate it.
#
# See also:
# var.c, the comment starting with 'The ApplyModifier functions'
# ApplyModifier, for the order of the modifiers
# ParseModifierPart, for evaluating nested expressions
# cond-short.mk
FAIL= ${:!echo unexpected 1>&2!}
# The following tests only ensure that nested expressions are not evaluated.
# They cannot ensure that any unexpanded text returned from ParseModifierPart
# is ignored as well. To do that, it is necessary to step through the code of
# each modifier.
.if 0 && ${FAIL}
.endif
.if 0 && ${VAR::=${FAIL}}
.elif defined(VAR)
. error
.endif
.if 0 && ${${FAIL}:?then:else}
.endif
.if 0 && ${1:?${FAIL}:${FAIL}}
.endif
.if 0 && ${0:?${FAIL}:${FAIL}}
.endif
# Before var.c 1.870 from 2021-03-14, the expression ${FAIL} was evaluated
# after the loop, when undefining the temporary global loop variable.
# Since var.c 1.907 from 2021-04-04, a '$' is no longer allowed in the
# variable name.
.if 0 && ${:Uword:@${FAIL}@expr@}
.endif
.if 0 && ${:Uword:@var@${FAIL}@}
.endif
# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand
# the nested expression ${FAIL} and then tried to parse the unexpanded text,
# which failed since '$' is not a valid range character.
.if 0 && ${:Uword:[${FAIL}]}
.endif
# Before var.c,v 1.867 from 2021-03-14, the modifier ':_' defined the variable
# even though the whole expression should have only been parsed, not
# evaluated.
.if 0 && ${:Uword:_=VAR}
.elif defined(VAR)
. error
.endif
# Before var.c,v 1.856 from 2021-03-14, the modifier ':C' did not expand the
# nested expression ${FAIL} and then tried to compile the unexpanded text as a
# regular expression, which failed both because of the '{FAIL}', which is not
# a valid repetition, and because of the '****', which are repeated
# repetitions as well.
# '${FAIL}'
.if 0 && ${:Uword:C,${FAIL}****,,}
.endif
DEFINED= # defined
.if 0 && ${DEFINED:D${FAIL}}
.endif
.if 0 && ${:Uword:E}
.endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
# ':gmtime' does not expand its argument.
.if 0 && ${:Uword:gmtime=${FAIL}}
.endif
.if 0 && ${:Uword:H}
.endif
.if 0 && ${:Uword:hash}
.endif
.if 0 && ${value:L}
.endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
# ':localtime' does not expand its argument.
.if 0 && ${:Uword:localtime=${FAIL}}
.endif
.if 0 && ${:Uword:M${FAIL}}
.endif
.if 0 && ${:Uword:N${FAIL}}
.endif
.if 0 && ${:Uword:O}
.endif
.if 0 && ${:Uword:Ox}
.endif
.if 0 && ${:Uword:P}
.endif
.if 0 && ${:Uword:Q}
.endif
.if 0 && ${:Uword:q}
.endif
.if 0 && ${:Uword:R}
.endif
.if 0 && ${:Uword:range}
.endif
.if 0 && ${:Uword:S,${FAIL},${FAIL},}
.endif
.if 0 && ${:Uword:sh}
.endif
.if 0 && ${:Uword:T}
.endif
.if 0 && ${:Uword:ts/}
.endif
.if 0 && ${:U${FAIL}}
.endif
.if 0 && ${:Uword:u}
.endif
.if 0 && ${:Uword:word=replacement}
.endif
# Before var.c 1.875 from 2021-03-14, Var_Parse returned "${FAIL}else" for the
# irrelevant right-hand side of the condition, even though this was not
# necessary. Since the return value from Var_Parse is supposed to be ignored
# anyway, and since it is actually ignored in an overly complicated way,
# an empty string suffices.
.MAKEFLAGS: -dcpv
.if 0 && ${0:?${FAIL}then:${FAIL}else}
.endif
# The ':L' is applied before the ':?' modifier, giving the expression a name
# and a value, just to see whether this value gets passed through or whether
# the parse-only mode results in an empty string (only visible in the debug
# log). As of var.c 1.875 from 2021-03-14, the value of the variable gets
# through, even though an empty string would suffice.
DEFINED= defined
.if 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
.endif
.MAKEFLAGS: -d0
all:

View File

@ -1,7 +1,7 @@
Var_Parse: ${:U\$\$\$\$\$\$\$\$} with VARE_WANTRES
Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF)
Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (VARE_WANTRES, none, VES_DEF)
Global:VAR.$$$$$$$$ = dollars
Global:.MAKEFLAGS = -r -k -d v -d
Global:.MAKEFLAGS = -r -k -d v -d 0
Var_Parse: ${:U\$\$\$\$\$\$\$\$} (eval)
Evaluating modifier ${:U...} on value "" (eval, undefined)
Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (eval, defined)
Global: VAR.$$$$$$$$ = dollars
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: var-op-append.mk,v 1.8 2021/02/03 08:40:47 rillig Exp $
# $NetBSD: var-op-append.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
#
# Tests for the += variable assignment operator, which appends to a variable,
# creating it if necessary.
@ -26,7 +26,7 @@ VAR+= # empty
# '+=' assignment operator. As far as possible, the '+' is interpreted as
# part of the assignment operator.
#
# See Parse_DoVar
# See Parse_Var
C++= value
.if ${C+} != "value" || defined(C++)
. error

View File

@ -1,4 +1,4 @@
# $NetBSD: var-op-assign.mk,v 1.7 2020/11/15 20:20:58 rillig Exp $
# $NetBSD: var-op-assign.mk,v 1.8 2021/03/15 19:15:04 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
@ -42,7 +42,7 @@ VAR= new value and \# some $$ special characters # comment
# This alone would not produce any side-effects, therefore the variable has
# a :!...! modifier that executes a shell command. The :!...! modifier turns
# an undefined expression into a defined one, see ApplyModifier_ShellCommand,
# the call to ApplyModifiersState_Define.
# the call to Expr_Define.
#
# Since the right-hand side of a '=' assignment is not expanded at the time
# when the variable is defined, the first command is not run at all.

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