Import bmake-20220204
Features of interest: Allow setting target local variables (similar to gmake) In META_MODE .MAKE.META.CMP_FILTER can be used for filtering commands before comparion. contrib/bmake/mk/cc-wrap.mk is an example of using these See ChangeLog for the gory details.
This commit is contained in:
parent
2935fe8237
commit
cdde9e894d
155
ChangeLog
155
ChangeLog
@ -1,3 +1,158 @@
|
||||
2022-02-04 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220204
|
||||
Merge with NetBSD make, pick up
|
||||
o use unsigned consistently for line numbers, avoid the need for %z
|
||||
o parse.c: do not step off end of input in Parse_IsVar
|
||||
when checking for target local variable assignments
|
||||
|
||||
2022-02-02 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220202
|
||||
Merge with NetBSD make, pick up
|
||||
o remove redundant declaration of HashIter_Init
|
||||
o make DEBUG0 simpler
|
||||
|
||||
2022-01-30 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* cast gn->lineno to avoid %z
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220130
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit tests
|
||||
o make GNode lineno unsigned to please lint
|
||||
o print location of recursive variable references in commands
|
||||
o print "stack trace" (makefile includes) on fatal errors
|
||||
o make.1: refine documentation for target local assignments
|
||||
|
||||
2022-01-28 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220128
|
||||
Merge with NetBSD make, pick up
|
||||
o inline functions called only once
|
||||
o for.c: clean up AddEscape for building the body of a .for loop
|
||||
o hash.c: merge duplicate code for finding an entry in a hash table
|
||||
replace HashEntry_KeyEquals with strncmp
|
||||
o make.1: document quirks of target local variable assignments.
|
||||
o parse.c: cleanup white-space
|
||||
|
||||
2022-01-26 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220126
|
||||
Merge with NetBSD make, pick up
|
||||
o allow setting target local variables
|
||||
o more unit tests
|
||||
o add missing newline after "cannot continue" message
|
||||
o meta.c: clean up eat_dots
|
||||
o parse.c: fix filename in warning about duplicate script
|
||||
o var.c: when expanding nested variables, check simple things first
|
||||
|
||||
2022-01-16 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220116
|
||||
Merge with NetBSD make, pick up
|
||||
o fix for unit-tests/varname-makeflags on non-BSD systems
|
||||
o use Var_Exists rather than Var_Value where appropriate
|
||||
o remove unnecessary functions for expanding variable names
|
||||
o cond.c: inline EvalBare
|
||||
o main.c: lint cleanup
|
||||
o parse.c: condense code in Parse_IsVar
|
||||
use islower for parsing directives (none have upper case)
|
||||
|
||||
2022-01-12 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220112
|
||||
Merge with NetBSD make, pick up
|
||||
o meta.c: add .MAKE.META.CMP_FILTER for filtering commands before
|
||||
comparion, rarely needed but useful when it is.
|
||||
|
||||
2022-01-10 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220110
|
||||
Merge with NetBSD make, pick up
|
||||
o inline Buf_Clear
|
||||
o remove redundant braces
|
||||
o rename and inline Targ_Precious
|
||||
o cond.c: remove redundant initializer in CondParser_ComparisonOrLeaf
|
||||
o for.c: clean up handling of .for loops
|
||||
fix reported line numbers of continuation lines
|
||||
add details about .for loop variables to stack traces
|
||||
o job.c: reduce code for initializing error handling in shell
|
||||
o main.c: in Cmd_Exec, return error message instead of format string
|
||||
have as few statements as possible between va_start and va_end
|
||||
add debug logging for capturing the output of external commands
|
||||
o make.c: use consistent variable names for varargs
|
||||
o make_malloc.c: remove duplicate code from bmake_strdup
|
||||
o parse.c: add missing printflike annotations
|
||||
remove redundant lines from stack traces
|
||||
fix stack traces in -dp mode
|
||||
reduce confusing code in ParseForLoop
|
||||
fix line number in debug log after returning from a file
|
||||
rename IFile and its fields to match their actual content
|
||||
clean up ParseDependencySources
|
||||
o var.c: shorten ApplyModifier_Assign
|
||||
rename is_shell_metachar, fix character conversion warning
|
||||
merge calls to ApplyModifier_Time
|
||||
merge duplicate code for modifiers 'gmtime' and 'localtime'
|
||||
|
||||
2022-01-04 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* parse.c: loadfile restore extra byte in buffer.
|
||||
|
||||
2022-01-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20220101
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit-tests
|
||||
o remove unnecessary words from command line options in CmdOpts
|
||||
o rename eunlink to unlink_file
|
||||
o cond.c: make ParseWord in condition parser simpler
|
||||
internally return false for irrelevant leaves in conditions
|
||||
replace table for function lookup in conditions with simple code
|
||||
merge duplicate types CondEvalResult and CondResult
|
||||
o for.c: clean up handling of .for loops and .include directives
|
||||
o main.c: constify cached_realpath
|
||||
clean up Cmd_Exec
|
||||
o parse.c: sync API documentation
|
||||
fix error message when reading more than 1 GB from stdin
|
||||
clean up parsing of makefiles
|
||||
fix line number in error message about open conditionals
|
||||
unexport types VarAssignOp and VarAssign
|
||||
clean up function names
|
||||
remove redundant parameters in dependency parsing functions
|
||||
reduce scope of the list of wildcard target names
|
||||
extract OP_NOTARGET into separate function
|
||||
clean up variable names for parsing dependency lines
|
||||
make debug logging a bit more human-friendly
|
||||
o var.c: condense code in ApplyModifier_Assign
|
||||
|
||||
2021-12-21 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20211221
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit-tests
|
||||
o style cleanup
|
||||
o in CLEANUP mode, free interned strings at the very end
|
||||
o fix memory leak for filenames in .for loops
|
||||
o buf.c: avoid memory leak
|
||||
o cond.c: condense CondParser_ComparisonOp
|
||||
o hash.c: change return type of HashTable_Set to void
|
||||
o job.c: change return type of Compat_RunCommand from int to bool
|
||||
o main.c: remove bmake_free
|
||||
o parse.c: condense repetetive code in ParseDirective
|
||||
remove dead code for handling traditional include directives
|
||||
clean up parsing of variable assignments
|
||||
remove unreachable code for parsing the dependency operator
|
||||
clean up loading of files
|
||||
fix memory leak in IncludeFile
|
||||
o var.c: fix memory leak when parsing a variable name
|
||||
fix memory leak from ${.SUFFIXES}
|
||||
reduce memory allocation in modifier ':?' and ':C'
|
||||
condense RegexReplace for the modifier ':C' and avoid strlen
|
||||
merge duplicate code for memory handling in Var_Parse
|
||||
distinguish between short-lived and environment variables
|
||||
rename VarFreeEnv to VarFreeShortLived
|
||||
|
||||
2021-12-15 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* cond.c: fix mem leak in CondParser_Leaf
|
||||
|
41
FILES
41
FILES
@ -53,7 +53,6 @@ metachar.c
|
||||
metachar.h
|
||||
missing/sys/cdefs.h
|
||||
mkdeps.sh
|
||||
nonints.h
|
||||
os.sh
|
||||
parse.c
|
||||
pathnames.h
|
||||
@ -173,10 +172,14 @@ unit-tests/dep-double-colon-indep.exp
|
||||
unit-tests/dep-double-colon-indep.mk
|
||||
unit-tests/dep-double-colon.exp
|
||||
unit-tests/dep-double-colon.mk
|
||||
unit-tests/dep-duplicate.exp
|
||||
unit-tests/dep-duplicate.mk
|
||||
unit-tests/dep-exclam.exp
|
||||
unit-tests/dep-exclam.mk
|
||||
unit-tests/dep-none.exp
|
||||
unit-tests/dep-none.mk
|
||||
unit-tests/dep-op-missing.exp
|
||||
unit-tests/dep-op-missing.mk
|
||||
unit-tests/dep-percent.exp
|
||||
unit-tests/dep-percent.mk
|
||||
unit-tests/dep-var.exp
|
||||
@ -371,8 +374,6 @@ unit-tests/doterror.exp
|
||||
unit-tests/doterror.mk
|
||||
unit-tests/dotwait.exp
|
||||
unit-tests/dotwait.mk
|
||||
unit-tests/envfirst.exp
|
||||
unit-tests/envfirst.mk
|
||||
unit-tests/error.exp
|
||||
unit-tests/error.mk
|
||||
unit-tests/escape.exp
|
||||
@ -427,10 +428,6 @@ unit-tests/modmatch.exp
|
||||
unit-tests/modmatch.mk
|
||||
unit-tests/modmisc.exp
|
||||
unit-tests/modmisc.mk
|
||||
unit-tests/modts.exp
|
||||
unit-tests/modts.mk
|
||||
unit-tests/modword.exp
|
||||
unit-tests/modword.mk
|
||||
unit-tests/objdir-writable.exp
|
||||
unit-tests/objdir-writable.mk
|
||||
unit-tests/opt-backwards.exp
|
||||
@ -535,6 +532,8 @@ unit-tests/opt-var-expanded.exp
|
||||
unit-tests/opt-var-expanded.mk
|
||||
unit-tests/opt-var-literal.exp
|
||||
unit-tests/opt-var-literal.mk
|
||||
unit-tests/opt-version.exp
|
||||
unit-tests/opt-version.mk
|
||||
unit-tests/opt-warnings-as-errors.exp
|
||||
unit-tests/opt-warnings-as-errors.mk
|
||||
unit-tests/opt-where-am-i.exp
|
||||
@ -547,6 +546,8 @@ unit-tests/order.exp
|
||||
unit-tests/order.mk
|
||||
unit-tests/parse-var.exp
|
||||
unit-tests/parse-var.mk
|
||||
unit-tests/parse.exp
|
||||
unit-tests/parse.mk
|
||||
unit-tests/phony-end.exp
|
||||
unit-tests/phony-end.mk
|
||||
unit-tests/posix.exp
|
||||
@ -625,18 +626,6 @@ unit-tests/unexport.exp
|
||||
unit-tests/unexport.mk
|
||||
unit-tests/use-inference.exp
|
||||
unit-tests/use-inference.mk
|
||||
unit-tests/var-class-cmdline.exp
|
||||
unit-tests/var-class-cmdline.mk
|
||||
unit-tests/var-class-env.exp
|
||||
unit-tests/var-class-env.mk
|
||||
unit-tests/var-class-global.exp
|
||||
unit-tests/var-class-global.mk
|
||||
unit-tests/var-class-local-legacy.exp
|
||||
unit-tests/var-class-local-legacy.mk
|
||||
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
|
||||
@ -655,6 +644,18 @@ unit-tests/var-op.exp
|
||||
unit-tests/var-op.mk
|
||||
unit-tests/var-recursive.exp
|
||||
unit-tests/var-recursive.mk
|
||||
unit-tests/var-scope-cmdline.exp
|
||||
unit-tests/var-scope-cmdline.mk
|
||||
unit-tests/var-scope-env.exp
|
||||
unit-tests/var-scope-env.mk
|
||||
unit-tests/var-scope-global.exp
|
||||
unit-tests/var-scope-global.mk
|
||||
unit-tests/var-scope-local-legacy.exp
|
||||
unit-tests/var-scope-local-legacy.mk
|
||||
unit-tests/var-scope-local.exp
|
||||
unit-tests/var-scope-local.mk
|
||||
unit-tests/var-scope.exp
|
||||
unit-tests/var-scope.mk
|
||||
unit-tests/varcmd.exp
|
||||
unit-tests/varcmd.mk
|
||||
unit-tests/vardebug.exp
|
||||
@ -663,6 +664,8 @@ unit-tests/varfind.exp
|
||||
unit-tests/varfind.mk
|
||||
unit-tests/varmisc.exp
|
||||
unit-tests/varmisc.mk
|
||||
unit-tests/varmod-assign-shell.exp
|
||||
unit-tests/varmod-assign-shell.mk
|
||||
unit-tests/varmod-assign.exp
|
||||
unit-tests/varmod-assign.mk
|
||||
unit-tests/varmod-defined.exp
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20211212
|
||||
_MAKE_VERSION=20220204
|
||||
|
90
arch.c
90
arch.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: arch.c,v 1.205 2021/12/12 22:41:47 rillig Exp $ */
|
||||
/* $NetBSD: arch.c,v 1.210 2022/01/15 18:34:41 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.205 2021/12/12 22:41:47 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: arch.c,v 1.210 2022/01/15 18:34:41 rillig Exp $");
|
||||
|
||||
typedef struct List ArchList;
|
||||
typedef struct ListNode ArchListNode;
|
||||
@ -247,19 +247,20 @@ FullName(const char *archive, const char *member)
|
||||
bool
|
||||
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
{
|
||||
char *cp; /* Pointer into line */
|
||||
char *spec; /* For modifying some bytes of *pp */
|
||||
const char *cp; /* Pointer into line */
|
||||
GNode *gn; /* New node */
|
||||
MFStr libName; /* Library-part of specification */
|
||||
FStr lib; /* Library-part of specification */
|
||||
FStr mem; /* Member-part of specification */
|
||||
char saveChar; /* Ending delimiter of member-name */
|
||||
bool expandLibName; /* Whether the parsed libName contains
|
||||
* variable expressions that need to be
|
||||
* expanded */
|
||||
bool expandLib; /* Whether the parsed lib contains variable
|
||||
* expressions that need to be expanded */
|
||||
|
||||
libName = MFStr_InitRefer(*pp);
|
||||
expandLibName = false;
|
||||
spec = *pp;
|
||||
lib = FStr_InitRefer(spec);
|
||||
expandLib = false;
|
||||
|
||||
for (cp = libName.str; *cp != '(' && *cp != '\0';) {
|
||||
for (cp = lib.str; *cp != '(' && *cp != '\0';) {
|
||||
if (*cp == '$') {
|
||||
/* Expand nested variable expressions. */
|
||||
/* XXX: This code can probably be shortened. */
|
||||
@ -276,20 +277,15 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
if (isError)
|
||||
return false;
|
||||
|
||||
expandLibName = true;
|
||||
expandLib = true;
|
||||
cp += nested_p - cp;
|
||||
} else
|
||||
cp++;
|
||||
}
|
||||
|
||||
*cp++ = '\0';
|
||||
if (expandLibName) {
|
||||
char *expanded;
|
||||
(void)Var_Subst(libName.str, scope, VARE_UNDEFERR, &expanded);
|
||||
/* TODO: handle errors */
|
||||
libName = MFStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
spec[cp++ - spec] = '\0';
|
||||
if (expandLib)
|
||||
Var_Expand(&lib, scope, VARE_UNDEFERR);
|
||||
|
||||
for (;;) {
|
||||
/*
|
||||
@ -299,13 +295,15 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
*/
|
||||
bool doSubst = false;
|
||||
|
||||
pp_skip_whitespace(&cp);
|
||||
cpp_skip_whitespace(&cp);
|
||||
|
||||
mem = FStr_InitRefer(cp);
|
||||
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
|
||||
if (*cp == '$') {
|
||||
/* Expand nested variable expressions. */
|
||||
/* XXX: This code can probably be shortened. */
|
||||
/*
|
||||
* XXX: This code can probably be shortened.
|
||||
*/
|
||||
FStr result;
|
||||
bool isError;
|
||||
const char *nested_p = cp;
|
||||
@ -334,8 +332,8 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
*/
|
||||
if (*cp == '\0') {
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"No closing parenthesis "
|
||||
"in archive specification");
|
||||
"No closing parenthesis "
|
||||
"in archive specification");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -346,7 +344,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
break;
|
||||
|
||||
saveChar = *cp;
|
||||
*cp = '\0';
|
||||
spec[cp - spec] = '\0';
|
||||
|
||||
/*
|
||||
* XXX: This should be taken care of intelligently by
|
||||
@ -363,19 +361,16 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
*/
|
||||
if (doSubst) {
|
||||
char *fullName;
|
||||
char *p, *expandedMem;
|
||||
char *p;
|
||||
const char *unexpandedMem = mem.str;
|
||||
|
||||
(void)Var_Subst(mem.str, scope, VARE_UNDEFERR,
|
||||
&expandedMem);
|
||||
/* TODO: handle errors */
|
||||
mem = FStr_InitOwn(expandedMem);
|
||||
Var_Expand(&mem, scope, VARE_UNDEFERR);
|
||||
|
||||
/*
|
||||
* Now form an archive spec and recurse to deal with
|
||||
* nested variables and multi-word variable values.
|
||||
*/
|
||||
fullName = FullName(libName.str, mem.str);
|
||||
fullName = FullName(lib.str, mem.str);
|
||||
p = fullName;
|
||||
|
||||
if (strcmp(mem.str, unexpandedMem) == 0) {
|
||||
@ -404,7 +399,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
|
||||
while (!Lst_IsEmpty(&members)) {
|
||||
char *member = Lst_Dequeue(&members);
|
||||
char *fullname = FullName(libName.str, member);
|
||||
char *fullname = FullName(lib.str, member);
|
||||
free(member);
|
||||
|
||||
gn = Targ_GetNode(fullname);
|
||||
@ -416,7 +411,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
Lst_Done(&members);
|
||||
|
||||
} else {
|
||||
char *fullname = FullName(libName.str, mem.str);
|
||||
char *fullname = FullName(lib.str, mem.str);
|
||||
gn = Targ_GetNode(fullname);
|
||||
free(fullname);
|
||||
|
||||
@ -432,15 +427,15 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
|
||||
}
|
||||
FStr_Done(&mem);
|
||||
|
||||
*cp = saveChar;
|
||||
spec[cp - spec] = saveChar;
|
||||
}
|
||||
|
||||
MFStr_Done(&libName);
|
||||
FStr_Done(&lib);
|
||||
|
||||
cp++; /* skip the ')' */
|
||||
/* We promised that pp would be set up at the next non-space. */
|
||||
pp_skip_whitespace(&cp);
|
||||
*pp = cp;
|
||||
cpp_skip_whitespace(&cp);
|
||||
*pp += cp - *pp;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -672,7 +667,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
|
||||
if (ar->fnametab != NULL) {
|
||||
DEBUG0(ARCH,
|
||||
"Attempted to redefine an SVR4 name table\n");
|
||||
"Attempted to redefine an SVR4 name table\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -693,8 +688,9 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
entry++;
|
||||
*ptr = '\0';
|
||||
}
|
||||
DEBUG1(ARCH, "Found svr4 archive name table with %lu entries\n",
|
||||
(unsigned long)entry);
|
||||
DEBUG1(ARCH,
|
||||
"Found svr4 archive name table with %lu entries\n",
|
||||
(unsigned long)entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -708,7 +704,7 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
}
|
||||
if (entry >= ar->fnamesize) {
|
||||
DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
|
||||
inout_name, (unsigned long)ar->fnamesize);
|
||||
inout_name, (unsigned long)ar->fnamesize);
|
||||
return 2;
|
||||
}
|
||||
|
||||
@ -737,8 +733,10 @@ ArchiveMember_HasName(const struct ar_hdr *hdr,
|
||||
if (ar_name[namelen] == ' ')
|
||||
return true;
|
||||
|
||||
/* In archives created by GNU binutils 2.27, the member names end with
|
||||
* a slash. */
|
||||
/*
|
||||
* 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;
|
||||
@ -809,9 +807,9 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
}
|
||||
|
||||
DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n",
|
||||
archive,
|
||||
(int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
|
||||
(int)sizeof out_arh->ar_date, out_arh->ar_date);
|
||||
archive,
|
||||
(int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
|
||||
(int)sizeof out_arh->ar_date, out_arh->ar_date);
|
||||
|
||||
if (ArchiveMember_HasName(out_arh, member, len)) {
|
||||
/*
|
||||
@ -912,7 +910,7 @@ Arch_Touch(GNode *gn)
|
||||
struct ar_hdr arh;
|
||||
|
||||
f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh,
|
||||
"r+");
|
||||
"r+");
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
|
51
bmake.1
51
bmake.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 sjg Exp $
|
||||
.\" $NetBSD: make.1,v 1.304 2022/01/29 20:54:58 sjg Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd December 12, 2021
|
||||
.Dd January 28, 2022
|
||||
.Dt BMAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -691,10 +691,38 @@ Variables defined as part of the command line.
|
||||
Variables that are defined specific to a certain target.
|
||||
.El
|
||||
.Pp
|
||||
Local variables are all built in and their values vary magically from
|
||||
target to target.
|
||||
It is not currently possible to define new local variables.
|
||||
The seven local variables are as follows:
|
||||
Local variables can be set on a dependency line, if
|
||||
.Va .MAKE.TARGET_LOCAL_VARIABLES ,
|
||||
is not set to
|
||||
.Ql false .
|
||||
The rest of the line
|
||||
(which will already have had Global variables expanded),
|
||||
is the variable value.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
COMPILER_WRAPPERS+= ccache distcc icecc
|
||||
|
||||
${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
|
||||
.Ed
|
||||
.Pp
|
||||
Only the targets
|
||||
.Ql ${OBJS}
|
||||
will be impacted by that filter (in "meta" mode) and
|
||||
simply enabling/disabling any of the wrappers will not render all
|
||||
of those targets out-of-date.
|
||||
.Pp
|
||||
.Em NOTE :
|
||||
target local variable assignments behave differently in that;
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Ic \&+=
|
||||
Only appends to a previous local assignment
|
||||
for the same target and variable.
|
||||
.It Ic \&:=
|
||||
Is redundant with respect to Global variables,
|
||||
which have already been expanded.
|
||||
.El
|
||||
.Pp
|
||||
The seven built-in local variables are as follows:
|
||||
.Bl -tag -width ".ARCHIVE" -offset indent
|
||||
.It Va .ALLSRC
|
||||
The list of all sources for this target; also known as
|
||||
@ -846,6 +874,11 @@ For example:
|
||||
would produce tokens like
|
||||
.Ql ---make[1234] target ---
|
||||
making it easier to track the degree of parallelism being achieved.
|
||||
.It .MAKE.TARGET_LOCAL_VARIABLES
|
||||
If set to
|
||||
.Ql false ,
|
||||
apparent variable assignments in dependency lines are
|
||||
treated as normal sources.
|
||||
.It Ev MAKEFLAGS
|
||||
The environment variable
|
||||
.Ql Ev MAKEFLAGS
|
||||
@ -954,6 +987,12 @@ If a file that was generated outside of
|
||||
.Va .OBJDIR
|
||||
but within said bailiwick is missing,
|
||||
the current target is considered out-of-date.
|
||||
.It Va .MAKE.META.CMP_FILTER
|
||||
In "meta" mode, it can (very rarely!) be useful to filter command
|
||||
lines before comparison.
|
||||
This variable can be set to a set of modifiers that will be applied to
|
||||
each line of the old and new command that differ, if the filtered
|
||||
commands still differ, the target is considered out-of-date.
|
||||
.It Va .MAKE.META.CREATED
|
||||
In "meta" mode, this variable contains a list of all the meta files
|
||||
updated.
|
||||
|
39
bmake.cat1
39
bmake.cat1
@ -443,9 +443,28 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
Local variables
|
||||
Variables that are defined specific to a certain target.
|
||||
|
||||
Local variables are all built in and their values vary magically from
|
||||
target to target. It is not currently possible to define new local vari-
|
||||
ables. The seven local variables are as follows:
|
||||
Local variables can be set on a dependency line, if
|
||||
[4m.MAKE.TARGET_LOCAL_VARIABLES[24m, is not set to `false'. The rest of the
|
||||
line (which will already have had Global variables expanded), is the
|
||||
variable value. For example:
|
||||
|
||||
COMPILER_WRAPPERS+= ccache distcc icecc
|
||||
|
||||
${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
|
||||
|
||||
Only the targets `${OBJS}' will be impacted by that filter (in "meta"
|
||||
mode) and simply enabling/disabling any of the wrappers will not render
|
||||
all of those targets out-of-date.
|
||||
|
||||
[4mNOTE[24m: target local variable assignments behave differently in that;
|
||||
|
||||
[1m+= [22mOnly appends to a previous local assignment for the same
|
||||
target and variable.
|
||||
|
||||
[1m:= [22mIs redundant with respect to Global variables, which have
|
||||
already been expanded.
|
||||
|
||||
The seven built-in local variables are as follows:
|
||||
|
||||
[4m.ALLSRC[24m The list of all sources for this target; also known as
|
||||
`[4m>[24m'.
|
||||
@ -538,6 +557,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
ing it easier to track the degree of parallelism being
|
||||
achieved.
|
||||
|
||||
.MAKE.TARGET_LOCAL_VARIABLES
|
||||
If set to `false', apparent variable assignments in de-
|
||||
pendency lines are treated as normal sources.
|
||||
|
||||
MAKEFLAGS The environment variable `MAKEFLAGS' may contain anything
|
||||
that may be specified on [1mbmake[22m's command line. Anything
|
||||
specified on [1mbmake[22m's command line is appended to the
|
||||
@ -616,6 +639,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
generated outside of [4m.OBJDIR[24m but within said bailiwick is
|
||||
missing, the current target is considered out-of-date.
|
||||
|
||||
[4m.MAKE.META.CMP_FILTER[0m
|
||||
In "meta" mode, it can (very rarely!) be useful to filter
|
||||
command lines before comparison. This variable can be
|
||||
set to a set of modifiers that will be applied to each
|
||||
line of the old and new command that differ, if the fil-
|
||||
tered commands still differ, the target is considered
|
||||
out-of-date.
|
||||
|
||||
[4m.MAKE.META.CREATED[0m
|
||||
In "meta" mode, this variable contains a list of all the
|
||||
meta files updated. If not empty, it can be used to
|
||||
@ -1591,4 +1622,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
There is no way of escaping a space character in a filename.
|
||||
|
||||
FreeBSD 13.0 December 12, 2021 FreeBSD 13.0
|
||||
FreeBSD 13.0 January 28, 2022 FreeBSD 13.0
|
||||
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 82 KiB |
15
buf.c
15
buf.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.c,v 1.53 2021/11/28 22:48:06 rillig Exp $ */
|
||||
/* $NetBSD: buf.c,v 1.55 2022/01/08 17:25:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -75,7 +75,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.53 2021/11/28 22:48:06 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.55 2022/01/08 17:25:19 rillig Exp $");
|
||||
|
||||
/* Make space in the buffer for adding at least 16 more bytes. */
|
||||
void
|
||||
@ -138,14 +138,6 @@ Buf_AddFlag(Buffer *buf, bool flag, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the buffer as empty, so it can be filled with data again. */
|
||||
void
|
||||
Buf_Empty(Buffer *buf)
|
||||
{
|
||||
buf->len = 0;
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Initialize a buffer. */
|
||||
void
|
||||
Buf_InitSize(Buffer *buf, size_t cap)
|
||||
@ -214,8 +206,9 @@ Buf_DoneDataCompact(Buffer *buf)
|
||||
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
|
||||
/* We trust realloc to be smart */
|
||||
char *data = bmake_realloc(buf->data, buf->len + 1);
|
||||
buf->data = NULL;
|
||||
data[buf->len] = '\0'; /* XXX: unnecessary */
|
||||
Buf_DoneData(buf);
|
||||
Buf_Done(buf);
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
|
19
buf.h
19
buf.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.h,v 1.44 2021/11/28 22:48:06 rillig Exp $ */
|
||||
/* $NetBSD: buf.h,v 1.47 2022/01/08 17:25:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -88,6 +88,14 @@ typedef struct Buffer {
|
||||
|
||||
void Buf_Expand(Buffer *);
|
||||
|
||||
/* Mark the buffer as empty, so it can be filled with data again. */
|
||||
MAKE_INLINE void
|
||||
Buf_Clear(Buffer *buf)
|
||||
{
|
||||
buf->len = 0;
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Buf_AddByte adds a single byte to a buffer. */
|
||||
MAKE_INLINE void
|
||||
Buf_AddByte(Buffer *buf, char byte)
|
||||
@ -101,7 +109,7 @@ Buf_AddByte(Buffer *buf, char byte)
|
||||
end[1] = '\0';
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
Buf_EndsWith(const Buffer *buf, char ch)
|
||||
{
|
||||
return buf->len > 0 && buf->data[buf->len - 1] == ch;
|
||||
@ -112,11 +120,10 @@ void Buf_AddBytesBetween(Buffer *, const char *, const char *);
|
||||
void Buf_AddStr(Buffer *, const char *);
|
||||
void Buf_AddInt(Buffer *, int);
|
||||
void Buf_AddFlag(Buffer *, bool, const char *);
|
||||
void Buf_Empty(Buffer *);
|
||||
void Buf_Init(Buffer *);
|
||||
void Buf_InitSize(Buffer *, size_t);
|
||||
void Buf_Done(Buffer *);
|
||||
char *Buf_DoneData(Buffer *);
|
||||
char *Buf_DoneDataCompact(Buffer *);
|
||||
char *Buf_DoneData(Buffer *) MAKE_ATTR_USE;
|
||||
char *Buf_DoneDataCompact(Buffer *) MAKE_ATTR_USE;
|
||||
|
||||
#endif /* MAKE_BUF_H */
|
||||
#endif
|
||||
|
93
compat.c
93
compat.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: compat.c,v 1.229 2021/11/28 23:12:51 rillig Exp $ */
|
||||
/* $NetBSD: compat.c,v 1.238 2022/01/22 18:59:23 rillig 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.229 2021/11/28 23:12:51 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.238 2022/01/22 18:59:23 rillig Exp $");
|
||||
|
||||
static GNode *curTarg = NULL;
|
||||
static pid_t compatChild;
|
||||
@ -112,10 +112,10 @@ static int compatSigno;
|
||||
static void
|
||||
CompatDeleteTarget(GNode *gn)
|
||||
{
|
||||
if (gn != NULL && !Targ_Precious(gn)) {
|
||||
if (gn != NULL && !GNode_IsPrecious(gn)) {
|
||||
const char *file = GNode_VarTarget(gn);
|
||||
|
||||
if (!opts.noExecute && eunlink(file) != -1) {
|
||||
if (!opts.noExecute && unlink_file(file)) {
|
||||
Error("*** %s removed", file);
|
||||
}
|
||||
}
|
||||
@ -135,7 +135,7 @@ CompatInterrupt(int signo)
|
||||
{
|
||||
CompatDeleteTarget(curTarg);
|
||||
|
||||
if (curTarg != NULL && !Targ_Precious(curTarg)) {
|
||||
if (curTarg != NULL && !GNode_IsPrecious(curTarg)) {
|
||||
/*
|
||||
* Run .INTERRUPT only if hit with interrupt signal
|
||||
*/
|
||||
@ -168,10 +168,12 @@ DebugFailedTarget(const char *cmd, const GNode *gn)
|
||||
{
|
||||
const char *p = cmd;
|
||||
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
gn->name);
|
||||
|
||||
/* Replace runs of whitespace with a single space, to reduce
|
||||
* the amount of whitespace for multi-line command lines. */
|
||||
/*
|
||||
* Replace runs of whitespace with a single space, to reduce the
|
||||
* amount of whitespace for multi-line command lines.
|
||||
*/
|
||||
while (*p != '\0') {
|
||||
if (ch_isspace(*p)) {
|
||||
debug_printf(" ");
|
||||
@ -220,24 +222,24 @@ UseShell(const char *cmd MAKE_ATTR_UNUSED)
|
||||
* ln List node that contains the command
|
||||
*
|
||||
* Results:
|
||||
* 0 if the command succeeded, 1 if an error occurred.
|
||||
* true if the command succeeded.
|
||||
*/
|
||||
int
|
||||
bool
|
||||
Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
{
|
||||
char *cmdStart; /* Start of expanded command */
|
||||
char *bp;
|
||||
bool silent; /* Don't print command */
|
||||
bool doIt; /* Execute even if -n */
|
||||
volatile bool errCheck; /* Check errors */
|
||||
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 */
|
||||
bool useShell; /* True if command should be executed
|
||||
* using a shell */
|
||||
bool useShell; /* True if command should be executed using a
|
||||
* shell */
|
||||
const char *volatile cmd = cmdp;
|
||||
|
||||
silent = (gn->type & OP_SILENT) != OP_NONE;
|
||||
@ -249,7 +251,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
|
||||
if (cmdStart[0] == '\0') {
|
||||
free(cmdStart);
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
cmd = cmdStart;
|
||||
LstNode_Set(ln, cmdStart);
|
||||
@ -269,12 +271,12 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
* usual '$$'.
|
||||
*/
|
||||
Lst_Append(&endNode->commands, cmdStart);
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (strcmp(cmdStart, "...") == 0) {
|
||||
gn->type |= OP_SAVE_CMDS;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
@ -298,7 +300,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
* If we did not end up with a command, just skip it.
|
||||
*/
|
||||
if (cmd[0] == '\0')
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
useShell = UseShell(cmd);
|
||||
/*
|
||||
@ -315,7 +317,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
* we go...
|
||||
*/
|
||||
if (!doIt && !GNode_ShouldExecute(gn))
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
DEBUG1(JOB, "Execute: '%s'\n", cmd);
|
||||
|
||||
@ -350,25 +352,20 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
}
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_compat_start();
|
||||
}
|
||||
#endif
|
||||
|
||||
Var_ReexportVars();
|
||||
|
||||
/*
|
||||
* Fork and execute the single command. If the fork fails, we abort.
|
||||
*/
|
||||
compatChild = cpid = vfork();
|
||||
if (cpid < 0) {
|
||||
if (cpid < 0)
|
||||
Fatal("Could not fork");
|
||||
}
|
||||
|
||||
if (cpid == 0) {
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_compat_child();
|
||||
}
|
||||
#endif
|
||||
(void)execvp(av[0], (char *const *)UNCONST(av));
|
||||
execDie("exec", av[0]);
|
||||
@ -382,9 +379,8 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
LstNode_SetNull(ln);
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_compat_parent(cpid);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -406,9 +402,8 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
} else if (WIFEXITED(reason)) {
|
||||
status = WEXITSTATUS(reason); /* exited */
|
||||
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
|
||||
if (useMeta) {
|
||||
meta_cmd_finish(NULL);
|
||||
}
|
||||
if (useMeta)
|
||||
meta_cmd_finish(NULL);
|
||||
#endif
|
||||
if (status != 0) {
|
||||
if (DEBUG(ERROR))
|
||||
@ -424,9 +419,8 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
if (!WIFEXITED(reason) || status != 0) {
|
||||
if (errCheck) {
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_job_error(NULL, gn, false, status);
|
||||
}
|
||||
#endif
|
||||
gn->made = ERROR;
|
||||
if (opts.keepgoing) {
|
||||
@ -457,7 +451,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
kill(myPid, compatSigno);
|
||||
}
|
||||
|
||||
return status;
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -467,7 +461,7 @@ RunCommands(GNode *gn)
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
if (Compat_RunCommand(cmd, gn, ln) != 0)
|
||||
if (!Compat_RunCommand(cmd, gn, ln))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -532,7 +526,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
|
||||
* to tell him/her "yes".
|
||||
*/
|
||||
DEBUG0(MAKE, "out-of-date.\n");
|
||||
if (opts.queryFlag)
|
||||
if (opts.query)
|
||||
exit(1);
|
||||
|
||||
/*
|
||||
@ -547,7 +541,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
|
||||
*/
|
||||
if (opts.ignoreErrors)
|
||||
gn->type |= OP_IGNORE;
|
||||
if (opts.beSilent)
|
||||
if (opts.silent)
|
||||
gn->type |= OP_SILENT;
|
||||
|
||||
if (Job_CheckCommands(gn, Fatal)) {
|
||||
@ -555,12 +549,11 @@ MakeUnmade(GNode *gn, GNode *pgn)
|
||||
* Our commands are ok, but we still have to worry about
|
||||
* the -t flag.
|
||||
*/
|
||||
if (!opts.touchFlag || (gn->type & OP_MAKE)) {
|
||||
if (!opts.touch || (gn->type & OP_MAKE)) {
|
||||
curTarg = gn;
|
||||
#ifdef USE_META
|
||||
if (useMeta && GNode_ShouldExecute(gn)) {
|
||||
if (useMeta && GNode_ShouldExecute(gn))
|
||||
meta_job_start(NULL, gn);
|
||||
}
|
||||
#endif
|
||||
RunCommands(gn);
|
||||
curTarg = NULL;
|
||||
@ -593,7 +586,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
|
||||
} else if (opts.keepgoing) {
|
||||
pgn->flags.remake = false;
|
||||
} else {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
PrintOnError(gn, "\nStop.\n");
|
||||
exit(1);
|
||||
}
|
||||
return true;
|
||||
@ -681,7 +674,7 @@ MakeBeginNode(void)
|
||||
|
||||
Compat_Make(gn, gn);
|
||||
if (GNode_IsError(gn)) {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
PrintOnError(gn, "\nStop.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -715,12 +708,14 @@ Compat_Run(GNodeList *targs)
|
||||
|
||||
InitSignals();
|
||||
|
||||
/* Create the .END node now, to keep the (debug) output of the
|
||||
* counter.mk test the same as before 2020-09-23. This implementation
|
||||
* detail probably doesn't matter though. */
|
||||
/*
|
||||
* Create the .END node now, to keep the (debug) output of the
|
||||
* counter.mk test the same as before 2020-09-23. This
|
||||
* implementation detail probably doesn't matter though.
|
||||
*/
|
||||
(void)Targ_GetEndNode();
|
||||
|
||||
if (!opts.queryFlag)
|
||||
if (!opts.query)
|
||||
MakeBeginNode();
|
||||
|
||||
/*
|
||||
@ -737,7 +732,7 @@ Compat_Run(GNodeList *targs)
|
||||
printf("`%s' is up to date.\n", gn->name);
|
||||
} else if (gn->made == ABORTED) {
|
||||
printf("`%s' not remade because of errors.\n",
|
||||
gn->name);
|
||||
gn->name);
|
||||
}
|
||||
if (GNode_IsError(gn) && errorNode == NULL)
|
||||
errorNode = gn;
|
||||
@ -756,7 +751,7 @@ Compat_Run(GNodeList *targs)
|
||||
Targ_PrintGraph(2);
|
||||
else if (DEBUG(GRAPH3))
|
||||
Targ_PrintGraph(3);
|
||||
PrintOnError(errorNode, "\nStop.");
|
||||
PrintOnError(errorNode, "\nStop.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
427
cond.c
427
cond.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $ */
|
||||
/* $NetBSD: cond.c,v 1.327 2022/01/29 01:12:36 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -95,10 +95,10 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.327 2022/01/29 01:12:36 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The parsing of conditional expressions is based on this grammar:
|
||||
* Conditional expressions conform to this grammar:
|
||||
* Or -> And ('||' And)*
|
||||
* And -> Term ('&&' Term)*
|
||||
* Term -> Function '(' Argument ')'
|
||||
@ -109,13 +109,13 @@ MAKE_RCSID("$NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $");
|
||||
* Leaf -> "string"
|
||||
* Leaf -> Number
|
||||
* Leaf -> VariableExpression
|
||||
* Leaf -> Symbol
|
||||
* Leaf -> BareWord
|
||||
* Operator -> '==' | '!=' | '>' | '<' | '>=' | '<='
|
||||
*
|
||||
* 'Symbol' is an unquoted string literal to which the default function is
|
||||
* applied.
|
||||
* BareWord is an unquoted string literal, its evaluation depends on the kind
|
||||
* of '.if' directive.
|
||||
*
|
||||
* The tokens are scanned by CondToken, which returns:
|
||||
* The tokens are scanned by CondParser_Token, which returns:
|
||||
* TOK_AND for '&&'
|
||||
* TOK_OR for '||'
|
||||
* TOK_NOT for '!'
|
||||
@ -123,18 +123,14 @@ MAKE_RCSID("$NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $");
|
||||
* TOK_RPAREN for ')'
|
||||
*
|
||||
* Other terminal symbols are evaluated using either the default function or
|
||||
* the function given in the terminal, they return either TOK_TRUE or
|
||||
* TOK_FALSE.
|
||||
* the function given in the terminal, they return either TOK_TRUE, TOK_FALSE
|
||||
* or TOK_ERROR.
|
||||
*/
|
||||
typedef enum Token {
|
||||
TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT,
|
||||
TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR
|
||||
} Token;
|
||||
|
||||
typedef enum CondResult {
|
||||
CR_FALSE, CR_TRUE, CR_ERROR
|
||||
} CondResult;
|
||||
|
||||
typedef enum ComparisonOp {
|
||||
LT, LE, GT, GE, EQ, NE
|
||||
} ComparisonOp;
|
||||
@ -186,10 +182,14 @@ static unsigned int cond_min_depth = 0; /* depth at makefile open */
|
||||
/* Names for ComparisonOp. */
|
||||
static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
|
||||
|
||||
static bool
|
||||
is_token(const char *str, const char *tok, unsigned char len)
|
||||
MAKE_INLINE bool
|
||||
skip_string(const char **pp, const char *str)
|
||||
{
|
||||
return strncmp(str, tok, (size_t)len) == 0 && !ch_isalpha(str[len]);
|
||||
size_t len = strlen(str);
|
||||
bool ok = strncmp(*pp, str, len) == 0;
|
||||
if (ok)
|
||||
*pp += len;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static Token
|
||||
@ -208,31 +208,13 @@ CondParser_SkipWhitespace(CondParser *par)
|
||||
* Parse a single word, taking into account balanced parentheses as well as
|
||||
* embedded expressions. Used for the argument of a built-in function as
|
||||
* well as for bare words, which are then passed to the default function.
|
||||
*
|
||||
* Arguments:
|
||||
* *pp initially points at the '(',
|
||||
* upon successful return it points right after the ')'.
|
||||
*
|
||||
* *out_arg receives the argument as string.
|
||||
*
|
||||
* func says whether the argument belongs to an actual function, or
|
||||
* NULL when parsing a bare word.
|
||||
*
|
||||
* Return the length of the argument, or an ambiguous 0 on error.
|
||||
*/
|
||||
static size_t
|
||||
ParseWord(CondParser *par, const char **pp, bool doEval, const char *func,
|
||||
char **out_arg)
|
||||
static char *
|
||||
ParseWord(const char **pp, bool doEval)
|
||||
{
|
||||
const char *p = *pp;
|
||||
Buffer argBuf;
|
||||
int paren_depth;
|
||||
size_t argLen;
|
||||
|
||||
if (func != NULL)
|
||||
p++; /* Skip opening '(' - verified by caller */
|
||||
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
Buf_InitSize(&argBuf, 16);
|
||||
|
||||
@ -243,7 +225,7 @@ ParseWord(CondParser *par, const char **pp, bool doEval, const char *func,
|
||||
break;
|
||||
if ((ch == '&' || ch == '|') && paren_depth == 0)
|
||||
break;
|
||||
if (*p == '$') {
|
||||
if (ch == '$') {
|
||||
/*
|
||||
* Parse the variable expression and install it as
|
||||
* part of the argument if it's valid. We tell
|
||||
@ -266,58 +248,73 @@ ParseWord(CondParser *par, const char **pp, bool doEval, const char *func,
|
||||
paren_depth++;
|
||||
else if (ch == ')' && --paren_depth < 0)
|
||||
break;
|
||||
Buf_AddByte(&argBuf, *p);
|
||||
Buf_AddByte(&argBuf, ch);
|
||||
p++;
|
||||
}
|
||||
|
||||
argLen = argBuf.len;
|
||||
*out_arg = Buf_DoneData(&argBuf);
|
||||
cpp_skip_hspace(&p);
|
||||
*pp = p;
|
||||
|
||||
return Buf_DoneData(&argBuf);
|
||||
}
|
||||
|
||||
/* Parse the function argument, including the surrounding parentheses. */
|
||||
static char *
|
||||
ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
|
||||
{
|
||||
const char *p = *pp;
|
||||
char *res;
|
||||
|
||||
p++; /* Skip opening '(' - verified by caller */
|
||||
cpp_skip_hspace(&p);
|
||||
res = ParseWord(&p, doEval);
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
if (func != NULL && *p++ != ')') {
|
||||
if (*p++ != ')') {
|
||||
int len = 0;
|
||||
while (ch_isalpha(func[len]))
|
||||
len++;
|
||||
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"Missing closing parenthesis for %s()", func);
|
||||
"Missing closing parenthesis for %.*s()", len, func);
|
||||
par->printedError = true;
|
||||
return 0;
|
||||
free(res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
return argLen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Test whether the given variable is defined. */
|
||||
static bool
|
||||
FuncDefined(const char *arg)
|
||||
FuncDefined(const char *var)
|
||||
{
|
||||
FStr value = Var_Value(SCOPE_CMDLINE, arg);
|
||||
bool result = value.str != NULL;
|
||||
FStr_Done(&value);
|
||||
return result;
|
||||
return Var_Exists(SCOPE_CMDLINE, var);
|
||||
}
|
||||
|
||||
/* See if the given target is requested to be made. */
|
||||
static bool
|
||||
FuncMake(const char *arg)
|
||||
FuncMake(const char *target)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = opts.create.first; ln != NULL; ln = ln->next)
|
||||
if (Str_Match(ln->datum, arg))
|
||||
if (Str_Match(ln->datum, target))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See if the given file exists. */
|
||||
static bool
|
||||
FuncExists(const char *arg)
|
||||
FuncExists(const char *file)
|
||||
{
|
||||
bool result;
|
||||
char *path;
|
||||
|
||||
path = Dir_FindFile(arg, &dirSearchPath);
|
||||
path = Dir_FindFile(file, &dirSearchPath);
|
||||
DEBUG2(COND, "exists(%s) result is \"%s\"\n",
|
||||
arg, path != NULL ? path : "");
|
||||
file, path != NULL ? path : "");
|
||||
result = path != NULL;
|
||||
free(path);
|
||||
return result;
|
||||
@ -325,9 +322,9 @@ FuncExists(const char *arg)
|
||||
|
||||
/* See if the given node exists and is an actual target. */
|
||||
static bool
|
||||
FuncTarget(const char *arg)
|
||||
FuncTarget(const char *node)
|
||||
{
|
||||
GNode *gn = Targ_FindNode(arg);
|
||||
GNode *gn = Targ_FindNode(node);
|
||||
return gn != NULL && GNode_IsTarget(gn);
|
||||
}
|
||||
|
||||
@ -336,20 +333,16 @@ FuncTarget(const char *arg)
|
||||
* associated with it.
|
||||
*/
|
||||
static bool
|
||||
FuncCommands(const char *arg)
|
||||
FuncCommands(const char *node)
|
||||
{
|
||||
GNode *gn = Targ_FindNode(arg);
|
||||
return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
|
||||
GNode *gn = Targ_FindNode(node);
|
||||
return gn != NULL && GNode_IsTarget(gn) &&
|
||||
!Lst_IsEmpty(&gn->commands);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the given number into a double.
|
||||
* We try a base 10 or 16 integer conversion first, if that fails
|
||||
* then we try a floating point conversion instead.
|
||||
*
|
||||
* Results:
|
||||
* Returns true if the conversion succeeded.
|
||||
* Sets 'out_value' to the converted number.
|
||||
* Convert the string into a floating-point number. Accepted formats are
|
||||
* base-10 integer, base-16 integer and finite floating point numbers.
|
||||
*/
|
||||
static bool
|
||||
TryParseNumber(const char *str, double *out_value)
|
||||
@ -399,7 +392,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
|
||||
Buffer *buf, FStr *inout_str)
|
||||
{
|
||||
VarEvalMode emode;
|
||||
const char *nested_p;
|
||||
const char *p;
|
||||
bool atStart;
|
||||
VarParseResult parseResult;
|
||||
|
||||
@ -407,9 +400,9 @@ CondParser_StringExpr(CondParser *par, const char *start,
|
||||
: doEval ? VARE_UNDEFERR
|
||||
: VARE_PARSE_ONLY;
|
||||
|
||||
nested_p = par->p;
|
||||
atStart = nested_p == start;
|
||||
parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, emode, inout_str);
|
||||
p = par->p;
|
||||
atStart = p == start;
|
||||
parseResult = Var_Parse(&p, SCOPE_CMDLINE, emode, inout_str);
|
||||
/* TODO: handle errors */
|
||||
if (inout_str->str == var_Error) {
|
||||
if (parseResult == VPR_ERR) {
|
||||
@ -433,7 +426,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
|
||||
*inout_str = FStr_InitRefer(NULL);
|
||||
return false;
|
||||
}
|
||||
par->p = nested_p;
|
||||
par->p = p;
|
||||
|
||||
/*
|
||||
* If the '$' started the string literal (which means no quotes), and
|
||||
@ -445,17 +438,16 @@ CondParser_StringExpr(CondParser *par, const char *start,
|
||||
|
||||
Buf_AddStr(buf, inout_str->str);
|
||||
FStr_Done(inout_str);
|
||||
*inout_str = FStr_InitRefer(NULL); /* not finished yet */
|
||||
*inout_str = FStr_InitRefer(NULL); /* not finished yet */
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Parse a string from a variable expression or an optionally quoted string,
|
||||
* on the left-hand and right-hand sides of comparisons.
|
||||
*
|
||||
* Results:
|
||||
* Returns the string, absent any quotes, or NULL on error.
|
||||
* Returns the string without any enclosing quotes, or NULL on error.
|
||||
* Sets out_quoted if the leaf was a quoted string literal.
|
||||
*/
|
||||
static void
|
||||
@ -486,7 +478,7 @@ CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
|
||||
case '"':
|
||||
par->p++;
|
||||
if (quoted)
|
||||
goto got_str; /* skip the closing quote */
|
||||
goto return_buf; /* skip the closing quote */
|
||||
Buf_AddByte(&buf, '"');
|
||||
continue;
|
||||
case ')': /* see is_separator */
|
||||
@ -497,14 +489,14 @@ CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (!quoted)
|
||||
goto got_str;
|
||||
goto return_buf;
|
||||
Buf_AddByte(&buf, par->p[0]);
|
||||
par->p++;
|
||||
continue;
|
||||
case '$':
|
||||
if (!CondParser_StringExpr(par,
|
||||
start, doEval, quoted, &buf, &str))
|
||||
goto cleanup;
|
||||
goto return_str;
|
||||
continue;
|
||||
default:
|
||||
if (!unquotedOK && !quoted && *start != '$' &&
|
||||
@ -514,28 +506,21 @@ CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
|
||||
* a variable expression or a number.
|
||||
*/
|
||||
str = FStr_InitRefer(NULL);
|
||||
goto cleanup;
|
||||
goto return_str;
|
||||
}
|
||||
Buf_AddByte(&buf, par->p[0]);
|
||||
par->p++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
got_str:
|
||||
return_buf:
|
||||
str = FStr_InitOwn(buf.data);
|
||||
buf.data = NULL;
|
||||
cleanup:
|
||||
return_str:
|
||||
Buf_Done(&buf);
|
||||
*out_str = str;
|
||||
}
|
||||
|
||||
static bool
|
||||
EvalBare(const CondParser *par, const char *arg)
|
||||
{
|
||||
bool res = par->evalBare(arg);
|
||||
return par->negateEvalBare ? !res : res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
|
||||
* ".if 0".
|
||||
@ -553,9 +538,11 @@ EvalNotEmpty(CondParser *par, const char *value, bool quoted)
|
||||
if (TryParseNumber(value, &num))
|
||||
return num != 0.0;
|
||||
|
||||
/* For .if ${...}, check for non-empty string. This is different from
|
||||
* the evaluation function from that .if variant, which would test
|
||||
* whether a variable of the given name were defined. */
|
||||
/*
|
||||
* For .if ${...}, check for non-empty string. This is different
|
||||
* from the evaluation function from that .if variant, which would
|
||||
* test whether a variable of the given name were defined.
|
||||
*/
|
||||
/*
|
||||
* XXX: Whitespace should count as empty, just as in
|
||||
* CondParser_FuncCallEmpty.
|
||||
@ -563,7 +550,7 @@ EvalNotEmpty(CondParser *par, const char *value, bool quoted)
|
||||
if (par->plain)
|
||||
return value[0] != '\0';
|
||||
|
||||
return EvalBare(par, value);
|
||||
return par->evalBare(value) != par->negateEvalBare;
|
||||
}
|
||||
|
||||
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
|
||||
@ -623,33 +610,19 @@ CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op)
|
||||
{
|
||||
const char *p = par->p;
|
||||
|
||||
if (p[0] == '<' && p[1] == '=') {
|
||||
*out_op = LE;
|
||||
goto length_2;
|
||||
} else if (p[0] == '<') {
|
||||
*out_op = LT;
|
||||
goto length_1;
|
||||
} else if (p[0] == '>' && p[1] == '=') {
|
||||
*out_op = GE;
|
||||
goto length_2;
|
||||
} else if (p[0] == '>') {
|
||||
*out_op = GT;
|
||||
goto length_1;
|
||||
} else if (p[0] == '=' && p[1] == '=') {
|
||||
*out_op = EQ;
|
||||
goto length_2;
|
||||
} else if (p[0] == '!' && p[1] == '=') {
|
||||
*out_op = NE;
|
||||
goto length_2;
|
||||
}
|
||||
if (p[0] == '<' && p[1] == '=')
|
||||
return par->p += 2, *out_op = LE, true;
|
||||
if (p[0] == '<')
|
||||
return par->p += 1, *out_op = LT, true;
|
||||
if (p[0] == '>' && p[1] == '=')
|
||||
return par->p += 2, *out_op = GE, true;
|
||||
if (p[0] == '>')
|
||||
return par->p += 1, *out_op = GT, true;
|
||||
if (p[0] == '=' && p[1] == '=')
|
||||
return par->p += 2, *out_op = EQ, true;
|
||||
if (p[0] == '!' && p[1] == '=')
|
||||
return par->p += 2, *out_op = NE, true;
|
||||
return false;
|
||||
|
||||
length_2:
|
||||
par->p = p + 2;
|
||||
return true;
|
||||
length_1:
|
||||
par->p = p + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -718,9 +691,8 @@ CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
|
||||
Token tok;
|
||||
FStr val;
|
||||
|
||||
if (!is_token(cp, "empty", 5))
|
||||
if (!skip_string(&cp, "empty"))
|
||||
return false;
|
||||
cp += 5;
|
||||
|
||||
cpp_skip_whitespace(&cp);
|
||||
if (*cp != '(')
|
||||
@ -735,7 +707,7 @@ CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
|
||||
tok = TOK_ERROR;
|
||||
else {
|
||||
cpp_skip_whitespace(&val.str);
|
||||
tok = val.str[0] != '\0' && doEval ? TOK_FALSE : TOK_TRUE;
|
||||
tok = ToToken(doEval && val.str[0] == '\0');
|
||||
}
|
||||
|
||||
FStr_Done(&val);
|
||||
@ -748,37 +720,34 @@ CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
|
||||
static bool
|
||||
CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
|
||||
{
|
||||
static const struct fn_def {
|
||||
const char fn_name[9];
|
||||
unsigned char fn_name_len;
|
||||
bool (*fn_eval)(const char *);
|
||||
} fns[] = {
|
||||
{ "defined", 7, FuncDefined },
|
||||
{ "make", 4, FuncMake },
|
||||
{ "exists", 6, FuncExists },
|
||||
{ "target", 6, FuncTarget },
|
||||
{ "commands", 8, FuncCommands }
|
||||
};
|
||||
const struct fn_def *fn;
|
||||
char *arg = NULL;
|
||||
size_t arglen;
|
||||
const char *cp = par->p;
|
||||
const struct fn_def *last_fn = fns + sizeof fns / sizeof fns[0] - 1;
|
||||
char *arg;
|
||||
const char *p = par->p;
|
||||
bool (*fn)(const char *);
|
||||
const char *fn_name = p;
|
||||
|
||||
for (fn = fns; !is_token(cp, fn->fn_name, fn->fn_name_len); fn++)
|
||||
if (fn == last_fn)
|
||||
return false;
|
||||
|
||||
cp += fn->fn_name_len;
|
||||
cpp_skip_whitespace(&cp);
|
||||
if (*cp != '(')
|
||||
if (skip_string(&p, "defined"))
|
||||
fn = FuncDefined;
|
||||
else if (skip_string(&p, "make"))
|
||||
fn = FuncMake;
|
||||
else if (skip_string(&p, "exists"))
|
||||
fn = FuncExists;
|
||||
else if (skip_string(&p, "target"))
|
||||
fn = FuncTarget;
|
||||
else if (skip_string(&p, "commands"))
|
||||
fn = FuncCommands;
|
||||
else
|
||||
return false;
|
||||
|
||||
arglen = ParseWord(par, &cp, doEval, fn->fn_name, &arg);
|
||||
*out_token = ToToken(arglen != 0 && (!doEval || fn->fn_eval(arg)));
|
||||
cpp_skip_whitespace(&p);
|
||||
if (*p != '(')
|
||||
return false;
|
||||
|
||||
arg = ParseFuncArg(par, &p, doEval, fn_name);
|
||||
*out_token = ToToken(doEval &&
|
||||
arg != NULL && arg[0] != '\0' && fn(arg));
|
||||
free(arg);
|
||||
par->p = cp;
|
||||
|
||||
par->p = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -793,9 +762,8 @@ static Token
|
||||
CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
|
||||
{
|
||||
Token t;
|
||||
char *arg = NULL;
|
||||
char *arg;
|
||||
const char *cp;
|
||||
const char *cp1;
|
||||
|
||||
/* Push anything numeric through the compare expression */
|
||||
cp = par->p;
|
||||
@ -811,13 +779,13 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
|
||||
* as an expression.
|
||||
*/
|
||||
/*
|
||||
* XXX: Is it possible to have a variable expression evaluated twice
|
||||
* at this point?
|
||||
* XXX: In edge cases, a variable expression may be evaluated twice,
|
||||
* see cond-token-plain.mk, keyword 'twice'.
|
||||
*/
|
||||
(void)ParseWord(par, &cp, doEval, NULL, &arg);
|
||||
cp1 = cp;
|
||||
cpp_skip_whitespace(&cp1);
|
||||
if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
|
||||
arg = ParseWord(&cp, doEval);
|
||||
assert(arg[0] != '\0');
|
||||
|
||||
if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
|
||||
return CondParser_Comparison(par, doEval);
|
||||
par->p = cp;
|
||||
|
||||
@ -827,7 +795,7 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
|
||||
* after .if must have been taken literally, so the argument cannot
|
||||
* be empty - even if it contained a variable expansion.
|
||||
*/
|
||||
t = ToToken(!doEval || EvalBare(par, arg));
|
||||
t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
|
||||
free(arg);
|
||||
return t;
|
||||
}
|
||||
@ -998,42 +966,32 @@ CondParser_Or(CondParser *par, bool doEval)
|
||||
return res;
|
||||
}
|
||||
|
||||
static CondEvalResult
|
||||
CondParser_Eval(CondParser *par, bool *out_value)
|
||||
static CondResult
|
||||
CondParser_Eval(CondParser *par)
|
||||
{
|
||||
CondResult res;
|
||||
|
||||
DEBUG1(COND, "CondParser_Eval: %s\n", par->p);
|
||||
|
||||
res = CondParser_Or(par, true);
|
||||
if (res == CR_ERROR)
|
||||
return COND_INVALID;
|
||||
if (res != CR_ERROR && CondParser_Token(par, false) != TOK_EOF)
|
||||
return CR_ERROR;
|
||||
|
||||
if (CondParser_Token(par, false) != TOK_EOF)
|
||||
return COND_INVALID;
|
||||
|
||||
*out_value = res == CR_TRUE;
|
||||
return COND_PARSE;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate the condition, including any side effects from the variable
|
||||
* expressions in the condition. The condition consists of &&, ||, !,
|
||||
* function(arg), comparisons and parenthetical groupings thereof.
|
||||
*
|
||||
* Results:
|
||||
* COND_PARSE if the condition was valid grammatically
|
||||
* COND_INVALID if not a valid conditional.
|
||||
*
|
||||
* *out_value is set to the boolean value of the condition
|
||||
*/
|
||||
static CondEvalResult
|
||||
CondEvalExpression(const char *cond, bool *out_value, bool plain,
|
||||
static CondResult
|
||||
CondEvalExpression(const char *cond, bool plain,
|
||||
bool (*evalBare)(const char *), bool negate,
|
||||
bool eprint, bool leftUnquotedOK)
|
||||
{
|
||||
CondParser par;
|
||||
CondEvalResult rval;
|
||||
CondResult rval;
|
||||
|
||||
cpp_skip_hspace(&cond);
|
||||
|
||||
@ -1045,9 +1003,9 @@ CondEvalExpression(const char *cond, bool *out_value, bool plain,
|
||||
par.curr = TOK_NONE;
|
||||
par.printedError = false;
|
||||
|
||||
rval = CondParser_Eval(&par, out_value);
|
||||
rval = CondParser_Eval(&par);
|
||||
|
||||
if (rval == COND_INVALID && eprint && !par.printedError)
|
||||
if (rval == CR_ERROR && eprint && !par.printedError)
|
||||
Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond);
|
||||
|
||||
return rval;
|
||||
@ -1057,10 +1015,10 @@ CondEvalExpression(const char *cond, bool *out_value, bool plain,
|
||||
* Evaluate a condition in a :? modifier, such as
|
||||
* ${"${VAR}" == value:?yes:no}.
|
||||
*/
|
||||
CondEvalResult
|
||||
Cond_EvalCondition(const char *cond, bool *out_value)
|
||||
CondResult
|
||||
Cond_EvalCondition(const char *cond)
|
||||
{
|
||||
return CondEvalExpression(cond, out_value, true,
|
||||
return CondEvalExpression(cond, true,
|
||||
FuncDefined, false, false, true);
|
||||
}
|
||||
|
||||
@ -1076,36 +1034,33 @@ DetermineKindOfConditional(const char **pp, bool *out_plain,
|
||||
bool (**out_evalBare)(const char *),
|
||||
bool *out_negate)
|
||||
{
|
||||
const char *p = *pp;
|
||||
const char *p = *pp + 2;
|
||||
|
||||
p += 2;
|
||||
*out_plain = false;
|
||||
*out_evalBare = FuncDefined;
|
||||
*out_negate = false;
|
||||
if (*p == 'n') {
|
||||
p++;
|
||||
*out_negate = true;
|
||||
}
|
||||
if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */
|
||||
p += 3;
|
||||
} else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */
|
||||
p += 4;
|
||||
*out_negate = skip_string(&p, "n");
|
||||
|
||||
if (skip_string(&p, "def")) { /* .ifdef and .ifndef */
|
||||
} else if (skip_string(&p, "make")) /* .ifmake and .ifnmake */
|
||||
*out_evalBare = FuncMake;
|
||||
} else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */
|
||||
else if (!*out_negate) /* plain .if */
|
||||
*out_plain = true;
|
||||
} else {
|
||||
/*
|
||||
* TODO: Add error message about unknown directive,
|
||||
* since there is no other known directive that starts
|
||||
* with 'el' or 'if'.
|
||||
*
|
||||
* Example: .elifx 123
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
else
|
||||
goto unknown_directive;
|
||||
if (ch_isalpha(*p))
|
||||
goto unknown_directive;
|
||||
|
||||
*pp = p;
|
||||
return true;
|
||||
|
||||
unknown_directive:
|
||||
/*
|
||||
* TODO: Add error message about unknown directive, since there is no
|
||||
* other known directive that starts with 'el' or 'if'.
|
||||
*
|
||||
* Example: .elifx 123
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1129,16 +1084,16 @@ DetermineKindOfConditional(const char **pp, bool *out_plain,
|
||||
* parenthetical groupings thereof.
|
||||
*
|
||||
* Results:
|
||||
* COND_PARSE to continue parsing the lines that follow the
|
||||
* CR_TRUE to continue parsing the lines that follow the
|
||||
* conditional (when <cond> evaluates to true)
|
||||
* COND_SKIP to skip the lines after the conditional
|
||||
* CR_FALSE to skip the lines after the conditional
|
||||
* (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
|
||||
* CR_ERROR if the conditional was not valid, either because of
|
||||
* a syntax error or because some variable was undefined
|
||||
* or because the condition could not be evaluated
|
||||
*/
|
||||
CondEvalResult
|
||||
CondResult
|
||||
Cond_EvalLine(const char *line)
|
||||
{
|
||||
typedef enum IfState {
|
||||
@ -1146,8 +1101,10 @@ Cond_EvalLine(const char *line)
|
||||
/* None of the previous <cond> evaluated to true. */
|
||||
IFS_INITIAL = 0,
|
||||
|
||||
/* The previous <cond> evaluated to true.
|
||||
* The lines following this condition are interpreted. */
|
||||
/*
|
||||
* The previous <cond> evaluated to true. The lines following
|
||||
* this condition are interpreted.
|
||||
*/
|
||||
IFS_ACTIVE = 1 << 0,
|
||||
|
||||
/* The previous directive was an '.else'. */
|
||||
@ -1165,7 +1122,7 @@ Cond_EvalLine(const char *line)
|
||||
bool (*evalBare)(const char *);
|
||||
bool negate;
|
||||
bool isElif;
|
||||
bool value;
|
||||
CondResult res;
|
||||
IfState state;
|
||||
const char *p = line;
|
||||
|
||||
@ -1186,13 +1143,13 @@ Cond_EvalLine(const char *line)
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less endif");
|
||||
return COND_PARSE;
|
||||
return CR_TRUE;
|
||||
}
|
||||
|
||||
/* Return state for previous conditional */
|
||||
cond_depth--;
|
||||
return cond_states[cond_depth] & IFS_ACTIVE
|
||||
? COND_PARSE : COND_SKIP;
|
||||
? CR_TRUE : CR_FALSE;
|
||||
}
|
||||
|
||||
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
|
||||
@ -1203,13 +1160,12 @@ Cond_EvalLine(const char *line)
|
||||
* transformation rule like '.err.txt',
|
||||
* therefore no error message here.
|
||||
*/
|
||||
return COND_INVALID;
|
||||
return CR_ERROR;
|
||||
}
|
||||
|
||||
/* Quite likely this is 'else' or 'elif' */
|
||||
p += 2;
|
||||
if (is_token(p, "se", 2)) { /* It is an 'else'. */
|
||||
|
||||
if (strncmp(p, "se", 2) == 0 && !ch_isalpha(p[2])) {
|
||||
if (p[2] != '\0')
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"The .else directive "
|
||||
@ -1217,7 +1173,7 @@ Cond_EvalLine(const char *line)
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less else");
|
||||
return COND_PARSE;
|
||||
return CR_TRUE;
|
||||
}
|
||||
|
||||
state = cond_states[cond_depth];
|
||||
@ -1226,12 +1182,12 @@ Cond_EvalLine(const char *line)
|
||||
} else {
|
||||
if (state & IFS_SEEN_ELSE)
|
||||
Parse_Error(PARSE_WARNING,
|
||||
"extra else");
|
||||
"extra else");
|
||||
state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
}
|
||||
cond_states[cond_depth] = state;
|
||||
|
||||
return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
|
||||
return state & IFS_ACTIVE ? CR_TRUE : CR_FALSE;
|
||||
}
|
||||
/* Assume for now it is an elif */
|
||||
isElif = true;
|
||||
@ -1243,27 +1199,27 @@ Cond_EvalLine(const char *line)
|
||||
* Unknown directive. It might still be a transformation rule
|
||||
* like '.elisp.scm', therefore no error message here.
|
||||
*/
|
||||
return COND_INVALID; /* Not an ifxxx or elifxxx line */
|
||||
return CR_ERROR; /* Not an ifxxx or elifxxx line */
|
||||
}
|
||||
|
||||
if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
|
||||
return COND_INVALID;
|
||||
return CR_ERROR;
|
||||
|
||||
if (isElif) {
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less elif");
|
||||
return COND_PARSE;
|
||||
return CR_TRUE;
|
||||
}
|
||||
state = cond_states[cond_depth];
|
||||
if (state & IFS_SEEN_ELSE) {
|
||||
Parse_Error(PARSE_WARNING, "extra elif");
|
||||
cond_states[cond_depth] =
|
||||
IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
return COND_SKIP;
|
||||
return CR_FALSE;
|
||||
}
|
||||
if (state != IFS_INITIAL) {
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
return CR_FALSE;
|
||||
}
|
||||
} else {
|
||||
/* Normal .if */
|
||||
@ -1275,8 +1231,7 @@ Cond_EvalLine(const char *line)
|
||||
*/
|
||||
cond_states_cap += 32;
|
||||
cond_states = bmake_realloc(cond_states,
|
||||
cond_states_cap *
|
||||
sizeof *cond_states);
|
||||
cond_states_cap * sizeof *cond_states);
|
||||
}
|
||||
state = cond_states[cond_depth];
|
||||
cond_depth++;
|
||||
@ -1286,26 +1241,22 @@ Cond_EvalLine(const char *line)
|
||||
* treat as always false.
|
||||
*/
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
return CR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* And evaluate the conditional expression */
|
||||
if (CondEvalExpression(p, &value, plain, evalBare, negate,
|
||||
true, false) == 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. */
|
||||
res = CondEvalExpression(p, plain, evalBare, negate, true, false);
|
||||
if (res == CR_ERROR) {
|
||||
/* Syntax error, error message already output. */
|
||||
/* Skip everything to the matching '.endif'. */
|
||||
/* An extra '.else' is not detected in this case. */
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
return CR_FALSE;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
cond_states[cond_depth] = IFS_INITIAL;
|
||||
return COND_SKIP;
|
||||
}
|
||||
cond_states[cond_depth] = IFS_ACTIVE;
|
||||
return COND_PARSE;
|
||||
cond_states[cond_depth] = res == CR_TRUE ? IFS_ACTIVE : IFS_INITIAL;
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1315,7 +1266,7 @@ Cond_restore_depth(unsigned int saved_depth)
|
||||
|
||||
if (open_conds != 0 || saved_depth > cond_depth) {
|
||||
Parse_Error(PARSE_FATAL, "%u open conditional%s",
|
||||
open_conds, open_conds == 1 ? "" : "s");
|
||||
open_conds, open_conds == 1 ? "" : "s");
|
||||
cond_depth = cond_min_depth;
|
||||
}
|
||||
|
||||
|
21
dir.c
21
dir.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 rillig Exp $ */
|
||||
/* $NetBSD: dir.c,v 1.278 2022/02/04 23:22:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -138,7 +138,7 @@
|
||||
#include "job.h"
|
||||
|
||||
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: dir.c,v 1.278 2022/02/04 23:22:19 rillig Exp $");
|
||||
|
||||
/*
|
||||
* A search path is a list of CachedDir structures. A CachedDir has in it the
|
||||
@ -672,8 +672,8 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
|
||||
|
||||
{
|
||||
char *fullName = isDot
|
||||
? bmake_strdup(base)
|
||||
: str_concat3(dirName, "/", base);
|
||||
? bmake_strdup(base)
|
||||
: str_concat3(dirName, "/", base);
|
||||
Lst_Append(expansions, fullName);
|
||||
}
|
||||
}
|
||||
@ -792,7 +792,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
|
||||
size_t piece_len = (size_t)(piece_end - piece);
|
||||
|
||||
char *file = concat3(prefix, prefix_len, piece, piece_len,
|
||||
suffix, suffix_len);
|
||||
suffix, suffix_len);
|
||||
|
||||
if (contains_wildcard(file)) {
|
||||
SearchPath_Expand(path, file, expansions);
|
||||
@ -984,8 +984,9 @@ static char *
|
||||
DirLookupSubdir(CachedDir *dir, const char *name)
|
||||
{
|
||||
struct cached_stat cst;
|
||||
char *file = dir == dot ? bmake_strdup(name)
|
||||
: str_concat3(dir->name, "/", name);
|
||||
char *file = dir == dot
|
||||
? bmake_strdup(name)
|
||||
: str_concat3(dir->name, "/", name);
|
||||
|
||||
DEBUG1(DIR, "checking %s ...\n", file);
|
||||
|
||||
@ -1424,9 +1425,9 @@ ResolveMovedDepends(GNode *gn)
|
||||
gn->path = bmake_strdup(fullName);
|
||||
if (!Job_RunTarget(".STALE", gn->fname))
|
||||
fprintf(stdout, /* XXX: Why stdout? */
|
||||
"%s: %s, %d: ignoring stale %s for %s, found %s\n",
|
||||
progname, gn->fname, gn->lineno,
|
||||
makeDependfile, gn->name, fullName);
|
||||
"%s: %s, %u: ignoring stale %s for %s, found %s\n",
|
||||
progname, gn->fname, gn->lineno,
|
||||
makeDependfile, gn->name, fullName);
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
14
dir.h
14
dir.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.h,v 1.44 2021/04/03 11:08:40 rillig Exp $ */
|
||||
/* $NetBSD: dir.h,v 1.46 2021/12/15 12:08:25 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -82,18 +82,18 @@ void Dir_InitCur(const char *);
|
||||
void Dir_InitDot(void);
|
||||
void Dir_End(void);
|
||||
void Dir_SetPATH(void);
|
||||
bool Dir_HasWildcards(const char *);
|
||||
bool Dir_HasWildcards(const char *) MAKE_ATTR_USE;
|
||||
void SearchPath_Expand(SearchPath *, const char *, StringList *);
|
||||
char *Dir_FindFile(const char *, SearchPath *);
|
||||
char *Dir_FindHereOrAbove(const char *, const char *);
|
||||
char *Dir_FindFile(const char *, SearchPath *) MAKE_ATTR_USE;
|
||||
char *Dir_FindHereOrAbove(const char *, const char *) MAKE_ATTR_USE;
|
||||
void Dir_UpdateMTime(GNode *, bool);
|
||||
CachedDir *SearchPath_Add(SearchPath *, const char *);
|
||||
char *SearchPath_ToFlags(SearchPath *, const char *);
|
||||
char *SearchPath_ToFlags(SearchPath *, const char *) MAKE_ATTR_USE;
|
||||
void SearchPath_Clear(SearchPath *);
|
||||
void SearchPath_AddAll(SearchPath *, SearchPath *);
|
||||
void Dir_PrintDirectories(void);
|
||||
void SearchPath_Print(const SearchPath *);
|
||||
SearchPath *Dir_CopyDirSearchPath(void);
|
||||
SearchPath *Dir_CopyDirSearchPath(void) MAKE_ATTR_USE;
|
||||
|
||||
/* Stripped-down variant of struct stat. */
|
||||
struct cached_stat {
|
||||
@ -104,4 +104,4 @@ struct cached_stat {
|
||||
int cached_lstat(const char *, struct cached_stat *);
|
||||
int cached_stat(const char *, struct cached_stat *);
|
||||
|
||||
#endif /* MAKE_DIR_H */
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon.h,v 1.5 2021/01/19 20:51:46 rillig Exp $ */
|
||||
/* $NetBSD: filemon.h,v 1.6 2021/12/15 12:08:25 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
@ -50,4 +50,4 @@ int filemon_setpid_child(const struct filemon *, pid_t);
|
||||
int filemon_readfd(const struct filemon *);
|
||||
int filemon_process(struct filemon *);
|
||||
|
||||
#endif /* MAKE_FILEMON_H */
|
||||
#endif
|
||||
|
228
for.c
228
for.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $ */
|
||||
/* $NetBSD: for.c,v 1.167 2022/02/04 23:22:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1992, The Regents of the University of California.
|
||||
@ -58,26 +58,18 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.167 2022/02/04 23:22:19 rillig Exp $");
|
||||
|
||||
|
||||
/* One of the variables to the left of the "in" in a .for loop. */
|
||||
typedef struct ForVar {
|
||||
char *name;
|
||||
size_t nameLen;
|
||||
} ForVar;
|
||||
|
||||
typedef struct ForLoop {
|
||||
Buffer body; /* Unexpanded body of the loop */
|
||||
Vector /* of ForVar */ vars; /* Iteration variables */
|
||||
Vector /* of 'char *' */ vars; /* Iteration variables */
|
||||
SubstringWords items; /* Substitution items */
|
||||
Buffer curBody; /* Expanded body of the current iteration */
|
||||
Buffer body; /* Unexpanded body of the loop */
|
||||
unsigned int nextItem; /* Where to continue iterating */
|
||||
} ForLoop;
|
||||
|
||||
|
||||
static ForLoop *accumFor; /* Loop being accumulated */
|
||||
static int forLevel = 0; /* Nesting level */
|
||||
|
||||
|
||||
static ForLoop *
|
||||
@ -85,38 +77,49 @@ ForLoop_New(void)
|
||||
{
|
||||
ForLoop *f = bmake_malloc(sizeof *f);
|
||||
|
||||
Buf_Init(&f->body);
|
||||
Vector_Init(&f->vars, sizeof(ForVar));
|
||||
Vector_Init(&f->vars, sizeof(char *));
|
||||
SubstringWords_Init(&f->items);
|
||||
Buf_Init(&f->curBody);
|
||||
Buf_Init(&f->body);
|
||||
f->nextItem = 0;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
ForLoop_Free(ForLoop *f)
|
||||
{
|
||||
Buf_Done(&f->body);
|
||||
|
||||
while (f->vars.len > 0) {
|
||||
ForVar *var = Vector_Pop(&f->vars);
|
||||
free(var->name);
|
||||
}
|
||||
while (f->vars.len > 0)
|
||||
free(*(char **)Vector_Pop(&f->vars));
|
||||
Vector_Done(&f->vars);
|
||||
|
||||
SubstringWords_Free(f->items);
|
||||
Buf_Done(&f->curBody);
|
||||
Buf_Done(&f->body);
|
||||
|
||||
free(f);
|
||||
}
|
||||
|
||||
static void
|
||||
ForLoop_AddVar(ForLoop *f, const char *name, size_t len)
|
||||
char *
|
||||
ForLoop_Details(ForLoop *f)
|
||||
{
|
||||
ForVar *var = Vector_Push(&f->vars);
|
||||
var->name = bmake_strldup(name, len);
|
||||
var->nameLen = len;
|
||||
size_t i, n;
|
||||
const char **vars;
|
||||
const Substring *items;
|
||||
Buffer buf;
|
||||
|
||||
n = f->vars.len;
|
||||
vars = f->vars.items;
|
||||
assert(f->nextItem >= n);
|
||||
items = f->items.words + f->nextItem - n;
|
||||
|
||||
Buf_Init(&buf);
|
||||
for (i = 0; i < n; i++) {
|
||||
if (i > 0)
|
||||
Buf_AddStr(&buf, ", ");
|
||||
Buf_AddStr(&buf, vars[i]);
|
||||
Buf_AddStr(&buf, " = ");
|
||||
Buf_AddBytesBetween(&buf, items[i].start, items[i].end);
|
||||
}
|
||||
return Buf_DoneData(&buf);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -145,7 +148,7 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
|
||||
break;
|
||||
}
|
||||
|
||||
ForLoop_AddVar(f, p, len);
|
||||
*(char **)Vector_Push(&f->vars) = bmake_strldup(p, len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
@ -174,9 +177,9 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
|
||||
free(items);
|
||||
|
||||
if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0]))
|
||||
f->items.len = 0; /* .for var in ${:U} */
|
||||
f->items.len = 0; /* .for var in ${:U} */
|
||||
|
||||
if (f->items.len != 0 && f->items.len % f->vars.len != 0) {
|
||||
if (f->items.len % f->vars.len != 0) {
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"Wrong number of words (%u) in .for "
|
||||
"substitution list with %u variables",
|
||||
@ -204,47 +207,38 @@ IsEndfor(const char *p)
|
||||
* Evaluate the for loop in the passed line. The line looks like this:
|
||||
* .for <varname...> in <value...>
|
||||
*
|
||||
* Input:
|
||||
* line Line to parse
|
||||
*
|
||||
* Results:
|
||||
* 0: Not a .for statement, parse the line
|
||||
* 1: We found a for loop
|
||||
* -1: A .for statement with a bad syntax error, discard.
|
||||
* 0 not a .for directive
|
||||
* 1 found a .for directive
|
||||
* -1 erroneous .for directive
|
||||
*/
|
||||
int
|
||||
For_Eval(const char *line)
|
||||
{
|
||||
ForLoop *f;
|
||||
const char *p;
|
||||
ForLoop *f;
|
||||
|
||||
p = line + 1; /* skip the '.' */
|
||||
cpp_skip_whitespace(&p);
|
||||
|
||||
if (!IsFor(p)) {
|
||||
if (IsEndfor(p)) {
|
||||
Parse_Error(PARSE_FATAL, "for-less endfor");
|
||||
if (IsFor(p)) {
|
||||
p += 3;
|
||||
|
||||
f = ForLoop_New();
|
||||
if (!ForLoop_ParseVarnames(f, &p)) {
|
||||
ForLoop_Free(f);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
p += 3;
|
||||
if (!ForLoop_ParseItems(f, p))
|
||||
f->items.len = 0; /* don't iterate */
|
||||
|
||||
f = ForLoop_New();
|
||||
|
||||
if (!ForLoop_ParseVarnames(f, &p)) {
|
||||
ForLoop_Free(f);
|
||||
accumFor = f;
|
||||
return 1;
|
||||
} else if (IsEndfor(p)) {
|
||||
Parse_Error(PARSE_FATAL, "for-less endfor");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ForLoop_ParseItems(f, p)) {
|
||||
/* Continue parsing the .for loop, but don't iterate. */
|
||||
f->items.len = 0;
|
||||
}
|
||||
|
||||
accumFor = f;
|
||||
forLevel = 1;
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -252,7 +246,7 @@ For_Eval(const char *line)
|
||||
* Returns false when the matching .endfor is reached.
|
||||
*/
|
||||
bool
|
||||
For_Accum(const char *line)
|
||||
For_Accum(const char *line, int *forLevel)
|
||||
{
|
||||
const char *p = line;
|
||||
|
||||
@ -261,12 +255,12 @@ For_Accum(const char *line)
|
||||
cpp_skip_whitespace(&p);
|
||||
|
||||
if (IsEndfor(p)) {
|
||||
DEBUG1(FOR, "For: end for %d\n", forLevel);
|
||||
if (--forLevel <= 0)
|
||||
DEBUG1(FOR, "For: end for %d\n", *forLevel);
|
||||
if (--*forLevel == 0)
|
||||
return false;
|
||||
} else if (IsFor(p)) {
|
||||
forLevel++;
|
||||
DEBUG1(FOR, "For: new loop %d\n", forLevel);
|
||||
(*forLevel)++;
|
||||
DEBUG1(FOR, "For: new loop %d\n", *forLevel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -325,12 +319,11 @@ NeedsEscapes(Substring value, char endc)
|
||||
|
||||
/*
|
||||
* While expanding the body of a .for loop, write the item in the ${:U...}
|
||||
* expression, escaping characters as needed.
|
||||
*
|
||||
* The result is later unescaped by ApplyModifier_Defined.
|
||||
* expression, escaping characters as needed. The result is later unescaped
|
||||
* by ApplyModifier_Defined.
|
||||
*/
|
||||
static void
|
||||
Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
{
|
||||
const char *p;
|
||||
char ch;
|
||||
@ -340,9 +333,7 @@ Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Escape ':', '$', '\\' and 'endc' - these will be removed later by
|
||||
* :U processing, see ApplyModifier_Defined. */
|
||||
for (p = item.start; p != item.end; p++) {
|
||||
for (p = item.start; p != item.end;) {
|
||||
ch = *p;
|
||||
if (ch == '$') {
|
||||
size_t len = ExprLen(p + 1, item.end);
|
||||
@ -352,7 +343,7 @@ Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
* See directive-for-escape.mk, ExprLen.
|
||||
*/
|
||||
Buf_AddBytes(cmds, p, 1 + len);
|
||||
p += len;
|
||||
p += 1 + len;
|
||||
continue;
|
||||
}
|
||||
Buf_AddByte(cmds, '\\');
|
||||
@ -363,6 +354,7 @@ Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
ch = ' '; /* prevent newline injection */
|
||||
}
|
||||
Buf_AddByte(cmds, ch);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,36 +363,30 @@ Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
|
||||
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
|
||||
*/
|
||||
static void
|
||||
ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
|
||||
char endc, const char **inout_mark)
|
||||
ForLoop_SubstVarLong(ForLoop *f, unsigned int firstItem, Buffer *body,
|
||||
const char **pp, char endc, const char **inout_mark)
|
||||
{
|
||||
size_t i;
|
||||
const char *p = *pp;
|
||||
const char *start = *pp;
|
||||
const char **vars = Vector_Get(&f->vars, 0);
|
||||
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
const ForVar *forVar = Vector_Get(&f->vars, i);
|
||||
const char *varname = forVar->name;
|
||||
size_t varnameLen = forVar->nameLen;
|
||||
const char *p = start;
|
||||
|
||||
if (varnameLen >= (size_t)(bodyEnd - p))
|
||||
continue;
|
||||
if (memcmp(p, varname, varnameLen) != 0)
|
||||
if (!cpp_skip_string(&p, vars[i]))
|
||||
continue;
|
||||
/* XXX: why test for backslash here? */
|
||||
if (p[varnameLen] != ':' && p[varnameLen] != endc &&
|
||||
p[varnameLen] != '\\')
|
||||
if (*p != ':' && *p != endc && *p != '\\')
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Found a variable match. Skip over the variable name and
|
||||
* instead add ':U<value>' to the current body.
|
||||
*/
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
|
||||
Buf_AddStr(&f->curBody, ":U");
|
||||
Buf_AddEscaped(&f->curBody,
|
||||
f->items.words[f->nextItem + i], endc);
|
||||
Buf_AddBytesBetween(body, *inout_mark, start);
|
||||
Buf_AddStr(body, ":U");
|
||||
AddEscaped(body, f->items.words[firstItem + i], endc);
|
||||
|
||||
p += varnameLen;
|
||||
*inout_mark = p;
|
||||
*pp = p;
|
||||
return;
|
||||
@ -412,10 +398,11 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
|
||||
* variable expressions like $i with their ${:U...} expansion.
|
||||
*/
|
||||
static void
|
||||
ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
|
||||
ForLoop_SubstVarShort(ForLoop *f, unsigned int firstItem, Buffer *body,
|
||||
const char *p, const char **inout_mark)
|
||||
{
|
||||
const char ch = *p;
|
||||
const ForVar *vars;
|
||||
const char **vars;
|
||||
size_t i;
|
||||
|
||||
/* Skip $$ and stupid ones. */
|
||||
@ -424,20 +411,20 @@ ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
|
||||
|
||||
vars = Vector_Get(&f->vars, 0);
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
const char *varname = vars[i].name;
|
||||
const char *varname = vars[i];
|
||||
if (varname[0] == ch && varname[1] == '\0')
|
||||
goto found;
|
||||
}
|
||||
return;
|
||||
|
||||
found:
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
|
||||
Buf_AddBytesBetween(body, *inout_mark, p);
|
||||
*inout_mark = p + 1;
|
||||
|
||||
/* Replace $<ch> with ${:U<value>} */
|
||||
Buf_AddStr(&f->curBody, "{:U");
|
||||
Buf_AddEscaped(&f->curBody, f->items.words[f->nextItem + i], '}');
|
||||
Buf_AddByte(&f->curBody, '}');
|
||||
Buf_AddStr(body, "{:U");
|
||||
AddEscaped(body, f->items.words[firstItem + i], '}');
|
||||
Buf_AddByte(body, '}');
|
||||
}
|
||||
|
||||
/*
|
||||
@ -454,68 +441,59 @@ ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
|
||||
* possible to contrive a makefile where an unwanted substitution happens.
|
||||
*/
|
||||
static void
|
||||
ForLoop_SubstBody(ForLoop *f)
|
||||
ForLoop_SubstBody(ForLoop *f, unsigned int firstItem, Buffer *body)
|
||||
{
|
||||
const char *p, *bodyEnd;
|
||||
const char *p, *end;
|
||||
const char *mark; /* where the last substitution left off */
|
||||
|
||||
Buf_Empty(&f->curBody);
|
||||
Buf_Clear(body);
|
||||
|
||||
mark = f->body.data;
|
||||
bodyEnd = f->body.data + f->body.len;
|
||||
end = f->body.data + f->body.len;
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
if (p[1] == '{' || p[1] == '(') {
|
||||
char endc = p[1] == '{' ? '}' : ')';
|
||||
p += 2;
|
||||
ForLoop_SubstVarLong(f, &p, bodyEnd, endc, &mark);
|
||||
ForLoop_SubstVarLong(f, firstItem, body,
|
||||
&p, endc, &mark);
|
||||
} else if (p[1] != '\0') {
|
||||
ForLoop_SubstVarShort(f, p + 1, &mark);
|
||||
ForLoop_SubstVarShort(f, firstItem, body,
|
||||
p + 1, &mark);
|
||||
p += 2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
|
||||
Buf_AddBytesBetween(body, mark, end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the body for the current iteration by copying the unexpanded body,
|
||||
* replacing the expressions for the iteration variables on the way.
|
||||
*/
|
||||
static char *
|
||||
ForReadMore(void *v_arg, size_t *out_len)
|
||||
bool
|
||||
For_NextIteration(ForLoop *f, Buffer *body)
|
||||
{
|
||||
ForLoop *f = v_arg;
|
||||
if (f->nextItem == f->items.len)
|
||||
return false;
|
||||
|
||||
if (f->nextItem == f->items.len) {
|
||||
/* No more iterations */
|
||||
ForLoop_Free(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ForLoop_SubstBody(f);
|
||||
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
|
||||
f->nextItem += (unsigned int)f->vars.len;
|
||||
|
||||
*out_len = f->curBody.len;
|
||||
return f->curBody.data;
|
||||
ForLoop_SubstBody(f, f->nextItem - (unsigned int)f->vars.len, body);
|
||||
DEBUG1(FOR, "For: loop body:\n%s", body->data);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Run the .for loop, imitating the actions of an include file. */
|
||||
void
|
||||
For_Run(int lineno)
|
||||
For_Run(unsigned headLineno, unsigned bodyReadLines)
|
||||
{
|
||||
Buffer buf;
|
||||
ForLoop *f = accumFor;
|
||||
accumFor = NULL;
|
||||
|
||||
if (f->items.len == 0) {
|
||||
/*
|
||||
* Nothing to expand - possibly due to an earlier syntax
|
||||
* error.
|
||||
*/
|
||||
if (f->items.len > 0) {
|
||||
Buf_Init(&buf);
|
||||
Parse_PushInput(NULL, headLineno, bodyReadLines, buf, f);
|
||||
} else
|
||||
ForLoop_Free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
Parse_PushInput(NULL, lineno, -1, ForReadMore, f);
|
||||
}
|
||||
|
80
hash.c
80
hash.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.c,v 1.66 2021/12/07 21:58:01 rillig Exp $ */
|
||||
/* $NetBSD: hash.c,v 1.71 2022/01/27 11:00:07 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.66 2021/12/07 21:58:01 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.71 2022/01/27 11:00:07 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.66 2021/12/07 21:58:01 rillig Exp $");
|
||||
|
||||
/* This hash function matches Gosling's Emacs and java.lang.String. */
|
||||
static unsigned int
|
||||
Hash_String(const char *key, size_t *out_keylen)
|
||||
Hash_String(const char *key, const char **out_keyEnd)
|
||||
{
|
||||
unsigned int h;
|
||||
const char *p;
|
||||
@ -93,8 +93,7 @@ Hash_String(const char *key, size_t *out_keylen)
|
||||
for (p = key; *p != '\0'; p++)
|
||||
h = 31 * h + (unsigned char)*p;
|
||||
|
||||
if (out_keylen != NULL)
|
||||
*out_keylen = (size_t)(p - key);
|
||||
*out_keyEnd = p;
|
||||
return h;
|
||||
}
|
||||
|
||||
@ -112,53 +111,22 @@ Hash_Substring(Substring key)
|
||||
}
|
||||
|
||||
static HashEntry *
|
||||
HashTable_Find(HashTable *t, unsigned int h, const char *key)
|
||||
HashTable_Find(HashTable *t, Substring key, unsigned int h)
|
||||
{
|
||||
HashEntry *e;
|
||||
unsigned int chainlen = 0;
|
||||
size_t keyLen = Substring_Length(key);
|
||||
|
||||
#ifdef DEBUG_HASH_LOOKUP
|
||||
DEBUG4(HASH, "%s: %p h=%08x key=%s\n", __func__, t, h, key);
|
||||
DEBUG4(HASH, "HashTable_Find: %p h=%08x key=%.*s\n",
|
||||
t, h, (int)keyLen, key.start);
|
||||
#endif
|
||||
|
||||
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
|
||||
chainlen++;
|
||||
if (e->key_hash == h && strcmp(e->key, key) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (chainlen > t->maxchain)
|
||||
t->maxchain = chainlen;
|
||||
|
||||
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
|
||||
DEBUG5(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))
|
||||
if (e->key_hash == h &&
|
||||
strncmp(e->key, key.start, keyLen) == 0 &&
|
||||
e->key[keyLen] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
@ -213,8 +181,9 @@ HashTable_Done(HashTable *t)
|
||||
HashEntry *
|
||||
HashTable_FindEntry(HashTable *t, const char *key)
|
||||
{
|
||||
unsigned int h = Hash_String(key, NULL);
|
||||
return HashTable_Find(t, h, key);
|
||||
const char *keyEnd;
|
||||
unsigned int h = Hash_String(key, &keyEnd);
|
||||
return HashTable_Find(t, Substring_Init(key, keyEnd), h);
|
||||
}
|
||||
|
||||
/* Find the value corresponding to the key, or return NULL. */
|
||||
@ -232,7 +201,7 @@ HashTable_FindValue(HashTable *t, const char *key)
|
||||
void *
|
||||
HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
|
||||
{
|
||||
HashEntry *he = HashTable_FindEntryBySubstring(t, key, h);
|
||||
HashEntry *he = HashTable_Find(t, key, h);
|
||||
return he != NULL ? he->value : NULL;
|
||||
}
|
||||
|
||||
@ -268,8 +237,8 @@ HashTable_Enlarge(HashTable *t)
|
||||
t->bucketsSize = newSize;
|
||||
t->bucketsMask = newMask;
|
||||
t->buckets = newBuckets;
|
||||
DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
|
||||
__func__, (void *)t, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
DEBUG4(HASH, "HashTable_Enlarge: %p size=%d entries=%d maxchain=%d\n",
|
||||
(void *)t, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
t->maxchain = 0;
|
||||
}
|
||||
|
||||
@ -280,9 +249,9 @@ HashTable_Enlarge(HashTable *t)
|
||||
HashEntry *
|
||||
HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
|
||||
{
|
||||
size_t keylen;
|
||||
unsigned int h = Hash_String(key, &keylen);
|
||||
HashEntry *he = HashTable_Find(t, h, key);
|
||||
const char *keyEnd;
|
||||
unsigned int h = Hash_String(key, &keyEnd);
|
||||
HashEntry *he = HashTable_Find(t, Substring_Init(key, keyEnd), h);
|
||||
|
||||
if (he != NULL) {
|
||||
if (out_isNew != NULL)
|
||||
@ -293,10 +262,10 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
|
||||
if (t->numEntries >= rebuildLimit * t->bucketsSize)
|
||||
HashTable_Enlarge(t);
|
||||
|
||||
he = bmake_malloc(sizeof *he + keylen);
|
||||
he = bmake_malloc(sizeof *he + (size_t)(keyEnd - key));
|
||||
he->value = NULL;
|
||||
he->key_hash = h;
|
||||
memcpy(he->key, key, keylen + 1);
|
||||
memcpy(he->key, key, (size_t)(keyEnd - key) + 1);
|
||||
|
||||
he->next = t->buckets[h & t->bucketsMask];
|
||||
t->buckets[h & t->bucketsMask] = he;
|
||||
@ -307,12 +276,11 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
|
||||
return he;
|
||||
}
|
||||
|
||||
HashEntry *
|
||||
void
|
||||
HashTable_Set(HashTable *t, const char *key, void *value)
|
||||
{
|
||||
HashEntry *he = HashTable_CreateEntry(t, key, NULL);
|
||||
HashEntry_Set(he, value);
|
||||
return he;
|
||||
}
|
||||
|
||||
/* Delete the entry from the table and free the associated memory. */
|
||||
@ -361,5 +329,5 @@ void
|
||||
HashTable_DebugStats(HashTable *t, const char *name)
|
||||
{
|
||||
DEBUG4(HASH, "HashTable %s: size=%u numEntries=%u maxchain=%u\n",
|
||||
name, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
name, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
}
|
||||
|
24
hash.h
24
hash.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.h,v 1.41 2021/12/07 21:58:01 rillig Exp $ */
|
||||
/* $NetBSD: hash.h,v 1.46 2022/01/31 22:58:26 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -88,8 +88,8 @@ typedef struct HashEntry {
|
||||
|
||||
/* The hash table containing the entries. */
|
||||
typedef struct HashTable {
|
||||
HashEntry **buckets; /* Pointers to HashEntry, one
|
||||
* for each bucket in the table. */
|
||||
HashEntry **buckets; /* Pointers to HashEntry, one for each bucket
|
||||
* in the table. */
|
||||
unsigned int bucketsSize;
|
||||
unsigned int numEntries; /* Number of entries in the table. */
|
||||
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
|
||||
@ -108,7 +108,7 @@ typedef struct HashSet {
|
||||
HashTable tbl;
|
||||
} HashSet;
|
||||
|
||||
MAKE_INLINE void *
|
||||
MAKE_INLINE void * MAKE_ATTR_USE
|
||||
HashEntry_Get(HashEntry *h)
|
||||
{
|
||||
return h->value;
|
||||
@ -131,16 +131,16 @@ HashIter_Init(HashIter *hi, HashTable *t)
|
||||
|
||||
void HashTable_Init(HashTable *);
|
||||
void HashTable_Done(HashTable *);
|
||||
HashEntry *HashTable_FindEntry(HashTable *, const char *);
|
||||
void *HashTable_FindValue(HashTable *, const char *);
|
||||
unsigned int Hash_Substring(Substring);
|
||||
void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int);
|
||||
HashEntry *HashTable_FindEntry(HashTable *, const char *) MAKE_ATTR_USE;
|
||||
void *HashTable_FindValue(HashTable *, const char *) MAKE_ATTR_USE;
|
||||
unsigned int Hash_Substring(Substring) MAKE_ATTR_USE;
|
||||
void *HashTable_FindValueBySubstringHash(HashTable *, Substring, unsigned int)
|
||||
MAKE_ATTR_USE;
|
||||
HashEntry *HashTable_CreateEntry(HashTable *, const char *, bool *);
|
||||
HashEntry *HashTable_Set(HashTable *, const char *, void *);
|
||||
void HashTable_Set(HashTable *, const char *, void *);
|
||||
void HashTable_DeleteEntry(HashTable *, HashEntry *);
|
||||
void HashTable_DebugStats(HashTable *, const char *);
|
||||
|
||||
void HashIter_Init(HashIter *, HashTable *);
|
||||
HashEntry *HashIter_Next(HashIter *);
|
||||
|
||||
MAKE_INLINE void
|
||||
@ -164,7 +164,7 @@ HashSet_Add(HashSet *set, const char *key)
|
||||
return isNew;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
HashSet_Contains(HashSet *set, const char *key)
|
||||
{
|
||||
return HashTable_FindEntry(&set->tbl, key) != NULL;
|
||||
@ -176,4 +176,4 @@ HashIter_InitSet(HashIter *hi, HashSet *set)
|
||||
HashIter_Init(hi, &set->tbl);
|
||||
}
|
||||
|
||||
#endif /* MAKE_HASH_H */
|
||||
#endif
|
||||
|
99
job.c
99
job.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $ */
|
||||
/* $NetBSD: job.c,v 1.451 2022/02/04 23:22:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -155,7 +155,7 @@
|
||||
#include "trace.h"
|
||||
|
||||
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: job.c,v 1.451 2022/02/04 23:22:19 rillig Exp $");
|
||||
|
||||
/*
|
||||
* A shell defines how the commands are run. All commands for a target are
|
||||
@ -214,13 +214,15 @@ typedef struct Shell {
|
||||
const char *errOff; /* command to turn off error checking */
|
||||
|
||||
const char *echoTmpl; /* template to echo a command */
|
||||
const char *runIgnTmpl; /* template to run a command
|
||||
* without error checking */
|
||||
const char *runChkTmpl; /* template to run a command
|
||||
* with error checking */
|
||||
const char *runIgnTmpl; /* template to run a command without error
|
||||
* checking */
|
||||
const char *runChkTmpl; /* template to run a command with error
|
||||
* checking */
|
||||
|
||||
/* string literal that results in a newline character when it appears
|
||||
* outside of any 'quote' or "quote" characters */
|
||||
/*
|
||||
* A string literal that results in a newline character when it
|
||||
* occurs outside of any 'quote' or "quote" characters.
|
||||
*/
|
||||
const char *newline;
|
||||
char commentChar; /* character used by shell for comment lines */
|
||||
|
||||
@ -438,7 +440,7 @@ static void watchfd(Job *);
|
||||
static void clearfd(Job *);
|
||||
static bool readyfd(Job *);
|
||||
|
||||
static char *targPrefix = NULL; /* To identify a job change in the output. */
|
||||
static char *targPrefix = NULL; /* To identify a job change in the output. */
|
||||
static Job tokenWaitJob; /* token wait pseudo-job */
|
||||
|
||||
static Job childExitJob; /* child exit pseudo-job */
|
||||
@ -517,13 +519,13 @@ JobDeleteTarget(GNode *gn)
|
||||
return;
|
||||
if (gn->type & OP_PHONY)
|
||||
return;
|
||||
if (Targ_Precious(gn))
|
||||
if (GNode_IsPrecious(gn))
|
||||
return;
|
||||
if (opts.noExecute)
|
||||
return;
|
||||
|
||||
file = GNode_Path(gn);
|
||||
if (eunlink(file) != -1)
|
||||
if (unlink_file(file))
|
||||
Error("*** %s removed", file);
|
||||
}
|
||||
|
||||
@ -924,7 +926,7 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
|
||||
|
||||
run = GNode_ShouldExecute(job->node);
|
||||
|
||||
Var_Subst(ucmd, job->node, VARE_WANTRES, &xcmd);
|
||||
(void)Var_Subst(ucmd, job->node, VARE_WANTRES, &xcmd);
|
||||
/* TODO: handle errors */
|
||||
xcmdStart = xcmd;
|
||||
|
||||
@ -938,7 +940,7 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
|
||||
* We're not actually executing anything...
|
||||
* but this one needs to be - use compat mode just for it.
|
||||
*/
|
||||
Compat_RunCommand(ucmd, job->node, ln);
|
||||
(void)Compat_RunCommand(ucmd, job->node, ln);
|
||||
free(xcmdStart);
|
||||
return;
|
||||
}
|
||||
@ -1119,7 +1121,7 @@ JobFinishDoneExitedError(Job *job, WAIT_T *inout_status)
|
||||
else {
|
||||
if (deleteOnError)
|
||||
JobDeleteTarget(job->node);
|
||||
PrintOnError(job->node, NULL);
|
||||
PrintOnError(job->node, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1279,9 +1281,11 @@ TouchRegular(GNode *gn)
|
||||
return; /* XXX: What about propagating the error? */
|
||||
}
|
||||
|
||||
/* Last resort: update the file's time stamps in the traditional way.
|
||||
/*
|
||||
* Last resort: update the file's time stamps in the traditional way.
|
||||
* XXX: This doesn't work for empty files, which are sometimes used
|
||||
* as marker files. */
|
||||
* as marker files.
|
||||
*/
|
||||
if (read(fd, &c, 1) == 1) {
|
||||
(void)lseek(fd, 0, SEEK_SET);
|
||||
while (write(fd, &c, 1) == -1 && errno == EAGAIN)
|
||||
@ -1383,7 +1387,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
|
||||
if (gn->flags.fromDepend) {
|
||||
if (!Job_RunTarget(".STALE", gn->fname))
|
||||
fprintf(stdout,
|
||||
"%s: %s, %d: ignoring stale %s for %s\n",
|
||||
"%s: %s, %u: ignoring stale %s for %s\n",
|
||||
progname, gn->fname, gn->lineno, makeDependfile,
|
||||
gn->name);
|
||||
return true;
|
||||
@ -1455,9 +1459,8 @@ JobExec(Job *job, char **argv)
|
||||
sigset_t tmask;
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_job_child(job);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Reset all signal handlers; this is necessary because we
|
||||
@ -1539,9 +1542,8 @@ JobExec(Job *job, char **argv)
|
||||
Trace_Log(JOBSTART, job);
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
meta_job_parent(job, cpid);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -1635,7 +1637,7 @@ JobWriteShellCommands(Job *job, GNode *gn, bool *out_run)
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
meta_job_start(job, gn);
|
||||
if (gn->type & OP_SILENT) /* might have changed */
|
||||
if (gn->type & OP_SILENT) /* might have changed */
|
||||
job->echo = false;
|
||||
}
|
||||
#endif
|
||||
@ -1679,7 +1681,7 @@ JobStart(GNode *gn, bool special)
|
||||
|
||||
job->special = special || gn->type & OP_SPECIAL;
|
||||
job->ignerr = opts.ignoreErrors || gn->type & OP_IGNORE;
|
||||
job->echo = !(opts.beSilent || gn->type & OP_SILENT);
|
||||
job->echo = !(opts.silent || gn->type & OP_SILENT);
|
||||
|
||||
/*
|
||||
* Check the commands now so any attributes from .DEFAULT have a
|
||||
@ -1698,11 +1700,11 @@ JobStart(GNode *gn, bool special)
|
||||
* also dead...
|
||||
*/
|
||||
if (!cmdsOK) {
|
||||
PrintOnError(gn, NULL); /* provide some clue */
|
||||
PrintOnError(gn, "\n"); /* provide some clue */
|
||||
DieHorribly();
|
||||
}
|
||||
} else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
|
||||
(!opts.noExecute && !opts.touchFlag)) {
|
||||
(!opts.noExecute && !opts.touch)) {
|
||||
/*
|
||||
* The above condition looks very similar to
|
||||
* GNode_ShouldExecute but is subtly different. It prevents
|
||||
@ -1715,7 +1717,7 @@ JobStart(GNode *gn, bool special)
|
||||
* also dead...
|
||||
*/
|
||||
if (!cmdsOK) {
|
||||
PrintOnError(gn, NULL); /* provide some clue */
|
||||
PrintOnError(gn, "\n"); /* provide some clue */
|
||||
DieHorribly();
|
||||
}
|
||||
|
||||
@ -1935,7 +1937,7 @@ CollectOutput(Job *job, bool finish)
|
||||
* we add one of our own free will.
|
||||
*/
|
||||
if (*cp != '\0') {
|
||||
if (!opts.beSilent)
|
||||
if (!opts.silent)
|
||||
SwitchOutputTo(job->node);
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
@ -1999,7 +2001,7 @@ JobRun(GNode *targ)
|
||||
Compat_Make(targ, targ);
|
||||
/* XXX: Replace with GNode_IsError(gn) */
|
||||
if (targ->made == ERROR) {
|
||||
PrintOnError(targ, "\n\nStop.");
|
||||
PrintOnError(targ, "\n\nStop.\n");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
@ -2147,9 +2149,8 @@ Job_CatchOutput(void)
|
||||
* than job->inPollfd.
|
||||
*/
|
||||
if (useMeta && job->inPollfd != &fds[i]) {
|
||||
if (meta_job_event(job) <= 0) {
|
||||
fds[i].events = 0; /* never mind */
|
||||
}
|
||||
if (meta_job_event(job) <= 0)
|
||||
fds[i].events = 0; /* never mind */
|
||||
}
|
||||
#endif
|
||||
if (--nready == 0)
|
||||
@ -2203,14 +2204,8 @@ Shell_Init(void)
|
||||
free(shellErrFlag);
|
||||
shellErrFlag = NULL;
|
||||
}
|
||||
if (shellErrFlag == NULL) {
|
||||
size_t n = strlen(shell->errFlag) + 2;
|
||||
|
||||
shellErrFlag = bmake_malloc(n);
|
||||
if (shellErrFlag != NULL)
|
||||
snprintf(shellErrFlag, n, "-%s",
|
||||
shell->errFlag);
|
||||
}
|
||||
if (shellErrFlag == NULL)
|
||||
shellErrFlag = str_concat2("-", shell->errFlag);
|
||||
} else if (shellErrFlag != NULL) {
|
||||
free(shellErrFlag);
|
||||
shellErrFlag = NULL;
|
||||
@ -2329,8 +2324,10 @@ Job_Init(void)
|
||||
AddSig(SIGCONT, JobContinueSig);
|
||||
|
||||
(void)Job_RunTarget(".BEGIN", NULL);
|
||||
/* Create the .END node now, even though no code in the unit tests
|
||||
* depends on it. See also Targ_GetEndNode in Compat_Run. */
|
||||
/*
|
||||
* Create the .END node now, even though no code in the unit tests
|
||||
* depends on it. See also Targ_GetEndNode in Compat_Run.
|
||||
*/
|
||||
(void)Targ_GetEndNode();
|
||||
}
|
||||
|
||||
@ -2470,13 +2467,17 @@ Job_ParseShell(char *line)
|
||||
} else if (strncmp(arg, "newline=", 8) == 0) {
|
||||
newShell.newline = arg + 8;
|
||||
} else if (strncmp(arg, "check=", 6) == 0) {
|
||||
/* Before 2020-12-10, these two variables
|
||||
* had been a single variable. */
|
||||
/*
|
||||
* Before 2020-12-10, these two variables had
|
||||
* been a single variable.
|
||||
*/
|
||||
newShell.errOn = arg + 6;
|
||||
newShell.echoTmpl = arg + 6;
|
||||
} else if (strncmp(arg, "ignore=", 7) == 0) {
|
||||
/* Before 2020-12-10, these two variables
|
||||
* had been a single variable. */
|
||||
/*
|
||||
* Before 2020-12-10, these two variables had
|
||||
* been a single variable.
|
||||
*/
|
||||
newShell.errOff = arg + 7;
|
||||
newShell.runIgnTmpl = arg + 7;
|
||||
} else if (strncmp(arg, "errout=", 7) == 0) {
|
||||
@ -2618,7 +2619,7 @@ JobInterrupt(bool runINTERRUPT, int signo)
|
||||
|
||||
JobSigUnlock(&mask);
|
||||
|
||||
if (runINTERRUPT && !opts.touchFlag) {
|
||||
if (runINTERRUPT && !opts.touch) {
|
||||
interrupt = Targ_FindNode(".INTERRUPT");
|
||||
if (interrupt != NULL) {
|
||||
opts.ignoreErrors = false;
|
||||
@ -2838,7 +2839,7 @@ Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz)
|
||||
JobSigLock(&mask);
|
||||
fd = mkTempFile(pattern, tfile, tfile_sz);
|
||||
if (tfile != NULL && !DEBUG(SCRIPT))
|
||||
unlink(tfile);
|
||||
unlink(tfile);
|
||||
JobSigUnlock(&mask);
|
||||
|
||||
return fd;
|
||||
@ -2970,7 +2971,7 @@ Job_RunTarget(const char *target, const char *fname)
|
||||
JobRun(gn);
|
||||
/* XXX: Replace with GNode_IsError(gn) */
|
||||
if (gn->made == ERROR) {
|
||||
PrintOnError(gn, "\n\nStop.");
|
||||
PrintOnError(gn, "\n\nStop.\n");
|
||||
exit(1);
|
||||
}
|
||||
return true;
|
||||
@ -3035,4 +3036,4 @@ emul_poll(struct pollfd *fd, int nfd, int timeout)
|
||||
|
||||
return npoll;
|
||||
}
|
||||
#endif /* USE_SELECT */
|
||||
#endif /* USE_SELECT */
|
||||
|
34
job.h
34
job.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.h,v 1.73 2021/04/03 11:08:40 rillig Exp $ */
|
||||
/* $NetBSD: job.h,v 1.77 2021/12/15 12:58:01 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -80,7 +80,7 @@
|
||||
#ifndef MAKE_JOB_H
|
||||
#define MAKE_JOB_H
|
||||
|
||||
#define TMPPAT "makeXXXXXX" /* relative to tmpdir */
|
||||
#define TMPPAT "makeXXXXXX" /* relative to tmpdir */
|
||||
|
||||
#ifdef USE_SELECT
|
||||
/*
|
||||
@ -92,16 +92,15 @@
|
||||
#define pollfd emul_pollfd
|
||||
|
||||
struct emul_pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
#define POLLIN 0x0001
|
||||
#define POLLOUT 0x0004
|
||||
|
||||
int
|
||||
emul_poll(struct pollfd *fd, int nfd, int timeout);
|
||||
int emul_poll(struct pollfd *, int, int);
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -145,9 +144,11 @@ typedef struct Job {
|
||||
/* The target the child is making */
|
||||
GNode *node;
|
||||
|
||||
/* If one of the shell commands is "...", all following commands are
|
||||
* delayed until the .END node is made. This list node points to the
|
||||
* first of these commands, if any. */
|
||||
/*
|
||||
* If one of the shell commands is "...", all following commands are
|
||||
* delayed until the .END node is made. This list node points to the
|
||||
* first of these commands, if any.
|
||||
*/
|
||||
StringListNode *tailCmds;
|
||||
|
||||
/* This is where the shell commands go. */
|
||||
@ -187,24 +188,25 @@ extern char *shellErrFlag;
|
||||
extern int jobTokensRunning; /* tokens currently "out" */
|
||||
|
||||
void Shell_Init(void);
|
||||
const char *Shell_GetNewline(void);
|
||||
const char *Shell_GetNewline(void) MAKE_ATTR_USE;
|
||||
void Job_Touch(GNode *, bool);
|
||||
bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
|
||||
bool Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...))
|
||||
MAKE_ATTR_USE;
|
||||
void Job_CatchChildren(void);
|
||||
void Job_CatchOutput(void);
|
||||
void Job_Make(GNode *);
|
||||
void Job_Init(void);
|
||||
bool Job_ParseShell(char *);
|
||||
bool Job_ParseShell(char *) MAKE_ATTR_USE;
|
||||
int Job_Finish(void);
|
||||
void Job_End(void);
|
||||
void Job_Wait(void);
|
||||
void Job_AbortAll(void);
|
||||
void Job_TokenReturn(void);
|
||||
bool Job_TokenWithdraw(void);
|
||||
bool Job_TokenWithdraw(void) MAKE_ATTR_USE;
|
||||
void Job_ServerStart(int, int, int);
|
||||
void Job_SetPrefix(void);
|
||||
bool Job_RunTarget(const char *, const char *);
|
||||
void Job_FlagsToString(const Job *, char *, size_t);
|
||||
int Job_TempFile(const char *, char *, size_t);
|
||||
int Job_TempFile(const char *, char *, size_t) MAKE_ATTR_USE;
|
||||
|
||||
#endif /* MAKE_JOB_H */
|
||||
#endif
|
||||
|
20
lst.h
20
lst.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.h,v 1.99 2021/12/05 10:11:31 rillig Exp $ */
|
||||
/* $NetBSD: lst.h,v 1.102 2021/12/15 12:24:13 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -109,7 +109,7 @@ typedef void LstFreeProc(void *);
|
||||
/* Create or destroy a list */
|
||||
|
||||
/* Create a new list. */
|
||||
List *Lst_New(void);
|
||||
List *Lst_New(void) MAKE_ATTR_USE;
|
||||
/* Free the list nodes, but not the list itself. */
|
||||
void Lst_Done(List *);
|
||||
/* Free the list nodes, freeing the node data using the given function. */
|
||||
@ -129,14 +129,14 @@ Lst_Init(List *list)
|
||||
|
||||
/* Get information about a list */
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
Lst_IsEmpty(List *list)
|
||||
{
|
||||
return list->first == NULL;
|
||||
}
|
||||
|
||||
/* Find the first node that contains the given datum, or NULL. */
|
||||
ListNode *Lst_FindDatum(List *, const void *);
|
||||
ListNode *Lst_FindDatum(List *, const void *) MAKE_ATTR_USE;
|
||||
|
||||
/* Modify a list */
|
||||
|
||||
@ -163,12 +163,13 @@ void LstNode_SetNull(ListNode *);
|
||||
|
||||
/* Add a datum at the tail of the queue. */
|
||||
MAKE_INLINE void
|
||||
Lst_Enqueue(List *list, void *datum) {
|
||||
Lst_Enqueue(List *list, void *datum)
|
||||
{
|
||||
Lst_Append(list, datum);
|
||||
}
|
||||
|
||||
/* Remove the head node of the queue and return its datum. */
|
||||
void *Lst_Dequeue(List *);
|
||||
void *Lst_Dequeue(List *) MAKE_ATTR_USE;
|
||||
|
||||
/*
|
||||
* A vector is an ordered collection of items, allowing for fast indexed
|
||||
@ -187,7 +188,7 @@ void Vector_Init(Vector *, size_t);
|
||||
* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
MAKE_INLINE void *
|
||||
MAKE_INLINE void * MAKE_ATTR_USE
|
||||
Vector_Get(Vector *v, size_t i)
|
||||
{
|
||||
unsigned char *items = v->items;
|
||||
@ -198,8 +199,9 @@ void *Vector_Push(Vector *);
|
||||
void *Vector_Pop(Vector *);
|
||||
|
||||
MAKE_INLINE void
|
||||
Vector_Done(Vector *v) {
|
||||
Vector_Done(Vector *v)
|
||||
{
|
||||
free(v->items);
|
||||
}
|
||||
|
||||
#endif /* MAKE_LST_H */
|
||||
#endif
|
||||
|
51
make.1
51
make.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 sjg Exp $
|
||||
.\" $NetBSD: make.1,v 1.304 2022/01/29 20:54:58 sjg Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd December 12, 2021
|
||||
.Dd January 28, 2022
|
||||
.Dt MAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -691,10 +691,38 @@ Variables defined as part of the command line.
|
||||
Variables that are defined specific to a certain target.
|
||||
.El
|
||||
.Pp
|
||||
Local variables are all built in and their values vary magically from
|
||||
target to target.
|
||||
It is not currently possible to define new local variables.
|
||||
The seven local variables are as follows:
|
||||
Local variables can be set on a dependency line, if
|
||||
.Va .MAKE.TARGET_LOCAL_VARIABLES ,
|
||||
is not set to
|
||||
.Ql false .
|
||||
The rest of the line
|
||||
(which will already have had Global variables expanded),
|
||||
is the variable value.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
COMPILER_WRAPPERS+= ccache distcc icecc
|
||||
|
||||
${OBJS}: .MAKE.META.CMP_FILTER=${COMPILER_WRAPPERS:S,^,N,}
|
||||
.Ed
|
||||
.Pp
|
||||
Only the targets
|
||||
.Ql ${OBJS}
|
||||
will be impacted by that filter (in "meta" mode) and
|
||||
simply enabling/disabling any of the wrappers will not render all
|
||||
of those targets out-of-date.
|
||||
.Pp
|
||||
.Em NOTE :
|
||||
target local variable assignments behave differently in that;
|
||||
.Bl -tag -width Ds -offset indent
|
||||
.It Ic \&+=
|
||||
Only appends to a previous local assignment
|
||||
for the same target and variable.
|
||||
.It Ic \&:=
|
||||
Is redundant with respect to Global variables,
|
||||
which have already been expanded.
|
||||
.El
|
||||
.Pp
|
||||
The seven built-in local variables are as follows:
|
||||
.Bl -tag -width ".ARCHIVE" -offset indent
|
||||
.It Va .ALLSRC
|
||||
The list of all sources for this target; also known as
|
||||
@ -846,6 +874,11 @@ For example:
|
||||
would produce tokens like
|
||||
.Ql ---make[1234] target ---
|
||||
making it easier to track the degree of parallelism being achieved.
|
||||
.It .MAKE.TARGET_LOCAL_VARIABLES
|
||||
If set to
|
||||
.Ql false ,
|
||||
apparent variable assignments in dependency lines are
|
||||
treated as normal sources.
|
||||
.It Ev MAKEFLAGS
|
||||
The environment variable
|
||||
.Ql Ev MAKEFLAGS
|
||||
@ -954,6 +987,12 @@ If a file that was generated outside of
|
||||
.Va .OBJDIR
|
||||
but within said bailiwick is missing,
|
||||
the current target is considered out-of-date.
|
||||
.It Va .MAKE.META.CMP_FILTER
|
||||
In "meta" mode, it can (very rarely!) be useful to filter command
|
||||
lines before comparison.
|
||||
This variable can be set to a set of modifiers that will be applied to
|
||||
each line of the old and new command that differ, if the filtered
|
||||
commands still differ, the target is considered out-of-date.
|
||||
.It Va .MAKE.META.CREATED
|
||||
In "meta" mode, this variable contains a list of all the meta files
|
||||
updated.
|
||||
|
32
make.c
32
make.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.c,v 1.248 2021/11/28 23:12:51 rillig Exp $ */
|
||||
/* $NetBSD: make.c,v 1.252 2022/01/09 15:48:30 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -104,7 +104,7 @@
|
||||
#include "job.h"
|
||||
|
||||
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: make.c,v 1.248 2021/11/28 23:12:51 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: make.c,v 1.252 2022/01/09 15:48:30 rillig Exp $");
|
||||
|
||||
/* Sequence # to detect recursion. */
|
||||
static unsigned int checked_seqno = 1;
|
||||
@ -120,11 +120,11 @@ static GNodeList toBeMade = LST_INIT;
|
||||
void
|
||||
debug_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_list ap;
|
||||
|
||||
va_start(args, fmt);
|
||||
vfprintf(opts.debug_file, fmt, args);
|
||||
va_end(args);
|
||||
va_start(ap, fmt);
|
||||
vfprintf(opts.debug_file, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
MAKE_ATTR_DEAD static void
|
||||
@ -370,9 +370,8 @@ GNode_IsOODate(GNode *gn)
|
||||
}
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
if (useMeta)
|
||||
oodate = meta_oodate(gn, oodate);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -599,8 +598,9 @@ Make_Recheck(GNode *gn)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: The returned mtime may differ from gn->mtime.
|
||||
* Intentionally? */
|
||||
/*
|
||||
* XXX: The returned mtime may differ from gn->mtime. Intentionally?
|
||||
*/
|
||||
return mtime;
|
||||
}
|
||||
|
||||
@ -952,7 +952,9 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext)
|
||||
|
||||
/* 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 */
|
||||
/*
|
||||
* Can't build this (or anything else in this child list) yet
|
||||
*/
|
||||
cn->made = DEFERRED;
|
||||
return false; /* but keep looking */
|
||||
}
|
||||
@ -1070,7 +1072,7 @@ MakeStartJobs(void)
|
||||
gn->made = BEINGMADE;
|
||||
if (GNode_IsOODate(gn)) {
|
||||
DEBUG0(MAKE, "out-of-date\n");
|
||||
if (opts.queryFlag)
|
||||
if (opts.query)
|
||||
return true;
|
||||
GNode_SetLocalVars(gn);
|
||||
Job_Make(gn);
|
||||
@ -1327,7 +1329,9 @@ add_wait_dependency(GNodeListNode *owln, GNode *wn)
|
||||
DEBUG3(MAKE, ".WAIT: add dependency %s%s -> %s\n",
|
||||
cn->name, cn->cohort_num, wn->name);
|
||||
|
||||
/* XXX: This pattern should be factored out, it repeats often */
|
||||
/*
|
||||
* XXX: This pattern should be factored out, it repeats often
|
||||
*/
|
||||
Lst_Append(&wn->children, cn);
|
||||
wn->unmade++;
|
||||
Lst_Append(&cn->parents, wn);
|
||||
@ -1436,7 +1440,7 @@ Make_Run(GNodeList *targs)
|
||||
Targ_PrintGraph(1);
|
||||
}
|
||||
|
||||
if (opts.queryFlag) {
|
||||
if (opts.query) {
|
||||
/*
|
||||
* We wouldn't do any work unless we could start some jobs
|
||||
* in the next loop... (we won't actually start any, of
|
||||
|
672
make.h
672
make.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.h,v 1.270 2021/11/28 23:12:51 rillig Exp $ */
|
||||
/* $NetBSD: make.h,v 1.298 2022/02/05 00:26:21 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -74,7 +74,7 @@
|
||||
|
||||
/*
|
||||
* make.h --
|
||||
* The global definitions for pmake
|
||||
* The global definitions for make
|
||||
*/
|
||||
|
||||
#ifndef MAKE_MAKE_H
|
||||
@ -110,9 +110,9 @@
|
||||
#define MAKE_GNUC_PREREQ(x, y) \
|
||||
((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
|
||||
(__GNUC__ > (x)))
|
||||
#else /* defined(__GNUC__) */
|
||||
#else
|
||||
#define MAKE_GNUC_PREREQ(x, y) 0
|
||||
#endif /* defined(__GNUC__) */
|
||||
#endif
|
||||
|
||||
#if MAKE_GNUC_PREREQ(2, 7)
|
||||
#define MAKE_ATTR_UNUSED __attribute__((__unused__))
|
||||
@ -135,7 +135,17 @@
|
||||
#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */
|
||||
#endif
|
||||
|
||||
#if MAKE_GNUC_PREREQ(4, 0)
|
||||
#define MAKE_ATTR_USE __attribute__((__warn_unused_result__))
|
||||
#else
|
||||
#define MAKE_ATTR_USE /* delete */
|
||||
#endif
|
||||
|
||||
#if __STDC__ >= 199901L || defined(lint)
|
||||
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
|
||||
#else
|
||||
#define MAKE_INLINE static MAKE_ATTR_UNUSED
|
||||
#endif
|
||||
|
||||
/* MAKE_STATIC marks a function that may or may not be inlined. */
|
||||
#if defined(lint)
|
||||
@ -206,26 +216,34 @@ typedef unsigned char bool;
|
||||
typedef enum GNodeMade {
|
||||
/* Not examined yet. */
|
||||
UNMADE,
|
||||
/* The node has been examined but is not yet ready since its
|
||||
* dependencies have to be made first. */
|
||||
/*
|
||||
* The node has been examined but is not yet ready since its
|
||||
* dependencies have to be made first.
|
||||
*/
|
||||
DEFERRED,
|
||||
|
||||
/* The node is on the toBeMade list. */
|
||||
REQUESTED,
|
||||
|
||||
/* The node is already being made. Trying to build a node in this
|
||||
* state indicates a cycle in the graph. */
|
||||
/*
|
||||
* The node is already being made. Trying to build a node in this
|
||||
* state indicates a cycle in the graph.
|
||||
*/
|
||||
BEINGMADE,
|
||||
|
||||
/* Was out-of-date and has been made. */
|
||||
MADE,
|
||||
/* Was already up-to-date, does not need to be made. */
|
||||
UPTODATE,
|
||||
/* An error occurred while it was being made.
|
||||
* Used only in compat mode. */
|
||||
/*
|
||||
* An error occurred while it was being made. Used only in compat
|
||||
* mode.
|
||||
*/
|
||||
ERROR,
|
||||
/* The target was aborted due to an error making a dependency.
|
||||
* Used only in compat mode. */
|
||||
/*
|
||||
* The target was aborted due to an error making a dependency. Used
|
||||
* only in compat mode.
|
||||
*/
|
||||
ABORTED
|
||||
} GNodeMade;
|
||||
|
||||
@ -241,16 +259,22 @@ typedef enum GNodeMade {
|
||||
typedef enum GNodeType {
|
||||
OP_NONE = 0,
|
||||
|
||||
/* The dependency operator ':' is the most common one. The commands
|
||||
* of this node are executed if any child is out-of-date. */
|
||||
/*
|
||||
* The dependency operator ':' is the most common one. The commands
|
||||
* of this node are executed if any child is out-of-date.
|
||||
*/
|
||||
OP_DEPENDS = 1 << 0,
|
||||
/* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date. */
|
||||
/*
|
||||
* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date.
|
||||
*/
|
||||
OP_FORCE = 1 << 1,
|
||||
/* The dependency operator '::' behaves like ':', except that it
|
||||
/*
|
||||
* The dependency operator '::' behaves like ':', except that it
|
||||
* allows multiple dependency groups to be defined. Each of these
|
||||
* groups is executed on its own, independently from the others.
|
||||
* Each individual dependency group is called a cohort. */
|
||||
* groups is executed on its own, independently from the others. Each
|
||||
* individual dependency group is called a cohort.
|
||||
*/
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
@ -260,21 +284,29 @@ typedef enum GNodeType {
|
||||
OP_OPTIONAL = 1 << 3,
|
||||
/* Use associated commands for parents. */
|
||||
OP_USE = 1 << 4,
|
||||
/* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of. */
|
||||
/*
|
||||
* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of.
|
||||
*/
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore non-zero exit status from shell commands when creating the
|
||||
* node. */
|
||||
/*
|
||||
* Ignore non-zero exit status from shell commands when creating the
|
||||
* node.
|
||||
*/
|
||||
OP_IGNORE = 1 << 6,
|
||||
/* Don't remove the target when interrupted. */
|
||||
OP_PRECIOUS = 1 << 7,
|
||||
/* Don't echo commands when executed. */
|
||||
OP_SILENT = 1 << 8,
|
||||
/* Target is a recursive make so its commands should always be
|
||||
* executed when it is out of date, regardless of the state of the
|
||||
* -n or -t flags. */
|
||||
/*
|
||||
* Target is a recursive make so its commands should always be
|
||||
* executed when it is out of date, regardless of the state of the -n
|
||||
* or -t flags.
|
||||
*/
|
||||
OP_MAKE = 1 << 9,
|
||||
/* Target is out-of-date only if any of its children was out-of-date. */
|
||||
/*
|
||||
* Target is out-of-date only if any of its children was out-of-date.
|
||||
*/
|
||||
OP_JOIN = 1 << 10,
|
||||
/* Assume the children of the node have been already made. */
|
||||
OP_MADE = 1 << 11,
|
||||
@ -282,20 +314,26 @@ typedef enum GNodeType {
|
||||
OP_SPECIAL = 1 << 12,
|
||||
/* Like .USE, only prepend commands. */
|
||||
OP_USEBEFORE = 1 << 13,
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in
|
||||
* the parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
/*
|
||||
* The node is invisible to its parents. I.e. it doesn't show up in
|
||||
* the parents' local variables (.IMPSRC, .ALLSRC).
|
||||
*/
|
||||
OP_INVISIBLE = 1 << 14,
|
||||
/* The node does not become the main target, even if it is the first
|
||||
* target in the first makefile. */
|
||||
/*
|
||||
* The node does not become the main target, even if it is the first
|
||||
* target in the first makefile.
|
||||
*/
|
||||
OP_NOTMAIN = 1 << 15,
|
||||
/* Not a file target; run always. */
|
||||
OP_PHONY = 1 << 16,
|
||||
/* Don't search for the file in the path. */
|
||||
OP_NOPATH = 1 << 17,
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is
|
||||
/*
|
||||
* In a dependency line "target: source1 .WAIT source2", source1 is
|
||||
* made first, including its children. Once that is finished,
|
||||
* source2 is made, including its children. The .WAIT keyword may
|
||||
* appear more than once in a single dependency declaration. */
|
||||
* appear more than once in a single dependency declaration.
|
||||
*/
|
||||
OP_WAIT = 1 << 18,
|
||||
/* .NOMETA do not create a .meta file */
|
||||
OP_NOMETA = 1 << 19,
|
||||
@ -313,28 +351,35 @@ typedef enum GNodeType {
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
OP_MEMBER = 1 << 29,
|
||||
/* The node is a library,
|
||||
* its name has the form "-l<libname>" */
|
||||
/*
|
||||
* The node is a library, its name has the form "-l<libname>".
|
||||
*/
|
||||
OP_LIB = 1 << 28,
|
||||
/* The node is an archive member,
|
||||
* its name has the form "archive(member)" */
|
||||
/*
|
||||
* The node is an archive member, its name has the form
|
||||
* "archive(member)".
|
||||
*/
|
||||
/* XXX: How does this differ from OP_MEMBER? */
|
||||
OP_ARCHV = 1 << 27,
|
||||
/* Target has all the commands it should. Used when parsing to catch
|
||||
/*
|
||||
* Target has all the commands it should. Used when parsing to catch
|
||||
* multiple command groups for a target. Only applies to the
|
||||
* dependency operators ':' and '!', but not to '::'. */
|
||||
* dependency operators ':' and '!', but not to '::'.
|
||||
*/
|
||||
OP_HAS_COMMANDS = 1 << 26,
|
||||
/* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed at
|
||||
* the very end. */
|
||||
/*
|
||||
* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed
|
||||
* at the very end.
|
||||
*/
|
||||
OP_SAVE_CMDS = 1 << 25,
|
||||
/* Already processed by Suff_FindDeps, to find dependencies from
|
||||
* suffix transformation rules. */
|
||||
/*
|
||||
* Already processed by Suff_FindDeps, to find dependencies from
|
||||
* suffix transformation rules.
|
||||
*/
|
||||
OP_DEPS_FOUND = 1 << 24,
|
||||
/* Node found while expanding .ALLSRC */
|
||||
OP_MARK = 1 << 23,
|
||||
|
||||
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
|
||||
OP_MARK = 1 << 23
|
||||
} GNodeType;
|
||||
|
||||
typedef struct GNodeFlags {
|
||||
@ -377,14 +422,20 @@ typedef struct GNode {
|
||||
char *name;
|
||||
/* The unexpanded name of a .USE node */
|
||||
char *uname;
|
||||
/* The full pathname of the file belonging to the target.
|
||||
/*
|
||||
* The full pathname of the file belonging to the target.
|
||||
*
|
||||
* XXX: What about .PHONY targets? These don't have an associated
|
||||
* path. */
|
||||
* path.
|
||||
*/
|
||||
char *path;
|
||||
|
||||
/* The type of operator used to define the sources (see the OP flags
|
||||
/*
|
||||
* The type of operator used to define the sources (see the OP flags
|
||||
* below).
|
||||
* XXX: This looks like a wild mixture of type and flags. */
|
||||
*
|
||||
* XXX: This looks like a wild mixture of type and flags.
|
||||
*/
|
||||
GNodeType type;
|
||||
GNodeFlags flags;
|
||||
|
||||
@ -393,29 +444,39 @@ typedef struct GNode {
|
||||
/* The number of unmade children */
|
||||
int unmade;
|
||||
|
||||
/* The modification time; 0 means the node does not have a
|
||||
* corresponding file; see GNode_IsOODate. */
|
||||
/*
|
||||
* The modification time; 0 means the node does not have a
|
||||
* corresponding file; see GNode_IsOODate.
|
||||
*/
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
|
||||
/* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node for
|
||||
* file.c has the node for file.o in this list. */
|
||||
/*
|
||||
* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node
|
||||
* for file.c has the node for file.o in this list.
|
||||
*/
|
||||
GNodeList implicitParents;
|
||||
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
/*
|
||||
* The nodes that depend on this one, or in other words, the nodes
|
||||
* for which this is a source.
|
||||
*/
|
||||
GNodeList parents;
|
||||
/* The nodes on which this one depends. */
|
||||
GNodeList children;
|
||||
|
||||
/* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
/*
|
||||
* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
* made) before this node can be made, but that do not enter into the
|
||||
* datedness of this node. */
|
||||
* datedness of this node.
|
||||
*/
|
||||
GNodeList order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're
|
||||
/*
|
||||
* .ORDER nodes who need us. The nodes that must be made (if they're
|
||||
* made at all) after this node is made, but that do not depend on
|
||||
* this node, in the normal sense. */
|
||||
* this node, in the normal sense.
|
||||
*/
|
||||
GNodeList order_succ;
|
||||
|
||||
/*
|
||||
@ -427,8 +488,10 @@ typedef struct GNode {
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
int unmade_cohorts;
|
||||
/* Pointer to the first instance of a '::' node; only set when on a
|
||||
* cohorts list */
|
||||
/*
|
||||
* Pointer to the first instance of a '::' node; only set when on a
|
||||
* cohorts list
|
||||
*/
|
||||
struct GNode *centurion;
|
||||
|
||||
/* Last time (sequence number) we tried to make this node */
|
||||
@ -447,21 +510,24 @@ typedef struct GNode {
|
||||
/* The commands to be given to a shell to create this target. */
|
||||
StringList commands;
|
||||
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to
|
||||
* everyone but the Suff module) */
|
||||
/*
|
||||
* Suffix for the node (determined by Suff_FindDeps and opaque to
|
||||
* everyone but the Suff module)
|
||||
*/
|
||||
struct Suffix *suffix;
|
||||
|
||||
/* Filename where the GNode got defined */
|
||||
/* XXX: What is the lifetime of this string? */
|
||||
/* Filename where the GNode got defined, unlimited lifetime */
|
||||
const char *fname;
|
||||
/* Line number where the GNode got defined */
|
||||
int lineno;
|
||||
/* Line number where the GNode got defined, 1-based */
|
||||
unsigned lineno;
|
||||
} GNode;
|
||||
|
||||
/* Error levels for diagnostics during parsing. */
|
||||
typedef enum ParseErrorLevel {
|
||||
/* Exit when the current top-level makefile has been parsed
|
||||
* completely. */
|
||||
/*
|
||||
* Exit when the current top-level makefile has been parsed
|
||||
* completely.
|
||||
*/
|
||||
PARSE_FATAL = 1,
|
||||
/* Print "warning"; may be upgraded to fatal by the -w option. */
|
||||
PARSE_WARNING,
|
||||
@ -472,20 +538,20 @@ typedef enum ParseErrorLevel {
|
||||
/*
|
||||
* Values returned by Cond_EvalLine and Cond_EvalCondition.
|
||||
*/
|
||||
typedef enum CondEvalResult {
|
||||
COND_PARSE, /* Parse the next lines */
|
||||
COND_SKIP, /* Skip the next lines */
|
||||
COND_INVALID /* Not a conditional statement */
|
||||
} CondEvalResult;
|
||||
typedef enum CondResult {
|
||||
CR_TRUE, /* Parse the next lines */
|
||||
CR_FALSE, /* Skip the next lines */
|
||||
CR_ERROR /* Unknown directive or parse error */
|
||||
} CondResult;
|
||||
|
||||
/* Names of the variables that are "local" to a specific target. */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
|
||||
/*
|
||||
* Global Variables
|
||||
@ -543,6 +609,7 @@ extern int makelevel;
|
||||
extern char *makeDependfile;
|
||||
/* If we replaced environ, this will be non-NULL. */
|
||||
extern char **savedEnv;
|
||||
extern GNode *mainNode;
|
||||
|
||||
extern pid_t myPid;
|
||||
|
||||
@ -560,34 +627,32 @@ extern pid_t myPid;
|
||||
# define MAKE_LEVEL_ENV "MAKELEVEL"
|
||||
#endif
|
||||
|
||||
typedef enum DebugFlags {
|
||||
DEBUG_NONE = 0,
|
||||
DEBUG_ARCH = 1 << 0,
|
||||
DEBUG_COND = 1 << 1,
|
||||
DEBUG_CWD = 1 << 2,
|
||||
DEBUG_DIR = 1 << 3,
|
||||
DEBUG_ERROR = 1 << 4,
|
||||
DEBUG_FOR = 1 << 5,
|
||||
DEBUG_GRAPH1 = 1 << 6,
|
||||
DEBUG_GRAPH2 = 1 << 7,
|
||||
DEBUG_GRAPH3 = 1 << 8,
|
||||
DEBUG_HASH = 1 << 9,
|
||||
DEBUG_JOB = 1 << 10,
|
||||
DEBUG_LOUD = 1 << 11,
|
||||
DEBUG_MAKE = 1 << 12,
|
||||
DEBUG_META = 1 << 13,
|
||||
DEBUG_PARSE = 1 << 14,
|
||||
DEBUG_SCRIPT = 1 << 15,
|
||||
DEBUG_SHELL = 1 << 16,
|
||||
DEBUG_SUFF = 1 << 17,
|
||||
DEBUG_TARG = 1 << 18,
|
||||
DEBUG_VAR = 1 << 19,
|
||||
DEBUG_ALL = (1 << 20) - 1
|
||||
typedef struct DebugFlags {
|
||||
bool DEBUG_ARCH:1;
|
||||
bool DEBUG_COND:1;
|
||||
bool DEBUG_CWD:1;
|
||||
bool DEBUG_DIR:1;
|
||||
bool DEBUG_ERROR:1;
|
||||
bool DEBUG_FOR:1;
|
||||
bool DEBUG_GRAPH1:1;
|
||||
bool DEBUG_GRAPH2:1;
|
||||
bool DEBUG_GRAPH3:1;
|
||||
bool DEBUG_HASH:1;
|
||||
bool DEBUG_JOB:1;
|
||||
bool DEBUG_LOUD:1;
|
||||
bool DEBUG_MAKE:1;
|
||||
bool DEBUG_META:1;
|
||||
bool DEBUG_PARSE:1;
|
||||
bool DEBUG_SCRIPT:1;
|
||||
bool DEBUG_SHELL:1;
|
||||
bool DEBUG_SUFF:1;
|
||||
bool DEBUG_TARG:1;
|
||||
bool DEBUG_VAR:1;
|
||||
} DebugFlags;
|
||||
|
||||
#define CONCAT(a, b) a##b
|
||||
|
||||
#define DEBUG(module) ((opts.debug & CONCAT(DEBUG_, module)) != 0)
|
||||
#define DEBUG(module) (opts.debug.CONCAT(DEBUG_, module))
|
||||
|
||||
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
|
||||
@ -597,8 +662,8 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
debug_printf args; \
|
||||
} while (false)
|
||||
|
||||
#define DEBUG0(module, text) \
|
||||
DEBUG_IMPL(module, ("%s", text))
|
||||
#define DEBUG0(module, fmt) \
|
||||
DEBUG_IMPL(module, (fmt))
|
||||
#define DEBUG1(module, fmt, arg1) \
|
||||
DEBUG_IMPL(module, (fmt, arg1))
|
||||
#define DEBUG2(module, fmt, arg1, arg2) \
|
||||
@ -621,17 +686,21 @@ typedef struct CmdOpts {
|
||||
/* -B: whether we are make compatible */
|
||||
bool compatMake;
|
||||
|
||||
/* -d: debug control: There is one bit per module. It is up to the
|
||||
* module what debug information to print. */
|
||||
/*
|
||||
* -d: debug control: There is one bit per module. It is up to the
|
||||
* module what debug information to print.
|
||||
*/
|
||||
DebugFlags debug;
|
||||
|
||||
/* -df: debug output is written here - default stderr */
|
||||
FILE *debug_file;
|
||||
|
||||
/* -dL: lint mode
|
||||
/*
|
||||
* -dL: lint mode
|
||||
*
|
||||
* Runs make in strict mode, with additional checks and better error
|
||||
* handling. */
|
||||
* handling.
|
||||
*/
|
||||
bool strict;
|
||||
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
@ -646,12 +715,16 @@ typedef struct CmdOpts {
|
||||
/* -i: if true, ignore all errors from shell commands */
|
||||
bool ignoreErrors;
|
||||
|
||||
/* -j: the maximum number of jobs that can run in parallel;
|
||||
* this is coordinated with the submakes */
|
||||
/*
|
||||
* -j: the maximum number of jobs that can run in parallel; this is
|
||||
* coordinated with the submakes
|
||||
*/
|
||||
int maxJobs;
|
||||
|
||||
/* -k: if true and an error occurs while making a node, continue
|
||||
* making nodes that do not depend on the erroneous node */
|
||||
/*
|
||||
* -k: if true and an error occurs while making a node, continue
|
||||
* making nodes that do not depend on the erroneous node
|
||||
*/
|
||||
bool keepgoing;
|
||||
|
||||
/* -N: execute no commands from the targets */
|
||||
@ -664,17 +737,19 @@ typedef struct CmdOpts {
|
||||
* -q: if true, do not really make anything, just see if the targets
|
||||
* are out-of-date
|
||||
*/
|
||||
bool queryFlag;
|
||||
bool query;
|
||||
|
||||
/* -r: raw mode, do not load the builtin rules. */
|
||||
bool noBuiltins;
|
||||
|
||||
/* -s: don't echo the shell commands before executing them */
|
||||
bool beSilent;
|
||||
bool silent;
|
||||
|
||||
/* -t: touch the targets if they are out-of-date, but don't actually
|
||||
* make them */
|
||||
bool touchFlag;
|
||||
/*
|
||||
* -t: touch the targets if they are out-of-date, but don't actually
|
||||
* make them
|
||||
*/
|
||||
bool touch;
|
||||
|
||||
/* -[Vv]: print expanded or unexpanded selected variables */
|
||||
PrintVarsMode printVars;
|
||||
@ -687,90 +762,364 @@ typedef struct CmdOpts {
|
||||
/* -w: print 'Entering' and 'Leaving' for submakes */
|
||||
bool enterFlag;
|
||||
|
||||
/* -X: if true, do not export variables set on the command line to the
|
||||
* environment. */
|
||||
/*
|
||||
* -X: if true, do not export variables set on the command line to
|
||||
* the environment.
|
||||
*/
|
||||
bool varNoExportEnv;
|
||||
|
||||
/* The target names specified on the command line.
|
||||
* Used to resolve .if make(...) statements. */
|
||||
/*
|
||||
* The target names specified on the command line. Used to resolve
|
||||
* .if make(...) statements.
|
||||
*/
|
||||
StringList create;
|
||||
|
||||
} CmdOpts;
|
||||
|
||||
extern CmdOpts opts;
|
||||
|
||||
#include "nonints.h"
|
||||
/* arch.c */
|
||||
void Arch_Init(void);
|
||||
void Arch_End(void);
|
||||
|
||||
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 *);
|
||||
bool Arch_LibOODate(GNode *) MAKE_ATTR_USE;
|
||||
bool Arch_IsLib(GNode *) MAKE_ATTR_USE;
|
||||
|
||||
/* compat.c */
|
||||
bool Compat_RunCommand(const char *, GNode *, StringListNode *);
|
||||
void Compat_Run(GNodeList *);
|
||||
void Compat_Make(GNode *, GNode *);
|
||||
|
||||
/* cond.c */
|
||||
CondResult Cond_EvalCondition(const char *) MAKE_ATTR_USE;
|
||||
CondResult Cond_EvalLine(const char *) MAKE_ATTR_USE;
|
||||
void Cond_restore_depth(unsigned int);
|
||||
unsigned int Cond_save_depth(void) MAKE_ATTR_USE;
|
||||
|
||||
/* dir.c; see also dir.h */
|
||||
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
str_basename(const char *pathname)
|
||||
{
|
||||
const char *lastSlash = strrchr(pathname, '/');
|
||||
return lastSlash != NULL ? lastSlash + 1 : pathname;
|
||||
}
|
||||
|
||||
MAKE_INLINE SearchPath * MAKE_ATTR_USE
|
||||
SearchPath_New(void)
|
||||
{
|
||||
SearchPath *path = bmake_malloc(sizeof *path);
|
||||
Lst_Init(&path->dirs);
|
||||
return path;
|
||||
}
|
||||
|
||||
void SearchPath_Free(SearchPath *);
|
||||
|
||||
/* for.c */
|
||||
struct ForLoop;
|
||||
int For_Eval(const char *) MAKE_ATTR_USE;
|
||||
bool For_Accum(const char *, int *) MAKE_ATTR_USE;
|
||||
void For_Run(unsigned, unsigned);
|
||||
bool For_NextIteration(struct ForLoop *, Buffer *);
|
||||
char *ForLoop_Details(struct ForLoop *);
|
||||
void ForLoop_Free(struct ForLoop *);
|
||||
|
||||
/* job.c */
|
||||
void JobReapChild(pid_t, int, bool);
|
||||
|
||||
/* main.c */
|
||||
void Main_ParseArgLine(const char *);
|
||||
char *Cmd_Exec(const char *, char **) MAKE_ATTR_USE;
|
||||
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void DieHorribly(void) MAKE_ATTR_DEAD;
|
||||
void Finish(int) MAKE_ATTR_DEAD;
|
||||
bool unlink_file(const char *) MAKE_ATTR_USE;
|
||||
void execDie(const char *, const char *);
|
||||
char *getTmpdir(void) MAKE_ATTR_USE;
|
||||
bool ParseBoolean(const char *, bool) MAKE_ATTR_USE;
|
||||
const char *cached_realpath(const char *, char *);
|
||||
bool GetBooleanExpr(const char *, bool);
|
||||
|
||||
/* parse.c */
|
||||
void Parse_Init(void);
|
||||
void Parse_End(void);
|
||||
|
||||
void PrintLocation(FILE *, bool, const char *, unsigned);
|
||||
void PrintStackTrace(bool);
|
||||
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE;
|
||||
void Parse_AddIncludeDir(const char *);
|
||||
void Parse_File(const char *, int);
|
||||
void Parse_PushInput(const char *, unsigned, unsigned, Buffer,
|
||||
struct ForLoop *);
|
||||
void Parse_MainName(GNodeList *);
|
||||
int Parse_NumErrors(void) MAKE_ATTR_USE;
|
||||
|
||||
|
||||
/* suff.c */
|
||||
void Suff_Init(void);
|
||||
void Suff_End(void);
|
||||
|
||||
void Suff_ClearSuffixes(void);
|
||||
bool Suff_IsTransform(const char *) MAKE_ATTR_USE;
|
||||
GNode *Suff_AddTransform(const char *);
|
||||
void Suff_EndTransform(GNode *);
|
||||
void Suff_AddSuffix(const char *);
|
||||
SearchPath *Suff_GetPath(const char *) MAKE_ATTR_USE;
|
||||
void Suff_ExtendPaths(void);
|
||||
void Suff_AddInclude(const char *);
|
||||
void Suff_AddLib(const char *);
|
||||
void Suff_FindDeps(GNode *);
|
||||
SearchPath *Suff_FindPath(GNode *) MAKE_ATTR_USE;
|
||||
void Suff_SetNull(const char *);
|
||||
void Suff_PrintAll(void);
|
||||
char *Suff_NamesStr(void) MAKE_ATTR_USE;
|
||||
|
||||
/* targ.c */
|
||||
void Targ_Init(void);
|
||||
void Targ_End(void);
|
||||
|
||||
void Targ_Stats(void);
|
||||
GNodeList *Targ_List(void) MAKE_ATTR_USE;
|
||||
GNode *GNode_New(const char *) MAKE_ATTR_USE;
|
||||
GNode *Targ_FindNode(const char *) MAKE_ATTR_USE;
|
||||
GNode *Targ_GetNode(const char *) MAKE_ATTR_USE;
|
||||
GNode *Targ_NewInternalNode(const char *) MAKE_ATTR_USE;
|
||||
GNode *Targ_GetEndNode(void);
|
||||
void Targ_FindList(GNodeList *, StringList *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
void Targ_PrintNodes(GNodeList *, int);
|
||||
const char *Targ_FmtTime(time_t) MAKE_ATTR_USE;
|
||||
void Targ_PrintType(GNodeType);
|
||||
void Targ_PrintGraph(int);
|
||||
void Targ_Propagate(void);
|
||||
const char *GNodeMade_Name(GNodeMade) MAKE_ATTR_USE;
|
||||
|
||||
/* var.c */
|
||||
void Var_Init(void);
|
||||
void Var_End(void);
|
||||
|
||||
typedef enum VarEvalMode {
|
||||
|
||||
/*
|
||||
* Only parse the expression but don't evaluate any part of it.
|
||||
*
|
||||
* TODO: Document what Var_Parse and Var_Subst return in this mode.
|
||||
* As of 2021-03-15, they return unspecified, inconsistent results.
|
||||
*/
|
||||
VARE_PARSE_ONLY,
|
||||
|
||||
/* Parse and evaluate the expression. */
|
||||
VARE_WANTRES,
|
||||
|
||||
/*
|
||||
* Parse and evaluate the expression. It is an error if a
|
||||
* subexpression evaluates to undefined.
|
||||
*/
|
||||
VARE_UNDEFERR,
|
||||
|
||||
/*
|
||||
* 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_EVAL_KEEP_DOLLAR,
|
||||
|
||||
/*
|
||||
* Parse and evaluate the expression. Keep undefined variables as-is
|
||||
* instead of expanding them to an empty string.
|
||||
*
|
||||
* Example for a ':=' assignment:
|
||||
* CFLAGS = $(.INCLUDES)
|
||||
* CFLAGS := -I.. $(CFLAGS)
|
||||
* # If .INCLUDES (an undocumented special variable, by the
|
||||
* # way) is still undefined, the updated CFLAGS becomes
|
||||
* # "-I.. $(.INCLUDES)".
|
||||
*/
|
||||
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,
|
||||
|
||||
/* do not export */
|
||||
VAR_SET_NO_EXPORT = 1 << 0,
|
||||
|
||||
/*
|
||||
* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag.
|
||||
*/
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
} VarSetFlags;
|
||||
|
||||
/* The state of error handling returned by Var_Parse. */
|
||||
typedef enum VarParseResult {
|
||||
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK,
|
||||
|
||||
/* Parsing or evaluating failed, with an error message. */
|
||||
VPR_ERR,
|
||||
|
||||
/*
|
||||
* Parsing succeeded, undefined expressions are allowed and the
|
||||
* expression was still undefined after applying all modifiers.
|
||||
* No error message is printed in this case.
|
||||
*
|
||||
* Some callers handle this case differently, so return this
|
||||
* information to them, for now.
|
||||
*
|
||||
* TODO: Instead of having this special return value, rather ensure
|
||||
* that VARE_EVAL_KEEP_UNDEF is processed properly.
|
||||
*/
|
||||
VPR_UNDEF
|
||||
|
||||
} VarParseResult;
|
||||
|
||||
typedef enum VarExportMode {
|
||||
/* .export-env */
|
||||
VEM_ENV,
|
||||
/* .export: Initial export or update an already exported variable. */
|
||||
VEM_PLAIN,
|
||||
/* .export-literal: Do not expand the variable value. */
|
||||
VEM_LITERAL
|
||||
} VarExportMode;
|
||||
|
||||
void Var_Delete(GNode *, const char *);
|
||||
void Var_Undef(const char *);
|
||||
void Var_Set(GNode *, const char *, const char *);
|
||||
void Var_SetExpand(GNode *, const char *, const char *);
|
||||
void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
|
||||
void Var_Append(GNode *, const char *, const char *);
|
||||
void Var_AppendExpand(GNode *, const char *, const char *);
|
||||
bool Var_Exists(GNode *, const char *) MAKE_ATTR_USE;
|
||||
bool Var_ExistsExpand(GNode *, const char *) MAKE_ATTR_USE;
|
||||
FStr Var_Value(GNode *, const char *) MAKE_ATTR_USE;
|
||||
const char *GNode_ValueDirect(GNode *, const char *) MAKE_ATTR_USE;
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalMode, FStr *);
|
||||
VarParseResult Var_Subst(const char *, GNode *, VarEvalMode, char **);
|
||||
void Var_Expand(FStr *, GNode *, VarEvalMode);
|
||||
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(bool, const char *);
|
||||
|
||||
void Global_Set(const char *, const char *);
|
||||
void Global_Append(const char *, const char *);
|
||||
void Global_Delete(const char *);
|
||||
|
||||
/* util.c */
|
||||
typedef void (*SignalProc)(int);
|
||||
SignalProc bmake_signal(int, SignalProc);
|
||||
|
||||
/* make.c */
|
||||
void GNode_UpdateYoungestChild(GNode *, GNode *);
|
||||
bool GNode_IsOODate(GNode *);
|
||||
bool GNode_IsOODate(GNode *) MAKE_ATTR_USE;
|
||||
void Make_ExpandUse(GNodeList *);
|
||||
time_t Make_Recheck(GNode *);
|
||||
time_t Make_Recheck(GNode *) MAKE_ATTR_USE;
|
||||
void Make_HandleUse(GNode *, GNode *);
|
||||
void Make_Update(GNode *);
|
||||
void GNode_SetLocalVars(GNode *);
|
||||
bool Make_Run(GNodeList *);
|
||||
bool shouldDieQuietly(GNode *, int);
|
||||
bool shouldDieQuietly(GNode *, int) MAKE_ATTR_USE;
|
||||
void PrintOnError(GNode *, const char *);
|
||||
void Main_ExportMAKEFLAGS(bool);
|
||||
bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
int mkTempFile(const char *, char *, size_t);
|
||||
int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE;
|
||||
int str2Lst_Append(StringList *, char *);
|
||||
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
|
||||
bool GNode_ShouldExecute(GNode *gn);
|
||||
bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE;
|
||||
|
||||
/* See if the node was seen on the left-hand side of a dependency operator. */
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsTarget(const GNode *gn)
|
||||
{
|
||||
return (gn->type & OP_OPMASK) != OP_NONE;
|
||||
}
|
||||
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_Path(const GNode *gn)
|
||||
{
|
||||
return gn->path != NULL ? gn->path : gn->name;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsWaitingFor(const GNode *gn)
|
||||
{
|
||||
return gn->flags.remake && gn->made <= REQUESTED;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsReady(const GNode *gn)
|
||||
{
|
||||
return gn->made > DEFERRED;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsDone(const GNode *gn)
|
||||
{
|
||||
return gn->made >= MADE;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsError(const GNode *gn)
|
||||
{
|
||||
return gn->made == ERROR || gn->made == ABORTED;
|
||||
}
|
||||
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsMainCandidate(const GNode *gn)
|
||||
{
|
||||
return (gn->type & (OP_NOTMAIN | OP_USE | OP_USEBEFORE |
|
||||
OP_EXEC | OP_TRANSFORM)) == 0;
|
||||
}
|
||||
|
||||
/* Return whether the target file should be preserved on interrupt. */
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
GNode_IsPrecious(const GNode *gn)
|
||||
{
|
||||
/* XXX: Why are '::' targets precious? */
|
||||
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
|
||||
}
|
||||
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
|
||||
MAKE_INLINE const char *
|
||||
MAKE_INLINE const char * MAKE_ATTR_USE
|
||||
GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
|
||||
|
||||
MAKE_INLINE void *
|
||||
MAKE_INLINE void * MAKE_ATTR_USE
|
||||
UNCONST(const void *ptr)
|
||||
{
|
||||
void *ret;
|
||||
@ -795,19 +1144,21 @@ UNCONST(const void *ptr)
|
||||
#define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
#endif
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_islower(char ch) { return islower((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE char
|
||||
MAKE_INLINE char MAKE_ATTR_USE
|
||||
ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }
|
||||
MAKE_INLINE char
|
||||
MAKE_INLINE char MAKE_ATTR_USE
|
||||
ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
|
||||
|
||||
MAKE_INLINE void
|
||||
@ -824,6 +1175,17 @@ cpp_skip_hspace(const char **pp)
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
cpp_skip_string(const char **pp, const char *s)
|
||||
{
|
||||
const char *p = *pp;
|
||||
while (*p == *s && *s != '\0')
|
||||
p++, s++;
|
||||
if (*s == '\0')
|
||||
*pp = p;
|
||||
return *s == '\0';
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
pp_skip_whitespace(char **pp)
|
||||
{
|
||||
@ -863,4 +1225,4 @@ pp_skip_hspace(char **pp)
|
||||
# define MAKE_RCSID(id) static volatile char rcsid[] = id
|
||||
#endif
|
||||
|
||||
#endif /* MAKE_MAKE_H */
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.c,v 1.26 2022/01/07 08:30:04 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.26 2022/01/07 08:30:04 rillig Exp $");
|
||||
|
||||
#ifndef USE_EMALLOC
|
||||
|
||||
@ -57,12 +57,12 @@ bmake_malloc(size_t len)
|
||||
char *
|
||||
bmake_strdup(const char *str)
|
||||
{
|
||||
size_t len;
|
||||
size_t size;
|
||||
char *p;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
p = bmake_malloc(len);
|
||||
return memcpy(p, str, len);
|
||||
size = strlen(str) + 1;
|
||||
p = bmake_malloc(size);
|
||||
return memcpy(p, str, size);
|
||||
}
|
||||
|
||||
/* Allocate a string starting from str with exactly len characters. */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.h,v 1.16 2021/01/19 20:51:46 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.h,v 1.18 2021/12/15 11:01:39 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -27,10 +27,10 @@
|
||||
*/
|
||||
|
||||
#ifndef USE_EMALLOC
|
||||
void *bmake_malloc(size_t);
|
||||
void *bmake_realloc(void *, size_t);
|
||||
char *bmake_strdup(const char *);
|
||||
char *bmake_strldup(const char *, size_t);
|
||||
void *bmake_malloc(size_t) MAKE_ATTR_USE;
|
||||
void *bmake_realloc(void *, size_t) MAKE_ATTR_USE;
|
||||
char *bmake_strdup(const char *) MAKE_ATTR_USE;
|
||||
char *bmake_strldup(const char *, size_t) MAKE_ATTR_USE;
|
||||
#else
|
||||
#include <util.h>
|
||||
#define bmake_malloc(n) emalloc(n)
|
||||
@ -39,18 +39,4 @@ char *bmake_strldup(const char *, size_t);
|
||||
#define bmake_strldup(s, n) estrndup(s, n)
|
||||
#endif
|
||||
|
||||
char *bmake_strsedup(const char *, const char *);
|
||||
|
||||
/*
|
||||
* Thin wrapper around free(3) to avoid the extra function call in case
|
||||
* p is NULL, to save a few machine instructions.
|
||||
*
|
||||
* The case of a NULL pointer happens especially often after Var_Value,
|
||||
* since only environment variables need to be freed, but not others.
|
||||
*/
|
||||
MAKE_INLINE void
|
||||
bmake_free(void *p)
|
||||
{
|
||||
if (p != NULL)
|
||||
free(p);
|
||||
}
|
||||
char *bmake_strsedup(const char *, const char *) MAKE_ATTR_USE;
|
||||
|
170
meta.c
170
meta.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.c,v 1.185 2021/11/27 22:04:02 rillig Exp $ */
|
||||
/* $NetBSD: meta.c,v 1.196 2022/02/04 23:22:19 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Implement 'meta' mode.
|
||||
@ -69,6 +69,9 @@ static char *metaIgnorePathsStr; /* string storage for the list */
|
||||
#ifndef MAKE_META_IGNORE_FILTER
|
||||
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
|
||||
#endif
|
||||
#ifndef MAKE_META_CMP_FILTER
|
||||
#define MAKE_META_CMP_FILTER ".MAKE.META.CMP_FILTER"
|
||||
#endif
|
||||
|
||||
bool useMeta = false;
|
||||
static bool useFilemon = false;
|
||||
@ -80,6 +83,7 @@ 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 metaCmpFilter = false; /* do we have CMP_FILTER ? */
|
||||
static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
|
||||
static bool metaSilent = false; /* if we have a .meta be SILENT */
|
||||
|
||||
@ -208,42 +212,25 @@ filemon_read(FILE *mfp, int fd)
|
||||
* we use this, to clean up ./ and ../
|
||||
*/
|
||||
static void
|
||||
eat_dots(char *buf, size_t bufsz, int dots)
|
||||
eat_dots(char *buf)
|
||||
{
|
||||
char *cp;
|
||||
char *cp2;
|
||||
const char *eat;
|
||||
size_t eatlen;
|
||||
char *p;
|
||||
|
||||
switch (dots) {
|
||||
case 1:
|
||||
eat = "/./";
|
||||
eatlen = 2;
|
||||
break;
|
||||
case 2:
|
||||
eat = "/../";
|
||||
eatlen = 3;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
while ((p = strstr(buf, "/./")) != NULL)
|
||||
memmove(p, p + 2, strlen(p + 2) + 1);
|
||||
|
||||
do {
|
||||
cp = strstr(buf, eat);
|
||||
if (cp != NULL) {
|
||||
cp2 = cp + eatlen;
|
||||
if (dots == 2 && cp > buf) {
|
||||
do {
|
||||
cp--;
|
||||
} while (cp > buf && *cp != '/');
|
||||
}
|
||||
if (*cp == '/') {
|
||||
strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
|
||||
} else {
|
||||
return; /* can't happen? */
|
||||
}
|
||||
while ((p = strstr(buf, "/../")) != NULL) {
|
||||
char *p2 = p + 3;
|
||||
if (p > buf) {
|
||||
do {
|
||||
p--;
|
||||
} while (p > buf && *p != '/');
|
||||
}
|
||||
} while (cp != NULL);
|
||||
if (*p == '/')
|
||||
memmove(p, p2, strlen(p2) + 1);
|
||||
else
|
||||
return; /* can't happen? */
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
@ -287,8 +274,7 @@ meta_name(char *mname, size_t mnamelen,
|
||||
} else {
|
||||
snprintf(buf, sizeof buf, "%s/%s", cwd, tname);
|
||||
}
|
||||
eat_dots(buf, sizeof buf, 1); /* ./ */
|
||||
eat_dots(buf, sizeof buf, 2); /* ../ */
|
||||
eat_dots(buf);
|
||||
tname = buf;
|
||||
}
|
||||
}
|
||||
@ -330,15 +316,14 @@ is_submake(const char *cmd, GNode *gn)
|
||||
static const char *p_make = NULL;
|
||||
static size_t p_len;
|
||||
char *mp = NULL;
|
||||
const char *cp, *cp2;
|
||||
const char *cp2;
|
||||
bool rc = false;
|
||||
|
||||
if (p_make == NULL) {
|
||||
p_make = Var_Value(gn, ".MAKE").str;
|
||||
p_len = strlen(p_make);
|
||||
}
|
||||
cp = strchr(cmd, '$');
|
||||
if (cp != NULL) {
|
||||
if (strchr(cmd, '$') != NULL) {
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
cmd = mp;
|
||||
@ -385,13 +370,7 @@ printCMD(const char *ucmd, FILE *fp, GNode *gn)
|
||||
{
|
||||
FStr xcmd = FStr_InitRefer(ucmd);
|
||||
|
||||
if (strchr(ucmd, '$') != NULL) {
|
||||
char *expanded;
|
||||
(void)Var_Subst(ucmd, gn, VARE_WANTRES, &expanded);
|
||||
/* TODO: handle errors */
|
||||
xcmd = FStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
Var_Expand(&xcmd, gn, VARE_WANTRES);
|
||||
fprintf(fp, "CMD %s\n", xcmd.str);
|
||||
FStr_Done(&xcmd);
|
||||
}
|
||||
@ -601,7 +580,6 @@ meta_mode_init(const char *make_mode)
|
||||
{
|
||||
static bool once = false;
|
||||
const char *cp;
|
||||
FStr value;
|
||||
|
||||
useMeta = true;
|
||||
useFilemon = true;
|
||||
@ -658,16 +636,9 @@ meta_mode_init(const char *make_mode)
|
||||
/*
|
||||
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
||||
*/
|
||||
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
|
||||
if (value.str != NULL) {
|
||||
metaIgnorePatterns = true;
|
||||
FStr_Done(&value);
|
||||
}
|
||||
value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
|
||||
if (value.str != NULL) {
|
||||
metaIgnoreFilter = true;
|
||||
FStr_Done(&value);
|
||||
}
|
||||
metaIgnorePatterns = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
|
||||
metaIgnoreFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
|
||||
metaCmpFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_CMP_FILTER);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1061,7 +1032,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
* Setting oodate true will have that effect.
|
||||
*/
|
||||
#define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
|
||||
warnx("%s: %d: malformed", fname, lineno); \
|
||||
warnx("%s: %u: malformed", fname, lineno); \
|
||||
oodate = true; \
|
||||
continue; \
|
||||
}
|
||||
@ -1084,6 +1055,39 @@ append_if_new(StringList *list, const char *str)
|
||||
Lst_Append(list, bmake_strdup(str));
|
||||
}
|
||||
|
||||
static char *
|
||||
meta_filter_cmd(Buffer *buf, GNode *gn, char *s)
|
||||
{
|
||||
Buf_Clear(buf);
|
||||
Buf_AddStr(buf, "${");
|
||||
Buf_AddStr(buf, s);
|
||||
Buf_AddStr(buf, ":L:${" MAKE_META_CMP_FILTER ":ts:}}");
|
||||
Var_Subst(buf->data, gn, VARE_WANTRES, &s);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
meta_cmd_cmp(GNode *gn, char *a, char *b, bool filter)
|
||||
{
|
||||
static bool once = false;
|
||||
static Buffer buf;
|
||||
int rc;
|
||||
|
||||
rc = strcmp(a, b);
|
||||
if (rc == 0 || !filter)
|
||||
return rc;
|
||||
if (!once) {
|
||||
once = true;
|
||||
Buf_Init(&buf);
|
||||
}
|
||||
a = meta_filter_cmd(&buf, gn, a);
|
||||
b = meta_filter_cmd(&buf, gn, b);
|
||||
rc = strcmp(a, b);
|
||||
free(a);
|
||||
free(b);
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool
|
||||
meta_oodate(GNode *gn, bool oodate)
|
||||
{
|
||||
@ -1108,6 +1112,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
bool needOODATE = false;
|
||||
StringList missingFiles;
|
||||
bool have_filemon = false;
|
||||
bool cmp_filter;
|
||||
|
||||
if (oodate)
|
||||
return oodate; /* we're done */
|
||||
@ -1139,7 +1144,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
if ((fp = fopen(fname, "r")) != NULL) {
|
||||
static char *buf = NULL;
|
||||
static size_t bufsz;
|
||||
int lineno = 0;
|
||||
unsigned lineno = 0;
|
||||
int lastpid = 0;
|
||||
int pid;
|
||||
int x;
|
||||
@ -1167,13 +1172,16 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
/* we want to track all the .meta we read */
|
||||
Global_Append(".MAKE.META.FILES", fname);
|
||||
|
||||
cmp_filter = metaCmpFilter ? metaCmpFilter :
|
||||
Var_Exists(gn, MAKE_META_CMP_FILTER);
|
||||
|
||||
cmdNode = gn->commands.first;
|
||||
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
|
||||
lineno++;
|
||||
if (buf[x - 1] == '\n')
|
||||
buf[x - 1] = '\0';
|
||||
else {
|
||||
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
||||
warnx("%s: %u: line truncated at %u", fname, lineno, x);
|
||||
oodate = true;
|
||||
break;
|
||||
}
|
||||
@ -1194,7 +1202,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
/* Delimit the record type. */
|
||||
p = buf;
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
|
||||
DEBUG3(META, "%s: %u: %s\n", fname, lineno, buf);
|
||||
#endif
|
||||
strsep(&p, " ");
|
||||
if (have_filemon) {
|
||||
@ -1240,8 +1248,8 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
|
||||
if (lastpid > 0) {
|
||||
/* We need to remember these. */
|
||||
Global_SetExpand(lcwd_vname, lcwd);
|
||||
Global_SetExpand(ldir_vname, latestdir);
|
||||
Global_Set(lcwd_vname, lcwd);
|
||||
Global_Set(ldir_vname, latestdir);
|
||||
}
|
||||
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
|
||||
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
|
||||
@ -1262,7 +1270,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
continue;
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
|
||||
debug_printf("%s: %u: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
pid, buf[0], cwd, lcwd, latestdir);
|
||||
#endif
|
||||
@ -1274,8 +1282,8 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
/* Process according to record type. */
|
||||
switch (buf[0]) {
|
||||
case 'X': /* eXit */
|
||||
Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname);
|
||||
Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname);
|
||||
Var_Delete(SCOPE_GLOBAL, lcwd_vname);
|
||||
Var_Delete(SCOPE_GLOBAL, ldir_vname);
|
||||
lastpid = 0; /* no need to save ldir_vname */
|
||||
break;
|
||||
|
||||
@ -1287,13 +1295,13 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
child = atoi(p);
|
||||
if (child > 0) {
|
||||
snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child);
|
||||
Global_SetExpand(cldir, lcwd);
|
||||
Global_Set(cldir, lcwd);
|
||||
snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child);
|
||||
Global_SetExpand(cldir, latestdir);
|
||||
Global_Set(cldir, latestdir);
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
debug_printf(
|
||||
"%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
|
||||
"%s: %u: %d: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
child, cwd, lcwd, latestdir);
|
||||
#endif
|
||||
@ -1305,10 +1313,10 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
/* Update lcwd and latest directory. */
|
||||
strlcpy(latestdir, p, sizeof latestdir);
|
||||
strlcpy(lcwd, p, sizeof lcwd);
|
||||
Global_SetExpand(lcwd_vname, lcwd);
|
||||
Global_SetExpand(ldir_vname, lcwd);
|
||||
Global_Set(lcwd_vname, lcwd);
|
||||
Global_Set(ldir_vname, lcwd);
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
|
||||
DEBUG4(META, "%s: %u: cwd=%s ldir=%s\n",
|
||||
fname, lineno, cwd, lcwd);
|
||||
#endif
|
||||
break;
|
||||
@ -1461,7 +1469,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
|
||||
for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG3(META, "%s: %d: looking for: %s\n",
|
||||
DEBUG3(META, "%s: %u: looking for: %s\n",
|
||||
fname, lineno, *sdp);
|
||||
#endif
|
||||
if (cached_stat(*sdp, &cst) == 0) {
|
||||
@ -1471,12 +1479,12 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
}
|
||||
if (found) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG3(META, "%s: %d: found: %s\n",
|
||||
DEBUG3(META, "%s: %u: found: %s\n",
|
||||
fname, lineno, p);
|
||||
#endif
|
||||
if (!S_ISDIR(cst.cst_mode) &&
|
||||
cst.cst_mtime > gn->mtime) {
|
||||
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
|
||||
DEBUG3(META, "%s: %u: file '%s' is newer than the target...\n",
|
||||
fname, lineno, p);
|
||||
oodate = true;
|
||||
} else if (S_ISDIR(cst.cst_mode)) {
|
||||
@ -1508,7 +1516,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
* meta data file.
|
||||
*/
|
||||
if (cmdNode == NULL) {
|
||||
DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
|
||||
DEBUG2(META, "%s: %u: there were more build commands in the meta data file than there are now...\n",
|
||||
fname, lineno);
|
||||
oodate = true;
|
||||
} else {
|
||||
@ -1525,7 +1533,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
}
|
||||
if (hasOODATE) {
|
||||
needOODATE = true;
|
||||
DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
|
||||
DEBUG2(META, "%s: %u: cannot compare command using .OODATE\n",
|
||||
fname, lineno);
|
||||
}
|
||||
(void)Var_Subst(cmd, gn, VARE_UNDEFERR, &cmd);
|
||||
@ -1548,7 +1556,7 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
x = n;
|
||||
lineno++;
|
||||
if (buf[x - 1] != '\n') {
|
||||
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
||||
warnx("%s: %u: line truncated at %u", fname, lineno, x);
|
||||
break;
|
||||
}
|
||||
cp = strchr(cp + 1, '\n');
|
||||
@ -1559,8 +1567,8 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
if (p != NULL &&
|
||||
!hasOODATE &&
|
||||
!(gn->type & OP_NOMETA_CMP) &&
|
||||
(strcmp(p, cmd) != 0)) {
|
||||
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
|
||||
(meta_cmd_cmp(gn, p, cmd, cmp_filter) != 0)) {
|
||||
DEBUG4(META, "%s: %u: a build command has changed\n%s\nvs\n%s\n",
|
||||
fname, lineno, p, cmd);
|
||||
if (!metaIgnoreCMDs)
|
||||
oodate = true;
|
||||
@ -1574,13 +1582,13 @@ meta_oodate(GNode *gn, bool oodate)
|
||||
* that weren't in the meta data file.
|
||||
*/
|
||||
if (!oodate && cmdNode != NULL) {
|
||||
DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
|
||||
DEBUG2(META, "%s: %u: there are extra build commands now that weren't in the meta data file\n",
|
||||
fname, lineno);
|
||||
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",
|
||||
DEBUG4(META, "%s: %u: the current working directory has changed from '%s' to '%s'\n",
|
||||
fname, lineno, p, curdir);
|
||||
oodate = true;
|
||||
}
|
||||
|
8
meta.h
8
meta.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.h,v 1.10 2021/04/03 11:08:40 rillig Exp $ */
|
||||
/* $NetBSD: meta.h,v 1.11 2021/12/15 09:53:41 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Things needed for 'meta' mode.
|
||||
@ -46,13 +46,13 @@ void meta_mode_init(const char *);
|
||||
void meta_job_start(struct Job *, GNode *);
|
||||
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 *);
|
||||
int meta_job_fd(struct Job *) MAKE_ATTR_USE;
|
||||
int meta_job_event(struct Job *) MAKE_ATTR_USE;
|
||||
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 *);
|
||||
bool meta_oodate(GNode *, bool);
|
||||
bool meta_oodate(GNode *, bool) MAKE_ATTR_USE;
|
||||
void meta_compat_start(void);
|
||||
void meta_compat_child(void);
|
||||
void meta_compat_parent(pid_t);
|
||||
|
12
metachar.h
12
metachar.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: metachar.h,v 1.17 2021/06/21 18:54:41 rillig Exp $ */
|
||||
/* $NetBSD: metachar.h,v 1.20 2022/01/08 11:04:13 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015 The NetBSD Foundation, Inc.
|
||||
@ -35,18 +35,18 @@
|
||||
|
||||
extern const unsigned char _metachar[];
|
||||
|
||||
MAKE_INLINE bool
|
||||
is_shell_metachar(char c)
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
ch_is_shell_meta(char c)
|
||||
{
|
||||
return _metachar[c & 0x7f] != 0;
|
||||
}
|
||||
|
||||
MAKE_INLINE bool
|
||||
MAKE_INLINE bool MAKE_ATTR_USE
|
||||
needshell(const char *cmd)
|
||||
{
|
||||
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
|
||||
while (!ch_is_shell_meta(*cmd) && *cmd != ':' && *cmd != '=')
|
||||
cmd++;
|
||||
return *cmd != '\0';
|
||||
}
|
||||
|
||||
#endif /* MAKE_METACHAR_H */
|
||||
#endif
|
||||
|
57
mk/ChangeLog
57
mk/ChangeLog
@ -1,3 +1,60 @@
|
||||
2022-02-04 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20220204
|
||||
|
||||
* host-target.mk: use .MAKE.OS if available
|
||||
|
||||
2022-02-02 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20220202
|
||||
|
||||
* cc-wrap.mk: allow other entries in CC_WRAP_FILTER
|
||||
We add our filter on extensions last, so prior filters
|
||||
can apply to the whole value of .IMPSRC
|
||||
|
||||
2022-02-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* cc-wrap.mk: take advantage of target local variables to
|
||||
wrap compilers like CC CXX with wrappers like ccache distcc etc
|
||||
|
||||
2022-01-28 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* meta2deps: we do not expect any trace data for setid apps
|
||||
|
||||
2022-01-26 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* dirdeps.mk: ensure TARGET_SPEC and TARGET_SPEC_VARS are passed
|
||||
to sub-make using DIRDEPS_CACHE
|
||||
|
||||
2022-01-07 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* dirdeps.mk: use _cache_script to minimize the number of shells
|
||||
forked when generating dirdeps.cache
|
||||
|
||||
2022-01-02 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20220101
|
||||
|
||||
* dirdeps.mk: initialize DEP_* and _debug_reldir earlier.
|
||||
If initial DIRDEPS are from command line, create the target
|
||||
_dirdeps_cmdline as an indication.
|
||||
|
||||
2022-01-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* init.mk (_SKIP_BUILD): when doing DIRDEPS_BUILD
|
||||
at top-level only some targets are allowed at level 0,
|
||||
for leaf makefiles only the default (all) target is restricted
|
||||
|
||||
2021-12-28 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20211228
|
||||
|
||||
* meta2deps.py: filemon on Linux is not as reliable as we might
|
||||
like, we do not want to update DIRDEPS if filemon output is
|
||||
incomplete. Track pids that we 'E'xec and make sure we see an
|
||||
e'X'it for each one. Throw an error if we are missing any 'X'
|
||||
records.
|
||||
|
||||
2021-12-12 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* sys.mk: simplify; include meta.sys.mk if MK_META_MODE is yes.
|
||||
|
1
mk/FILES
1
mk/FILES
@ -6,6 +6,7 @@ auto.obj.mk
|
||||
autoconf.mk
|
||||
autodep.mk
|
||||
auto.dep.mk
|
||||
cc-wrap.mk
|
||||
compiler.mk
|
||||
cython.mk
|
||||
dep.mk
|
||||
|
61
mk/cc-wrap.mk
Normal file
61
mk/cc-wrap.mk
Normal file
@ -0,0 +1,61 @@
|
||||
# $Id: cc-wrap.mk,v 1.4 2022/02/02 17:41:56 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2022, Simon J. Gerraty
|
||||
#
|
||||
# This file is provided in the hope that it will
|
||||
# be of use. There is absolutely NO WARRANTY.
|
||||
# Permission to copy, redistribute or otherwise
|
||||
# use this file is hereby granted provided that
|
||||
# the above copyright notice and this notice are
|
||||
# left intact.
|
||||
#
|
||||
# Please send copies of changes and bug-fixes to:
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
.if ${MAKE_VERSION} >= 20220126
|
||||
# which targets are we interested in?
|
||||
CC_WRAP_TARGETS ?= ${OBJS:U} ${POBJS:U} ${SOBJS:U}
|
||||
|
||||
.if !empty(CC_WRAP_TARGETS)
|
||||
# cleanup
|
||||
# all the target assignments below are effectively := anyway
|
||||
# so we might as well do this once
|
||||
CC_WRAP_TARGETS := ${CC_WRAP_TARGETS:O:u}
|
||||
|
||||
# what do we wrap?
|
||||
CC_WRAP_LIST += CC CXX
|
||||
CC_WRAP_LIST := ${CC_WRAP_LIST:O:u}
|
||||
|
||||
# what might we wrap them with?
|
||||
CC_WRAPPERS += ccache distcc icecc
|
||||
CC_WRAPPERS := ${CC_WRAPPERS:O:u}
|
||||
.for w in ${CC_WRAPPERS}
|
||||
${w:tu} ?= $w
|
||||
.endfor
|
||||
|
||||
# we do not want to make all these targets out-of-date
|
||||
# just because one of the above wrappers are enabled/disabled
|
||||
${CC_WRAP_TARGETS}: .MAKE.META.CMP_FILTER = ${CC_WRAPPERS:tu@W@${$W}@:S,^,N,}
|
||||
|
||||
# some object src types we should not wrap
|
||||
CC_WRAP_SKIP_EXTS += s
|
||||
|
||||
# We add the sequence we care about - excluding CC_WRAP_SKIP_EXTS
|
||||
# but prior filters can apply to full value of .IMPSRC
|
||||
CC_WRAP_FILTER += E:tl:${CC_WRAP_SKIP_EXTS:${M_ListToSkip}}
|
||||
CC_WRAP_FILTER := ${CC_WRAP_FILTER:ts:}
|
||||
|
||||
# last one enabled wins!
|
||||
.for W in ${CC_WRAPPERS:tu}
|
||||
.if ${MK_$W:U} == "yes"
|
||||
.for C in ${CC_WRAP_LIST}
|
||||
# we have to protect the check of .IMPSRC from Global expansion
|
||||
${CC_WRAP_TARGETS}: $C = $${"$${.IMPSRC:${CC_WRAP_FILTER}}":?${$W}:} ${$C}
|
||||
.endfor
|
||||
.endif
|
||||
.endfor
|
||||
|
||||
.endif
|
||||
.endif
|
||||
|
101
mk/dirdeps.mk
101
mk/dirdeps.mk
@ -1,6 +1,6 @@
|
||||
# $Id: dirdeps.mk,v 1.147 2021/12/14 02:09:53 sjg Exp $
|
||||
# $Id: dirdeps.mk,v 1.151 2022/01/28 01:13:14 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2021, Simon J. Gerraty
|
||||
# Copyright (c) 2010-2022, Simon J. Gerraty
|
||||
# Copyright (c) 2010-2018, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
@ -134,6 +134,16 @@
|
||||
# A list of MACHINEs the current directory should not be
|
||||
# built for.
|
||||
#
|
||||
# DIRDEPS_EXPORT_VARS (DEP_EXPORT_VARS)
|
||||
# It is discouraged, but sometimes necessary for a
|
||||
# Makefile.depend file to influence the environment.
|
||||
# Doing this is correctly (especially if using DIRDEPS_CACHE) is
|
||||
# tricky so a Makefile.depend file can set DIRDEPS_EXPORT_VARS
|
||||
# and dirdeps.mk will do the deed:
|
||||
#
|
||||
# MK_UEFI = yes
|
||||
# DIRDEPS_EXPORT_VARS = MK_UEFI
|
||||
#
|
||||
# _build_xtra_dirs
|
||||
# local.dirdeps.mk can add targets to this variable.
|
||||
# They will be hooked into the build, but independent of
|
||||
@ -161,6 +171,8 @@ _DIRDEP_USE_LEVEL?= 0
|
||||
.if ${.MAKE.LEVEL} == ${_DIRDEP_USE_LEVEL}
|
||||
# only the first instance is interested in all this
|
||||
|
||||
# the first time we are included the _DIRDEP_USE target will not be defined
|
||||
# we can use this as a clue to do initialization and other one time things.
|
||||
.if !target(_DIRDEP_USE)
|
||||
|
||||
# do some setup we only need once
|
||||
@ -184,6 +196,8 @@ TARGET_MACHINE := ${MACHINE}
|
||||
.endif
|
||||
# disable DIRDEPS_CACHE as it does not like this trick
|
||||
MK_DIRDEPS_CACHE = no
|
||||
# incase anyone needs to know
|
||||
_dirdeps_cmdline:
|
||||
.endif
|
||||
|
||||
# make sure we get the behavior we expect
|
||||
@ -257,12 +271,36 @@ _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*}
|
||||
.endif
|
||||
.endif
|
||||
|
||||
|
||||
# this is how we identify non-machine specific dependfiles
|
||||
N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
|
||||
|
||||
# this gets reset for each dirdep we check
|
||||
DEP_RELDIR ?= ${RELDIR}
|
||||
|
||||
# remember the initial value of DEP_RELDIR - we test for it below.
|
||||
_DEP_RELDIR := ${DEP_RELDIR}
|
||||
|
||||
# this can cause lots of output!
|
||||
# set to a set of glob expressions that might match RELDIR
|
||||
DEBUG_DIRDEPS ?= no
|
||||
|
||||
# make sure this target exists
|
||||
dirdeps: beforedirdeps .WAIT
|
||||
beforedirdeps:
|
||||
|
||||
.endif # !target(_DIRDEP_USE)
|
||||
|
||||
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
|
||||
_debug_reldir = 1
|
||||
.else
|
||||
_debug_reldir = 0
|
||||
.endif
|
||||
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend depend:L:M$x}@} != ""
|
||||
_debug_search = 1
|
||||
.else
|
||||
_debug_search = 0
|
||||
.endif
|
||||
|
||||
# First off, we want to know what ${MACHINE} to build for.
|
||||
# This can be complicated if we are using a mixture of ${MACHINE} specific
|
||||
# and non-specific Makefile.depend*
|
||||
@ -297,26 +335,6 @@ DEP_MACHINE := ${_DEP_TARGET_SPEC}
|
||||
_build_all_dirs =
|
||||
_build_xtra_dirs =
|
||||
|
||||
# the first time we are included the _DIRDEP_USE target will not be defined
|
||||
# we can use this as a clue to do initialization and other one time things.
|
||||
.if !target(_DIRDEP_USE)
|
||||
# make sure this target exists
|
||||
dirdeps: beforedirdeps .WAIT
|
||||
beforedirdeps:
|
||||
|
||||
# We normally expect to be included by Makefile.depend.*
|
||||
# which sets the DEP_* macros below.
|
||||
DEP_RELDIR ?= ${RELDIR}
|
||||
|
||||
# this can cause lots of output!
|
||||
# set to a set of glob expressions that might match RELDIR
|
||||
DEBUG_DIRDEPS ?= no
|
||||
|
||||
# remember the initial value of DEP_RELDIR - we test for it below.
|
||||
_DEP_RELDIR := ${DEP_RELDIR}
|
||||
|
||||
.endif
|
||||
|
||||
# DIRDEPS_CACHE can be very handy for debugging.
|
||||
# Also if repeatedly building the same target,
|
||||
# we can avoid the overhead of re-computing the tree dependencies.
|
||||
@ -329,16 +347,6 @@ BUILD_DIRDEPS ?= yes
|
||||
DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.TARGETS}:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.}
|
||||
.endif
|
||||
|
||||
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
|
||||
_debug_reldir = 1
|
||||
.else
|
||||
_debug_reldir = 0
|
||||
.endif
|
||||
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend:L:M$x}@} != ""
|
||||
_debug_search = 1
|
||||
.else
|
||||
_debug_search = 0
|
||||
.endif
|
||||
|
||||
# pickup customizations
|
||||
# as below you can use !target(_DIRDEP_USE) to protect things
|
||||
@ -532,7 +540,10 @@ BUILD_DIRDEPS = no
|
||||
dirdeps: dirdeps-cached
|
||||
dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
|
||||
@echo "${TRACER}Using ${DIRDEPS_CACHE}"
|
||||
@MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
|
||||
@MAKELEVEL=${.MAKE.LEVEL} \
|
||||
TARGET_SPEC=${TARGET_SPEC} \
|
||||
${TARGET_SPEC_VARS:@v@$v=${$v}@} \
|
||||
${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
|
||||
dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no
|
||||
|
||||
# leaf makefiles rarely work for building DIRDEPS_CACHE
|
||||
@ -701,15 +712,18 @@ _build_all_dirs := ${_build_all_dirs:O:u}
|
||||
.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS:U}} == ""
|
||||
.if !empty(_build_all_dirs)
|
||||
.if ${BUILD_DIRDEPS_CACHE} == "yes"
|
||||
x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3
|
||||
# we use _cache_script to minimize the number of times we fork the shell
|
||||
_cache_script = echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}';
|
||||
# guard against _new_dirdeps being too big for a single command line
|
||||
_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,}
|
||||
_cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,}
|
||||
.export _cache_xtra_deps _new_dirdeps
|
||||
.if !empty(DEP_EXPORT_VARS)
|
||||
.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
|
||||
# Discouraged, but there are always exceptions.
|
||||
# Handle it here rather than explain how.
|
||||
x!= echo; { echo; ${DEP_EXPORT_VARS:@v@echo '$v=${$v}';@} echo '.export ${DEP_EXPORT_VARS}'; echo; } >&3
|
||||
DIRDEPS_EXPORT_VARS ?= ${DEP_EXPORT_VARS}
|
||||
_cache_xvars := echo; ${DIRDEPS_EXPORT_VARS:@v@echo '$v = ${$v}';@} echo '.export ${DIRDEPS_EXPORT_VARS}'; echo;
|
||||
_cache_script += ${_cache_xvars}
|
||||
.endif
|
||||
.else
|
||||
# this makes it all happen
|
||||
@ -721,16 +735,17 @@ ${_build_all_dirs}: _DIRDEP_USE
|
||||
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs:S,^${SRCTOP}/,,}
|
||||
.endif
|
||||
|
||||
.if !empty(DEP_EXPORT_VARS)
|
||||
.export ${DEP_EXPORT_VARS}
|
||||
DEP_EXPORT_VARS=
|
||||
.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
|
||||
.export ${DIRDEPS_EXPORT_VARS} ${DEP_EXPORT_VARS}
|
||||
DIRDEPS_EXPORT_VARS =
|
||||
DEP_EXPORT_VARS =
|
||||
.endif
|
||||
|
||||
# this builds the dependency graph
|
||||
.for m in ${_machines}
|
||||
.if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs)
|
||||
_cache_deps =
|
||||
x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3
|
||||
_cache_script += echo; echo 'DIRDEPS.${_this_dir}.$m = \';
|
||||
.endif
|
||||
# it would be nice to do :N${.TARGET}
|
||||
.if !empty(__qual_depdirs)
|
||||
@ -753,10 +768,10 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q}
|
||||
_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,}
|
||||
.if !empty(_cache_deps)
|
||||
.export _cache_deps
|
||||
x!= echo; for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done >&3
|
||||
_cache_script += for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done;
|
||||
.endif
|
||||
# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
|
||||
x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
|
||||
x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
|
||||
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
|
||||
for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \
|
||||
echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
|
||||
|
@ -1,9 +1,10 @@
|
||||
# RCSid:
|
||||
# $Id: host-target.mk,v 1.13 2020/08/05 23:32:08 sjg Exp $
|
||||
# $Id: host-target.mk,v 1.14 2022/02/04 18:05:22 sjg Exp $
|
||||
|
||||
# Host platform information; may be overridden
|
||||
.if !defined(_HOST_OSNAME)
|
||||
_HOST_OSNAME != uname -s
|
||||
# use .MAKE.OS if available
|
||||
_HOST_OSNAME := ${.MAKE.OS:U${uname -s:L:sh}}
|
||||
.export _HOST_OSNAME
|
||||
.endif
|
||||
.if !defined(_HOST_OSREL)
|
||||
@ -15,7 +16,7 @@ _HOST_MACHINE != uname -m
|
||||
.export _HOST_MACHINE
|
||||
.endif
|
||||
.if !defined(_HOST_ARCH)
|
||||
# for NetBSD prefer $MACHINE (amd64 rather than x86_64)
|
||||
# for Darwin and NetBSD prefer $MACHINE (amd64 rather than x86_64)
|
||||
.if ${_HOST_OSNAME:NDarwin:NNetBSD} == ""
|
||||
_HOST_ARCH := ${_HOST_MACHINE}
|
||||
.else
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: init.mk,v 1.26 2021/12/08 05:56:50 sjg Exp $
|
||||
# $Id: init.mk,v 1.27 2022/01/01 17:32:18 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2002, Simon J. Gerraty
|
||||
#
|
||||
@ -66,13 +66,17 @@ CXX_PIC?= ${CC_PIC}
|
||||
PROFFLAGS?= -DGPROF -DPROF
|
||||
|
||||
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
|
||||
# targets that are ok at level 0
|
||||
.if ${RELDIR} == "."
|
||||
# top-level targets that are ok at level 0
|
||||
DIRDEPS_BUILD_LEVEL0_TARGETS += clean* destroy*
|
||||
M_ListToSkip?= O:u:S,^,N,:ts:
|
||||
.if ${.TARGETS:Uall:${DIRDEPS_BUILD_LEVEL0_TARGETS:${M_ListToSkip}}} != ""
|
||||
# this tells lib.mk and prog.mk to not actually build anything
|
||||
_SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
.elif ${.TARGETS:U:Nall} == ""
|
||||
_SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
.endif
|
||||
|
||||
.if !defined(.PARSEDIR)
|
||||
|
@ -55,7 +55,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: install-mk,v 1.206 2021/12/11 18:57:41 sjg Exp $
|
||||
# $Id: install-mk,v 1.213 2022/02/05 01:39:12 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 1994 Simon J. Gerraty
|
||||
#
|
||||
@ -70,7 +70,7 @@
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
MK_VERSION=20211212
|
||||
MK_VERSION=20220204
|
||||
OWNER=
|
||||
GROUP=
|
||||
MODE=444
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
"""
|
||||
RCSid:
|
||||
$Id: meta2deps.py,v 1.40 2021/12/13 19:32:46 sjg Exp $
|
||||
$Id: meta2deps.py,v 1.44 2022/01/29 02:42:01 sjg Exp $
|
||||
|
||||
Copyright (c) 2011-2020, Simon J. Gerraty
|
||||
Copyright (c) 2011-2017, Juniper Networks, Inc.
|
||||
@ -66,7 +66,10 @@
|
||||
|
||||
"""
|
||||
|
||||
import os, re, sys
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import stat
|
||||
|
||||
def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
|
||||
"""
|
||||
@ -244,6 +247,7 @@ def __init__(self, name, conf={}):
|
||||
self.curdir = conf.get('CURDIR')
|
||||
self.reldir = conf.get('RELDIR')
|
||||
self.dpdeps = conf.get('DPDEPS')
|
||||
self.pids = {}
|
||||
self.line = 0
|
||||
|
||||
if not self.conf:
|
||||
@ -449,7 +453,7 @@ def parse(self, name=None, file=None):
|
||||
if self.curdir:
|
||||
self.seenit(self.curdir) # we ignore this
|
||||
|
||||
interesting = 'CEFLRV'
|
||||
interesting = 'CEFLRVX'
|
||||
for line in f:
|
||||
self.line += 1
|
||||
# ignore anything we don't care about
|
||||
@ -505,6 +509,13 @@ def parse(self, name=None, file=None):
|
||||
print("cwd=", cwd, file=self.debug_out)
|
||||
continue
|
||||
|
||||
if w[0] == 'X':
|
||||
try:
|
||||
del self.pids[pid]
|
||||
except KeyError:
|
||||
pass
|
||||
continue
|
||||
|
||||
if w[2] in self.seen:
|
||||
if self.debug > 2:
|
||||
print("seen:", w[2], file=self.debug_out)
|
||||
@ -518,11 +529,30 @@ def parse(self, name=None, file=None):
|
||||
continue
|
||||
elif w[0] in 'ERWS':
|
||||
path = w[2]
|
||||
if path == '.':
|
||||
if w[0] == 'E':
|
||||
self.pids[pid] = path
|
||||
elif path == '.':
|
||||
continue
|
||||
self.parse_path(path, cwd, w[0], w)
|
||||
|
||||
assert(version > 0)
|
||||
setid_pids = []
|
||||
# self.pids should be empty!
|
||||
for pid,path in self.pids.items():
|
||||
try:
|
||||
# no guarantee that path is still valid
|
||||
if os.stat(path).st_mode & (stat.S_ISUID|stat.S_ISGID):
|
||||
# we do not expect anything after Exec
|
||||
setid_pids.append(pid)
|
||||
continue
|
||||
except:
|
||||
# we do not care why the above fails,
|
||||
# we do not want to miss the ERROR below.
|
||||
pass
|
||||
print("ERROR: missing eXit for {} pid {}".format(path, pid))
|
||||
for pid in setid_pids:
|
||||
del self.pids[pid]
|
||||
assert(len(self.pids) == 0)
|
||||
if not file:
|
||||
f.close()
|
||||
|
||||
|
@ -77,7 +77,7 @@
|
||||
|
||||
|
||||
# RCSid:
|
||||
# $Id: meta2deps.sh,v 1.15 2020/11/08 06:31:08 sjg Exp $
|
||||
# $Id: meta2deps.sh,v 1.18 2022/01/28 21:17:43 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2013, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
@ -241,8 +241,8 @@ meta2deps() {
|
||||
;;
|
||||
*) cat /dev/null "$@";;
|
||||
esac 2> /dev/null |
|
||||
sed -e 's,^CWD,C C,;/^[CREFLMV] /!d' -e "s,',,g" |
|
||||
$_excludes | ( version=no
|
||||
sed -e 's,^CWD,C C,;/^[CREFLMVX] /!d' -e "s,',,g" |
|
||||
$_excludes | ( version=no epids= xpids=
|
||||
while read op pid path junk
|
||||
do
|
||||
: op=$op pid=$pid path=$path
|
||||
@ -273,8 +273,9 @@ meta2deps() {
|
||||
;;
|
||||
esac
|
||||
|
||||
: op=$op path=$path
|
||||
case "$op,$path" in
|
||||
V,*) version=$path; continue;;
|
||||
V,*) version=$pid; continue;;
|
||||
W,*srcrel|*.dirdep) continue;;
|
||||
C,*)
|
||||
case "$path" in
|
||||
@ -291,6 +292,15 @@ meta2deps() {
|
||||
continue
|
||||
;;
|
||||
*) dir=${path%/*}
|
||||
case "$op" in
|
||||
E) # setid apps get no tracing so we won't see eXit
|
||||
case `'ls' -l $path 2> /dev/null | sed 's, .*,,'` in
|
||||
*s*) ;;
|
||||
*) epids="$epids $pid";;
|
||||
esac
|
||||
;;
|
||||
X) xpids="$xpids $pid"; continue;;
|
||||
esac
|
||||
case "$path" in
|
||||
$src_re|$obj_re) ;;
|
||||
/*/stage/*) ;;
|
||||
@ -380,9 +390,18 @@ meta2deps() {
|
||||
echo $dir;;
|
||||
esac
|
||||
done > $tf.dirdep
|
||||
: version=$version
|
||||
case "$version" in
|
||||
0) error "no filemon data";;
|
||||
esac ) || exit 1
|
||||
esac
|
||||
for p in $epids
|
||||
do
|
||||
: p=$p
|
||||
case " $xpids " in
|
||||
*" $p "*) ;;
|
||||
*) error "missing eXit for pid $p";;
|
||||
esac
|
||||
done ) || exit 1
|
||||
_nl=echo
|
||||
for f in $tf.dirdep $tf.qual $tf.srcdep
|
||||
do
|
||||
|
@ -143,6 +143,8 @@ examples/sys.clean-env.mk
|
||||
host-target.mk
|
||||
Is used to set macros like ``HOST_TARGET``, ``HOST_OS`` and
|
||||
``host_os`` which are used to find the next step.
|
||||
Note: since 20130303 bmake provides ``.MAKE.OS`` set to
|
||||
the equivalent of ``HOST_OS``.
|
||||
|
||||
sys/\*.mk
|
||||
Platform specific additions, such as ``Darwin.mk`` or ``SunOS.mk``
|
||||
@ -159,27 +161,40 @@ local.sys.mk
|
||||
The above arrangement makes it easy for the mk files to be part of a
|
||||
src tree on an NFS volume and to allow building on multiple platforms.
|
||||
|
||||
options.mk
|
||||
----------
|
||||
|
||||
Inspired by FreeBSD's ``bsd.own.mk`` but more flexible.
|
||||
FreeBSD now have similar functionality in ``bsd.mkopt.mk``.
|
||||
|
||||
It allows users to express their intent with respect to options
|
||||
``MK_*`` by setting ``WITH_*`` or ``WITHOUT_*``.
|
||||
|
||||
Note: ``WITHOUT_*`` wins if both are set, and makefiles can set
|
||||
``NO_*`` to say they cannot handle that option, or even ``MK_*`` if
|
||||
they really need to.
|
||||
|
||||
lib.mk
|
||||
------
|
||||
|
||||
This file is used to build a number of different libraries from the
|
||||
same SRCS.
|
||||
|
||||
lib${LIB}.a
|
||||
``lib${LIB}.a``
|
||||
An archive lib of ``.o`` files, this is the default
|
||||
|
||||
lib${LIB}_p.a
|
||||
``lib${LIB}_p.a``
|
||||
A profiled lib of ``.po`` files.
|
||||
Still an archive lib, but all the objects are built with
|
||||
profiling in mind - hence the different extension.
|
||||
It is skipped if ``MKPROFILE`` is "no".
|
||||
It is skipped if ``MK_PROFILE`` is "no".
|
||||
|
||||
lib${LIB}_pic.a
|
||||
``lib${LIB}_pic.a``
|
||||
An archive of ``.so`` objects compiled for relocation.
|
||||
On NetBSD this is the input to ``lib${LIB}.${LD_so}``, it is
|
||||
skipped if ``MKPICLIB`` is "no".
|
||||
skipped if ``MK_PIC`` or ``MK_PICLIB`` are "no".
|
||||
|
||||
lib${LIB}.${LD_so}
|
||||
``lib${LIB}.${LD_so}``
|
||||
A shared library. The value of ``LD_so`` is very platform
|
||||
specific. For example::
|
||||
|
||||
@ -190,7 +205,7 @@ lib${LIB}.${LD_so}
|
||||
libsslfd.1.dylib
|
||||
|
||||
This library will only be built if ``SHLIB_MAJOR`` has
|
||||
a value, and ``MKPIC`` is not set to "no".
|
||||
a value, and ``MK_PIC`` is not set to "no".
|
||||
|
||||
There is a lot of platform specific tweaking in ``lib.mk``, largely the
|
||||
result of the original distributions trying to avoid interfering with
|
||||
@ -202,7 +217,7 @@ libnames.mk
|
||||
This is included by both ``prog.mk`` and ``lib.mk`` and tries to
|
||||
include ``*.libnames.mk`` of which:
|
||||
|
||||
local.libnames.mk
|
||||
``local.libnames.mk``
|
||||
does not exist unless you create it. It is a handy way for you
|
||||
to customize without touching the distributed files.
|
||||
For example, on a test machine I needed to build openssl but
|
||||
@ -217,7 +232,7 @@ local.libnames.mk
|
||||
The makefile created an openssl dir in ``${OBJ_libcrypto}`` to
|
||||
gather all the headers. dpadd.mk_ did the rest.
|
||||
|
||||
host.libnames.mk
|
||||
``host.libnames.mk``
|
||||
contains logic to find any libs named in ``HOST_LIBS`` in
|
||||
``HOST_LIBDIRS``.
|
||||
|
||||
@ -256,15 +271,15 @@ else in various ways::
|
||||
Any library (referenced by its full path) in any of the above, is
|
||||
added to ``DPMAGIC_LIBS`` with the following results, for each lib *foo*.
|
||||
|
||||
SRC_libfoo
|
||||
``SRC_libfoo``
|
||||
Is set to indicate where the src for libfoo is.
|
||||
By default it is derived from ``LIBFOO`` by replacing
|
||||
``${OBJTOP}`` with ``${SRCTOP}``.
|
||||
|
||||
OBJ_libfoo
|
||||
``OBJ_libfoo``
|
||||
Not very exciting, is just the dir where libfoo lives.
|
||||
|
||||
INCLUDES_libfoo
|
||||
``INCLUDES_libfoo``
|
||||
What to add to ``CFLAGS`` to find the public headers.
|
||||
The default varies. If ``${SRC_libfoo}/h`` exists, it is assumed
|
||||
to be the home of all public headers and thus the default is
|
||||
@ -273,7 +288,7 @@ INCLUDES_libfoo
|
||||
Otherwise we make no assumptions and the default is
|
||||
``-I${SRC_libfoo} -I${OBJ_libfoo}``
|
||||
|
||||
LDADD_libfoo
|
||||
``LDADD_libfoo``
|
||||
This only applies to libs reference via ``DPLIBS``.
|
||||
The default is ``-lfoo``, ``LDADD_*`` provides a hook to
|
||||
instantiate other linker flags at the appropriate point
|
||||
@ -303,13 +318,16 @@ object files from the src tree. This is also the source of much
|
||||
confusion to some.
|
||||
|
||||
Traditionally one had to do a separate ``make obj`` pass through the
|
||||
tree. If ``MKOBJDIRS`` is "auto", we include auto.obj.mk_.
|
||||
tree. If ``MK_AUTO_OBJ`` is set we include auto.obj.mk_.
|
||||
|
||||
In fact if ``MKOBJDIRS`` is set to "auto", `sys.mk`_ will set
|
||||
``MK_AUTO_OBJ=yes`` and include auto.obj.mk_ since it is best done early.
|
||||
|
||||
auto.obj.mk
|
||||
-----------
|
||||
|
||||
This leverages the ``.OBJDIR`` target introduced some years ago to
|
||||
NetBSD make, to automatically create the desired object dir.
|
||||
NetBSD make, to automatically create and use the desired object dir.
|
||||
|
||||
subdir.mk
|
||||
---------
|
||||
@ -334,6 +352,8 @@ you can suppress that - or enhance it by setting ``ECHO_DIR``::
|
||||
# print time stamps
|
||||
ECHO_DIR=echo @ `date "+%s [%Y-%m-%d %T] "`
|
||||
|
||||
I prefer to use `dirdeps.mk`_ which makes ``subdir.mk`` irrelevant.
|
||||
|
||||
links.mk
|
||||
--------
|
||||
|
||||
@ -367,9 +387,12 @@ dep.mk
|
||||
|
||||
Deals with collecting dependencies. Another useful feature of BSD
|
||||
make is the separation of this sort of information into a ``.depend``
|
||||
file. ``MKDEP`` needs to point to a suitable tool (like mkdeps.sh_)
|
||||
file. ``MKDEP_CMD`` needs to point to a suitable tool (like mkdeps.sh_)
|
||||
|
||||
If ``USE_AUTODEP_MK`` is "yes" includes autodep.mk_
|
||||
If ``MK_AUTODEP`` is "yes" it sets ``MKDEP_MK`` to autodep.mk_ by default.
|
||||
|
||||
``MKDEP_MK`` can also be set to `auto.dep.mk`_ which is more efficient
|
||||
but does not support an explicit ``depend`` target.
|
||||
|
||||
autodep.mk
|
||||
----------
|
||||
@ -397,19 +420,9 @@ to avoid possible conflicts during parallel builds.
|
||||
This precludes the use of suffix rules to drive ``make depend``, so
|
||||
dep.mk_ handles that if specifically requested.
|
||||
|
||||
options.mk
|
||||
----------
|
||||
|
||||
Inspired by FreeBSD's ``bsd.own.mk`` more flexible.
|
||||
FreeBSD now have similar functionality in ``bsd.mkopt.mk``.
|
||||
|
||||
It allows users to express their intent with respect to options
|
||||
``MK_*`` by setting ``WITH_*`` or ``WITHOUT_*``.
|
||||
|
||||
Note: ``WITHOUT_*`` wins if both are set, and makefiles can set
|
||||
``NO_*`` to say they cannot handle that option, or even ``MK_*`` if
|
||||
they really need to.
|
||||
|
||||
If ``bmake`` is 20160218 or newer, ``auto.dep.mk`` uses ``.dinclude``
|
||||
to includes the ``*.d`` files directly thus avoiding the need to
|
||||
create a ``.depend`` file from them.
|
||||
|
||||
own.mk
|
||||
------
|
||||
@ -424,7 +437,7 @@ ldorder.mk
|
||||
|
||||
Leverages ``bmake`` to compute optimal link order for libraries.
|
||||
This works nicely and makes refactoring a breeze - so long as you
|
||||
have not (or few) cicular dependencies between libraries.
|
||||
have no (or few) cicular dependencies between libraries.
|
||||
|
||||
man.mk
|
||||
------
|
||||
@ -463,6 +476,22 @@ Logic to build Python C interface modules using Cython_
|
||||
|
||||
.. _Cython: http://www.cython.org/
|
||||
|
||||
cc-wrap.mk
|
||||
----------
|
||||
|
||||
This makefile leverages two new features in bmake 20220126 and later.
|
||||
|
||||
First is the ablity to set target local variables (GNU make has done
|
||||
this for ages).
|
||||
|
||||
The second (only intersting if using `meta mode`_)
|
||||
allows filtering commands before comparison with previous run to
|
||||
decide if a target is out-of-date.
|
||||
|
||||
In the past, making use of compiler wrappers like ``ccache``,
|
||||
``distcc`` or the newer ``icecc`` could get quite ugly.
|
||||
Using ``cc-wrap.mk`` it could not be simpler.
|
||||
|
||||
Meta mode
|
||||
=========
|
||||
|
||||
@ -496,5 +525,5 @@ where you unpacked the tar file, you can::
|
||||
.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
|
||||
|
||||
:Author: sjg@crufty.net
|
||||
:Revision: $Id: mk-files.txt,v 1.20 2020/08/19 17:51:53 sjg Exp $
|
||||
:Revision: $Id: mk-files.txt,v 1.21 2022/02/04 19:01:05 sjg Exp $
|
||||
:Copyright: Crufty.NET
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: sys.clean-env.mk,v 1.23 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: sys.clean-env.mk,v 1.24 2022/01/15 17:34:42 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2009, Simon J. Gerraty
|
||||
#
|
||||
@ -52,7 +52,7 @@ MAKE_ENV_SAVE_PREFIX_LIST += \
|
||||
|
||||
|
||||
# This could be a list of vars or patterns to explicitly exclude.
|
||||
MAKE_ENV_SAVE_EXCLUDE_LIST ?= _
|
||||
MAKE_ENV_SAVE_EXCLUDE_LIST += _
|
||||
|
||||
# This is the actual list that we will save
|
||||
# HOME is probably something worth clobbering eg.
|
||||
@ -115,7 +115,7 @@ MAKEOBJDIR = $${.CURDIR:S,${_srctop},$${OBJTOP},}
|
||||
$v := ${$v}
|
||||
.endfor
|
||||
.else
|
||||
# we cannot use the '$$' trick, anymore
|
||||
# we cannot rely on the '$$' trick (depending on .MAKE.SAVE_DOLLARS)
|
||||
# but we can export a literal (unexpanded) value
|
||||
SRCTOP := ${_srctop}
|
||||
OBJROOT := ${_objroot}
|
||||
|
348
nonints.h
348
nonints.h
@ -1,348 +0,0 @@
|
||||
/* $NetBSD: nonints.h,v 1.217 2021/12/12 20:45:48 sjg Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Adam de Boor.
|
||||
*
|
||||
* 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.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*
|
||||
* from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1989 by Berkeley Softworks
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Adam de Boor.
|
||||
*
|
||||
* 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.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
|
||||
*
|
||||
* from: @(#)nonints.h 8.3 (Berkeley) 3/19/94
|
||||
*/
|
||||
|
||||
/* arch.c */
|
||||
void Arch_Init(void);
|
||||
void Arch_End(void);
|
||||
|
||||
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 *);
|
||||
bool Arch_LibOODate(GNode *);
|
||||
bool Arch_IsLib(GNode *);
|
||||
|
||||
/* compat.c */
|
||||
int Compat_RunCommand(const char *, GNode *, StringListNode *);
|
||||
void Compat_Run(GNodeList *);
|
||||
void Compat_Make(GNode *, GNode *);
|
||||
|
||||
/* cond.c */
|
||||
CondEvalResult Cond_EvalCondition(const char *, bool *);
|
||||
CondEvalResult Cond_EvalLine(const char *);
|
||||
void Cond_restore_depth(unsigned int);
|
||||
unsigned int Cond_save_depth(void);
|
||||
|
||||
/* dir.c; see also dir.h */
|
||||
|
||||
MAKE_INLINE const char *
|
||||
str_basename(const char *pathname)
|
||||
{
|
||||
const char *lastSlash = strrchr(pathname, '/');
|
||||
return lastSlash != NULL ? lastSlash + 1 : pathname;
|
||||
}
|
||||
|
||||
MAKE_INLINE SearchPath *
|
||||
SearchPath_New(void)
|
||||
{
|
||||
SearchPath *path = bmake_malloc(sizeof *path);
|
||||
Lst_Init(&path->dirs);
|
||||
return path;
|
||||
}
|
||||
|
||||
void SearchPath_Free(SearchPath *);
|
||||
|
||||
/* for.c */
|
||||
int For_Eval(const char *);
|
||||
bool For_Accum(const char *);
|
||||
void For_Run(int);
|
||||
|
||||
/* job.c */
|
||||
#ifdef WAIT_T
|
||||
void JobReapChild(pid_t, WAIT_T, bool);
|
||||
#endif
|
||||
|
||||
/* main.c */
|
||||
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);
|
||||
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void DieHorribly(void) MAKE_ATTR_DEAD;
|
||||
void Finish(int) MAKE_ATTR_DEAD;
|
||||
int eunlink(const char *);
|
||||
void execDie(const char *, const char *);
|
||||
char *getTmpdir(void);
|
||||
bool ParseBoolean(const char *, bool);
|
||||
char *cached_realpath(const char *, char *);
|
||||
|
||||
/* parse.c */
|
||||
void Parse_Init(void);
|
||||
void Parse_End(void);
|
||||
|
||||
typedef enum VarAssignOp {
|
||||
VAR_NORMAL, /* = */
|
||||
VAR_SUBST, /* := */
|
||||
VAR_SHELL, /* != or :sh= */
|
||||
VAR_APPEND, /* += */
|
||||
VAR_DEFAULT /* ?= */
|
||||
} VarAssignOp;
|
||||
|
||||
typedef struct VarAssign {
|
||||
char *varname; /* unexpanded */
|
||||
VarAssignOp op;
|
||||
const char *value; /* unexpanded */
|
||||
} VarAssign;
|
||||
|
||||
typedef char *(*ReadMoreProc)(void *, size_t *);
|
||||
|
||||
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
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_PushInput(const char *, int, int, ReadMoreProc, void *);
|
||||
void Parse_MainName(GNodeList *);
|
||||
int Parse_NumErrors(void);
|
||||
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
/* strlcpy.c */
|
||||
size_t strlcpy(char *, const char *, size_t);
|
||||
#endif
|
||||
|
||||
/* suff.c */
|
||||
void Suff_Init(void);
|
||||
void Suff_End(void);
|
||||
|
||||
void Suff_ClearSuffixes(void);
|
||||
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_ExtendPaths(void);
|
||||
void Suff_AddInclude(const char *);
|
||||
void Suff_AddLib(const char *);
|
||||
void Suff_FindDeps(GNode *);
|
||||
SearchPath *Suff_FindPath(GNode *);
|
||||
void Suff_SetNull(const char *);
|
||||
void Suff_PrintAll(void);
|
||||
const char *Suff_NamesStr(void);
|
||||
|
||||
/* targ.c */
|
||||
void Targ_Init(void);
|
||||
void Targ_End(void);
|
||||
|
||||
void Targ_Stats(void);
|
||||
GNodeList *Targ_List(void);
|
||||
GNode *GNode_New(const char *);
|
||||
GNode *Targ_FindNode(const char *);
|
||||
GNode *Targ_GetNode(const char *);
|
||||
GNode *Targ_NewInternalNode(const char *);
|
||||
GNode *Targ_GetEndNode(void);
|
||||
void Targ_FindList(GNodeList *, StringList *);
|
||||
bool Targ_Precious(const GNode *);
|
||||
void Targ_SetMain(GNode *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
void Targ_PrintNodes(GNodeList *, int);
|
||||
const char *Targ_FmtTime(time_t);
|
||||
void Targ_PrintType(GNodeType);
|
||||
void Targ_PrintGraph(int);
|
||||
void Targ_Propagate(void);
|
||||
const char *GNodeMade_Name(GNodeMade);
|
||||
|
||||
/* var.c */
|
||||
void Var_Init(void);
|
||||
void Var_End(void);
|
||||
|
||||
typedef enum VarEvalMode {
|
||||
|
||||
/*
|
||||
* Only parse the expression but don't evaluate any part of it.
|
||||
*
|
||||
* TODO: Document what Var_Parse and Var_Subst return in this mode.
|
||||
* As of 2021-03-15, they return unspecified, inconsistent results.
|
||||
*/
|
||||
VARE_PARSE_ONLY,
|
||||
|
||||
/* Parse and evaluate the expression. */
|
||||
VARE_WANTRES,
|
||||
|
||||
/*
|
||||
* Parse and evaluate the expression. It is an error if a
|
||||
* subexpression evaluates to undefined.
|
||||
*/
|
||||
VARE_UNDEFERR,
|
||||
|
||||
/*
|
||||
* 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_EVAL_KEEP_DOLLAR,
|
||||
|
||||
/*
|
||||
* Parse and evaluate the expression. Keep undefined variables as-is
|
||||
* instead of expanding them to an empty string.
|
||||
*
|
||||
* Example for a ':=' assignment:
|
||||
* CFLAGS = $(.INCLUDES)
|
||||
* CFLAGS := -I.. $(CFLAGS)
|
||||
* # If .INCLUDES (an undocumented special variable, by the
|
||||
* # way) is still undefined, the updated CFLAGS becomes
|
||||
* # "-I.. $(.INCLUDES)".
|
||||
*/
|
||||
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,
|
||||
|
||||
/* do not export */
|
||||
VAR_SET_NO_EXPORT = 1 << 0,
|
||||
|
||||
/* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag. */
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
} VarSetFlags;
|
||||
|
||||
/* The state of error handling returned by Var_Parse. */
|
||||
typedef enum VarParseResult {
|
||||
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK,
|
||||
|
||||
/* Parsing or evaluating failed, with an error message. */
|
||||
VPR_ERR,
|
||||
|
||||
/*
|
||||
* Parsing succeeded, undefined expressions are allowed and the
|
||||
* expression was still undefined after applying all modifiers.
|
||||
* No error message is printed in this case.
|
||||
*
|
||||
* Some callers handle this case differently, so return this
|
||||
* information to them, for now.
|
||||
*
|
||||
* TODO: Instead of having this special return value, rather ensure
|
||||
* that VARE_EVAL_KEEP_UNDEF is processed properly.
|
||||
*/
|
||||
VPR_UNDEF
|
||||
|
||||
} VarParseResult;
|
||||
|
||||
typedef enum VarExportMode {
|
||||
/* .export-env */
|
||||
VEM_ENV,
|
||||
/* .export: Initial export or update an already exported variable. */
|
||||
VEM_PLAIN,
|
||||
/* .export-literal: Do not expand the variable value. */
|
||||
VEM_LITERAL
|
||||
} VarExportMode;
|
||||
|
||||
void Var_Delete(GNode *, const char *);
|
||||
void Var_DeleteExpand(GNode *, const char *);
|
||||
void Var_Undef(const char *);
|
||||
void Var_Set(GNode *, const char *, const char *);
|
||||
void Var_SetExpand(GNode *, const char *, const char *);
|
||||
void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags);
|
||||
void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags);
|
||||
void Var_Append(GNode *, const char *, const char *);
|
||||
void Var_AppendExpand(GNode *, const char *, const char *);
|
||||
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 *, 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(bool, const char *);
|
||||
|
||||
void Global_Set(const char *, const char *);
|
||||
void Global_SetExpand(const char *, const char *);
|
||||
void Global_Append(const char *, const char *);
|
||||
void Global_Delete(const char *);
|
||||
|
||||
/* util.c */
|
||||
typedef void (*SignalProc)(int);
|
||||
SignalProc bmake_signal(int, SignalProc);
|
29
str.c
29
str.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: str.c,v 1.86 2021/06/21 16:59:18 rillig Exp $ */
|
||||
/* $NetBSD: str.c,v 1.88 2021/12/15 10:57:01 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -71,7 +71,11 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.86 2021/06/21 16:59:18 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.88 2021/12/15 10:57:01 rillig Exp $");
|
||||
|
||||
|
||||
static HashTable interned_strings;
|
||||
|
||||
|
||||
/* Return the concatenation of s1 and s2, freshly allocated. */
|
||||
char *
|
||||
@ -395,3 +399,24 @@ Str_Match(const char *str, const char *pat)
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Str_Intern_Init(void)
|
||||
{
|
||||
HashTable_Init(&interned_strings);
|
||||
}
|
||||
|
||||
void
|
||||
Str_Intern_End(void)
|
||||
{
|
||||
#ifdef CLEANUP
|
||||
HashTable_Done(&interned_strings);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return a canonical instance of str, with unlimited lifetime. */
|
||||
const char *
|
||||
Str_Intern(const char *str)
|
||||
{
|
||||
return HashTable_CreateEntry(&interned_strings, str, NULL)->key;
|
||||
}
|
||||
|
46
str.h
46
str.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: str.h,v 1.12 2021/12/12 13:43:47 rillig Exp $ */
|
||||
/* $NetBSD: str.h,v 1.15 2021/12/15 10:57:01 rillig Exp $ */
|
||||
|
||||
/*
|
||||
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
|
||||
@ -39,12 +39,6 @@ typedef struct FStr {
|
||||
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;
|
||||
@ -111,40 +105,6 @@ FStr_Done(FStr *fstr)
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
@ -383,3 +343,7 @@ char *str_concat2(const char *, const char *);
|
||||
char *str_concat3(const char *, const char *, const char *);
|
||||
|
||||
bool Str_Match(const char *, const char *);
|
||||
|
||||
void Str_Intern_Init(void);
|
||||
void Str_Intern_End(void);
|
||||
const char *Str_Intern(const char *);
|
||||
|
66
suff.c
66
suff.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg Exp $ */
|
||||
/* $NetBSD: suff.c,v 1.364 2022/01/07 20:54:45 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -115,7 +115,7 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
|
||||
MAKE_RCSID("$NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg Exp $");
|
||||
MAKE_RCSID("$NetBSD: suff.c,v 1.364 2022/01/07 20:54:45 rillig Exp $");
|
||||
|
||||
typedef List SuffixList;
|
||||
typedef ListNode SuffixListNode;
|
||||
@ -207,20 +207,26 @@ typedef struct Suffix {
|
||||
typedef struct Candidate {
|
||||
/* The file or node to look for. */
|
||||
char *file;
|
||||
/* The prefix from which file was formed.
|
||||
* Its memory is shared among all candidates. */
|
||||
/*
|
||||
* The prefix from which file was formed. Its memory is shared among
|
||||
* all candidates.
|
||||
*/
|
||||
char *prefix;
|
||||
/* The suffix on the file. */
|
||||
Suffix *suff;
|
||||
|
||||
/* The candidate that can be made from this,
|
||||
* or NULL for the top-level candidate. */
|
||||
/*
|
||||
* The candidate that can be made from this, or NULL for the
|
||||
* top-level candidate.
|
||||
*/
|
||||
struct Candidate *parent;
|
||||
/* The node describing the file. */
|
||||
GNode *node;
|
||||
|
||||
/* Count of existing children, only used for memory management, so we
|
||||
* don't free this candidate too early or too late. */
|
||||
/*
|
||||
* Count of existing children, only used for memory management, so we
|
||||
* don't free this candidate too early or too late.
|
||||
*/
|
||||
int numChildren;
|
||||
#ifdef DEBUG_SRC
|
||||
CandidateList childrenList;
|
||||
@ -240,7 +246,7 @@ typedef struct CandidateSearcher {
|
||||
|
||||
|
||||
/* TODO: Document the difference between nullSuff and emptySuff. */
|
||||
/* The NULL suffix for this run */
|
||||
/* The NULL suffix is used when a file has no known suffix */
|
||||
static Suffix *nullSuff;
|
||||
/* The empty suffix required for POSIX single-suffix transformation rules */
|
||||
static Suffix *emptySuff;
|
||||
@ -692,7 +698,9 @@ RebuildGraph(GNode *transform, Suffix *suff)
|
||||
size_t nameLen = strlen(name);
|
||||
const char *toName;
|
||||
|
||||
/* See if it is a transformation from this suffix to another suffix. */
|
||||
/*
|
||||
* See if it is a transformation from this suffix to another suffix.
|
||||
*/
|
||||
toName = StrTrimPrefix(suff->name, name);
|
||||
if (toName != NULL) {
|
||||
Suffix *to = FindSuffixByName(toName);
|
||||
@ -702,7 +710,9 @@ RebuildGraph(GNode *transform, Suffix *suff)
|
||||
}
|
||||
}
|
||||
|
||||
/* See if it is a transformation from another suffix to this suffix. */
|
||||
/*
|
||||
* See if it is a transformation from another suffix to this suffix.
|
||||
*/
|
||||
toName = Suffix_TrimSuffix(suff, nameLen, name + nameLen);
|
||||
if (toName != NULL) {
|
||||
Suffix *from = FindSuffixByNameLen(name,
|
||||
@ -724,17 +734,15 @@ RebuildGraph(GNode *transform, Suffix *suff)
|
||||
* true iff a new main target has been selected.
|
||||
*/
|
||||
static bool
|
||||
UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
|
||||
bool *inout_removedMain)
|
||||
UpdateTarget(GNode *target, Suffix *suff, bool *inout_removedMain)
|
||||
{
|
||||
Suffix *srcSuff, *targSuff;
|
||||
char *ptr;
|
||||
|
||||
if (*inout_main == NULL && *inout_removedMain &&
|
||||
!(target->type & OP_NOTARGET)) {
|
||||
if (mainNode == NULL && *inout_removedMain &&
|
||||
GNode_IsMainCandidate(target)) {
|
||||
DEBUG1(MAKE, "Setting main node to \"%s\"\n", target->name);
|
||||
*inout_main = target;
|
||||
Targ_SetMain(target);
|
||||
mainNode = target;
|
||||
/*
|
||||
* XXX: Why could it be a good idea to return true here?
|
||||
* The main task of this function is to turn ordinary nodes
|
||||
@ -772,13 +780,12 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
|
||||
return false;
|
||||
|
||||
if (ParseTransform(target->name, &srcSuff, &targSuff)) {
|
||||
if (*inout_main == target) {
|
||||
if (mainNode == target) {
|
||||
DEBUG1(MAKE,
|
||||
"Setting main node from \"%s\" back to null\n",
|
||||
target->name);
|
||||
*inout_removedMain = true;
|
||||
*inout_main = NULL;
|
||||
Targ_SetMain(NULL);
|
||||
mainNode = NULL;
|
||||
}
|
||||
Lst_Done(&target->children);
|
||||
Lst_Init(&target->children);
|
||||
@ -802,14 +809,14 @@ UpdateTarget(GNode *target, GNode **inout_main, Suffix *suff,
|
||||
* suffix rules.
|
||||
*/
|
||||
static void
|
||||
UpdateTargets(GNode **inout_main, Suffix *suff)
|
||||
UpdateTargets(Suffix *suff)
|
||||
{
|
||||
bool removedMain = false;
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = Targ_List()->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
if (UpdateTarget(gn, inout_main, suff, &removedMain))
|
||||
if (UpdateTarget(gn, suff, &removedMain))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -828,7 +835,7 @@ UpdateTargets(GNode **inout_main, Suffix *suff)
|
||||
* name the name of the suffix to add
|
||||
*/
|
||||
void
|
||||
Suff_AddSuffix(const char *name, GNode **inout_main)
|
||||
Suff_AddSuffix(const char *name)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
@ -840,7 +847,7 @@ Suff_AddSuffix(const char *name, GNode **inout_main)
|
||||
Lst_Append(&sufflist, suff);
|
||||
DEBUG1(SUFF, "Adding suffix \"%s\"\n", suff->name);
|
||||
|
||||
UpdateTargets(inout_main, suff);
|
||||
UpdateTargets(suff);
|
||||
|
||||
/*
|
||||
* Look for any existing transformations from or to this suffix.
|
||||
@ -1197,7 +1204,9 @@ FindCmds(Candidate *targ, CandidateSearcher *cs)
|
||||
base = str_basename(sgn->name);
|
||||
if (strncmp(base, targ->prefix, prefLen) != 0)
|
||||
continue;
|
||||
/* The node matches the prefix, see if it has a known suffix. */
|
||||
/*
|
||||
* The node matches the prefix, see if it has a known suffix.
|
||||
*/
|
||||
suff = FindSuffixByName(base + prefLen);
|
||||
if (suff == NULL)
|
||||
continue;
|
||||
@ -1254,7 +1263,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
|
||||
DEBUG1(SUFF, "%s...", cp);
|
||||
gn = Targ_GetNode(cp);
|
||||
|
||||
/* Add gn to the parents child list before the original child */
|
||||
/* Insert gn before the original child. */
|
||||
Lst_InsertBefore(&pgn->children, cln, gn);
|
||||
Lst_Append(&gn->parents, pgn);
|
||||
pgn->unmade++;
|
||||
@ -1767,8 +1776,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ)
|
||||
|
||||
free(gn->path);
|
||||
gn->path = Dir_FindFile(gn->name,
|
||||
(targ == NULL ? &dirSearchPath :
|
||||
targ->suff->searchPath));
|
||||
targ == NULL ? &dirSearchPath : targ->suff->searchPath);
|
||||
if (gn->path == NULL)
|
||||
return;
|
||||
|
||||
@ -2178,7 +2186,7 @@ Suff_PrintAll(void)
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
char *
|
||||
Suff_NamesStr(void)
|
||||
{
|
||||
Buffer buf;
|
||||
|
40
targ.c
40
targ.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $ */
|
||||
/* $NetBSD: targ.c,v 1.176 2022/01/07 20:50:35 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -78,10 +78,8 @@
|
||||
*
|
||||
* Targ_List Return the list of all targets so far.
|
||||
*
|
||||
* GNode_New Create a new GNode for the passed target
|
||||
* (string). The node is *not* placed in the
|
||||
* hash table, though all its fields are
|
||||
* initialized.
|
||||
* GNode_New Create a new GNode with the given name, don't add it
|
||||
* to allNodes.
|
||||
*
|
||||
* Targ_FindNode Find the node, or return NULL.
|
||||
*
|
||||
@ -93,9 +91,6 @@
|
||||
* 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
|
||||
* should not be removed if we are interrupted.
|
||||
*
|
||||
* Targ_Propagate Propagate information between related nodes.
|
||||
* Should be called after the makefiles are parsed
|
||||
* but before any action is taken.
|
||||
@ -103,8 +98,7 @@
|
||||
* Debugging:
|
||||
* Targ_PrintGraph
|
||||
* Print out the entire graph, all variables and
|
||||
* statistics for the directory cache. Should print
|
||||
* something for suffixes, too, but...
|
||||
* statistics for the directory cache.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
@ -113,7 +107,7 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.176 2022/01/07 20:50:35 rillig Exp $");
|
||||
|
||||
/*
|
||||
* All target nodes that appeared on the left-hand side of one of the
|
||||
@ -346,27 +340,6 @@ Targ_FindList(GNodeList *gns, StringList *names)
|
||||
}
|
||||
}
|
||||
|
||||
/* See if the given target is precious. */
|
||||
bool
|
||||
Targ_Precious(const GNode *gn)
|
||||
{
|
||||
/* XXX: Why are '::' targets precious? */
|
||||
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
|
||||
}
|
||||
|
||||
/*
|
||||
* The main target to be made; only for debugging output.
|
||||
* See mainNode in parse.c for the definitive source.
|
||||
*/
|
||||
static GNode *mainTarg;
|
||||
|
||||
/* Remember the main target to make; only used for debugging. */
|
||||
void
|
||||
Targ_SetMain(GNode *gn)
|
||||
{
|
||||
mainTarg = gn;
|
||||
}
|
||||
|
||||
static void
|
||||
PrintNodeNames(GNodeList *gnodes)
|
||||
{
|
||||
@ -508,7 +481,7 @@ Targ_PrintNode(GNode *gn, int pass)
|
||||
return;
|
||||
|
||||
debug_printf("#\n");
|
||||
if (gn == mainTarg)
|
||||
if (gn == mainNode)
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
|
||||
if (pass >= 2) {
|
||||
@ -556,7 +529,6 @@ Targ_PrintNodes(GNodeList *gnodes, int pass)
|
||||
Targ_PrintNode(ln->datum, pass);
|
||||
}
|
||||
|
||||
/* Print only those targets that are just a source. */
|
||||
static void
|
||||
PrintOnlySources(void)
|
||||
{
|
||||
|
17
trace.c
17
trace.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $ */
|
||||
/* $NetBSD: trace.c,v 1.31 2022/02/05 00:26:21 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
@ -48,7 +48,7 @@
|
||||
#include "job.h"
|
||||
#include "trace.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: trace.c,v 1.31 2022/02/05 00:26:21 rillig Exp $");
|
||||
|
||||
static FILE *trfile;
|
||||
static pid_t trpid;
|
||||
@ -69,8 +69,10 @@ Trace_Init(const char *pathname)
|
||||
if (pathname != NULL) {
|
||||
FStr curDir;
|
||||
trpid = getpid();
|
||||
/* XXX: This variable may get overwritten later, which
|
||||
* would make trwd point to undefined behavior. */
|
||||
/*
|
||||
* XXX: This variable may get overwritten later, which would
|
||||
* make trwd point to undefined behavior.
|
||||
*/
|
||||
curDir = Var_Value(SCOPE_GLOBAL, ".CURDIR");
|
||||
trwd = curDir.str;
|
||||
|
||||
@ -88,10 +90,17 @@ Trace_Log(TrEvent event, Job *job)
|
||||
|
||||
gettimeofday(&rightnow, NULL);
|
||||
|
||||
#if __STDC__ >= 199901L
|
||||
fprintf(trfile, "%lld.%06ld %d %s %d %s",
|
||||
(long long)rightnow.tv_sec, (long)rightnow.tv_usec,
|
||||
jobTokensRunning,
|
||||
evname[event], trpid, trwd);
|
||||
#else
|
||||
fprintf(trfile, "%ld.%06ld %d %s %d %s",
|
||||
(long)rightnow.tv_sec, (long)rightnow.tv_usec,
|
||||
jobTokensRunning,
|
||||
evname[event], trpid, trwd);
|
||||
#endif
|
||||
if (job != NULL) {
|
||||
char flags[4];
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $Id: Makefile,v 1.164 2021/12/12 22:50:00 sjg Exp $
|
||||
# $Id: Makefile,v 1.171 2022/01/28 21:33:18 sjg Exp $
|
||||
#
|
||||
# $NetBSD: Makefile,v 1.288 2021/12/12 22:16:48 rillig Exp $
|
||||
# $NetBSD: Makefile,v 1.302 2022/01/27 21:50:50 sjg Exp $
|
||||
#
|
||||
# Unit tests for make(1)
|
||||
#
|
||||
@ -91,8 +91,10 @@ TESTS+= dep-colon
|
||||
TESTS+= dep-colon-bug-cross-file
|
||||
TESTS+= dep-double-colon
|
||||
TESTS+= dep-double-colon-indep
|
||||
TESTS+= dep-duplicate
|
||||
TESTS+= dep-exclam
|
||||
TESTS+= dep-none
|
||||
TESTS+= dep-op-missing
|
||||
TESTS+= dep-percent
|
||||
TESTS+= dep-var
|
||||
TESTS+= dep-wildcards
|
||||
@ -189,7 +191,6 @@ TESTS+= directive-warning
|
||||
TESTS+= dollar
|
||||
TESTS+= doterror
|
||||
TESTS+= dotwait
|
||||
TESTS+= envfirst
|
||||
TESTS+= error
|
||||
TESTS+= # escape # broken by reverting POSIX changes
|
||||
TESTS+= export
|
||||
@ -216,8 +217,6 @@ TESTS+= meta-cmd-cmp
|
||||
TESTS+= moderrs
|
||||
TESTS+= modmatch
|
||||
TESTS+= modmisc
|
||||
TESTS+= modts
|
||||
TESTS+= modword
|
||||
.if ${.MAKE.UID} > 0
|
||||
TESTS+= objdir-writable
|
||||
.endif
|
||||
@ -273,10 +272,12 @@ TESTS+= opt-touch-jobs
|
||||
TESTS+= opt-tracefile
|
||||
TESTS+= opt-var-expanded
|
||||
TESTS+= opt-var-literal
|
||||
TESTS+= opt-version
|
||||
TESTS+= opt-warnings-as-errors
|
||||
TESTS+= opt-where-am-i
|
||||
TESTS+= opt-x-reduce-exported
|
||||
TESTS+= order
|
||||
TESTS+= parse
|
||||
TESTS+= parse-var
|
||||
TESTS+= phony-end
|
||||
TESTS+= posix
|
||||
@ -319,12 +320,12 @@ TESTS+= ternary
|
||||
TESTS+= unexport
|
||||
TESTS+= unexport-env
|
||||
TESTS+= use-inference
|
||||
TESTS+= var-class
|
||||
TESTS+= var-class-cmdline
|
||||
TESTS+= var-class-env
|
||||
TESTS+= var-class-global
|
||||
TESTS+= var-class-local
|
||||
TESTS+= var-class-local-legacy
|
||||
TESTS+= var-scope
|
||||
TESTS+= var-scope-cmdline
|
||||
TESTS+= var-scope-env
|
||||
TESTS+= var-scope-global
|
||||
TESTS+= var-scope-local
|
||||
TESTS+= var-scope-local-legacy
|
||||
TESTS+= var-eval-short
|
||||
TESTS+= var-op
|
||||
TESTS+= var-op-append
|
||||
@ -340,6 +341,7 @@ TESTS+= varfind
|
||||
TESTS+= varmisc
|
||||
TESTS+= varmod
|
||||
TESTS+= varmod-assign
|
||||
TESTS+= varmod-assign-shell
|
||||
TESTS+= varmod-defined
|
||||
TESTS+= varmod-edge
|
||||
TESTS+= varmod-exclam-shell
|
||||
@ -498,7 +500,6 @@ ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
|
||||
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
|
||||
# settings FLAGS.test=-dv here, since that is closer to the test code.
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none, especially not -k
|
||||
FLAGS.jobs-error-indirect= # none, especially not -k
|
||||
FLAGS.jobs-error-nested= # none, especially not -k
|
||||
@ -531,6 +532,7 @@ 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}
|
||||
SED_CMDS.opt-debug-hash= -e 's,\(numEntries\)=[1-9][0-9],\1=<entries>,'
|
||||
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
|
||||
@ -541,6 +543,7 @@ 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}
|
||||
SED_CMDS.opt-where-am-i= -e '/usr.obj/d'
|
||||
# For Compat_RunCommand, useShell == false.
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
|
||||
# For Compat_RunCommand, useShell == true.
|
||||
@ -736,6 +739,9 @@ _SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,'
|
||||
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
|
||||
# replace anything after 'stopped in' with unit-tests
|
||||
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
|
||||
# Allow the test files to be placed anywhere.
|
||||
_SED_CMDS+= -e 's,\(\.PARSEDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
|
||||
_SED_CMDS+= -e 's,\(\.INCLUDEDFROMDIR}\) = `'"/[^']*'"',\1 = <some-dir>,'
|
||||
_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g'
|
||||
# canonicalize ${.OBJDIR} and ${.CURDIR}
|
||||
.if ${.OBJDIR} != ${.CURDIR}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: comment.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
|
||||
# $NetBSD: comment.mk,v 1.4 2022/01/23 18:00:53 rillig Exp $
|
||||
#
|
||||
# Demonstrate how comments are written in makefiles.
|
||||
|
||||
@ -15,7 +15,9 @@ on and on.
|
||||
# Comments can be indented with spaces, but that is rather unusual.
|
||||
|
||||
# Comments can be indented with a tab.
|
||||
# These are not shell commands, they are just makefile comments.
|
||||
# Since parse.c 1.127 from 2007-01-01, these are not shell commands,
|
||||
# they are just makefile comments. Before that commit, these comments
|
||||
# triggered the error message "Unassociated shell command".
|
||||
|
||||
.if 1 # There can be comments after conditions.
|
||||
.endif # And after the closing directive.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-empty.mk,v 1.16 2021/12/11 10:41:31 rillig Exp $
|
||||
# $NetBSD: cond-func-empty.mk,v 1.17 2021/12/28 22:13:56 rillig Exp $
|
||||
#
|
||||
# Tests for the empty() function in .if conditions, which tests a variable
|
||||
# expression for emptiness.
|
||||
@ -189,5 +189,16 @@ VARNAME= ${VARNAME${:U1}}
|
||||
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
||||
# If the word 'empty' is not followed by '(', it is not a function call but an
|
||||
# ordinary bare word. This bare word is interpreted as 'defined(empty)', and
|
||||
# since there is no variable named 'empty', the condition evaluates to false.
|
||||
.if empty
|
||||
. error
|
||||
.endif
|
||||
|
||||
empty= # defined but empty
|
||||
.if empty
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
@ -2,11 +2,11 @@ make: "cond-func.mk" line 36: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 51: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 54: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 94: The empty variable is never defined.
|
||||
make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
|
||||
make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).
|
||||
make: "cond-func.mk" line 119: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 124: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 130: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 103: A plain function name is parsed as defined(...).
|
||||
make: "cond-func.mk" line 110: A plain function name is parsed as defined(...).
|
||||
make: "cond-func.mk" line 120: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 125: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 131: Missing closing parenthesis for defined()
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func.mk,v 1.9 2020/11/15 14:07:53 rillig Exp $
|
||||
# $NetBSD: cond-func.mk,v 1.11 2022/01/07 19:30:17 rillig Exp $
|
||||
#
|
||||
# Tests for those parts of the functions in .if conditions that are common
|
||||
# among several functions.
|
||||
@ -94,19 +94,20 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
|
||||
. info The empty variable is never defined.
|
||||
.endif
|
||||
|
||||
# The plain word 'defined' is interpreted as '!empty(defined)'.
|
||||
# The plain word 'defined' is interpreted as 'defined(defined)', see
|
||||
# CondParser_ComparisonOrLeaf.
|
||||
# That variable is not defined (yet).
|
||||
.if defined
|
||||
. error
|
||||
.else
|
||||
. info A plain function name is parsed as !empty(...).
|
||||
. info A plain function name is parsed as defined(...).
|
||||
.endif
|
||||
|
||||
# If a variable named 'defined' is actually defined and not empty, the plain
|
||||
# symbol 'defined' evaluates to true.
|
||||
defined= non-empty
|
||||
# If a variable named 'defined' is actually defined, the bare word 'defined'
|
||||
# is interpreted as 'defined(defined)', and the condition evaluates to true.
|
||||
defined= # defined but empty
|
||||
.if defined
|
||||
. info A plain function name is parsed as !empty(...).
|
||||
. info A plain function name is parsed as defined(...).
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
@ -119,7 +120,7 @@ defined= non-empty
|
||||
. info Symbols may start with a function name.
|
||||
.endif
|
||||
|
||||
defined-var= non-empty
|
||||
defined-var= # defined but empty
|
||||
.if defined-var
|
||||
. info Symbols may start with a function name.
|
||||
.else
|
||||
@ -132,6 +133,3 @@ defined-var= non-empty
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,6 +1,7 @@
|
||||
make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112.
|
||||
make: "cond-op-parentheses.mk" line 19: Malformed conditional (()
|
||||
make: "cond-op-parentheses.mk" line 29: Malformed conditional ())
|
||||
make: "cond-op-parentheses.mk" line 19: String comparison operator must be either == or !=
|
||||
make: "cond-op-parentheses.mk" line 22: Malformed conditional ((3) > 2)
|
||||
make: "cond-op-parentheses.mk" line 40: Malformed conditional (()
|
||||
make: "cond-op-parentheses.mk" line 53: Malformed conditional ())
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,26 @@
|
||||
# $NetBSD: cond-op-parentheses.mk,v 1.4 2021/01/19 17:49:13 rillig Exp $
|
||||
# $NetBSD: cond-op-parentheses.mk,v 1.5 2022/01/22 21:50:41 rillig Exp $
|
||||
#
|
||||
# Tests for parentheses in .if conditions.
|
||||
# Tests for parentheses in .if conditions, which group expressions to override
|
||||
# the precedence of the operators '!', '&&' and '||'. Parentheses cannot be
|
||||
# used to form arithmetic expressions such as '(3+4)' though.
|
||||
|
||||
# TODO: Implementation
|
||||
# Contrary to the C family of programming languages, the outermost condition
|
||||
# does not have to be enclosed in parentheses.
|
||||
.if defined(VAR)
|
||||
. error
|
||||
.elif !1
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Parentheses cannot enclose numbers as there is no need for it. Make does
|
||||
# not implement any arithmetic functions in its condition parser. If
|
||||
# absolutely necessary, use expr(1).
|
||||
# expect+1: String comparison operator must be either == or !=
|
||||
.if 3 > (2)
|
||||
.endif
|
||||
# expect+1: Malformed conditional ((3) > 2)
|
||||
.if (3) > 2
|
||||
.endif
|
||||
|
||||
# Test for deeply nested conditions.
|
||||
.if (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
|
||||
@ -10,7 +28,10 @@
|
||||
1 \
|
||||
)))))))))))))))))))))))))))))))))))))))))))))))))))))))) \
|
||||
))))))))))))))))))))))))))))))))))))))))))))))))))))))))
|
||||
. info Parentheses can be nested at least to depth 112.
|
||||
# Parentheses can be nested at least to depth 112. There is nothing special
|
||||
# about this number though, much higher numbers work as well, at least on
|
||||
# NetBSD. The actual limit depends on the allowed call stack depth for C code
|
||||
# of the platform. Anyway, 112 should be enough for all practical purposes.
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
@ -24,8 +45,11 @@
|
||||
|
||||
# An unbalanced closing parenthesis is a parse error.
|
||||
#
|
||||
# As of 2021-01-19, CondParser_Term returned TOK_RPAREN even though this
|
||||
# function promised to only ever return TOK_TRUE, TOK_FALSE or TOK_ERROR.
|
||||
# Before cond.c 1.237 from 2021-01-19, CondParser_Term returned TOK_RPAREN
|
||||
# even though the documentation of that function promised to only ever return
|
||||
# TOK_TRUE, TOK_FALSE or TOK_ERROR. In cond.c 1.241, the return type of that
|
||||
# function was changed to a properly restricted enum type, to prevent this bug
|
||||
# from occurring again.
|
||||
.if )
|
||||
. error
|
||||
.else
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-short.mk,v 1.18 2021/12/12 09:49:09 rillig Exp $
|
||||
# $NetBSD: cond-short.mk,v 1.19 2021/12/27 18:54:19 rillig Exp $
|
||||
#
|
||||
# Demonstrates that in conditions, the right-hand side of an && or ||
|
||||
# is only evaluated if it can actually influence the result.
|
||||
@ -14,14 +14,14 @@
|
||||
# relevant variable expressions was that in the irrelevant variable
|
||||
# expressions, undefined variables were allowed. This allowed for conditions
|
||||
# like 'defined(VAR) && ${VAR:S,from,to,} != ""', which no longer produced an
|
||||
# error message 'Malformed conditional', but it still evaluated the
|
||||
# expression, even though the expression was irrelevant.
|
||||
# error message 'Malformed conditional', but the irrelevant expression was
|
||||
# still evaluated.
|
||||
#
|
||||
# Since the initial commit on 1993-03-21, the manual page has been saying that
|
||||
# make 'will only evaluate a conditional as far as is necessary to determine',
|
||||
# but that was wrong. The code in cond.c 1.1 from 1993-03-21 looks good since
|
||||
# it calls Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree), but the
|
||||
# definition of Var_Parse does not call the third parameter 'doEval', as would
|
||||
# definition of Var_Parse did not call the third parameter 'doEval', as would
|
||||
# be expected, but instead 'err', accompanied by the comment 'TRUE if
|
||||
# undefined variables are an error'. This subtle difference between 'do not
|
||||
# evaluate at all' and 'allow undefined variables' led to the unexpected
|
||||
@ -122,7 +122,9 @@ VAR= # empty again, for the following tests
|
||||
.if 0 || empty(${echo "expected or empty" 1>&2 :L:sh})
|
||||
.endif
|
||||
|
||||
# Unreachable nested conditions are skipped completely as well.
|
||||
# Unreachable nested conditions are skipped completely as well. These skipped
|
||||
# lines may even contain syntax errors. This allows to skip syntactically
|
||||
# incompatible new features in older versions of make.
|
||||
|
||||
.if 0
|
||||
. if ${echo "unexpected nested and" 1>&2 :L:sh}
|
||||
|
@ -2,7 +2,7 @@ make: "cond-token-number.mk" line 15: Malformed conditional (-0)
|
||||
make: "cond-token-number.mk" line 25: Malformed conditional (+0)
|
||||
make: "cond-token-number.mk" line 35: Malformed conditional (!-1)
|
||||
make: "cond-token-number.mk" line 45: Malformed conditional (!+1)
|
||||
make: "cond-token-number.mk" line 80: End of the tests.
|
||||
make: "cond-token-number.mk" line 89: End of the tests.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-token-number.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
|
||||
# $NetBSD: cond-token-number.mk,v 1.7 2022/01/02 02:57:39 rillig Exp $
|
||||
#
|
||||
# Tests for number tokens in .if conditions.
|
||||
#
|
||||
@ -69,13 +69,22 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This is not a hexadecimal number, even though it has an x.
|
||||
# It is interpreted as a string instead, effectively meaning defined(3x4).
|
||||
# This is not a hexadecimal number, even though it has an x. It is
|
||||
# interpreted as a string instead. In a plain '.if', such a token evaluates
|
||||
# to true if it is non-empty. In other '.if' directives, such a token is
|
||||
# evaluated by either FuncDefined or FuncMake.
|
||||
.if 3x4
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Make can do radix conversion from hex.
|
||||
HEX= dead
|
||||
.if 0x${HEX} == 57005
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Ensure that parsing continues until here.
|
||||
.info End of the tests.
|
||||
|
||||
|
@ -27,7 +27,7 @@ lhs = "var&&name", rhs = "var&&name", op = !=
|
||||
CondParser_Eval: ${:Uvar}||name != "var||name"
|
||||
lhs = "var||name", rhs = "var||name", op = !=
|
||||
CondParser_Eval: bare
|
||||
make: "cond-token-plain.mk" line 106: A bare word is treated like defined(...), and the variable 'bare' is not defined.
|
||||
make: "cond-token-plain.mk" line 105: A bare word is treated like defined(...), and the variable 'bare' is not defined.
|
||||
CondParser_Eval: VAR
|
||||
make: "cond-token-plain.mk" line 111: A bare word is treated like defined(...).
|
||||
CondParser_Eval: V${:UA}R
|
||||
@ -56,6 +56,7 @@ CondParser_Eval: 0
|
||||
make: "cond-token-plain.mk" line 201: Malformed conditional (${0:?:} || left == right)
|
||||
CondParser_Eval: left == right || ${0:?:}
|
||||
make: "cond-token-plain.mk" line 206: Malformed conditional (left == right || ${0:?:})
|
||||
make: "cond-token-plain.mk" line 225: Malformed conditional (VAR.${IF_COUNT::+=1} != "")
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-token-plain.mk,v 1.14 2021/12/12 09:36:00 rillig Exp $
|
||||
# $NetBSD: cond-token-plain.mk,v 1.15 2021/12/30 02:14:55 rillig Exp $
|
||||
#
|
||||
# Tests for plain tokens (that is, string literals without quotes)
|
||||
# in .if conditions. These are also called bare words.
|
||||
@ -209,5 +209,48 @@ ${:U\\\\}= backslash
|
||||
# See cond-token-string.mk for similar tests where the condition is enclosed
|
||||
# in "quotes".
|
||||
|
||||
all:
|
||||
@:;
|
||||
.MAKEFLAGS: -d0
|
||||
|
||||
|
||||
# As of cond.c 1.320 from 2021-12-30, the code in CondParser_ComparisonOrLeaf
|
||||
# looks suspicious of evaluating the expression twice: first for parsing a
|
||||
# bare word and second for parsing the left-hand side of a comparison.
|
||||
#
|
||||
# In '.if' directives, the left-hand side of a comparison must not be a bare
|
||||
# word though, and this keeps CondParser_Leaf from evaluating the expression
|
||||
# for the second time. The right-hand side of a comparison may be a bare
|
||||
# word, but that side has no risk of being parsed more than once.
|
||||
#
|
||||
# expect+1: Malformed conditional (VAR.${IF_COUNT::+=1} != "")
|
||||
.if VAR.${IF_COUNT::+=1} != ""
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
.if ${IF_COUNT} != "1"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A different situation is when CondParser.leftUnquotedOK is true. This
|
||||
# situation arises in expressions of the form ${cond:?yes:no}. As of
|
||||
# 2021-12-30, the condition in such an expression is evaluated before parsing
|
||||
# the condition, see varmod-ifelse.mk. To pass a variable expression to the
|
||||
# condition parser, it needs to be escaped. This rarely happens in practice,
|
||||
# in most cases the conditions are simple enough that it doesn't matter
|
||||
# whether the condition is first evaluated and then parsed, or vice versa.
|
||||
# A half-baked attempt at hiding this implementation detail is
|
||||
# CondParser.leftUnquotedOK, but that is a rather leaky abstraction.
|
||||
|
||||
#.MAKEFLAGS: -dcv
|
||||
COND= VAR.$${MOD_COUNT::+=1}
|
||||
.if ${${COND} == "VAR.":?yes:no} != "yes"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The value "1 1" demonstrates that the expression ${MOD_COUNT::+=1} was
|
||||
# evaluated twice. In practice, expressions that occur in conditions do not
|
||||
# have side effects, making this problem rather academic, but it is there.
|
||||
.if ${MOD_COUNT} != "1 1"
|
||||
. error
|
||||
.endif
|
||||
#.MAKEFLAGS: -d0
|
||||
|
@ -6,9 +6,9 @@ make: "cond-token-string.mk" line 37: Expected.
|
||||
CondParser_Eval: "UNDEF"
|
||||
make: "cond-token-string.mk" line 46: The string literal "UNDEF" is not empty.
|
||||
CondParser_Eval: " "
|
||||
make: "cond-token-string.mk" line 55: The string literal " " is not empty, even though it consists of whitespace only.
|
||||
make: "cond-token-string.mk" line 54: The string literal " " is not empty, even though it consists of whitespace only.
|
||||
CondParser_Eval: "${UNDEF}"
|
||||
make: "cond-token-string.mk" line 64: An undefined variable in quotes expands to an empty string, which then evaluates to false.
|
||||
make: "cond-token-string.mk" line 63: An undefined variable in quotes expands to an empty string, which then evaluates to false.
|
||||
CondParser_Eval: "${:Uvalue}"
|
||||
make: "cond-token-string.mk" line 68: A nonempty variable expression evaluates to true.
|
||||
CondParser_Eval: "${:U}"
|
||||
|
4
unit-tests/dep-duplicate.exp
Normal file
4
unit-tests/dep-duplicate.exp
Normal file
@ -0,0 +1,4 @@
|
||||
make: "dep-duplicate.inc" line 1: warning: duplicate script for target "all" ignored
|
||||
make: "dep-duplicate.main" line 3: warning: using previous script for "all" defined here
|
||||
main-output
|
||||
exit status 0
|
27
unit-tests/dep-duplicate.mk
Normal file
27
unit-tests/dep-duplicate.mk
Normal file
@ -0,0 +1,27 @@
|
||||
# $NetBSD: dep-duplicate.mk,v 1.3 2022/01/20 19:24:53 rillig Exp $
|
||||
#
|
||||
# Test for a target whose commands are defined twice. This generates a
|
||||
# warning, not an error, so ensure that the correct commands are kept.
|
||||
#
|
||||
# Also ensure that the diagnostics mention the correct file in case of
|
||||
# included files. Since parse.c 1.231 from 2018-12-22 and before parse.c
|
||||
# 1.653 from 2022-01-20, the wrong filename had been printed if the file of
|
||||
# the first commands section was included by its relative path.
|
||||
|
||||
all: .PHONY
|
||||
@exec > dep-duplicate.main; \
|
||||
echo '# empty line 1'; \
|
||||
echo '# empty line 2'; \
|
||||
echo 'all:; @echo main-output'; \
|
||||
echo '.include "dep-duplicate.inc"'
|
||||
|
||||
@exec > dep-duplicate.inc; \
|
||||
echo 'all:; @echo inc-output'
|
||||
|
||||
# The main file must be specified using a relative path, just like the
|
||||
# default 'makefile' or 'Makefile', to produce the same result when
|
||||
# run via ATF or 'make test'.
|
||||
@${MAKE} -r -f dep-duplicate.main
|
||||
|
||||
@rm -f dep-duplicate.main
|
||||
@rm -f dep-duplicate.inc
|
4
unit-tests/dep-op-missing.exp
Normal file
4
unit-tests/dep-op-missing.exp
Normal file
@ -0,0 +1,4 @@
|
||||
make: "dep-op-missing.tmp" line 1: Invalid line type
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 0
|
13
unit-tests/dep-op-missing.mk
Normal file
13
unit-tests/dep-op-missing.mk
Normal file
@ -0,0 +1,13 @@
|
||||
# $NetBSD: dep-op-missing.mk,v 1.1 2021/12/14 00:02:57 rillig Exp $
|
||||
#
|
||||
# Test for a missing dependency operator, in a line with trailing whitespace.
|
||||
|
||||
# Before parse.c 1.578 from 2021-12-14, there was some unreachable error
|
||||
# handling code in ParseDependencyOp. This test tried to reach it and failed.
|
||||
# To reach that point, there would have to be trailing whitespace in the line,
|
||||
# but that is removed in an early stage of parsing.
|
||||
|
||||
all: .PHONY
|
||||
@printf 'target ' > dep-op-missing.tmp
|
||||
@${MAKE} -r -f dep-op-missing.tmp || exit 0
|
||||
@rm dep-op-missing.tmp
|
@ -2,8 +2,10 @@ dep-colon-bug-cross-file.mk
|
||||
dep-colon.mk
|
||||
dep-double-colon-indep.mk
|
||||
dep-double-colon.mk
|
||||
dep-duplicate.mk
|
||||
dep-exclam.mk
|
||||
dep-none.mk
|
||||
dep-op-missing.mk
|
||||
dep-percent.mk
|
||||
dep-var.mk
|
||||
dep-wildcards.mk
|
||||
|
@ -1 +1,5 @@
|
||||
exit status 0
|
||||
make: "dep.mk" line 11: Inconsistent operator for only-colon
|
||||
make: "dep.mk" line 13: Inconsistent operator for only-colon
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,18 @@
|
||||
# $NetBSD: dep.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: dep.mk,v 1.3 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for dependency declarations, such as "target: sources".
|
||||
|
||||
# TODO: Implementation
|
||||
.MAIN: all
|
||||
|
||||
# As soon as a target is defined using one of the dependency operators, it is
|
||||
# restricted to this dependency operator and cannot use the others anymore.
|
||||
only-colon:
|
||||
# expect+1: Inconsistent operator for only-colon
|
||||
only-colon!
|
||||
# expect+1: Inconsistent operator for only-colon
|
||||
only-colon::
|
||||
# Ensure that the target still has the original operator. If it hadn't, there
|
||||
# would be another error message.
|
||||
only-colon:
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -2,4 +2,6 @@ Skipping meta for actual-test: no commands
|
||||
Skipping meta for .END: .SPECIAL
|
||||
Targets from meta mode:
|
||||
| TARGET depsrc-meta-target
|
||||
Targets from meta mode in jobs mode:
|
||||
| TARGET depsrc-meta-target
|
||||
exit status 0
|
||||
|
@ -1,31 +1,30 @@
|
||||
# $NetBSD: depsrc-meta.mk,v 1.4 2020/11/27 08:39:07 rillig Exp $
|
||||
# $NetBSD: depsrc-meta.mk,v 1.6 2022/01/26 22:47:03 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .META in dependency declarations.
|
||||
|
||||
# TODO: Implementation
|
||||
# TODO: Explanation
|
||||
|
||||
.if make(actual-test)
|
||||
.MAIN: all
|
||||
|
||||
.if make(actual-test)
|
||||
.MAKEFLAGS: -dM
|
||||
.MAKE.MODE= meta curDirOk=true
|
||||
.endif
|
||||
|
||||
actual-test: depsrc-meta-target
|
||||
depsrc-meta-target: .META
|
||||
@> ${.TARGET}-file
|
||||
@rm -f ${.TARGET}-file
|
||||
|
||||
.elif make(check-results)
|
||||
|
||||
check-results:
|
||||
@echo 'Targets from meta mode:'
|
||||
@echo 'Targets from meta mode${.MAKE.JOBS:D in jobs mode}:'
|
||||
@awk '/^TARGET/ { print "| " $$0 }' depsrc-meta-target.meta
|
||||
@rm depsrc-meta-target.meta
|
||||
|
||||
.else
|
||||
|
||||
all:
|
||||
@${MAKE} -f ${MAKEFILE} actual-test
|
||||
@${MAKE} -f ${MAKEFILE} check-results
|
||||
@${MAKE} -r -f ${MAKEFILE} actual-test
|
||||
@${MAKE} -r -f ${MAKEFILE} check-results
|
||||
|
||||
.endif
|
||||
@${MAKE} -r -f ${MAKEFILE} actual-test -j1
|
||||
@${MAKE} -r -f ${MAKEFILE} check-results -j1
|
||||
|
@ -1,8 +1,13 @@
|
||||
# $NetBSD: depsrc-use.mk,v 1.4 2020/08/22 12:30:57 rillig Exp $
|
||||
# $NetBSD: depsrc-use.mk,v 1.5 2021/12/28 14:22:51 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .USE in dependency declarations,
|
||||
# which allows to append common commands to other targets.
|
||||
|
||||
# Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally
|
||||
# regarded as a candidate for the main target. On the other hand, a .USE
|
||||
# target was not.
|
||||
not-a-main-candidate: .USE
|
||||
|
||||
all: action directly
|
||||
|
||||
first: .USE
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: depsrc-usebefore.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: depsrc-usebefore.mk,v 1.7 2021/12/28 14:22:51 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .USEBEFORE in dependency declarations,
|
||||
# which allows to prepend common commands to other targets.
|
||||
@ -7,6 +7,11 @@
|
||||
# .USE
|
||||
# depsrc-use.mk
|
||||
|
||||
# Before make.h 1.280 from 2021-12-28, a .USEBEFORE target was accidentally
|
||||
# regarded as a candidate for the main target. On the other hand, a .USE
|
||||
# target was not.
|
||||
not-a-main-candidate: .USEBEFORE
|
||||
|
||||
all: action directly
|
||||
|
||||
first: .USEBEFORE
|
||||
|
@ -1,4 +1,5 @@
|
||||
: 'Undefined variables are expanded directly in the dependency'
|
||||
: 'declaration. They are not preserved and maybe expanded later.'
|
||||
: 'This is in contrast to local variables such as ${.TARGET}.'
|
||||
: Making .UNKNOWN from nothing.
|
||||
exit status 0
|
||||
|
@ -1,7 +1,7 @@
|
||||
# $NetBSD: depsrc.mk,v 1.4 2020/12/22 19:38:44 rillig Exp $
|
||||
# $NetBSD: depsrc.mk,v 1.5 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for special sources (those starting with a dot, followed by
|
||||
# uppercase letters) in dependency declarations, such as .PHONY.
|
||||
# uppercase letters) in dependency declarations, such as '.PHONY'.
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
@ -14,13 +14,19 @@ target: .PHONY source-${DEFINED_LATER}
|
||||
DEFINED_LATER= later
|
||||
#
|
||||
source-: .PHONY
|
||||
# This section applies.
|
||||
: 'Undefined variables are expanded directly in the dependency'
|
||||
: 'declaration. They are not preserved and maybe expanded later.'
|
||||
: 'This is in contrast to local variables such as $${.TARGET}.'
|
||||
source-later: .PHONY
|
||||
# This section doesn't apply.
|
||||
: 'Undefined variables are tried to be expanded in a dependency'
|
||||
: 'declaration. If that fails because the variable is undefined,'
|
||||
: 'the expression is preserved and tried to be expanded later.'
|
||||
|
||||
all:
|
||||
@:;
|
||||
# Sources that look like keywords but are not known are interpreted as
|
||||
# ordinary sources.
|
||||
target: .UNKNOWN
|
||||
|
||||
.UNKNOWN:
|
||||
: Making ${.TARGET} from ${.ALLSRC:S,^$,nothing,W}.
|
||||
|
@ -1 +1,9 @@
|
||||
exit status 0
|
||||
false fails
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
ERROR_INFO='This information is printed on 'errors'.'
|
||||
Making sub-error as prerequisite.
|
||||
Making .ERROR out of nothing.
|
||||
exit status 1
|
||||
|
@ -1,9 +1,21 @@
|
||||
# $NetBSD: deptgt-error.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: deptgt-error.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .ERROR in dependency declarations, which
|
||||
# collects commands that are run when another target fails.
|
||||
# is made when another target fails.
|
||||
|
||||
# TODO: Implementation
|
||||
all: .PHONY
|
||||
false fails
|
||||
|
||||
all:
|
||||
@:;
|
||||
.ERROR:
|
||||
@echo 'Making ${.TARGET} out of nothing.'
|
||||
|
||||
.ERROR: sub-error
|
||||
sub-error: .PHONY
|
||||
@echo 'Making ${.TARGET} as prerequisite.'
|
||||
|
||||
# Before making the '.ERROR' target, these variable values are printed.
|
||||
MAKE_PRINT_VAR_ON_ERROR= ERROR_INFO
|
||||
|
||||
# Use single quotes to demonstrate that the output is only informational, it
|
||||
# does not use any established escaping mechanism.
|
||||
ERROR_INFO= This information is ${:Uprinted} on 'errors'.
|
||||
|
@ -1 +1,11 @@
|
||||
exit status 0
|
||||
error-failed before
|
||||
*** Error code 1 (continuing)
|
||||
error-ignored before
|
||||
*** Error code 1 (ignored)
|
||||
error-ignored after
|
||||
Making depends-on-ignored from error-ignored.
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,9 +1,31 @@
|
||||
# $NetBSD: deptgt-ignore.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: deptgt-ignore.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .IGNORE in dependency declarations, which
|
||||
# does not stop if a command from this target exits with a non-zero status.
|
||||
#
|
||||
# This test only applies to compatibility mode. In jobs mode such as with
|
||||
# '-j1', all commands for a single target are bundled into a single shell
|
||||
# program, which is a different implementation technique, the .IGNORE applies
|
||||
# there as well.
|
||||
|
||||
# TODO: Implementation
|
||||
.MAKEFLAGS: -d0 # force stdout to be unbuffered
|
||||
|
||||
all:
|
||||
@:;
|
||||
all: depends-on-failed depends-on-ignored
|
||||
.PHONY: all depends-on-failed depends-on-ignored error-failed error-ignored
|
||||
|
||||
error-failed error-ignored:
|
||||
@echo '${.TARGET} before'
|
||||
@false
|
||||
@echo '${.TARGET} after'
|
||||
|
||||
depends-on-failed: error-failed
|
||||
@echo 'Making ${.TARGET} from ${.ALLSRC}.'
|
||||
depends-on-ignored: error-ignored
|
||||
@echo 'Making ${.TARGET} from ${.ALLSRC}.'
|
||||
|
||||
# Even though the command 'false' in the middle fails, the remaining commands
|
||||
# are still run. After that, the target is marked made, so targets depending
|
||||
# on the target with the ignored commands are made.
|
||||
.IGNORE: error-ignored
|
||||
|
||||
#.MAKEFLAGS: -dg2
|
||||
|
@ -1 +1,2 @@
|
||||
exit status 0
|
||||
Ctrl-C
|
||||
exit status 130
|
||||
|
@ -1,10 +1,11 @@
|
||||
# $NetBSD: deptgt-interrupt.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: deptgt-interrupt.mk,v 1.4 2022/01/22 21:50:41 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .INTERRUPT in dependency declarations, which
|
||||
# collects commands to be run when make is interrupted while building another
|
||||
# target.
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
all:
|
||||
@:;
|
||||
@kill -INT ${.MAKE.PID}
|
||||
|
||||
.INTERRUPT:
|
||||
@echo 'Ctrl-C'
|
||||
|
@ -1 +1,2 @@
|
||||
This target real-main is the one that is made.
|
||||
exit status 0
|
||||
|
@ -1,10 +1,29 @@
|
||||
# $NetBSD: deptgt-main.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: deptgt-main.mk,v 1.4 2022/01/23 21:48:59 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .MAIN in dependency declarations, which defines
|
||||
# the main target. This main target is built if no target has been specified
|
||||
# on the command line or via MAKEFLAGS.
|
||||
|
||||
# TODO: Implementation
|
||||
# The first target becomes the main target by default. It can be overridden
|
||||
# though.
|
||||
all: .PHONY
|
||||
@echo 'This target is not made.'
|
||||
|
||||
all:
|
||||
@:;
|
||||
# This target is not the first to be defined, but it lists '.MAIN' as one of
|
||||
# its sources. The word '.MAIN' only has a special meaning when it appears as
|
||||
# a _target_ in a dependency declaration, not as a _source_. It is thus
|
||||
# ignored.
|
||||
depsrc-main: .PHONY .MAIN
|
||||
@echo 'This target is not made either.'
|
||||
|
||||
# This target is the first to be marked with '.MAIN', so it replaces the
|
||||
# previous main target, which was 'all'.
|
||||
.MAIN: real-main
|
||||
real-main: .PHONY
|
||||
@echo 'This target ${.TARGET} is the one that is made.'
|
||||
|
||||
# This target is marked with '.MAIN' but there already is a main target. The
|
||||
# attribute '.MAIN' is thus ignored.
|
||||
.MAIN: too-late
|
||||
too-late: .PHONY
|
||||
@echo 'This target comes too late, there is already a .MAIN target.'
|
||||
|
@ -1 +1,9 @@
|
||||
: Making 1 out of nothing.
|
||||
: Making 2 out of nothing.
|
||||
: Making 3 out of nothing.
|
||||
: Making 4 out of nothing.
|
||||
: Making 5 out of nothing.
|
||||
: Making 6 out of nothing.
|
||||
: Making 7 out of nothing.
|
||||
: Making 8 out of nothing.
|
||||
exit status 0
|
||||
|
@ -1,8 +1,16 @@
|
||||
# $NetBSD: deptgt-notparallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: deptgt-notparallel.mk,v 1.3 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .NOTPARALLEL in dependency declarations.
|
||||
# Tests for the special target .NOTPARALLEL in dependency declarations, which
|
||||
# prevents the job module from doing anything in parallel, by setting the
|
||||
# maximum jobs to 1. This only applies to the current make, it is not
|
||||
# exported to submakes.
|
||||
|
||||
# TODO: Implementation
|
||||
.MAKEFLAGS: -j4
|
||||
|
||||
all:
|
||||
@:;
|
||||
# Set opts.maxJobs back to 1. Without this line, the output would be in
|
||||
# random order, interleaved with separators like '--- 1 ---'.
|
||||
.NOTPARALLEL:
|
||||
|
||||
all: 1 2 3 4 5 6 7 8
|
||||
1 2 3 4 5 6 7 8: .PHONY
|
||||
: Making ${.TARGET} out of nothing.
|
||||
|
@ -1,3 +1,10 @@
|
||||
Parsing line 15: .ORDER: three one
|
||||
ParseDependency(.ORDER: three one)
|
||||
# .ORDER forces 'three' to be made before 'one'
|
||||
# three, unmade, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS, flags none
|
||||
# one, unmade, type OP_DEPENDS|OP_PHONY, flags none
|
||||
Parsing line 16: .MAKEFLAGS: -d0
|
||||
ParseDependency(.MAKEFLAGS: -d0)
|
||||
: 'Making two out of one.'
|
||||
: 'Making three out of two.'
|
||||
: 'Making all out of three.'
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: deptgt-order.mk,v 1.3 2021/06/17 15:25:33 rillig Exp $
|
||||
# $NetBSD: deptgt-order.mk,v 1.4 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .ORDER in dependency declarations.
|
||||
|
||||
@ -11,7 +11,9 @@ three: two
|
||||
|
||||
# This .ORDER creates a circular dependency since 'three' depends on 'one'
|
||||
# but 'one' is supposed to be built after 'three'.
|
||||
.MAKEFLAGS: -dp
|
||||
.ORDER: three one
|
||||
.MAKEFLAGS: -d0
|
||||
|
||||
# XXX: The circular dependency should be detected here.
|
||||
all: three
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "deptgt-path-suffix.mk" line 8: Suffix '.c' not defined (yet)
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,16 @@
|
||||
# $NetBSD: deptgt-path-suffix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: deptgt-path-suffix.mk,v 1.3 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .PATH.suffix in dependency declarations.
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
# expect+1: Suffix '.c' not defined (yet)
|
||||
.PATH.c: ..
|
||||
|
||||
.SUFFIXES: .c
|
||||
|
||||
# Now the suffix is defined, and the path is recorded.
|
||||
.PATH.c: ..
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,14 +1,17 @@
|
||||
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'
|
||||
Parsing line 34: ${:U}: empty-source
|
||||
ParseDependency(: empty-source)
|
||||
ParseReadLine (35): ' : command for empty targets list'
|
||||
ParseReadLine (36): ': empty-source'
|
||||
Parsing line 35: : command for empty targets list
|
||||
Parsing line 36: : empty-source
|
||||
ParseDependency(: empty-source)
|
||||
ParseReadLine (37): ' : command for empty targets list'
|
||||
ParseReadLine (38): '.MAKEFLAGS: -d0'
|
||||
Parsing line 37: : command for empty targets list
|
||||
Parsing line 38: .MAKEFLAGS: -d0
|
||||
ParseDependency(.MAKEFLAGS: -d0)
|
||||
make: "deptgt.mk" line 46: Unknown modifier "Z"
|
||||
make: "deptgt.mk" line 49: warning: Extra target ignored
|
||||
make: "deptgt.mk" line 52: warning: Extra target (ordinary) ignored
|
||||
make: "deptgt.mk" line 55: warning: Special and mundane targets don't mix. Mundane ones ignored
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: deptgt.mk,v 1.11 2021/04/04 10:13:09 rillig Exp $
|
||||
# $NetBSD: deptgt.mk,v 1.12 2021/12/13 23:38:54 rillig Exp $
|
||||
#
|
||||
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
|
||||
# declarations.
|
||||
@ -45,5 +45,14 @@ ${:U}: empty-source
|
||||
# that nobody uses it.
|
||||
$$$$$$$${:U:Z}:
|
||||
|
||||
# expect+1: warning: Extra target ignored
|
||||
.END ordinary:
|
||||
|
||||
# expect+1: warning: Extra target (ordinary) ignored
|
||||
.PATH ordinary:
|
||||
|
||||
# expect+1: Special and mundane targets don't mix. Mundane ones ignored
|
||||
ordinary .PATH:
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "directive-dinclude-error.inc" line 1: Invalid line type
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,9 +1,24 @@
|
||||
# $NetBSD: directive-dinclude.mk,v 1.1 2020/09/13 09:20:23 rillig Exp $
|
||||
# $NetBSD: directive-dinclude.mk,v 1.2 2022/01/23 21:48:59 rillig Exp $
|
||||
#
|
||||
# Tests for the .dinclude directive, which includes another file,
|
||||
# typically named .depend.
|
||||
# silently skipping it if it cannot be opened. This is primarily used for
|
||||
# including '.depend' files, that's where the 'd' comes from.
|
||||
#
|
||||
# The 'silently skipping' only applies to the case where the file cannot be
|
||||
# opened. Parse errors and other errors are handled the same way as in the
|
||||
# other .include directives.
|
||||
|
||||
# TODO: Implementation
|
||||
# No complaint that there is no such file.
|
||||
.dinclude "${.CURDIR}/directive-dinclude-nonexistent.inc"
|
||||
|
||||
all:
|
||||
@:;
|
||||
# No complaint either, even though the operating system error is ENOTDIR, not
|
||||
# ENOENT.
|
||||
.dinclude "${MAKEFILE}/subdir"
|
||||
|
||||
# Errors that are not related to opening the file are still reported.
|
||||
# expect: make: "directive-dinclude-error.inc" line 1: Invalid line type
|
||||
_!= echo 'syntax error' > directive-dinclude-error.inc
|
||||
.dinclude "${.CURDIR}/directive-dinclude-error.inc"
|
||||
_!= rm directive-dinclude-error.inc
|
||||
|
||||
all: .PHONY
|
||||
|
@ -1,8 +1,21 @@
|
||||
# $NetBSD: directive-elifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: directive-elifdef.mk,v 1.3 2022/01/22 16:23:56 rillig Exp $
|
||||
#
|
||||
# Tests for the .elifdef directive.
|
||||
# Tests for the .elifdef directive, which is seldom used. Instead of writing
|
||||
# '.elifdef VAR', the usual form is the more versatile '.elif defined(VAR)'.
|
||||
|
||||
# TODO: Implementation
|
||||
# At this point, VAR is not defined, so the condition evaluates to false.
|
||||
.if 0
|
||||
.elifdef VAR
|
||||
. error
|
||||
.endif
|
||||
|
||||
VAR= # defined
|
||||
|
||||
# At this point, VAR is defined, so the condition evaluates to true.
|
||||
.if 0
|
||||
.elifdef VAR
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,8 +1,23 @@
|
||||
# $NetBSD: directive-elifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: directive-elifndef.mk,v 1.3 2022/01/22 21:50:41 rillig Exp $
|
||||
#
|
||||
# Tests for the .elifndef directive.
|
||||
# Tests for the .elifndef directive, which is an obscure form of writing the
|
||||
# more usual '.elif !defined(VAR)'.
|
||||
|
||||
# TODO: Implementation
|
||||
# At this point, VAR is not yet defined, and due to the 'n' in 'elifndef' the
|
||||
# condition evaluates to true.
|
||||
.if 0
|
||||
.elifndef VAR && VAR || VAR
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
VAR= # defined
|
||||
|
||||
# At this point, VAR is defined, and due to the 'n' in 'elifndef' the
|
||||
# condition evaluates to false.
|
||||
.if 0
|
||||
.elifndef VAR && VAR || VAR
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,8 +1,8 @@
|
||||
ParseReadLine (21): 'UT_VAR= <${REF}>'
|
||||
Parsing line 21: UT_VAR= <${REF}>
|
||||
Global: UT_VAR = <${REF}>
|
||||
ParseReadLine (28): '.export UT_VAR'
|
||||
Parsing line 28: .export UT_VAR
|
||||
Global: .MAKE.EXPORTED = UT_VAR
|
||||
ParseReadLine (32): ': ${UT_VAR:N*}'
|
||||
Parsing line 32: : ${UT_VAR:N*}
|
||||
Var_Parse: ${UT_VAR:N*} (eval-defined)
|
||||
Var_Parse: ${REF}> (eval-defined)
|
||||
Evaluating modifier ${UT_VAR:N...} on value "<>"
|
||||
@ -14,6 +14,7 @@ CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
|
||||
Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" (eval-defined)
|
||||
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
|
||||
Modifier part: "echo "$UT_VAR""
|
||||
Capturing the output of command "echo "$UT_VAR""
|
||||
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
|
||||
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
|
||||
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
|
||||
@ -23,7 +24,7 @@ Var_Parse: ${UT_VAR} (eval)
|
||||
Var_Parse: ${REF}> (eval)
|
||||
Result of ${:!echo "\$UT_VAR"!} is "<>" (eval-defined, defined)
|
||||
lhs = "<>", rhs = "<>", op = !=
|
||||
ParseReadLine (50): ': ${UT_VAR:N*}'
|
||||
Parsing line 50: : ${UT_VAR:N*}
|
||||
Var_Parse: ${UT_VAR:N*} (eval-defined)
|
||||
Var_Parse: ${REF}> (eval-defined)
|
||||
Evaluating modifier ${UT_VAR:N...} on value "<>"
|
||||
@ -31,12 +32,13 @@ Pattern for ':N' is "*"
|
||||
ModifyWords: split "<>" into 1 word
|
||||
Result of ${UT_VAR:N*} is ""
|
||||
ParseDependency(: )
|
||||
ParseReadLine (54): 'REF= defined'
|
||||
Parsing line 54: REF= defined
|
||||
Global: REF = defined
|
||||
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<defined>"
|
||||
Var_Parse: ${:!echo "\$UT_VAR"!} != "<defined>" (eval-defined)
|
||||
Evaluating modifier ${:!...} on value "" (eval-defined, undefined)
|
||||
Modifier part: "echo "$UT_VAR""
|
||||
Capturing the output of command "echo "$UT_VAR""
|
||||
Var_Parse: ${.MAKE.EXPORTED:O:u} (eval)
|
||||
Evaluating modifier ${.MAKE.EXPORTED:O} on value "UT_VAR"
|
||||
Result of ${.MAKE.EXPORTED:O} is "UT_VAR"
|
||||
@ -46,10 +48,10 @@ 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 (62): 'all:'
|
||||
Parsing line 62: all:
|
||||
ParseDependency(all:)
|
||||
Global: .ALLTARGETS = all
|
||||
ParseReadLine (63): '.MAKEFLAGS: -d0'
|
||||
Parsing line 63: .MAKEFLAGS: -d0
|
||||
ParseDependency(.MAKEFLAGS: -d0)
|
||||
Global: .MAKEFLAGS = -r -k -d cpv -d
|
||||
Global: .MAKEFLAGS = -r -k -d cpv -d 0
|
||||
|
@ -27,29 +27,29 @@ make: "directive-for-escape.mk" line 43: value-with-modifier
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info ${:U\${UNDEF\:U\\$\\$}
|
||||
make: "directive-for-escape.mk" line 57: ${UNDEF:U\$
|
||||
make: "directive-for-escape.mk" line 72: ${UNDEF:U\backslash$
|
||||
For: loop body:
|
||||
. info ${:U{{\}\}}
|
||||
make: "directive-for-escape.mk" line 57: {{}}
|
||||
make: "directive-for-escape.mk" line 72: {{}}
|
||||
For: loop body:
|
||||
. info ${:Uend\}}
|
||||
make: "directive-for-escape.mk" line 57: end}
|
||||
make: "directive-for-escape.mk" line 72: end}
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
|
||||
make: "directive-for-escape.mk" line 69: begin<fallback>end
|
||||
make: "directive-for-escape.mk" line 84: begin<fallback>end
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info ${:U\$}
|
||||
make: "directive-for-escape.mk" line 77: $
|
||||
make: "directive-for-escape.mk" line 92: $
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info ${NUMBERS} ${:Ureplaced}
|
||||
make: "directive-for-escape.mk" line 85: one two three replaced
|
||||
make: "directive-for-escape.mk" line 100: one two three replaced
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info ${:Ureplaced}
|
||||
make: "directive-for-escape.mk" line 95: replaced
|
||||
make: "directive-for-escape.mk" line 110: replaced
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info . $$i: ${:Uinner}
|
||||
@ -62,31 +62,85 @@ For: loop body:
|
||||
. info . $${i2}: ${i2}
|
||||
. info . $${i,}: ${i,}
|
||||
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
|
||||
make: "directive-for-escape.mk" line 103: . $i: 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): inner
|
||||
make: "directive-for-escape.mk" line 107: . $(i:M*): inner
|
||||
make: "directive-for-escape.mk" line 108: . ${i${:U}}: outer
|
||||
make: "directive-for-escape.mk" line 109: . ${i\}}: inner}
|
||||
make: "directive-for-escape.mk" line 110: . ${i2}: two
|
||||
make: "directive-for-escape.mk" line 111: . ${i,}: comma
|
||||
make: "directive-for-escape.mk" line 112: . adjacent: innerinnerinnerinner
|
||||
make: "directive-for-escape.mk" line 118: . $i: inner
|
||||
make: "directive-for-escape.mk" line 119: . ${i}: inner
|
||||
make: "directive-for-escape.mk" line 120: . ${i:M*}: inner
|
||||
make: "directive-for-escape.mk" line 121: . $(i): inner
|
||||
make: "directive-for-escape.mk" line 122: . $(i:M*): inner
|
||||
make: "directive-for-escape.mk" line 123: . ${i${:U}}: outer
|
||||
make: "directive-for-escape.mk" line 124: . ${i\}}: inner}
|
||||
make: "directive-for-escape.mk" line 125: . ${i2}: two
|
||||
make: "directive-for-escape.mk" line 126: . ${i,}: comma
|
||||
make: "directive-for-escape.mk" line 127: . adjacent: innerinnerinnerinner
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
. info eight $$$$$$$$ and no cents.
|
||||
. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents.
|
||||
make: "directive-for-escape.mk" line 120: eight $$$$ and no cents.
|
||||
make: "directive-for-escape.mk" line 121: eight dollardollardollardollar and no cents.
|
||||
make: "directive-for-escape.mk" line 130: eight and no cents.
|
||||
make: "directive-for-escape.mk" line 135: eight $$$$ and no cents.
|
||||
make: "directive-for-escape.mk" line 136: eight dollardollardollardollar and no cents.
|
||||
make: "directive-for-escape.mk" line 145: eight and no cents.
|
||||
For: end for 1
|
||||
make: "directive-for-escape.mk" line 137: newline in .for value
|
||||
make: "directive-for-escape.mk" line 137: newline in .for value
|
||||
make: "directive-for-escape.mk" line 152: newline in .for value
|
||||
make: "directive-for-escape.mk" line 152: newline in .for value
|
||||
For: loop body:
|
||||
. info short: ${:U" "}
|
||||
. info long: ${:U" "}
|
||||
make: "directive-for-escape.mk" line 138: short: " "
|
||||
make: "directive-for-escape.mk" line 139: long: " "
|
||||
make: "directive-for-escape.mk" line 153: short: " "
|
||||
make: "directive-for-escape.mk" line 154: long: " "
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
For: end for 1
|
||||
Parse_PushInput: .for loop in directive-for-escape.mk, line 167
|
||||
make: "directive-for-escape.mk" line 167: newline in .for value
|
||||
in .for loop from directive-for-escape.mk:167 with i = "
|
||||
"
|
||||
For: loop body:
|
||||
: ${:U" "}
|
||||
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
|
||||
Parsing line 168: : ${:U" "}
|
||||
ParseDependency(: " ")
|
||||
ParseEOF: returning to file directive-for-escape.mk, line 170
|
||||
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
|
||||
Parsing line 170: .MAKEFLAGS: -d0
|
||||
ParseDependency(.MAKEFLAGS: -d0)
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
# ${:U#}
|
||||
For: loop body:
|
||||
# ${:U\\\\#}
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
# ${:U\$}
|
||||
For: loop body:
|
||||
# ${:U$i}
|
||||
For: loop body:
|
||||
# ${:U$(i)}
|
||||
For: loop body:
|
||||
# ${:U${i}}
|
||||
For: loop body:
|
||||
# ${:U$$}
|
||||
For: loop body:
|
||||
# ${:U$$$$}
|
||||
For: loop body:
|
||||
# ${:U${:U\$\$}}
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
# ${:U${.TARGET}}
|
||||
For: loop body:
|
||||
# ${:U${.TARGET}}
|
||||
For: loop body:
|
||||
# ${:U$${.TARGET\}}
|
||||
For: loop body:
|
||||
# ${:U$${.TARGET\}}
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
# ${:U(((}
|
||||
For: loop body:
|
||||
# ${:U{{{}
|
||||
For: loop body:
|
||||
# ${:U)))}
|
||||
For: loop body:
|
||||
# ${:U\}\}\}}
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: directive-for-escape.mk,v 1.12 2021/12/05 11:40:03 rillig Exp $
|
||||
# $NetBSD: directive-for-escape.mk,v 1.15 2022/01/27 20:15:14 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
|
||||
@ -50,7 +50,22 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
|
||||
# being that each '$' is written as '$$'.
|
||||
#
|
||||
# The .for loop splits ${VALUES} into 3 words, at the space characters, since
|
||||
# these are not escaped.
|
||||
# the '$$' is an ordinary character and the spaces are not escaped.
|
||||
# Word 1 is '${UNDEF:U\$\$'
|
||||
# Word 2 is '{{}}'
|
||||
# Word 3 is 'end}'
|
||||
# The first iteration expands the body of the .for loop to:
|
||||
# expect: . info ${:U\${UNDEF\:U\\$\\$}
|
||||
# The modifier ':U' unescapes the '\$' to a simple '$'.
|
||||
# The modifier ':U' unescapes the '\:' to a simple ':'.
|
||||
# The modifier ':U' unescapes the '\\' to a simple '\'.
|
||||
# The modifier ':U' resolves the expression '$\' to the word 'backslash', due
|
||||
# to the following variable definition.
|
||||
${:U\\}= backslash
|
||||
# FIXME: There was no expression '$\' in the original text of the previous
|
||||
# line, that's a surprise in the parser.
|
||||
# The modifier ':U' unescapes the '\$' to a simple '$'.
|
||||
# expect+4: ${UNDEF:U\backslash$
|
||||
VALUES= $${UNDEF:U\$$\$$ {{}} end}
|
||||
# XXX: Where in the code does the '\$\$' get converted into a single '\$'?
|
||||
.for i in ${VALUES}
|
||||
@ -139,4 +154,47 @@ ${closing-brace}= <closing-brace> # alternative interpretation
|
||||
. info long: ${i}
|
||||
.endfor
|
||||
|
||||
# No error since the newline character is not actually used.
|
||||
.for i in "${.newline}"
|
||||
.endfor
|
||||
|
||||
# Between for.c 1.161 from 2022-01-08 and before for.c 1.163 from 2022-01-09,
|
||||
# a newline character in a .for loop led to a crash since at the point where
|
||||
# the error message including the stack trace is printed, the body of the .for
|
||||
# loop is assembled, and at that point, ForLoop.nextItem had already been
|
||||
# advanced.
|
||||
.MAKEFLAGS: -dp
|
||||
.for i in "${.newline}"
|
||||
: $i
|
||||
.endfor
|
||||
.MAKEFLAGS: -d0
|
||||
|
||||
.MAKEFLAGS: -df
|
||||
.for i in \# \\\#
|
||||
# $i
|
||||
.endfor
|
||||
|
||||
.for i in $$ $$i $$(i) $${i} $$$$ $$$$$$$$ $${:U\$$\$$}
|
||||
# $i
|
||||
.endfor
|
||||
|
||||
# The expression '${.TARGET}' must be preserved as it is one of the 7 built-in
|
||||
# target-local variables. See for.c 1.45 from 2009-01-14.
|
||||
.for i in ${.TARGET} $${.TARGET} $$${.TARGET} $$$${.TARGET}
|
||||
# $i
|
||||
.endfor
|
||||
# expect: # ${:U${.TARGET}}
|
||||
# XXX: Why does '$' result in the same text as '$$'?
|
||||
# expect: # ${:U${.TARGET}}
|
||||
# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'?
|
||||
# expect: # ${:U$${.TARGET\}}
|
||||
# XXX: Why does '$' result in the same text as '$$'?
|
||||
# XXX: Why does the '$$' before the '${.TARGET}' lead to an escaped '}'?
|
||||
# expect: # ${:U$${.TARGET\}}
|
||||
|
||||
.for i in ((( {{{ ))) }}}
|
||||
# $i
|
||||
.endfor
|
||||
.MAKEFLAGS: -d0
|
||||
|
||||
all:
|
||||
|
@ -1,7 +1,7 @@
|
||||
make: "directive-for-generating-endif.mk" line 21: if-less endif
|
||||
make: "directive-for-generating-endif.mk" line 21: if-less endif
|
||||
make: "directive-for-generating-endif.mk" line 21: if-less endif
|
||||
make: "directive-for-generating-endif.mk" line 0: 3 open conditionals
|
||||
make: "directive-for-generating-endif.mk" line 26: 3 open conditionals
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -19,6 +19,24 @@ make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
|
||||
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: "directive-for.mk" line 160: no iteration variables in for
|
||||
make: "directive-for.mk" line 162: Missing argument for ".error"
|
||||
make: "directive-for.mk" line 163: for-less endfor
|
||||
make: "directive-for.mk" line 187: 1 open conditional
|
||||
make: "directive-for.mk" line 203: for-less endfor
|
||||
make: "directive-for.mk" line 204: if-less endif
|
||||
make: "directive-for.mk" line 212: if-less endif
|
||||
For: end for 1
|
||||
For: loop body:
|
||||
.\
|
||||
for inner in i
|
||||
.\
|
||||
endfor
|
||||
make: "directive-for.mk" line 229: Unexpected end of file in .for loop
|
||||
For: loop body:
|
||||
.\
|
||||
endfor
|
||||
make: "directive-for.mk" line 227: for-less endfor
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: directive-for.mk,v 1.10 2020/12/27 09:58:35 rillig Exp $
|
||||
# $NetBSD: directive-for.mk,v 1.13 2022/01/15 12:35:18 rillig Exp $
|
||||
#
|
||||
# Tests for the .for directive.
|
||||
#
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
# Using the .for loop, lists of values can be produced.
|
||||
# In simple cases, the :@var@${var}@ variable modifier can be used to
|
||||
# reach the same effects.
|
||||
# achieve the same effects.
|
||||
#
|
||||
.undef NUMBERS
|
||||
.for num in 1 2 3
|
||||
@ -135,7 +135,7 @@ EXPANSION${plus}= value
|
||||
|
||||
# Ensure that braces and parentheses are properly escaped by the .for loop.
|
||||
# Each line must print the same word 3 times.
|
||||
# See GetEscapes.
|
||||
# See ForLoop_SubstBody.
|
||||
.for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{
|
||||
. info $v ${v} $(v)
|
||||
.endfor
|
||||
@ -155,5 +155,76 @@ var= outer
|
||||
. info XXX: Not reached ${var}
|
||||
.endfor
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
||||
# An empty list of variables to the left of the 'in' is a parse error.
|
||||
.for in value # expect+0: no iteration variables in for
|
||||
# XXX: The loop body is evaluated once, even with the parse error above.
|
||||
. error # expect+0: Missing argument for ".error"
|
||||
.endfor # expect+0: for-less endfor
|
||||
|
||||
# An empty list of iteration values to the right of the 'in' is accepted.
|
||||
# Unlike in the shell, it is not a parse error.
|
||||
.for var in
|
||||
. error
|
||||
.endfor
|
||||
|
||||
# If the iteration values become empty after expanding the expressions, the
|
||||
# body of the loop is not evaluated. It is not a parse error.
|
||||
.for var in ${:U}
|
||||
. error
|
||||
.endfor
|
||||
|
||||
|
||||
# The loop body can be empty.
|
||||
.for var in 1 2 3
|
||||
.endfor
|
||||
|
||||
|
||||
# A mismatched .if inside a .for loop is detected each time when the loop body
|
||||
# is processed.
|
||||
.for var in value
|
||||
. if 0
|
||||
.endfor # expect+0: 1 open conditional
|
||||
|
||||
# If there are no iteration values, the loop body is not processed, and the
|
||||
# check for mismatched conditionals is not performed.
|
||||
.for var in ${:U}
|
||||
. if 0
|
||||
.endfor
|
||||
|
||||
|
||||
# When a .for without the corresponding .endfor occurs in an inactive branch
|
||||
# of an .if, the .for directive is just skipped, it does not even need a
|
||||
# corresponding .endfor. In other words, the behavior of the parser depends
|
||||
# on the actual values of the conditions in the .if clauses.
|
||||
.if 0
|
||||
. for var in value # does not need a corresponding .endfor
|
||||
.endif
|
||||
.endfor # expect+0: for-less endfor
|
||||
.endif # expect+0: if-less endif
|
||||
|
||||
|
||||
# When a .for without the corresponding .endfor occurs in an active branch of
|
||||
# an .if, the parser just counts the number of .for and .endfor directives,
|
||||
# without looking at any other directives.
|
||||
.if 1
|
||||
. for var in value
|
||||
. endif # expect+0: if-less endif
|
||||
. endfor # no 'for-less endfor'
|
||||
.endif # no 'if-less endif'
|
||||
|
||||
|
||||
# When make parses a .for loop, it assumes that there is no line break between
|
||||
# the '.' and the 'for' or 'endfor', as there is no practical reason to break
|
||||
# the line at this point. When make scans the outer .for loop, it does not
|
||||
# recognize the inner directives as such. When make scans the inner .for
|
||||
# loop, it recognizes the '.\n for' but does not recognize the '.\n endfor',
|
||||
# as LK_FOR_BODY preserves the backslash-newline sequences.
|
||||
.MAKEFLAGS: -df
|
||||
.for outer in o
|
||||
.\
|
||||
for inner in i
|
||||
.\
|
||||
endfor
|
||||
.endfor
|
||||
.MAKEFLAGS: -d0
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "directive-hyphen-include-error.inc" line 1: Invalid line type
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,9 +1,23 @@
|
||||
# $NetBSD: directive-hyphen-include.mk,v 1.1 2020/09/13 09:20:23 rillig Exp $
|
||||
# $NetBSD: directive-hyphen-include.mk,v 1.2 2022/01/23 21:48:59 rillig Exp $
|
||||
#
|
||||
# Tests for the .-include directive, which includes another file,
|
||||
# silently skipping it if it cannot be opened.
|
||||
#
|
||||
# The 'silently skipping' only applies to the case where the file cannot be
|
||||
# opened. Parse errors and other errors are handled the same way as in the
|
||||
# other .include directives.
|
||||
|
||||
# TODO: Implementation
|
||||
# No complaint that there is no such file.
|
||||
.-include "${.CURDIR}/directive-hyphen-include-nonexistent.inc"
|
||||
|
||||
all:
|
||||
@:;
|
||||
# No complaint either, even though the operating system error is ENOTDIR, not
|
||||
# ENOENT.
|
||||
.-include "${MAKEFILE}/subdir"
|
||||
|
||||
# Errors that are not related to opening the file are still reported.
|
||||
# expect: make: "directive-hyphen-include-error.inc" line 1: Invalid line type
|
||||
_!= echo 'syntax error' > directive-hyphen-include-error.inc
|
||||
.-include "${.CURDIR}/directive-hyphen-include-error.inc"
|
||||
_!= rm directive-hyphen-include-error.inc
|
||||
|
||||
all: .PHONY
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user