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>
* VERSION (_MAKE_VERSION): 20230414

View file

@ -392,8 +392,6 @@ unit-tests/export-variants.exp
unit-tests/export-variants.mk
unit-tests/export.exp
unit-tests/export.mk
unit-tests/forloop.exp
unit-tests/forloop.mk
unit-tests/forsubst.exp
unit-tests/forsubst.mk
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.exp
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.mk
unit-tests/varmod-order-numeric.exp

View file

@ -1,2 +1,2 @@
# 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
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
.Dd March 22, 2023
.Dd May 10, 2023
.Dt BMAKE 1
.Os
.Sh NAME
@ -796,12 +796,10 @@ The list of sources for this target that were deemed out-of-date; also
known as
.Sq Va \&? .
.It Va .PREFIX
The file prefix of the target, containing only the file portion, no suffix
or preceding directory components; also known as
The name of the target with suffix (if declared in
.Ic .SUFFIXES )
removed; also known as
.Sq Va * .
The suffix must be one of the known suffixes declared with
.Ic .SUFFIXES ,
or it is not recognized.
.It Va .TARGET
The name of the target; also known as
.Sq Va @ .
@ -1513,6 +1511,25 @@ producing the formatted timestamp.
If a
.Ar timestamp
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
Attempts to convert the value to an absolute path using
.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,
can result in a seemingly unexplained
.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-
of-date; also known as `?'.
.PREFIX The file prefix of the target, containing only the file
portion, no suffix or preceding directory components;
also known as `*'. The suffix must be one of the known
suffixes declared with .SUFFIXES, or it is not recog-
nized.
.PREFIX The name of the target with suffix (if declared in
.SUFFIXES) removed; also known as `*'.
.TARGET The name of the target; also known as `@'. For compati-
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
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).
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
seemingly unexplained `*** Error code 6'
FreeBSD 13.0 March 22, 2023 FreeBSD 13.0
FreeBSD 13.0 May 10, 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.
@ -94,7 +94,7 @@
#include "pathnames.h"
/* "@(#)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 pid_t compatChild;
@ -224,7 +224,7 @@ bool
Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
{
char *cmdStart; /* Start of expanded command */
char *bp;
char *volatile bp;
bool silent; /* Don't print command */
bool doIt; /* Execute even if -n */
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.
@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)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 {
@ -72,6 +72,22 @@ typedef struct ForLoop {
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 *
ForLoop_New(void)
{
@ -123,6 +139,13 @@ ForLoop_Details(ForLoop *f)
}
static bool
IsValidInVarname(char c)
{
return c != '$' && c != ':' && c != '\\' &&
c != '(' && c != '{' && c != ')' && c != '}';
}
static void
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
@ -133,15 +156,20 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
return false;
f->vars.len = 0;
return;
}
/*
* XXX: This allows arbitrary variable names;
* see directive-for.mk.
*/
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
continue;
for (len = 0; p[len] != '\0' && !ch_isspace(p[len]); len++) {
if (!IsValidInVarname(p[len])) {
Parse_Error(PARSE_FATAL,
"invalid character '%c' "
"in .for loop variable name",
p[len]);
f->vars.len = 0;
return;
}
}
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
@ -154,11 +182,10 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
return false;
return;
}
*pp = p;
return true;
}
static bool
@ -221,17 +248,14 @@ For_Eval(const char *line)
ForLoop *f;
p = line + 1; /* skip the '.' */
cpp_skip_whitespace(&p);
skip_whitespace_or_line_continuation(&p);
if (IsFor(p)) {
p += 3;
f = ForLoop_New();
if (!ForLoop_ParseVarnames(f, &p)) {
ForLoop_Free(f);
return -1;
}
if (!ForLoop_ParseItems(f, p))
ForLoop_ParseVarnames(f, &p);
if (f->vars.len > 0 && !ForLoop_ParseItems(f, p))
f->items.len = 0; /* don't iterate */
accumFor = f;
@ -254,7 +278,7 @@ For_Accum(const char *line, int *forLevel)
if (*p == '.') {
p++;
cpp_skip_whitespace(&p);
skip_whitespace_or_line_continuation(&p);
if (IsEndfor(p)) {
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
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
.Dd March 22, 2023
.Dd May 10, 2023
.Dt MAKE 1
.Os
.Sh NAME
@ -796,12 +796,10 @@ The list of sources for this target that were deemed out-of-date; also
known as
.Sq Va \&? .
.It Va .PREFIX
The file prefix of the target, containing only the file portion, no suffix
or preceding directory components; also known as
The name of the target with suffix (if declared in
.Ic .SUFFIXES )
removed; also known as
.Sq Va * .
The suffix must be one of the known suffixes declared with
.Ic .SUFFIXES ,
or it is not recognized.
.It Va .TARGET
The name of the target; also known as
.Sq Va @ .
@ -1524,6 +1522,25 @@ producing the formatted timestamp.
If a
.Ar timestamp
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
Attempts to convert the value to an absolute path using
.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,
can result in a seemingly unexplained
.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>
* install-mk (MK_VERSION): 20230420

View file

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

View file

@ -1,5 +1,5 @@
# 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
#
@ -113,16 +113,17 @@ tqtdeps := ${DIRDEPS_TARGETS_MACHINE_LIST:@m@${tdeps:M*.$m,*}@:S,/${.MAKE.DEPEND
.endif
# now work out what we want in DIRDEPS
DIRDEPS = ${ptdeps}
.if empty(REQUESTED_MACHINE)
# we want them all just as found
DIRDEPS = ${ptdeps} ${mqtdeps} ${tqtdeps}
DIRDEPS += ${mqtdeps} ${tqtdeps}
.else
# we only want those that match REQUESTED_MACHINE/REQUESTED_TARGET_SPEC
# or REQUESTED_TARGET_SPEC (TARGET_SPEC)
DIRDEPS = \
${ptdeps:@d@$d.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC:U${REQUESTED_MACHINE}}}@} \
DIRDEPS += \
${mqtdeps:M*.${REQUESTED_MACHINE}} \
${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}}
${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}} \
.endif
# clean up
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
#
@ -273,6 +273,10 @@ _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*}
.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
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}
.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
_build_all_dirs =
_build_xtra_dirs =
@ -653,7 +665,7 @@ _machines := ${_machines:${M_dep_qual_fixes:ts:}:O:u}
# reset each time through
_build_dirs =
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
.if ${DEP_RELDIR} == ${_DEP_RELDIR} && ${_CURDIR} != ${SRCTOP}
# pickup other machines for this dir if necessary
_build_dirs += ${_machines:@m@${_CURDIR}.$m@}
.endif

View file

@ -59,7 +59,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# 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
#
@ -74,7 +74,7 @@
# sjg@crufty.net
#
MK_VERSION=20230420
MK_VERSION=20230510
OWNER=
GROUP=
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
#
@ -38,21 +38,30 @@ now_utc ?= ${%s:L:gmtime}
start_utc := ${now_utc}
.endif
.info ${.newline}${TIME_STAMP} Start ${.TARGETS}
.if make(*-jobs)
.info ${.newline}${TIME_STAMP} Start ${.TARGETS}
JOB_LOGDIR ?= ${SRCTOP:H}
JOB_LOG = ${JOB_LOGDIR}/${.TARGET:S,-jobs,,:S,/,_,g}.log
JOB_LOG_GENS ?= 4
# we like to rotate logs
.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
NEWLOG_SH := ${newlog.sh:L:${M_whence}}
.else
NEWLOG_SH := ${(type newlog.sh) 2> /dev/null:L:sh:M/*}
.endif
.endif
.endif
.if !empty(NEWLOG_SH) && exists(${NEWLOG_SH})
NEWLOG := sh ${NEWLOG_SH}
JOB_NEWLOG_ARGS ?= -S -n ${JOB_LOG_GENS}
@ -72,7 +81,7 @@ JOB_ARGS+= -j${JOB_MAX}
# build orchestration works as expected (DIRDEPS_BUILD)
${.TARGETS:M*-jobs}:
@${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 \
${.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
@ -139,6 +139,10 @@ FORCE_DPADD += ${_nonlibs:@x@${DPADD:M*/$x}@}
.END: gendirdeps
.endif
.if ${LOCAL_DEPENDS_GUARD:U} == "no"
.depend:
.endif
# if we don't have OBJS, then .depend isn't useful
.if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "")
# 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
# be of use. There is absolutely NO WARRANTY.
@ -17,56 +17,21 @@
# include this if you want to enable meta mode
# for maximum benefit, requires filemon(4) driver.
.if ${MAKE_VERSION:U0} > 20100901
.if !target(.ERROR)
# absolute path to what we are reading.
_PARSEDIR ?= ${.PARSEDIR:tA}
.-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)
SYS_MK_DIR := ${_PARSEDIR}
.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)
# we do not support filemon
META_MODE += nofilemon
@ -102,19 +67,7 @@ META_MODE += silent=yes
.endif
.endif
# we use the pseudo machine "host" for the build host.
# 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 ${MK_DIRDEPS_BUILD:Uno} == "yes"
.if !defined(META2DEPS)
.if defined(PYTHON) && exists(${PYTHON})
@ -134,6 +87,11 @@ MAKE_PRINT_VAR_ON_ERROR += \
MAKEFILE \
.MAKE.MODE
MK_META_ERROR_TARGET = yes
.endif
.if ${MK_META_ERROR_TARGET:Uno} == "yes"
.if !defined(SB) && defined(SRCTOP)
SB = ${SRCTOP:H}
.endif
@ -150,21 +108,12 @@ _metaError: .NOMETA .NOTMAIN
echo "ERROR: log ${meta_error_log}" >&2; }; :
.endif
.endif
# Are we, after all, in meta mode?
.if ${.MAKE.MODE:Uno:Mmeta*} != ""
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
# re-running needlessly
META_COOKIE_TOUCH?= touch ${COOKIE.${.TARGET}:U${.OBJDIR}/${.TARGET:T}}
@ -192,27 +141,13 @@ UPDATE_DEPENDFILE= NO
.endif
.endif
.if ${.MAKE.LEVEL} == 0
.if ${MK_DIRDEPS_BUILD:Uyes} == "yes"
# make sure dirdeps target exists and do it first
all: dirdeps .WAIT
dirdeps:
.NOPATH: dirdeps
.else # in meta mode?
.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=
# some targets need to be .PHONY in non-meta mode
META_NOPHONY= .PHONY
META_NOECHO= echo
.endif
.endif
.endif # in meta mode?
.-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
have no (or few) cicular dependencies between libraries.
Consider this experimental.
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
optimally for the host.
Meta mode
META_MODE
=========
The 20110505 and later versions of ``mk-files`` include a number of
makefiles contributed by Juniper Networks, Inc.
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
.. _`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
=======
@ -538,9 +581,11 @@ where you unpacked the tar file, you can::
.. _bmake: bmake.htm
.. _NetBSD: http://www.netbsd.org/
.. _mkdeps.sh: http://www.crufty.net/ftp/pub/sjg/mkdeps.sh
.. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
.. _mkdeps.sh: https://www.crufty.net/ftp/pub/sjg/mkdeps.sh
.. _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
: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

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
# be of use. There is absolutely NO WARRANTY.
@ -13,7 +13,10 @@
# 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
# dependency files, which can be differentiated by their names.
# As per dirdeps.mk we only require:
@ -57,3 +60,5 @@ MACHINE := ${_m}
.endif
.endif
.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
#
@ -85,6 +85,9 @@ OPTIONS_DEFAULT_DEPENDENT += \
.-include <options.mk>
# :Uno incase options.mk not installed
.if ${MK_DIRDEPS_BUILD:Uno} == "yes"
.-include <sys.dirdeps.mk>
.endif
.if ${MK_META_MODE:Uno} == "yes"
.-include <meta.sys.mk>
.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
@ -91,7 +91,7 @@
* Parse_Error Report a parse error, a warning or an informational
* 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>
@ -121,7 +121,7 @@
#include "pathnames.h"
/* "@(#)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.
@ -257,10 +257,7 @@ SearchPath *defSysIncPath; /* default for sysIncPath */
/*
* The parseKeywords table is searched using binary search when deciding
* if a target or source is special. The 'spec' field is the ParseSpecial
* 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)
* if a target or source is special.
*/
static const struct {
const char name[17];
@ -325,7 +322,7 @@ GetInclude(size_t i)
return Vector_Get(&includes, i);
}
/* The file that is currently being read. */
/* The makefile that is currently being read. */
static IncludedFile *
CurFile(void)
{
@ -403,8 +400,11 @@ PrintStackTrace(bool includingInnermost)
const char *fname = entry->name.str;
char dirbuf[MAXPATHLEN + 1];
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0)
fname = realpath(fname, dirbuf);
if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) {
const char *realPath = realpath(fname, dirbuf);
if (realPath != NULL)
fname = realPath;
}
if (entry->forLoop != NULL) {
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 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
* and commands on this dependency line since each of these
* dependency groups has its own attributes and commands,
@ -3004,10 +3004,7 @@ Parse_End(void)
}
/*
* Return a list containing the single main target to create.
* If no such target exists, we Punt with an obnoxious error message.
*/
/* Populate the list with the single main target to create, or error out. */
void
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)
#
@ -211,7 +211,6 @@ TESTS+= export
TESTS+= export-all
TESTS+= export-env
TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst
TESTS+= gnode-submake
TESTS+= hanoi-include
@ -375,6 +374,7 @@ TESTS+= varmod-loop-delete
TESTS+= varmod-loop-varname
TESTS+= varmod-match
TESTS+= varmod-match-escape
TESTS+= varmod-mtime
TESTS+= varmod-no-match
TESTS+= varmod-order
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
# among several functions.
#
# The below test uses the function defined(...) since it has no side-effects,
# the other functions (except empty(...)) would work equally well. The
# function empty is special because it uses a different parsing algorithm for
# its argument.
# The below test uses the 'defined' function since it has no side-effects.
# The other functions would work equally well, except for 'empty', which
# parses its argument differently from the other functions.
#
DEF= defined
${: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
# 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.
.if ! defined ( DEF )
. 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
# expressions that are evaluated late, at expansion time.
#
# Any variables appearing in these
# conditions are expanded before parsing the condition. This is
# different from many other places.
# Any expressions appearing in these conditions are expanded before parsing
# the condition. This is different from conditions in .if directives, where
# 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
# 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
# and then expand the variables, the output would change from the
# current "yes no" to "yes yes", since both variables are non-empty.
# expect: yes
# expect: no
cond-literal:
@echo ${ ${COND.true} :?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)
.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.
#
@ -91,5 +91,6 @@ undef1 def2 a-def2-b 1-2-$$INDIRECT_2-2-1 ${:U\$)}:
.MAKEFLAGS: -d0
# XXX: Why is the exit status still 0, even though Parse_Error is called
# with PARSE_FATAL in SuffExpandChildren?
# XXX: The exit status is still 0, even though Parse_Error is called with
# 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 8: warning:
make: "directive-for-errors.mk" line 9: for-less endfor
make: "directive-for-errors.mk" line 19: Unknown directive "for"
make: "directive-for-errors.mk" line 20: warning:
make: "directive-for-errors.mk" line 21: for-less endfor
make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2.
make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4.
make: "directive-for-errors.mk" line 43: no iteration variables in for
make: "directive-for-errors.mk" line 47: warning: Should not be reached.
make: "directive-for-errors.mk" line 48: for-less endfor
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 64: missing `in' in for
make: "directive-for-errors.mk" line 66: warning: Should not be reached.
make: "directive-for-errors.mk" line 67: for-less endfor
make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
make: "directive-for-errors.mk" line 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: "directive-for-errors.mk" line 11: Unknown directive "fori"
make: "directive-for-errors.mk" line 12: warning: <>
make: "directive-for-errors.mk" line 13: for-less endfor
make: "directive-for-errors.mk" line 27: Unknown directive "for"
make: "directive-for-errors.mk" line 28: warning: <>
make: "directive-for-errors.mk" line 29: for-less endfor
make: "directive-for-errors.mk" line 46: invalid character '$' in .for loop variable name
make: "directive-for-errors.mk" line 54: 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 80: missing `in' in for
make: "directive-for-errors.mk" line 91: Unknown modifier "Z"
make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: "directive-for-errors.mk" line 92: warning: Should not be reached.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
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.
# expect-all
# A .for directive must be followed by whitespace, everything else results
# in a parse error.
# expect+1: Unknown directive "fori"
.fori in 1 2 3
. warning ${i}
. warning <${i}>
.endfor
# expect-2: <>
# expect-2: for-less endfor
# 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
# "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
# continues in ParseLine > ParseDependencyLine > ParseDependency >
# ParseDependencyTargets > ParseErrorNoDependency, and there the directive
# name is parsed a bit differently.
# expect+1: Unknown directive "for"
.for/i in 1 2 3
. warning ${i}
. warning <${i}>
.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,
# which is not useful in practice.
# Before for.c 1.173 from 2023-05-08, the variable name could be an arbitrary
# 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,
# instead it is kept as-is, and when the .info directive expands its argument,
# each '$$' gets replaced with a single '$'. The "long variable expression"
# ${$} gets replaced though, even though this would be a parse error everywhere
# outside a .for loop.
#
# The '\' on the other hand is treated as a normal variable name.
# The '$$' was not replaced with the values '1' or '3' from the .for loop,
# instead it was kept as-is, and when the .info directive expanded its
# argument, each '$$' got replaced with a single '$'. The "long variable
# expression" ${$} got replaced though, even though this would be a parse
# error everywhere outside a .for loop.
${:U\$}= dollar # 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
. info Dollar $$ ${$} $($) and backslash $\ ${\} $(\).
.endfor
# 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
# 3 values.
# since this would end up in an endless loop, consuming 0 of the 3 values in
# each iteration.
# expect+1: no iteration variables in for
.for in 1 2 3
# 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,
@ -47,29 +58,39 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
. warning Should not be reached.
.endfor
# 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
# 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
. warning Should not be reached.
.endfor
# 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.
.for i in
. info Would be reached if there were items to loop over.
.endfor
# 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.
. warning Should not be reached.
.endfor
# 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
# the loop body is expanded as if no error had happened.
# expect+1: Unknown modifier "Z"
.for i in 1 2 ${:U3:Z} 4
. warning Should not be reached.
.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:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
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: loop body:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
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: loop body:
. info ${:U\$}
make: "directive-for-escape.mk" line 43: $
make: "directive-for-escape.mk" line 47: $
For: loop body:
. info ${:U${V}}
make: "directive-for-escape.mk" line 43: value
make: "directive-for-escape.mk" line 47: value
For: loop body:
. 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:
. info ${:U$(V)}
make: "directive-for-escape.mk" line 43: value
make: "directive-for-escape.mk" line 47: value
For: loop body:
. 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: loop body:
# ${:U\${UNDEF\:U\\$\\$}
@ -34,29 +34,25 @@ For: loop body:
For: end for 1
For: loop body:
. 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:
. info ${:U{{\}\}}
make: "directive-for-escape.mk" line 92: {{}}
make: "directive-for-escape.mk" line 101: {{}}
For: loop body:
. info ${:Uend\}}
make: "directive-for-escape.mk" line 92: end}
make: "directive-for-escape.mk" line 101: end}
For: end for 1
For: loop body:
. 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: loop body:
. 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: loop body:
. info ${NUMBERS} ${:Ureplaced}
make: "directive-for-escape.mk" line 129: one two three replaced
make: "directive-for-escape.mk" line 150: invalid character '}' in .for loop variable name
For: end for 1
For: loop body:
. info ${:Ureplaced}
make: "directive-for-escape.mk" line 139: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@ -69,46 +65,42 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
make: "directive-for-escape.mk" line 147: . $i: inner
make: "directive-for-escape.mk" line 148: . ${i}: inner
make: "directive-for-escape.mk" line 149: . ${i:M*}: inner
make: "directive-for-escape.mk" line 150: . $(i): inner
make: "directive-for-escape.mk" line 151: . $(i:M*): inner
make: "directive-for-escape.mk" line 152: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 153: . ${i\}}: inner}
make: "directive-for-escape.mk" line 154: . ${i2}: two
make: "directive-for-escape.mk" line 155: . ${i,}: comma
make: "directive-for-escape.mk" line 156: . adjacent: innerinnerinnerinner
make: "directive-for-escape.mk" line 159: . $i: inner
make: "directive-for-escape.mk" line 160: . ${i}: inner
make: "directive-for-escape.mk" line 161: . ${i:M*}: inner
make: "directive-for-escape.mk" line 162: . $(i): inner
make: "directive-for-escape.mk" line 163: . $(i:M*): inner
make: "directive-for-escape.mk" line 164: . ${i${:U}}: outer
make: "directive-for-escape.mk" line 165: . ${i\}}: inner}
make: "directive-for-escape.mk" line 166: . ${i2}: two
make: "directive-for-escape.mk" line 167: . ${i,}: comma
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: loop body:
. 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.
make: "directive-for-escape.mk" line 199: eight and no cents.
For: end for 1
make: "directive-for-escape.mk" line 181: 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
make: "directive-for-escape.mk" line 212: newline in .for value
For: loop body:
. info short: ${:U" "}
. info long: ${:U" "}
make: "directive-for-escape.mk" line 182: short: " "
make: "directive-for-escape.mk" line 183: long: " "
make: "directive-for-escape.mk" line 213: short: " "
make: "directive-for-escape.mk" line 214: long: " "
For: end for 1
For: loop body:
For: end for 1
Parse_PushInput: .for loop in directive-for-escape.mk, line 196
make: "directive-for-escape.mk" line 196: newline in .for value
in .for loop from directive-for-escape.mk:196 with i = "
Parse_PushInput: .for loop in directive-for-escape.mk, line 230
make: "directive-for-escape.mk" line 230: newline in .for value
in .for loop from directive-for-escape.mk:230 with i = "
"
For: loop body:
: ${:U" "}
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
Parsing line 197: : ${:U" "}
Parsing line 231: : ${:U" "}
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'
Parsing line 199: .MAKEFLAGS: -d0
Parsing line 233: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
For: end for 1
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.
# These values get expanded later using the :U variable modifier, and this
# escaping and unescaping must pass all characters and strings effectively
# unmodified.
# escaping and unescaping must pass all characters and strings unmodified.
# expect-all
.MAKEFLAGS: -df
@ -12,12 +13,14 @@
# This could be considered a bug.
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
# 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
# ':U' but also the escaping for the line-end comment.
.for chars in ${ASCII}
. info ${chars}
.endfor
# expect-2: !"
# As of 2020-12-31, using 2 backslashes before be '#' would treat the '#'
# 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}
. info ${chars}
.endfor
# expect-2: !"\\
# Cover the code in ExprLen.
#
@ -42,6 +46,11 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
.for i in ${VALUES}
. info $i
.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.
@ -112,6 +121,7 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
.for i in ${VALUES}
. info $i
.endfor
# expect-2: begin<fallback>end
# A single trailing dollar doesn't happen in practice.
# 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\$}
. info ${i}
.endfor
# expect-2: $
# As of 2020-12-31, the name of the iteration variable can even contain
# colons, which then affects variable expressions having this exact modifier.
# This is clearly an unintended side effect of the implementation.
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# could contain colons, which affected variable expressions having this exact
# modifier. This possibility was neither intended nor documented.
NUMBERS= one two three
# expect+1: invalid character ':' in .for loop variable name
.for NUMBERS:M*e in replaced
. info ${NUMBERS} ${NUMBERS:M*e}
.endfor
# As of 2020-12-31, the name of the iteration variable can contain braces,
# which gets even more surprising than colons, since it allows to replace
# sequences of variable expressions. There is no practical use case for
# this, though.
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# could contain braces, which allowed to replace sequences of variable
# expressions. This possibility was neither intended nor documented.
BASENAME= one
EXT= .c
# expect+1: invalid character '}' in .for loop variable name
.for BASENAME}${EXT in replaced
. info ${BASENAME}${EXT}
.endfor
@ -155,11 +167,23 @@ i,= comma
. info . $${i,}: ${i,}
. info . adjacent: $i${i}${i:M*}$i
.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
# variable names. ForLoop_SubstVarShort skips "stupid" variable names though,
# but ForLoop_SubstVarLong naively parses the body of the loop, substituting
# each '${$}' with an actual 'dollar'.
# Before for.c 1.173 from 2023-05-08, the variable name could be a single '$'
# since there was no check on valid variable names. ForLoop_SubstVarShort
# skipped "stupid" variable names though, but ForLoop_SubstVarLong naively
# parsed the body of the loop, substituting each '${$}' with an actual
# '${:Udollar}'.
# expect+1: invalid character '$' in .for loop variable name
.for $ in dollar
. info eight $$$$$$$$ and no cents.
. info eight ${$}${$}${$}${$} and no cents.
@ -171,6 +195,7 @@ i,= comma
# evaluates to an empty string.
closing-brace= } # guard against an
${closing-brace}= <closing-brace> # alternative interpretation
# expect+1: eight and no cents.
.info eight ${$}${$}${$}${$} and no cents.
# 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,
# leading to syntax errors such as "Unclosed variable expression" in the upper
# 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}"
. info short: $i
. info long: ${i}
.endfor
# expect-3: short: " "
# expect-3: long: " "
# No error since the newline character is not actually used.
.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
# advanced.
.MAKEFLAGS: -dp
# expect+1: newline in .for value
.for i in "${.newline}"
: $i
.endfor

View file

@ -1,42 +1,41 @@
make: "directive-for.mk" line 108: outer
make: "directive-for.mk" line 133: a:\ a:\file.txt
make: "directive-for.mk" line 133: d:\\
make: "directive-for.mk" line 133: d:\\file.txt
make: "directive-for.mk" line 140: ( ( (
make: "directive-for.mk" line 140: [ [ [
make: "directive-for.mk" line 140: { { {
make: "directive-for.mk" line 140: ) ) )
make: "directive-for.mk" line 140: ] ] ]
make: "directive-for.mk" line 140: } } }
make: "directive-for.mk" line 140: (()) (()) (())
make: "directive-for.mk" line 140: [[]] [[]] [[]]
make: "directive-for.mk" line 140: {{}} {{}} {{}}
make: "directive-for.mk" line 140: )( )( )(
make: "directive-for.mk" line 140: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{
make: "directive-for.mk" line 148: outer value value
make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
make: "directive-for.mk" line 154: Unknown modifier "Z"
make: "directive-for.mk" line 155: XXX: Not reached word1
make: "directive-for.mk" line 155: XXX: Not reached word3
make: "directive-for.mk" line 160: no iteration variables in for
make: "directive-for.mk" line 162: Missing argument for ".error"
make: "directive-for.mk" line 163: for-less endfor
make: "directive-for.mk" line 187: 1 open conditional
make: "directive-for.mk" line 203: for-less endfor
make: "directive-for.mk" line 204: if-less endif
make: "directive-for.mk" line 212: if-less endif
make: "directive-for.mk" line 119: outer
make: "directive-for.mk" line 137: a:\ a:\file.txt
make: "directive-for.mk" line 137: d:\\
make: "directive-for.mk" line 137: d:\\file.txt
make: "directive-for.mk" line 148: ( ( (
make: "directive-for.mk" line 148: [ [ [
make: "directive-for.mk" line 148: { { {
make: "directive-for.mk" line 148: ) ) )
make: "directive-for.mk" line 148: ] ] ]
make: "directive-for.mk" line 148: } } }
make: "directive-for.mk" line 148: (()) (()) (())
make: "directive-for.mk" line 148: [[]] [[]] [[]]
make: "directive-for.mk" line 148: {{}} {{}} {{}}
make: "directive-for.mk" line 148: )( )( )(
make: "directive-for.mk" line 148: ][ ][ ][
make: "directive-for.mk" line 148: }{ }{ }{
make: "directive-for.mk" line 168: invalid character ':' in .for loop variable name
make: "directive-for.mk" line 175: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 187: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 198: Unknown modifier "Z"
make: "directive-for.mk" line 199: XXX: Not reached word1
make: "directive-for.mk" line 199: XXX: Not reached word3
make: "directive-for.mk" line 206: no iteration variables in for
make: "directive-for.mk" line 232: 1 open conditional
make: "directive-for.mk" line 248: for-less endfor
make: "directive-for.mk" line 249: if-less endif
make: "directive-for.mk" line 257: if-less endif
For: new loop 2
For: end for 2
For: end for 1
For: loop body:
.\
for inner in i
.\
endfor
make: "directive-for.mk" line 229: Unexpected end of file in .for loop
For: end for 1
For: loop body:
.\
endfor
make: "directive-for.mk" line 227: for-less endfor
make: "directive-for.mk" line 305: newline-item=(a)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
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.
#
@ -8,11 +8,15 @@
# .for _FILE_ in values
# .for .FILE. 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
.for num in 1 2 3
NUMBERS+= ${num}
@ -21,8 +25,9 @@ NUMBERS+= ${num}
. error
.endif
# 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
${name}= ${value}
.endfor
@ -30,12 +35,12 @@ ${name}= ${value}
. error
.endif
# The .for loop splits the items at whitespace, taking quotes into account,
# just like the :M or :S variable 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.
# just like the :M or :S modifiers.
#
# 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
.for var in one t\ w\ o "three three" 'four four' `five six`
WORDS+= counted
@ -44,16 +49,19 @@ WORDS+= counted
. error
.endif
# In the body of the .for loop, the iteration variables can be accessed
# like normal variables, even though they are not really variables.
#
# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so
# on, before the loop body is evaluated.
# Instead, before interpreting the body of the .for loop, the body is
# 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
# 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
var2= value before
.for var var2 in 1 2 3 4
@ -66,9 +74,8 @@ var2= value before
.endif
# 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
# also empty.
#
# empty. In this particular example, the items to be iterated are empty as
# well.
var= value before
var2= value before
.for var var2 in ${:U}
@ -82,11 +89,13 @@ var2= value before
# 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
# 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
# Since that date, the .for loop expands to:
# Since that date, the .for loop below expands to:
# EXPANSION${:U+}= value
#
EXPANSION= before
@ -102,13 +111,16 @@ EXPANSION${plus}= value
.endif
# 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 inner
# expect+1: outer
. info ${i}
. endfor
.endfor
# From https://gnats.netbsd.org/29985.
#
# 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
# variable values have been replaced with expressions of the form ${:U...},
# 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
. info ${path}
.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.
# Each line must print the same word 3 times.
@ -139,28 +147,65 @@ EXPANSION${plus}= value
.for v in ( [ { ) ] } (()) [[]] {{}} )( ][ }{
. info $v ${v} $(v)
.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,
# except for whitespace. This allows for creative side effects. Hopefully
# nobody is misusing this "feature".
# Before 2023-05-09, the variable names could contain arbitrary characters,
# except for whitespace, allowing for creative side effects, as usual for
# arbitrary code injection.
var= outer
# expect+1: invalid character ':' in .for loop variable name
.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
# 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
. info XXX: Not reached ${var}
.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.
.for in value # expect+0: no iteration variables in for
# XXX: The loop body is evaluated once, even with the parse error above.
. error # expect+0: Missing argument for ".error"
.endfor # expect+0: for-less endfor
. error
.endfor
# An empty list of iteration values to the right of the 'in' is accepted.
# Unlike in the shell, it is not a parse error.
@ -214,12 +259,19 @@ var= outer
.endif # no 'if-less endif'
# When make parses a .for loop, it assumes that there is no line break between
# the '.' and the 'for' or 'endfor', as there is no practical reason to break
# the line at this point. When make scans the outer .for loop, it does not
# recognize the inner directives as such. When make scans the inner .for
# loop, it recognizes the '.\n for' but does not recognize the '.\n endfor',
# as LK_FOR_BODY preserves the backslash-newline sequences.
# Before for.c 1.172 from 2023-05-08, when make parsed a .for loop, it
# assumed that there was no line continuation between the '.' and the 'for'
# or 'endfor', as there is no practical reason to break the line at this
# point.
#
# 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
.for outer in o
.\
@ -244,3 +296,12 @@ var= outer
. error
. endif
.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
# categories.
@ -22,3 +22,33 @@
#
# https://bugs.freebsd.org/265119
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: 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}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...}
@ -12,7 +8,7 @@ Modifier part: "${FAIL}then"
Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
Parsing line 163: DEFINED= defined
Parsing line 165: DEFINED= defined
Global: DEFINED = defined
CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
@ -24,7 +20,7 @@ Modifier part: "${FAIL}then"
Var_Parse: ${FAIL}else} (parse-only)
Modifier part: "${FAIL}else"
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)
Global: .MAKEFLAGS = -r -k -d cpv -d
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
# necessary computations. If the result of the expression is irrelevant,
@ -79,8 +79,9 @@ DEFINED= # defined
.if 0 && ${:Uword:E}
.endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
# ':gmtime' does not expand its argument.
# Before var.c 1.1050 from 2023-05-09, the ':gmtime' modifier produced the
# error message 'Invalid time value: ${FAIL}}' since it did not expand its
# argument.
.if 0 && ${:Uword:gmtime=${FAIL}}
.endif
@ -93,8 +94,9 @@ DEFINED= # defined
.if 0 && ${value:L}
.endif
# As of 2021-03-14, the error 'Invalid time value: ${FAIL}}' is ok since
# ':localtime' does not expand its argument.
# Before var.c 1.1050 from 2023-05-09, the ':localtime' modifier produced the
# error message 'Invalid time value: ${FAIL}}' since it did not expand its
# argument.
.if 0 && ${:Uword:localtime=${FAIL}}
.endif

View file

@ -1,5 +1,5 @@
Global: .ALLTARGETS = 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
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_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: .MAKEFLAGS = -r -k -d v -d
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.o from var-scope-local.c.
: 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
# are relatively short-lived as they are created just before making the
@ -12,6 +12,64 @@
.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
# variables. When these expressions are evaluated outside of a target, these
# 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
# 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
# command line option '-e'). In that phase, expressions involving
# scope and from the environment are resolved, in this precedence order (but
# see the command line option '-e'). In that phase, expressions involving
# target-local variables need to be preserved, including the exact names of
# the variables.
#
@ -77,13 +135,17 @@
.endif
# Custom local variables
#
# Additional target-local variables may be defined in dependency lines.
.MAKEFLAGS: -dv
# In the following line, the ':=' may either be interpreted as an assignment
# operator or as the dependency operator ':', followed by an empty variable
# 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
# variable name is expanded twice, once for 'one' and once for 'two'.
# assignment, the left-hand side must be a single word or empty.
#
# 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
one two:=three
@ -205,32 +267,3 @@ a_use: .USE VAR=use
all: var-scope-local-use.o
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 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}")
make: "varmod-gmtime.mk" line 67: Invalid time value at "-1} != """
make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "")
make: "varmod-gmtime.mk" line 76: Invalid time value at " 1} != """
make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "")
make: "varmod-gmtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """
make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """
make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "")
make: "varmod-gmtime.mk" line 59: Invalid time value "-1"
make: "varmod-gmtime.mk" line 59: Malformed conditional (${:L:gmtime=-1} != "")
make: "varmod-gmtime.mk" line 68: Invalid time value " 1"
make: "varmod-gmtime.mk" line 68: Malformed conditional (${:L:gmtime= 1} != "")
make: "varmod-gmtime.mk" line 114: Invalid time value "10000000000000000000000000000000"
make: "varmod-gmtime.mk" line 114: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
make: "varmod-gmtime.mk" line 125: Invalid time value "error"
make: "varmod-gmtime.mk" line 125: Malformed conditional (${:L:gmtime=error} != "")
make: "varmod-gmtime.mk" line 134: Invalid time value "100000S,1970,bad,"
make: "varmod-gmtime.mk" line 134: Malformed conditional (${%Y:L:gmtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
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
# using strftime(3) in UTC.
#
# See also:
# varmod-localtime.mk
.if ${TZ:Uundefined} != "undefined" # see unit-tests/Makefile
. error
@ -41,20 +44,9 @@
.endif
# As of 2020-08-16, it is not possible to pass the seconds via a
# variable expression. This is because parsing of the :gmtime
# modifier stops at the '$' and returns to ApplyModifiers.
#
# 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}"
# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
# seconds via a variable expression.
.if ${%Y:L:gmtime=${:U1593536400}} != "2020"
. error
.endif
@ -75,6 +67,8 @@
# because it would make sense but just as a side-effect from using strtoul.
.if ${:L:gmtime= 1} != ""
. error
.else
. error
.endif
@ -115,7 +109,8 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# 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} != ""
. error
.else
@ -133,5 +128,11 @@
. error
.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:

View file

@ -1,13 +1,13 @@
make: "varmod-localtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}""
make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}")
make: "varmod-localtime.mk" line 67: Invalid time value at "-1} != """
make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "")
make: "varmod-localtime.mk" line 76: Invalid time value at " 1} != """
make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "")
make: "varmod-localtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """
make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """
make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "")
make: "varmod-localtime.mk" line 59: Invalid time value "-1"
make: "varmod-localtime.mk" line 59: Malformed conditional (${:L:localtime=-1} != "")
make: "varmod-localtime.mk" line 68: Invalid time value " 1"
make: "varmod-localtime.mk" line 68: Malformed conditional (${:L:localtime= 1} != "")
make: "varmod-localtime.mk" line 114: Invalid time value "10000000000000000000000000000000"
make: "varmod-localtime.mk" line 114: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
make: "varmod-localtime.mk" line 125: Invalid time value "error"
make: "varmod-localtime.mk" line 125: Malformed conditional (${:L:localtime=error} != "")
make: "varmod-localtime.mk" line 134: Invalid time value "100000S,1970,bad,"
make: "varmod-localtime.mk" line 134: Malformed conditional (${%Y:L:localtime=100000S,1970,bad,} != "bad")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
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
# using strftime(3) in local time.
#
# See also:
# varmod-gmtime.mk
.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile
. error
@ -41,20 +44,9 @@
.endif
# As of 2020-08-16, it is not possible to pass the seconds via a
# variable expression. This is because parsing of the :localtime
# modifier stops at the '$' and returns to ApplyModifiers.
#
# 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}"
# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
# seconds via a variable expression.
.if ${%Y:L:localtime=${:U1593536400}} != "2020"
. error
.endif
@ -75,6 +67,8 @@
# because it would make sense but just as a side-effect from using strtoul.
.if ${:L:localtime= 1} != ""
. error
.else
. error
.endif
@ -115,7 +109,8 @@
# ULONG_MAX, which got converted to -1. This resulted in a time stamp of
# 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} != ""
. error
.else
@ -133,5 +128,11 @@
. error
.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:

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
# target.
@ -7,11 +7,12 @@
# as of 2020-08-23 it is nevertheless resolved to a path. This is probably
# unintended.
#
# The real target is located in a subdirectory, and its full path is returned.
# If it had been in the current directory, the difference between its path and
# its name would not be visible.
# In this test, the real target is located in a subdirectory, and its full
# path is returned. If it had been in the current directory, the difference
# 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
@ -20,7 +21,8 @@ _!= mkdir varmod-path.subdir
_!= > varmod-path.subdir/varmod-path.phony
_!= > 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
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
@ -147,7 +147,7 @@
#include "metachar.h"
/* "@(#)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
@ -2196,6 +2196,8 @@ ParseModifierPartBalanced(const char **pp, LazyBuf *part)
static bool
ParseModifierPartSubst(
const char **pp,
/* If true, parse up to but excluding the next ':' or ch->endc. */
bool whole,
char delim,
VarEvalMode emode,
ModChain *ch,
@ -2213,11 +2215,14 @@ ParseModifierPartSubst(
)
{
const char *p;
char end1, end2;
p = *pp;
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)) {
LazyBuf_Add(part, p[1]);
p += 2;
@ -2239,15 +2244,15 @@ ParseModifierPartSubst(
ParseModifierPartExpr(&p, part, ch, emode);
}
if (*p != delim) {
*pp = p;
*pp = p;
if (*p != end1 && *p != end2) {
Error("Unfinished modifier for \"%s\" ('%c' missing)",
ch->expr->name, delim);
ch->expr->name, end2);
LazyBuf_Done(part);
return false;
}
*pp = p + 1;
if (!whole)
(*pp)++;
{
Substring sub = LazyBuf_Get(part);
@ -2280,7 +2285,8 @@ ParseModifierPart(
LazyBuf *part
)
{
return ParseModifierPartSubst(pp, delim, emode, ch, part, NULL, NULL);
return ParseModifierPartSubst(pp, false, delim, emode, ch, part,
NULL, NULL);
}
MAKE_INLINE bool
@ -2576,11 +2582,23 @@ ApplyModifier_Time(const char **pp, ModChain *ch)
if (args[0] == '=') {
const char *p = args + 1;
if (!TryParseTime(&p, &t)) {
Parse_Error(PARSE_FATAL,
"Invalid time value at \"%s\"", p);
LazyBuf buf;
if (!ParseModifierPartSubst(&p, true, '\0', ch->expr->emode,
ch, &buf, NULL, NULL))
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;
} else {
t = 0;
@ -2823,6 +2841,71 @@ ApplyModifier_Match(const char **pp, ModChain *ch)
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
ParsePatternFlags(const char **pp, PatternFlags *pflags, bool *oneBigWord)
{
@ -2870,13 +2953,13 @@ ApplyModifier_Subst(const char **pp, ModChain *ch)
(*pp)++;
}
if (!ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &lhsBuf,
&args.pflags, NULL))
if (!ParseModifierPartSubst(pp,
false, delim, ch->expr->emode, ch, &lhsBuf, &args.pflags, NULL))
return AMR_CLEANUP;
args.lhs = LazyBuf_Get(&lhsBuf);
if (!ParseModifierPartSubst(pp, delim, ch->expr->emode, ch, &rhsBuf,
NULL, &args)) {
if (!ParseModifierPartSubst(pp,
false, delim, ch->expr->emode, ch, &rhsBuf, NULL, &args)) {
LazyBuf_Done(&lhsBuf);
return AMR_CLEANUP;
}
@ -3805,6 +3888,8 @@ ApplyModifier(const char **pp, ModChain *ch)
case 'M':
case 'N':
return ApplyModifier_Match(pp, ch);
case 'm':
return ApplyModifier_Mtime(pp, ch);
case 'O':
return ApplyModifier_Order(pp, ch);
case 'P':