Merge bmake-20230510

Merge commit '945078deae448e0a13c34b3393d836087719fb16'
This commit is contained in:
Simon J. Gerraty 2023-05-13 10:05:48 -07:00
commit c1d01b5fd6
47 changed files with 1545 additions and 557 deletions

View file

@ -1,3 +1,27 @@
2023-05-10 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230510
Merge with NetBSD make, pick up
o parse.c: don't print null filename in stack traces
o var.c: :mtime operate on each word in variable value
2023-05-09 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230509
Merge with NetBSD make, pick up
o for.c: skip syntactically wrong .for loops
o var.c: allow for :gmtime=${mtime}
add :mtime[=timestamp] where timestamp is used if stat(2)
fails, if :mtime=error stat(2) failure causes error.
2023-05-05 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230504
Merge with NetBSD make, pick up
o compat.c: fix compile on NetBSD 7.2
o make.1: fix documentation of .PREFIX to match reality and POSIX
o unit-tests: improved var-scope-local
2023-04-14 Simon J Gerraty <sjg@beast.crufty.net> 2023-04-14 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230414 * VERSION (_MAKE_VERSION): 20230414

View file

@ -392,8 +392,6 @@ unit-tests/export-variants.exp
unit-tests/export-variants.mk unit-tests/export-variants.mk
unit-tests/export.exp unit-tests/export.exp
unit-tests/export.mk unit-tests/export.mk
unit-tests/forloop.exp
unit-tests/forloop.mk
unit-tests/forsubst.exp unit-tests/forsubst.exp
unit-tests/forsubst.mk unit-tests/forsubst.mk
unit-tests/gnode-submake.exp unit-tests/gnode-submake.exp
@ -713,6 +711,8 @@ unit-tests/varmod-match-escape.exp
unit-tests/varmod-match-escape.mk unit-tests/varmod-match-escape.mk
unit-tests/varmod-match.exp unit-tests/varmod-match.exp
unit-tests/varmod-match.mk unit-tests/varmod-match.mk
unit-tests/varmod-mtime.exp
unit-tests/varmod-mtime.mk
unit-tests/varmod-no-match.exp unit-tests/varmod-no-match.exp
unit-tests/varmod-no-match.mk unit-tests/varmod-no-match.mk
unit-tests/varmod-order-numeric.exp unit-tests/varmod-order-numeric.exp

View file

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

View file

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.361 2023/03/23 03:29:28 sjg Exp $ .\" $NetBSD: make.1,v 1.366 2023/05/10 18:22:33 sjg Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\" .\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\" .\"
.Dd March 22, 2023 .Dd May 10, 2023
.Dt BMAKE 1 .Dt BMAKE 1
.Os .Os
.Sh NAME .Sh NAME
@ -796,12 +796,10 @@ The list of sources for this target that were deemed out-of-date; also
known as known as
.Sq Va \&? . .Sq Va \&? .
.It Va .PREFIX .It Va .PREFIX
The file prefix of the target, containing only the file portion, no suffix The name of the target with suffix (if declared in
or preceding directory components; also known as .Ic .SUFFIXES )
removed; also known as
.Sq Va * . .Sq Va * .
The suffix must be one of the known suffixes declared with
.Ic .SUFFIXES ,
or it is not recognized.
.It Va .TARGET .It Va .TARGET
The name of the target; also known as The name of the target; also known as
.Sq Va @ . .Sq Va @ .
@ -1513,6 +1511,25 @@ producing the formatted timestamp.
If a If a
.Ar timestamp .Ar timestamp
value is not provided or is 0, the current time is used. value is not provided or is 0, the current time is used.
.It Cm \&:mtime Ns Oo Cm = Ns Ar timestamp Oc
Call
.Xr stat 2
with each word as pathname;
use
.Ql st_mtime
as the new value.
If
.Xr stat 2
fails; use
.Ar timestamp
or current time.
If
.Ar timestamp
is set to
.Ql error ,
then
.Xr stat 2
failure will cause an error.
.It Cm \&:tA .It Cm \&:tA
Attempts to convert the value to an absolute path using Attempts to convert the value to an absolute path using
.Xr realpath 3 . .Xr realpath 3 .
@ -2735,5 +2752,3 @@ using that token pool to abort the build and exit with error code 6.
Sometimes the attempt to suppress a cascade of unnecessary errors, Sometimes the attempt to suppress a cascade of unnecessary errors,
can result in a seemingly unexplained can result in a seemingly unexplained
.Ql *** Error code 6 .Ql *** Error code 6

View file

@ -515,11 +515,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.OODATE The list of sources for this target that were deemed out- .OODATE The list of sources for this target that were deemed out-
of-date; also known as `?'. of-date; also known as `?'.
.PREFIX The file prefix of the target, containing only the file .PREFIX The name of the target with suffix (if declared in
portion, no suffix or preceding directory components; .SUFFIXES) removed; also known as `*'.
also known as `*'. The suffix must be one of the known
suffixes declared with .SUFFIXES, or it is not recog-
nized.
.TARGET The name of the target; also known as `@'. For compati- .TARGET The name of the target; also known as `@'. For compati-
bility with other makes this is an alias for .ARCHIVE in bility with other makes this is an alias for .ARCHIVE in
@ -986,6 +983,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
localtime(3), producing the formatted timestamp. If a timestamp localtime(3), producing the formatted timestamp. If a timestamp
value is not provided or is 0, the current time is used. value is not provided or is 0, the current time is used.
:mtime[=timestamp]
Call stat(2) with each word as pathname; use `st_mtime' as the new
value. If stat(2) fails; use timestamp or current time. If
timestamp is set to `error', then stat(2) failure will cause an er-
ror.
:tA Attempts to convert the value to an absolute path using realpath(3). :tA Attempts to convert the value to an absolute path using realpath(3).
If that fails, the value is unchanged. If that fails, the value is unchanged.
@ -1750,6 +1753,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
attempt to suppress a cascade of unnecessary errors, can result in a attempt to suppress a cascade of unnecessary errors, can result in a
seemingly unexplained `*** Error code 6' seemingly unexplained `*** Error code 6'
FreeBSD 13.0 May 10, 2023 FreeBSD 13.0
FreeBSD 13.0 March 22, 2023 FreeBSD 13.0

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View file

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.246 2023/03/18 22:20:11 sjg Exp $ */ /* $NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -94,7 +94,7 @@
#include "pathnames.h" #include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.246 2023/03/18 22:20:11 sjg Exp $"); MAKE_RCSID("$NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $");
static GNode *curTarg = NULL; static GNode *curTarg = NULL;
static pid_t compatChild; static pid_t compatChild;
@ -224,7 +224,7 @@ bool
Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
{ {
char *cmdStart; /* Start of expanded command */ char *cmdStart; /* Start of expanded command */
char *bp; char *volatile bp;
bool silent; /* Don't print command */ bool silent; /* Don't print command */
bool doIt; /* Execute even if -n */ bool doIt; /* Execute even if -n */
volatile bool errCheck; /* Check errors */ volatile bool errCheck; /* Check errors */

View file

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.171 2023/02/14 21:38:31 rillig Exp $ */ /* $NetBSD: for.c,v 1.174 2023/05/09 19:43:12 rillig Exp $ */
/* /*
* Copyright (c) 1992, The Regents of the University of California. * Copyright (c) 1992, The Regents of the University of California.
@ -58,7 +58,7 @@
#include "make.h" #include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.171 2023/02/14 21:38:31 rillig Exp $"); MAKE_RCSID("$NetBSD: for.c,v 1.174 2023/05/09 19:43:12 rillig Exp $");
typedef struct ForLoop { typedef struct ForLoop {
@ -72,6 +72,22 @@ typedef struct ForLoop {
static ForLoop *accumFor; /* Loop being accumulated */ static ForLoop *accumFor; /* Loop being accumulated */
/* See LK_FOR_BODY. */
static void
skip_whitespace_or_line_continuation(const char **pp)
{
const char *p = *pp;
for (;;) {
if (ch_isspace(*p))
p++;
else if (p[0] == '\\' && p[1] == '\n')
p += 2;
else
break;
}
*pp = p;
}
static ForLoop * static ForLoop *
ForLoop_New(void) ForLoop_New(void)
{ {
@ -123,6 +139,13 @@ ForLoop_Details(ForLoop *f)
} }
static bool static bool
IsValidInVarname(char c)
{
return c != '$' && c != ':' && c != '\\' &&
c != '(' && c != '{' && c != ')' && c != '}';
}
static void
ForLoop_ParseVarnames(ForLoop *f, const char **pp) ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{ {
const char *p = *pp; const char *p = *pp;
@ -133,15 +156,20 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
cpp_skip_whitespace(&p); cpp_skip_whitespace(&p);
if (*p == '\0') { if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for"); Parse_Error(PARSE_FATAL, "missing `in' in for");
return false; f->vars.len = 0;
return;
} }
/* for (len = 0; p[len] != '\0' && !ch_isspace(p[len]); len++) {
* XXX: This allows arbitrary variable names; if (!IsValidInVarname(p[len])) {
* see directive-for.mk. Parse_Error(PARSE_FATAL,
*/ "invalid character '%c' "
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) "in .for loop variable name",
continue; p[len]);
f->vars.len = 0;
return;
}
}
if (len == 2 && p[0] == 'i' && p[1] == 'n') { if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2; p += 2;
@ -154,11 +182,10 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
if (f->vars.len == 0) { if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for"); Parse_Error(PARSE_FATAL, "no iteration variables in for");
return false; return;
} }
*pp = p; *pp = p;
return true;
} }
static bool static bool
@ -221,17 +248,14 @@ For_Eval(const char *line)
ForLoop *f; ForLoop *f;
p = line + 1; /* skip the '.' */ p = line + 1; /* skip the '.' */
cpp_skip_whitespace(&p); skip_whitespace_or_line_continuation(&p);
if (IsFor(p)) { if (IsFor(p)) {
p += 3; p += 3;
f = ForLoop_New(); f = ForLoop_New();
if (!ForLoop_ParseVarnames(f, &p)) { ForLoop_ParseVarnames(f, &p);
ForLoop_Free(f); if (f->vars.len > 0 && !ForLoop_ParseItems(f, p))
return -1;
}
if (!ForLoop_ParseItems(f, p))
f->items.len = 0; /* don't iterate */ f->items.len = 0; /* don't iterate */
accumFor = f; accumFor = f;
@ -254,7 +278,7 @@ For_Accum(const char *line, int *forLevel)
if (*p == '.') { if (*p == '.') {
p++; p++;
cpp_skip_whitespace(&p); skip_whitespace_or_line_continuation(&p);
if (IsEndfor(p)) { if (IsEndfor(p)) {
DEBUG1(FOR, "For: end for %d\n", *forLevel); DEBUG1(FOR, "For: end for %d\n", *forLevel);

View file

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.361 2023/03/23 03:29:28 sjg Exp $ .\" $NetBSD: make.1,v 1.366 2023/05/10 18:22:33 sjg Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\" .\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\" .\"
.Dd March 22, 2023 .Dd May 10, 2023
.Dt MAKE 1 .Dt MAKE 1
.Os .Os
.Sh NAME .Sh NAME
@ -796,12 +796,10 @@ The list of sources for this target that were deemed out-of-date; also
known as known as
.Sq Va \&? . .Sq Va \&? .
.It Va .PREFIX .It Va .PREFIX
The file prefix of the target, containing only the file portion, no suffix The name of the target with suffix (if declared in
or preceding directory components; also known as .Ic .SUFFIXES )
removed; also known as
.Sq Va * . .Sq Va * .
The suffix must be one of the known suffixes declared with
.Ic .SUFFIXES ,
or it is not recognized.
.It Va .TARGET .It Va .TARGET
The name of the target; also known as The name of the target; also known as
.Sq Va @ . .Sq Va @ .
@ -1524,6 +1522,25 @@ producing the formatted timestamp.
If a If a
.Ar timestamp .Ar timestamp
value is not provided or is 0, the current time is used. value is not provided or is 0, the current time is used.
.It Cm \&:mtime Ns Oo Cm = Ns Ar timestamp Oc
Call
.Xr stat 2
with each word as pathname;
use
.Ql st_mtime
as the new value.
If
.Xr stat 2
fails; use
.Ar timestamp
or current time.
If
.Ar timestamp
is set to
.Ql error ,
then
.Xr stat 2
failure will cause an error.
.It Cm \&:tA .It Cm \&:tA
Attempts to convert the value to an absolute path using Attempts to convert the value to an absolute path using
.Xr realpath 3 . .Xr realpath 3 .
@ -2742,5 +2759,3 @@ using that token pool to abort the build and exit with error code 6.
Sometimes the attempt to suppress a cascade of unnecessary errors, Sometimes the attempt to suppress a cascade of unnecessary errors,
can result in a seemingly unexplained can result in a seemingly unexplained
.Ql *** Error code 6 .Ql *** Error code 6

View file

@ -1,3 +1,38 @@
2023-05-10 Simon J Gerraty <sjg@beast.crufty.net>
* meta.autodep.mk: if LOCAL_DEPENDS_GUARD is "no"
suppress processing of .depend
2023-05-09 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps.mk: do not add _CURDIR to DIRDEPS for SRCTOP
* meta.sys.mk sys.dirdeps.mk:
originally DIRDEPS_BUILD and META_MODE were the same thing,
but META_MODE is useful by itself.
Move things from meta.sys.mk which actually pertain to
DIRDEPS_BUILD to sys.dirdeps.mk
2023-05-04 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20230504 May the Forth be with you
* dirdeps.mk: as with meta.sys.mk we treat "host" as special.
DEP_TARGET_SPEC is just ${DEP_MACHINE}
* meta.sys.mk: ensure DEP_* for TARGET_SPEC_VARS are set at
level > 0 since these are often refered to in Makefile.depend*
2023-04-26 Simon J Gerraty <sjg@beast.crufty.net>
* jobs.mk: report ${.TARGET} ${JOB_ARGS} ${JOB_LOG} and
anything in ${JOB_LOG_START}
* jobs.mk: look for newlog.sh in ${.SYSPATH:U${.PARSEDIR}}
or a scripts subdir before searching $PATH.
* FILES: include newlog.sh for jobs.mk
2023-04-20 Simon J Gerraty <sjg@beast.crufty.net> 2023-04-20 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20230420 * install-mk (MK_VERSION): 20230420

View file

@ -30,6 +30,7 @@ man.mk
manifest.mk manifest.mk
mk-files.txt mk-files.txt
mkopt.sh mkopt.sh
newlog.sh
nls.mk nls.mk
obj.mk obj.mk
options.mk options.mk
@ -47,6 +48,7 @@ sys.mk
sys.clean-env.mk sys.clean-env.mk
sys.debug.mk sys.debug.mk
sys.dependfile.mk sys.dependfile.mk
sys.dirdeps.mk
sys.vars.mk sys.vars.mk
sys/AIX.mk sys/AIX.mk
sys/Darwin.mk sys/Darwin.mk

View file

@ -1,5 +1,5 @@
# RCSid: # RCSid:
# $Id: dirdeps-targets.mk,v 1.24 2020/12/11 18:15:43 sjg Exp $ # $Id: dirdeps-targets.mk,v 1.25 2023/05/11 05:07:28 sjg Exp $
# #
# @(#) Copyright (c) 2019-2020 Simon J. Gerraty # @(#) Copyright (c) 2019-2020 Simon J. Gerraty
# #
@ -113,16 +113,17 @@ tqtdeps := ${DIRDEPS_TARGETS_MACHINE_LIST:@m@${tdeps:M*.$m,*}@:S,/${.MAKE.DEPEND
.endif .endif
# now work out what we want in DIRDEPS # now work out what we want in DIRDEPS
DIRDEPS = ${ptdeps}
.if empty(REQUESTED_MACHINE) .if empty(REQUESTED_MACHINE)
# we want them all just as found # we want them all just as found
DIRDEPS = ${ptdeps} ${mqtdeps} ${tqtdeps} DIRDEPS += ${mqtdeps} ${tqtdeps}
.else .else
# we only want those that match REQUESTED_MACHINE/REQUESTED_TARGET_SPEC # we only want those that match REQUESTED_MACHINE/REQUESTED_TARGET_SPEC
# or REQUESTED_TARGET_SPEC (TARGET_SPEC) # or REQUESTED_TARGET_SPEC (TARGET_SPEC)
DIRDEPS = \ DIRDEPS += \
${ptdeps:@d@$d.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC:U${REQUESTED_MACHINE}}}@} \
${mqtdeps:M*.${REQUESTED_MACHINE}} \ ${mqtdeps:M*.${REQUESTED_MACHINE}} \
${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}} ${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}} \
.endif .endif
# clean up # clean up
DIRDEPS := ${DIRDEPS:O:u} DIRDEPS := ${DIRDEPS:O:u}

View file

@ -1,4 +1,4 @@
# $Id: dirdeps.mk,v 1.157 2023/04/22 21:07:51 sjg Exp $ # $Id: dirdeps.mk,v 1.160 2023/05/10 20:44:58 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause # SPDX-License-Identifier: BSD-2-Clause
# #
@ -273,6 +273,10 @@ _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*}
.endif .endif
.endif .endif
# turn a list into a set of :N modifiers
# NskipFoo = ${Foo:${M_ListToSkip}}
M_ListToSkip ?= O:u:S,^,N,:ts:
# this is how we identify non-machine specific dependfiles # this is how we identify non-machine specific dependfiles
N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}} N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
@ -333,6 +337,14 @@ DEP_${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]}
DEP_MACHINE := ${_DEP_TARGET_SPEC} DEP_MACHINE := ${_DEP_TARGET_SPEC}
.endif .endif
# host is special
.if ${DEP_MACHINE:Mhost*} != ""
DEP_TARGET_SPEC = ${DEP_MACHINE}
.for v in ${TARGET_SPEC_VARS:O:u:NMACHINE}
.undef DEP_$v
.endfor
.endif
# reset each time through # reset each time through
_build_all_dirs = _build_all_dirs =
_build_xtra_dirs = _build_xtra_dirs =
@ -653,7 +665,7 @@ _machines := ${_machines:${M_dep_qual_fixes:ts:}:O:u}
# reset each time through # reset each time through
_build_dirs = _build_dirs =
.if ${DEP_RELDIR} == ${_DEP_RELDIR} .if ${DEP_RELDIR} == ${_DEP_RELDIR} && ${_CURDIR} != ${SRCTOP}
# pickup other machines for this dir if necessary # pickup other machines for this dir if necessary
_build_dirs += ${_machines:@m@${_CURDIR}.$m@} _build_dirs += ${_machines:@m@${_CURDIR}.$m@}
.endif .endif

View file

@ -59,7 +59,7 @@
# Simon J. Gerraty <sjg@crufty.net> # Simon J. Gerraty <sjg@crufty.net>
# RCSid: # RCSid:
# $Id: install-mk,v 1.231 2023/04/20 17:45:03 sjg Exp $ # $Id: install-mk,v 1.234 2023/05/13 15:52:24 sjg Exp $
# #
# @(#) Copyright (c) 1994-2023 Simon J. Gerraty # @(#) Copyright (c) 1994-2023 Simon J. Gerraty
# #
@ -74,7 +74,7 @@
# sjg@crufty.net # sjg@crufty.net
# #
MK_VERSION=20230420 MK_VERSION=20230510
OWNER= OWNER=
GROUP= GROUP=
MODE=444 MODE=444

View file

@ -1,4 +1,4 @@
# $Id: jobs.mk,v 1.7 2023/04/18 23:32:28 sjg Exp $ # $Id: jobs.mk,v 1.9 2023/04/27 18:10:27 sjg Exp $
# #
# @(#) Copyright (c) 2012-2023, Simon J. Gerraty # @(#) Copyright (c) 2012-2023, Simon J. Gerraty
# #
@ -38,21 +38,30 @@ now_utc ?= ${%s:L:gmtime}
start_utc := ${now_utc} start_utc := ${now_utc}
.endif .endif
.info ${.newline}${TIME_STAMP} Start ${.TARGETS}
.if make(*-jobs) .if make(*-jobs)
.info ${.newline}${TIME_STAMP} Start ${.TARGETS}
JOB_LOGDIR ?= ${SRCTOP:H} JOB_LOGDIR ?= ${SRCTOP:H}
JOB_LOG = ${JOB_LOGDIR}/${.TARGET:S,-jobs,,:S,/,_,g}.log JOB_LOG = ${JOB_LOGDIR}/${.TARGET:S,-jobs,,:S,/,_,g}.log
JOB_LOG_GENS ?= 4 JOB_LOG_GENS ?= 4
# we like to rotate logs # we like to rotate logs
.if empty(NEWLOG_SH) .if empty(NEWLOG_SH)
.for d in ${.SYSPATH:U${.PARSEDIR}:@x@$x $x/scripts@}
.if exists($d/newlog.sh)
NEWLOG_SH := $d/newlog.sh
.if ${MAKE_VERSION} > 20220924
.break
.endif
.endif
.endfor
.if empty(NEWLOG_SH)
.ifdef M_whence .ifdef M_whence
NEWLOG_SH := ${newlog.sh:L:${M_whence}} NEWLOG_SH := ${newlog.sh:L:${M_whence}}
.else .else
NEWLOG_SH := ${(type newlog.sh) 2> /dev/null:L:sh:M/*} NEWLOG_SH := ${(type newlog.sh) 2> /dev/null:L:sh:M/*}
.endif .endif
.endif .endif
.endif
.if !empty(NEWLOG_SH) && exists(${NEWLOG_SH}) .if !empty(NEWLOG_SH) && exists(${NEWLOG_SH})
NEWLOG := sh ${NEWLOG_SH} NEWLOG := sh ${NEWLOG_SH}
JOB_NEWLOG_ARGS ?= -S -n ${JOB_LOG_GENS} JOB_NEWLOG_ARGS ?= -S -n ${JOB_LOG_GENS}
@ -72,7 +81,7 @@ JOB_ARGS+= -j${JOB_MAX}
# build orchestration works as expected (DIRDEPS_BUILD) # build orchestration works as expected (DIRDEPS_BUILD)
${.TARGETS:M*-jobs}: ${.TARGETS:M*-jobs}:
@${NEWLOG} ${JOB_NEWLOG_ARGS} ${JOB_LOG} @${NEWLOG} ${JOB_NEWLOG_ARGS} ${JOB_LOG}
@echo Logging to ${JOB_LOG} @echo "${TIME_STAMP} Start ${.TARGET:S,-jobs,,} ${JOB_ARGS} ${JOB_LOG_START} log=${JOB_LOG}" | tee ${JOB_LOG}
@cd ${.CURDIR} && env MAKELEVEL=0 \ @cd ${.CURDIR} && env MAKELEVEL=0 \
${.MAKE} ${JOB_ARGS} _TARGETS=${.TARGET:S,-jobs,,} ${.TARGET:S,-jobs,,} >> ${JOB_LOG} 2>&1 ${.MAKE} ${JOB_ARGS} _TARGETS=${.TARGET:S,-jobs,,} ${.TARGET:S,-jobs,,} >> ${JOB_LOG} 2>&1

View file

@ -1,4 +1,4 @@
# $Id: meta.autodep.mk,v 1.56 2022/09/09 17:44:29 sjg Exp $ # $Id: meta.autodep.mk,v 1.57 2023/05/13 15:52:24 sjg Exp $
# #
# @(#) Copyright (c) 2010, Simon J. Gerraty # @(#) Copyright (c) 2010, Simon J. Gerraty
@ -139,6 +139,10 @@ FORCE_DPADD += ${_nonlibs:@x@${DPADD:M*/$x}@}
.END: gendirdeps .END: gendirdeps
.endif .endif
.if ${LOCAL_DEPENDS_GUARD:U} == "no"
.depend:
.endif
# if we don't have OBJS, then .depend isn't useful # if we don't have OBJS, then .depend isn't useful
.if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "") .if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "")
# some makefiles and/or targets contain # some makefiles and/or targets contain

View file

@ -1,7 +1,7 @@
# $Id: meta.sys.mk,v 1.46 2023/04/18 18:43:00 sjg Exp $ # $Id: meta.sys.mk,v 1.51 2023/05/11 20:05:32 sjg Exp $
# #
# @(#) Copyright (c) 2010-2021, Simon J. Gerraty # @(#) Copyright (c) 2010-2023, Simon J. Gerraty
# #
# This file is provided in the hope that it will # This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY. # be of use. There is absolutely NO WARRANTY.
@ -17,56 +17,21 @@
# include this if you want to enable meta mode # include this if you want to enable meta mode
# for maximum benefit, requires filemon(4) driver. # for maximum benefit, requires filemon(4) driver.
.if ${MAKE_VERSION:U0} > 20100901 # absolute path to what we are reading.
.if !target(.ERROR) _PARSEDIR ?= ${.PARSEDIR:tA}
.-include <local.meta.sys.env.mk> .-include <local.meta.sys.env.mk>
# If TARGET_SPEC_VARS is other than just MACHINE
# it should be set by now.
# TARGET_SPEC must not contain any '.'s.
TARGET_SPEC_VARS ?= MACHINE
.if !target(_meta_tspec_env_done_)
_meta_tspec_env_done_: .NOTMAIN
# Allow for local.meta.sys.env.mk to have done this
.if ${TARGET_SPEC:Uno:M*,*} != ""
# deal with TARGET_SPEC from env
_tspec := ${TARGET_SPEC:S/,/ /g}
.for i in ${TARGET_SPEC_VARS:${M_RANGE:Urange}}
${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]}
.endfor
# We need to stop that TARGET_SPEC affecting any submakes
TARGET_SPEC=
# so export but do not track
.export-env TARGET_SPEC
.export ${TARGET_SPEC_VARS}
.for v in ${TARGET_SPEC_VARS:O:u}
.if empty($v)
.undef $v
.endif
.endfor
.endif
.endif
# Now make sure we know what TARGET_SPEC is
# as we may need it to find Makefile.depend*
.if ${MACHINE:Mhost*} != ""
# host is special
TARGET_SPEC = ${MACHINE}
.else
TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,}
.endif
# absolute path to what we are reading.
_PARSEDIR = ${.PARSEDIR:tA}
.if !defined(SYS_MK_DIR) .if !defined(SYS_MK_DIR)
SYS_MK_DIR := ${_PARSEDIR} SYS_MK_DIR := ${_PARSEDIR}
.endif .endif
META_MODE += meta verbose .if !target(.ERROR)
META_MODE += meta
.if empty(.MAKEFLAGS:M-s)
META_MODE += verbose
.endif
.if ${MAKE_VERSION:U0} > 20130323 && empty(.MAKE.PATH_FILEMON) .if ${MAKE_VERSION:U0} > 20130323 && empty(.MAKE.PATH_FILEMON)
# we do not support filemon # we do not support filemon
META_MODE += nofilemon META_MODE += nofilemon
@ -102,19 +67,7 @@ META_MODE += silent=yes
.endif .endif
.endif .endif
# we use the pseudo machine "host" for the build host. .if ${MK_DIRDEPS_BUILD:Uno} == "yes"
# this should be taken care of before we get here
.if ${OBJTOP:Ua} == ${HOST_OBJTOP:Ub}
MACHINE = host
.endif
.if !defined(MACHINE0)
# it can be handy to know which MACHINE kicked off the build
# for example, if using Makefild.depend for multiple machines,
# allowing only MACHINE0 to update can keep things simple.
MACHINE0 := ${MACHINE}
.export MACHINE0
.endif
.if !defined(META2DEPS) .if !defined(META2DEPS)
.if defined(PYTHON) && exists(${PYTHON}) .if defined(PYTHON) && exists(${PYTHON})
@ -134,6 +87,11 @@ MAKE_PRINT_VAR_ON_ERROR += \
MAKEFILE \ MAKEFILE \
.MAKE.MODE .MAKE.MODE
MK_META_ERROR_TARGET = yes
.endif
.if ${MK_META_ERROR_TARGET:Uno} == "yes"
.if !defined(SB) && defined(SRCTOP) .if !defined(SB) && defined(SRCTOP)
SB = ${SRCTOP:H} SB = ${SRCTOP:H}
.endif .endif
@ -150,21 +108,12 @@ _metaError: .NOMETA .NOTMAIN
echo "ERROR: log ${meta_error_log}" >&2; }; : echo "ERROR: log ${meta_error_log}" >&2; }; :
.endif .endif
.endif
# Are we, after all, in meta mode? # Are we, after all, in meta mode?
.if ${.MAKE.MODE:Uno:Mmeta*} != "" .if ${.MAKE.MODE:Uno:Mmeta*} != ""
MKDEP_MK ?= meta.autodep.mk MKDEP_MK ?= meta.autodep.mk
.if ${.MAKE.MAKEFILES:M*sys.dependfile.mk} == ""
# this does all the smarts of setting .MAKE.DEPENDFILE
.-include <sys.dependfile.mk>
# check if we got anything sane
.if ${.MAKE.DEPENDFILE} == ".depend"
.undef .MAKE.DEPENDFILE
.endif
.MAKE.DEPENDFILE ?= Makefile.depend
.endif
# we can afford to use cookies to prevent some targets # we can afford to use cookies to prevent some targets
# re-running needlessly # re-running needlessly
META_COOKIE_TOUCH?= touch ${COOKIE.${.TARGET}:U${.OBJDIR}/${.TARGET:T}} META_COOKIE_TOUCH?= touch ${COOKIE.${.TARGET}:U${.OBJDIR}/${.TARGET:T}}
@ -192,27 +141,13 @@ UPDATE_DEPENDFILE= NO
.endif .endif
.endif .endif
.if ${.MAKE.LEVEL} == 0 .else # in meta mode?
.if ${MK_DIRDEPS_BUILD:Uyes} == "yes"
# make sure dirdeps target exists and do it first
all: dirdeps .WAIT
dirdeps:
.NOPATH: dirdeps
.if defined(ALL_MACHINES)
# the first .MAIN: is what counts
# by default dirdeps is all we want at level0
.MAIN: dirdeps
.endif
.endif
.endif
.else
META_COOKIE_TOUCH= META_COOKIE_TOUCH=
# some targets need to be .PHONY in non-meta mode # some targets need to be .PHONY in non-meta mode
META_NOPHONY= .PHONY META_NOPHONY= .PHONY
META_NOECHO= echo META_NOECHO= echo
.endif
.endif .endif # in meta mode?
.-include <local.meta.sys.mk> .-include <local.meta.sys.mk>

View file

@ -441,6 +441,8 @@ Leverages ``bmake`` to compute optimal link order for libraries.
This works nicely and makes refactoring a breeze - so long as you This works nicely and makes refactoring a breeze - so long as you
have no (or few) cicular dependencies between libraries. have no (or few) cicular dependencies between libraries.
Consider this experimental.
man.mk man.mk
------ ------
@ -509,17 +511,58 @@ then ``jobs.mk`` will run::
this ensures you get a build log and JOB_MAX is assumed to be set this ensures you get a build log and JOB_MAX is assumed to be set
optimally for the host. optimally for the host.
Meta mode META_MODE
========= =========
The 20110505 and later versions of ``mk-files`` include a number of The 20110505 and later versions of ``mk-files`` include a number of
makefiles contributed by Juniper Networks, Inc. makefiles contributed by Juniper Networks, Inc.
These allow the latest version of bmake_ to run in `meta mode`_ These allow the latest version of bmake_ to run in `meta mode`_
see `dirdeps.mk`_ see `dirdeps.mk`_ and DIRDEPS_BUILD_ below.
.. _`dirdeps.mk`: /help/sjg/dirdeps.htm .. _`dirdeps.mk`: /help/sjg/dirdeps.htm
.. _`meta mode`: bmake-meta-mode.htm .. _`meta mode`: bmake-meta-mode.htm
DIRDEPS_BUILD
=============
When the `meta mode`_ was originally done, there was no distinction
between META_MODE_ and ``DIRDEPS_BUILD``, but as these were integrated
into FreeBSD it became clear that META_MODE_ could be useful to many
developers independently of ``DIRDEPS_BUILD``.
Thus today we distinguish between the two.
We have the following makefiles which are relevant to
``DIRDEPS_BUILD`` or META_MODE_::
share/mk/auto.obj.mk
share/mk/dirdeps-cache-update.mk
share/mk/dirdeps-only.mk
share/mk/dirdeps-options.mk
share/mk/dirdeps-targets.mk
share/mk/dirdeps.mk
share/mk/gendirdeps.mk
share/mk/host-target.mk
share/mk/install-new.mk
share/mk/meta.autodep.mk
share/mk/meta.stage.mk
share/mk/meta.sys.mk
share/mk/meta2deps.py
share/mk/meta2deps.sh
share/mk/sys.dependfile.mk
share/mk/sys.dirdeps.mk
and the following are typically used for customization.
See `freebsd-meta-mode`_ and `netbsd-meta-mode`_::
share/mk/local.dirdeps-build.mk
share/mk/local.dirdeps-missing.mk
share/mk/local.dirdeps.mk
share/mk/local.meta.sys.mk
share/mk/local.sys.dirdeps.env.mk
share/mk/local.sys.dirdeps.mk
share/mk/local.sys.mk
Install Install
======= =======
@ -538,9 +581,11 @@ where you unpacked the tar file, you can::
.. _bmake: bmake.htm .. _bmake: bmake.htm
.. _NetBSD: http://www.netbsd.org/ .. _NetBSD: http://www.netbsd.org/
.. _mkdeps.sh: http://www.crufty.net/ftp/pub/sjg/mkdeps.sh .. _mkdeps.sh: https://www.crufty.net/ftp/pub/sjg/mkdeps.sh
.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz .. _mk.tar.gz: https://www.crufty.net/ftp/pub/sjg/mk.tar.gz
.. _`freebsd-meta-mode`: https://www.crufty.net/sjg/docs/freebsd-meta-mode.htm
.. _`netbsd-meta-mode`: https://www.crufty.net/sjg/docs/netbsd-meta-mode.htm
:Author: sjg@crufty.net :Author: sjg@crufty.net
:Revision: $Id: mk-files.txt,v 1.22 2023/04/16 23:43:33 sjg Exp $ :Revision: $Id: mk-files.txt,v 1.23 2023/05/11 22:55:08 sjg Exp $
:Copyright: Crufty.NET :Copyright: Crufty.NET

412
contrib/bmake/mk/newlog.sh Executable file
View file

@ -0,0 +1,412 @@
#!/bin/sh
# NAME:
# newlog - rotate log files
#
# SYNOPSIS:
# newlog.sh [options] "log"[:"num"] ...
#
# DESCRIPTION:
# This script saves multiple generations of each "log".
# The "logs" are kept compressed except for the current and
# previous ones.
#
# Options:
#
# -C "compress"
# Compact old logs (other than .0) with "compress"
# (default is 'gzip' or 'compress' if no 'gzip').
#
# -E "ext"
# If "compress" produces a file extention other than
# '.Z' or '.gz' we need to know.
#
# -G "gens"
# "gens" is a comma separated list of "log":"num" pairs
# that allows certain logs to handled differently.
#
# -N Don't actually do anything, just show us.
#
# -R Rotate rather than save logs by default.
# This is the default anyway.
#
# -S Save rather than rotate logs by default.
# Each log is saved to a unique name that remains
# unchanged. This results in far less churn.
#
# -f "fmt"
# Format ('%Y%m%d.%H%M%S') for suffix added to "log" to
# uniquely name it when using the '-S' option.
# If a "log" is saved more than once per second we add
# an extra suffix of our process-id.
#
# -d The "log" to be rotated/saved is a directory.
# We leave the mode of old directories alone.
#
# -e Normally logs are only cycled if non-empty, this
# option forces empty logs to be cycled as well.
#
# -g "group"
# Set the group of "log" to "group".
#
# -m "mode"
# Set the mode of "log".
#
# -M "mode"
# Set the mode of old logs (default 444).
#
# -n "num"
# Keep "num" generations of "log".
#
# -o "owner"
# Set the owner of "log".
#
# Regardless of whether '-R' or '-S' is provided, we attempt to
# choose the correct behavior based on observation of "log.0" if
# it exists; if it is a symbolic link, we save, otherwise
# we rotate.
#
# BUGS:
# 'Newlog.sh' tries to avoid being fooled by symbolic links, but
# multiply indirect symlinks are only handled on machines where
# test(1) supports a check for symlinks.
#
# AUTHOR:
# Simon J. Gerraty <sjg@crufty.net>
#
# RCSid:
# $Id: newlog.sh,v 1.26 2021/04/30 16:29:02 sjg Exp $
#
# @(#) Copyright (c) 1993-2016 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
#
Mydir=`dirname $0`
case $Mydir in
/*) ;;
*) Mydir=`cd $Mydir; pwd`;;
esac
# places to find chown (and setopts.sh)
PATH=$PATH:/usr/etc:/sbin:/usr/sbin:/usr/local/share/bin:/share/bin:$Mydir
# linux doesn't necessarily have compress,
# and gzip appears in various locations...
Which() {
case "$1" in
-*) t=$1; shift;;
*) t=-x;;
esac
case "$1" in
/*) test $t $1 && echo $1;;
*)
for d in `IFS=:; echo ${2:-$PATH}`
do
test $t $d/$1 && { echo $d/$1; break; }
done
;;
esac
}
# shell's typically have test(1) as built-in
# and not all support all options.
test_opt() {
_o=$1
_a=$2
_t=${3:-/}
case `test -$_o $_t 2>&1` in
*:*) eval test_$_o=$_a;;
*) eval test_$_o=-$_o;;
esac
}
# convert find/ls mode to octal
fmode() {
eval `echo $1 |
sed 's,\(.\)\(...\)\(...\)\(...\),ft=\1 um=\2 gm=\3 om=\4,'`
sm=
case "$um" in
*s*) sm=r
um=`echo $um | sed 's,s,x,'`
;;
*) sm=-;;
esac
case "$gm" in
*[Ss]*)
sm=${sm}w
gm=`echo $gm | sed 's,s,x,;s,S,-,'`
;;
*) sm=${sm}-;;
esac
case "$om" in
*t)
sm=${sm}x
om=`echo $om | sed 's,t,x,'`
;;
*) sm=${sm}-;;
esac
echo $sm $um $gm $om |
sed 's,rwx,7,g;s,rw-,6,g;s,r-x,5,g;s,r--,4,g;s,-wx,3,g;s,-w-,2,g;s,--x,1,g;s,---,0,g;s, ,,g'
}
get_mode() {
case "$OS,$STAT" in
FreeBSD,*)
$STAT -f %Op $1 | sed 's,.*\(....\),\1,'
return
;;
esac
# fallback to find
fmode `find $1 -ls -prune | awk '{ print $3 }'`
}
get_mtime_suffix() {
case "$OS,$STAT" in
FreeBSD,*)
$STAT -t "${2:-$opt_f}" -f %Sm $1
return
;;
esac
# this will have to do
date "+${2:-$opt_f}"
}
case /$0 in
*/newlog*) rotate_func=rotate_log;;
*/save*) rotate_func=save_log;;
*) rotate_func=rotate_log;;
esac
opt_n=7
opt_m=
opt_M=444
opt_f=%Y%m%d.%H%M%S
opt_str=dNn:o:g:G:C:M:m:eE:f:RS
. setopts.sh
test $# -gt 0 || exit 0 # nothing to do.
OS=${OS:-`uname`}
STAT=${STAT:-`Which stat`}
# sorry, setops semantics for booleans changed.
case "${opt_d:-0}" in
0) rm_f=-f
opt_d=-f
for x in $opt_C gzip compress
do
opt_C=`Which $x "/bin:/usr/bin:$PATH"`
test -x $opt_C && break
done
empty() { test ! -s $1; }
;;
*) rm_f=-rf
opt_d=-d
opt_M=
opt_C=:
empty() {
if [ -d $1 ]; then
n=`'ls' -a1 $1/. | wc -l`
[ $n -gt 2 ] && return 1
fi
return 0
}
;;
esac
case "${opt_N:-0}" in
0) ECHO=;;
*) ECHO=echo;;
esac
case "${opt_e:-0}" in
0) force=;;
*) force=yes;;
esac
case "${opt_R:-0}" in
0) ;;
*) rotate_func=rotate_log;;
esac
case "${opt_S:-0}" in
0) ;;
*) rotate_func=save_log opt_S=;;
esac
# see whether test handles -h or -L
test_opt L -h
test_opt h ""
case "$test_L,$test_h" in
-h,) test_L= ;; # we don't support either!
esac
case "$test_L" in
"") # No, so this is about all we can do...
logs=`'ls' -ld $* | awk '{ print $NF }'`
;;
*) # it does
logs="$*"
;;
esac
read_link() {
case "$test_L" in
"") 'ls' -ld $1 | awk '{ print $NF }'; return;;
esac
if test $test_L $1; then
'ls' -ld $1 | sed 's,.*> ,,'
else
echo $1
fi
}
# create the new log
new_log() {
log=$1
mode=$2
if test "x$opt_M" != x; then
$ECHO chmod $opt_M $log.0 2> /dev/null
fi
# someone may have managed to write to it already
# so don't truncate it.
case "$opt_d" in
-d) $ECHO mkdir -p $log;;
*) $ECHO touch $log;;
esac
# the order here matters
test "x$opt_o" = x || $ECHO chown $opt_o $log
test "x$opt_g" = x || $ECHO chgrp $opt_g $log
test "x$mode" = x || $ECHO chmod $mode $log
}
rotate_log() {
log=$1
n=${2:-$opt_n}
# make sure excess generations are trimmed
$ECHO rm $rm_f `echo $log.$n | sed 's/\([0-9]\)$/[\1-9]*/'`
mode=${opt_m:-`get_mode $log`}
while test $n -gt 0
do
p=`expr $n - 1`
if test -s $log.$p; then
$ECHO rm $rm_f $log.$p.*
$ECHO $opt_C $log.$p
if test "x$opt_M" != x; then
$ECHO chmod $opt_M $log.$p.* 2> /dev/null
fi
fi
for ext in $opt_E .gz .Z ""
do
test $opt_d $log.$p$ext || continue
$ECHO mv $log.$p$ext $log.$n$ext
done
n=$p
done
# leave $log.0 uncompressed incase some one still has it open.
$ECHO mv $log $log.0
new_log $log $mode
}
# unlike rotate_log we do not rotate files,
# but give each log a unique (but stable name).
# This avoids churn for folk who rsync things.
# We make log.0 a symlink to the most recent log
# so it can be found and compressed next time around.
save_log() {
log=$1
n=${2:-$opt_n}
fmt=$3
last=`read_link $log.0`
case "$last" in
$log.0) # should never happen
test -s $last && $ECHO mv $last $log.$$;;
$log.*)
$ECHO $opt_C $last
;;
*.*) $ECHO $opt_C `dirname $log`/$last
;;
esac
$ECHO rm -f $log.0
# remove excess logs - we rely on mtime!
$ECHO rm $rm_f `'ls' -1td $log.* 2> /dev/null | sed "1,${n}d"`
mode=${opt_m:-`get_mode $log`}
# this is our default suffix
opt_S=${opt_S:-`get_mtime_suffix $log $fmt`}
case "$fmt" in
""|$opt_f) suffix=$opt_S;;
*) suffix=`get_mtime_suffix $log $fmt`;;
esac
# find a unique name to save current log as
for nlog in $log.$suffix $log.$suffix.$$
do
for f in $nlog*
do
break
done
test $opt_d $f || break
done
# leave $log.0 uncompressed incase some one still has it open.
$ECHO mv $log $nlog
test "x$opt_M" = x || $ECHO chmod $opt_M $nlog 2> /dev/null
$ECHO ln -s `basename $nlog` $log.0
new_log $log $mode
}
for f in $logs
do
n=$opt_n
save=
case "$f" in
*:[1-9]*)
set -- `IFS=:; echo $f`; f=$1; n=$2;;
*:n=*|*:save=*)
eval `echo "f=$f" | tr ':' ' '`;;
esac
# try and pick the right function to use
rfunc=$rotate_func # default
if test $opt_d $f.0; then
case `read_link $f.0` in
$f.0) rfunc=rotate_log;;
*) rfunc=save_log;;
esac
fi
case "$test_L" in
-?)
while test $test_L $f # it is [still] a symlink
do
f=`read_link $f`
done
;;
esac
case ",${opt_G}," in
*,${f}:n=*|,${f}:save=*)
eval `echo ",${opt_G}," | sed "s!.*,${f}:\([^,]*\),.*!\1!;s,:, ,g"`
;;
*,${f}:*)
# opt_G is a , separated list of log:n pairs
n=`echo ,$opt_G, | sed -e "s,.*${f}:\([0-9][0-9]*\).*,\1,"`
;;
esac
if empty $f; then
test "$force" || continue
fi
test "$save" && rfunc=save_log
$rfunc $f $n $save
done

View file

@ -1,6 +1,6 @@
# $Id: sys.dependfile.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $ # $Id: sys.dependfile.mk,v 1.10 2023/05/10 19:23:26 sjg Exp $
# #
# @(#) Copyright (c) 2012, Simon J. Gerraty # @(#) Copyright (c) 2012-2023, Simon J. Gerraty
# #
# This file is provided in the hope that it will # This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY. # be of use. There is absolutely NO WARRANTY.
@ -13,7 +13,10 @@
# sjg@crufty.net # sjg@crufty.net
# #
# This only makes sense in meta mode. .if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
# This only makes sense for DIRDEPS_BUILD.
# This allows a mixture of auto generated as well as manually edited # This allows a mixture of auto generated as well as manually edited
# dependency files, which can be differentiated by their names. # dependency files, which can be differentiated by their names.
# As per dirdeps.mk we only require: # As per dirdeps.mk we only require:
@ -57,3 +60,5 @@ MACHINE := ${_m}
.endif .endif
.endif .endif
.MAKE.DEPENDFILE ?= ${.MAKE.DEPENDFILE_DEFAULT} .MAKE.DEPENDFILE ?= ${.MAKE.DEPENDFILE_DEFAULT}
.endif

View file

@ -0,0 +1,183 @@
# $Id: sys.dirdeps.mk,v 1.9 2023/05/11 20:05:42 sjg Exp $
#
# @(#) Copyright (c) 2012-2023, 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
#
# Originally DIRDEPS_BUILD and META_MODE were the same thing.
# So, much of this was done in *meta.sys.mk and local*mk
# but properly belongs here.
# Include from [local.]sys.mk - if doing DIRDEPS_BUILD
# we should not be here otherwise
MK_DIRDEPS_BUILD ?= yes
# these are all implied
MK_AUTO_OBJ ?= yes
MK_META_MODE ?= yes
MK_STAGING ?= yes
_PARSEDIR ?= ${.PARSEDIR:tA}
.-include <local.sys.dirdeps.env.mk>
.if ${.MAKE.LEVEL} == 0
# make sure dirdeps target exists and do it first
dirdeps:
# first .MAIN is what counts
.MAIN: dirdeps
.NOPATH: dirdeps
all: dirdeps .WAIT
.endif
.if empty(SRCTOP)
# fallback assumes share/mk!
SRCTOP := ${SB_SRC:U${.PARSEDIR:tA:H:H}}
.export SRCTOP
.endif
# fake SB if not using mk wrapper
.if !defined(SB)
SB := ${SRCTOP:H}
.export SB
.endif
.if empty(OBJROOT)
OBJROOT := ${SB_OBJROOT:U${MAKEOBJDIRPREFIX:U${SB}/obj}/}
.export OBJROOT
.endif
.if empty(STAGE_ROOT)
STAGE_ROOT ?= ${OBJROOT}stage
.export STAGE_ROOT
.endif
# We should be included before meta.sys.mk
# If TARGET_SPEC_VARS is other than just MACHINE
# it should be set by now.
# TARGET_SPEC must not contain any '.'s.
TARGET_SPEC_VARS ?= MACHINE
.if !target(_tspec_env_done_)
_tspec_env_done_: .NOTMAIN
.if ${TARGET_SPEC:Uno:M*,*} != ""
# deal with TARGET_SPEC from env
_tspec := ${TARGET_SPEC:S/,/ /g}
.for i in ${TARGET_SPEC_VARS:${M_RANGE:Urange}}
${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]}
.endfor
# We need to stop that TARGET_SPEC affecting any submakes
TARGET_SPEC=
# so export but do not track
.export-env TARGET_SPEC
.export ${TARGET_SPEC_VARS}
.for v in ${TARGET_SPEC_VARS:O:u}
.if empty($v)
.undef $v
.endif
.endfor
.endif
.endif
# Now make sure we know what TARGET_SPEC is
# as we may need it to find Makefile.depend*
.if ${MACHINE:Mhost*} != ""
# host is special
TARGET_SPEC = ${MACHINE}
.else
TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,}
.endif
.if ${TARGET_SPEC_VARS:[#]} > 1
TARGET_OBJ_SPEC ?= ${TARGET_SPEC_VARS:@v@${$v:U}@:ts.}
.else
TARGET_OBJ_SPEC ?= ${MACHINE}
.endif
MAKE_PRINT_VAR_ON_ERROR += ${TARGET_SPEC_VARS}
.if !defined(MACHINE0)
# it can be handy to know which MACHINE kicked off the build
# for example, if using Makefild.depend for multiple machines,
# allowing only MACHINE0 to update can keep things simple.
MACHINE0 := ${MACHINE}
.export MACHINE0
.endif
.if ${MACHINE} == "host"
OBJTOP = ${HOST_OBJTOP}
.elif ${MACHINE} == "host32"
OBJTOP = ${HOST_OBJTOP32}
.endif
MACHINE_OBJ.host = ${HOST_TARGET}
MACHINE_OBJ.host32 = ${HOST_TARGET32}
MACHINE_OBJ.${MACHINE} ?= ${TARGET_OBJ_SPEC}
MACHINE_OBJDIR = ${MACHINE_OBJ.${MACHINE}}
OBJTOP = ${OBJROOT}/${MACHINE_OBJDIR}
# we do not use MAKEOBJDIRPREFIX
.undef MAKEOBJDIRPREFIX
# we use this
MAKEOBJDIR ?= ${.CURDIR:S,${SRCTOP},${OBJTOP},}
STAGE_MACHINE ?= ${MACHINE_OBJDIR}
STAGE_OBJTOP ?= ${STAGE_ROOT}/${STAGE_MACHINE}
STAGE_COMMON_OBJTOP ?= ${STAGE_ROOT}/common
STAGE_HOST_OBJTOP ?= ${STAGE_ROOT}/${HOST_TARGET}
STAGE_HOST_OBJTOP32 ?= ${STAGE_ROOT}/${HOST_TARGET32}
STAGE_INCLUDEDIR ?= ${STAGE_OBJTOP}${INCLUDEDIR:U/usr/include}
STAGE_LIBDIR ?= ${STAGE_OBJTOP}${LIBDIR:U/lib}
TIME_STAMP_FMT ?= @ %s [%Y-%m-%d %T] ${:U}
DATE_TIME_STAMP ?= `date '+${TIME_STAMP_FMT}'`
TIME_STAMP ?= ${TIME_STAMP_FMT:localtime}
.if ${MK_TIME_STAMPS:Uyes} == "yes"
TRACER = ${TIME_STAMP}
ECHO_DIR = echo ${TIME_STAMP}
ECHO_TRACE = echo ${TIME_STAMP}
.endif
.if ${.CURDIR} == ${SRCTOP}
RELDIR= .
RELTOP= .
.elif ${.CURDIR:M${SRCTOP}/*}
RELDIR:= ${.CURDIR:S,${SRCTOP}/,,}
.else
RELDIR:= ${.OBJDIR:S,${OBJTOP}/,,}
.endif
RELTOP?= ${RELDIR:C,[^/]+,..,g}
RELOBJTOP?= ${RELTOP}
RELSRCTOP?= ${RELTOP}
# this does all the smarts of setting .MAKE.DEPENDFILE
.-include <sys.dependfile.mk>
.-include <local.sys.dirdeps.mk>
# check if we got anything sane
.if ${.MAKE.DEPENDFILE} == ".depend"
.undef .MAKE.DEPENDFILE
.endif
# just in case
.MAKE.DEPENDFILE ?= Makefile.depend
.if ${.MAKE.LEVEL} > 0
# Makefile.depend* also get read at level 1+
# and often refer to DEP_MACHINE etc,
# so ensure DEP_* (for TARGET_SPEC_VARS anyway) are set
.for V in ${TARGET_SPEC_VARS}
DEP_$V = ${$V}
.endfor
.endif

View file

@ -1,4 +1,4 @@
# $Id: sys.mk,v 1.54 2022/09/09 17:44:29 sjg Exp $ # $Id: sys.mk,v 1.55 2023/05/10 19:23:26 sjg Exp $
# #
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty # @(#) Copyright (c) 2003-2009, Simon J. Gerraty
# #
@ -85,6 +85,9 @@ OPTIONS_DEFAULT_DEPENDENT += \
.-include <options.mk> .-include <options.mk>
# :Uno incase options.mk not installed # :Uno incase options.mk not installed
.if ${MK_DIRDEPS_BUILD:Uno} == "yes"
.-include <sys.dirdeps.mk>
.endif
.if ${MK_META_MODE:Uno} == "yes" .if ${MK_META_MODE:Uno} == "yes"
.-include <meta.sys.mk> .-include <meta.sys.mk>
.MAKE.MODE ?= meta verbose {META_MODE} .MAKE.MODE ?= meta verbose {META_MODE}

View file

@ -1,4 +1,4 @@
/* $NetBSD: parse.c,v 1.696 2023/02/15 06:52:58 rillig Exp $ */ /* $NetBSD: parse.c,v 1.698 2023/05/10 16:10:02 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -91,7 +91,7 @@
* Parse_Error Report a parse error, a warning or an informational * Parse_Error Report a parse error, a warning or an informational
* message. * message.
* *
* Parse_MainName Returns a list of the single main target to create. * Parse_MainName Populate the list of targets to create.
*/ */
#include <sys/types.h> #include <sys/types.h>
@ -121,7 +121,7 @@
#include "pathnames.h" #include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: parse.c,v 1.696 2023/02/15 06:52:58 rillig Exp $"); MAKE_RCSID("$NetBSD: parse.c,v 1.698 2023/05/10 16:10:02 rillig Exp $");
/* /*
* A file being read. * A file being read.
@ -257,10 +257,7 @@ SearchPath *defSysIncPath; /* default for sysIncPath */
/* /*
* The parseKeywords table is searched using binary search when deciding * The parseKeywords table is searched using binary search when deciding
* if a target or source is special. The 'spec' field is the ParseSpecial * if a target or source is special.
* type of the keyword (SP_NOT if the keyword isn't special as a target) while
* the 'op' field is the operator to apply to the list of targets if the
* keyword is used as a source ("0" if the keyword isn't special as a source)
*/ */
static const struct { static const struct {
const char name[17]; const char name[17];
@ -325,7 +322,7 @@ GetInclude(size_t i)
return Vector_Get(&includes, i); return Vector_Get(&includes, i);
} }
/* The file that is currently being read. */ /* The makefile that is currently being read. */
static IncludedFile * static IncludedFile *
CurFile(void) CurFile(void)
{ {
@ -403,8 +400,11 @@ PrintStackTrace(bool includingInnermost)
const char *fname = entry->name.str; const char *fname = entry->name.str;
char dirbuf[MAXPATHLEN + 1]; char dirbuf[MAXPATHLEN + 1];
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) {
fname = realpath(fname, dirbuf); const char *realPath = realpath(fname, dirbuf);
if (realPath != NULL)
fname = realPath;
}
if (entry->forLoop != NULL) { if (entry->forLoop != NULL) {
char *details = ForLoop_Details(entry->forLoop); char *details = ForLoop_Details(entry->forLoop);
@ -658,7 +658,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
/* /*
* If the node was of the left-hand side of a '::' operator, * If the node was on the left-hand side of a '::' operator,
* we need to create a new instance of it for the children * we need to create a new instance of it for the children
* and commands on this dependency line since each of these * and commands on this dependency line since each of these
* dependency groups has its own attributes and commands, * dependency groups has its own attributes and commands,
@ -3004,10 +3004,7 @@ Parse_End(void)
} }
/* /* Populate the list with the single main target to create, or error out. */
* Return a list containing the single main target to create.
* If no such target exists, we Punt with an obnoxious error message.
*/
void void
Parse_MainName(GNodeList *mainList) Parse_MainName(GNodeList *mainList)
{ {

View file

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.193 2023/02/25 20:03:25 sjg Exp $ # $Id: Makefile,v 1.195 2023/05/10 18:26:24 sjg Exp $
# #
# $NetBSD: Makefile,v 1.333 2023/02/25 19:30:32 sjg Exp $ # $NetBSD: Makefile,v 1.335 2023/05/10 13:03:06 rillig Exp $
# #
# Unit tests for make(1) # Unit tests for make(1)
# #
@ -211,7 +211,6 @@ TESTS+= export
TESTS+= export-all TESTS+= export-all
TESTS+= export-env TESTS+= export-env
TESTS+= export-variants TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst TESTS+= forsubst
TESTS+= gnode-submake TESTS+= gnode-submake
TESTS+= hanoi-include TESTS+= hanoi-include
@ -375,6 +374,7 @@ TESTS+= varmod-loop-delete
TESTS+= varmod-loop-varname TESTS+= varmod-loop-varname
TESTS+= varmod-match TESTS+= varmod-match
TESTS+= varmod-match-escape TESTS+= varmod-match-escape
TESTS+= varmod-mtime
TESTS+= varmod-no-match TESTS+= varmod-no-match
TESTS+= varmod-order TESTS+= varmod-order
TESTS+= varmod-order-numeric TESTS+= varmod-order-numeric

View file

@ -1,12 +1,12 @@
# $NetBSD: cond-func.mk,v 1.11 2022/01/07 19:30:17 rillig Exp $ # $NetBSD: cond-func.mk,v 1.12 2023/05/10 15:53:32 rillig Exp $
# #
# Tests for those parts of the functions in .if conditions that are common # Tests for those parts of the functions in .if conditions that are common
# among several functions. # among several functions.
# #
# The below test uses the function defined(...) since it has no side-effects, # The below test uses the 'defined' function since it has no side-effects.
# the other functions (except empty(...)) would work equally well. The # The other functions would work equally well, except for 'empty', which
# function empty is special because it uses a different parsing algorithm for # parses its argument differently from the other functions.
# its argument. #
DEF= defined DEF= defined
${:UA B}= variable name with spaces ${:UA B}= variable name with spaces
@ -74,7 +74,7 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
# There may be spaces around the operators and parentheses, and even # There may be spaces around the operators and parentheses, and even
# inside the parentheses. The spaces inside the parentheses are not # inside the parentheses. The spaces inside the parentheses are not
# allowed for the empty() function (see cond-func-empty.mk), therefore # allowed for the 'empty' function (see cond-func-empty.mk), therefore
# they are typically omitted for the other functions as well. # they are typically omitted for the other functions as well.
.if ! defined ( DEF ) .if ! defined ( DEF )
. error . error

View file

@ -1,11 +1,12 @@
# $NetBSD: cond-late.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $ # $NetBSD: cond-late.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $
# #
# Using the :? modifier, variable expressions can contain conditional # Using the :? modifier, variable expressions can contain conditional
# expressions that are evaluated late, at expansion time. # expressions that are evaluated late, at expansion time.
# #
# Any variables appearing in these # Any expressions appearing in these conditions are expanded before parsing
# conditions are expanded before parsing the condition. This is # the condition. This is different from conditions in .if directives, where
# different from many other places. # expressions are evaluated individually and only as far as necessary, see
# cond-short.mk.
# #
# Because of this, variables that are used in these lazy conditions # Because of this, variables that are used in these lazy conditions
# should not contain double-quotes, or the parser will probably fail. # should not contain double-quotes, or the parser will probably fail.
@ -22,10 +23,14 @@ COND.false= "yes" != "yes"
# If the order of evaluation were to change to first parse the condition # If the order of evaluation were to change to first parse the condition
# and then expand the variables, the output would change from the # and then expand the variables, the output would change from the
# current "yes no" to "yes yes", since both variables are non-empty. # current "yes no" to "yes yes", since both variables are non-empty.
# expect: yes
# expect: no
cond-literal: cond-literal:
@echo ${ ${COND.true} :?yes:no} @echo ${ ${COND.true} :?yes:no}
@echo ${ ${COND.false} :?yes:no} @echo ${ ${COND.false} :?yes:no}
VAR+= ${${UNDEF} != "no":?:} VAR= ${${UNDEF} != "no":?:}
# expect-reset
# expect: make: Bad conditional expression ' != "no"' in ' != "no"?:'
.if empty(VAR:Mpattern) .if empty(VAR:Mpattern)
.endif .endif

View file

@ -1,4 +1,4 @@
# $NetBSD: dep-var.mk,v 1.7 2023/02/13 21:01:46 rillig Exp $ # $NetBSD: dep-var.mk,v 1.8 2023/05/10 15:53:32 rillig Exp $
# #
# Tests for variable references in dependency declarations. # Tests for variable references in dependency declarations.
# #
@ -91,5 +91,6 @@ undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
.MAKEFLAGS: -d0 .MAKEFLAGS: -d0
# XXX: Why is the exit status still 0, even though Parse_Error is called # XXX: The exit status is still 0, even though Parse_Error is called with
# with PARSE_FATAL in SuffExpandChildren? # PARSE_FATAL in SuffExpandChildren. The exit status is only affected by
# parse errors when they occur in the parsing phase, see Parse_File.

View file

@ -1,22 +1,17 @@
make: "directive-for-errors.mk" line 7: Unknown directive "fori" make: "directive-for-errors.mk" line 11: Unknown directive "fori"
make: "directive-for-errors.mk" line 8: warning: make: "directive-for-errors.mk" line 12: warning: <>
make: "directive-for-errors.mk" line 9: for-less endfor make: "directive-for-errors.mk" line 13: for-less endfor
make: "directive-for-errors.mk" line 19: Unknown directive "for" make: "directive-for-errors.mk" line 27: Unknown directive "for"
make: "directive-for-errors.mk" line 20: warning: make: "directive-for-errors.mk" line 28: warning: <>
make: "directive-for-errors.mk" line 21: for-less endfor make: "directive-for-errors.mk" line 29: for-less endfor
make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2. make: "directive-for-errors.mk" line 46: invalid character '$' in .for loop variable name
make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4. make: "directive-for-errors.mk" line 54: no iteration variables in for
make: "directive-for-errors.mk" line 43: no iteration variables in for make: "directive-for-errors.mk" line 66: Wrong number of words (5) in .for substitution list with 3 variables
make: "directive-for-errors.mk" line 47: warning: Should not be reached. make: "directive-for-errors.mk" line 80: missing `in' in for
make: "directive-for-errors.mk" line 48: for-less endfor make: "directive-for-errors.mk" line 91: Unknown modifier "Z"
make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for substitution list with 3 variables make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: "directive-for-errors.mk" line 64: missing `in' in for make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: "directive-for-errors.mk" line 66: warning: Should not be reached. make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: "directive-for-errors.mk" line 67: for-less endfor
make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: "directive-for-errors.mk" line 74: warning: Should not be reached.
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View file

@ -1,45 +1,56 @@
# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $ # $NetBSD: directive-for-errors.mk,v 1.5 2023/05/09 19:43:12 rillig Exp $
# #
# Tests for error handling in .for loops. # Tests for error handling in .for loops.
# expect-all
# A .for directive must be followed by whitespace, everything else results # A .for directive must be followed by whitespace, everything else results
# in a parse error. # in a parse error.
# expect+1: Unknown directive "fori"
.fori in 1 2 3 .fori in 1 2 3
. warning ${i} . warning <${i}>
.endfor .endfor
# expect-2: <>
# expect-2: for-less endfor
# A slash is not whitespace, therefore this is not parsed as a .for loop. # A slash is not whitespace, therefore this is not parsed as a .for loop.
# #
# XXX: The error message is misleading though. As of 2020-12-31, it says # XXX: The error message is misleading though. As of 2020-12-31, it says
# "Unknown directive "for"", but that directive is actually known. This is # 'Unknown directive "for"', but that directive is actually known. This is
# because ForEval does not detect the .for loop as such, so parsing # because ForEval does not detect the .for loop as such, so parsing
# continues in ParseLine > ParseDependencyLine > ParseDependency > # continues in ParseLine > ParseDependencyLine > ParseDependency >
# ParseDependencyTargets > ParseErrorNoDependency, and there the directive # ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently. # name is parsed a bit differently.
# expect+1: Unknown directive "for"
.for/i in 1 2 3 .for/i in 1 2 3
. warning ${i} . warning <${i}>
.endfor .endfor
# expect-2: warning: <>
# expect-2: for-less endfor
# As of 2020-12-31, the variable name can be an arbitrary word, it just needs
# to be separated by whitespace. Even '$' and '\' are valid variable names, # Before for.c 1.173 from 2023-05-08, the variable name could be an arbitrary
# which is not useful in practice. # word, it only needed to be separated by whitespace. Even '$' and '\' were
# valid variable names, which was not useful in practice.
# #
# The '$$' is not replaced with the values '1' or '3' from the .for loop, # The '$$' was not replaced with the values '1' or '3' from the .for loop,
# instead it is kept as-is, and when the .info directive expands its argument, # instead it was kept as-is, and when the .info directive expanded its
# each '$$' gets replaced with a single '$'. The "long variable expression" # argument, each '$$' got replaced with a single '$'. The "long variable
# ${$} gets replaced though, even though this would be a parse error everywhere # expression" ${$} got replaced though, even though this would be a parse
# outside a .for loop. # error everywhere outside a .for loop.
#
# The '\' on the other hand is treated as a normal variable name.
${:U\$}= dollar # see whether the "variable" '$' is local ${:U\$}= dollar # see whether the "variable" '$' is local
${:U\\}= backslash # see whether the "variable" '\' is local ${:U\\}= backslash # see whether the "variable" '\' is local
# expect+1: invalid character '$' in .for loop variable name
.for $ \ in 1 2 3 4 .for $ \ in 1 2 3 4
. info Dollar $$ ${$} $($) and backslash $\ ${\} $(\). . info Dollar $$ ${$} $($) and backslash $\ ${\} $(\).
.endfor .endfor
# If there are no variables, there is no point in expanding the .for loop # If there are no variables, there is no point in expanding the .for loop
# since this would end up in an endless loop, each time consuming 0 of the # since this would end up in an endless loop, consuming 0 of the 3 values in
# 3 values. # each iteration.
# expect+1: no iteration variables in for
.for in 1 2 3 .for in 1 2 3
# XXX: This should not be reached. It should be skipped, as already done # XXX: This should not be reached. It should be skipped, as already done
# when the number of values is not a multiple of the number of variables, # when the number of values is not a multiple of the number of variables,
@ -47,29 +58,39 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
. warning Should not be reached. . warning Should not be reached.
.endfor .endfor
# There are 3 variables and 5 values. These 5 values cannot be split evenly # There are 3 variables and 5 values. These 5 values cannot be split evenly
# among the variables, therefore the loop is not expanded at all, it is # among the variables, therefore the loop is not expanded at all, it is
# rather skipped. # skipped instead.
# expect+1: Wrong number of words (5) in .for substitution list with 3 variables
.for a b c in 1 2 3 4 5 .for a b c in 1 2 3 4 5
. warning Should not be reached. . warning Should not be reached.
.endfor .endfor
# The list of values after the 'in' may be empty, no matter if this emptiness # The list of values after the 'in' may be empty, no matter if this emptiness
# comes from an empty expansion or even from a syntactically empty line. # comes from an empty expansion or even from a syntactically empty line.
.for i in .for i in
. info Would be reached if there were items to loop over. . info Would be reached if there were items to loop over.
.endfor .endfor
# A missing 'in' should parse the .for loop but skip the body. # A missing 'in' should parse the .for loop but skip the body.
.for i : k # expect+1: missing `in' in for
.for i over k
# XXX: As of 2020-12-31, this line is reached once. # XXX: As of 2020-12-31, this line is reached once.
. warning Should not be reached. . warning Should not be reached.
.endfor .endfor
# A malformed modifier should be detected and skip the body of the loop. # A malformed modifier should be detected and skip the body of the loop.
# #
# XXX: As of 2020-12-31, Var_Subst doesn't report any errors, therefore # XXX: As of 2020-12-31, Var_Subst doesn't report any errors, therefore
# the loop body is expanded as if no error had happened. # the loop body is expanded as if no error had happened.
# expect+1: Unknown modifier "Z"
.for i in 1 2 ${:U3:Z} 4 .for i in 1 2 ${:U3:Z} 4
. warning Should not be reached. . warning Should not be reached.
.endfor .endfor
# expect-2: Should not be reached.
# expect-3: Should not be reached.
# expect-4: Should not be reached.

View file

@ -2,28 +2,28 @@ For: end for 1
For: loop body: For: loop body:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~} . info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!"" make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !" make: "directive-for-escape.mk" line 21: !"
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~} . info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\" make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 29: !"\\ make: "directive-for-escape.mk" line 32: !"\\
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U\$} . info ${:U\$}
make: "directive-for-escape.mk" line 43: $ make: "directive-for-escape.mk" line 47: $
For: loop body: For: loop body:
. info ${:U${V}} . info ${:U${V}}
make: "directive-for-escape.mk" line 43: value make: "directive-for-escape.mk" line 47: value
For: loop body: For: loop body:
. info ${:U${V:=-with-modifier}} . info ${:U${V:=-with-modifier}}
make: "directive-for-escape.mk" line 43: value-with-modifier make: "directive-for-escape.mk" line 47: value-with-modifier
For: loop body: For: loop body:
. info ${:U$(V)} . info ${:U$(V)}
make: "directive-for-escape.mk" line 43: value make: "directive-for-escape.mk" line 47: value
For: loop body: For: loop body:
. info ${:U$(V:=-with-modifier)} . info ${:U$(V:=-with-modifier)}
make: "directive-for-escape.mk" line 43: value-with-modifier make: "directive-for-escape.mk" line 47: value-with-modifier
For: end for 1 For: end for 1
For: loop body: For: loop body:
# ${:U\${UNDEF\:U\\$\\$} # ${:U\${UNDEF\:U\\$\\$}
@ -34,29 +34,25 @@ For: loop body:
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U\${UNDEF\:U\\$\\$} . info ${:U\${UNDEF\:U\\$\\$}
make: "directive-for-escape.mk" line 92: ${UNDEF:U\backslash$ make: "directive-for-escape.mk" line 101: ${UNDEF:U\backslash$
For: loop body: For: loop body:
. info ${:U{{\}\}} . info ${:U{{\}\}}
make: "directive-for-escape.mk" line 92: {{}} make: "directive-for-escape.mk" line 101: {{}}
For: loop body: For: loop body:
. info ${:Uend\}} . info ${:Uend\}}
make: "directive-for-escape.mk" line 92: end} make: "directive-for-escape.mk" line 101: end}
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end} . info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
make: "directive-for-escape.mk" line 113: begin<fallback>end make: "directive-for-escape.mk" line 122: begin<fallback>end
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info ${:U\$} . info ${:U\$}
make: "directive-for-escape.mk" line 121: $ make: "directive-for-escape.mk" line 131: $
make: "directive-for-escape.mk" line 140: invalid character ':' in .for loop variable name
For: end for 1 For: end for 1
For: loop body: make: "directive-for-escape.mk" line 150: invalid character '}' in .for loop variable name
. info ${NUMBERS} ${:Ureplaced}
make: "directive-for-escape.mk" line 129: one two three replaced
For: end for 1 For: end for 1
For: loop body:
. info ${:Ureplaced}
make: "directive-for-escape.mk" line 139: replaced
For: end for 1 For: end for 1
For: loop body: For: loop body:
. info . $$i: ${:Uinner} . info . $$i: ${:Uinner}
@ -69,46 +65,42 @@ For: loop body:
. info . $${i2}: ${i2} . info . $${i2}: ${i2}
. info . $${i,}: ${i,} . info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
make: "directive-for-escape.mk" line 147: . $i: inner make: "directive-for-escape.mk" line 159: . $i: inner
make: "directive-for-escape.mk" line 148: . ${i}: inner make: "directive-for-escape.mk" line 160: . ${i}: inner
make: "directive-for-escape.mk" line 149: . ${i:M*}: inner make: "directive-for-escape.mk" line 161: . ${i:M*}: inner
make: "directive-for-escape.mk" line 150: . $(i): inner make: "directive-for-escape.mk" line 162: . $(i): inner
make: "directive-for-escape.mk" line 151: . $(i:M*): inner make: "directive-for-escape.mk" line 163: . $(i:M*): inner
make: "directive-for-escape.mk" line 152: . ${i${:U}}: outer make: "directive-for-escape.mk" line 164: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 153: . ${i\}}: inner} make: "directive-for-escape.mk" line 165: . ${i\}}: inner}
make: "directive-for-escape.mk" line 154: . ${i2}: two make: "directive-for-escape.mk" line 166: . ${i2}: two
make: "directive-for-escape.mk" line 155: . ${i,}: comma make: "directive-for-escape.mk" line 167: . ${i,}: comma
make: "directive-for-escape.mk" line 156: . adjacent: innerinnerinnerinner make: "directive-for-escape.mk" line 168: . adjacent: innerinnerinnerinner
make: "directive-for-escape.mk" line 187: invalid character '$' in .for loop variable name
For: end for 1 For: end for 1
For: loop body: make: "directive-for-escape.mk" line 199: eight and no cents.
. info eight $$$$$$$$ and no cents.
. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents.
make: "directive-for-escape.mk" line 164: eight $$$$ and no cents.
make: "directive-for-escape.mk" line 165: eight dollardollardollardollar and no cents.
make: "directive-for-escape.mk" line 174: eight and no cents.
For: end for 1 For: end for 1
make: "directive-for-escape.mk" line 181: newline in .for value make: "directive-for-escape.mk" line 212: newline in .for value
make: "directive-for-escape.mk" line 181: newline in .for value make: "directive-for-escape.mk" line 212: newline in .for value
For: loop body: For: loop body:
. info short: ${:U" "} . info short: ${:U" "}
. info long: ${:U" "} . info long: ${:U" "}
make: "directive-for-escape.mk" line 182: short: " " make: "directive-for-escape.mk" line 213: short: " "
make: "directive-for-escape.mk" line 183: long: " " make: "directive-for-escape.mk" line 214: long: " "
For: end for 1 For: end for 1
For: loop body: For: loop body:
For: end for 1 For: end for 1
Parse_PushInput: .for loop in directive-for-escape.mk, line 196 Parse_PushInput: .for loop in directive-for-escape.mk, line 230
make: "directive-for-escape.mk" line 196: newline in .for value make: "directive-for-escape.mk" line 230: newline in .for value
in .for loop from directive-for-escape.mk:196 with i = " in .for loop from directive-for-escape.mk:230 with i = "
" "
For: loop body: For: loop body:
: ${:U" "} : ${:U" "}
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk' SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
Parsing line 197: : ${:U" "} Parsing line 231: : ${:U" "}
ParseDependency(: " ") ParseDependency(: " ")
ParseEOF: returning to file directive-for-escape.mk, line 199 ParseEOF: returning to file directive-for-escape.mk, line 233
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk' SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
Parsing line 199: .MAKEFLAGS: -d0 Parsing line 233: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0) ParseDependency(.MAKEFLAGS: -d0)
For: end for 1 For: end for 1
For: loop body: For: loop body:

View file

@ -1,9 +1,10 @@
# $NetBSD: directive-for-escape.mk,v 1.16 2022/06/12 16:09:21 rillig Exp $ # $NetBSD: directive-for-escape.mk,v 1.18 2023/05/09 19:43:12 rillig Exp $
# #
# Test escaping of special characters in the iteration values of a .for loop. # Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this # These values get expanded later using the :U variable modifier, and this
# escaping and unescaping must pass all characters and strings effectively # escaping and unescaping must pass all characters and strings unmodified.
# unmodified.
# expect-all
.MAKEFLAGS: -df .MAKEFLAGS: -df
@ -12,12 +13,14 @@
# This could be considered a bug. # This could be considered a bug.
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
# XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of # XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of
# the loop. Not only would it need the escaping for the variable modifier # the loop. Not only would it need the escaping for the variable modifier
# ':U' but also the escaping for the line-end comment. # ':U' but also the escaping for the line-end comment.
.for chars in ${ASCII} .for chars in ${ASCII}
. info ${chars} . info ${chars}
.endfor .endfor
# expect-2: !"
# As of 2020-12-31, using 2 backslashes before be '#' would treat the '#' # As of 2020-12-31, using 2 backslashes before be '#' would treat the '#'
# as comment character. Using 3 backslashes doesn't help either since # as comment character. Using 3 backslashes doesn't help either since
@ -28,6 +31,7 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
.for chars in ${ASCII.2020-12-31} .for chars in ${ASCII.2020-12-31}
. info ${chars} . info ${chars}
.endfor .endfor
# expect-2: !"\\
# Cover the code in ExprLen. # Cover the code in ExprLen.
# #
@ -42,6 +46,11 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
.for i in ${VALUES} .for i in ${VALUES}
. info $i . info $i
.endfor .endfor
# expect-2: $
# expect-3: value
# expect-4: value-with-modifier
# expect-5: value
# expect-6: value-with-modifier
# Try to cover the code for nested '{}' in ExprLen, without success. # Try to cover the code for nested '{}' in ExprLen, without success.
@ -112,6 +121,7 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES} .for i in ${VALUES}
. info $i . info $i
.endfor .endfor
# expect-2: begin<fallback>end
# A single trailing dollar doesn't happen in practice. # A single trailing dollar doesn't happen in practice.
# The dollar sign is correctly passed through to the body of the .for loop. # The dollar sign is correctly passed through to the body of the .for loop.
@ -120,21 +130,23 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${:U\$} .for i in ${:U\$}
. info ${i} . info ${i}
.endfor .endfor
# expect-2: $
# As of 2020-12-31, the name of the iteration variable can even contain # Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# colons, which then affects variable expressions having this exact modifier. # could contain colons, which affected variable expressions having this exact
# This is clearly an unintended side effect of the implementation. # modifier. This possibility was neither intended nor documented.
NUMBERS= one two three NUMBERS= one two three
# expect+1: invalid character ':' in .for loop variable name
.for NUMBERS:M*e in replaced .for NUMBERS:M*e in replaced
. info ${NUMBERS} ${NUMBERS:M*e} . info ${NUMBERS} ${NUMBERS:M*e}
.endfor .endfor
# As of 2020-12-31, the name of the iteration variable can contain braces, # Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# which gets even more surprising than colons, since it allows to replace # could contain braces, which allowed to replace sequences of variable
# sequences of variable expressions. There is no practical use case for # expressions. This possibility was neither intended nor documented.
# this, though.
BASENAME= one BASENAME= one
EXT= .c EXT= .c
# expect+1: invalid character '}' in .for loop variable name
.for BASENAME}${EXT in replaced .for BASENAME}${EXT in replaced
. info ${BASENAME}${EXT} . info ${BASENAME}${EXT}
.endfor .endfor
@ -155,11 +167,23 @@ i,= comma
. info . $${i,}: ${i,} . info . $${i,}: ${i,}
. info . adjacent: $i${i}${i:M*}$i . info . adjacent: $i${i}${i:M*}$i
.endfor .endfor
# expect-11: . $i: inner
# expect-11: . ${i}: inner
# expect-11: . ${i:M*}: inner
# expect-11: . $(i): inner
# expect-11: . $(i:M*): inner
# expect-11: . ${i${:U}}: outer
# expect-11: . ${i\}}: inner}
# expect-11: . ${i2}: two
# expect-11: . ${i,}: comma
# expect-11: . adjacent: innerinnerinnerinner
# The variable name can be a single '$' since there is no check on valid # Before for.c 1.173 from 2023-05-08, the variable name could be a single '$'
# variable names. ForLoop_SubstVarShort skips "stupid" variable names though, # since there was no check on valid variable names. ForLoop_SubstVarShort
# but ForLoop_SubstVarLong naively parses the body of the loop, substituting # skipped "stupid" variable names though, but ForLoop_SubstVarLong naively
# each '${$}' with an actual 'dollar'. # parsed the body of the loop, substituting each '${$}' with an actual
# '${:Udollar}'.
# expect+1: invalid character '$' in .for loop variable name
.for $ in dollar .for $ in dollar
. info eight $$$$$$$$ and no cents. . info eight $$$$$$$$ and no cents.
. info eight ${$}${$}${$}${$} and no cents. . info eight ${$}${$}${$}${$} and no cents.
@ -171,6 +195,7 @@ i,= comma
# evaluates to an empty string. # evaluates to an empty string.
closing-brace= } # guard against an closing-brace= } # guard against an
${closing-brace}= <closing-brace> # alternative interpretation ${closing-brace}= <closing-brace> # alternative interpretation
# expect+1: eight and no cents.
.info eight ${$}${$}${$}${$} and no cents. .info eight ${$}${$}${$}${$} and no cents.
# What happens if the values from the .for loop contain a literal newline? # What happens if the values from the .for loop contain a literal newline?
@ -178,10 +203,18 @@ ${closing-brace}= <closing-brace> # alternative interpretation
# body of the .for loop, where it was then interpreted as a literal newline, # body of the .for loop, where it was then interpreted as a literal newline,
# leading to syntax errors such as "Unclosed variable expression" in the upper # leading to syntax errors such as "Unclosed variable expression" in the upper
# line and "Invalid line type" in the lower line. # line and "Invalid line type" in the lower line.
#
# The error message occurs in the line of the .for loop since that's the place
# where the body of the .for loop is constructed, and at this point the
# newline character gets replaced with a plain space.
# expect+2: newline in .for value
# expect+1: newline in .for value
.for i in "${.newline}" .for i in "${.newline}"
. info short: $i . info short: $i
. info long: ${i} . info long: ${i}
.endfor .endfor
# expect-3: short: " "
# expect-3: long: " "
# No error since the newline character is not actually used. # No error since the newline character is not actually used.
.for i in "${.newline}" .for i in "${.newline}"
@ -193,6 +226,7 @@ ${closing-brace}= <closing-brace> # alternative interpretation
# loop is assembled, and at that point, ForLoop.nextItem had already been # loop is assembled, and at that point, ForLoop.nextItem had already been
# advanced. # advanced.
.MAKEFLAGS: -dp .MAKEFLAGS: -dp
# expect+1: newline in .for value
.for i in "${.newline}" .for i in "${.newline}"
: $i : $i
.endfor .endfor

View file

@ -1,42 +1,41 @@
make: "directive-for.mk" line 108: outer make: "directive-for.mk" line 119: outer
make: "directive-for.mk" line 133: a:\ a:\file.txt make: "directive-for.mk" line 137: a:\ a:\file.txt
make: "directive-for.mk" line 133: d:\\ make: "directive-for.mk" line 137: d:\\
make: "directive-for.mk" line 133: d:\\file.txt make: "directive-for.mk" line 137: d:\\file.txt
make: "directive-for.mk" line 140: ( ( ( make: "directive-for.mk" line 148: ( ( (
make: "directive-for.mk" line 140: [ [ [ make: "directive-for.mk" line 148: [ [ [
make: "directive-for.mk" line 140: { { { make: "directive-for.mk" line 148: { { {
make: "directive-for.mk" line 140: ) ) ) make: "directive-for.mk" line 148: ) ) )
make: "directive-for.mk" line 140: ] ] ] make: "directive-for.mk" line 148: ] ] ]
make: "directive-for.mk" line 140: } } } make: "directive-for.mk" line 148: } } }
make: "directive-for.mk" line 140: (()) (()) (()) make: "directive-for.mk" line 148: (()) (()) (())
make: "directive-for.mk" line 140: [[]] [[]] [[]] make: "directive-for.mk" line 148: [[]] [[]] [[]]
make: "directive-for.mk" line 140: {{}} {{}} {{}} make: "directive-for.mk" line 148: {{}} {{}} {{}}
make: "directive-for.mk" line 140: )( )( )( make: "directive-for.mk" line 148: )( )( )(
make: "directive-for.mk" line 140: ][ ][ ][ make: "directive-for.mk" line 148: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{ make: "directive-for.mk" line 148: }{ }{ }{
make: "directive-for.mk" line 148: outer value value make: "directive-for.mk" line 168: invalid character ':' in .for loop variable name
make: "directive-for.mk" line 148: outer "quoted" \"quoted\" make: "directive-for.mk" line 175: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 154: Unknown modifier "Z" make: "directive-for.mk" line 187: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 155: XXX: Not reached word1 make: "directive-for.mk" line 198: Unknown modifier "Z"
make: "directive-for.mk" line 155: XXX: Not reached word3 make: "directive-for.mk" line 199: XXX: Not reached word1
make: "directive-for.mk" line 160: no iteration variables in for make: "directive-for.mk" line 199: XXX: Not reached word3
make: "directive-for.mk" line 162: Missing argument for ".error" make: "directive-for.mk" line 206: no iteration variables in for
make: "directive-for.mk" line 163: for-less endfor make: "directive-for.mk" line 232: 1 open conditional
make: "directive-for.mk" line 187: 1 open conditional make: "directive-for.mk" line 248: for-less endfor
make: "directive-for.mk" line 203: for-less endfor make: "directive-for.mk" line 249: if-less endif
make: "directive-for.mk" line 204: if-less endif make: "directive-for.mk" line 257: if-less endif
make: "directive-for.mk" line 212: if-less endif For: new loop 2
For: end for 2
For: end for 1 For: end for 1
For: loop body: For: loop body:
.\ .\
for inner in i for inner in i
.\ .\
endfor endfor
make: "directive-for.mk" line 229: Unexpected end of file in .for loop For: end for 1
For: loop body: For: loop body:
.\ make: "directive-for.mk" line 305: newline-item=(a)
endfor
make: "directive-for.mk" line 227: for-less endfor
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View file

@ -1,4 +1,4 @@
# $NetBSD: directive-for.mk,v 1.15 2022/10/01 09:23:04 rillig Exp $ # $NetBSD: directive-for.mk,v 1.20 2023/05/10 13:03:06 rillig Exp $
# #
# Tests for the .for directive. # Tests for the .for directive.
# #
@ -8,11 +8,15 @@
# .for _FILE_ in values # .for _FILE_ in values
# .for .FILE. in values # .for .FILE. in values
# .for _f_ in values # .for _f_ in values
# Using the .for loop, lists of values can be produced.
# In simple cases, the :@var@${var}@ variable modifier can be used to
# achieve the same effects.
# #
# See also:
# varmod-loop.mk The ':@var@...@' modifier
# expect-all
# A typical use case for a .for loop is to populate a variable with a list of
# values depending on other variables. In simple cases, the same effect can
# be achieved using the ':@var@${var}@' modifier.
.undef NUMBERS .undef NUMBERS
.for num in 1 2 3 .for num in 1 2 3
NUMBERS+= ${num} NUMBERS+= ${num}
@ -21,8 +25,9 @@ NUMBERS+= ${num}
. error . error
.endif .endif
# The .for loop also works for multiple iteration variables. # The .for loop also works for multiple iteration variables.
# This is something that the variable modifier :@ cannot do. # This is something that the modifier :@ cannot do.
.for name value in VARNAME value NAME2 value2 .for name value in VARNAME value NAME2 value2
${name}= ${value} ${name}= ${value}
.endfor .endfor
@ -30,12 +35,12 @@ ${name}= ${value}
. error . error
.endif .endif
# The .for loop splits the items at whitespace, taking quotes into account, # The .for loop splits the items at whitespace, taking quotes into account,
# just like the :M or :S variable modifiers. # just like the :M or :S modifiers.
#
# Until 2012-06-03, it had split the items exactly at whitespace, without
# taking the quotes into account. This had resulted in 10 words.
# #
# Until 2012-06-03, the .for loop had split the items exactly at whitespace,
# without taking the quotes into account. This had resulted in 10 words.
.undef WORDS .undef WORDS
.for var in one t\ w\ o "three three" 'four four' `five six` .for var in one t\ w\ o "three three" 'four four' `five six`
WORDS+= counted WORDS+= counted
@ -44,16 +49,19 @@ WORDS+= counted
. error . error
.endif .endif
# In the body of the .for loop, the iteration variables can be accessed # In the body of the .for loop, the iteration variables can be accessed
# like normal variables, even though they are not really variables. # like normal variables, even though they are not really variables.
# #
# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so # Instead, before interpreting the body of the .for loop, the body is
# on, before the loop body is evaluated. # generated by replacing each expression ${var} with ${:U1}, ${:U2} and so
# on.
# #
# A notable effect of this implementation technique is that the .for # A noticeable effect of this implementation technique is that the .for
# iteration variables and the normal global variables live in separate # iteration variables and the normal global variables live in separate
# namespaces and do not influence each other. # namespaces and do not influence each other. The "scope" of the .for loop
# # variables is restricted to the current makefile, it does not reach over to
# any included makefiles.
var= value before var= value before
var2= value before var2= value before
.for var var2 in 1 2 3 4 .for var var2 in 1 2 3 4
@ -66,9 +74,8 @@ var2= value before
.endif .endif
# Everything from the paragraph above also applies if the loop body is # Everything from the paragraph above also applies if the loop body is
# empty, even if there is no actual iteration since the loop items are # empty. In this particular example, the items to be iterated are empty as
# also empty. # well.
#
var= value before var= value before
var2= value before var2= value before
.for var var2 in ${:U} .for var var2 in ${:U}
@ -82,11 +89,13 @@ var2= value before
# Until 2008-12-21, the values of the iteration variables were simply # Until 2008-12-21, the values of the iteration variables were simply
# inserted as plain text and then parsed as usual, which made it possible # inserted as plain text and then parsed as usual, which made it possible
# to achieve all kinds of strange effects. # to achieve all kinds of strange effects, such as generating '.if'
# directives or inserting '$' characters in random places, thereby changing
# how following '$' are interpreted.
# #
# Before that date, the .for loop expanded to: # Before that date, the .for loop below expanded to:
# EXPANSION+= value # EXPANSION+= value
# Since that date, the .for loop expands to: # Since that date, the .for loop below expands to:
# EXPANSION${:U+}= value # EXPANSION${:U+}= value
# #
EXPANSION= before EXPANSION= before
@ -102,13 +111,16 @@ EXPANSION${plus}= value
.endif .endif
# When the outer .for loop is expanded, it sees the expression ${i} and # When the outer .for loop is expanded, it sees the expression ${i} and
# expands it. The inner loop then has nothing more to expand. # expands it. The inner loop then only sees the expression ${:Uouter} and
# has nothing more to expand.
.for i in outer .for i in outer
. for i in inner . for i in inner
# expect+1: outer
. info ${i} . info ${i}
. endfor . endfor
.endfor .endfor
# From https://gnats.netbsd.org/29985. # From https://gnats.netbsd.org/29985.
# #
# Until 2008-12-21, the .for loop was expanded by replacing the variable # Until 2008-12-21, the .for loop was expanded by replacing the variable
@ -121,17 +133,13 @@ EXPANSION${plus}= value
# like "a:\ a:\file.txt" that ended in a single backslash. Since then, the # like "a:\ a:\file.txt" that ended in a single backslash. Since then, the
# variable values have been replaced with expressions of the form ${:U...}, # variable values have been replaced with expressions of the form ${:U...},
# which are not interpreted as code anymore. # which are not interpreted as code anymore.
#
# As of 2020-09-22, a comment in for.c says that it may be possible to
# produce an "unwanted substitution", but there is no demonstration code yet.
#
# The above changes prevent a backslash at the end of a word from being
# interpreted as part of the code. Because of this, the trailingBackslash
# hack in Var_Subst is no longer needed and as of 2020-09-22, has been
# removed.
.for path in a:\ a:\file.txt d:\\ d:\\file.txt .for path in a:\ a:\file.txt d:\\ d:\\file.txt
. info ${path} . info ${path}
.endfor .endfor
# expect-2: a:\ a:\file.txt
# expect-3: d:\\
# expect-4: d:\\file.txt
# Ensure that braces and parentheses are properly escaped by the .for loop. # Ensure that braces and parentheses are properly escaped by the .for loop.
# Each line must print the same word 3 times. # Each line must print the same word 3 times.
@ -139,28 +147,65 @@ EXPANSION${plus}= value
.for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{ .for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{
. info $v ${v} $(v) . info $v ${v} $(v)
.endfor .endfor
# expect-02: ( ( (
# expect-03: [ [ [
# expect-04: { { {
# expect-05: ) ) )
# expect-06: ] ] ]
# expect-07: } } }
# expect-08: (()) (()) (())
# expect-09: [[]] [[]] [[]]
# expect-10: {{}} {{}} {{}}
# expect-11: )( )( )(
# expect-12: ][ ][ ][
# expect-13: }{ }{ }{
# As of 2020-10-25, the variable names may contain arbitrary characters, # Before 2023-05-09, the variable names could contain arbitrary characters,
# except for whitespace. This allows for creative side effects. Hopefully # except for whitespace, allowing for creative side effects, as usual for
# nobody is misusing this "feature". # arbitrary code injection.
var= outer var= outer
# expect+1: invalid character ':' in .for loop variable name
.for var:Q in value "quoted" .for var:Q in value "quoted"
. info ${var} ${var:Q} ${var:Q:Q} . info <${var}> <${var:Q}> <${var:Q:Q}>
.endfor
# Before 2023-05-09, when variable names could contain '$', the short
# expression '$$' was preserved, the long expressions were substituted.
# expect+1: invalid character '$' in .for loop variable name
.for $ in value
. info <$$> <${$}> <$($)>
.endfor
# https://gnats.netbsd.org/53146 mentions the idea of using a dynamic
# variable name in .for loops, based on some other variable. The .for loops
# are already tricky enough to understand in detail, even without this
# possibility, therefore the variable names are restricted to using harmless
# characters only.
INDIRECT= direct
# expect+1: invalid character '$' in .for loop variable name
.for $(INDIRECT) in value
# If the variable name could be chosen dynamically, the iteration variable
# might have been 'direct', thereby expanding the expression '${direct}'.
. info <$(INDIRECT)> <$(direct)> <$($(INDIRECT))>
.endfor .endfor
# XXX: A parse error or evaluation error in the items of the .for loop # XXX: A parse error or evaluation error in the items of the .for loop
# should skip the whole loop. As of 2020-12-27, the loop is expanded twice. # should skip the whole loop. As of 2023-05-09, the loop is expanded as
# usual.
# expect+1: Unknown modifier "Z"
.for var in word1 ${:Uword2:Z} word3 .for var in word1 ${:Uword2:Z} word3
. info XXX: Not reached ${var} . info XXX: Not reached ${var}
.endfor .endfor
# expect-2: XXX: Not reached word1
# expect-3: XXX: Not reached word3
# An empty list of variables to the left of the 'in' is a parse error. # 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 .for in value # expect+0: no iteration variables in for
# XXX: The loop body is evaluated once, even with the parse error above. . error
. error # expect+0: Missing argument for ".error" .endfor
.endfor # expect+0: for-less endfor
# An empty list of iteration values to the right of the 'in' is accepted. # An empty list of iteration values to the right of the 'in' is accepted.
# Unlike in the shell, it is not a parse error. # Unlike in the shell, it is not a parse error.
@ -214,12 +259,19 @@ var= outer
.endif # no 'if-less endif' .endif # no 'if-less endif'
# When make parses a .for loop, it assumes that there is no line break between # Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it
# the '.' and the 'for' or 'endfor', as there is no practical reason to break # assumed that there was no line continuation between the '.' and the 'for'
# the line at this point. When make scans the outer .for loop, it does not # or 'endfor', as there is no practical reason to break the line at this
# recognize the inner directives as such. When make scans the inner .for # point.
# loop, it recognizes the '.\n for' but does not recognize the '.\n endfor', #
# as LK_FOR_BODY preserves the backslash-newline sequences. # When make scanned the outer .for loop, it did not recognize the inner .for
# loop as such and instead treated it as an unknown directive. The body of
# the outer .for loop thus ended above the '.endfor'.
#
# When make scanned the inner .for loop, it did not recognize the inner
# .endfor as such, which led to a parse error 'Unexpected end of file in .for
# loop' from the '.endfor' line, followed by a second parse error 'for-less
# .endfor' from the '.\\n endfor' line.
.MAKEFLAGS: -df .MAKEFLAGS: -df
.for outer in o .for outer in o
.\ .\
@ -244,3 +296,12 @@ var= outer
. error . error
. endif . endif
.endfor .endfor
# Since at least 1993, iteration stops at the first newline.
# Back then, the .newline variable didn't exist, therefore it was unlikely
# that a newline ever occurred.
.for var in a${.newline}b${.newline}c
. info newline-item=(${var})
.endfor
# expect-2: newline-item=(a)

View file

@ -1,20 +0,0 @@
make: "forloop.mk" line 14: x=one
make: "forloop.mk" line 14: x="two and three"
make: "forloop.mk" line 14: x=four
make: "forloop.mk" line 14: x="five"
make: "forloop.mk" line 20: x=-I/this
make: "forloop.mk" line 20: x=-I"This or that"
make: "forloop.mk" line 20: x=-Ithat
make: "forloop.mk" line 20: x="-DTHIS=\"this and that\""
make: "forloop.mk" line 27: cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
make: "forloop.mk" line 41: newline-item=(a)
make: "forloop.mk" line 47: a=one b="two and three"
make: "forloop.mk" line 47: a=four b="five"
make: "forloop.mk" line 47: a=ONE b="TWO AND THREE"
make: "forloop.mk" line 47: a=FOUR b="FIVE"
We expect an error next:
make: "forloop.mk" line 46: Wrong number of words (9) in .for substitution list with 2 variables
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
OK
exit status 0

View file

@ -1,53 +0,0 @@
# $NetBSD: forloop.mk,v 1.7 2020/11/03 17:37:57 rillig Exp $
all: for-loop
LIST= one "two and three" four "five"
.if make(for-fail)
for-fail:
XTRA_LIST= xtra
.else
. for x in ${LIST}
. info x=$x
. endfor
CFL= -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
cfl=
. for x in ${CFL}
. info x=$x
. if empty(cfl)
cfl= $x
. else
cfl+= $x
. endif
. endfor
. info cfl=${cfl}
. if ${cfl} != ${CFL}
. error ${.newline}${cfl} != ${.newline}${CFL}
. endif
. for a b in ${EMPTY}
. info a=$a b=$b
. endfor
# Since at least 1993, iteration stops at the first newline.
# Back then, the .newline variable didn't exist, therefore it was unlikely
# that a newline ever occurred.
. for var in a${.newline}b${.newline}c
. info newline-item=(${var})
. endfor
.endif # for-fail
.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST}
. info a=$a b=$b
.endfor
for-loop:
@echo We expect an error next:
@(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \
{ echo "Oops that should have failed!"; exit 1; } || echo OK

View file

@ -1,4 +1,4 @@
# $NetBSD: parse.mk,v 1.3 2022/07/24 20:25:23 rillig Exp $ # $NetBSD: parse.mk,v 1.4 2023/04/28 13:09:48 rillig Exp $
# #
# Test those parts of the parsing that do not belong in any of the other # Test those parts of the parsing that do not belong in any of the other
# categories. # categories.
@ -22,3 +22,33 @@
# #
# https://bugs.freebsd.org/265119 # https://bugs.freebsd.org/265119
one-target ${:U } one-target ${:U }
# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from
# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for
# a variable assignment in a dependency line with trailing whitespace. Lines
# without trailing whitespace were not affected. Global variable assignments
# were guaranteed to have no trailing whitespace and were thus not affected.
#
# Try to reproduce some variants that may lead to a crash, depending on the
# memory allocator. To get a crash, the terminating '\0' of the line must be
# the last byte of a memory page. The expression '${:U}' forces this trailing
# whitespace.
# On FreeBSD x86_64, a crash could in some cases be forced using the following
# line, which has length 47, and if the memory for the expanded line starts at
# 0xXXXX_XXd0, the terminating '\0' may end up at 0xXXXX_Xfff:
Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U}
# The following line has length 4095 after being expanded, so line[4095] ==
# '\0'. If the line is
# allocated on a page boundary and the following page is not mapped, this line
# leads to a segmentation fault.
${:U:range=511:@_@1234567@:ts.}: 12345 ${:U}
# The following line has length 8191, so line[8191] == '\0'. If the line is
# allocated on a page boundary and the following page is not mapped, this line
# leads to a segmentation fault.
${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U}
12345:

View file

@ -1,9 +1,5 @@
make: "var-eval-short.mk" line 44: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar make: "var-eval-short.mk" line 44: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar
make: "var-eval-short.mk" line 44: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@}) make: "var-eval-short.mk" line 44: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
make: "var-eval-short.mk" line 84: Invalid time value at "${FAIL}}"
make: "var-eval-short.mk" line 84: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
make: "var-eval-short.mk" line 98: Invalid time value at "${FAIL}}"
make: "var-eval-short.mk" line 98: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else} CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only) Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...} Parsing modifier ${0:?...}
@ -12,7 +8,7 @@ Modifier part: "${FAIL}then"
Var_Parse: ${FAIL}else} (parse-only) Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else" Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined) Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
Parsing line 163: DEFINED= defined Parsing line 165: DEFINED= defined
Global: DEFINED = defined Global: DEFINED = defined
CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else} CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only) Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
@ -24,7 +20,7 @@ Modifier part: "${FAIL}then"
Var_Parse: ${FAIL}else} (parse-only) Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else" Modifier part: "${FAIL}else"
Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular) Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
Parsing line 166: .MAKEFLAGS: -d0 Parsing line 168: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0) ParseDependency(.MAKEFLAGS: -d0)
Global: .MAKEFLAGS = -r -k -d cpv -d Global: .MAKEFLAGS = -r -k -d cpv -d
Global: .MAKEFLAGS = -r -k -d cpv -d 0 Global: .MAKEFLAGS = -r -k -d cpv -d 0

View file

@ -1,4 +1,4 @@
# $NetBSD: var-eval-short.mk,v 1.8 2021/12/27 18:54:19 rillig Exp $ # $NetBSD: var-eval-short.mk,v 1.9 2023/05/09 16:27:00 rillig Exp $
# #
# Tests for each variable modifier to ensure that they only do the minimum # Tests for each variable modifier to ensure that they only do the minimum
# necessary computations. If the result of the expression is irrelevant, # necessary computations. If the result of the expression is irrelevant,
@ -79,8 +79,9 @@ DEFINED= # defined
.if 0 && ${:Uword:E} .if 0 && ${:Uword:E}
.endif .endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since # Before var.c 1.1050 from 2023-05-09, the ':gmtime' modifier produced the
# ':gmtime' does not expand its argument. # error message 'Invalid time value: ${FAIL}}' since it did not expand its
# argument.
.if 0 && ${:Uword:gmtime=${FAIL}} .if 0 && ${:Uword:gmtime=${FAIL}}
.endif .endif
@ -93,8 +94,9 @@ DEFINED= # defined
.if 0 && ${value:L} .if 0 && ${value:L}
.endif .endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since # Before var.c 1.1050 from 2023-05-09, the ':localtime' modifier produced the
# ':localtime' does not expand its argument. # error message 'Invalid time value: ${FAIL}}' since it did not expand its
# argument.
.if 0 && ${:Uword:localtime=${FAIL}} .if 0 && ${:Uword:localtime=${FAIL}}
.endif .endif

View file

@ -1,5 +1,5 @@
Global: .ALLTARGETS = one Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one
Global: .ALLTARGETS = one two Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one two
Var_Parse: ${.MAKE.TARGET_LOCAL_VARIABLES} (eval) Var_Parse: ${.MAKE.TARGET_LOCAL_VARIABLES} (eval)
Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
@ -7,6 +7,56 @@ Global: one two = # (empty)
Global: one two = three Global: one two = three
Global: .MAKEFLAGS = -r -k -d v -d Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0 Global: .MAKEFLAGS = -r -k -d v -d 0
target-rule.ext: @ = <target-rule.ext>
target-rule.ext: % = <undefined>
target-rule.ext: ? = <>
target-rule.ext: < = <undefined>
target-rule.ext: * = <target-rule.ext>
dir/subdir/target-rule.ext: @ = <dir/subdir/target-rule.ext>
dir/subdir/target-rule.ext: % = <undefined>
dir/subdir/target-rule.ext: ? = <>
dir/subdir/target-rule.ext: < = <undefined>
dir/subdir/target-rule.ext: * = <dir/subdir/target-rule.ext>
target-rule.ir-gen-from: @ = <target-rule.ir-gen-from>
target-rule.ir-gen-from: % = <undefined>
target-rule.ir-gen-from: ? = <>
target-rule.ir-gen-from: < = <undefined>
target-rule.ir-gen-from: * = <target-rule>
dir/subdir/target-rule-dir.ir-gen-from: @ = <dir/subdir/target-rule-dir.ir-gen-from>
dir/subdir/target-rule-dir.ir-gen-from: % = <undefined>
dir/subdir/target-rule-dir.ir-gen-from: ? = <>
dir/subdir/target-rule-dir.ir-gen-from: < = <undefined>
dir/subdir/target-rule-dir.ir-gen-from: * = <dir/subdir/target-rule-dir>
inference-rule.ir-to: @ = <inference-rule.ir-to>
inference-rule.ir-to: % = <undefined>
inference-rule.ir-to: ? = <inference-rule.ir-from>
inference-rule.ir-to: < = <inference-rule.ir-from>
inference-rule.ir-to: * = <inference-rule>
dir/subdir/inference-rule.ir-to: @ = <dir/subdir/inference-rule.ir-to>
dir/subdir/inference-rule.ir-to: % = <undefined>
dir/subdir/inference-rule.ir-to: ? = <dir/subdir/inference-rule.ir-from>
dir/subdir/inference-rule.ir-to: < = <dir/subdir/inference-rule.ir-from>
dir/subdir/inference-rule.ir-to: * = <dir/subdir/inference-rule>
inference-rule-chain.ir-from: @ = <inference-rule-chain.ir-from>
inference-rule-chain.ir-from: % = <undefined>
inference-rule-chain.ir-from: ? = <inference-rule-chain.ir-gen-from>
inference-rule-chain.ir-from: < = <inference-rule-chain.ir-gen-from>
inference-rule-chain.ir-from: * = <inference-rule-chain>
inference-rule-chain.ir-to: @ = <inference-rule-chain.ir-to>
inference-rule-chain.ir-to: % = <undefined>
inference-rule-chain.ir-to: ? = <inference-rule-chain.ir-from>
inference-rule-chain.ir-to: < = <inference-rule-chain.ir-from>
inference-rule-chain.ir-to: * = <inference-rule-chain>
dir/subdir/inference-rule-chain.ir-from: @ = <dir/subdir/inference-rule-chain.ir-from>
dir/subdir/inference-rule-chain.ir-from: % = <undefined>
dir/subdir/inference-rule-chain.ir-from: ? = <dir/subdir/inference-rule-chain.ir-gen-from>
dir/subdir/inference-rule-chain.ir-from: < = <dir/subdir/inference-rule-chain.ir-gen-from>
dir/subdir/inference-rule-chain.ir-from: * = <dir/subdir/inference-rule-chain>
dir/subdir/inference-rule-chain.ir-to: @ = <dir/subdir/inference-rule-chain.ir-to>
dir/subdir/inference-rule-chain.ir-to: % = <undefined>
dir/subdir/inference-rule-chain.ir-to: ? = <dir/subdir/inference-rule-chain.ir-from>
dir/subdir/inference-rule-chain.ir-to: < = <dir/subdir/inference-rule-chain.ir-from>
dir/subdir/inference-rule-chain.ir-to: * = <dir/subdir/inference-rule-chain>
: Making var-scope-local.c out of nothing. : Making var-scope-local.c out of nothing.
: Making var-scope-local.o from var-scope-local.c. : Making var-scope-local.o from var-scope-local.c.
: Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".". : Making basename "var-scope-local.o" in "." from "var-scope-local.c" in ".".

View file

@ -1,4 +1,4 @@
# $NetBSD: var-scope-local.mk,v 1.5 2022/02/09 21:09:24 rillig Exp $ # $NetBSD: var-scope-local.mk,v 1.7 2023/04/29 10:16:24 rillig Exp $
# #
# Tests for target-local variables, such as ${.TARGET} or $@. These variables # Tests for target-local variables, such as ${.TARGET} or $@. These variables
# are relatively short-lived as they are created just before making the # are relatively short-lived as they are created just before making the
@ -12,6 +12,64 @@
.MAIN: all .MAIN: all
# Target-local variables in a target rule
#
# In target rules, '$*' only strips the extension off the pathname if the
# extension is listed in '.SUFFIXES'.
#
# expect: target-rule.ext: * = <target-rule.ext>
all: target-rule.ext dir/subdir/target-rule.ext
target-rule.ext dir/subdir/target-rule.ext: .PHONY
@echo '$@: @ = <${@:Uundefined}>'
@echo '$@: % = <${%:Uundefined}>'
@echo '$@: ? = <${?:Uundefined}>'
@echo '$@: < = <${<:Uundefined}>'
@echo '$@: * = <${*:Uundefined}>'
.SUFFIXES: .ir-gen-from .ir-from .ir-to
# In target rules, '$*' strips the extension off the pathname of the target
# if the extension is listed in '.SUFFIXES'.
#
# expect: target-rule.ir-gen-from: * = <target-rule>
all: target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from
target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from:
@echo '$@: @ = <${@:Uundefined}>'
@echo '$@: % = <${%:Uundefined}>'
@echo '$@: ? = <${?:Uundefined}>'
@echo '$@: < = <${<:Uundefined}>'
@echo '$@: * = <${*:Uundefined}>'
.ir-from.ir-to:
@echo '$@: @ = <${@:Uundefined}>'
@echo '$@: % = <${%:Uundefined}>'
@echo '$@: ? = <${?:Uundefined}>'
@echo '$@: < = <${<:Uundefined}>'
@echo '$@: * = <${*:Uundefined}>'
.ir-gen-from.ir-from:
@echo '$@: @ = <${@:Uundefined}>'
@echo '$@: % = <${%:Uundefined}>'
@echo '$@: ? = <${?:Uundefined}>'
@echo '$@: < = <${<:Uundefined}>'
@echo '$@: * = <${*:Uundefined}>'
# Target-local variables in an inference rule
all: inference-rule.ir-to dir/subdir/inference-rule.ir-to
inference-rule.ir-from: .PHONY
dir/subdir/inference-rule.ir-from: .PHONY
# Target-local variables in a chain of inference rules
all: inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to
inference-rule-chain.ir-gen-from: .PHONY
dir/subdir/inference-rule-chain.ir-gen-from: .PHONY
# The run-time 'check' directives from above happen after the parse-time
# 'check' directives from below.
#
# expect-reset
# Deferred evaluation during parsing
#
# The target-local variables can be used in expressions, just like other # The target-local variables can be used in expressions, just like other
# variables. When these expressions are evaluated outside of a target, these # variables. When these expressions are evaluated outside of a target, these
# expressions are not yet expanded, instead their text is preserved, to allow # expressions are not yet expanded, instead their text is preserved, to allow
@ -20,8 +78,8 @@
# #
# Conditions from .if directives are evaluated in the scope of the command # Conditions from .if directives are evaluated in the scope of the command
# line, which means that variables from the command line, from the global # line, which means that variables from the command line, from the global
# scope and from the environment are resolved, in this order (but see the # scope and from the environment are resolved, in this precedence order (but
# command line option '-e'). In that phase, expressions involving # see the command line option '-e'). In that phase, expressions involving
# target-local variables need to be preserved, including the exact names of # target-local variables need to be preserved, including the exact names of
# the variables. # the variables.
# #
@ -77,13 +135,17 @@
.endif .endif
# Custom local variables
#
# Additional target-local variables may be defined in dependency lines. # Additional target-local variables may be defined in dependency lines.
.MAKEFLAGS: -dv .MAKEFLAGS: -dv
# In the following line, the ':=' may either be interpreted as an assignment # In the following line, the ':=' may either be interpreted as an assignment
# operator or as the dependency operator ':', followed by an empty variable # operator or as the dependency operator ':', followed by an empty variable
# name and the assignment operator '='. It is the latter since in an # name and the assignment operator '='. It is the latter since in an
# assignment, the left-hand side must be at most a single word. The empty # assignment, the left-hand side must be a single word or empty.
# variable name is expanded twice, once for 'one' and once for 'two'. #
# The empty variable name is expanded twice, once for 'one' and once for
# 'two'.
# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored # expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored # expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
one two:=three one two:=three
@ -205,32 +267,3 @@ a_use: .USE VAR=use
all: var-scope-local-use.o all: var-scope-local-use.o
var-scope-local-use.o: a_use var-scope-local-use.o: a_use
# Since parse.c 1.656 from 2022-01-27 and before parse.c 1.662 from
# 2022-02-05, there was an out-of-bounds read in Parse_IsVar when looking for
# a variable assignment in a dependency line with trailing whitespace. Lines
# without trailing whitespace were not affected. Global variable assignments
# were guaranteed to have no trailing whitespace and were thus not affected.
#
# Try to reproduce some variants that may lead to a crash, depending on the
# memory allocator. To get a crash, the terminating '\0' of the line must be
# the last byte of a memory page. The expression '${:U}' forces this trailing
# whitespace.
# On FreeBSD x86_64, a crash could in some cases be forced using the following
# line, which has length 47, so the terminating '\0' may end up at an address
# of the form 0xXXXX_XXXX_XXXX_Xfff:
Try_to_crash_FreeBSD.xxxxxxxxxxxxxxxxxx: 12345 ${:U}
# The following line has length 4095, so line[4095] == '\0'. If the line is
# allocated on a page boundary and the following page is not mapped, this line
# leads to a segmentation fault.
${:U:range=511:@_@1234567@:ts.}: 12345 ${:U}
# The following line has length 8191, so line[8191] == '\0'. If the line is
# allocated on a page boundary and the following page is not mapped, this line
# leads to a segmentation fault.
${:U:range=1023:@_@1234567@:ts.}: 12345 ${:U}
12345:

View file

@ -1,13 +1,13 @@
make: "varmod-gmtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}"" make: "varmod-gmtime.mk" line 59: Invalid time value "-1"
make: "varmod-gmtime.mk" line 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}") make: "varmod-gmtime.mk" line 59: Malformed conditional (${:L:gmtime=-1} != "")
make: "varmod-gmtime.mk" line 67: Invalid time value at "-1} != """ make: "varmod-gmtime.mk" line 68: Invalid time value " 1"
make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "") make: "varmod-gmtime.mk" line 68: Malformed conditional (${:L:gmtime= 1} != "")
make: "varmod-gmtime.mk" line 76: Invalid time value at " 1} != """ make: "varmod-gmtime.mk" line 114: Invalid time value "10000000000000000000000000000000"
make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "") make: "varmod-gmtime.mk" line 114: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
make: "varmod-gmtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """ make: "varmod-gmtime.mk" line 125: Invalid time value "error"
make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") make: "varmod-gmtime.mk" line 125: Malformed conditional (${:L:gmtime=error} != "")
make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-gmtime.mk" line 134: Invalid time value "100000S,1970,bad,"
make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "") make: "varmod-gmtime.mk" line 134: Malformed conditional (${%Y:L:gmtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View file

@ -1,7 +1,10 @@
# $NetBSD: varmod-gmtime.mk,v 1.10 2021/01/19 05:26:34 rillig Exp $ # $NetBSD: varmod-gmtime.mk,v 1.14 2023/05/10 15:53:32 rillig Exp $
# #
# Tests for the :gmtime variable modifier, which formats a timestamp # Tests for the :gmtime variable modifier, which formats a timestamp
# using strftime(3) in UTC. # using strftime(3) in UTC.
#
# See also:
# varmod-localtime.mk
.if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile .if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile
. error . error
@ -41,20 +44,9 @@
.endif .endif
# As of 2020-08-16, it is not possible to pass the seconds via a # Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
# variable expression. This is because parsing of the :gmtime # seconds via a variable expression.
# modifier stops at the '$' and returns to ApplyModifiers. .if ${%Y:L:gmtime=${:U1593536400}} != "2020"
#
# There, a colon would be skipped but not a dollar.
# Parsing therefore continues at the '$' of the ${:U159...}, looking
# for an ordinary variable modifier.
#
# At this point, the ${:U} is expanded and interpreted as a variable
# modifier, which results in the error message "Unknown modifier '1'".
#
# If ApplyModifier_Gmtime were to pass its argument through
# ParseModifierPart, this would work.
.if ${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}"
. error . error
.endif .endif
@ -75,6 +67,8 @@
# because it would make sense but just as a side-effect from using strtoul. # because it would make sense but just as a side-effect from using strtoul.
.if ${:L:gmtime= 1} != "" .if ${:L:gmtime= 1} != ""
. error . error
.else
. error
.endif .endif
@ -115,7 +109,8 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of # ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# the second before 1970. # the second before 1970.
# #
# Since var.c 1.631, the overflow is detected and produces a parse error. # Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a
# parse error.
.if ${:L:gmtime=10000000000000000000000000000000} != "" .if ${:L:gmtime=10000000000000000000000000000000} != ""
. error . error
.else .else
@ -133,5 +128,11 @@
. error . error
.endif .endif
# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly
# followed by the next modifier, without a ':' separator. This was the same
# bug as for the ':L' and ':P' modifiers.
.if ${%Y:L:gmtime=100000S,1970,bad,} != "bad"
. error
.endif
all: all:

View file

@ -1,13 +1,13 @@
make: "varmod-localtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}"" make: "varmod-localtime.mk" line 59: Invalid time value "-1"
make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}") make: "varmod-localtime.mk" line 59: Malformed conditional (${:L:localtime=-1} != "")
make: "varmod-localtime.mk" line 67: Invalid time value at "-1} != """ make: "varmod-localtime.mk" line 68: Invalid time value " 1"
make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "") make: "varmod-localtime.mk" line 68: Malformed conditional (${:L:localtime= 1} != "")
make: "varmod-localtime.mk" line 76: Invalid time value at " 1} != """ make: "varmod-localtime.mk" line 114: Invalid time value "10000000000000000000000000000000"
make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "") make: "varmod-localtime.mk" line 114: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
make: "varmod-localtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """ make: "varmod-localtime.mk" line 125: Invalid time value "error"
make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") make: "varmod-localtime.mk" line 125: Malformed conditional (${:L:localtime=error} != "")
make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-localtime.mk" line 134: Invalid time value "100000S,1970,bad,"
make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "") make: "varmod-localtime.mk" line 134: Malformed conditional (${%Y:L:localtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View file

@ -1,7 +1,10 @@
# $NetBSD: varmod-localtime.mk,v 1.8 2021/01/19 05:26:34 rillig Exp $ # $NetBSD: varmod-localtime.mk,v 1.12 2023/05/10 15:53:32 rillig Exp $
# #
# Tests for the :localtime variable modifier, which formats a timestamp # Tests for the :localtime variable modifier, which formats a timestamp
# using strftime(3) in local time. # using strftime(3) in local time.
#
# See also:
# varmod-gmtime.mk
.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile .if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile
. error . error
@ -41,20 +44,9 @@
.endif .endif
# As of 2020-08-16, it is not possible to pass the seconds via a # Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
# variable expression. This is because parsing of the :localtime # seconds via a variable expression.
# modifier stops at the '$' and returns to ApplyModifiers. .if ${%Y:L:localtime=${:U1593536400}} != "2020"
#
# There, a colon would be skipped but not a dollar.
# Parsing therefore continues at the '$' of the ${:U159...}, looking
# for an ordinary variable modifier.
#
# At this point, the ${:U} is expanded and interpreted as a variable
# modifier, which results in the error message "Unknown modifier '1'".
#
# If ApplyModifier_Localtime were to pass its argument through
# ParseModifierPart, this would work.
.if ${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}"
. error . error
.endif .endif
@ -75,6 +67,8 @@
# because it would make sense but just as a side-effect from using strtoul. # because it would make sense but just as a side-effect from using strtoul.
.if ${:L:localtime= 1} != "" .if ${:L:localtime= 1} != ""
. error . error
.else
. error
.endif .endif
@ -115,7 +109,8 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of # ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# the second before 1970. # the second before 1970.
# #
# Since var.c 1.631, the overflow is detected and produces a parse error. # Since var.c 1.631 from 2020-10-31, the overflow is detected and produces a
# parse error.
.if ${:L:localtime=10000000000000000000000000000000} != "" .if ${:L:localtime=10000000000000000000000000000000} != ""
. error . error
.else .else
@ -133,5 +128,11 @@
. error . error
.endif .endif
# Before var.c 1.1050 from 2023-05-09, the timestamp could be directly
# followed by the next modifier, without a ':' separator. This was the same
# bug as for the ':L' and ':P' modifiers.
.if ${%Y:L:localtime=100000S,1970,bad,} != "bad"
. error
.endif
all: all:

View file

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

View file

@ -0,0 +1,30 @@
# $NetBSD: varmod-mtime.mk,v 1.1 2023/05/09 20:14:27 sjg Exp $
#
# Tests for the :mtime variable modifier, which provides mtime
# of variable value assumed to be a pathname.
all:
# mtime of this makefile
mtime:= ${MAKEFILE:mtime}
# if pathname does not exist and timestamp is provided
# that is the result
.if ${no/such:L:mtime=0} != "0"
. error
.endif
.if ${no/such:L:mtime=42} != "42"
. error
.endif
# if no timestamp is provided and stat(2) fails use current time
.if ${no/such:L:mtime} < ${mtime}
. error no/such:L:mtime ${no/such:L:mtime} < ${mtime}
.endif
COOKIE = ${TMPDIR}/varmod-mtime.cookie
x!= touch ${COOKIE}
.if ${COOKIE:mtime=0} < ${mtime}
. error COOKIE:mtime=0 ${COOKIE:mtime=0} < ${mtime}
.endif

View file

@ -1,4 +1,4 @@
# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $ # $NetBSD: varmod-path.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $
# #
# Tests for the :P variable modifier, which looks up the path for a given # Tests for the :P variable modifier, which looks up the path for a given
# target. # target.
@ -7,11 +7,12 @@
# as of 2020-08-23 it is nevertheless resolved to a path. This is probably # as of 2020-08-23 it is nevertheless resolved to a path. This is probably
# unintended. # unintended.
# #
# The real target is located in a subdirectory, and its full path is returned. # In this test, the real target is located in a subdirectory, and its full
# If it had been in the current directory, the difference between its path and # path is returned. If it had been in the current directory, the difference
# its name would not be visible. # between its path and its name would not be visible.
# #
# The enoent target does not exist, therefore the target name is returned. # The enoent target does not exist, therefore the plain name of the target
# is returned.
.MAIN: all .MAIN: all
@ -20,7 +21,8 @@ _!= mkdir varmod-path.subdir
_!= > varmod-path.subdir/varmod-path.phony _!= > varmod-path.subdir/varmod-path.phony
_!= > varmod-path.subdir/varmod-path.real _!= > varmod-path.subdir/varmod-path.real
# To have an effect, this .PATH declaration must be after the directory is created. # To have an effect, this .PATH declaration must be processed after the
# directory has been created.
.PATH: varmod-path.subdir .PATH: varmod-path.subdir
varmod-path.phony: .PHONY varmod-path.phony: .PHONY

View file

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.1049 2023/03/28 14:39:31 rillig Exp $ */ /* $NetBSD: var.c,v 1.1054 2023/05/10 18:22:33 sjg Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -147,7 +147,7 @@
#include "metachar.h" #include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: var.c,v 1.1049 2023/03/28 14:39:31 rillig Exp $"); MAKE_RCSID("$NetBSD: var.c,v 1.1054 2023/05/10 18:22:33 sjg Exp $");
/* /*
* Variables are defined using one of the VAR=value assignments. Their * Variables are defined using one of the VAR=value assignments. Their
@ -2196,6 +2196,8 @@ ParseModifierPartBalanced(const char **pp, LazyBuf *part)
static bool static bool
ParseModifierPartSubst( ParseModifierPartSubst(
const char **pp, const char **pp,
/* If true, parse up to but excluding the next ':' or ch->endc. */
bool whole,
char delim, char delim,
VarEvalMode emode, VarEvalMode emode,
ModChain *ch, ModChain *ch,
@ -2213,11 +2215,14 @@ ParseModifierPartSubst(
) )
{ {
const char *p; const char *p;
char end1, end2;
p = *pp; p = *pp;
LazyBuf_Init(part, p); LazyBuf_Init(part, p);
while (*p != '\0' && *p != delim) { end1 = whole ? ':' : delim;
end2 = whole ? ch->endc : delim;
while (*p != '\0' && *p != end1 && *p != end2) {
if (IsEscapedModifierPart(p, delim, subst)) { if (IsEscapedModifierPart(p, delim, subst)) {
LazyBuf_Add(part, p[1]); LazyBuf_Add(part, p[1]);
p += 2; p += 2;
@ -2239,15 +2244,15 @@ ParseModifierPartSubst(
ParseModifierPartExpr(&p, part, ch, emode); ParseModifierPartExpr(&p, part, ch, emode);
} }
if (*p != delim) { *pp = p;
*pp = p; if (*p != end1 && *p != end2) {
Error("Unfinished modifier for \"%s\" ('%c' missing)", Error("Unfinished modifier for \"%s\" ('%c' missing)",
ch->expr->name, delim); ch->expr->name, end2);
LazyBuf_Done(part); LazyBuf_Done(part);
return false; return false;
} }
if (!whole)
*pp = p + 1; (*pp)++;
{ {
Substring sub = LazyBuf_Get(part); Substring sub = LazyBuf_Get(part);
@ -2280,7 +2285,8 @@ ParseModifierPart(
LazyBuf *part LazyBuf *part
) )
{ {
return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL); return ParseModifierPartSubst(pp, false, delim, emode, ch, part,
NULL, NULL);
} }
MAKE_INLINE bool MAKE_INLINE bool
@ -2576,11 +2582,23 @@ ApplyModifier_Time(const char **pp, ModChain *ch)
if (args[0] == '=') { if (args[0] == '=') {
const char *p = args + 1; const char *p = args + 1;
if (!TryParseTime(&p, &t)) { LazyBuf buf;
Parse_Error(PARSE_FATAL, if (!ParseModifierPartSubst(&p, true, '\0', ch->expr->emode,
"Invalid time value at \"%s\"", p); ch, &buf, NULL, NULL))
return AMR_CLEANUP; return AMR_CLEANUP;
} if (ModChain_ShouldEval(ch)) {
Substring arg = LazyBuf_Get(&buf);
const char *arg_p = arg.start;
if (!TryParseTime(&arg_p, &t) || arg_p != arg.end) {
Parse_Error(PARSE_FATAL,
"Invalid time value \"%.*s\"",
(int)Substring_Length(arg), arg.start);
LazyBuf_Done(&buf);
return AMR_CLEANUP;
}
} else
t = 0;
LazyBuf_Done(&buf);
*pp = p; *pp = p;
} else { } else {
t = 0; t = 0;
@ -2823,6 +2841,71 @@ ApplyModifier_Match(const char **pp, ModChain *ch)
return AMR_OK; return AMR_OK;
} }
struct ModifyWord_MtimeArgs {
bool error;
bool fallback;
ApplyModifierResult rc;
time_t t;
};
static void
ModifyWord_Mtime(Substring word, SepBuf *buf, void *data)
{
char tbuf[BUFSIZ];
struct stat st;
struct ModifyWord_MtimeArgs *args = data;
if (Substring_IsEmpty(word))
return;
assert(word.end[0] == '\0'); /* assume null-terminated word */
if (stat(word.start, &st) < 0) {
if (args->error) {
Parse_Error(PARSE_FATAL,
"Cannot determine mtime for '%s': %s",
word.start, strerror(errno));
args->rc = AMR_CLEANUP;
return;
}
if (args->fallback)
st.st_mtime = args->t;
else
time(&st.st_mtime);
}
snprintf(tbuf, sizeof(tbuf), "%u", (unsigned)st.st_mtime);
SepBuf_AddStr(buf, tbuf);
}
/* :mtime */
static ApplyModifierResult
ApplyModifier_Mtime(const char **pp, ModChain *ch)
{
const char *p, *mod = *pp;
struct ModifyWord_MtimeArgs args;
if (!ModMatchEq(mod, "mtime", ch))
return AMR_UNKNOWN;
*pp += 5;
p = *pp;
args.error = args.fallback = false;
args.rc = AMR_OK;
if (p[0] == '=') {
p++;
args.fallback = true;
if (!TryParseTime(&p, &args.t)) {
if (strncmp(p, "error", 5) == 0) {
args.error = true;
p += 5;
} else
return AMR_BAD;
}
*pp = p;
}
if (!ModChain_ShouldEval(ch))
return AMR_OK;
ModifyWords(ch, ModifyWord_Mtime, &args, ch->oneBigWord);
return args.rc;
}
static void static void
ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord) ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
{ {
@ -2870,13 +2953,13 @@ ApplyModifier_Subst(const char **pp, ModChain *ch)
(*pp)++; (*pp)++;
} }
if (!ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf, if (!ParseModifierPartSubst(pp,
&args.pflags, NULL)) false, delim, ch->expr->emode, ch, &lhsBuf, &args.pflags, NULL))
return AMR_CLEANUP; return AMR_CLEANUP;
args.lhs = LazyBuf_Get(&lhsBuf); args.lhs = LazyBuf_Get(&lhsBuf);
if (!ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf, if (!ParseModifierPartSubst(pp,
NULL, &args)) { false, delim, ch->expr->emode, ch, &rhsBuf, NULL, &args)) {
LazyBuf_Done(&lhsBuf); LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP; return AMR_CLEANUP;
} }
@ -3805,6 +3888,8 @@ ApplyModifier(const char **pp, ModChain *ch)
case 'M': case 'M':
case 'N': case 'N':
return ApplyModifier_Match(pp, ch); return ApplyModifier_Match(pp, ch);
case 'm':
return ApplyModifier_Mtime(pp, ch);
case 'O': case 'O':
return ApplyModifier_Order(pp, ch); return ApplyModifier_Order(pp, ch);
case 'P': case 'P':