diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog index 35235e1f8205..5eb2d7c5bb4a 100644 --- a/contrib/bmake/ChangeLog +++ b/contrib/bmake/ChangeLog @@ -1,3 +1,234 @@ +2021-12-15 Simon J Gerraty + + * cond.c: fix mem leak in CondParser_Leaf + +2021-12-12 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211212 + Merge with NetBSD make, pick up + o rename Parse_SetInput to Parse_PushInput + o remove remove period from end of error messages and warnings + to be more consistent + o arch.c: use simpler memory management for parsing archive members + o cond.c: rework and reduce recursion + o for.c: rename some functions to better reflect purpose + o suff.c: add Suff_NamesStr to provide .SUFFIXES as a string. + o var.c: in parse errors, mark whitespace more clearly + inline ParseEmptyArg into CondParser_FuncCallEmpty + minimize calls to LazyBuf_Get in ParseVarnameLong + treat .SUFFIXES as a read-only variable + +2021-12-07 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211207 + Merge with NetBSD make, pick up + o inline HashIter_Init + o parse.c: inline common subexpression in ParseRawLine + o var.c: merge branches for modifiers ':D' and ':U' + extract common code into Expr_Words + extract common code into Expr_Str + move low-level implementation details out of Var_Parse + +2021-12-06 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211206 + Merge with NetBSD make, pick up + o add unit-tests/varmod-loop-delete + o for.c: inline Str_Words - reduce memory allocation + o parse.c: do not try to expand fixed variable names + only allocate the name of an included file if necessary + clean up ParseInclude + o var.c: fix use-after-free in modifier ':@' + save a memory allocation in each modifier ':O' and ':u' + save a memory allocation in the modifier ':[...]' + in UnexportVars, replace Str_Words with Substring_Words to + reduce allocations and copying. + +2021-12-04 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211204 + Merge with NetBSD make, pick up + o flesh out a number of tests + o replace enums with bitfields, this simplifies a lot of code. + o var.c: refactor ParseModifierPartSubst + +2021-10-24 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211024 + Merge with NetBSD make, pick up + o Punt on write errors - ENOSPC etc. + +2021-10-22 Simon J Gerraty + + * configure.in: use_defshell, set both DEFSHELL_INDEX + and defshell_path if appropriate. + This makes it easier to use say the KSH specification with + and alternate path for the shell. + + * configure.in compat.c: for SCO we need to force UseShell + + * configure.in: SCO /bin/sh is not usable, provide a list of + alternatives for use as .SHELL. + We still have to mark some tests as broken, plus more if we end up + with ksh as .SHELL. + Issue a warning about skipped tests. + + * boot-strap: leave TOOL_DIFF to configure + + * configure.in: on SCO native cc is not usable, + gcc is to be found in /usr/gnu/bin + and while ancient is at least able to compile bmake. + Thus we add /usr/gnu/bin to PATH if it exists, and later + check if $CC would have been found via $PATH. + If not we set CC to the full path of $CC. + Also gnu diff is known to support -u, so if it exists use it. + + * configure.in: move getopt to AC_REPLACE_FUNCS + also add AC_C_INLINE - in an attempt to compile using + native cc on SCO. + + * configure.in: check for stresep as well as strsep, since we + define the later to the former if necessary, and if we have to + provide stresep we also need to provide a prototype. + + * configure.in: we no longer need to worry about + sys/cdefs.h providing __RCSID which simplifies things quite a bit. + + * make.h: make sure we have __RCSID + + * unit-tests/Makefile.config.in: add TOOL_DIFF so configure + can control it. + +2021-10-20 Simon J Gerraty + + * VERSION: 20211020 + Merge with NetBSD make, pick up + o confirm sync of unit-tests + +2021-10-18 Simon J Gerraty + + * configure.in: check if timezone Europe/Berlin is supported + if not try UTC-1 + * configure.in: if .OBJDIR is $srcdir/obj we need to create a + symlink unit-tests -> ../unit-tests/obj so that + unit-tests/Makefile.config is put in the right place. + * refine filtering of .OBJDIR in unit-tests + +2021-10-16 Simon J Gerraty + + * Fix unit-tests on Minix 3.2.0 + o job.c: do not punt if read of token pipe fails for EAGAIN. + On Minix at least, we are not ready to read the childExitJob pipe + when poll says we are. + There should actually be no reason for this pipe to be + non-blocking, but while that works fine on {Net,Free}BSD it + breaks another test case on Minix. + o unit-tests/Makefile: deal with variants of error messages + and use of obj as .OBJDIR + +2021-10-14 Simon J Gerraty + + * configure.in: add sigaction to AC_REPLACE_FUNCS + we also need to check for sigaddset etc just for the benefit of + sigact.c + + * Add sigact.c as sigaction.c so this "just works". + This should have been done back when bmake_signal started using + sigaction (I only just noticed that sigact.c wasn't here ;-) + Note: I no longer have access to any system where this would matter. + +2021-10-13 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211011 + + * Makefile: cleanup a little + + * configure.in: check for sigsetmask + +2021-10-01 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20211001 + Merge with NetBSD make, pick up + o reduce locations reducing text size + o remove unnecessary const + o cond.c: fix lint warning on i386 + do not allow unquoted 'left == right' after modifier ':?' + o hash.c: fix build for DEBUG_HASH_LOOKUP + o var.c: fix memory leak in error case of the ':?' modifier + +2021-09-11 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210911 + Merge with NetBSD make, pick up + o var.c: replace remaining ModChain_ShouldEval with Expr_ShouldEval + +2021-09-08 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210906 + Merge with NetBSD make, pick up + o more unit tests + o lint cleanup + o rename some functions to better fit purpose + o for.c: cleanup - remove unnecessary optimization + fix embedded newlines + o parse.c: correct case for CVS/RCS + +2021-08-11 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210808 + Merge with NetBSD make, pick up + o var.c: remove redundant initialization in ApplyModifier_Order + + * mk/options.mk: issue warning for incorrect usage + +2021-08-03 Simon J Gerraty + + * var.c: use long for :On if we don't have a 64bit int type + + * VERSION (_MAKE_VERSION): 20210803 + Merge with NetBSD make, pick up + o rework varmod-order tests to avoid qsort instability + o make.1: clarify :On entry + +2021-07-31 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210731 + Merge with NetBSD make, pick up + o fix some lint issues + o more unit tests + o var.c: rework of ApplyModifier_Order + +2021-07-30 Simon J Gerraty + + * util.c: add strto*l if HAVE_STRTO*L not defined + + * VERSION (_MAKE_VERSION): 20210730 + Merge with NetBSD make, pick up + o var.c: add :On and :Orn for numeric sort + disabled if no 64bit type available. + o _strtol.h: to implement strto*l functions + +2021-07-04 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210704 + Merge with NetBSD make, pick up + o unit-tests: fix some tests to be more portable + - job-output-null not all shells do the same number of write calls + - objdir-writable if TMPDIR is set; /tmp may not be usable + +2021-07-01 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210701 + Merge with NetBSD make, pick up + o unit-tests: allow for BROKEN_TESTS to list TESTS to be skipped; + some tests just cannot work in some environments. + o buf.c: simpler upper bound for length in Buf_AddInt + o cond.c: fix grammar in error message for malformed conditional + o for.c: prevent newline injection (from ${.newline}) in .for loops + o var.c: use more practical data type in RegexReplace + (avoid need for %zu) + extract RegexReplace from ModifyWord_SubstRegex + 2021-06-21 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210621 diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES index dc0f6f33c763..53b961468db6 100644 --- a/contrib/bmake/FILES +++ b/contrib/bmake/FILES @@ -7,6 +7,7 @@ PSD.doc/Makefile PSD.doc/tutorial.ms README VERSION +_strtol.h aclocal.m4 arch.c bmake.1 @@ -23,8 +24,6 @@ configure.in dir.c dir.h dirname.c -enum.c -enum.h filemon/filemon.h filemon/filemon_dev.c filemon/filemon_ktrace.c @@ -61,6 +60,8 @@ pathnames.h ranlib.h realpath.c setenv.c +sigact.h +sigaction.c sigcompat.c str.c str.h @@ -322,6 +323,8 @@ unit-tests/directive-for-escape.exp unit-tests/directive-for-escape.mk unit-tests/directive-for-generating-endif.exp unit-tests/directive-for-generating-endif.mk +unit-tests/directive-for-if.exp +unit-tests/directive-for-if.mk unit-tests/directive-for-lines.exp unit-tests/directive-for-lines.mk unit-tests/directive-for-null.exp @@ -684,6 +687,8 @@ unit-tests/varmod-l-name-to-value.exp unit-tests/varmod-l-name-to-value.mk unit-tests/varmod-localtime.exp unit-tests/varmod-localtime.mk +unit-tests/varmod-loop-delete.exp +unit-tests/varmod-loop-delete.mk unit-tests/varmod-loop-varname.exp unit-tests/varmod-loop-varname.mk unit-tests/varmod-loop.exp @@ -694,10 +699,14 @@ unit-tests/varmod-match.exp unit-tests/varmod-match.mk unit-tests/varmod-no-match.exp unit-tests/varmod-no-match.mk +unit-tests/varmod-order-numeric.exp +unit-tests/varmod-order-numeric.mk unit-tests/varmod-order-reverse.exp unit-tests/varmod-order-reverse.mk unit-tests/varmod-order-shuffle.exp unit-tests/varmod-order-shuffle.mk +unit-tests/varmod-order-string.exp +unit-tests/varmod-order-string.mk unit-tests/varmod-order.exp unit-tests/varmod-order.mk unit-tests/varmod-path.exp @@ -814,6 +823,8 @@ unit-tests/varname-dot-path.exp unit-tests/varname-dot-path.mk unit-tests/varname-dot-shell.exp unit-tests/varname-dot-shell.mk +unit-tests/varname-dot-suffixes.exp +unit-tests/varname-dot-suffixes.mk unit-tests/varname-dot-targets.exp unit-tests/varname-dot-targets.mk unit-tests/varname-empty.exp diff --git a/contrib/bmake/Makefile b/contrib/bmake/Makefile index 38ccb8a6a636..82d9db52af76 100644 --- a/contrib/bmake/Makefile +++ b/contrib/bmake/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.114 2020/11/13 21:47:25 sjg Exp $ +# $Id: Makefile,v 1.117 2021/12/04 18:51:30 sjg Exp $ PROG= bmake @@ -8,7 +8,6 @@ SRCS= \ compat.c \ cond.c \ dir.c \ - enum.c \ for.c \ hash.c \ job.c \ @@ -92,10 +91,11 @@ isBSD44:=${BSD44_LIST:M${OS}} .if ${isBSD44} == "" MANTARGET= cat INSTALL?=${srcdir}/install-sh -.if (${MACHINE} == "sun386") +.if ${MACHINE} == "sun386" # even I don't have one of these anymore :-) CFLAGS+= -DPORTAR -.elif (${MACHINE} != "sunos") +.elif ${OS} != "SunOS" +# assume the worst SRCS+= sigcompat.c CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART .endif @@ -131,7 +131,7 @@ EXTRACT_MAN=no MAN= ${PROG}.1 MAN1= ${MAN} -.if (${PROG} != "make") +.if ${PROG} != "make" CLEANFILES+= my.history .if make(${MAN}) || !exists(${srcdir}/${MAN}) my.history: @@ -189,11 +189,12 @@ main.o: ${srcdir}/VERSION CONFIGURE_DEPS += ${.CURDIR}/VERSION # we do not need or want the generated makefile CONFIGURE_ARGS += --without-makefile +AUTOCONF_GENERATED_MAKEFILE = Makefile.config .include .endif -SHARE_MK?=${SHAREDIR}/mk -MKSRC=${srcdir}/mk -INSTALL?=${srcdir}/install-sh +SHARE_MK ?= ${SHAREDIR}/mk +MKSRC = ${srcdir}/mk +INSTALL ?= ${srcdir}/install-sh .if ${MK_INSTALL_MK} == "yes" install: install-mk diff --git a/contrib/bmake/Makefile.config.in b/contrib/bmake/Makefile.config.in index 55cd60ca80ba..042b2570ff88 100644 --- a/contrib/bmake/Makefile.config.in +++ b/contrib/bmake/Makefile.config.in @@ -4,7 +4,7 @@ _MAKE_VERSION?=@_MAKE_VERSION@ prefix?= @prefix@ srcdir= @srcdir@ -CC?= @CC@ +CC= @CC@ @force_machine@MACHINE?= @machine@ @force_machine_arch@MACHINE_ARCH?= @machine_arch@ DEFAULT_SYS_PATH?= @default_sys_path@ diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION index 7c28f11013b7..8485c4810bf0 100644 --- a/contrib/bmake/VERSION +++ b/contrib/bmake/VERSION @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20210621 +_MAKE_VERSION=20211212 diff --git a/contrib/bmake/_strtol.h b/contrib/bmake/_strtol.h new file mode 100644 index 000000000000..51c71490ae57 --- /dev/null +++ b/contrib/bmake/_strtol.h @@ -0,0 +1,213 @@ +/* $NetBSD: _strtol.h,v 1.11 2017/07/06 21:08:44 joerg Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Original version ID: + * NetBSD: src/lib/libc/locale/_wcstol.h,v 1.2 2003/08/07 16:43:03 agc Exp + */ + +/* + * function template for strtol, strtoll and strtoimax. + * + * parameters: + * _FUNCNAME : function name + * __INT : return type + * __INT_MIN : lower limit of the return type + * __INT_MAX : upper limit of the return type + */ +#if defined(_KERNEL) || defined(_STANDALONE) || defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY) +__INT +_FUNCNAME(const char *nptr, char **endptr, int base) +#else +#include +#include "setlocale_local.h" +#define INT_FUNCNAME_(pre, name, post) pre ## name ## post +#define INT_FUNCNAME(pre, name, post) INT_FUNCNAME_(pre, name, post) + +static __INT +INT_FUNCNAME(_int_, _FUNCNAME, _l)(const char *nptr, char **endptr, + int base, locale_t loc) +#endif +{ + const char *s; + __INT acc, cutoff; + unsigned char c; + int i, neg, any, cutlim; + + _DIAGASSERT(nptr != NULL); + /* endptr may be NULL */ + + /* check base value */ + if (base && (base < 2 || base > 36)) { +#if !defined(_KERNEL) && !defined(_STANDALONE) + errno = EINVAL; + if (endptr != NULL) + /* LINTED interface specification */ + *endptr = __UNCONST(nptr); + return 0; +#else + panic("%s: invalid base %d", __func__, base); +#endif + } + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = nptr; +#if defined(_KERNEL) || defined(_STANDALONE) || \ + defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY) + do { + c = *s++; + } while (isspace(c)); +#else + do { + c = *s++; + } while (isspace_l(c, loc)); +#endif + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X') && + ((s[1] >= '0' && s[1] <= '9') || + (s[1] >= 'a' && s[1] <= 'f') || + (s[1] >= 'A' && s[1] <= 'F'))) { + c = s[1]; + s += 2; + base = 16; +#if 0 + } else if ((base == 0 || base == 2) && + c == '0' && (*s == 'b' || *s == 'B') && + (s[1] >= '0' && s[1] <= '1')) { + c = s[1]; + s += 2; + base = 2; +#endif + } else if (base == 0) + base = (c == '0' ? 8 : 10); + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = (__INT)(neg ? __INT_MIN : __INT_MAX); + cutlim = (int)(cutoff % base); + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = *s++) { + if (c >= '0' && c <= '9') + i = c - '0'; + else if (c >= 'a' && c <= 'z') + i = (c - 'a') + 10; + else if (c >= 'A' && c <= 'Z') + i = (c - 'A') + 10; + else + break; + if (i >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && i > cutlim)) { + acc = __INT_MIN; +#if !defined(_KERNEL) && !defined(_STANDALONE) + any = -1; + errno = ERANGE; +#else + any = 0; + break; +#endif + } else { + any = 1; + acc *= base; + acc -= i; + } + } else { + if (acc > cutoff || (acc == cutoff && i > cutlim)) { + acc = __INT_MAX; +#if !defined(_KERNEL) && !defined(_STANDALONE) + any = -1; + errno = ERANGE; +#else + any = 0; + break; +#endif + } else { + any = 1; + acc *= base; + acc += i; + } + } + } + if (endptr != NULL) + /* LINTED interface specification */ + *endptr = __UNCONST(any ? s - 1 : nptr); + return(acc); +} + +#if !defined(_KERNEL) && !defined(_STANDALONE) && \ + !defined(HAVE_NBTOOL_CONFIG_H) && !defined(BCS_ONLY) +__INT +_FUNCNAME(const char *nptr, char **endptr, int base) +{ + return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, _current_locale()); +} + +__INT +INT_FUNCNAME(, _FUNCNAME, _l)(const char *nptr, char **endptr, int base, locale_t loc) +{ + return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, loc); +} +#endif diff --git a/contrib/bmake/arch.c b/contrib/bmake/arch.c index 6d9dd60dfbe9..515713af7af7 100644 --- a/contrib/bmake/arch.c +++ b/contrib/bmake/arch.c @@ -1,4 +1,4 @@ -/* $NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $ */ +/* $NetBSD: arch.c,v 1.205 2021/12/12 22:41:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -147,7 +147,7 @@ struct ar_hdr { #include "dir.h" /* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $"); +MAKE_RCSID("$NetBSD: arch.c,v 1.205 2021/12/12 22:41:47 rillig Exp $"); typedef struct List ArchList; typedef struct ListNode ArchListNode; @@ -250,7 +250,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) char *cp; /* Pointer into line */ GNode *gn; /* New node */ MFStr libName; /* Library-part of specification */ - char *memName; /* Member-part of specification */ + FStr mem; /* Member-part of specification */ char saveChar; /* Ending delimiter of member-name */ bool expandLibName; /* Whether the parsed libName contains * variable expressions that need to be @@ -301,7 +301,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) pp_skip_whitespace(&cp); - memName = cp; + mem = FStr_InitRefer(cp); while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) { if (*cp == '$') { /* Expand nested variable expressions. */ @@ -342,7 +342,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) /* * If we didn't move anywhere, we must be done */ - if (cp == memName) + if (cp == mem.str) break; saveChar = *cp; @@ -363,22 +363,22 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) */ if (doSubst) { char *fullName; - char *p; - char *unexpandedMemName = memName; + char *p, *expandedMem; + const char *unexpandedMem = mem.str; - (void)Var_Subst(memName, scope, VARE_UNDEFERR, - &memName); + (void)Var_Subst(mem.str, scope, VARE_UNDEFERR, + &expandedMem); /* TODO: handle errors */ + mem = FStr_InitOwn(expandedMem); /* * Now form an archive spec and recurse to deal with * nested variables and multi-word variable values. */ - fullName = FullName(libName.str, memName); + fullName = FullName(libName.str, mem.str); p = fullName; - if (strchr(memName, '$') != NULL && - strcmp(memName, unexpandedMemName) == 0) { + if (strcmp(mem.str, unexpandedMem) == 0) { /* * Must contain dynamic sources, so we can't * deal with it now. Just create an ARCHV node @@ -398,9 +398,9 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) free(fullName); /* XXX: does unexpandedMemName leak? */ - } else if (Dir_HasWildcards(memName)) { + } else if (Dir_HasWildcards(mem.str)) { StringList members = LST_INIT; - SearchPath_Expand(&dirSearchPath, memName, &members); + SearchPath_Expand(&dirSearchPath, mem.str, &members); while (!Lst_IsEmpty(&members)) { char *member = Lst_Dequeue(&members); @@ -416,7 +416,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) Lst_Done(&members); } else { - char *fullname = FullName(libName.str, memName); + char *fullname = FullName(libName.str, mem.str); gn = Targ_GetNode(fullname); free(fullname); @@ -430,8 +430,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) gn->type |= OP_ARCHV; Lst_Append(gns, gn); } - if (doSubst) - free(memName); + FStr_Done(&mem); *cp = saveChar; } @@ -600,14 +599,14 @@ ArchStatMember(const char *archive, const char *member, bool addToCache) if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 && ch_isdigit(memName[sizeof AR_EFMT1 - 1])) { - int elen = atoi(memName + sizeof AR_EFMT1 - 1); + size_t elen = atoi(memName + sizeof AR_EFMT1 - 1); - if ((unsigned int)elen > MAXPATHLEN) + if (elen > MAXPATHLEN) goto badarch; - if (fread(memName, (size_t)elen, 1, arch) != 1) + if (fread(memName, elen, 1, arch) != 1) goto badarch; memName[elen] = '\0'; - if (fseek(arch, -elen, SEEK_CUR) != 0) + if (fseek(arch, -(long)elen, SEEK_CUR) != 0) goto badarch; if (DEBUG(ARCH) || DEBUG(MAKE)) debug_printf( @@ -839,14 +838,15 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh, if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 && (ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))) { - int elen = atoi(&out_arh->AR_NAME[sizeof AR_EFMT1 - 1]); + size_t elen = atoi( + &out_arh->AR_NAME[sizeof AR_EFMT1 - 1]); char ename[MAXPATHLEN + 1]; - if ((unsigned int)elen > MAXPATHLEN) { + if (elen > MAXPATHLEN) { fclose(arch); return NULL; } - if (fread(ename, (size_t)elen, 1, arch) != 1) { + if (fread(ename, elen, 1, arch) != 1) { fclose(arch); return NULL; } @@ -859,14 +859,14 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh, if (strncmp(ename, member, len) == 0) { /* Found as extended name */ if (fseek(arch, - -(long)sizeof(struct ar_hdr) - elen, - SEEK_CUR) != 0) { + -(long)(sizeof(struct ar_hdr) - elen), + SEEK_CUR) != 0) { fclose(arch); return NULL; } return arch; } - if (fseek(arch, -elen, SEEK_CUR) != 0) { + if (fseek(arch, -(long)elen, SEEK_CUR) != 0) { fclose(arch); return NULL; } @@ -882,7 +882,7 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh, */ out_arh->AR_SIZE[sizeof out_arh->AR_SIZE - 1] = '\0'; size = (int)strtol(out_arh->AR_SIZE, NULL, 10); - if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) { + if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) { fclose(arch); return NULL; } @@ -991,12 +991,12 @@ Arch_UpdateMemberMTime(GNode *gn) const char *nameEnd = strchr(nameStart, ')'); size_t nameLen = (size_t)(nameEnd - nameStart); - if ((pgn->flags & REMAKE) && + if (pgn->flags.remake && strncmp(nameStart, gn->name, nameLen) == 0) { Arch_UpdateMTime(pgn); gn->mtime = pgn->mtime; } - } else if (pgn->flags & REMAKE) { + } else if (pgn->flags.remake) { /* * Something which isn't a library depends on the * existence of this target, so it needs to exist. @@ -1036,6 +1036,35 @@ Arch_FindLib(GNode *gn, SearchPath *path) #endif } +/* ARGSUSED */ +static bool +RanlibOODate(const GNode *gn MAKE_ATTR_UNUSED) +{ +#ifdef RANLIBMAG + struct ar_hdr *arh; /* Header for __.SYMDEF */ + int tocModTime; /* The table-of-contents' mod time */ + + arh = ArchStatMember(gn->path, RANLIBMAG, false); + + if (arh == NULL) { + /* A library without a table of contents is out-of-date. */ + if (DEBUG(ARCH) || DEBUG(MAKE)) + debug_printf("no toc..."); + return true; + } + + tocModTime = (int)strtol(arh->ar_date, NULL, 10); + + if (DEBUG(ARCH) || DEBUG(MAKE)) + debug_printf("%s modified %s...", + RANLIBMAG, Targ_FmtTime(tocModTime)); + return gn->youngestChild == NULL || + gn->youngestChild->mtime > tocModTime; +#else + return false; +#endif +} + /* * Decide if a node with the OP_LIB attribute is out-of-date. Called from * GNode_IsOODate to make its life easier. @@ -1069,46 +1098,19 @@ Arch_FindLib(GNode *gn, SearchPath *path) bool Arch_LibOODate(GNode *gn) { - bool oodate; if (gn->type & OP_PHONY) { - oodate = true; + return true; } else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) { - oodate = false; + return false; } else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) || (gn->mtime > now) || (gn->youngestChild != NULL && gn->mtime < gn->youngestChild->mtime)) { - oodate = true; + return true; } else { -#ifdef RANLIBMAG - struct ar_hdr *arh; /* Header for __.SYMDEF */ - int modTimeTOC; /* The table-of-contents' mod time */ - - arh = ArchStatMember(gn->path, RANLIBMAG, false); - - if (arh != NULL) { - modTimeTOC = (int)strtol(arh->ar_date, NULL, 10); - - if (DEBUG(ARCH) || DEBUG(MAKE)) - debug_printf("%s modified %s...", - RANLIBMAG, - Targ_FmtTime(modTimeTOC)); - oodate = gn->youngestChild == NULL || - gn->youngestChild->mtime > modTimeTOC; - } else { - /* - * A library without a table of contents is out-of-date. - */ - if (DEBUG(ARCH) || DEBUG(MAKE)) - debug_printf("no toc..."); - oodate = true; - } -#else - oodate = false; -#endif + return RanlibOODate(gn); } - return oodate; } /* Initialize the archives module. */ diff --git a/contrib/bmake/bmake.1 b/contrib/bmake/bmake.1 index e0d8267994c1..daea73cf538d 100644 --- a/contrib/bmake/bmake.1 +++ b/contrib/bmake/bmake.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $ +.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd December 22, 2020 +.Dd December 12, 2021 .Dt BMAKE 1 .Os .Sh NAME @@ -1163,6 +1163,9 @@ executes. .It Ev .SHELL The pathname of the shell used to run target scripts. It is read-only. +.It Ev .SUFFIXES +The list of known suffixes. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH @@ -1233,8 +1236,20 @@ but selects all words which do not match .Ar pattern . .It Cm \&:O Orders every word in variable alphabetically. +.It Cm \&:On +Orders every word in variable numerically. +A number followed by one of +.Ql k , +.Ql M +or +.Ql G +is multiplied by the appropriate factor (1024 (k), 1048576 (M), or +1073741824 (G)). +Both upper- and lower-case letters are accepted. .It Cm \&:Or Orders every word in variable in reverse alphabetical order. +.It Cm \&:Orn +Orders every word in variable in reverse numerical order. .It Cm \&:Ox Shuffles the words in variable. The results will be different each time you are referring to the diff --git a/contrib/bmake/bmake.cat1 b/contrib/bmake/bmake.cat1 index d9723b35cf7f..73191dea7344 100644 --- a/contrib/bmake/bmake.cat1 +++ b/contrib/bmake/bmake.cat1 @@ -757,6 +757,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) .SHELL The pathname of the shell used to run target scripts. It is read-only. + .SUFFIXES The list of known suffixes. It is read-only. + .TARGETS The list of targets explicitly specified on the command line, if any. @@ -809,8 +811,16 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) :O Orders every word in variable alphabetically. + :On Orders every word in variable numerically. A number followed by one + of `k', `M' or `G' is multiplied by the appropriate factor (1024 + (k), 1048576 (M), or 1073741824 (G)). Both upper- and lower-case + letters are accepted. + :Or Orders every word in variable in reverse alphabetical order. + :Orn + Orders every word in variable in reverse numerical order. + :Ox Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion (`:=') to prevent such behavior. For example, @@ -1581,4 +1591,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) There is no way of escaping a space character in a filename. -FreeBSD 13.0 December 22, 2020 FreeBSD 13.0 +FreeBSD 13.0 December 12, 2021 FreeBSD 13.0 diff --git a/contrib/bmake/boot-strap b/contrib/bmake/boot-strap index d251649db670..40984edcbe62 100755 --- a/contrib/bmake/boot-strap +++ b/contrib/bmake/boot-strap @@ -119,7 +119,7 @@ # Simon J. Gerraty # RCSid: -# $Id: boot-strap,v 1.54 2020/11/13 21:47:25 sjg Exp $ +# $Id: boot-strap,v 1.57 2021/10/22 20:32:21 sjg Exp $ # # @(#) Copyright (c) 2001 Simon J. Gerraty # @@ -405,17 +405,6 @@ Bmake() { ) } -# there is actually a shell where type is not a builtin -# if type is missing, which(1) had better exists! -if (type cat) > /dev/null 2>&1; then -which() { - type "$@" | sed 's,[()],,g;s,^[^/][^/]*,,;q' -} -fi -# make sure test below uses the same diff that configure did -TOOL_DIFF=`which diff` -export TOOL_DIFF - op_configure() { $srcdir/configure $CONFIGURE_ARGS || exit 1 } @@ -431,7 +420,7 @@ op_build() { op_test() { [ -x bmake ] || op_build - Bmake test || exit 1 + Bmake test "$@" || exit 1 } op_clean() { @@ -483,5 +472,5 @@ You may need the -r or -R option to more/less to view it correctly. EOM } -op_$op +op_$op "$@" exit 0 diff --git a/contrib/bmake/bsd.after-import.mk b/contrib/bmake/bsd.after-import.mk index 0d48f3c26648..fec42c9deeed 100644 --- a/contrib/bmake/bsd.after-import.mk +++ b/contrib/bmake/bsd.after-import.mk @@ -1,4 +1,4 @@ -# $Id: bsd.after-import.mk,v 1.16 2020/07/12 03:39:01 sjg Exp $ +# $Id: bsd.after-import.mk,v 1.17 2021/10/22 06:31:32 sjg Exp $ # This makefile is for use when integrating bmake into a BSD build # system. Use this makefile after importing bmake. @@ -59,6 +59,7 @@ bootstrap: ${BMAKE_SRC}/boot-strap ${MAKEFILE} # Makefiles need a little more tweaking than say config.h MAKEFILE_SED = sed -e '/^MACHINE/d' \ -e '/include.*VERSION/d' \ + -e '/^CC=/s,=,?=,' \ -e '/^PROG/ { s,=,?=,;s,bmake,$${.CURDIR:T},; }' \ -e 's,^.-include,.sinclude,' \ -e '/^\..*include *len > 0) + Buf_AddByte(buf, '|'); + Buf_AddBytes(buf, name, strlen(name)); + } +} + /* Mark the buffer as empty, so it can be filled with data again. */ void Buf_Empty(Buffer *buf) diff --git a/contrib/bmake/buf.h b/contrib/bmake/buf.h index 938820e4745f..af36773405b0 100644 --- a/contrib/bmake/buf.h +++ b/contrib/bmake/buf.h @@ -1,4 +1,4 @@ -/* $NetBSD: buf.h,v 1.43 2021/04/03 11:08:40 rillig Exp $ */ +/* $NetBSD: buf.h,v 1.44 2021/11/28 22:48:06 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -111,6 +111,7 @@ void Buf_AddBytes(Buffer *, const char *, size_t); void Buf_AddBytesBetween(Buffer *, const char *, const char *); void Buf_AddStr(Buffer *, const char *); void Buf_AddInt(Buffer *, int); +void Buf_AddFlag(Buffer *, bool, const char *); void Buf_Empty(Buffer *); void Buf_Init(Buffer *); void Buf_InitSize(Buffer *, size_t); diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c index f8c47397f3df..df487dbdf254 100644 --- a/contrib/bmake/compat.c +++ b/contrib/bmake/compat.c @@ -1,4 +1,4 @@ -/* $NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $ */ +/* $NetBSD: compat.c,v 1.229 2021/11/28 23:12:51 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -99,7 +99,7 @@ #include "pathnames.h" /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $"); +MAKE_RCSID("$NetBSD: compat.c,v 1.229 2021/11/28 23:12:51 rillig Exp $"); static GNode *curTarg = NULL; static pid_t compatChild; @@ -187,7 +187,7 @@ DebugFailedTarget(const char *cmd, const GNode *gn) static bool UseShell(const char *cmd MAKE_ATTR_UNUSED) { -#if !defined(MAKE_NATIVE) +#if defined(FORCE_USE_SHELL) || !defined(MAKE_NATIVE) /* * In a non-native build, the host environment might be weird enough * that it's necessary to go through a shell to get the correct @@ -240,7 +240,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) * using a shell */ const char *volatile cmd = cmdp; - silent = (gn->type & OP_SILENT) != 0; + silent = (gn->type & OP_SILENT) != OP_NONE; errCheck = !(gn->type & OP_IGNORE); doIt = false; @@ -497,7 +497,7 @@ MakeUnmade(GNode *gn, GNode *pgn) * again. This is our signal to not attempt to do anything but abort * our parent as well. */ - gn->flags |= REMAKE; + gn->flags.remake = true; gn->made = BEINGMADE; if (!(gn->type & OP_MADE)) @@ -505,9 +505,9 @@ MakeUnmade(GNode *gn, GNode *pgn) MakeNodes(&gn->children, gn); - if (!(gn->flags & REMAKE)) { + if (!gn->flags.remake) { gn->made = ABORTED; - pgn->flags &= ~(unsigned)REMAKE; + pgn->flags.remake = false; return false; } @@ -565,7 +565,7 @@ MakeUnmade(GNode *gn, GNode *pgn) RunCommands(gn); curTarg = NULL; } else { - Job_Touch(gn, (gn->type & OP_SILENT) != 0); + Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE); } } else { gn->made = ERROR; @@ -585,13 +585,13 @@ MakeUnmade(GNode *gn, GNode *pgn) */ gn->made = MADE; if (Make_Recheck(gn) == 0) - pgn->flags |= FORCE; + pgn->flags.force = true; if (!(gn->type & OP_EXEC)) { - pgn->flags |= CHILDMADE; + pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, gn); } } else if (opts.keepgoing) { - pgn->flags &= ~(unsigned)REMAKE; + pgn->flags.remake = false; } else { PrintOnError(gn, "\nStop."); exit(1); @@ -612,11 +612,11 @@ MakeOther(GNode *gn, GNode *pgn) case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->made = ERROR; - pgn->flags &= ~(unsigned)REMAKE; + pgn->flags.remake = false; break; case MADE: if (!(gn->type & OP_EXEC)) { - pgn->flags |= CHILDMADE; + pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, gn); } break; @@ -663,7 +663,7 @@ Compat_Make(GNode *gn, GNode *pgn) * Already had an error when making this. * Tell the parent to abort. */ - pgn->flags &= ~(unsigned)REMAKE; + pgn->flags.remake = false; } else { MakeOther(gn, pgn); } diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c index a8d88d1d6816..96609682866b 100644 --- a/contrib/bmake/cond.c +++ b/contrib/bmake/cond.c @@ -1,4 +1,4 @@ -/* $NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $ */ +/* $NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -95,14 +95,12 @@ #include "dir.h" /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $"); +MAKE_RCSID("$NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $"); /* * The parsing of conditional expressions is based on this grammar: - * Or -> And '||' Or - * Or -> And - * And -> Term '&&' And - * And -> Term + * Or -> And ('||' And)* + * And -> Term ('&&' Term)* * Term -> Function '(' Argument ')' * Term -> Leaf Operator Leaf * Term -> Leaf @@ -151,9 +149,23 @@ typedef struct CondParser { bool plain; /* The function to apply on unquoted bare words. */ - bool (*evalBare)(size_t, const char *); + bool (*evalBare)(const char *); bool negateEvalBare; + /* + * Whether the left-hand side of a comparison may be an unquoted + * string. This is allowed for expressions of the form + * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is + * expanded before it is evaluated, due to ease of implementation. + * This means that at the point where the condition is evaluated, + * make cannot know anymore whether the left-hand side had originally + * been a variable expression or a plain word. + * + * In all other contexts, the left-hand side must either be a + * variable expression, a quoted string or a number. + */ + bool leftUnquotedOK; + const char *p; /* The remaining condition to parse */ Token curr; /* Single push-back token used in parsing */ @@ -171,24 +183,13 @@ static CondResult CondParser_Or(CondParser *par, bool); static unsigned int cond_depth = 0; /* current .if nesting level */ static unsigned int cond_min_depth = 0; /* depth at makefile open */ -static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" }; - -/* - * Indicate when we should be strict about lhs of comparisons. - * In strict mode, the lhs must be a variable expression or a string literal - * in quotes. In non-strict mode it may also be an unquoted string literal. - * - * True when CondEvalExpression is called from Cond_EvalLine (.if etc). - * False when CondEvalExpression is called from ApplyModifier_IfElse - * since lhs is already expanded, and at that point we cannot tell if - * it was a variable reference or not. - */ -static bool lhsStrict; +/* Names for ComparisonOp. */ +static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" }; static bool -is_token(const char *str, const char *tok, size_t len) +is_token(const char *str, const char *tok, unsigned char len) { - return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]); + return strncmp(str, tok, (size_t)len) == 0 && !ch_isalpha(str[len]); } static Token @@ -197,16 +198,6 @@ ToToken(bool cond) return cond ? TOK_TRUE : TOK_FALSE; } -/* Push back the most recent token read. We only need one level of this. */ -static void -CondParser_PushBack(CondParser *par, Token t) -{ - assert(par->curr == TOK_NONE); - assert(t != TOK_NONE); - - par->curr = t; -} - static void CondParser_SkipWhitespace(CondParser *par) { @@ -214,7 +205,9 @@ CondParser_SkipWhitespace(CondParser *par) } /* - * Parse the argument of a built-in function. + * Parse a single word, taking into account balanced parentheses as well as + * embedded expressions. Used for the argument of a built-in function as + * well as for bare words, which are then passed to the default function. * * Arguments: * *pp initially points at the '(', @@ -223,12 +216,12 @@ CondParser_SkipWhitespace(CondParser *par) * *out_arg receives the argument as string. * * func says whether the argument belongs to an actual function, or - * whether the parsed argument is passed to the default function. + * NULL when parsing a bare word. * - * Return the length of the argument, or 0 on error. + * Return the length of the argument, or an ambiguous 0 on error. */ static size_t -ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func, +ParseWord(CondParser *par, const char **pp, bool doEval, const char *func, char **out_arg) { const char *p = *pp; @@ -239,11 +232,6 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func, if (func != NULL) p++; /* Skip opening '(' - verified by caller */ - if (*p == '\0') { - *out_arg = NULL; /* Missing closing parenthesis: */ - return 0; /* .if defined( */ - } - cpp_skip_hspace(&p); Buf_InitSize(&argBuf, 16); @@ -299,9 +287,8 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func, } /* Test whether the given variable is defined. */ -/*ARGSUSED*/ static bool -FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg) +FuncDefined(const char *arg) { FStr value = Var_Value(SCOPE_CMDLINE, arg); bool result = value.str != NULL; @@ -309,10 +296,9 @@ FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg) return result; } -/* See if the given target is being made. */ -/*ARGSUSED*/ +/* See if the given target is requested to be made. */ static bool -FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg) +FuncMake(const char *arg) { StringListNode *ln; @@ -323,9 +309,8 @@ FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg) } /* See if the given file exists. */ -/*ARGSUSED*/ static bool -FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg) +FuncExists(const char *arg) { bool result; char *path; @@ -339,9 +324,8 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg) } /* See if the given node exists and is an actual target. */ -/*ARGSUSED*/ static bool -FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg) +FuncTarget(const char *arg) { GNode *gn = Targ_FindNode(arg); return gn != NULL && GNode_IsTarget(gn); @@ -351,9 +335,8 @@ FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg) * See if the given node exists and is an actual target with commands * associated with it. */ -/*ARGSUSED*/ static bool -FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg) +FuncCommands(const char *arg) { GNode *gn = Targ_FindNode(arg); return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands); @@ -375,12 +358,12 @@ TryParseNumber(const char *str, double *out_value) unsigned long ul_val; double dbl_val; - errno = 0; if (str[0] == '\0') { /* XXX: why is an empty string a number? */ *out_value = 0.0; return true; } + errno = 0; ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10); if (*end == '\0' && errno != ERANGE) { *out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val; @@ -412,17 +395,16 @@ is_separator(char ch) */ static bool CondParser_StringExpr(CondParser *par, const char *start, - bool const doEval, bool const quoted, - Buffer *buf, FStr *const inout_str) + bool doEval, bool quoted, + Buffer *buf, FStr *inout_str) { VarEvalMode emode; const char *nested_p; bool atStart; VarParseResult parseResult; - /* if we are in quotes, an undefined variable is ok */ - emode = doEval && !quoted ? VARE_UNDEFERR - : doEval ? VARE_WANTRES + emode = doEval && quoted ? VARE_WANTRES + : doEval ? VARE_UNDEFERR : VARE_PARSE_ONLY; nested_p = par->p; @@ -477,7 +459,7 @@ CondParser_StringExpr(CondParser *par, const char *start, * Sets out_quoted if the leaf was a quoted string literal. */ static void -CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS, +CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK, FStr *out_str, bool *out_quoted) { Buffer buf; @@ -525,11 +507,11 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS, goto cleanup; continue; default: - if (strictLHS && !quoted && *start != '$' && + if (!unquotedOK && !quoted && *start != '$' && !ch_isdigit(*start)) { /* * The left-hand side must be quoted, - * a variable reference or a number. + * a variable expression or a number. */ str = FStr_InitRefer(NULL); goto cleanup; @@ -541,15 +523,16 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS, } got_str: str = FStr_InitOwn(buf.data); + buf.data = NULL; cleanup: - Buf_DoneData(&buf); /* XXX: memory leak on failure? */ + Buf_Done(&buf); *out_str = str; } static bool -EvalBare(const CondParser *par, const char *arg, size_t arglen) +EvalBare(const CondParser *par, const char *arg) { - bool res = par->evalBare(arglen, arg); + bool res = par->evalBare(arg); return par->negateEvalBare ? !res : res; } @@ -573,11 +556,14 @@ EvalNotEmpty(CondParser *par, const char *value, bool quoted) /* For .if ${...}, check for non-empty string. This is different from * the evaluation function from that .if variant, which would test * whether a variable of the given name were defined. */ - /* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */ + /* + * XXX: Whitespace should count as empty, just as in + * CondParser_FuncCallEmpty. + */ if (par->plain) return value[0] != '\0'; - return EvalBare(par, value, strlen(value)); + return EvalBare(par, value); } /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ @@ -682,11 +668,7 @@ CondParser_Comparison(CondParser *par, bool doEval) ComparisonOp op; bool lhsQuoted, rhsQuoted; - /* - * Parse the variable spec and skip over it, saving its - * value in lhs. - */ - CondParser_Leaf(par, doEval, lhsStrict, &lhs, &lhsQuoted); + CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted); if (lhs.str == NULL) goto done_lhs; @@ -702,12 +684,12 @@ CondParser_Comparison(CondParser *par, bool doEval) if (par->p[0] == '\0') { Parse_Error(PARSE_FATAL, - "Missing right-hand-side of operator '%s'", opname[op]); + "Missing right-hand side of operator '%s'", opname[op]); par->printedError = true; goto done_lhs; } - CondParser_Leaf(par, doEval, false, &rhs, &rhsQuoted); + CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted); if (rhs.str == NULL) goto done_rhs; @@ -729,50 +711,37 @@ CondParser_Comparison(CondParser *par, bool doEval) * The argument to empty() is a variable name, optionally followed by * variable modifiers. */ -/*ARGSUSED*/ -static size_t -ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp, - bool doEval, const char *func MAKE_ATTR_UNUSED, - char **out_arg) +static bool +CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token) { + const char *cp = par->p; + Token tok; FStr val; - size_t magic_res; - /* We do all the work here and return the result as the length */ - *out_arg = NULL; + if (!is_token(cp, "empty", 5)) + return false; + cp += 5; - (*pp)--; /* Make (*pp)[1] point to the '('. */ - (void)Var_Parse(pp, SCOPE_CMDLINE, + cpp_skip_whitespace(&cp); + if (*cp != '(') + return false; + + cp--; /* Make cp[1] point to the '('. */ + (void)Var_Parse(&cp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val); /* TODO: handle errors */ - /* If successful, *pp points beyond the closing ')' now. */ - if (val.str == var_Error) { - FStr_Done(&val); - return (size_t)-1; + if (val.str == var_Error) + tok = TOK_ERROR; + else { + cpp_skip_whitespace(&val.str); + tok = val.str[0] != '\0' && doEval ? TOK_FALSE : TOK_TRUE; } - /* - * A variable is empty when it just contains spaces... - * 4/15/92, christos - */ - cpp_skip_whitespace(&val.str); - - /* - * For consistency with the other functions we can't generate the - * true/false here. - */ - magic_res = val.str[0] != '\0' ? 2 : 1; FStr_Done(&val); - return magic_res; -} - -/*ARGSUSED*/ -static bool -FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED) -{ - /* Magic values ahead, see ParseEmptyArg. */ - return arglen == 1; + *out_token = tok; + par->p = cp; + return true; } /* Parse a function call expression, such as 'defined(${file})'. */ @@ -780,61 +749,51 @@ static bool CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token) { static const struct fn_def { - const char *fn_name; - size_t fn_name_len; - size_t (*fn_parse)(CondParser *, const char **, bool, - const char *, char **); - bool (*fn_eval)(size_t, const char *); + const char fn_name[9]; + unsigned char fn_name_len; + bool (*fn_eval)(const char *); } fns[] = { - { "defined", 7, ParseFuncArg, FuncDefined }, - { "make", 4, ParseFuncArg, FuncMake }, - { "exists", 6, ParseFuncArg, FuncExists }, - { "empty", 5, ParseEmptyArg, FuncEmpty }, - { "target", 6, ParseFuncArg, FuncTarget }, - { "commands", 8, ParseFuncArg, FuncCommands } + { "defined", 7, FuncDefined }, + { "make", 4, FuncMake }, + { "exists", 6, FuncExists }, + { "target", 6, FuncTarget }, + { "commands", 8, FuncCommands } }; const struct fn_def *fn; char *arg = NULL; size_t arglen; const char *cp = par->p; - const struct fn_def *fns_end = fns + sizeof fns / sizeof fns[0]; + const struct fn_def *last_fn = fns + sizeof fns / sizeof fns[0] - 1; - for (fn = fns; fn != fns_end; fn++) { - if (!is_token(cp, fn->fn_name, fn->fn_name_len)) - continue; + for (fn = fns; !is_token(cp, fn->fn_name, fn->fn_name_len); fn++) + if (fn == last_fn) + return false; - cp += fn->fn_name_len; - cpp_skip_whitespace(&cp); - if (*cp != '(') - break; + cp += fn->fn_name_len; + cpp_skip_whitespace(&cp); + if (*cp != '(') + return false; - arglen = fn->fn_parse(par, &cp, doEval, fn->fn_name, &arg); - if (arglen == 0 || arglen == (size_t)-1) { - par->p = cp; - *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR; - return true; - } + arglen = ParseWord(par, &cp, doEval, fn->fn_name, &arg); + *out_token = ToToken(arglen != 0 && (!doEval || fn->fn_eval(arg))); - /* Evaluate the argument using the required function. */ - *out_token = ToToken(!doEval || fn->fn_eval(arglen, arg)); - free(arg); - par->p = cp; - return true; - } - - return false; + free(arg); + par->p = cp; + return true; } /* - * Parse a comparison such as '${VAR} == "value"', or a simple leaf without + * Parse a comparison that neither starts with '"' nor '$', such as the + * unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without * operator, which is a number, a variable expression or a string literal. + * + * TODO: Can this be merged into CondParser_Comparison? */ static Token CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) { Token t; char *arg = NULL; - size_t arglen; const char *cp; const char *cp1; @@ -855,7 +814,7 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) * XXX: Is it possible to have a variable expression evaluated twice * at this point? */ - arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg); + (void)ParseWord(par, &cp, doEval, NULL, &arg); cp1 = cp; cpp_skip_whitespace(&cp1); if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>') @@ -868,7 +827,7 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval) * after .if must have been taken literally, so the argument cannot * be empty - even if it contained a variable expansion. */ - t = ToToken(!doEval || EvalBare(par, arg, arglen)); + t = ToToken(!doEval || EvalBare(par, arg)); free(arg); return t; } @@ -934,12 +893,30 @@ CondParser_Token(CondParser *par, bool doEval) return CondParser_Comparison(par, doEval); default: + if (CondParser_FuncCallEmpty(par, doEval, &t)) + return t; if (CondParser_FuncCall(par, doEval, &t)) return t; return CondParser_ComparisonOrLeaf(par, doEval); } } +/* Skip the next token if it equals t. */ +static bool +CondParser_Skip(CondParser *par, Token t) +{ + Token actual; + + actual = CondParser_Token(par, false); + if (actual == t) + return true; + + assert(par->curr == TOK_NONE); + assert(actual != TOK_NONE); + par->curr = actual; + return false; +} + /* * Term -> '(' Or ')' * Term -> '!' Term @@ -980,56 +957,44 @@ CondParser_Term(CondParser *par, bool doEval) } /* - * And -> Term '&&' And - * And -> Term + * And -> Term ('&&' Term)* */ static CondResult CondParser_And(CondParser *par, bool doEval) { - CondResult res; - Token op; + CondResult res, rhs; - res = CondParser_Term(par, doEval); - if (res == CR_ERROR) - return CR_ERROR; - - op = CondParser_Token(par, doEval); - if (op == TOK_AND) { - if (res == CR_TRUE) - return CondParser_And(par, doEval); - if (CondParser_And(par, false) == CR_ERROR) + res = CR_TRUE; + do { + if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR) return CR_ERROR; - return res; - } + if (rhs == CR_FALSE) { + res = CR_FALSE; + doEval = false; + } + } while (CondParser_Skip(par, TOK_AND)); - CondParser_PushBack(par, op); return res; } /* - * Or -> And '||' Or - * Or -> And + * Or -> And ('||' And)* */ static CondResult CondParser_Or(CondParser *par, bool doEval) { - CondResult res; - Token op; + CondResult res, rhs; - res = CondParser_And(par, doEval); - if (res == CR_ERROR) - return CR_ERROR; - - op = CondParser_Token(par, doEval); - if (op == TOK_OR) { - if (res == CR_FALSE) - return CondParser_Or(par, doEval); - if (CondParser_Or(par, false) == CR_ERROR) + res = CR_FALSE; + do { + if ((rhs = CondParser_And(par, doEval)) == CR_ERROR) return CR_ERROR; - return res; - } + if (rhs == CR_TRUE) { + res = CR_TRUE; + doEval = false; + } + } while (CondParser_Skip(par, TOK_OR)); - CondParser_PushBack(par, op); return res; } @@ -1060,23 +1025,22 @@ CondParser_Eval(CondParser *par, bool *out_value) * COND_PARSE if the condition was valid grammatically * COND_INVALID if not a valid conditional. * - * (*value) is set to the boolean value of the condition + * *out_value is set to the boolean value of the condition */ static CondEvalResult CondEvalExpression(const char *cond, bool *out_value, bool plain, - bool (*evalBare)(size_t, const char *), bool negate, - bool eprint, bool strictLHS) + bool (*evalBare)(const char *), bool negate, + bool eprint, bool leftUnquotedOK) { CondParser par; CondEvalResult rval; - lhsStrict = strictLHS; - cpp_skip_hspace(&cond); par.plain = plain; par.evalBare = evalBare; par.negateEvalBare = negate; + par.leftUnquotedOK = leftUnquotedOK; par.p = cond; par.curr = TOK_NONE; par.printedError = false; @@ -1097,7 +1061,7 @@ CondEvalResult Cond_EvalCondition(const char *cond, bool *out_value) { return CondEvalExpression(cond, out_value, true, - FuncDefined, false, false, false); + FuncDefined, false, false, true); } static bool @@ -1109,7 +1073,7 @@ IsEndif(const char *p) static bool DetermineKindOfConditional(const char **pp, bool *out_plain, - bool (**out_evalBare)(size_t, const char *), + bool (**out_evalBare)(const char *), bool *out_negate) { const char *p = *pp; @@ -1198,7 +1162,7 @@ Cond_EvalLine(const char *line) static unsigned int cond_states_cap = 128; bool plain; - bool (*evalBare)(size_t, const char *); + bool (*evalBare)(const char *); bool negate; bool isElif; bool value; @@ -1217,7 +1181,7 @@ Cond_EvalLine(const char *line) if (IsEndif(p)) { /* It is an '.endif'. */ if (p[5] != '\0') { Parse_Error(PARSE_FATAL, - "The .endif directive does not take arguments."); + "The .endif directive does not take arguments"); } if (cond_depth == cond_min_depth) { @@ -1236,7 +1200,7 @@ Cond_EvalLine(const char *line) if (p[1] != 'l') { /* * Unknown directive. It might still be a - * transformation rule like '.elisp.scm', + * transformation rule like '.err.txt', * therefore no error message here. */ return COND_INVALID; @@ -1248,8 +1212,8 @@ Cond_EvalLine(const char *line) if (p[2] != '\0') Parse_Error(PARSE_FATAL, - "The .else directive " - "does not take arguments."); + "The .else directive " + "does not take arguments"); if (cond_depth == cond_min_depth) { Parse_Error(PARSE_FATAL, "if-less else"); @@ -1328,7 +1292,7 @@ Cond_EvalLine(const char *line) /* And evaluate the conditional expression */ if (CondEvalExpression(p, &value, plain, evalBare, negate, - true, true) == COND_INVALID) { + true, false) == COND_INVALID) { /* Syntax error in conditional, error message already output. */ /* Skip everything to matching .endif */ /* XXX: An extra '.else' is not detected in this case. */ diff --git a/contrib/bmake/config.h.in b/contrib/bmake/config.h.in index 18f07aeda37f..aebebe783a44 100644 --- a/contrib/bmake/config.h.in +++ b/contrib/bmake/config.h.in @@ -9,6 +9,9 @@ /* Shell spec to use by default */ #undef DEFSHELL_INDEX +/* Path of default shell */ +#undef DEFSHELL_PATH + /* Define to 1 if you have the header file. */ #undef HAVE_AR_H @@ -65,6 +68,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H +/* Define to 1 if the system has the type `long long int'. */ +#undef HAVE_LONG_LONG_INT + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -98,15 +104,36 @@ /* Define to 1 if you have the `setpgid' function. */ #undef HAVE_SETPGID +/* Define to 1 if you have the `setrlimit' function. */ +#undef HAVE_SETRLIMIT + /* Define to 1 if you have the `setsid' function. */ #undef HAVE_SETSID /* Define to 1 if you have the `sigaction' function. */ #undef HAVE_SIGACTION +/* Define to 1 if you have the `sigaddset' function. */ +#undef HAVE_SIGADDSET + +/* Define to 1 if you have the `sigpending' function. */ +#undef HAVE_SIGPENDING + +/* Define to 1 if you have the `sigprocmask' function. */ +#undef HAVE_SIGPROCMASK + +/* Define to 1 if you have the `sigsetmask' function. */ +#undef HAVE_SIGSETMASK + +/* Define to 1 if you have the `sigsuspend' function. */ +#undef HAVE_SIGSUSPEND + /* Define to 1 if you have the `sigvec' function. */ #undef HAVE_SIGVEC +/* Define to 1 if the system has the type `sig_atomic_t'. */ +#undef HAVE_SIG_ATOMIC_T + /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF @@ -143,6 +170,12 @@ /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL +/* Define to 1 if you have the `strtoll' function. */ +#undef HAVE_STRTOLL + +/* Define to 1 if you have the `strtoul' function. */ +#undef HAVE_STRTOUL + /* Define to 1 if `st_rdev' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_RDEV @@ -197,6 +230,9 @@ /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV +/* Define to 1 if the system has the type `unsigned long long int'. */ +#undef HAVE_UNSIGNED_LONG_LONG_INT + /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H @@ -324,6 +360,16 @@ /* Define to empty if `const' does not conform to ANSI C. */ #undef const +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to the type of a signed integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef int64_t + /* Define to `int' if does not define. */ #undef mode_t diff --git a/contrib/bmake/configure b/contrib/bmake/configure index 6dbf5ad31a00..42295d1db3e3 100755 --- a/contrib/bmake/configure +++ b/contrib/bmake/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bmake 20210201. +# Generated by GNU Autoconf 2.69 for bmake 20211020. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bmake' PACKAGE_TARNAME='bmake' -PACKAGE_VERSION='20210201' -PACKAGE_STRING='bmake 20210201' +PACKAGE_VERSION='20211020' +PACKAGE_STRING='bmake 20211020' PACKAGE_BUGREPORT='sjg@NetBSD.org' PACKAGE_URL='' @@ -622,11 +622,13 @@ ac_includes_default="\ #endif" ac_subst_vars='LTLIBOBJS +UTC_1 _MAKE_VERSION filemon_h use_filemon use_meta diff_u +diff GCC INSTALL default_sys_path @@ -1255,7 +1257,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bmake 20210201 to adapt to many kinds of systems. +\`configure' configures bmake 20211020 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1316,7 +1318,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bmake 20210201:";; + short | recursive ) echo "Configuration of bmake 20211020:";; esac cat <<\_ACEOF @@ -1422,7 +1424,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bmake configure 20210201 +bmake configure 20211020 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1721,6 +1723,82 @@ fi } # ac_fn_c_try_link +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache @@ -2002,7 +2080,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bmake $as_me 20210201, which was +It was created by bmake $as_me 20211020, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2355,26 +2433,41 @@ ac_config_headers="$ac_config_headers config.h" case "$srcdir" in /*) ;; -*) srcdir=`cd $srcdir && pwd`;; +*) srcdir=`cd $srcdir && 'pwd'`;; esac . $srcdir/VERSION OS=`uname -s` +use_defshell() { + case "$defshell_path$DEFSHELL_INDEX" in + "") ;; + *) return 0;; + esac + case "$1" in + *csh) # we must be desperate + DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; + *ksh) + DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; + sh|/bin/sh) + DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; + *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM + defshell_path=$1 + ;; + esac + case "$1" in + /bin/*) ;; + */*) defshell_path=$1;; + esac +} # Check whether --with-defshell was given. if test "${with_defshell+set}" = set; then : withval=$with_defshell; case "${withval}" in yes) as_fn_error $? "bad value ${withval} given for bmake DEFSHELL" "$LINENO" 5 ;; no) ;; -*) case "$with_defshell" in - sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway - ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; - csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right? - *) defshell_path=$with_defshell;; # better be sh compatible! - esac - ;; - esac +*) use_defshell $with_defshell;; +esac fi case "$OS" in @@ -2446,6 +2539,37 @@ yes) esac ;; esac +case "$OS" in +Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE" + test -x /usr/pkg/bin/clang && CC=${CC:-clang} + ;; +SCO_SV) # /bin/sh is not usable + ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh" + CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL" + ;; +esac +echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6 +if test -f /usr/share/zoneinfo/Europe/Berlin; then + echo yes >&6 + # seems a safe bet + UTC_1=Europe/Berlin +else + utcH=`TZ=UTC date +%H 2> /dev/null` + utc_1H=`TZ=UTC-1 date +%H 2> /dev/null` + if test ${utcH-0} -lt ${utc_1H-0}; then + UTC_1=UTC-1 + echo no, using UTC-1 >&6 + else + echo no >&6 + fi +fi +oldPATH=$PATH +for d in /usr/gnu/bin +do + test -d $d || continue + PATH=$PATH:$d +done +export PATH ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -4543,7 +4667,29 @@ if test $bmake_path_max -gt 1024; then bmake_path_max=1024 fi echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6 - +if (type cat) > /dev/null 2>&1; then +: which +which() { + type "$@" | sed 's,[()],,g;s,^[^/][^/]*,,;q' +} +fi +case "$CC" in +/*) ;; +*) + for x in $CC + do + _cc=`which $x` + break + done + if test -x ${_cc:-/dev/null}; then + _cc_dir=`dirname $_cc` + case ":$oldPATH:" in + *:$_cc_dir:*) ;; + *) CC=$_cc_dir/$CC;; + esac + fi + ;; +esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 $as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } if ${ac_cv_header_sys_wait_h+:} false; then : @@ -4803,22 +4949,6 @@ done ac_fn_c_check_header_mongrel "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default" if test "x$ac_cv_header_sys_cdefs_h" = xyes; then : - echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6 -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#ifdef __RCSID -yes -#endif - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "yes" >/dev/null 2>&1; then : - echo yes >&6 -else - echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H" -fi -rm -f conftest* else CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`" @@ -5175,6 +5305,176 @@ $as_echo "#define const /**/" >>confdefs.h fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" +case $ac_cv_c_int64_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int64_t $ac_cv_c_int64_t +_ACEOF +;; +esac + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5 +$as_echo_n "checking for unsigned long long int... " >&6; } +if ${ac_cv_type_unsigned_long_long_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_type_unsigned_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + /* For now, do not test the preprocessor; as of 2007 there are too many + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ + /* Test literals. */ + long long int ll = 9223372036854775807ll; + long long int nll = -9223372036854775807LL; + unsigned long long int ull = 18446744073709551615ULL; + /* Test constant expressions. */ + typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) + ? 1 : -1)]; + typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 + ? 1 : -1)]; + int i = 63; +int +main () +{ +/* Test availability of runtime routines for shift and division. */ + long long int llmax = 9223372036854775807ll; + unsigned long long int ullmax = 18446744073709551615ull; + return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull)); + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +else + ac_cv_type_unsigned_long_long_int=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5 +$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; } + if test $ac_cv_type_unsigned_long_long_int = yes; then + +$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h + + fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5 +$as_echo_n "checking for long long int... " >&6; } +if ${ac_cv_type_long_long_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_type_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #ifndef LLONG_MAX + # define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + # define LLONG_MAX (HALF - 1 + HALF) + #endif +int +main () +{ +long long int n = 1; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_type_long_long_int=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 +$as_echo "$ac_cv_type_long_long_int" >&6; } + if test $ac_cv_type_long_long_int = yes; then + +$as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h + + fi + ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" if test "x$ac_cv_type_mode_t" = xyes; then : @@ -5332,9 +5632,9 @@ ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" " " if test "x$ac_cv_type_sig_atomic_t" = xyes; then : -else - -$as_echo "#define sig_atomic_t int" >>confdefs.h +cat >>confdefs.h <<_ACEOF +#define HAVE_SIG_ATOMIC_T 1 +_ACEOF fi @@ -5674,7 +5974,6 @@ for ac_func in \ errx \ getcwd \ getenv \ - getopt \ getwd \ killpg \ mmap \ @@ -5682,15 +5981,23 @@ for ac_func in \ select \ setenv \ setpgid \ + setrlimit \ setsid \ - sigaction \ + sigaddset \ + sigpending \ + sigprocmask \ + sigsetmask \ + sigsuspend \ sigvec \ snprintf \ strerror \ + stresep \ strftime \ strsep \ strtod \ strtol \ + strtoll \ + strtoul \ sysctl \ unsetenv \ vsnprintf \ @@ -5712,6 +6019,19 @@ fi done +ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt" +if test "x$ac_cv_func_getopt" = xyes; then : + $as_echo "#define HAVE_GETOPT 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" getopt.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getopt.$ac_objext" + ;; +esac + +fi + ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath" if test "x$ac_cv_func_realpath" = xyes; then : $as_echo "#define HAVE_REALPATH 1" >>confdefs.h @@ -5738,6 +6058,19 @@ esac fi +ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction" +if test "x$ac_cv_func_sigaction" = xyes; then : + $as_echo "#define HAVE_SIGACTION 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" sigaction.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS sigaction.$ac_objext" + ;; +esac + +fi + ac_fn_c_check_func "$LINENO" "stresep" "ac_cv_func_stresep" if test "x$ac_cv_func_stresep" = xyes; then : $as_echo "#define HAVE_STRESEP 1" >>confdefs.h @@ -6006,13 +6339,19 @@ $as_echo "#define __func__ __FUNCTION__" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6 -if diff -u /dev/null /dev/null > /dev/null 2>&1; then - diff_u=-u - echo yes >&6 +if test -x /usr/gnu/bin/diff; then + diff=/usr/gnu/bin/diff + diff_u=-u else - diff_u= - echo no >&6 + diff=${diff:-diff} + echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6 + if $diff -u /dev/null /dev/null > /dev/null 2>&1; then + diff_u=-u + echo yes >&6 + else + diff_u= + echo no >&6 + fi fi echo "checking for MACHINE & MACHINE_ARCH..." >&6 cat > conftest.$ac_ext <&6 -if test -x /usr/xpg4/bin/sh; then - defshell_path=${defshell_path:-/usr/xpg4/bin/sh} -fi -if test -n "$defshell_path"; then +for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS +do + test -x $sh || continue + use_defshell $sh + break +done +case "$defshell_path$DEFSHELL_INDEX" in +"") ;; +*DEFSHELL_INDEX_CUSTOM) echo "Using: SHELL=$defshell_path" >&6 cat >>confdefs.h <<_ACEOF #define DEFSHELL_CUSTOM "$defshell_path" _ACEOF -fi -if test -n "$DEFSHELL_INDEX"; then + ;; +/*INDEX*) + echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6 cat >>confdefs.h <<_ACEOF #define DEFSHELL_INDEX $DEFSHELL_INDEX _ACEOF -fi + +cat >>confdefs.h <<_ACEOF +#define DEFSHELL_PATH "$defshell_path" +_ACEOF + + ;; +*) + echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6 + +cat >>confdefs.h <<_ACEOF +#define DEFSHELL_INDEX $DEFSHELL_INDEX +_ACEOF + + ;; +esac + + @@ -6175,6 +6536,18 @@ bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh" if test $use_makefile = yes; then bm_outfiles="makefile $bm_outfiles" fi + +here=`'pwd'` +: srcdir=$srcdir +: here= $here +case "$here" in +$srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place + obj=`basename $here` + mkdir -p $srcdir/unit-tests/$obj + test -d unit-tests || ln -s ../unit-tests/$obj unit-tests + ;; +esac + ac_config_files="$ac_config_files $bm_outfiles" cat >confcache <<\_ACEOF @@ -6684,7 +7057,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bmake $as_me 20210201, which was +This file was extended by bmake $as_me 20211020, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6746,7 +7119,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bmake config.status 20210201 +bmake config.status 20211020 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/contrib/bmake/configure.in b/contrib/bmake/configure.in index 877493594a20..3eefa1f25c39 100644 --- a/contrib/bmake/configure.in +++ b/contrib/bmake/configure.in @@ -1,37 +1,53 @@ dnl dnl RCSid: -dnl $Id: configure.in,v 1.70 2021/02/01 18:29:26 sjg Exp $ +dnl $Id: configure.in,v 1.85 2021/10/23 20:57:08 sjg Exp $ dnl dnl Process this file with autoconf to produce a configure script dnl AC_PREREQ(2.50) -AC_INIT([bmake], [20210201], [sjg@NetBSD.org]) +AC_INIT([bmake], [20211020], [sjg@NetBSD.org]) AC_CONFIG_HEADERS(config.h) dnl make srcdir absolute case "$srcdir" in /*) ;; -*) srcdir=`cd $srcdir && pwd`;; +*) srcdir=`cd $srcdir && 'pwd'`;; esac dnl get _MAKE_VERSION . $srcdir/VERSION OS=`uname -s` +dnl function to set DEFSHELL_INDEX +use_defshell() { + case "$defshell_path$DEFSHELL_INDEX" in + "") ;; + *) return 0;; + esac + case "$1" in + *csh) # we must be desperate + DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; + *ksh) + DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; + sh|/bin/sh) + DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; + *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM + defshell_path=$1 + ;; + esac + case "$1" in + /bin/*) ;; + */*) defshell_path=$1;; + esac +} dnl AC_ARG_WITH(defshell, [ --with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions], [case "${withval}" in yes) AC_MSG_ERROR(bad value ${withval} given for bmake DEFSHELL) ;; no) ;; -*) case "$with_defshell" in - sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway - ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;; - csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right? - *) defshell_path=$with_defshell;; # better be sh compatible! - esac - ;; - esac]) +*) use_defshell $with_defshell;; +esac]) dnl case "$OS" in CYGWIN*|MINGW*) use_makefile=no;; @@ -97,6 +113,45 @@ yes) esac dnl dnl Check for OS problems +dnl +dnl Minix 3 at least has bugs in headers where _NETBSD_SOURCE +dnl is needed for compilation +case "$OS" in +Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE" + test -x /usr/pkg/bin/clang && CC=${CC:-clang} + ;; +SCO_SV) # /bin/sh is not usable + ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh" + CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL" + ;; +esac +dnl +dnl Not everyone groks TZ=Europe/Berlin +dnl which is used by the localtime tests +echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6 +if test -f /usr/share/zoneinfo/Europe/Berlin; then + echo yes >&6 + # seems a safe bet + UTC_1=Europe/Berlin +else + utcH=`TZ=UTC date +%H 2> /dev/null` + utc_1H=`TZ=UTC-1 date +%H 2> /dev/null` + if test ${utcH-0} -lt ${utc_1H-0}; then + UTC_1=UTC-1 + echo no, using UTC-1 >&6 + else + echo no >&6 + fi +fi +dnl +dnl Add some places to look for compilers +oldPATH=$PATH +for d in /usr/gnu/bin +do + test -d $d || continue + PATH=$PATH:$d +done +export PATH dnl Solaris's signal.h only privides sigset_t etc if one of dnl _EXTENSIONS_ _POSIX_C_SOURCE or _XOPEN_SOURCE are defined. dnl The later two seem to cause more problems than they solve so if we @@ -126,7 +181,33 @@ AC_SUBST(bmake_path_max)dnl dnl dnl AC_C_CROSS dnl - +dnl if type does not work which(1) had better! +dnl note we cannot rely on type returning non-zero on failure +if (type cat) > /dev/null 2>&1; then +: which +which() { + type "$@" | sed 's,[[()]],,g;s,^[[^/]][[^/]]*,,;q' +} +fi +dnl if CC is somewhere that was not in PATH we need its full path +dnl watch out for included flags! +case "$CC" in +/*) ;; +*) + for x in $CC + do + _cc=`which $x` + break + done + if test -x ${_cc:-/dev/null}; then + _cc_dir=`dirname $_cc` + case ":$oldPATH:" in + *:$_cc_dir:*) ;; + *) CC=$_cc_dir/$CC;; + esac + fi + ;; +esac dnl Checks for header files. AC_HEADER_SYS_WAIT AC_HEADER_DIRENT @@ -158,24 +239,16 @@ AC_CHECK_HEADERS( \ dnl Both *BSD and Linux have sys/cdefs.h, most do not. dnl If it is missing, we add -I${srcdir}/missing to CFLAGS -dnl also if sys/cdefs.h does not have __RCSID we need to use ours -dnl but we need to include the host's one too *sigh* -AC_CHECK_HEADER(sys/cdefs.h, -echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6 -AC_EGREP_CPP(yes, -[#include -#ifdef __RCSID -yes -#endif -], -echo yes >&6, -echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H"), +AC_CHECK_HEADER(sys/cdefs.h,, CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`") dnl Checks for typedefs, structures, and compiler characteristics. AC_C___ATTRIBUTE__ AC_C_BIGENDIAN AC_C_CONST +AC_C_INLINE +AC_TYPE_INT64_T +AC_TYPE_LONG_LONG_INT AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_PID_T @@ -187,9 +260,8 @@ AC_STRUCT_TM dnl we need sig_atomic_t AH_TEMPLATE([sig_atomic_t],[type that signal handlers can safely frob]) -AC_CHECK_TYPE(sig_atomic_t,,[ -AC_DEFINE([sig_atomic_t],[int],) -],[ +AC_CHECK_TYPES([sig_atomic_t],[],[], +[ #ifdef HAVE_SYS_TYPES_H #include #endif @@ -207,7 +279,6 @@ AC_CHECK_FUNCS( \ errx \ getcwd \ getenv \ - getopt \ getwd \ killpg \ mmap \ @@ -215,15 +286,23 @@ AC_CHECK_FUNCS( \ select \ setenv \ setpgid \ + setrlimit \ setsid \ - sigaction \ + sigaddset \ + sigpending \ + sigprocmask \ + sigsetmask \ + sigsuspend \ sigvec \ snprintf \ strerror \ + stresep \ strftime \ strsep \ strtod \ strtol \ + strtoll \ + strtoul \ sysctl \ unsetenv \ vsnprintf \ @@ -236,8 +315,10 @@ AC_CHECK_FUNCS( \ dnl functions which we may need to provide AC_REPLACE_FUNCS( \ + getopt \ realpath \ dirname \ + sigaction \ stresep \ strlcpy \ ) @@ -261,13 +342,20 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[[const char *func = __func__;]])],, AC_DEFINE(__func__, __FUNCTION__, C99 function name)) dnl dnl we want this for unit-tests/Makefile -echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6 -if diff -u /dev/null /dev/null > /dev/null 2>&1; then - diff_u=-u - echo yes >&6 +dnl GNU diff is known to support -u +if test -x /usr/gnu/bin/diff; then + diff=/usr/gnu/bin/diff + diff_u=-u else - diff_u= - echo no >&6 + diff=${diff:-diff} + echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6 + if $diff -u /dev/null /dev/null > /dev/null 2>&1; then + diff_u=-u + echo yes >&6 + else + diff_u= + echo no >&6 + fi fi dnl dnl AC_* don't quite cut it. @@ -404,16 +492,28 @@ done mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"` echo "Using: MKSRC=$mksrc" 1>&6 dnl On some systems we want a different default shell by default -if test -x /usr/xpg4/bin/sh; then - defshell_path=${defshell_path:-/usr/xpg4/bin/sh} -fi -if test -n "$defshell_path"; then +for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS +do + test -x $sh || continue + use_defshell $sh + break +done +case "$defshell_path$DEFSHELL_INDEX" in +"") ;; +*DEFSHELL_INDEX_CUSTOM) echo "Using: SHELL=$defshell_path" >&6 AC_DEFINE_UNQUOTED(DEFSHELL_CUSTOM, "$defshell_path", Path of default shell) -fi -if test -n "$DEFSHELL_INDEX"; then - AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) -fi + ;; +/*INDEX*) + echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6 + AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) +AC_DEFINE_UNQUOTED(DEFSHELL_PATH, "$defshell_path", Path of default shell) + ;; +*) + echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6 + AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default) + ;; +esac dnl AC_SUBST(machine) AC_SUBST(force_machine) @@ -423,15 +523,29 @@ AC_SUBST(mksrc) AC_SUBST(default_sys_path) AC_SUBST(INSTALL) AC_SUBST(GCC) +AC_SUBST(diff) AC_SUBST(diff_u) AC_SUBST(use_meta) AC_SUBST(use_filemon) AC_SUBST(filemon_h) AC_SUBST(_MAKE_VERSION) +AC_SUBST(UTC_1) bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh" if test $use_makefile = yes; then bm_outfiles="makefile $bm_outfiles" fi + +here=`'pwd'` +: srcdir=$srcdir +: here= $here +case "$here" in +$srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place + obj=`basename $here` + mkdir -p $srcdir/unit-tests/$obj + test -d unit-tests || ln -s ../unit-tests/$obj unit-tests + ;; +esac + AC_OUTPUT($bm_outfiles) cat <cst_mtime), pathname); return 0; } - rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st); + rc = (useLstat ? lstat : stat)(pathname, &sys_st); if (rc == -1) return -1; /* don't cache negative lookups */ @@ -462,13 +456,13 @@ cached_stats(const char *pathname, struct cached_stat *out_cst, int cached_stat(const char *pathname, struct cached_stat *cst) { - return cached_stats(pathname, cst, CST_NONE); + return cached_stats(pathname, cst, false, false); } int cached_lstat(const char *pathname, struct cached_stat *cst) { - return cached_stats(pathname, cst, CST_LSTAT); + return cached_stats(pathname, cst, true, false); } /* Initialize the directories module. */ @@ -1127,9 +1121,8 @@ FindFileRelative(SearchPath *path, bool seenDotLast, } static bool -FindFileAbsolute(SearchPath *path, bool const seenDotLast, - const char *const name, const char *const base, - char **out_file) +FindFileAbsolute(SearchPath *path, bool seenDotLast, + const char *name, const char *base, char **out_file) { char *file; SearchPathNode *ln; @@ -1448,7 +1441,7 @@ ResolveFullName(GNode *gn) fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); - if (fullName == NULL && gn->flags & FROM_DEPEND && + if (fullName == NULL && gn->flags.fromDepend && !Lst_IsEmpty(&gn->implicitParents)) fullName = ResolveMovedDepends(gn); @@ -1471,7 +1464,7 @@ ResolveFullName(GNode *gn) * The found file is stored in gn->path, unless the node already had a path. */ void -Dir_UpdateMTime(GNode *gn, bool recheck) +Dir_UpdateMTime(GNode *gn, bool forceRefresh) { char *fullName; struct cached_stat cst; @@ -1488,7 +1481,7 @@ Dir_UpdateMTime(GNode *gn, bool recheck) fullName = ResolveFullName(gn); - if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) { + if (cached_stats(fullName, &cst, false, forceRefresh) < 0) { if (gn->type & OP_MEMBER) { if (fullName != gn->path) free(fullName); diff --git a/contrib/bmake/enum.c b/contrib/bmake/enum.c deleted file mode 100755 index a0ae8569b322..000000000000 --- a/contrib/bmake/enum.c +++ /dev/null @@ -1,80 +0,0 @@ -/* $NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $ */ - -/* - Copyright (c) 2020 Roland Illig - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS - BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - */ - -#include "make.h" - -MAKE_RCSID("$NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $"); - -/* - * Convert a bitset into a string representation, showing the names of the - * individual bits. - * - * Optionally, shortcuts for groups of bits can be added. To have an effect, - * they need to be listed before their individual bits. - */ -const char * -Enum_FlagsToString(char *buf, size_t buf_size, - int value, const EnumToStringSpec *spec) -{ - const char *buf_start = buf; - const char *sep = ""; - size_t sep_len = 0; - - for (; spec->es_value != 0; spec++) { - size_t name_len; - - if ((value & spec->es_value) != spec->es_value) - continue; - value &= ~spec->es_value; - - assert(buf_size >= sep_len + 1); - memcpy(buf, sep, sep_len); - buf += sep_len; - buf_size -= sep_len; - - name_len = strlen(spec->es_name); - assert(buf_size >= name_len + 1); - memcpy(buf, spec->es_name, name_len); - buf += name_len; - buf_size -= name_len; - - sep = ENUM__SEP; - sep_len = sizeof ENUM__SEP - 1; - } - - /* If this assertion fails, the listed enum values are incomplete. */ - assert(value == 0); - - if (buf == buf_start) - return "none"; - - assert(buf_size >= 1); - buf[0] = '\0'; - return buf_start; -} diff --git a/contrib/bmake/enum.h b/contrib/bmake/enum.h deleted file mode 100755 index e10fcae045f6..000000000000 --- a/contrib/bmake/enum.h +++ /dev/null @@ -1,179 +0,0 @@ -/* $NetBSD: enum.h,v 1.19 2021/03/15 16:00:05 rillig Exp $ */ - -/* - Copyright (c) 2020 Roland Illig - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS - BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef MAKE_ENUM_H -#define MAKE_ENUM_H - -/* Generate string representations for bitmasks and simple enums. */ - -#include - -typedef struct EnumToStringSpec { - int es_value; - const char *es_name; -} EnumToStringSpec; - - -const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *); - - -/* For Enum_FlagsToString, the separator between flags. */ -#define ENUM__SEP "|" - -/* - * Generate the string that joins all possible flags, to see how large the - * buffer must be. - */ -#define ENUM__JOIN_STR_1(v1) \ - #v1 -#define ENUM__JOIN_STR_2(v1, v2) \ - ENUM__JOIN_STR_1(v1) ENUM__SEP \ - ENUM__JOIN_STR_1(v2) -#define ENUM__JOIN_STR_4(v1, v2, v3, v4) \ - ENUM__JOIN_STR_2(v1, v2) ENUM__SEP \ - ENUM__JOIN_STR_2(v3, v4) -#define ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8) \ - ENUM__JOIN_STR_4(v1, v2, v3, v4) ENUM__SEP \ - ENUM__JOIN_STR_4(v5, v6, v7, v8) -#define ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ - v09, v10, v11, v12, v13, v14, v15, v16) \ - ENUM__JOIN_STR_8(v01, v02, v03, v04, v05, v06, v07, v08) ENUM__SEP \ - ENUM__JOIN_STR_8(v09, v10, v11, v12, v13, v14, v15, v16) - -#define ENUM__JOIN_2(part1, part2) \ - part1 ENUM__SEP part2 -#define ENUM__JOIN_3(part1, part2, part3) \ - part1 ENUM__SEP part2 ENUM__SEP part3 -#define ENUM__JOIN_4(part1, part2, part3, part4) \ - part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 -#define ENUM__JOIN_5(part1, part2, part3, part4, part5) \ - part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 ENUM__SEP part5 - -/* List the pairs of enum value and corresponding name. */ -#define ENUM__SPEC_1(v1) \ - { v1, #v1 } -#define ENUM__SPEC_2(v1, v2) \ - ENUM__SPEC_1(v1), \ - ENUM__SPEC_1(v2) -#define ENUM__SPEC_4(v1, v2, v3, v4) \ - ENUM__SPEC_2(v1, v2), \ - ENUM__SPEC_2(v3, v4) -#define ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8) \ - ENUM__SPEC_4(v1, v2, v3, v4), \ - ENUM__SPEC_4(v5, v6, v7, v8) -#define ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ - v09, v10, v11, v12, v13, v14, v15, v16) \ - ENUM__SPEC_8(v01, v02, v03, v04, v05, v06, v07, v08), \ - ENUM__SPEC_8(v09, v10, v11, v12, v13, v14, v15, v16) - -#define ENUM__SPECS_2(part1, part2) \ - { part1, part2, { 0, "" } } -#define ENUM__SPECS_3(part1, part2, part3) \ - { part1, part2, part3, { 0, "" } } -#define ENUM__SPECS_4(part1, part2, part3, part4) \ - { part1, part2, part3, part4, { 0, "" } } -#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \ - { part1, part2, part3, part4, part5, { 0, "" } } - - -/* Declare the necessary data structures for calling Enum_FlagsToString. */ -#define ENUM__FLAGS_RTTI(typnam, specs, joined) \ - static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ - enum { typnam ## _ ## ToStringSize = sizeof (joined) }; \ - MAKE_INLINE const char *typnam ## _ToString(char *buf, typnam value) \ - { return Enum_FlagsToString(buf, typnam ## _ ## ToStringSize, \ - value, typnam ## _ ## ToStringSpecs); \ - } \ - extern void enum_flags_rtti_dummy(void) - -/* - * Declare the necessary data structures for calling Enum_FlagsToString - * for an enum with 3 flags. - */ -#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \ - ENUM__FLAGS_RTTI(typnam, \ - ENUM__SPECS_2( \ - ENUM__SPEC_2(v1, v2), \ - ENUM__SPEC_1(v3)), \ - ENUM__JOIN_2( \ - ENUM__JOIN_STR_2(v1, v2), \ - ENUM__JOIN_STR_1(v3))) - -/* - * Declare the necessary data structures for calling Enum_FlagsToString - * for an enum with 6 flags. - */ -#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \ - ENUM__FLAGS_RTTI(typnam, \ - ENUM__SPECS_2( \ - ENUM__SPEC_4(v1, v2, v3, v4), \ - ENUM__SPEC_2(v5, v6)), \ - ENUM__JOIN_2( \ - ENUM__JOIN_STR_4(v1, v2, v3, v4), \ - ENUM__JOIN_STR_2(v5, v6))) - -/* - * Declare the necessary data structures for calling Enum_FlagsToString - * for an enum with 9 flags. - */ -#define ENUM_FLAGS_RTTI_9(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9) \ - ENUM__FLAGS_RTTI(typnam, \ - ENUM__SPECS_2( \ - ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \ - ENUM__SPEC_1(v9)), \ - ENUM__JOIN_2( \ - ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ - ENUM__JOIN_STR_1(v9))) - -/* - * Declare the necessary data structures for calling Enum_FlagsToString - * for an enum with 31 flags. - */ -#define ENUM_FLAGS_RTTI_31(typnam, \ - v01, v02, v03, v04, v05, v06, v07, v08, \ - v09, v10, v11, v12, v13, v14, v15, v16, \ - v17, v18, v19, v20, v21, v22, v23, v24, \ - v25, v26, v27, v28, v29, v30, v31) \ - ENUM__FLAGS_RTTI(typnam, \ - ENUM__SPECS_5( \ - ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ - v09, v10, v11, v12, v13, v14, v15, v16), \ - ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \ - ENUM__SPEC_4(v25, v26, v27, v28), \ - ENUM__SPEC_2(v29, v30), \ - ENUM__SPEC_1(v31)), \ - ENUM__JOIN_5( \ - ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ - v09, v10, v11, v12, v13, v14, v15, v16), \ - ENUM__JOIN_STR_8(v17, v18, v19, v20, v21, v22, v23, v24), \ - ENUM__JOIN_STR_4(v25, v26, v27, v28), \ - ENUM__JOIN_STR_2(v29, v30), \ - ENUM__JOIN_STR_1(v31))) - -#endif diff --git a/contrib/bmake/filemon/filemon_ktrace.c b/contrib/bmake/filemon/filemon_ktrace.c index 1abef7e78af1..53a85cbe74f7 100644 --- a/contrib/bmake/filemon/filemon_ktrace.c +++ b/contrib/bmake/filemon/filemon_ktrace.c @@ -1,4 +1,4 @@ -/* $NetBSD: filemon_ktrace.c,v 1.14 2021/02/01 21:34:41 rillig Exp $ */ +/* $NetBSD: filemon_ktrace.c,v 1.15 2021/07/31 09:30:17 rillig Exp $ */ /* * Copyright (c) 2019 The NetBSD Foundation, Inc. @@ -227,7 +227,6 @@ filemon_open(void) /* Success! */ return F; - (void)fclose(F->in); fail1: (void)close(ktrpipe[0]); (void)close(ktrpipe[1]); fail0: free(F); diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c index 615efb7634c9..013b0f21d841 100644 --- a/contrib/bmake/for.c +++ b/contrib/bmake/for.c @@ -1,4 +1,4 @@ -/* $NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $ */ +/* $NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. @@ -45,9 +45,9 @@ * * After reaching the .endfor, the values from the .for line are grouped * according to the number of variables. For each such group, the unexpanded - * body is scanned for variable expressions, and those that match the variable - * names are replaced with expressions of the form ${:U...} or $(:U...). - * After that, the body is treated like a file from an .include directive. + * body is scanned for variable expressions, and those that match the + * variable names are replaced with expressions of the form ${:U...}. After + * that, the body is treated like a file from an .include directive. * * Interface: * For_Eval Evaluate the loop in the passed line. @@ -58,7 +58,7 @@ #include "make.h" /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $"); +MAKE_RCSID("$NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $"); /* One of the variables to the left of the "in" in a .for loop. */ @@ -70,17 +70,13 @@ typedef struct ForVar { typedef struct ForLoop { Buffer body; /* Unexpanded body of the loop */ Vector /* of ForVar */ vars; /* Iteration variables */ - Words items; /* Substitution items */ + SubstringWords items; /* Substitution items */ Buffer curBody; /* Expanded body of the current iteration */ - /* Is any of the names 1 character long? If so, when the variable values - * are substituted, the parser must handle $V expressions as well, not - * only ${V} and $(V). */ - bool short_var; - unsigned int sub_next; /* Where to continue iterating */ + unsigned int nextItem; /* Where to continue iterating */ } ForLoop; -static ForLoop *accumFor; /* Loop being accumulated */ +static ForLoop *accumFor; /* Loop being accumulated */ static int forLevel = 0; /* Nesting level */ @@ -91,11 +87,9 @@ ForLoop_New(void) Buf_Init(&f->body); Vector_Init(&f->vars, sizeof(ForVar)); - f->items.words = NULL; - f->items.freeIt = NULL; + SubstringWords_Init(&f->items); Buf_Init(&f->curBody); - f->short_var = false; - f->sub_next = 0; + f->nextItem = 0; return f; } @@ -111,7 +105,7 @@ ForLoop_Free(ForLoop *f) } Vector_Done(&f->vars); - Words_Free(f->items); + SubstringWords_Free(f->items); Buf_Done(&f->curBody); free(f); @@ -150,8 +144,6 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp) p += 2; break; } - if (len == 1) - f->short_var = true; ForLoop_AddVar(f, p, len); p += len; @@ -178,10 +170,10 @@ ForLoop_ParseItems(ForLoop *f, const char *p) return false; } - f->items = Str_Words(items, false); + f->items = Substring_Words(items, false); free(items); - if (f->items.len == 1 && f->items.words[0][0] == '\0') + if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0])) f->items.len = 0; /* .for var in ${:U} */ if (f->items.len != 0 && f->items.len % f->vars.len != 0) { @@ -285,33 +277,32 @@ For_Accum(const char *line) static size_t -for_var_len(const char *var) +ExprLen(const char *s, const char *e) { - char ch, var_start, var_end; + char expr_open, expr_close; int depth; - size_t len; + const char *p; - var_start = *var; - if (var_start == '\0') - /* just escape the $ */ - return 0; + if (s == e) + return 0; /* just escape the '$' */ - if (var_start == '(') - var_end = ')'; - else if (var_start == '{') - var_end = '}'; + expr_open = s[0]; + if (expr_open == '(') + expr_close = ')'; + else if (expr_open == '{') + expr_close = '}'; else return 1; /* Single char variable */ depth = 1; - for (len = 1; (ch = var[len++]) != '\0';) { - if (ch == var_start) + for (p = s + 1; p != e; p++) { + if (*p == expr_open) depth++; - else if (ch == var_end && --depth == 0) - return len; + else if (*p == expr_close && --depth == 0) + return (size_t)(p + 1 - s); } - /* Variable end not found, escape the $ */ + /* Expression end not found, escape the $ */ return 0; } @@ -320,12 +311,13 @@ for_var_len(const char *var) * that characters that break this syntax must be backslash-escaped. */ static bool -NeedsEscapes(const char *value, char endc) +NeedsEscapes(Substring value, char endc) { const char *p; - for (p = value; *p != '\0'; p++) { - if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) + for (p = value.start; p != value.end; p++) { + if (*p == ':' || *p == '$' || *p == '\\' || *p == endc || + *p == '\n') return true; } return false; @@ -338,34 +330,44 @@ NeedsEscapes(const char *value, char endc) * The result is later unescaped by ApplyModifier_Defined. */ static void -Buf_AddEscaped(Buffer *cmds, const char *item, char endc) +Buf_AddEscaped(Buffer *cmds, Substring item, char endc) { + const char *p; char ch; if (!NeedsEscapes(item, endc)) { - Buf_AddStr(cmds, item); + Buf_AddBytesBetween(cmds, item.start, item.end); return; } /* Escape ':', '$', '\\' and 'endc' - these will be removed later by * :U processing, see ApplyModifier_Defined. */ - while ((ch = *item++) != '\0') { + for (p = item.start; p != item.end; p++) { + ch = *p; if (ch == '$') { - size_t len = for_var_len(item); + size_t len = ExprLen(p + 1, item.end); if (len != 0) { - Buf_AddBytes(cmds, item - 1, len + 1); - item += len; + /* + * XXX: Should a '\' be added here? + * See directive-for-escape.mk, ExprLen. + */ + Buf_AddBytes(cmds, p, 1 + len); + p += len; continue; } Buf_AddByte(cmds, '\\'); } else if (ch == ':' || ch == '\\' || ch == endc) Buf_AddByte(cmds, '\\'); + else if (ch == '\n') { + Parse_Error(PARSE_FATAL, "newline in .for value"); + ch = ' '; /* prevent newline injection */ + } Buf_AddByte(cmds, ch); } } /* - * While expanding the body of a .for loop, replace the variable name of an + * When expanding the body of a .for loop, replace the variable name of an * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". */ static void @@ -376,8 +378,8 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, const char *p = *pp; for (i = 0; i < f->vars.len; i++) { - ForVar *forVar = Vector_Get(&f->vars, i); - char *varname = forVar->name; + const ForVar *forVar = Vector_Get(&f->vars, i); + const char *varname = forVar->name; size_t varnameLen = forVar->nameLen; if (varnameLen >= (size_t)(bodyEnd - p)) @@ -396,7 +398,7 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, Buf_AddBytesBetween(&f->curBody, *inout_mark, p); Buf_AddStr(&f->curBody, ":U"); Buf_AddEscaped(&f->curBody, - f->items.words[f->sub_next + i], endc); + f->items.words[f->nextItem + i], endc); p += varnameLen; *inout_mark = p; @@ -406,18 +408,18 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, } /* - * While expanding the body of a .for loop, replace single-character + * When expanding the body of a .for loop, replace single-character * variable expressions like $i with their ${:U...} expansion. */ static void ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) { const char ch = *p; - ForVar *vars; + const ForVar *vars; size_t i; /* Skip $$ and stupid ones. */ - if (!f->short_var || strchr("}):$", ch) != NULL) + if (ch == '}' || ch == ')' || ch == ':' || ch == '$') return; vars = Vector_Get(&f->vars, 0); @@ -429,10 +431,12 @@ ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) return; found: + Buf_AddBytesBetween(&f->curBody, *inout_mark, p); + *inout_mark = p + 1; + /* Replace $ with ${:U} */ - Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1; Buf_AddStr(&f->curBody, "{:U"); - Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}'); + Buf_AddEscaped(&f->curBody, f->items.words[f->nextItem + i], '}'); Buf_AddByte(&f->curBody, '}'); } @@ -446,14 +450,14 @@ ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) * defined, see unit-tests/varname-empty.mk for more details. * * The detection of substitutions of the loop control variables is naive. - * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible - * to contrive a makefile where an unwanted substitution happens. + * Many of the modifiers use '\$' instead of '$$' to escape '$', so it is + * possible to contrive a makefile where an unwanted substitution happens. */ static void ForLoop_SubstBody(ForLoop *f) { const char *p, *bodyEnd; - const char *mark; /* where the last replacement left off */ + const char *mark; /* where the last substitution left off */ Buf_Empty(&f->curBody); @@ -461,9 +465,9 @@ ForLoop_SubstBody(ForLoop *f) bodyEnd = f->body.data + f->body.len; for (p = mark; (p = strchr(p, '$')) != NULL;) { if (p[1] == '{' || p[1] == '(') { + char endc = p[1] == '{' ? '}' : ')'; p += 2; - ForLoop_SubstVarLong(f, &p, bodyEnd, - p[-1] == '{' ? '}' : ')', &mark); + ForLoop_SubstVarLong(f, &p, bodyEnd, endc, &mark); } else if (p[1] != '\0') { ForLoop_SubstVarShort(f, p + 1, &mark); p += 2; @@ -483,7 +487,7 @@ ForReadMore(void *v_arg, size_t *out_len) { ForLoop *f = v_arg; - if (f->sub_next == f->items.len) { + if (f->nextItem == f->items.len) { /* No more iterations */ ForLoop_Free(f); return NULL; @@ -491,7 +495,7 @@ ForReadMore(void *v_arg, size_t *out_len) ForLoop_SubstBody(f); DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); - f->sub_next += (unsigned int)f->vars.len; + f->nextItem += (unsigned int)f->vars.len; *out_len = f->curBody.len; return f->curBody.data; @@ -513,5 +517,5 @@ For_Run(int lineno) return; } - Parse_SetInput(NULL, lineno, -1, ForReadMore, f); + Parse_PushInput(NULL, lineno, -1, ForReadMore, f); } diff --git a/contrib/bmake/hash.c b/contrib/bmake/hash.c index 8b503ac31fb5..ec0079fa4ba4 100644 --- a/contrib/bmake/hash.c +++ b/contrib/bmake/hash.c @@ -1,4 +1,4 @@ -/* $NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $ */ +/* $NetBSD: hash.c,v 1.66 2021/12/07 21:58:01 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -74,7 +74,7 @@ #include "make.h" /* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $"); +MAKE_RCSID("$NetBSD: hash.c,v 1.66 2021/12/07 21:58:01 rillig Exp $"); /* * The ratio of # entries to # buckets at which we rebuild the table to @@ -152,7 +152,7 @@ HashTable_FindEntryBySubstring(HashTable *t, Substring key, unsigned int h) unsigned int chainlen = 0; #ifdef DEBUG_HASH_LOOKUP - DEBUG4(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h, + DEBUG5(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h, (int)Substring_Length(key), key.start); #endif @@ -333,15 +333,6 @@ HashTable_DeleteEntry(HashTable *t, HashEntry *he) abort(); } -/* Set things up for iterating over all entries in the hash table. */ -void -HashIter_Init(HashIter *hi, HashTable *t) -{ - hi->table = t; - hi->nextBucket = 0; - hi->entry = NULL; -} - /* * Return the next entry in the hash table, or NULL if the end of the table * is reached. diff --git a/contrib/bmake/hash.h b/contrib/bmake/hash.h index 8e7a567b6dba..8ff5490bdd95 100644 --- a/contrib/bmake/hash.h +++ b/contrib/bmake/hash.h @@ -1,4 +1,4 @@ -/* $NetBSD: hash.h,v 1.40 2021/04/11 12:46:54 rillig Exp $ */ +/* $NetBSD: hash.h,v 1.41 2021/12/07 21:58:01 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -120,6 +120,15 @@ HashEntry_Set(HashEntry *h, void *datum) h->value = datum; } +/* Set things up for iterating over all entries in the hash table. */ +MAKE_INLINE void +HashIter_Init(HashIter *hi, HashTable *t) +{ + hi->table = t; + hi->nextBucket = 0; + hi->entry = NULL; +} + void HashTable_Init(HashTable *); void HashTable_Done(HashTable *); HashEntry *HashTable_FindEntry(HashTable *, const char *); diff --git a/contrib/bmake/import.sh b/contrib/bmake/import.sh index b80e120daab1..08f352609fe8 100755 --- a/contrib/bmake/import.sh +++ b/contrib/bmake/import.sh @@ -26,6 +26,7 @@ option_parsing() { *=*) eval "$1"; shift;; --) shift; break;; -a) TARBALL=$2; shift 2;; + -d) RM=echo; shift;; -n) ECHO=echo; shift;; -P) PR=$2; shift 2;; -r) REVIEWER=$2; shift 2;; @@ -55,6 +56,7 @@ TF=/tmp/.$USER.$$ Cd `dirname $0` test -s ${TARBALL:-/dev/null} || Error need TARBALL here=`pwd` +SB=${SB:-`dirname $here`} # thing should match what the TARBALL contains thing=`basename $here` @@ -66,7 +68,7 @@ esac VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'` rm -f *~ -mkdir -p ../tmp +mkdir -p $SB/tmp # new files are handled automatically # but we need to rm if needed @@ -81,12 +83,15 @@ comm -13 $TF.adds $TF.rms > $TF.rm if [ -z "$ECHO" ]; then test -s $TF.rm && xargs rm -f < $TF.rm $GIT add -A - $GIT diff --staged | tee ../tmp/bmake-import.diff - echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh - echo "After you commit, run $here/../tmp/bmake-post.sh" + $GIT diff --staged | tee $SB/tmp/bmake-import.diff + { echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" + echo "echo \"When ready do: $GIT push --follow-tags\"" + } > $SB/tmp/bmake-post.sh + echo "After you commit, run $SB/tmp/bmake-post.sh" else comm -23 $TF.adds $TF.rms > $TF.add test -s $TF.rm && { echo Removing:; cat $TF.rm; } test -s $TF.add && { echo Adding:; cat $TF.add; } $GIT diff fi +${RM:-rm} -f $TF.* diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c index e4b77477e816..f90c35bb6d49 100644 --- a/contrib/bmake/job.c +++ b/contrib/bmake/job.c @@ -1,4 +1,4 @@ -/* $NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $ */ +/* $NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -155,7 +155,7 @@ #include "trace.h" /* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $"); +MAKE_RCSID("$NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $"); /* * A shell defines how the commands are run. All commands for a target are @@ -794,8 +794,8 @@ ShellWriter_WriteFmt(ShellWriter *wr, const char *fmt, const char *arg) DEBUG1(JOB, fmt, arg); (void)fprintf(wr->f, fmt, arg); - /* XXX: Is flushing needed in any case, or only if f == stdout? */ - (void)fflush(wr->f); + if (wr->f == stdout) + (void)fflush(wr->f); } static void @@ -1097,10 +1097,20 @@ DebugFailedJob(const Job *job) if (!DEBUG(ERROR)) return; - debug_printf("\n*** Failed target: %s\n*** Failed commands:\n", - job->node->name); - for (ln = job->node->commands.first; ln != NULL; ln = ln->next) - debug_printf("\t%s\n", (const char *)ln->datum); + debug_printf("\n"); + debug_printf("*** Failed target: %s\n", job->node->name); + debug_printf("*** Failed commands:\n"); + for (ln = job->node->commands.first; ln != NULL; ln = ln->next) { + const char *cmd = ln->datum; + debug_printf("\t%s\n", cmd); + + if (strchr(cmd, '$') != NULL) { + char *xcmd; + (void)Var_Subst(cmd, job->node, VARE_WANTRES, &xcmd); + debug_printf("\t=> %s\n", xcmd); + free(xcmd); + } + } } static void @@ -1192,7 +1202,9 @@ JobFinish (Job *job, WAIT_T status) JobClosePipes(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { - (void)fclose(job->cmdFILE); + if (fclose(job->cmdFILE) != 0) + Punt("Cannot write shell script for '%s': %s", + job->node->name, strerror(errno)); job->cmdFILE = NULL; } done = true; @@ -1384,7 +1396,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) * this node's parents so they never get examined. */ - if (gn->flags & FROM_DEPEND) { + if (gn->flags.fromDepend) { if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n", @@ -1558,7 +1570,9 @@ JobExec(Job *job, char **argv) watchfd(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { - (void)fclose(job->cmdFILE); + if (fclose(job->cmdFILE) != 0) + Punt("Cannot write shell script for '%s': %s", + job->node->name, strerror(errno)); job->cmdFILE = NULL; } @@ -2128,7 +2142,7 @@ Job_CatchOutput(void) JobRestartJobs(); } else if (count == 0) Punt("unexpected eof on token pipe"); - else + else if (errno != EAGAIN) Punt("token pipe read: %s", strerror(errno)); nready--; } @@ -2182,8 +2196,11 @@ InitShellNameAndPath(void) return; } #endif - +#ifdef DEFSHELL_PATH + shellPath = DEFSHELL_PATH; +#else shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName); +#endif } void diff --git a/contrib/bmake/lst.h b/contrib/bmake/lst.h index cddd6439f611..02f1ae9ec38c 100644 --- a/contrib/bmake/lst.h +++ b/contrib/bmake/lst.h @@ -1,4 +1,4 @@ -/* $NetBSD: lst.h,v 1.98 2021/04/03 11:08:40 rillig Exp $ */ +/* $NetBSD: lst.h,v 1.99 2021/12/05 10:11:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -142,9 +142,9 @@ ListNode *Lst_FindDatum(List *, const void *); /* Insert a datum before the given node. */ void Lst_InsertBefore(List *, ListNode *, void *); -/* Place a datum at the front of the list. */ +/* Add a datum at the front of the list. */ void Lst_Prepend(List *, void *); -/* Place a datum at the end of the list. */ +/* Add a datum at the end of the list. */ void Lst_Append(List *, void *); /* Remove the node from the list. */ void Lst_Remove(List *, ListNode *); diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c index 85a8a1cce7a1..60be60c7ca90 100644 --- a/contrib/bmake/main.c +++ b/contrib/bmake/main.c @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $ */ +/* $NetBSD: main.c,v 1.541 2021/08/14 13:32:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -111,7 +111,7 @@ #include "trace.h" /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $"); +MAKE_RCSID("$NetBSD: main.c,v 1.541 2021/08/14 13:32:12 rillig Exp $"); #if defined(MAKE_NATIVE) && !defined(lint) __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " "The Regents of the University of California. " @@ -1127,7 +1127,7 @@ InitObjdir(const char *machine, const char *machine_arch) static void UnlimitFiles(void) { -#if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) +#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { @@ -1685,7 +1685,7 @@ main_CleanUp(void) static int main_Exit(bool outOfDate) { - if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0)) + if (opts.strict && (main_errors > 0 || Parse_NumErrors() > 0)) return 2; /* Not 1 so -q can distinguish error */ return outOfDate ? 1 : 0; } diff --git a/contrib/bmake/make-bootstrap.sh.in b/contrib/bmake/make-bootstrap.sh.in index 0ecce455da74..829ae7e280e2 100755 --- a/contrib/bmake/make-bootstrap.sh.in +++ b/contrib/bmake/make-bootstrap.sh.in @@ -59,7 +59,7 @@ do_link() { ${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS} } -BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \ +BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o hash.o \ lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o \ suff.o targ.o trace.o var.o util.o" diff --git a/contrib/bmake/make.1 b/contrib/bmake/make.1 index b50a3b4a74b9..dbbff753673a 100644 --- a/contrib/bmake/make.1 +++ b/contrib/bmake/make.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $ +.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd December 22, 2020 +.Dd December 12, 2021 .Dt MAKE 1 .Os .Sh NAME @@ -1174,6 +1174,9 @@ executes. .It Ev .SHELL The pathname of the shell used to run target scripts. It is read-only. +.It Ev .SUFFIXES +The list of known suffixes. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH @@ -1244,8 +1247,20 @@ but selects all words which do not match .Ar pattern . .It Cm \&:O Orders every word in variable alphabetically. +.It Cm \&:On +Orders every word in variable numerically. +A number followed by one of +.Ql k , +.Ql M +or +.Ql G +is multiplied by the appropriate factor (1024 (k), 1048576 (M), or +1073741824 (G)). +Both upper- and lower-case letters are accepted. .It Cm \&:Or Orders every word in variable in reverse alphabetical order. +.It Cm \&:Orn +Orders every word in variable in reverse numerical order. .It Cm \&:Ox Shuffles the words in variable. The results will be different each time you are referring to the diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c index a85a497be32d..2c1a243965f1 100644 --- a/contrib/bmake/make.c +++ b/contrib/bmake/make.c @@ -1,4 +1,4 @@ -/* $NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $ */ +/* $NetBSD: make.c,v 1.248 2021/11/28 23:12:51 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -104,7 +104,7 @@ #include "job.h" /* "@(#)make.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $"); +MAKE_RCSID("$NetBSD: make.c,v 1.248 2021/11/28 23:12:51 rillig Exp $"); /* Sequence # to detect recursion. */ static unsigned int checked_seqno = 1; @@ -138,35 +138,83 @@ make_abort(GNode *gn, int lineno) abort(); } -ENUM_FLAGS_RTTI_31(GNodeType, - OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP, -/* OP_OPMASK is omitted since it combines other flags */ - OP_OPTIONAL, OP_USE, OP_EXEC, OP_IGNORE, - OP_PRECIOUS, OP_SILENT, OP_MAKE, OP_JOIN, - OP_MADE, OP_SPECIAL, OP_USEBEFORE, OP_INVISIBLE, - OP_NOTMAIN, OP_PHONY, OP_NOPATH, OP_WAIT, - OP_NOMETA, OP_META, OP_NOMETA_CMP, OP_SUBMAKE, - OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV, - OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK); +static const char * +GNodeType_ToString(GNodeType type, void **freeIt) +{ + Buffer buf; -ENUM_FLAGS_RTTI_9(GNodeFlags, - REMAKE, CHILDMADE, FORCE, DONE_WAIT, - DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE, - DONECYCLE); + Buf_InitSize(&buf, 32); +#define ADD(flag) Buf_AddFlag(&buf, (type & (flag)) != OP_NONE, #flag) + ADD(OP_DEPENDS); + ADD(OP_FORCE); + ADD(OP_DOUBLEDEP); + ADD(OP_OPTIONAL); + ADD(OP_USE); + ADD(OP_EXEC); + ADD(OP_IGNORE); + ADD(OP_PRECIOUS); + ADD(OP_SILENT); + ADD(OP_MAKE); + ADD(OP_JOIN); + ADD(OP_MADE); + ADD(OP_SPECIAL); + ADD(OP_USEBEFORE); + ADD(OP_INVISIBLE); + ADD(OP_NOTMAIN); + ADD(OP_PHONY); + ADD(OP_NOPATH); + ADD(OP_WAIT); + ADD(OP_NOMETA); + ADD(OP_META); + ADD(OP_NOMETA_CMP); + ADD(OP_SUBMAKE); + ADD(OP_TRANSFORM); + ADD(OP_MEMBER); + ADD(OP_LIB); + ADD(OP_ARCHV); + ADD(OP_HAS_COMMANDS); + ADD(OP_SAVE_CMDS); + ADD(OP_DEPS_FOUND); + ADD(OP_MARK); +#undef ADD + return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf)); +} + +static const char * +GNodeFlags_ToString(GNodeFlags flags, void **freeIt) +{ + Buffer buf; + + Buf_InitSize(&buf, 32); +#define ADD(flag, name) Buf_AddFlag(&buf, flags.flag, name) + ADD(remake, "REMAKE"); + ADD(childMade, "CHILDMADE"); + ADD(force, "FORCE"); + ADD(doneWait, "DONE_WAIT"); + ADD(doneOrder, "DONE_ORDER"); + ADD(fromDepend, "FROM_DEPEND"); + ADD(doneAllsrc, "DONE_ALLSRC"); + ADD(cycle, "CYCLE"); + ADD(doneCycle, "DONECYCLE"); +#undef ADD + return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf)); +} void GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, const char *suffix) { - char type_buf[GNodeType_ToStringSize]; - char flags_buf[GNodeFlags_ToStringSize]; + void *type_freeIt = NULL; + void *flags_freeIt = NULL; fprintf(f, "%s%s, type %s, flags %s%s", prefix, GNodeMade_Name(gn->made), - GNodeType_ToString(type_buf, gn->type), - GNodeFlags_ToString(flags_buf, gn->flags), + GNodeType_ToString(gn->type, &type_freeIt), + GNodeFlags_ToString(gn->flags, &flags_freeIt), suffix); + free(type_freeIt); + free(flags_freeIt); } bool @@ -286,8 +334,8 @@ GNode_IsOODate(GNode *gn) */ DEBUG0(MAKE, ".JOIN node..."); DEBUG1(MAKE, "source %smade...", - gn->flags & CHILDMADE ? "" : "not "); - oodate = (gn->flags & CHILDMADE) != 0; + gn->flags.childMade ? "" : "not "); + oodate = gn->flags.childMade; } else if (gn->type & (OP_FORCE | OP_EXEC | OP_PHONY)) { /* * A node which is the object of the force (!) operator or @@ -315,10 +363,10 @@ GNode_IsOODate(GNode *gn) * child after it was considered made. */ if (DEBUG(MAKE)) { - if (gn->flags & FORCE) + if (gn->flags.force) debug_printf("non existing child..."); } - oodate = (gn->flags & FORCE) != 0; + oodate = gn->flags.force; } #ifdef USE_META @@ -568,7 +616,7 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname) for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) { GNode *pgn = ln->datum; - if (pgn->flags & REMAKE) { + if (pgn->flags.remake) { Var_Set(pgn, IMPSRC, cname); if (cpref != NULL) Var_Set(pgn, PREFIX, cpref); @@ -585,7 +633,7 @@ IsWaitingForOrder(GNode *gn) for (ln = gn->order_pred.first; ln != NULL; ln = ln->next) { GNode *ogn = ln->datum; - if (GNode_IsDone(ogn) || !(ogn->flags & REMAKE)) + if (GNode_IsDone(ogn) || !ogn->flags.remake) continue; DEBUG2(MAKE, @@ -684,13 +732,13 @@ Make_Update(GNode *cgn) debug_printf(", unmade %d ", pgn->unmade - 1); } - if (!(pgn->flags & REMAKE)) { + if (!pgn->flags.remake) { /* This parent isn't needed */ DEBUG0(MAKE, "- not needed\n"); continue; } if (mtime == 0 && !(cgn->type & OP_WAIT)) - pgn->flags |= FORCE; + pgn->flags.force = true; /* * If the parent has the .MADE attribute, its timestamp got @@ -707,7 +755,7 @@ Make_Update(GNode *cgn) if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE))) { if (cgn->made == MADE) - pgn->flags |= CHILDMADE; + pgn->flags.childMade = true; GNode_UpdateYoungestChild(pgn, cgn); } @@ -740,7 +788,7 @@ Make_Update(GNode *cgn) * nodes. */ if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) - && !(centurion->flags & DONE_ORDER)) { + && !centurion->flags.doneOrder) { DEBUG0(MAKE, "- unmade children\n"); continue; } @@ -873,7 +921,7 @@ GNode_SetLocalVars(GNode *gn) { GNodeListNode *ln; - if (gn->flags & DONE_ALLSRC) + if (gn->flags.doneAllsrc) return; UnmarkChildren(gn); @@ -887,7 +935,7 @@ GNode_SetLocalVars(GNode *gn) if (gn->type & OP_JOIN) Var_Set(gn, TARGET, GNode_VarAllsrc(gn)); - gn->flags |= DONE_ALLSRC; + gn->flags.doneAllsrc = true; } static bool @@ -942,7 +990,7 @@ MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext) if (!MakeBuildChild(pn, toBeMadeNext)) { /* When this node is built, reschedule its parents. */ - pn->flags |= DONE_ORDER; + pn->flags.doneOrder = true; } } @@ -1085,7 +1133,7 @@ static void MakePrintStatusList(GNodeList *, int *); static bool MakePrintStatus(GNode *gn, int *errors) { - if (gn->flags & DONECYCLE) { + if (gn->flags.doneCycle) { /* * We've completely processed this node before, don't do * it again. @@ -1094,7 +1142,7 @@ MakePrintStatus(GNode *gn, int *errors) } if (gn->unmade == 0) { - gn->flags |= DONECYCLE; + gn->flags.doneCycle = true; switch (gn->made) { case UPTODATE: printf("`%s%s' is up to date.\n", gn->name, @@ -1138,17 +1186,17 @@ MakePrintStatus(GNode *gn, int *errors) * If printing cycles and came to one that has unmade children, * print out the cycle by recursing on its children. */ - if (!(gn->flags & CYCLE)) { + if (!gn->flags.cycle) { /* First time we've seen this node, check all children */ - gn->flags |= CYCLE; + gn->flags.cycle = true; MakePrintStatusList(&gn->children, errors); /* Mark that this node needn't be processed again */ - gn->flags |= DONECYCLE; + gn->flags.doneCycle = true; return false; } /* Only output the error once per node */ - gn->flags |= DONECYCLE; + gn->flags.doneCycle = true; Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num); if ((*errors)++ > 100) /* Abandon the whole error report */ @@ -1177,7 +1225,7 @@ ExamineLater(GNodeList *examine, GNodeList *toBeExamined) for (ln = toBeExamined->first; ln != NULL; ln = ln->next) { GNode *gn = ln->datum; - if (gn->flags & REMAKE) + if (gn->flags.remake) continue; if (gn->type & (OP_USE | OP_USEBEFORE)) continue; @@ -1213,10 +1261,10 @@ Make_ExpandUse(GNodeList *targs) while (!Lst_IsEmpty(&examine)) { GNode *gn = Lst_Dequeue(&examine); - if (gn->flags & REMAKE) + if (gn->flags.remake) /* We've looked at this one already */ continue; - gn->flags |= REMAKE; + gn->flags.remake = true; DEBUG2(MAKE, "Make_ExpandUse: examine %s%s\n", gn->name, gn->cohort_num); @@ -1301,7 +1349,7 @@ Make_ProcessWait(GNodeList *targs) */ pgn = GNode_New(".MAIN"); - pgn->flags = REMAKE; + pgn->flags.remake = true; pgn->type = OP_PHONY | OP_DEPENDS; /* Get it displayed in the diag dumps */ Lst_Prepend(Targ_List(), pgn); @@ -1329,9 +1377,9 @@ Make_ProcessWait(GNodeList *targs) pgn = Lst_Dequeue(&examine); /* We only want to process each child-list once */ - if (pgn->flags & DONE_WAIT) + if (pgn->flags.doneWait) continue; - pgn->flags |= DONE_WAIT; + pgn->flags.doneWait = true; DEBUG1(MAKE, "Make_ProcessWait: examine %s\n", pgn->name); if (pgn->type & OP_DOUBLEDEP) diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h index a074923c643e..6f14c44eb067 100644 --- a/contrib/bmake/make.h +++ b/contrib/bmake/make.h @@ -1,4 +1,4 @@ -/* $NetBSD: make.h,v 1.263 2021/06/21 10:33:11 rillig Exp $ */ +/* $NetBSD: make.h,v 1.270 2021/11/28 23:12:51 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -136,25 +136,38 @@ #endif #define MAKE_INLINE static inline MAKE_ATTR_UNUSED + +/* MAKE_STATIC marks a function that may or may not be inlined. */ +#if defined(lint) +/* As of 2021-07-31, NetBSD lint ignores __attribute__((unused)). */ +#define MAKE_STATIC MAKE_INLINE +#else #define MAKE_STATIC static MAKE_ATTR_UNUSED +#endif #if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN) #include +#elif defined(__bool_true_false_are_defined) +/* + * All files of make must be compiled with the same definition of bool. + * Since one of the files includes , that means the header is + * available on this platform. Recompile everything with -DUSE_C99_BOOLEAN. + */ +#error " is included in pre-C99 mode" +#elif defined(bool) || defined(true) || defined(false) +/* + * In pre-C99 mode, make does not expect that bool is already defined. + * You need to ensure that all translation units use the same definition for + * bool. + */ +#error "bool/true/false is defined in pre-C99 mode" #else -#ifndef bool -typedef unsigned int Boolean; -#define bool Boolean -#endif -#ifndef true +typedef unsigned char bool; #define true 1 -#endif -#ifndef false #define false 0 #endif -#endif #include "lst.h" -#include "enum.h" #include "make_malloc.h" #include "str.h" #include "hash.h" @@ -324,26 +337,25 @@ typedef enum GNodeType { OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM } GNodeType; -typedef enum GNodeFlags { - GNF_NONE = 0, +typedef struct GNodeFlags { /* this target needs to be (re)made */ - REMAKE = 1 << 0, + bool remake:1; /* children of this target were made */ - CHILDMADE = 1 << 1, + bool childMade:1; /* children don't exist, and we pretend made */ - FORCE = 1 << 2, + bool force:1; /* Set by Make_ProcessWait() */ - DONE_WAIT = 1 << 3, + bool doneWait:1; /* Build requested by .ORDER processing */ - DONE_ORDER = 1 << 4, + bool doneOrder:1; /* Node created from .depend */ - FROM_DEPEND = 1 << 5, + bool fromDepend:1; /* We do it once only */ - DONE_ALLSRC = 1 << 6, + bool doneAllsrc:1; /* Used by MakePrintStatus */ - CYCLE = 1 << 12, + bool cycle:1; /* Used by MakePrintStatus */ - DONECYCLE = 1 << 13 + bool doneCycle:1; } GNodeFlags; typedef struct List StringList; @@ -583,7 +595,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); do { \ if (DEBUG(module)) \ debug_printf args; \ - } while (/*CONSTCOND*/false) + } while (false) #define DEBUG0(module, text) \ DEBUG_IMPL(module, ("%s", text)) @@ -710,7 +722,7 @@ bool GNode_ShouldExecute(GNode *gn); MAKE_INLINE bool GNode_IsTarget(const GNode *gn) { - return (gn->type & OP_OPMASK) != 0; + return (gn->type & OP_OPMASK) != OP_NONE; } MAKE_INLINE const char * @@ -722,7 +734,7 @@ GNode_Path(const GNode *gn) MAKE_INLINE bool GNode_IsWaitingFor(const GNode *gn) { - return (gn->flags & REMAKE) && gn->made <= REQUESTED; + return gn->flags.remake && gn->made <= REQUESTED; } MAKE_INLINE bool @@ -758,16 +770,13 @@ GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } MAKE_INLINE const char * GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } -#if defined(__GNUC__) && __STDC_VERSION__ >= 199901L -#define UNCONST(ptr) ({ \ - union __unconst { \ - const void *__cp; \ - void *__p; \ - } __d; \ - __d.__cp = ptr, __d.__p; }) -#else -#define UNCONST(ptr) (void *)(ptr) -#endif +MAKE_INLINE void * +UNCONST(const void *ptr) +{ + void *ret; + memcpy(&ret, &ptr, sizeof(ret)); + return ret; +} /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ #ifdef HAVE_LIMITS_H @@ -833,6 +842,16 @@ pp_skip_hspace(char **pp) # define MAKE_RCSID(id) extern void do_not_define_rcsid(void) #elif defined(MAKE_NATIVE) # include +# ifndef __IDSTRING +# define __IDSTRING(name,string) \ + static const char name[] MAKE_ATTR_UNUSED = string +# endif +# ifndef __RCSID +# define __RCSID(s) __IDSTRING(rcsid,s) +# endif +# ifndef __COPYRIGHT +# define __COPYRIGHT(s) __IDSTRING(copyright,s) +# endif # define MAKE_RCSID(id) __RCSID(id) #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c index c1f78136fb7c..67c57647f2af 100644 --- a/contrib/bmake/meta.c +++ b/contrib/bmake/meta.c @@ -1,4 +1,4 @@ -/* $NetBSD: meta.c,v 1.181 2021/04/04 10:05:08 rillig Exp $ */ +/* $NetBSD: meta.c,v 1.185 2021/11/27 22:04:02 rillig Exp $ */ /* * Implement 'meta' mode. @@ -98,6 +98,9 @@ extern char **environ; #if !defined(HAVE_STRSEP) # define strsep(s, d) stresep((s), (d), '\0') #endif +#if !defined(HAVE_STRESEP) +char * stresep(char **, const char *, int); +#endif /* * Filemon is a kernel module which snoops certain syscalls. @@ -149,11 +152,11 @@ meta_open_filemon(BuildMon *pbm) else pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0); if ((dupfd = dup(pbm->mon_fd)) == -1) { - err(1, "Could not dup filemon output!"); + Punt("Could not dup filemon output: %s", strerror(errno)); } (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC); if (filemon_setfd(pbm->filemon, dupfd) == -1) { - err(1, "Could not set filemon file descriptor!"); + Punt("Could not set filemon file descriptor: %s", strerror(errno)); } /* we don't need these once we exec */ (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC); @@ -191,7 +194,9 @@ filemon_read(FILE *mfp, int fd) error = EIO; } } - fflush(mfp); + if (fflush(mfp) != 0) + Punt("Cannot write filemon data to meta file: %s", + strerror(errno)); if (close(fd) < 0) error = errno; return error; @@ -248,8 +253,8 @@ meta_name(char *mname, size_t mnamelen, const char *cwd) { char buf[MAXPATHLEN]; - char *rp; - char *cp; + char *rp, *cp; + const char *tname_base; char *tp; char *dtp; size_t ldname; @@ -261,13 +266,13 @@ meta_name(char *mname, size_t mnamelen, * So we use realpath() just to get the dirname, and leave the * basename as given to us. */ - if ((cp = strrchr(tname, '/')) != NULL) { + if ((tname_base = strrchr(tname, '/')) != NULL) { if (cached_realpath(tname, buf) != NULL) { if ((rp = strrchr(buf, '/')) != NULL) { rp++; - cp++; - if (strcmp(cp, rp) != 0) - strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf)); + tname_base++; + if (strcmp(tname_base, rp) != 0) + strlcpy(rp, tname_base, sizeof buf - (size_t)(rp - buf)); } tname = buf; } else { @@ -325,8 +330,7 @@ is_submake(const char *cmd, GNode *gn) static const char *p_make = NULL; static size_t p_len; char *mp = NULL; - char *cp; - char *cp2; + const char *cp, *cp2; bool rc = false; if (p_make == NULL) { @@ -412,7 +416,7 @@ printCMDs(GNode *gn, FILE *fp) } \ return false; \ } \ -} while (/*CONSTCOND*/false) +} while (false) /* @@ -523,7 +527,7 @@ meta_create(BuildMon *pbm, GNode *gn) #endif if ((fp = fopen(fname, "w")) == NULL) - err(1, "Could not open meta file '%s'", fname); + Punt("Could not open meta file '%s': %s", fname, strerror(errno)); fprintf(fp, "# Meta data file %s\n", fname); @@ -541,7 +545,9 @@ meta_create(BuildMon *pbm, GNode *gn) } fprintf(fp, "-- command output --\n"); - fflush(fp); + if (fflush(fp) != 0) + Punt("Cannot write expanded command to meta file: %s", + strerror(errno)); Global_Append(".MAKE.META.FILES", fname); Global_Append(".MAKE.META.CREATED", fname); @@ -557,9 +563,9 @@ meta_create(BuildMon *pbm, GNode *gn) } static bool -boolValue(char *s) +boolValue(const char *s) { - switch(*s) { + switch (*s) { case '0': case 'N': case 'n': @@ -594,7 +600,7 @@ void meta_mode_init(const char *make_mode) { static bool once = false; - char *cp; + const char *cp; FStr value; useMeta = true; @@ -715,7 +721,7 @@ meta_job_child(Job *job) pid = getpid(); if (filemon_setpid_child(pbm->filemon, pid) == -1) { - err(1, "Could not set filemon pid!"); + Punt("Could not set filemon pid: %s", strerror(errno)); } } } @@ -855,8 +861,10 @@ meta_cmd_finish(void *pbmp) if (pbm->filemon != NULL) { while (filemon_process(pbm->filemon) > 0) continue; - if (filemon_close(pbm->filemon) == -1) + if (filemon_close(pbm->filemon) == -1) { error = errno; + Punt("filemon failed: %s", strerror(errno)); + } x = filemon_read(pbm->mfp, pbm->mon_fd); if (error == 0 && x != 0) error = x; @@ -1710,7 +1718,7 @@ meta_compat_parent(pid_t child) fflush(stdout); buf[nread] = '\0'; meta_job_output(NULL, buf, ""); - } while (/*CONSTCOND*/false); + } while (false); if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) { if (meta_job_event(NULL) <= 0) metafd = -1; diff --git a/contrib/bmake/metachar.c b/contrib/bmake/metachar.c index dcb049dff44d..e99630602e3d 100644 --- a/contrib/bmake/metachar.c +++ b/contrib/bmake/metachar.c @@ -1,4 +1,4 @@ -/* $NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $ */ +/* $NetBSD: metachar.c,v 1.10 2021/06/21 18:54:41 rillig Exp $ */ /* * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -39,7 +39,7 @@ #include "metachar.h" -MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: metachar.c,v 1.10 2021/06/21 18:54:41 rillig Exp $"); /* * The following array is used to make a fast determination of which @@ -48,7 +48,7 @@ MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $"); * directly by us. */ -unsigned char _metachar[128] = { +const unsigned char _metachar[128] = { /* nul soh stx etx eot enq ack bel */ 1, 0, 0, 0, 0, 0, 0, 0, /* bs ht nl vt np cr so si */ diff --git a/contrib/bmake/metachar.h b/contrib/bmake/metachar.h index 1fd1397cfe63..d6fd2299d43e 100644 --- a/contrib/bmake/metachar.h +++ b/contrib/bmake/metachar.h @@ -1,4 +1,4 @@ -/* $NetBSD: metachar.h,v 1.16 2021/04/03 11:08:40 rillig Exp $ */ +/* $NetBSD: metachar.h,v 1.17 2021/06/21 18:54:41 rillig Exp $ */ /* * Copyright (c) 2015 The NetBSD Foundation, Inc. @@ -33,9 +33,13 @@ #include "make.h" -extern unsigned char _metachar[]; +extern const unsigned char _metachar[]; -#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0) +MAKE_INLINE bool +is_shell_metachar(char c) +{ + return _metachar[c & 0x7f] != 0; +} MAKE_INLINE bool needshell(const char *cmd) diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog index f73c4fb68c6b..93bde20d754d 100644 --- a/contrib/bmake/mk/ChangeLog +++ b/contrib/bmake/mk/ChangeLog @@ -1,3 +1,107 @@ +2021-12-12 Simon J Gerraty + + * sys.mk: simplify; include meta.sys.mk if MK_META_MODE is yes. + + * meta.sys.mk: do not check for /dev/filemon if .MAKE.PATH_FILEMON + is something else. + + * meta.autodep.mk: we can now reference ${.SUFFIXES} + + * meta2deps.py: derive a list of dirdep extensions from + TARGET_SPEC to trim from dirdeps. + + * dirdeps.mk: flip the computation of qualified vs unqualified + dirdeps - it is much simpler to check for unqualified first. + +2021-12-11 Simon J Gerraty + + * install-mk (MK_VERSION): 20211212 + + * auto.dep.mk: rearrange so that the trivial implementation + for recent bmake is more obvious. + +2021-12-07 Simon J Gerraty + + * install-mk (MK_VERSION): 20211207 + + * Ensure guard targets are .NOTMAIN + + * meta.sys.mk: check for nofilemon support when we skip level 0 + + * auto.dep.mk: make this usable in meta mode + for platforms that cannot use meta.autodep.mk + + * meta2deps.py: avoid confusion if MACHINE and another + TARGET_SPEC_VAR have same value. + +2021-11-27 Simon J Gerraty + + * dirdeps.mk: when building dirdeps.cache, minimize the amount of + data put into env, by stripping ${SRCTOP}/ from each entry. + A long sandbox name can double the amount of memory consumed and + in extreme cases cause failure. + While we are at it, strip ${SRCTOP}/ from a lot of the debug output. + +2021-11-11 Simon J Gerraty + + * install-mk (MK_VERSION): 20211111 + + * meta.stage.mk (LN_CP_SCRIPT): if staging to NFS cp -p can fail + so fallback to cp if necessary. + +2021-10-30 Simon J Gerraty + + * man.mk (CMT2DOC): use cmt2doc.py rather than the 30 year + old cmt2doc.pl + +2021-10-24 Simon J Gerraty + + * meta.stage.mk: stage_as_and_symlink use ${STAGE_LINK_AS_$f:U$f} + as the symlink (rare) + +2021-10-16 Simon J Gerraty + + * autoconf.mk: if AUTOCONF_GENERATED_MAKEFILE is set and has not + been read, throw an error after running configure telling user to + restart. + +2021-10-13 Simon J Gerraty + + * install-mk (MK_VERSION): 20211011 + + * Add support for SCO_SV + +2021-10-01 Simon J Gerraty + + * install-mk (MK_VERSION): 20211001 + + * man.mk: use MAN_SUFFIXES and CMT2DOC_SUFFIXES for more + flexibility + +2021-09-13 Simon J Gerraty + + * options.mk (describe-options): print options and their values + and optional description + +2021-09-11 Simon J Gerraty + + * install-mk (MK_VERSION): 20210911 + + * options.mk (show-options): print options and their values + +2021-09-08 Simon J Gerraty + + * install-mk (MK_VERSION): 20210909 + + * lib.mk: apply patch from + to fix shared libs on Linux + +2021-08-08 Simon J Gerraty + + * install-mk (MK_VERSION): 20210808 + + * options.mk: issue warning for WITH_*=no + 2021-06-16 Simon J Gerraty * install-mk (MK_VERSION): 20210616 diff --git a/contrib/bmake/mk/FILES b/contrib/bmake/mk/FILES index 51b1acd716b2..91c4387e8ccb 100644 --- a/contrib/bmake/mk/FILES +++ b/contrib/bmake/mk/FILES @@ -54,6 +54,7 @@ sys/Linux.mk sys/NetBSD.mk sys/OSF1.mk sys/OpenBSD.mk +sys/SCO_SV.mk sys/SunOS.mk sys/UnixWare.mk target-flags.mk diff --git a/contrib/bmake/mk/auto.dep.mk b/contrib/bmake/mk/auto.dep.mk index d905649ab206..c30b37f44053 100644 --- a/contrib/bmake/mk/auto.dep.mk +++ b/contrib/bmake/mk/auto.dep.mk @@ -1,8 +1,8 @@ # # RCSid: -# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $ +# $Id: auto.dep.mk,v 1.10 2021/12/11 18:57:41 sjg Exp $ # -# @(#) Copyright (c) 2010, Simon J. Gerraty +# @(#) Copyright (c) 2010-2021, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. @@ -30,39 +30,24 @@ # dep.mk will handle that itself. # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: - -# this what bmake > 20100401 will look for -.MAKE.DEPENDFILE ?= .depend +__${.PARSEFILE}__: .NOTMAIN # set this to -MMD to ignore /usr/include # actually it ignores <> so may not be a great idea CFLAGS_MD ?= -MD # -MF etc not available on all gcc versions. +.if ${COMPILER_TYPE:Ugcc} == "gcc" && ${COMPILER_VERSION:U0} < 30000 +CFLAGS_MF= +.endif CFLAGS_MF ?= -MF ${.TARGET:T}.d -MT ${.TARGET:T} CFLAGS += ${CFLAGS_MD} ${CFLAGS_MF} CXXFLAGS += ${CFLAGS_MD} ${CFLAGS_MF} -CLEANFILES += .depend ${.MAKE.DEPENDFILE} *.d +CLEANFILES += .depend *.d -.if ${MAKE_VERSION} < 20160218 -# skip generating dependfile for misc targets -.if ${.TARGETS:Uall:M*all} != "" -.END: ${.MAKE.DEPENDFILE} -.endif +.if ${MAKE_VERSION} >= 20160218 -# doing 'make depend' isn't a big win with this model -.if !target(depend) -depend: ${.MAKE.DEPENDFILE} -.endif - -# this is trivial -${.MAKE.DEPENDFILE}: ${OBJS} ${POBJS} ${SOBJS} - -@for f in ${.ALLSRC:M*o:T:O:u:%=%.d}; do \ - echo ".-include \"$$f\""; \ - done > $@ -.else -# we have .dinclude +# we have .dinclude and this is all that is required .if empty(_SKIP_BUILD) _all_objs = ${OBJS} ${POBJS} ${SOBJS} .for d in ${_all_objs:M*o:T:O:u:%=%.d} @@ -70,5 +55,34 @@ _all_objs = ${OBJS} ${POBJS} ${SOBJS} .endfor .endif +.else # we lack .dinclude + +.if ${.MAKE.MODE:Unormal:Mmeta} != "" +# ignore .MAKE.DEPENDFILE +DEPENDFILE = .depend +.else +# this what bmake > 20100401 will look for +.MAKE.DEPENDFILE ?= .depend +DEPENDFILE ?= ${.MAKE.DEPENDFILE} +.endif + +CLEANFILES += ${DEPENDFILE} + +# skip generating dependfile for misc targets +.if ${.TARGETS:Uall:M*all} != "" +.END: ${DEPENDFILE} +.endif + +# doing 'make depend' isn't a big win with this model +.if !target(depend) +depend: ${DEPENDFILE} +.endif + +# this is trivial +${DEPENDFILE}: ${OBJS} ${POBJS} ${SOBJS} + -@for f in ${.ALLSRC:M*o:T:O:u:%=%.d}; do \ + echo ".-include \"$$f\""; \ + done > $@ + .endif .endif diff --git a/contrib/bmake/mk/autoconf.mk b/contrib/bmake/mk/autoconf.mk index 61e6978043a8..6011a8af4df5 100644 --- a/contrib/bmake/mk/autoconf.mk +++ b/contrib/bmake/mk/autoconf.mk @@ -1,4 +1,4 @@ -# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ +# $Id: autoconf.mk,v 1.16 2021/10/19 17:36:06 sjg Exp $ # # @(#) Copyright (c) 1996-2009, Simon J. Gerraty # @@ -13,32 +13,37 @@ # sjg@crufty.net # -.NOPATH: config.h config.status +.NOPATH: config.h config.gen config.recheck config.status CONFIGURE_DEPS += ${.CURDIR}/config.h.in ${.CURDIR}/configure .if !target(config.h) -config.h: ${CONFIGURE_DEPS} config.status +config.h: .NOTMAIN ${CONFIGURE_DEPS} config.status ./config.status +.if !empty(AUTOCONF_GENERATED_MAKEFILE) && ${AUTOCONF_GENERATED_MAKEFILE:T:@m@${"${.MAKE.MAKEFILES:T:M$m}":?yes:no}@:Mno} != "" + @echo Generated ${AUTOCONF_GENERATED_MAKEFILE}, you need to restart; exit 1 +.endif .endif .if !target(config.status) # avoid the targets behaving differently +config.status: .NOTMAIN .if exists(${.OBJDIR}/config.status) config.status: config.recheck .else config.status: config.gen .endif -config.recheck: ${CONFIGURE_DEPS} +config.recheck: .NOTMAIN ${CONFIGURE_DEPS} config.gen ./config.status --recheck @touch $@ -config.gen: ${CONFIGURE_DEPS} +config.gen: .NOTMAIN ${CONFIGURE_DEPS} CC="${CC} ${CCMODE}" ${.CURDIR}/configure --no-create ${CONFIGURE_ARGS} @touch $@ config.recheck -CLEANFILES+= config.recheck config.gen config.status *.meta +CLEANFILES+= config.recheck config.gen config.status *.meta \ + ${AUTOCONF_GENERATED_MAKEFILE:U} .endif # avoid things blowing up if these are not here... @@ -67,14 +72,14 @@ ACLOCAL += aclocal.m4 ACCONFIG += acconfig.h .endif -config.h.in: ${.CURDIR}/configure.in ${ACCONFIG} +config.h.in: .NOTMAIN ${.CURDIR}/configure.in ${ACCONFIG} (cd ${.CURDIR} && ${AUTOHEADER}) -configure: ${.CURDIR}/configure.in ${ACLOCAL} +configure: .NOTMAIN ${.CURDIR}/configure.in ${ACLOCAL} (cd ${.CURDIR} && ${AUTOCONF}) AUTOCONF_INPUTS += configure -autoconf-input: ${AUTOCONF_INPUTS} +autoconf-input: .NOTMAIN ${AUTOCONF_INPUTS} .endif .endif diff --git a/contrib/bmake/mk/autodep.mk b/contrib/bmake/mk/autodep.mk index a7bb942278c9..9104da2e2567 100644 --- a/contrib/bmake/mk/autodep.mk +++ b/contrib/bmake/mk/autodep.mk @@ -1,6 +1,6 @@ # # RCSid: -# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ +# $Id: autodep.mk,v 1.40 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 1999-2010, Simon J. Gerraty # @@ -20,7 +20,7 @@ # dependencies are normally updated as part of compilation. .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN DEPENDFILE?= .depend .for d in ${DEPENDFILE:N.depend} @@ -67,6 +67,9 @@ __dependsrcs= ${__dependsrcsx:O:u} CFLAGS_MD?=-MD # -MF etc not available on all gcc versions. # we "fix" the .o later +.if ${COMPILER_TYPE:Ugcc} == "gcc" && ${COMPILER_VERSION:U0} < 30000 +CFLAGS_MF= +.endif CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o CFLAGS+= ${CFLAGS_MD} ${CFLAGS_MF} RM?= rm diff --git a/contrib/bmake/mk/compiler.mk b/contrib/bmake/mk/compiler.mk index b20ecaa047e3..83c0cead8f25 100644 --- a/contrib/bmake/mk/compiler.mk +++ b/contrib/bmake/mk/compiler.mk @@ -1,4 +1,4 @@ -# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ +# $Id: compiler.mk,v 1.10 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2019, Simon J. Gerraty # @@ -14,7 +14,7 @@ # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .if ${MACHINE} == "common" COMPILER_TYPE = none @@ -22,12 +22,12 @@ COMPILER_VERSION = 0 .endif .if empty(COMPILER_TYPE) || empty(COMPILER_VERSION) # gcc does not always say gcc -_v != ${CC} --version 2> /dev/null | \ - egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation' +_v != (${CC} --version) 2> /dev/null | \ + egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'; echo .if empty(COMPILER_TYPE) .if ${_v:Mclang} != "" COMPILER_TYPE = clang -.elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != "" +.elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != "" || ${CC:T:M*gcc*} != "" COMPILER_TYPE = gcc .endif .endif diff --git a/contrib/bmake/mk/dep.mk b/contrib/bmake/mk/dep.mk index b07191a09cbf..ea73846f5fd8 100644 --- a/contrib/bmake/mk/dep.mk +++ b/contrib/bmake/mk/dep.mk @@ -1,7 +1,7 @@ -# $Id: dep.mk,v 1.17 2014/08/04 05:12:27 sjg Exp $ +# $Id: dep.mk,v 1.18 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # handle Proc*C as well... .if defined(SRCS) diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk index 38ead3de37cd..5e735f236b9f 100644 --- a/contrib/bmake/mk/dirdeps.mk +++ b/contrib/bmake/mk/dirdeps.mk @@ -1,4 +1,4 @@ -# $Id: dirdeps.mk,v 1.140 2021/06/20 23:42:38 sjg Exp $ +# $Id: dirdeps.mk,v 1.147 2021/12/14 02:09:53 sjg Exp $ # Copyright (c) 2010-2021, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. @@ -66,6 +66,10 @@ # processing is recursive and results in .MAKE.LEVEL 0 learning the # dependencies of the tree wrt the initial directory (_DEP_RELDIR). # +# NOTE: given the extent of processing that DIRDEPS undergoes it +# is important that any variables in entries use :U to guard +# against surprises when undefined. +# # TARGET_SPEC_VARS # The default value is just MACHINE, and for most environments # this is sufficient. The _DIRDEP_USE target actually sets @@ -399,7 +403,7 @@ DIRDEP_LOADAVG_REPORT = \ _DIRDEP_USE: .USE .MAKE @for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \ test -s ${.TARGET:R}/$$m || continue; \ - echo "${TRACER}Checking ${.TARGET:S,${SRCTOP}/,,} for ${.TARGET:E} ..."; \ + echo "${TRACER}Checking ${.TARGET:S,^${SRCTOP}/,,} for ${.TARGET:E} ..."; \ ${DIRDEP_USE_PRELUDE} \ MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \ TARGET_SPEC=${.TARGET:E} \ @@ -562,7 +566,7 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP ${"${DEBUG_DIRDEPS:Nno}":?DEBUG_DIRDEPS='${DEBUG_DIRDEPS}':} \ ${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \ ${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \ - 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \ + 3>&1 1>&2 | sed 's,${SRCTOP},_{SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \ mv ${.TARGET}.new ${.TARGET} .endif @@ -638,6 +642,7 @@ _build_dirs += ${_machines:@m@${_CURDIR}.$m@} .endif .if ${_debug_reldir} +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: nDIRDEPS=${DIRDEPS:[#]} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}' .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}' .endif @@ -656,11 +661,10 @@ DEP_DIRDEPS_FILTER = U # this is what we start with __depdirs := ${DIRDEPS:${NSkipDir}:${DEP_DIRDEPS_FILTER:ts:}:C,//+,/,g:O:u:@d@${SRCTOP}/$d@} -# some entries may be qualified with . -# the :M*/*/*.* just tries to limit the dirs we check to likely ones. -# the ${d:E:M*/*} ensures we don't consider junos/usr.sbin/mgd -__qual_depdirs := ${__depdirs:M*/*/*.*:@d@${exists($d):?:${"${d:E:M*/*}":?:${exists(${d:R}):?$d:}}}@} -__unqual_depdirs := ${__depdirs:${__qual_depdirs:Uno:${M_ListToSkip}}} +# some entries may be qualified with . or . +# we can tell the unqualified ones easily - because they exist +__unqual_depdirs := ${__depdirs:@d@${exists($d):?$d:}@} +__qual_depdirs := ${__depdirs:${__unqual_depdirs:Uno:${M_ListToSkip}}} .if ${DEP_RELDIR} == ${_DEP_RELDIR} # if it was called out - we likely need it. @@ -671,9 +675,9 @@ __qual_depdirs += ${__hostdpadd} .if ${_debug_reldir} .info DEP_DIRDEPS_FILTER=${DEP_DIRDEPS_FILTER:ts:} -.info depdirs=${__depdirs} -.info qualified=${__qual_depdirs} -.info unqualified=${__unqual_depdirs} +.info depdirs=${__depdirs:S,^${SRCTOP}/,,} +.info qualified=${__qual_depdirs:S,^${SRCTOP}/,,} +.info unqualified=${__unqual_depdirs:S,^${SRCTOP}/,,} .endif # _build_dirs is what we will feed to _DIRDEP_USE @@ -694,13 +698,14 @@ _build_all_dirs := ${_build_all_dirs:O:u} # Normally if doing make -V something, # we do not want to waste time chasing DIRDEPS # but if we want to count the number of Makefile.depend* read, we do. -.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS}} == "" +.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS:U}} == "" .if !empty(_build_all_dirs) .if ${BUILD_DIRDEPS_CACHE} == "yes" x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3 # guard against _new_dirdeps being too big for a single command line -_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@} -.export _build_xtra_dirs _new_dirdeps +_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,} +_cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,} +.export _cache_xtra_deps _new_dirdeps .if !empty(DEP_EXPORT_VARS) # Discouraged, but there are always exceptions. # Handle it here rather than explain how. @@ -713,7 +718,7 @@ dirdeps: ${_build_all_dirs} ${_build_all_dirs}: _DIRDEP_USE .if ${_debug_reldir} -.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs} +.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs:S,^${SRCTOP}/,,} .endif .if !empty(DEP_EXPORT_VARS) @@ -724,37 +729,37 @@ DEP_EXPORT_VARS= # this builds the dependency graph .for m in ${_machines} .if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs) -x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3 _cache_deps = +x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3 .endif # it would be nice to do :N${.TARGET} .if !empty(__qual_depdirs) .for q in ${__qual_depdirs:${M_dep_qual_fixes:ts:}:E:O:u:N$m} .if ${_debug_reldir} || ${DEBUG_DIRDEPS:@x@${${DEP_RELDIR}.$m:L:M$x}${${DEP_RELDIR}.$q:L:M$x}@} != "" -.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q} +.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q:S,^${SRCTOP}/,,} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" -_cache_deps += ${_build_dirs:M*.$q} +_cache_deps += ${_build_dirs:M*.$q:S,^${SRCTOP}/,,} .else ${_this_dir}.$m: ${_build_dirs:M*.$q} .endif .endfor .endif .if ${_debug_reldir} -.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m} +.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" .if !empty(_build_dirs) -_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m} +_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,} .if !empty(_cache_deps) .export _cache_deps -x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3 +x!= echo; for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done >&3 .endif -# anything in _build_xtra_dirs is hooked to dirdeps: only +# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \ echo; echo 'dirdeps: ${_this_dir}.$m \'; \ - for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \ - echo; for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3 + for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \ + echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3 .endif .else ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} @@ -769,7 +774,7 @@ ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} # once only _dirdeps_checked.$d: .if ${_debug_search} -.info checking $d +.info checking ${d:S,^${SRCTOP}/,,} .endif # Note: _build_all_dirs is fully qualifed so d:R is always the directory .if exists(${d:R}) @@ -798,7 +803,7 @@ _qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}} .endif # set this "just in case" # we can skip :tA since we computed the path above -DEP_RELDIR := ${_m:H:S,${SRCTOP}/,,} +DEP_RELDIR := ${_m:H:S,^${SRCTOP}/,,} # and reset this DIRDEPS = .if ${_debug_reldir} && ${_qm} != ${_m} diff --git a/contrib/bmake/mk/doc.mk b/contrib/bmake/mk/doc.mk index b6ebd8ad3d3e..06dd517473ca 100644 --- a/contrib/bmake/mk/doc.mk +++ b/contrib/bmake/mk/doc.mk @@ -1,7 +1,7 @@ -# $Id: doc.mk,v 1.7 2019/06/09 16:22:08 sjg Exp $ +# $Id: doc.mk,v 1.8 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .include diff --git a/contrib/bmake/mk/dpadd.mk b/contrib/bmake/mk/dpadd.mk index aef12528f163..07528f9e926c 100644 --- a/contrib/bmake/mk/dpadd.mk +++ b/contrib/bmake/mk/dpadd.mk @@ -1,4 +1,4 @@ -# $Id: dpadd.mk,v 1.29 2021/04/20 02:30:44 sjg Exp $ +# $Id: dpadd.mk,v 1.30 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2004, Simon J. Gerraty # @@ -77,7 +77,7 @@ # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # sometimes we play games with .CURDIR etc # _* hold the original values of .* diff --git a/contrib/bmake/mk/final.mk b/contrib/bmake/mk/final.mk index e7285ee9a5c1..3670dfc67a55 100644 --- a/contrib/bmake/mk/final.mk +++ b/contrib/bmake/mk/final.mk @@ -1,7 +1,7 @@ -# $Id: final.mk,v 1.9 2018/01/24 22:57:11 sjg Exp $ +# $Id: final.mk,v 1.10 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # provide a hook for folk who want to do scary stuff .-include <${.CURDIR:H}/Makefile-final.inc> diff --git a/contrib/bmake/mk/init.mk b/contrib/bmake/mk/init.mk index 8f6f8f2fbaf4..356c8501367e 100644 --- a/contrib/bmake/mk/init.mk +++ b/contrib/bmake/mk/init.mk @@ -1,4 +1,4 @@ -# $Id: init.mk,v 1.25 2020/11/27 17:59:46 sjg Exp $ +# $Id: init.mk,v 1.26 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2002, Simon J. Gerraty # @@ -14,7 +14,7 @@ # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .if ${MAKE_VERSION:U0} > 20100408 _this_mk_dir := ${.PARSEDIR:tA} diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk index 96c35b1052ec..21d00ae919a5 100755 --- a/contrib/bmake/mk/install-mk +++ b/contrib/bmake/mk/install-mk @@ -55,7 +55,7 @@ # Simon J. Gerraty # RCSid: -# $Id: install-mk,v 1.196 2021/06/19 15:30:41 sjg Exp $ +# $Id: install-mk,v 1.206 2021/12/11 18:57:41 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # @@ -70,7 +70,7 @@ # sjg@crufty.net # -MK_VERSION=20210616 +MK_VERSION=20211212 OWNER= GROUP= MODE=444 diff --git a/contrib/bmake/mk/java.mk b/contrib/bmake/mk/java.mk index e2149e7089a7..3a4e7911e536 100644 --- a/contrib/bmake/mk/java.mk +++ b/contrib/bmake/mk/java.mk @@ -1,6 +1,6 @@ # # RCSid: -# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ +# $Id: java.mk,v 1.16 2021/12/08 05:56:50 sjg Exp $ # @(#) Copyright (c) 1998-2001, Simon J. Gerraty # @@ -16,7 +16,7 @@ # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .include diff --git a/contrib/bmake/mk/ldorder.mk b/contrib/bmake/mk/ldorder.mk index 3d44df5874fe..5ae54385993e 100644 --- a/contrib/bmake/mk/ldorder.mk +++ b/contrib/bmake/mk/ldorder.mk @@ -1,4 +1,4 @@ -# $Id: ldorder.mk,v 1.25 2018/04/24 23:50:26 sjg Exp $ +# $Id: ldorder.mk,v 1.26 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2015, Simon J. Gerraty # @@ -130,7 +130,7 @@ ${_ldorder}: ${_ldorders} # it can also add to CFLAGS etc. .for __inc in ${LDORDER_LIBS:S,$,.${LDORDER_INC},} .if !target(__${__inc}__) -__${__inc}__: +__${__inc}__: .NOTMAIN # make sure this is reset LDORDER_LIBS = _ldorders = diff --git a/contrib/bmake/mk/lib.mk b/contrib/bmake/mk/lib.mk index c3979414ec49..259107458ae9 100644 --- a/contrib/bmake/mk/lib.mk +++ b/contrib/bmake/mk/lib.mk @@ -1,7 +1,7 @@ -# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $ +# $Id: lib.mk,v 1.73 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .include @@ -170,7 +170,7 @@ LD_solib= lib${LIB}_pic.a .elif ${TARGET_OSNAME} == "Linux" SHLIB_LD = ${CC} # this is ambiguous of course -LD_shared=-shared -Wl,"-soname lib${LIB}.so.${SHLIB_MAJOR}" +LD_shared=-shared -Wl,-soname,lib${LIB}.so.${SHLIB_MAJOR} LD_solib= -Wl,--whole-archive lib${LIB}_pic.a -Wl,--no-whole-archive .if ${COMPILER_TYPE} == "gcc" # Linux uses GNU ld, which is a multi-pass linker diff --git a/contrib/bmake/mk/man.mk b/contrib/bmake/mk/man.mk index 391b08afe491..ce4380fedc55 100644 --- a/contrib/bmake/mk/man.mk +++ b/contrib/bmake/mk/man.mk @@ -1,9 +1,12 @@ -# $Id: man.mk,v 1.20 2012/12/13 01:51:01 sjg Exp $ +# $Id: man.mk,v 1.25 2021/10/31 03:03:14 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN + +OPTIONS_DEFAULT_NO += CMT2DOC .include +.include # unlike bsd.man.mk we handle 3 approaches # 1. install unformated nroff (default) @@ -21,45 +24,38 @@ NROFF?= nroff MANDIR?= /usr/share/man MANDOC?= man -.SUFFIXES: .1 .2 .3 .4 .5 .6 .7 .8 .9 .cat1 .cat2 .cat3 .cat4 .cat5 .cat6 \ - .cat7 .cat8 .cat9 +MAN_SUFFIXES?= .1 .2 .3 .4 .5 .6 .7 .8 .9 +.SUFFIXES: ${MAN_SUFFIXES} +.if ${MANTARGET} == "cat" +.SUFFIXES: ${MAN_SUFFIXES:S,.,.cat,} +.endif -.9.cat9 .8.cat8 .7.cat7 .6.cat6 .5.cat5 .4.cat4 .3.cat3 .2.cat2 .1.cat1: +${MAN_SUFFIXES:@s@$s${s:S,.,.cat,}@}: @echo "${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}" - @${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T} || ( rm -f ${.TARGET:T} ; false ) + @${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}.new && \ + mv ${.TARGET:T}.new ${.TARGET:T} .if defined(MAN) && !empty(MAN) -# we use cmt2doc.pl to extract manpages from source -# this is triggered by the setting of EXTRACT_MAN or MAN being set but -# not existsing. - -.if !exists(${MAN:[1]}) && !target(${MAN:[1]}) -.if defined(EXTRACT_MAN) && ${EXTRACT_MAN} == "no" -MAN= -.else -.if exists(/usr/local/share/bin/cmt2doc.pl) -CMT2DOC?= cmt2doc.pl +.if ${MK_CMT2DOC} == "yes" +# use cmt2doc.py to extract manpages from source +CMT2DOC?= cmt2doc.py CMT2DOC_OPTS?= ${CMT2DOC_ORGOPT} -pmS${.TARGET:E} -.endif -.ifdef CMT2DOC -.c.8 .c.5 .c.3 .c.4 .c.1 \ - .cc.8 .cc.5 .cc.3 .cc.4 .cc.1 \ - .h.8 .h.5 .h.3 .h.4 .h.1 \ - .sh.8 .sh.5 .sh.3 .sh.4 .sh.1 \ - .pl.8 .pl.5 .pl.3 .pl.4 .pl.1: +CMT2DOC_SUFFIXES+= .c .h .sh .pl .py + +.SUFFIXES: ${CMT2DOC_SUFFIXES} + +${CMT2DOC_SUFFIXES:@s@${MAN_SUFFIXES:@m@$s$m@}@}: @echo "${CMT2DOC} ${.IMPSRC} > ${.TARGET:T}" - @${CMT2DOC} ${CMT2DOC_OPTS} ${.IMPSRC} > ${.TARGET:T} || ( rm -f ${.TARGET:T} ; false ) -.else -MAN= -.endif -.endif + @${CMT2DOC} ${CMT2DOC_OPTS} ${.IMPSRC} > ${.TARGET:T}.new && \ + mv ${.TARGET:T}.new ${.TARGET:T} + .endif _mandir=${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'` .if ${MANTARGET} == "cat" _mfromdir?=. -MANALL= ${MAN:S/.1$/.cat1/g:S/.2$/.cat2/g:S/.3$/.cat3/g:S/.4$/.cat4/g:S/.5$/.cat5/g:S/.6$/.cat6/g:S/.7$/.cat7/g:S/.8$/.cat8/g:S/.9$/.cat9/g} +MANALL= ${MAN:${MAN_SUFFIXES:S,.,,:@m@S/.$m/.cat$m/@:ts:}} .if ${MCATEXT} == "" _minstpage=`echo $$page | sed 's/\.cat/./'` .else diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk index 5d09dbd88e81..f9ed6d305b99 100644 --- a/contrib/bmake/mk/meta.autodep.mk +++ b/contrib/bmake/mk/meta.autodep.mk @@ -1,4 +1,4 @@ -# $Id: meta.autodep.mk,v 1.54 2021/03/06 17:03:18 sjg Exp $ +# $Id: meta.autodep.mk,v 1.55 2021/12/13 08:12:01 sjg Exp $ # # @(#) Copyright (c) 2010, Simon J. Gerraty @@ -23,8 +23,12 @@ __${_this}__: .NOTMAIN PICO?= .pico .if defined(SRCS) +.if ${MAKE_VERSION:U0} >= 20211212 +OBJ_EXTENSIONS += ${.SUFFIXES:M*o} +.else # it would be nice to be able to query .SUFFIXES -OBJ_EXTENSIONS+= .o .po .lo ${PICO} +OBJ_EXTENSIONS += .o .po .lo ${PICO} +.endif # explicit dependencies help short-circuit .SUFFIX searches SRCS_DEP_FILTER+= N*.[hly] @@ -163,7 +167,11 @@ _depend = .depend # it would be nice to be able to get .SUFFIXES as ${.SUFFIXES} # we actually only care about the .SUFFIXES of files that might be # generated by tools like yacc. +.if ${MAKE_VERSION:U0} >= 20211212 +DEPEND_SUFFIXES += ${.SUFFIXES:N.sh:N*[0-9aFfglopmnrSsty]} +.else DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh +.endif .depend: .NOMETA $${.MAKE.META.CREATED} ${_this} @echo "Updating $@: ${.OODATE:T:[1..8]}" @egrep -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ diff --git a/contrib/bmake/mk/meta.stage.mk b/contrib/bmake/mk/meta.stage.mk index 463521b79595..1da45f4f2167 100644 --- a/contrib/bmake/mk/meta.stage.mk +++ b/contrib/bmake/mk/meta.stage.mk @@ -1,4 +1,4 @@ -# $Id: meta.stage.mk,v 1.61 2021/01/31 04:43:12 sjg Exp $ +# $Id: meta.stage.mk,v 1.64 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2011-2017, Simon J. Gerraty # @@ -66,7 +66,7 @@ GENDIRDEPS_FILTER += Nnot-empty-is-important \ LN_CP_SCRIPT = LnCp() { \ rm -f $$2 2> /dev/null; \ { [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \ - cp -p $$1 $$2; } + cp -p $$1 $$2 2> /dev/null || cp $$1 $$2; } # a staging conflict should cause an error # a warning is handy when bootstapping different options. @@ -264,7 +264,8 @@ CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@} # sometimes things need to be renamed as they are staged # each ${file} will be staged as ${STAGE_AS_${file:T}} # one could achieve the same with SYMLINKS -# stage_as_and_symlink makes the original name a symlink to the new name +# stage_as_and_symlink makes the original name (or ${STAGE_LINK_AS_${name}}) +# a symlink to the new name # it is the same as using stage_as and stage_symlinks but ensures # both operations happen together .for s in ${STAGE_AS_SETS:O:u} @@ -294,7 +295,7 @@ STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_as_and_symlink: stage_as_and_symlink.$s stage_as_and_symlink.$s: .dirdep @${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@} - @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} $f@} + @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} ${STAGE_LINK_AS_${f}:U$f}@} @touch $@ .endif .endif @@ -306,7 +307,7 @@ CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes # this lot also only makes sense the first time... .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # stage_*links usually needs to follow any others. # for non-jobs mode the order here matters diff --git a/contrib/bmake/mk/meta.sys.mk b/contrib/bmake/mk/meta.sys.mk index 77b4893a8785..7cc802e2f2fe 100644 --- a/contrib/bmake/mk/meta.sys.mk +++ b/contrib/bmake/mk/meta.sys.mk @@ -1,7 +1,7 @@ -# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ +# $Id: meta.sys.mk,v 1.42 2021/12/13 05:50:55 sjg Exp $ # -# @(#) Copyright (c) 2010-2020, Simon J. Gerraty +# @(#) Copyright (c) 2010-2021, Simon J. Gerraty # # This file is provided in the hope that it will # be of use. There is absolutely NO WARRANTY. @@ -30,21 +30,31 @@ SYS_MK_DIR := ${_PARSEDIR} .endif META_MODE += meta verbose +.if ${MAKE_VERSION:U0} > 20130323 && empty(.MAKE.PATH_FILEMON) +# we do not support filemon +META_MODE += nofilemon +MKDEP_MK ?= auto.dep.mk +.endif + .MAKE.MODE ?= ${META_MODE} -.if ${.MAKE.LEVEL} == 0 +_filemon := ${.MAKE.PATH_FILEMON:U/dev/filemon} + +.if empty(UPDATE_DEPENDFILE) _make_mode := ${.MAKE.MODE} ${META_MODE} .if ${_make_mode:M*read*} != "" || ${_make_mode:M*nofilemon*} != "" # tell everyone we are not updating Makefile.depend* UPDATE_DEPENDFILE = NO .export UPDATE_DEPENDFILE .endif -.if ${UPDATE_DEPENDFILE:Uyes:tl} == "no" && !exists(/dev/filemon) +.if ${_filemon:T:Mfilemon} == "filemon" +.if ${UPDATE_DEPENDFILE:Uyes:tl} == "no" && !exists(${_filemon}) # we should not get upset META_MODE += nofilemon .export META_MODE .endif .endif +.endif .if !defined(NO_SILENT) .if ${MAKE_VERSION} > 20110818 @@ -106,7 +116,7 @@ _metaError: .NOMETA .NOTMAIN # Are we, after all, in meta mode? .if ${.MAKE.MODE:Uno:Mmeta*} != "" -MKDEP_MK = meta.autodep.mk +MKDEP_MK ?= meta.autodep.mk .if ${.MAKE.MAKEFILES:M*sys.dependfile.mk} == "" # this does all the smarts of setting .MAKE.DEPENDFILE @@ -140,12 +150,13 @@ META_NOECHO= : .warning Setting UPDATE_DEPENDFILE=NO due to -k UPDATE_DEPENDFILE= NO .export UPDATE_DEPENDFILE -.elif !exists(/dev/filemon) -.error ${.newline}ERROR: The filemon module (/dev/filemon) is not loaded. +.elif ${_filemon:T} == "filemon" && !exists(${_filemon}) +.error ${.newline}ERROR: The filemon module (${_filemon}) is not loaded. .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: @@ -156,6 +167,7 @@ dirdeps: # by default dirdeps is all we want at level0 .MAIN: dirdeps .endif +.endif .endif .else diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py index 193e303de3da..76ac59465b9b 100755 --- a/contrib/bmake/mk/meta2deps.py +++ b/contrib/bmake/mk/meta2deps.py @@ -37,7 +37,7 @@ """ RCSid: - $Id: meta2deps.py,v 1.38 2021/06/17 05:20:08 sjg Exp $ + $Id: meta2deps.py,v 1.40 2021/12/13 19:32:46 sjg Exp $ Copyright (c) 2011-2020, Simon J. Gerraty Copyright (c) 2011-2017, Juniper Networks, Inc. @@ -165,6 +165,19 @@ def add_trims(x): x + '/', x] +def target_spec_exts(target_spec): + """return a list of dirdep extensions that could match target_spec""" + + if target_spec.find(',') < 0: + return ['.'+target_spec] + w = target_spec.split(',') + n = len(w) + e = [] + while n > 0: + e.append('.'+','.join(w[0:n])) + n -= 1 + return e + class MetaFile: """class to parse meta files generated by bmake.""" @@ -226,7 +239,8 @@ def __init__(self, name, conf={}): self.machine = conf.get('MACHINE', '') self.machine_arch = conf.get('MACHINE_ARCH', '') - self.target_spec = conf.get('TARGET_SPEC', '') + self.target_spec = conf.get('TARGET_SPEC', self.machine) + self.exts = target_spec_exts(self.target_spec) self.curdir = conf.get('CURDIR') self.reldir = conf.get('RELDIR') self.dpdeps = conf.get('DPDEPS') @@ -250,7 +264,7 @@ def __init__(self, name, conf={}): trim_list = add_trims(self.machine) if self.machine == 'host': trim_list += add_trims(self.host_target) - if self.target_spec: + if self.target_spec != self.machine: trim_list += add_trims(self.target_spec) for objroot in conf.get('OBJROOTS', []): @@ -280,6 +294,7 @@ def __init__(self, name, conf={}): print("srctops=", self.srctops, file=self.debug_out) print("objroots=", self.objroots, file=self.debug_out) print("excludes=", self.excludes, file=self.debug_out) + print("ext_list=", self.exts, file=self.debug_out) self.dirdep_re = re.compile(r'([^/]+)/(.+)') @@ -355,10 +370,10 @@ def find_obj(self, objroot, dir, path, input): ddep = open(ddepf, 'r').readline().strip('# \n') if self.debug > 1: print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out) - if ddep.endswith(self.machine): - ddep = ddep[0:-(1+len(self.machine))] - elif self.target_spec and ddep.endswith(self.target_spec): - ddep = ddep[0:-(1+len(self.target_spec))] + for e in self.exts: + if ddep.endswith(e): + ddep = ddep[0:-len(e)] + break if not ddep: # no .dirdeps, so remember that we've seen the raw input diff --git a/contrib/bmake/mk/obj.mk b/contrib/bmake/mk/obj.mk index 487e25a55b6c..0df5a087f972 100644 --- a/contrib/bmake/mk/obj.mk +++ b/contrib/bmake/mk/obj.mk @@ -1,4 +1,4 @@ -# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ +# $Id: obj.mk,v 1.17 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 1999-2010, Simon J. Gerraty # @@ -14,7 +14,7 @@ # .if !target(__${.PARSEFILE:S,bsd.,,}__) -__${.PARSEFILE:S,bsd.,,}__: +__${.PARSEFILE:S,bsd.,,}__: .NOTMAIN .include diff --git a/contrib/bmake/mk/options.mk b/contrib/bmake/mk/options.mk index eb5253a6b7e8..bf78ba24f1ca 100644 --- a/contrib/bmake/mk/options.mk +++ b/contrib/bmake/mk/options.mk @@ -1,4 +1,4 @@ -# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $ +# $Id: options.mk,v 1.19 2021/10/03 16:29:51 sjg Exp $ # # @(#) Copyright (c) 2012, Simon J. Gerraty # @@ -26,8 +26,8 @@ # User sets WITH_* and WITHOUT_* to indicate what they want. # We set ${OPTION_PREFIX:UMK_}* which is then all we need care about. OPTIONS_DEFAULT_VALUES += \ - ${OPTIONS_DEFAULT_NO:O:u:S,$,/no,} \ - ${OPTIONS_DEFAULT_YES:O:u:S,$,/yes,} + ${OPTIONS_DEFAULT_NO:U:O:u:S,$,/no,} \ + ${OPTIONS_DEFAULT_YES:U:O:u:S,$,/yes,} OPTION_PREFIX ?= MK_ @@ -36,6 +36,10 @@ OPTION_PREFIX ?= MK_ # DOMINANT_* is set to "yes" # Otherwise WITH_* and WITHOUT_* override the default. .for o in ${OPTIONS_DEFAULT_VALUES:M*/*} +.if defined(WITH_${o:H}) && ${WITH_${o:H}} == "no" +# a common miss-use - point out correct usage +.warning use WITHOUT_${o:H}=1 not WITH_${o:H}=no +.endif .if defined(NO_${o:H}) || defined(NO${o:H}) # we cannot do it ${OPTION_PREFIX}${o:H} ?= no @@ -77,6 +81,32 @@ ${OPTION_PREFIX}${o:H} ?= no ${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}} .endif .endfor -.undef OPTIONS_DEFAULT_VALUES + +# allow displaying/describing set options +.set_options := ${.set_options} \ + ${OPTIONS_DEFAULT_VALUES:H:N.} \ + ${OPTIONS_DEFAULT_DEPENDENT:U:H:N.} \ + +# this can be used in .info as well as target below +OPTIONS_SHOW ?= ${.set_options:O:u:@o@${OPTION_PREFIX}$o=${${OPTION_PREFIX}$o}@} +# prefix for variables describing options +OPTION_DESCRIPTION_PREFIX ?= DESCRIPTION_ +OPTION_DESCRIPTION_SEPARATOR ?= == + +OPTIONS_DESCRIBE ?= ${.set_options:O:u:@o@${OPTION_PREFIX}$o=${${OPTION_PREFIX}$o}${${OPTION_DESCRIPTION_PREFIX}$o:S,^, ${OPTION_DESCRIPTION_SEPARATOR} ,1}${.newline}@} + +.if !commands(show-options) +show-options: .NOTMAIN .PHONY + @echo; echo "${OPTIONS_SHOW:ts\n}"; echo +.endif + +.if !commands(describe-options) +describe-options: .NOTMAIN .PHONY + @echo; echo "${OPTIONS_DESCRIBE}"; echo +.endif + +# we expect to be included more than once +.undef OPTIONS_DEFAULT_DEPENDENT .undef OPTIONS_DEFAULT_NO +.undef OPTIONS_DEFAULT_VALUES .undef OPTIONS_DEFAULT_YES diff --git a/contrib/bmake/mk/own.mk b/contrib/bmake/mk/own.mk index 63322297420b..7b0d74caba8a 100644 --- a/contrib/bmake/mk/own.mk +++ b/contrib/bmake/mk/own.mk @@ -1,7 +1,7 @@ -# $Id: own.mk,v 1.42 2020/11/27 18:00:08 sjg Exp $ +# $Id: own.mk,v 1.44 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .if !target(__init.mk__) .include "init.mk" @@ -257,11 +257,13 @@ MK_NLS= no .if ${MK_META_MODE:Uno} == "yes" # should all be set by sys.mk if not default TARGET_SPEC_VARS ?= MACHINE +.if ${MAKE_VERSION} >= 20120325 .if ${TARGET_SPEC_VARS:[#]} > 1 TARGET_SPEC_VARS_REV := ${TARGET_SPEC_VARS:[-1..1]} .else TARGET_SPEC_VARS_REV = ${TARGET_SPEC_VARS} .endif +.endif .if ${MK_STAGING} == "yes" STAGE_ROOT?= ${OBJROOT}/stage STAGE_OBJTOP?= ${STAGE_ROOT}/${TARGET_SPEC_VARS_REV:ts/} diff --git a/contrib/bmake/mk/prlist.mk b/contrib/bmake/mk/prlist.mk index aca1fde25555..3308a9522c89 100644 --- a/contrib/bmake/mk/prlist.mk +++ b/contrib/bmake/mk/prlist.mk @@ -1,4 +1,4 @@ -# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ +# $Id: prlist.mk,v 1.5 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2006, Simon J. Gerraty # @@ -14,7 +14,7 @@ # .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN # this needs to be included after all the lists it will process # are defined - which is why it is a separate file. diff --git a/contrib/bmake/mk/prog.mk b/contrib/bmake/mk/prog.mk index ea48837d5544..902b221f7fe5 100644 --- a/contrib/bmake/mk/prog.mk +++ b/contrib/bmake/mk/prog.mk @@ -1,7 +1,7 @@ -# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $ +# $Id: prog.mk,v 1.37 2021/12/08 05:56:50 sjg Exp $ .if !target(__${.PARSEFILE}__) -__${.PARSEFILE}__: +__${.PARSEFILE}__: .NOTMAIN .include diff --git a/contrib/bmake/mk/stage-install.sh b/contrib/bmake/mk/stage-install.sh index 674652d1d482..97d50871f95c 100755 --- a/contrib/bmake/mk/stage-install.sh +++ b/contrib/bmake/mk/stage-install.sh @@ -37,7 +37,7 @@ # # RCSid: -# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $ +# $Id: stage-install.sh,v 1.10 2021/11/17 07:06:31 sjg Exp $ # # @(#) Copyright (c) 2013-2020, Simon J. Gerraty # @@ -117,8 +117,12 @@ StageDirdep() { t=$1 if [ -s $t.dirdep ]; then cmp -s $_DIRDEP $t.dirdep && return - echo "ERROR: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2 - exit 1 + case "${STAGE_CONFLICT:-error}" in + [Ee]*) STAGE_CONFLICT=ERROR action=exit;; + *) STAGE_CONFLICT=WARNING action=: ;; + esac + echo "$STAGE_CONFLICT: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2 + $action 1 fi LnCp $_DIRDEP $t.dirdep || exit 1 } diff --git a/contrib/bmake/mk/sys.mk b/contrib/bmake/mk/sys.mk index 7ef8f724ef10..e39d5ac74548 100644 --- a/contrib/bmake/mk/sys.mk +++ b/contrib/bmake/mk/sys.mk @@ -1,4 +1,4 @@ -# $Id: sys.mk,v 1.52 2020/12/22 20:44:24 sjg Exp $ +# $Id: sys.mk,v 1.53 2021/12/13 05:50:13 sjg Exp $ # # @(#) Copyright (c) 2003-2009, Simon J. Gerraty # @@ -81,11 +81,10 @@ OPTIONS_DEFAULT_DEPENDENT += \ .-include -.if ${MK_DIRDEPS_BUILD:Uno} == "yes" -MK_META_MODE = yes +# :Uno incase options.mk not installed +.if ${MK_META_MODE:Uno} == "yes" .-include -.elif ${MK_META_MODE:Uno} == "yes" -.MAKE.MODE = meta verbose ${META_MODE} +.MAKE.MODE ?= meta verbose {META_MODE} .endif # make sure we have a harmless value .MAKE.MODE ?= normal diff --git a/contrib/bmake/mk/sys.vars.mk b/contrib/bmake/mk/sys.vars.mk index 592cbdc644dc..391967b18a2b 100644 --- a/contrib/bmake/mk/sys.vars.mk +++ b/contrib/bmake/mk/sys.vars.mk @@ -1,4 +1,4 @@ -# $Id: sys.vars.mk,v 1.6 2020/10/28 20:50:04 sjg Exp $ +# $Id: sys.vars.mk,v 1.7 2021/12/08 05:56:50 sjg Exp $ # # @(#) Copyright (c) 2003-2009, Simon J. Gerraty # @@ -19,7 +19,7 @@ # # _this ?= ${.PARSEFILE} # .if !target(__${_this}__) -# __${_this}__: +# __${_this}__: .NOTMAIN # .if ${MAKE_VERSION:U0} > 20100408 _this = ${.PARSEDIR:tA}/${.PARSEFILE} diff --git a/contrib/bmake/mk/sys/SCO_SV.mk b/contrib/bmake/mk/sys/SCO_SV.mk new file mode 100644 index 000000000000..9bf4b7c587c5 --- /dev/null +++ b/contrib/bmake/mk/sys/SCO_SV.mk @@ -0,0 +1,13 @@ +# $Id: SCO_SV.mk,v 1.1 2021/10/13 16:45:52 sjg Exp $ + +OS = SCO_SV +OS_DEF_FLAG := -D${OS} + +CC ?= gcc +CXX ?= g++ +DEV_TOOLS_PREFIX ?= /usr/xdev +FC ?= gfortran +INSTALL ?= /usr/gnu/bin/install +LD ?= gcc + +.include "UnixWare.mk" diff --git a/contrib/bmake/mk/sys/UnixWare.mk b/contrib/bmake/mk/sys/UnixWare.mk index 272d3e65c2d8..9e0216399ade 100644 --- a/contrib/bmake/mk/sys/UnixWare.mk +++ b/contrib/bmake/mk/sys/UnixWare.mk @@ -1,16 +1,18 @@ -# $Id: UnixWare.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ +# $Id: UnixWare.mk,v 1.8 2021/10/13 16:45:52 sjg Exp $ # based on "Id: SunOS.5.sys.mk,v 1.6 2003/09/30 16:42:23 sjg Exp " # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 OS ?= UnixWare +OS_DEF_FLAG ?= -DUNIXWARE unix ?= We run ${OS}. ROOT_GROUP ?= root +DEV_TOOLS_PREFIX ?= /usr/local -# can't fine one anywhere, so just stop the dependency +# can't find one anywhere, so just stop the dependency LIBCRT0 ?= /dev/null -PATH ?=/usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:/usr/local/bin +PATH ?= /usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:${DEV_TOOLS_PREFIX}/bin .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 @@ -32,8 +34,8 @@ COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} # at least gcc 2.95 on UnixWare has no internal macro to identify the system -.if exists(/usr/local/bin/gcc) -CC ?= gcc -pipe -DUNIXWARE +.if exists(${DEV_TOOLS_PREFIX}/bin/gcc) +CC ?= gcc -pipe ${OS_DEF_FLAG} DBG ?= -O -g STATIC ?= -static .else @@ -45,8 +47,8 @@ CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -.if exists(/usr/local/bin/g++) -CXX ?= g++ -DUNIXWARE +.if exists(${DEV_TOOLS_PREFIX}/bin/g++) +CXX ?= g++ ${OS_DEF_FLAG} .else CXX ?= c++ # XXX: don't know about UDK compilers .endif @@ -54,13 +56,17 @@ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} +.if exists(${DEV_TOOLS_PREFIX}/bin/cpp) +CPP ?= cpp +.else CPP ?= /usr/ccs/lib/cpp +.endif .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N -.if exists(/usr/local/bin/g77) +.if exists(${DEV_TOOLS_PREFIX}/bin/g77) FC ?= g77 .else FC ?= f77 # XXX: don't know about UDK compilers @@ -125,7 +131,7 @@ SIZE ?= size TSORT ?= tsort -.if exists(/usr/local/bin/bison) +.if exists(${DEV_TOOLS_PREFIX}/bin/bison) YACC ?= bison -y .else YACC ?= yacc diff --git a/contrib/bmake/nonints.h b/contrib/bmake/nonints.h index 7119d798432b..8583e50270fd 100644 --- a/contrib/bmake/nonints.h +++ b/contrib/bmake/nonints.h @@ -1,4 +1,4 @@ -/* $NetBSD: nonints.h,v 1.213 2021/04/11 13:35:56 rillig Exp $ */ +/* $NetBSD: nonints.h,v 1.217 2021/12/12 20:45:48 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -165,9 +165,9 @@ bool Parse_IsVar(const char *, VarAssign *out_var); void Parse_Var(VarAssign *, GNode *); void Parse_AddIncludeDir(const char *); void Parse_File(const char *, int); -void Parse_SetInput(const char *, int, int, ReadMoreProc, void *); +void Parse_PushInput(const char *, int, int, ReadMoreProc, void *); void Parse_MainName(GNodeList *); -int Parse_GetFatals(void); +int Parse_NumErrors(void); #ifndef HAVE_STRLCPY @@ -192,6 +192,7 @@ void Suff_FindDeps(GNode *); SearchPath *Suff_FindPath(GNode *); void Suff_SetNull(const char *); void Suff_PrintAll(void); +const char *Suff_NamesStr(void); /* targ.c */ void Targ_Init(void); @@ -211,7 +212,7 @@ void Targ_PrintCmds(GNode *); void Targ_PrintNode(GNode *, int); void Targ_PrintNodes(GNodeList *, int); const char *Targ_FmtTime(time_t); -void Targ_PrintType(int); +void Targ_PrintType(GNodeType); void Targ_PrintGraph(int); void Targ_Propagate(void); const char *GNodeMade_Name(GNodeMade); diff --git a/contrib/bmake/os.sh b/contrib/bmake/os.sh index 7e6823b240c3..1fc66ecc6a63 100644 --- a/contrib/bmake/os.sh +++ b/contrib/bmake/os.sh @@ -17,7 +17,7 @@ # Simon J. Gerraty # RCSid: -# $Id: os.sh,v 1.56 2020/08/05 23:25:22 sjg Exp $ +# $Id: os.sh,v 1.59 2021/11/14 04:18:00 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # @@ -172,7 +172,7 @@ Interix) MACHINE=i386 MACHINE_ARCH=i386 ;; -UnixWare) +UnixWare|SCO_SV) OSREL=`uname -v` OSMAJOR=`IFS=.; set $OSREL; echo $1` MACHINE_ARCH=`uname -m` diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c index 3c2d75acdf9a..6a310c74ba5c 100644 --- a/contrib/bmake/parse.c +++ b/contrib/bmake/parse.c @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -124,7 +124,7 @@ #include "pathnames.h" /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $"); +MAKE_RCSID("$NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $"); /* types and constants */ @@ -139,10 +139,14 @@ typedef struct IFile { unsigned int cond_depth; /* 'if' nesting when file opened */ bool depending; /* state of doing_depend on EOF */ - /* The buffer from which the file's content is read. */ + /* + * The buffer from which the file's content is read. The buffer + * always ends with '\n', the buffer is not null-terminated, that is, + * buf_end[0] is already out of bounds. + */ char *buf_freeIt; char *buf_ptr; /* next char to be read */ - char *buf_end; + char *buf_end; /* buf_end[-1] == '\n' */ /* Function to read more data, with a single opaque argument. */ ReadMoreProc readMore; @@ -233,7 +237,7 @@ static GNode *order_pred; /* parser state */ /* number of fatal errors */ -static int fatals = 0; +static int parseErrors = 0; /* * Variables for doing includes @@ -276,7 +280,7 @@ SearchPath *defSysIncPath; /* default for sysIncPath */ * keyword is used as a source ("0" if the keyword isn't special as a source) */ static const struct { - const char *name; /* Name of keyword */ + const char name[17]; /* Name of keyword */ ParseSpecial spec; /* Type when used as a target */ GNodeType op; /* Operator when used as a source */ } parseKeywords[] = { @@ -329,8 +333,6 @@ static const struct { /* file loader */ struct loadedfile { - /* XXX: What is the lifetime of this path? Who manages the memory? */ - const char *path; /* name, for error reports */ char *buf; /* contents buffer */ size_t len; /* length of contents */ bool used; /* XXX: have we used the data yet */ @@ -338,12 +340,11 @@ struct loadedfile { /* XXX: What is the lifetime of the path? Who manages the memory? */ static struct loadedfile * -loadedfile_create(const char *path, char *buf, size_t buflen) +loadedfile_create(char *buf, size_t buflen) { struct loadedfile *lf; lf = bmake_malloc(sizeof *lf); - lf->path = path == NULL ? "(stdin)" : path; lf->buf = buf; lf->len = buflen; lf->used = false; @@ -468,8 +469,7 @@ loadfile(const char *path, int fd) close(fd); { - struct loadedfile *lf = loadedfile_create(path, - buf.data, buf.len); + struct loadedfile *lf = loadedfile_create(buf.data, buf.len); Buf_DoneData(&buf); return lf; } @@ -545,7 +545,7 @@ ParseIsEscaped(const char *line, const char *c) * was first defined. */ static void -ParseMark(GNode *gn) +RememberLocation(GNode *gn) { IFile *curFile = CurFile(); gn->fname = curFile->fname; @@ -628,7 +628,7 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno, goto print_stack_trace; if (type == PARSE_WARNING && !opts.parseWarnFatal) goto print_stack_trace; - fatals++; + parseErrors++; if (type == PARSE_WARNING && !fatal_warning_error_printed) { Error("parsing warnings being treated as errors"); fatal_warning_error_printed = true; @@ -797,7 +797,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op) cohort = Targ_NewInternalNode(gn->name); if (doing_depend) - ParseMark(cohort); + RememberLocation(cohort); /* * Make the cohort invisible as well to avoid duplicating it * into other variables. True, parents of this target won't @@ -852,7 +852,7 @@ ParseDependencySourceWait(bool isSpecial) snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); gn = Targ_NewInternalNode(wait_src); if (doing_depend) - ParseMark(gn); + RememberLocation(gn); gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; LinkToTargets(gn, isSpecial); @@ -912,7 +912,7 @@ ParseDependencySourceOrder(const char *src) */ gn = Targ_GetNode(src); if (doing_depend) - ParseMark(gn); + RememberLocation(gn); if (order_pred != NULL) { Lst_Append(&order_pred->order_succ, gn); Lst_Append(&gn->order_pred, order_pred); @@ -949,7 +949,7 @@ ParseDependencySourceOther(const char *src, GNodeType tOp, /* Find/create the 'src' node and attach to all targets */ gn = Targ_GetNode(src); if (doing_depend) - ParseMark(gn); + RememberLocation(gn); if (tOp != OP_NONE) gn->type |= tOp; else @@ -1019,7 +1019,7 @@ ParseErrorNoDependency(const char *lstart) (strncmp(lstart, "======", 6) == 0) || (strncmp(lstart, ">>>>>>", 6) == 0)) Parse_Error(PARSE_FATAL, - "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); + "Makefile appears to contain unresolved CVS/RCS/??? merge conflicts"); else if (lstart[0] == '.') { const char *dirstart = lstart + 1; const char *dirend; @@ -1100,7 +1100,7 @@ ParseDependencyTargetSpecial(ParseSpecial *inout_specType, case SP_INTERRUPT: { GNode *gn = Targ_GetNode(targetName); if (doing_depend) - ParseMark(gn); + RememberLocation(gn); gn->type |= OP_NOTMAIN | OP_SPECIAL; Lst_Append(targets, gn); break; @@ -1230,7 +1230,7 @@ ParseDependencyTargetMundane(char *targetName, StringList *curTargs) ? Suff_AddTransform(targName) : Targ_GetNode(targName); if (doing_depend) - ParseMark(gn); + RememberLocation(gn); Lst_Append(targets, gn); } @@ -1612,10 +1612,8 @@ ParseDependencySourcesMundane(char *start, char *end, * See the tests depsrc-*.mk. */ static void -ParseDependencySources(char *const line, char *const cp, - GNodeType const tOp, - ParseSpecial const specType, - SearchPathList ** inout_paths) +ParseDependencySources(char *line, char *cp, GNodeType tOp, + ParseSpecial specType, SearchPathList **inout_paths) { if (line[0] == '\0') { ParseDependencySourcesEmpty(specType, *inout_paths); @@ -2099,7 +2097,7 @@ ParseAddCmd(GNode *gn, char *cmd) Lst_Append(&gn->commands, cmd); if (MaybeSubMake(cmd)) gn->type |= OP_SUBMAKE; - ParseMark(gn); + RememberLocation(gn); } else { #if 0 /* XXX: We cannot do this until we fix the tree */ @@ -2132,7 +2130,7 @@ Parse_AddIncludeDir(const char *dir) /* * Handle one of the .[-ds]include directives by remembering the current file * and pushing the included file on the stack. After the included file has - * finished, parsing continues with the including file; see Parse_SetInput + * finished, parsing continues with the including file; see Parse_PushInput * and ParseEOF. * * System includes are looked up in sysIncPath, any other includes are looked @@ -2140,7 +2138,7 @@ Parse_AddIncludeDir(const char *dir) * line options. */ static void -IncludeFile(char *file, bool isSystem, bool depinc, bool silent) +IncludeFile(const char *file, bool isSystem, bool depinc, bool silent) { struct loadedfile *lf; char *fullname; /* full pathname of file */ @@ -2239,60 +2237,63 @@ IncludeFile(char *file, bool isSystem, bool depinc, bool silent) lf = loadfile(fullname, fd); /* Start reading from this file next */ - Parse_SetInput(fullname, 0, -1, loadedfile_readMore, lf); + Parse_PushInput(fullname, 0, -1, loadedfile_readMore, lf); CurFile()->lf = lf; if (depinc) doing_depend = depinc; /* only turn it on */ + /* TODO: consider free(fullname); */ } +/* + * Parse a directive like '.include' or '.-include'. + * + * .include "user-makefile.mk" + * .include + */ static void ParseInclude(char *directive) { - char endc; /* the character which ends the file spec */ - char *cp; /* current position in file spec */ + char endc; /* '>' or '"' */ + char *p; bool silent = directive[0] != 'i'; - char *file = directive + (silent ? 8 : 7); + FStr file; - /* Skip to delimiter character so we know where to look */ - pp_skip_hspace(&file); + p = directive + (silent ? 8 : 7); + pp_skip_hspace(&p); - if (*file != '"' && *file != '<') { + if (*p != '"' && *p != '<') { Parse_Error(PARSE_FATAL, ".include filename must be delimited by '\"' or '<'"); return; } - /* - * Set the search path on which to find the include file based on the - * characters which bracket its name. Angle-brackets imply it's - * a system Makefile while double-quotes imply it's a user makefile - */ - if (*file == '<') + if (*p++ == '<') endc = '>'; else endc = '"'; + file = FStr_InitRefer(p); /* Skip to matching delimiter */ - for (cp = ++file; *cp != '\0' && *cp != endc; cp++) - continue; + while (*p != '\0' && *p != endc) + p++; - if (*cp != endc) { + if (*p != endc) { Parse_Error(PARSE_FATAL, "Unclosed .include filename. '%c' expected", endc); return; } - *cp = '\0'; + *p = '\0'; - /* - * Substitute for any variables in the filename before trying to - * find the file. - */ - (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &file); - /* TODO: handle errors */ + if (strchr(file.str, '$') != NULL) { + char *xfile; + Var_Subst(file.str, SCOPE_CMDLINE, VARE_WANTRES, &xfile); + /* TODO: handle errors */ + file = FStr_InitOwn(xfile); + } - IncludeFile(file, endc == '>', directive[0] == 'd', silent); - free(file); + IncludeFile(file.str, endc == '>', directive[0] == 'd', silent); + FStr_Done(&file); } /* @@ -2314,8 +2315,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) basename = slash + 1; } - Global_SetExpand(dirvar, dirname.str); - Global_SetExpand(filevar, basename); + Global_Set(dirvar, dirname.str); + Global_Set(filevar, basename); DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n", __func__, dirvar, dirname.str, filevar, basename); @@ -2418,7 +2419,7 @@ ParseTrackInput(const char *name) * The given file is added to the includes stack. */ void -Parse_SetInput(const char *name, int lineno, int fd, +Parse_PushInput(const char *name, int lineno, int fd, ReadMoreProc readMore, void *readMoreArg) { IFile *curFile; @@ -2431,7 +2432,7 @@ Parse_SetInput(const char *name, int lineno, int fd, else ParseTrackInput(name); - DEBUG3(PARSE, "Parse_SetInput: %s %s, line %d\n", + DEBUG3(PARSE, "Parse_PushInput: %s %s, line %d\n", readMore == loadedfile_readMore ? "file" : ".for loop in", name, lineno); @@ -2649,13 +2650,15 @@ typedef enum ParseRawLineResult { /* * Parse until the end of a line, taking into account lines that end with - * backslash-newline. + * backslash-newline. The resulting line goes from out_line to out_line_end; + * the line is not null-terminated. */ static ParseRawLineResult ParseRawLine(IFile *curFile, char **out_line, char **out_line_end, char **out_firstBackslash, char **out_firstComment) { char *line = curFile->buf_ptr; + char *buf_end = curFile->buf_end; char *p = line; char *line_end = line; char *firstBackslash = NULL; @@ -2667,14 +2670,14 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end, for (;;) { char ch; - if (p == curFile->buf_end) { + if (p == buf_end) { res = PRLR_EOF; break; } ch = *p; if (ch == '\0' || - (ch == '\\' && p + 1 < curFile->buf_end && p[1] == '\0')) { + (ch == '\\' && p + 1 < buf_end && p[1] == '\0')) { Parse_Error(PARSE_FATAL, "Zero byte read from file"); return PRLR_ERROR; } @@ -2685,7 +2688,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end, firstBackslash = p; if (p[1] == '\n') { curFile->lineno++; - if (p + 2 == curFile->buf_end) { + if (p + 2 == buf_end) { line_end = p; *line_end = '\n'; p += 2; @@ -2694,7 +2697,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end, } p += 2; line_end = p; - assert(p <= curFile->buf_end); + assert(p <= buf_end); continue; } @@ -2904,7 +2907,7 @@ ParseForLoop(const char *line) line = ParseGetLine(GLM_FOR_BODY); if (line == NULL) { Parse_Error(PARSE_FATAL, - "Unexpected end of file in for loop."); + "Unexpected end of file in .for loop"); break; } } while (For_Accum(line)); @@ -3012,12 +3015,6 @@ ParseLine_ShellCommand(const char *p) } } -MAKE_INLINE bool -IsDirective(const char *dir, size_t dirlen, const char *name) -{ - return dirlen == strlen(name) && memcmp(dir, name, dirlen) == 0; -} - /* * See if the line starts with one of the known directives, and if so, handle * the directive. @@ -3026,8 +3023,8 @@ static bool ParseDirective(char *line) { char *cp = line + 1; - const char *dir, *arg; - size_t dirlen; + const char *arg; + Substring dir; pp_skip_whitespace(&cp); if (IsInclude(cp, false)) { @@ -3035,10 +3032,10 @@ ParseDirective(char *line) return true; } - dir = cp; + dir.start = cp; while (ch_isalpha(*cp) || *cp == '-') cp++; - dirlen = (size_t)(cp - dir); + dir.end = cp; if (*cp != '\0' && !ch_isspace(*cp)) return false; @@ -3046,31 +3043,31 @@ ParseDirective(char *line) pp_skip_whitespace(&cp); arg = cp; - if (IsDirective(dir, dirlen, "undef")) { - Var_Undef(cp); + if (Substring_Equals(dir, "undef")) { + Var_Undef(arg); return true; - } else if (IsDirective(dir, dirlen, "export")) { + } else if (Substring_Equals(dir, "export")) { Var_Export(VEM_PLAIN, arg); return true; - } else if (IsDirective(dir, dirlen, "export-env")) { + } else if (Substring_Equals(dir, "export-env")) { Var_Export(VEM_ENV, arg); return true; - } else if (IsDirective(dir, dirlen, "export-literal")) { + } else if (Substring_Equals(dir, "export-literal")) { Var_Export(VEM_LITERAL, arg); return true; - } else if (IsDirective(dir, dirlen, "unexport")) { + } else if (Substring_Equals(dir, "unexport")) { Var_UnExport(false, arg); return true; - } else if (IsDirective(dir, dirlen, "unexport-env")) { + } else if (Substring_Equals(dir, "unexport-env")) { Var_UnExport(true, arg); return true; - } else if (IsDirective(dir, dirlen, "info")) { + } else if (Substring_Equals(dir, "info")) { ParseMessage(PARSE_INFO, "info", arg); return true; - } else if (IsDirective(dir, dirlen, "warning")) { + } else if (Substring_Equals(dir, "warning")) { ParseMessage(PARSE_WARNING, "warning", arg); return true; - } else if (IsDirective(dir, dirlen, "error")) { + } else if (Substring_Equals(dir, "error")) { ParseMessage(PARSE_FATAL, "error", arg); return true; } @@ -3251,7 +3248,7 @@ Parse_File(const char *name, int fd) if (name == NULL) name = "(stdin)"; - Parse_SetInput(name, 0, -1, loadedfile_readMore, lf); + Parse_PushInput(name, 0, -1, loadedfile_readMore, lf); CurFile()->lf = lf; do { @@ -3265,7 +3262,7 @@ Parse_File(const char *name, int fd) FinishDependencyGroup(); - if (fatals != 0) { + if (parseErrors != 0) { (void)fflush(stdout); (void)fprintf(stderr, "%s: Fatal errors encountered -- cannot continue", @@ -3320,7 +3317,7 @@ Parse_MainName(GNodeList *mainList) } int -Parse_GetFatals(void) +Parse_NumErrors(void) { - return fatals; + return parseErrors; } diff --git a/contrib/bmake/sigact.h b/contrib/bmake/sigact.h new file mode 100644 index 000000000000..1dc04ba2dbdd --- /dev/null +++ b/contrib/bmake/sigact.h @@ -0,0 +1,104 @@ +/* NAME: + * sigact.h - sigaction et al + * + * SYNOPSIS: + * #include "sigact.h" + * + * DESCRIPTION: + * This header is the interface to a fake sigaction(2) + * implementation. It provides a POSIX compliant interface + * to whatever signal handling mechanisms are available. + * It also provides a Signal() function that is implemented + * in terms of sigaction(). + * If not using signal(2) as part of the underlying + * implementation (USE_SIGNAL or USE_SIGMASK), and + * NO_SIGNAL is not defined, it also provides a signal() + * function that calls Signal(). + * + * SEE ALSO: + * sigact.c + */ +/* + * RCSid: + * $Id: sigact.h,v 1.4 2021/10/14 19:39:17 sjg Exp $ + */ +#ifndef _SIGACT_H +#define _SIGACT_H + +#include + +/* + * most modern systems use void for signal handlers but + * not all. + */ +#ifndef SIG_HDLR +# define SIG_HDLR void +#endif + +/* + * if you want to install this header as signal.h, + * modify this to pick up the original signal.h + */ +#ifndef SIGKILL +# include +#endif +#ifndef SIGKILL +# include +#endif + +#ifndef SIG_ERR +# define SIG_ERR (SIG_HDLR (*)())-1 +#endif +#ifndef BADSIG +# define BADSIG SIG_ERR +#endif + +#ifndef SA_NOCLDSTOP +/* we assume we need the fake sigaction */ +/* sa_flags */ +#define SA_NOCLDSTOP 1 /* don't send SIGCHLD on child stop */ +#define SA_RESTART 2 /* re-start I/O */ + +/* sigprocmask flags */ +#define SIG_BLOCK 1 +#define SIG_UNBLOCK 2 +#define SIG_SETMASK 4 + +/* + * this is a bit untidy + */ +#ifdef _SIGSET_T_ +typedef _SIGSET_T_ sigset_t; +#endif + +/* + * POSIX sa_handler should return void, but since we are + * implementing in terms of something else, it may + * be appropriate to use the normal SIG_HDLR return type + */ +struct sigaction +{ + SIG_HDLR (*sa_handler)(); + sigset_t sa_mask; + int sa_flags; +}; + + +int sigaction ( int /*sig*/, const struct sigaction */*act*/, struct sigaction */*oact*/ ); +int sigaddset ( sigset_t */*mask*/, int /*sig*/ ); +int sigdelset ( sigset_t */*mask*/, int /*sig*/ ); +int sigemptyset ( sigset_t */*mask*/ ); +int sigfillset ( sigset_t */*mask*/ ); +int sigismember ( const sigset_t */*mask*/, int /*sig*/ ); +int sigpending ( sigset_t */*set*/ ); +int sigprocmask ( int how, const sigset_t */*set*/, sigset_t */*oset*/ ); +int sigsuspend ( sigset_t */*mask*/ ); + +#ifndef sigmask +# define sigmask(s) (1<<((s)-1) & (32 - 1)) /* convert SIGnum to mask */ +#endif +#if !defined(NSIG) && defined(_NSIG) +# define NSIG _NSIG +#endif +#endif /* ! SA_NOCLDSTOP */ +#endif /* _SIGACT_H */ diff --git a/contrib/bmake/sigaction.c b/contrib/bmake/sigaction.c new file mode 100644 index 000000000000..dc647e7b058f --- /dev/null +++ b/contrib/bmake/sigaction.c @@ -0,0 +1,397 @@ +/* NAME: + * sigact.c - fake sigaction(2) + * + * SYNOPSIS: + * #include "sigact.h" + * + * int sigaction(int sig, struct sigaction *act, + * struct sigaction *oact); + * int sigaddset(sigset_t *mask, int sig); + * int sigdelset(sigset_t *mask, int sig); + * int sigemptyset(sigset_t *mask); + * int sigfillset(sigset_t *mask); + * int sigismember(sigset_t *mask, int sig); + * int sigpending(sigset_t *set); + * int sigprocmask(int how, sigset_t *set, sigset_t *oset); + * int sigsuspend(sigset_t *mask); + * + * SIG_HDLR (*Signal(int sig, SIG_HDLR (*disp)(int)))(int); + * + * DESCRIPTION: + * This is a fake sigaction implementation. It uses + * sigsetmask(2) et al or sigset(2) and friends if + * available, otherwise it just uses signal(2). If it + * thinks sigaction(2) really exists it compiles to "almost" + * nothing. + * + * In any case it provides a Signal() function that is + * implemented in terms of sigaction(). + * If not using signal(2) as part of the underlying + * implementation (USE_SIGNAL or USE_SIGMASK), and + * NO_SIGNAL is not defined, it also provides a signal() + * function that calls Signal(). + * + * The need for all this mucking about is the problems + * caused by mixing various signal handling mechanisms in + * the one process. This module allows for a consistent + * POSIX compliant interface to whatever is actually + * available. + * + * sigaction() allows the caller to examine and/or set the + * action to be associated with a given signal. "act" and + * "oact" are pointers to 'sigaction structs': + *.nf + * + * struct sigaction + * { + * SIG_HDLR (*sa_handler)(); + * sigset_t sa_mask; + * int sa_flags; + * }; + *.fi + * + * SIG_HDLR is normally 'void' in the POSIX implementation + * and for most current systems. On some older UNIX + * systems, signal handlers do not return 'void', so + * this implementation keeps 'sa_handler' inline with the + * hosts normal signal handling conventions. + * 'sa_mask' controls which signals will be blocked while + * the selected signal handler is active. It is not used + * in this implementation. + * 'sa_flags' controls various semantics such as whether + * system calls should be automagically restarted + * (SA_RESTART) etc. It is not used in this + * implementation. + * Either "act" or "oact" may be NULL in which case the + * appropriate operation is skipped. + * + * sigaddset() adds "sig" to the sigset_t pointed to by "mask". + * + * sigdelset() removes "sig" from the sigset_t pointed to + * by "mask". + * + * sigemptyset() makes the sigset_t pointed to by "mask" empty. + * + * sigfillset() makes the sigset_t pointed to by "mask" + * full ie. match all signals. + * + * sigismember() returns true if "sig" is found in "*mask". + * + * sigpending() is supposed to return "set" loaded with the + * set of signals that are blocked and pending for the + * calling process. It does nothing in this impementation. + * + * sigprocmask() is used to examine and/or change the + * signal mask for the calling process. Either "set" or + * "oset" may be NULL in which case the appropriate + * operation is skipped. "how" may be one of SIG_BLOCK, + * SIG_UNBLOCK or SIG_SETMASK. If this package is built + * with USE_SIGNAL, then this routine achieves nothing. + * + * sigsuspend() sets the signal mask to "*mask" and waits + * for a signal to be delivered after which the previous + * mask is restored. + * + * + * RETURN VALUE: + * 0==success, -1==failure + * + * BUGS: + * Since we fake most of this, don't expect fancy usage to + * work. + * + * AUTHOR: + * Simon J. Gerraty + */ +/* COPYRIGHT: + * @(#)Copyright (c) 1992-2021, Simon J. Gerraty + * + * This is free software. It comes with NO WARRANTY. + * Permission to use, modify and distribute this source code + * is granted subject to the following conditions. + * 1/ that that the above copyright notice and this notice + * are preserved in all copies and that due credit be given + * to the author. + * 2/ that any changes to this code are clearly commented + * as such so that the author does get blamed for bugs + * other than his own. + * + * Please send copies of changes and bug-fixes to: + * sjg@crufty.net + * + */ +#ifndef lint +static char *RCSid = "$Id: sigact.c,v 1.8 2021/10/14 19:39:17 sjg Exp $"; +#endif + +#undef _ANSI_SOURCE /* causes problems */ + +#include +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +# ifdef NO_SIGSET +# undef HAVE_SIGSET +# endif +# ifndef HAVE_SIGACTION +# ifdef HAVE_SIGSETMASK +# define USE_SIGMASK +# else +# ifdef HAVE_SIGSET +# define USE_SIGSET +# else +# define USE_SIGNAL +# endif +# endif +# endif +#endif + +/* + * some systems have a faulty sigaction() implementation! + * Allow us to bypass it. + * Or they may have installed sigact.h as signal.h which is why + * we have SA_NOCLDSTOP defined. + */ +#if !defined(SA_NOCLDSTOP) || defined(_SIGACT_H) || defined(USE_SIGNAL) || defined(USE_SIGSET) || defined(USE_SIGMASK) + +/* + * if we haven't been told, + * try and guess what we should implement with. + */ +#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL) +# if defined(sigmask) || defined(BSD) || defined(_BSD) && !defined(BSD41) +# define USE_SIGMASK +# else +# ifndef NO_SIGSET +# define USE_SIGSET +# else +# define USE_SIGNAL +# endif +# endif +#endif +/* + * if we still don't know, we're in trouble + */ +#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL) +error must know what to implement with +#endif + +#include "sigact.h" + +/* + * in case signal() has been mapped to our Signal(). + */ +#undef signal + + +int +sigaction(int sig, + const struct sigaction *act, + struct sigaction *oact) +{ + SIG_HDLR(*oldh) (); + + if (act) { +#ifdef USE_SIGSET + oldh = sigset(sig, act->sa_handler); +#else + oldh = signal(sig, act->sa_handler); +#endif + } else { + if (oact) { +#ifdef USE_SIGSET + oldh = sigset(sig, SIG_IGN); +#else + oldh = signal(sig, SIG_IGN); +#endif + if (oldh != SIG_IGN && oldh != SIG_ERR) { +#ifdef USE_SIGSET + (void) sigset(sig, oldh); +#else + (void) signal(sig, oldh); +#endif + } + } + } + if (oact) { + oact->sa_handler = oldh; + } + return 0; /* hey we're faking it */ +} + +#ifndef HAVE_SIGADDSET +int +sigaddset(sigset_t *mask, int sig) +{ + *mask |= sigmask(sig); + return 0; +} + + +int +sigdelset(sigset_t *mask, int sig) +{ + *mask &= ~(sigmask(sig)); + return 0; +} + + +int +sigemptyset(sigset_t *mask) +{ + *mask = 0; + return 0; +} + + +int +sigfillset(sigset_t *mask) +{ + *mask = ~0; + return 0; +} + + +int +sigismember(const sigset_t *mask, int sig) +{ + return ((*mask) & sigmask(sig)); +} +#endif + +#ifndef HAVE_SIGPENDING +int +sigpending(sigset_t *set) +{ + return 0; /* faking it! */ +} +#endif + +#ifndef HAVE_SIGPROCMASK +int +sigprocmask(int how, const sigset_t *set, sigset_t *oset) +{ +#ifdef USE_SIGSET + int i; +#endif + static sigset_t sm; + static int once = 0; + + if (!once) { + /* + * initally we clear sm, + * there after, it represents the last + * thing we did. + */ + once++; +#ifdef USE_SIGMASK + sm = sigblock(0); +#else + sm = 0; +#endif + } + if (oset) + *oset = sm; + if (set) { + switch (how) { + case SIG_BLOCK: + sm |= *set; + break; + case SIG_UNBLOCK: + sm &= ~(*set); + break; + case SIG_SETMASK: + sm = *set; + break; + } +#ifdef USE_SIGMASK + (void) sigsetmask(sm); +#else +#ifdef USE_SIGSET + for (i = 1; i < NSIG; i++) { + if (how == SIG_UNBLOCK) { + if (*set & sigmask(i)) + sigrelse(i); + } else + if (sm & sigmask(i)) { + sighold(i); + } + } +#endif +#endif + } + return 0; +} +#endif + +#ifndef HAVE_SIGSUSPEND +int +sigsuspend(sigset_t *mask) +{ +#ifdef USE_SIGMASK + sigpause(*mask); +#else + int i; + +#ifdef USE_SIGSET + + for (i = 1; i < NSIG; i++) { + if (*mask & sigmask(i)) { + /* not the same sigpause() as above! */ + sigpause(i); + break; + } + } +#else /* signal(2) only */ + SIG_HDLR(*oldh) (); + + /* + * make sure that signals in mask will not + * be ignored. + */ + for (i = 1; i < NSIG; i++) { + if (*mask & sigmask(i)) { + if ((oldh = signal(i, SIG_DFL)) != SIG_ERR && + oldh != SIG_IGN && + oldh != SIG_DFL) + (void) signal(i, oldh); /* restore handler */ + } + } + pause(); /* wait for a signal */ +#endif +#endif + return 0; +} +#endif +#endif /* ! SA_NOCLDSTOP */ + +#if 0 +#if !defined(SIG_HDLR) +#define SIG_HDLR void +#endif +#if !defined(SIG_ERR) +#define SIG_ERR (SIG_HDLR (*)())-1 +#endif + +#if !defined(USE_SIGNAL) && !defined(USE_SIGMASK) && !defined(NO_SIGNAL) +/* + * ensure we avoid signal mayhem + */ + +extern void (*Signal (int sig, void (*handler) (int)))(int); + +SIG_HDLR(*signal(int sig, SIG_HDLR(*handler)(int)) +{ + return (Signal(sig, handler)); +} +#endif +#endif + +/* This lot (for GNU-Emacs) goes at the end of the file. */ +/* + * Local Variables: + * version-control:t + * comment-column:40 + * End: + */ diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c index b4baede4d417..11e7595dd867 100644 --- a/contrib/bmake/str.c +++ b/contrib/bmake/str.c @@ -1,4 +1,4 @@ -/* $NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $ */ +/* $NetBSD: str.c,v 1.86 2021/06/21 16:59:18 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -71,7 +71,7 @@ #include "make.h" /* "@(#)str.c 5.8 (Berkeley) 6/1/90" */ -MAKE_RCSID("$NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $"); +MAKE_RCSID("$NetBSD: str.c,v 1.86 2021/06/21 16:59:18 rillig Exp $"); /* Return the concatenation of s1 and s2, freshly allocated. */ char * @@ -188,10 +188,9 @@ Substring_Words(const char *str, bool expand) *word_end++ = '\0'; if (words_len == words_cap) { - size_t new_size; words_cap *= 2; - new_size = (words_cap + 1) * sizeof(words[0]); - words = bmake_realloc(words, new_size); + words = bmake_realloc(words, + (words_cap + 1) * sizeof(words[0])); } words[words_len++] = Substring_Init(word_start, word_end - 1); diff --git a/contrib/bmake/str.h b/contrib/bmake/str.h index ce0bb5ad82bc..bd56a2981785 100644 --- a/contrib/bmake/str.h +++ b/contrib/bmake/str.h @@ -1,4 +1,4 @@ -/* $NetBSD: str.h,v 1.9 2021/05/30 21:16:54 rillig Exp $ */ +/* $NetBSD: str.h,v 1.12 2021/12/12 13:43:47 rillig Exp $ */ /* Copyright (c) 2021 Roland Illig @@ -60,7 +60,6 @@ typedef struct LazyBuf { size_t len; size_t cap; const char *expected; - void *freeIt; } LazyBuf; /* The result of splitting a string into words. */ @@ -182,6 +181,14 @@ Substring_Equals(Substring sub, const char *str) memcmp(sub.start, str, len) == 0; } +MAKE_INLINE bool +Substring_Eq(Substring sub, Substring str) +{ + size_t len = Substring_Length(sub); + return len == Substring_Length(str) && + memcmp(sub.start, str.start, len) == 0; +} + MAKE_STATIC Substring Substring_Sub(Substring sub, size_t start, size_t end) { @@ -266,13 +273,12 @@ LazyBuf_Init(LazyBuf *buf, const char *expected) buf->len = 0; buf->cap = 0; buf->expected = expected; - buf->freeIt = NULL; } MAKE_INLINE void LazyBuf_Done(LazyBuf *buf) { - free(buf->freeIt); + free(buf->data); } MAKE_STATIC void @@ -329,6 +335,11 @@ LazyBuf_Get(const LazyBuf *buf) return Substring_Init(start, start + buf->len); } +/* + * Returns the content of the buffer as a newly allocated string. + * + * See LazyBuf_Get to avoid unnecessary memory allocations. + */ MAKE_STATIC FStr LazyBuf_DoneGet(LazyBuf *buf) { @@ -352,6 +363,14 @@ Words_Free(Words w) SubstringWords Substring_Words(const char *, bool); +MAKE_INLINE void +SubstringWords_Init(SubstringWords *w) +{ + w->words = NULL; + w->len = 0; + w->freeIt = NULL; +} + MAKE_INLINE void SubstringWords_Free(SubstringWords w) { diff --git a/contrib/bmake/suff.c b/contrib/bmake/suff.c index b2c926a2b8bc..ab7ac6184173 100644 --- a/contrib/bmake/suff.c +++ b/contrib/bmake/suff.c @@ -1,4 +1,4 @@ -/* $NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $ */ +/* $NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -115,7 +115,7 @@ #include "dir.h" /* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */ -MAKE_RCSID("$NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $"); +MAKE_RCSID("$NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg Exp $"); typedef List SuffixList; typedef ListNode SuffixListNode; @@ -142,37 +142,6 @@ static GNodeList transforms = LST_INIT; */ static int sNum = 0; -typedef enum SuffixFlags { - SUFF_NONE = 0, - - /* - * This suffix marks include files. Their search path ends up in the - * undocumented special variable '.INCLUDES'. - */ - SUFF_INCLUDE = 1 << 0, - - /* - * This suffix marks library files. Their search path ends up in the - * undocumented special variable '.LIBS'. - */ - SUFF_LIBRARY = 1 << 1, - - /* - * The empty suffix. - * - * XXX: What is the difference between the empty suffix and the null - * suffix? - * - * XXX: Why is SUFF_NULL needed at all? Wouldn't nameLen == 0 mean - * the same? - */ - SUFF_NULL = 1 << 2 - -} SuffixFlags; - -ENUM_FLAGS_RTTI_3(SuffixFlags, - SUFF_INCLUDE, SUFF_LIBRARY, SUFF_NULL); - typedef List SuffixListList; /* @@ -184,24 +153,45 @@ typedef struct Suffix { char *name; /* Length of the name, to avoid strlen calls */ size_t nameLen; - /* Type of suffix */ - SuffixFlags flags; + /* + * This suffix marks include files. Their search path ends up in the + * undocumented special variable '.INCLUDES'. + */ + bool include:1; + /* + * This suffix marks library files. Their search path ends up in the + * undocumented special variable '.LIBS'. + */ + bool library:1; + /* + * The empty suffix. + * + * XXX: What is the difference between the empty suffix and the null + * suffix? + * + * XXX: Why is SUFF_NULL needed at all? Wouldn't nameLen == 0 mean + * the same? + */ + bool isNull:1; /* The path along which files of this suffix may be found */ SearchPath *searchPath; + /* The suffix number; TODO: document the purpose of this number */ int sNum; /* Reference count of list membership and several other places */ int refCount; + /* Suffixes we have a transformation to */ SuffixList parents; /* Suffixes we have a transformation from */ SuffixList children; - - /* Lists in which this suffix is referenced. + /* + * Lists in which this suffix is referenced. * * XXX: These lists are used nowhere, they are just appended to, for * no apparent reason. They do have the side effect of increasing - * refCount though. */ + * refCount though. + */ SuffixListList ref; } Suffix; @@ -475,7 +465,9 @@ Suffix_New(const char *name) Lst_Init(&suff->parents); Lst_Init(&suff->ref); suff->sNum = sNum++; - suff->flags = SUFF_NONE; + suff->include = false; + suff->library = false; + suff->isNull = false; suff->refCount = 1; /* XXX: why 1? It's not assigned anywhere yet. */ return suff; @@ -502,7 +494,9 @@ Suff_ClearSuffixes(void) emptySuff = nullSuff = Suffix_New(""); SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath); - nullSuff->flags = SUFF_NULL; + nullSuff->include = false; + nullSuff->library = false; + nullSuff->isNull = true; } /* @@ -619,6 +613,7 @@ Suff_AddTransform(const char *name) /* TODO: Avoid the redundant parsing here. */ bool ok = ParseTransform(name, &srcSuff, &targSuff); assert(ok); + /* LINTED 129 *//* expression has null effect */ (void)ok; } @@ -888,12 +883,12 @@ Suff_ExtendPaths(void) Suffix *suff = ln->datum; if (!Lst_IsEmpty(&suff->searchPath->dirs)) { #ifdef INCLUDES - if (suff->flags & SUFF_INCLUDE) + if (suff->include) SearchPath_AddAll(includesPath, suff->searchPath); #endif #ifdef LIBRARIES - if (suff->flags & SUFF_LIBRARY) + if (suff->library) SearchPath_AddAll(libsPath, suff->searchPath); #endif SearchPath_AddAll(suff->searchPath, &dirSearchPath); @@ -926,7 +921,7 @@ Suff_AddInclude(const char *suffName) { Suffix *suff = FindSuffixByName(suffName); if (suff != NULL) - suff->flags |= SUFF_INCLUDE; + suff->include = true; } /* @@ -940,7 +935,7 @@ Suff_AddLib(const char *suffName) { Suffix *suff = FindSuffixByName(suffName); if (suff != NULL) - suff->flags |= SUFF_LIBRARY; + suff->library = true; } /********** Implicit Source Search Functions *********/ @@ -1043,7 +1038,7 @@ CandidateList_AddCandidatesFor(CandidateList *list, Candidate *cand) for (ln = cand->suff->children.first; ln != NULL; ln = ln->next) { Suffix *suff = ln->datum; - if ((suff->flags & SUFF_NULL) && suff->name[0] != '\0') { + if (suff->isNull && suff->name[0] != '\0') { /* * If the suffix has been marked as the NULL suffix, * also create a candidate for a file with no suffix @@ -1918,7 +1913,7 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs) * If the suffix indicates that the target is a library, mark that in * the node's type field. */ - if (targ->suff->flags & SUFF_LIBRARY) + if (targ->suff->library) gn->type |= OP_LIB; /* @@ -2077,14 +2072,14 @@ Suff_SetNull(const char *name) Suffix *suff = FindSuffixByName(name); if (suff == NULL) { Parse_Error(PARSE_WARNING, - "Desired null suffix %s not defined.", + "Desired null suffix %s not defined", name); return; } if (nullSuff != NULL) - nullSuff->flags &= ~(unsigned)SUFF_NULL; - suff->flags |= SUFF_NULL; + nullSuff->isNull = false; + suff->isNull = true; /* XXX: Here's where the transformation mangling would take place. */ nullSuff = suff; } @@ -2117,31 +2112,36 @@ Suff_End(void) static void -PrintSuffNames(const char *prefix, SuffixList *suffs) +PrintSuffNames(const char *prefix, const SuffixList *suffs) { SuffixListNode *ln; debug_printf("#\t%s: ", prefix); for (ln = suffs->first; ln != NULL; ln = ln->next) { - Suffix *suff = ln->datum; + const Suffix *suff = ln->datum; debug_printf("%s ", suff->name); } debug_printf("\n"); } static void -Suffix_Print(Suffix *suff) +Suffix_Print(const Suffix *suff) { + Buffer buf; + + Buf_InitSize(&buf, 16); + Buf_AddFlag(&buf, suff->include, "SUFF_INCLUDE"); + Buf_AddFlag(&buf, suff->library, "SUFF_LIBRARY"); + Buf_AddFlag(&buf, suff->isNull, "SUFF_NULL"); + debug_printf("# \"%s\" (num %d, ref %d)", suff->name, suff->sNum, suff->refCount); - if (suff->flags != 0) { - char flags_buf[SuffixFlags_ToStringSize]; - - debug_printf(" (%s)", - SuffixFlags_ToString(flags_buf, suff->flags)); - } + if (buf.len > 0) + debug_printf(" (%s)", buf.data); debug_printf("\n"); + Buf_Done(&buf); + PrintSuffNames("To", &suff->parents); PrintSuffNames("From", &suff->children); @@ -2177,3 +2177,20 @@ Suff_PrintAll(void) PrintTransformation(ln->datum); } } + +const char * +Suff_NamesStr(void) +{ + Buffer buf; + SuffixListNode *ln; + Suffix *suff; + + Buf_InitSize(&buf, 16); + for (ln = sufflist.first; ln != NULL; ln = ln->next) { + suff = ln->datum; + if (ln != sufflist.first) + Buf_AddByte(&buf, ' '); + Buf_AddStr(&buf, suff->name); + } + return Buf_DoneData(&buf); +} diff --git a/contrib/bmake/targ.c b/contrib/bmake/targ.c index 68573644ff35..beb038fa1a4b 100644 --- a/contrib/bmake/targ.c +++ b/contrib/bmake/targ.c @@ -1,4 +1,4 @@ -/* $NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -113,7 +113,7 @@ #include "dir.h" /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $"); +MAKE_RCSID("$NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $"); /* * All target nodes that appeared on the left-hand side of one of the @@ -187,7 +187,7 @@ GNode_New(const char *name) gn->uname = NULL; gn->path = NULL; gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE; - gn->flags = GNF_NONE; + memset(&gn->flags, 0, sizeof(gn->flags)); gn->made = UNMADE; gn->unmade = 0; gn->mtime = 0; @@ -309,7 +309,7 @@ Targ_NewInternalNode(const char *name) Lst_Append(&allTargets, gn); DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name); if (doing_depend) - gn->flags |= FROM_DEPEND; + gn->flags.fromDepend = true; return gn; } @@ -416,36 +416,37 @@ Targ_FmtTime(time_t tm) /* Print out a type field giving only those attributes the user can set. */ void -Targ_PrintType(int type) +Targ_PrintType(GNodeType type) { - int tbit; + static const struct { + GNodeType bit; + bool internal; + const char name[10]; + } names[] = { + { OP_MEMBER, true, "MEMBER" }, + { OP_LIB, true, "LIB" }, + { OP_ARCHV, true, "ARCHV" }, + { OP_PHONY, true, "PHONY" }, + { OP_NOTMAIN, false, "NOTMAIN" }, + { OP_INVISIBLE, false, "INVISIBLE" }, + { OP_MADE, true, "MADE" }, + { OP_JOIN, false, "JOIN" }, + { OP_MAKE, false, "MAKE" }, + { OP_SILENT, false, "SILENT" }, + { OP_PRECIOUS, false, "PRECIOUS" }, + { OP_IGNORE, false, "IGNORE" }, + { OP_EXEC, false, "EXEC" }, + { OP_USE, false, "USE" }, + { OP_OPTIONAL, false, "OPTIONAL" }, + }; + size_t i; - type &= ~OP_OPMASK; - - while (type != 0) { - tbit = 1 << (ffs(type) - 1); - type &= ~tbit; - - switch (tbit) { -#define PRINTBIT(bit, attr) case bit: debug_printf(" " attr); break -#define PRINTDBIT(bit, attr) case bit: DEBUG0(TARG, " " attr); break - PRINTBIT(OP_OPTIONAL, ".OPTIONAL"); - PRINTBIT(OP_USE, ".USE"); - PRINTBIT(OP_EXEC, ".EXEC"); - PRINTBIT(OP_IGNORE, ".IGNORE"); - PRINTBIT(OP_PRECIOUS, ".PRECIOUS"); - PRINTBIT(OP_SILENT, ".SILENT"); - PRINTBIT(OP_MAKE, ".MAKE"); - PRINTBIT(OP_JOIN, ".JOIN"); - PRINTBIT(OP_INVISIBLE, ".INVISIBLE"); - PRINTBIT(OP_NOTMAIN, ".NOTMAIN"); - PRINTDBIT(OP_LIB, ".LIB"); - PRINTDBIT(OP_MEMBER, ".MEMBER"); - PRINTDBIT(OP_ARCHV, ".ARCHV"); - PRINTDBIT(OP_MADE, ".MADE"); - PRINTDBIT(OP_PHONY, ".PHONY"); -#undef PRINTBIT -#undef PRINTDBIT + for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) { + if (type & names[i].bit) { + if (names[i].internal) + DEBUG1(TARG, " .%s", names[i].name); + else + debug_printf(" .%s", names[i].name); } } } @@ -480,13 +481,27 @@ GNode_OpName(const GNode *gn) return ""; } +static bool +GNodeFlags_IsNone(GNodeFlags flags) +{ + return !flags.remake + && !flags.childMade + && !flags.force + && !flags.doneWait + && !flags.doneOrder + && !flags.fromDepend + && !flags.doneAllsrc + && !flags.cycle + && !flags.doneCycle; +} + /* Print the contents of a node. */ void Targ_PrintNode(GNode *gn, int pass) { debug_printf("# %s%s", gn->name, gn->cohort_num); GNode_FprintDetails(opts.debug_file, ", ", gn, "\n"); - if (gn->flags == 0) + if (GNodeFlags_IsNone(gn->flags)) return; if (!GNode_IsTarget(gn)) diff --git a/contrib/bmake/trace.c b/contrib/bmake/trace.c index 840c7995e5c1..8e2c507d14dc 100644 --- a/contrib/bmake/trace.c +++ b/contrib/bmake/trace.c @@ -1,4 +1,4 @@ -/* $NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $ */ +/* $NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $ */ /* * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -48,13 +48,13 @@ #include "job.h" #include "trace.h" -MAKE_RCSID("$NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $"); +MAKE_RCSID("$NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $"); static FILE *trfile; static pid_t trpid; const char *trwd; -static const char *evname[] = { +static const char evname[][4] = { "BEG", "END", "ERR", diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile index 784223a56652..98ed3907cd5a 100644 --- a/contrib/bmake/unit-tests/Makefile +++ b/contrib/bmake/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $ +# $Id: Makefile,v 1.164 2021/12/12 22:50:00 sjg Exp $ # -# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $ +# $NetBSD: Makefile,v 1.288 2021/12/12 22:16:48 rillig Exp $ # # Unit tests for make(1) # @@ -167,6 +167,7 @@ TESTS+= directive-for TESTS+= directive-for-errors TESTS+= directive-for-escape TESTS+= directive-for-generating-endif +TESTS+= directive-for-if TESTS+= directive-for-lines TESTS+= directive-for-null TESTS+= directive-hyphen-include @@ -351,13 +352,16 @@ TESTS+= varmod-indirect TESTS+= varmod-l-name-to-value TESTS+= varmod-localtime TESTS+= varmod-loop +TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape TESTS+= varmod-no-match TESTS+= varmod-order +TESTS+= varmod-order-numeric TESTS+= varmod-order-reverse TESTS+= varmod-order-shuffle +TESTS+= varmod-order-string TESTS+= varmod-path TESTS+= varmod-quote TESTS+= varmod-quote-dollar @@ -415,6 +419,7 @@ TESTS+= varname-dot-parsedir TESTS+= varname-dot-parsefile TESTS+= varname-dot-path TESTS+= varname-dot-shell +TESTS+= varname-dot-suffixes TESTS+= varname-dot-targets TESTS+= varname-empty TESTS+= varname-make @@ -430,12 +435,41 @@ TESTS+= varparse-mod TESTS+= varparse-undef-partial TESTS+= varquote +# for now at least +.if ${.SHELL:T} == "ksh" +BROKEN_TESTS+= sh-flags +.endif +.if ${.MAKE.OS:NDarwin} == "" +BROKEN_TESTS+= shell-ksh +.endif +.if ${.MAKE.OS} == "SCO_SV" +BROKEN_TESTS+= \ + opt-debug-graph[23] \ + varmod-localtime \ + varmod-to-separator \ + +.if ${.SHELL:T} == "bash" +BROKEN_TESTS+= job-output-null +.else +BROKEN_TESTS+= \ + cmd-interrupt \ + job-flags \ + +.endif +.endif + +# Some tests just do not work on some platforms or environments +# so allow for some filtering. +.if !empty(BROKEN_TESTS) +.warning Skipping broken tests: ${BROKEN_TESTS:O:u} +TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}} +.endif + # Ideas for more tests: # char-0020-space.mk # char-005C-backslash.mk # escape-cond-str.mk # escape-cond-func-arg.mk -# escape-cond-func-arg.mk # escape-varmod.mk # escape-varmod-define.mk # escape-varmod-match.mk @@ -457,7 +491,7 @@ ENV.envfirst= FROM_ENV=value-from-env ENV.varmisc= FROM_ENV=env ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_AFTER=env -ENV.varmod-localtime+= TZ=Europe/Berlin +ENV.varmod-localtime+= TZ=${UTC_1:UEurope/Berlin} ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2 # Override make flags for some of the tests; default is -k. @@ -490,7 +524,10 @@ SED_CMDS.job-output-long-lines= \ ${:D marker should always be at the beginning of the line. } \ -e '/^aa*--- job-b ---$$/d' \ -e '/^bb*--- job-a ---$$/d' -SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' +SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \ + -e '/name/s,file,File,' \ + -e 's,no such,No such,' \ + -e 's,Filename,File name,' SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1} SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2} SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3} @@ -511,11 +548,13 @@ SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,,' SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1,' SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj} SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output} +SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space} SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1} SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell} SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,' +SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space} SED_CMDS.vardebug+= -e 's,${.SHELL},,' SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex} SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: ",' @@ -523,9 +562,7 @@ SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: ",g' -SED_CMDS.varname-empty+= -e '/\.PARSEDIR/d' -SED_CMDS.varname-empty+= -e '/\.SHELL/d' +SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@} # Some tests need an additional round of postprocessing. POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' @@ -541,7 +578,7 @@ unexport-env.rawout: export.mk # Some standard sed commands, to be used in the SED_CMDS above. # Omit details such as process IDs from the output of the -dg1 option. -STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,,' +STD_SED_CMDS.dg1= -e '/\#.* \.$$/d' STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d' STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d' STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, ,' @@ -597,6 +634,8 @@ STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,' STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,' +STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,' + # The actual error messages for a failed regcomp or regexec differ between the # implementations. STD_SED_CMDS.regex= \ @@ -661,7 +700,8 @@ TMPDIR:= /tmp/uid${.MAKE.UID} x!= echo; mkdir -p ${TMPDIR} .endif -MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc +MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100 +MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510 MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" @@ -697,13 +737,22 @@ _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' _SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g' -# strip ${.CURDIR}/ from the output -_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' +# canonicalize ${.OBJDIR} and ${.CURDIR} +.if ${.OBJDIR} != ${.CURDIR} +# yes this is inaccurate but none of the tests expect anywhere +# which we get depending on how MAKEOBJDIR is set. +_SED_CMDS+= -e 's,${.OBJDIR},,g' +.endif +_SED_CMDS+= -e 's,${.CURDIR},,g' +_SED_CMDS+= -e 's,/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' -# on AT&T derrived systems; false exits 255 not 1 +# on AT&T derived systems: false exits 255 not 1 .if ${.MAKE.OS:N*BSD} != "" _SED_CMDS+= -e 's,\(Error code\) 255,\1 1,' .endif +.if ${.SHELL:T} == "ksh" +_SED_CMDS+= -e '/^set [+-]v/d' +.endif .rawout.out: @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \ diff --git a/contrib/bmake/unit-tests/Makefile.config.in b/contrib/bmake/unit-tests/Makefile.config.in index 0fe24f08d2f9..3139a0d4d0b5 100644 --- a/contrib/bmake/unit-tests/Makefile.config.in +++ b/contrib/bmake/unit-tests/Makefile.config.in @@ -1,4 +1,6 @@ -# $Id: Makefile.config.in,v 1.1 2018/12/30 17:14:24 sjg Exp $ +# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $ srcdir= @srcdir@ +TOOL_DIFF?= @diff@ DIFF_FLAGS?= @diff_u@ +UTC_1= @UTC_1@ diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp index 4a97b6879e7a..d10262aa8823 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp @@ -6,6 +6,10 @@ CondParser_Eval: !(${:UNaN} == NaN) lhs = "NaN", rhs = "NaN", op = == CondParser_Eval: 123 ! 123 make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) +CondParser_Eval: ${:U 123} < 124 +lhs = 123.000000, rhs = 124.000000, op = < +CondParser_Eval: ${:U123 } < 124 +make: "cond-cmp-numeric.mk" line 50: String comparison operator must be either == or != make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk index b1ec3e719d47..b34e5bfc0a06 100644 --- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk +++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-cmp-numeric.mk,v 1.4 2020/11/08 22:56:16 rillig Exp $ +# $NetBSD: cond-cmp-numeric.mk,v 1.5 2021/07/29 06:31:18 rillig Exp $ # # Tests for numeric comparisons in .if conditions. @@ -37,5 +37,21 @@ . error .endif +# Leading spaces are allowed for numbers. +# See EvalCompare and TryParseNumber. +.if ${:U 123} < 124 +.else +. error +.endif + +# Trailing spaces are NOT allowed for numbers. +# See EvalCompare and TryParseNumber. +# expect+1: String comparison operator must be either == or != +.if ${:U123 } < 124 +. error +.else +. error +.endif + all: @:; diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk index 9f3e731b2eb0..305a41099b98 100644 --- a/contrib/bmake/unit-tests/cond-cmp-string.mk +++ b/contrib/bmake/unit-tests/cond-cmp-string.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-cmp-string.mk,v 1.14 2021/01/19 19:54:57 rillig Exp $ +# $NetBSD: cond-cmp-string.mk,v 1.15 2021/12/11 09:53:53 rillig Exp $ # # Tests for string comparisons in .if conditions. @@ -26,7 +26,7 @@ # starting point for variable expressions. Applying the :U modifier to such # an undefined expression turns it into a defined expression. # -# See ApplyModifier_Defined and VEF_DEF. +# See ApplyModifier_Defined and DEF_DEFINED. .if ${:Ustr} != "str" . error .endif diff --git a/contrib/bmake/unit-tests/cond-eof.exp b/contrib/bmake/unit-tests/cond-eof.exp index 3b1e6eb1f056..3016a9b27805 100644 --- a/contrib/bmake/unit-tests/cond-eof.exp +++ b/contrib/bmake/unit-tests/cond-eof.exp @@ -1,8 +1,5 @@ -side effect make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2}) -side effect make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2}) -side effect make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/contrib/bmake/unit-tests/cond-eof.mk b/contrib/bmake/unit-tests/cond-eof.mk index 08f432bc4593..ddf4a4cd20c8 100644 --- a/contrib/bmake/unit-tests/cond-eof.mk +++ b/contrib/bmake/unit-tests/cond-eof.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $ +# $NetBSD: cond-eof.mk,v 1.3 2021/12/10 23:12:44 rillig Exp $ # # Tests for parsing conditions, especially the end of such conditions, which # are represented as the token TOK_EOF. @@ -7,11 +7,11 @@ SIDE_EFFECT= ${:!echo 'side effect' 1>&2!} SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!} # In the following conditions, ${SIDE_EFFECT} is the position of the first -# parse error. It is always fully evaluated, even if it were not necessary -# to expand the variable expression. This is because these syntax errors are -# an edge case that does not occur during normal operation, therefore there -# is no need to optimize for this case, and it would slow down the common -# case as well. +# parse error. Before cond.c 1.286 from 2021-12-10, it was always fully +# evaluated, even if it was not necessary to expand the variable expression. +# These syntax errors are an edge case that does not occur during normal +# operation. Still, it is easy to avoid evaluating these expressions, just in +# case they have side effects. .if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2} .endif .if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2} diff --git a/contrib/bmake/unit-tests/cond-func-defined.mk b/contrib/bmake/unit-tests/cond-func-defined.mk index 2aa49ccbf147..43db548a572b 100644 --- a/contrib/bmake/unit-tests/cond-func-defined.mk +++ b/contrib/bmake/unit-tests/cond-func-defined.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-func-defined.mk,v 1.7 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cond-func-defined.mk,v 1.8 2021/12/12 08:55:28 rillig Exp $ # # Tests for the defined() function in .if conditions. @@ -29,7 +29,7 @@ ${:UA B}= variable name with spaces . error .endif -# Parse error: missing closing parenthesis; see ParseFuncArg. +# Parse error: missing closing parenthesis; see ParseWord. .if defined(DEF . error .else diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp index 77a4edd47f49..d1dfda7c03ee 100644 --- a/contrib/bmake/unit-tests/cond-func-empty.exp +++ b/contrib/bmake/unit-tests/cond-func-empty.exp @@ -1,5 +1,5 @@ -make: "cond-func-empty.mk" line 152: Unclosed variable "WORD" -make: "cond-func-empty.mk" line 152: Malformed conditional (empty(WORD) +make: "cond-func-empty.mk" line 149: Unclosed variable "WORD" +make: "cond-func-empty.mk" line 149: Malformed conditional (empty(WORD) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk index 11a990cbbce1..25c850d23d93 100644 --- a/contrib/bmake/unit-tests/cond-func-empty.mk +++ b/contrib/bmake/unit-tests/cond-func-empty.mk @@ -1,10 +1,10 @@ -# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: cond-func-empty.mk,v 1.16 2021/12/11 10:41:31 rillig Exp $ # # Tests for the empty() function in .if conditions, which tests a variable # expression for emptiness. # -# Note that the argument in the parentheses is indeed a variable name, -# optionally followed by variable modifiers. +# Note that the argument in the parentheses is a variable name, not a variable +# expression, optionally followed by variable modifiers. # .undef UNDEF @@ -25,14 +25,10 @@ WORD= word .endif # The :S modifier replaces the empty value with an actual word. The -# expression is now no longer empty, but it is still possible to see whether -# the expression was based on an undefined variable. The expression has the -# flag VEF_UNDEF. -# -# The expression does not have the flag VEF_DEF though, therefore it is still -# considered undefined. Yes, indeed, undefined but not empty. There are a -# few variable modifiers that turn an undefined expression into a defined -# expression, among them :U and :D, but not :S. +# expression is now no longer empty, but it is still based on an undefined +# variable (DEF_UNDEF). There are a few variable modifiers that turn an +# undefined expression into a defined expression, among them :U and :D, but +# not :S. # # XXX: This is hard to explain to someone who doesn't know these # implementation details. @@ -41,19 +37,19 @@ WORD= word . error .endif -# The :U modifier modifies expressions based on undefined variables -# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression -# as "being interesting enough to be further processed". +# The :U modifier changes the state of a previously undefined expression from +# DEF_UNDEF to DEF_DEFINED. This marks the expression as "being interesting +# enough to be further processed". # .if empty(UNDEF:S,^$,value,W:Ufallback) . error .endif # And now to the surprising part. Applying the following :S modifier to the -# undefined expression makes it non-empty, but the marker VEF_UNDEF is -# preserved nevertheless. The :U modifier that follows only looks at the -# VEF_UNDEF flag to decide whether the variable is defined or not. This kind -# of makes sense since the :U modifier tests the _variable_, not the +# undefined expression makes it non-empty, but the expression is still in +# state DEF_UNDEF. The :U modifier that follows only looks at the state +# DEF_UNDEF to decide whether the variable is defined or not. This kind of +# makes sense since the :U modifier tests the _variable_, not the # _expression_. # # But since the variable was undefined to begin with, the fallback value from @@ -78,12 +74,13 @@ WORD= word . error .endif -# The empty variable named "" gets a fallback value of " ", which counts as -# empty. +# The following example constructs an expression with the variable name "" +# and the value " ". This expression counts as empty since the value contains +# only whitespace. # # Contrary to the other functions in conditionals, the trailing space is not # stripped off, as can be seen in the -dv debug log. If the space had been -# stripped, it wouldn't make a difference in this case. +# stripped, it wouldn't make a difference in this case, but in other cases. # .if !empty(:U ) . error @@ -92,8 +89,8 @@ WORD= word # Now the variable named " " gets a non-empty value, which demonstrates that # neither leading nor trailing spaces are trimmed in the argument of the # function. If the spaces were trimmed, the variable name would be "" and -# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse -# without VARE_UNDEFERR, the value of the undefined variable is +# that variable is indeed undefined. Since CondParser_FuncCallEmpty calls +# Var_Parse without VARE_UNDEFERR, the value of the undefined variable is # returned as an empty string. ${:U }= space .if empty( ) @@ -129,8 +126,8 @@ ${:U }= space # # If everything goes well, the argument expands to "WORD", and that variable # is defined at the beginning of this file. The surrounding 'W' and 'D' -# ensure that the parser in ParseEmptyArg has the correct position, both -# before and after the call to Var_Parse. +# ensure that CondParser_FuncCallEmpty keeps track of the parsing position, +# both before and after the call to Var_Parse. .if empty(W${:UOR}D) . error .endif @@ -155,17 +152,29 @@ ${:U WORD }= variable name with spaces . error .endif -# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated -# a wrong error message "Variable VARNAME is recursive". +# Since cond.c 1.76 from 2020-06-28 and before var.c 1.226 from 2020-07-02, +# the following example generated a wrong error message "Variable VARNAME is +# recursive". # -# The bug was that the !empty() condition was evaluated, even though this was -# not necessary since the defined() condition already evaluated to false. +# Since at least 1993, the manual page claimed that irrelevant parts of +# conditions were not evaluated, but that was wrong for a long time. The +# expressions in irrelevant parts of the condition were actually evaluated, +# they just allowed undefined variables to be used in the conditions, and the +# result of evaluating them was not used further. These unnecessary +# evaluations were fixed in several commits, starting with var.c 1.226 from +# 2020-07-02. +# +# In this example, the variable "VARNAME2" is not defined, so evaluation of +# the condition should have stopped at this point, and the rest of the +# condition should have been processed in parse-only mode. The right-hand +# side containing the '!empty' was evaluated though, as it had always been. # # When evaluating the !empty condition, the variable name was parsed as # "VARNAME${:U2}", but without expanding any nested variable expression, in -# this case the ${:U2}. Therefore, the variable name came out as simply -# "VARNAME". Since this variable name should have been discarded quickly after -# parsing it, this unrealistic variable name should have done no harm. +# this case the ${:U2}. The expression '${:U2}' was replaced with an empty +# string, the resulting variable name was thus "VARNAME". This conceptually +# wrong variable name should have been discarded quickly after parsing it, to +# prevent it from doing any harm. # # The variable expression was expanded though, and this was wrong. The # expansion was done without VARE_WANTRES (called VARF_WANTRES back diff --git a/contrib/bmake/unit-tests/cond-func.exp b/contrib/bmake/unit-tests/cond-func.exp index 855b9e5210fd..8dc0f821a255 100644 --- a/contrib/bmake/unit-tests/cond-func.exp +++ b/contrib/bmake/unit-tests/cond-func.exp @@ -6,7 +6,7 @@ make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 119: Symbols may start with a function name. make: "cond-func.mk" line 124: Symbols may start with a function name. -make: "cond-func.mk" line 130: Malformed conditional (defined() +make: "cond-func.mk" line 130: Missing closing parenthesis for defined() make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-op-and.exp b/contrib/bmake/unit-tests/cond-op-and.exp index 173b6861a98b..cd6b03a8359e 100644 --- a/contrib/bmake/unit-tests/cond-op-and.exp +++ b/contrib/bmake/unit-tests/cond-op-and.exp @@ -1,4 +1,7 @@ -make: "cond-op-and.mk" line 43: Malformed conditional (0 &&& 0) +make: "cond-op-and.mk" line 36: Malformed conditional (0 || (${DEF} && ${UNDEF})) +make: "cond-op-and.mk" line 40: Malformed conditional (0 || (${UNDEF} && ${UNDEF})) +make: "cond-op-and.mk" line 42: Malformed conditional (0 || (!${UNDEF} && ${UNDEF})) +make: "cond-op-and.mk" line 71: Malformed conditional (0 &&& 0) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-op-and.mk b/contrib/bmake/unit-tests/cond-op-and.mk index 83c694f15723..83386ed77de4 100644 --- a/contrib/bmake/unit-tests/cond-op-and.mk +++ b/contrib/bmake/unit-tests/cond-op-and.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-and.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: cond-op-and.mk,v 1.6 2021/12/10 19:14:35 rillig Exp $ # # Tests for the && operator in .if conditions. @@ -18,11 +18,39 @@ . error .endif + # The right-hand side is not evaluated since the left-hand side is already # false. .if 0 && ${UNDEF} .endif +# When an outer condition makes the inner '&&' condition irrelevant, neither +# of its operands must be evaluated. +# +.if 1 || (${UNDEF} && ${UNDEF}) +.endif + +# Test combinations of outer '||' with inner '&&', to ensure that the operands +# of the inner '&&' are only evaluated if necessary. +DEF= defined +.if 0 || (${DEF} && ${UNDEF}) +.endif +.if 0 || (!${DEF} && ${UNDEF}) +.endif +.if 0 || (${UNDEF} && ${UNDEF}) +.endif +.if 0 || (!${UNDEF} && ${UNDEF}) +.endif +.if 1 || (${DEF} && ${UNDEF}) +.endif +.if 1 || (!${DEF} && ${UNDEF}) +.endif +.if 1 || (${UNDEF} && ${UNDEF}) +.endif +.if 1 || (!${UNDEF} && ${UNDEF}) +.endif + + # The && operator may be abbreviated as &. This is not widely known though # and is also not documented in the manual page. diff --git a/contrib/bmake/unit-tests/cond-op-or.exp b/contrib/bmake/unit-tests/cond-op-or.exp index 7888a475e3e4..43b9a5438a31 100644 --- a/contrib/bmake/unit-tests/cond-op-or.exp +++ b/contrib/bmake/unit-tests/cond-op-or.exp @@ -1,4 +1,7 @@ -make: "cond-op-or.mk" line 43: Malformed conditional (0 ||| 0) +make: "cond-op-or.mk" line 46: Malformed conditional (1 && (!${DEF} || ${UNDEF})) +make: "cond-op-or.mk" line 48: Malformed conditional (1 && (${UNDEF} || ${UNDEF})) +make: "cond-op-or.mk" line 50: Malformed conditional (1 && (!${UNDEF} || ${UNDEF})) +make: "cond-op-or.mk" line 71: Malformed conditional (0 ||| 0) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-op-or.mk b/contrib/bmake/unit-tests/cond-op-or.mk index c6993e7c277e..0b7ac55e6c35 100644 --- a/contrib/bmake/unit-tests/cond-op-or.mk +++ b/contrib/bmake/unit-tests/cond-op-or.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-or.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: cond-op-or.mk,v 1.8 2021/12/10 19:14:35 rillig Exp $ # # Tests for the || operator in .if conditions. @@ -18,11 +18,39 @@ . error .endif + # The right-hand side is not evaluated since the left-hand side is already # true. .if 1 || ${UNDEF} .endif +# When an outer condition makes the inner '||' condition irrelevant, neither +# of its operands must be evaluated. This had been wrong in cond.c 1.283 from +# 2021-12-09 and was reverted in cond.c 1.284 an hour later. +.if 0 && (!defined(UNDEF) || ${UNDEF}) +.endif + +# Test combinations of outer '&&' with inner '||', to ensure that the operands +# of the inner '||' is only evaluated if necessary. +DEF= defined +.if 0 && (${DEF} || ${UNDEF}) +.endif +.if 0 && (!${DEF} || ${UNDEF}) +.endif +.if 0 && (${UNDEF} || ${UNDEF}) +.endif +.if 0 && (!${UNDEF} || ${UNDEF}) +.endif +.if 1 && (${DEF} || ${UNDEF}) +.endif +.if 1 && (!${DEF} || ${UNDEF}) +.endif +.if 1 && (${UNDEF} || ${UNDEF}) +.endif +.if 1 && (!${UNDEF} || ${UNDEF}) +.endif + + # The || operator may be abbreviated as |. This is not widely known though # and is also not documented in the manual page. diff --git a/contrib/bmake/unit-tests/cond-op.exp b/contrib/bmake/unit-tests/cond-op.exp index 28e8d48e2697..b8f6a4301819 100644 --- a/contrib/bmake/unit-tests/cond-op.exp +++ b/contrib/bmake/unit-tests/cond-op.exp @@ -1,20 +1,22 @@ make: "cond-op.mk" line 50: Malformed conditional ("!word" == !word) -make: "cond-op.mk" line 75: Malformed conditional (0 ${ERR::=evaluated}) -make: "cond-op.mk" line 79: After detecting a parse error, the rest is evaluated. -make: "cond-op.mk" line 83: Parsing continues until here. -make: "cond-op.mk" line 86: A B C => (A || B) && C A || B && C A || (B && C) -make: "cond-op.mk" line 93: 0 0 0 => 0 0 0 -make: "cond-op.mk" line 93: 0 0 1 => 0 0 0 -make: "cond-op.mk" line 93: 0 1 0 => 0 0 0 -make: "cond-op.mk" line 93: 0 1 1 => 1 1 1 -make: "cond-op.mk" line 93: 1 0 0 => 0 1 1 -make: "cond-op.mk" line 93: 1 0 1 => 1 1 1 -make: "cond-op.mk" line 93: 1 1 0 => 0 1 1 -make: "cond-op.mk" line 93: 1 1 1 => 1 1 1 -make: "cond-op.mk" line 104: Malformed conditional (1 &&) -make: "cond-op.mk" line 112: Malformed conditional (0 &&) -make: "cond-op.mk" line 120: Malformed conditional (1 ||) -make: "cond-op.mk" line 129: Malformed conditional (0 ||) +make: "cond-op.mk" line 76: Malformed conditional (0 ${ERR::=evaluated}) +make: "cond-op.mk" line 80: A misplaced expression after 0 is not evaluated. +make: "cond-op.mk" line 84: Malformed conditional (1 ${ERR::=evaluated}) +make: "cond-op.mk" line 88: A misplaced expression after 1 is not evaluated. +make: "cond-op.mk" line 92: Parsing continues until here. +make: "cond-op.mk" line 95: A B C => (A || B) && C A || B && C A || (B && C) +make: "cond-op.mk" line 102: 0 0 0 => 0 0 0 +make: "cond-op.mk" line 102: 0 0 1 => 0 0 0 +make: "cond-op.mk" line 102: 0 1 0 => 0 0 0 +make: "cond-op.mk" line 102: 0 1 1 => 1 1 1 +make: "cond-op.mk" line 102: 1 0 0 => 0 1 1 +make: "cond-op.mk" line 102: 1 0 1 => 1 1 1 +make: "cond-op.mk" line 102: 1 1 0 => 0 1 1 +make: "cond-op.mk" line 102: 1 1 1 => 1 1 1 +make: "cond-op.mk" line 113: Malformed conditional (1 &&) +make: "cond-op.mk" line 121: Malformed conditional (0 &&) +make: "cond-op.mk" line 129: Malformed conditional (1 ||) +make: "cond-op.mk" line 138: Malformed conditional (0 ||) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-op.mk b/contrib/bmake/unit-tests/cond-op.mk index 2ed451c90391..c3ab09f7709a 100644 --- a/contrib/bmake/unit-tests/cond-op.mk +++ b/contrib/bmake/unit-tests/cond-op.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 rillig Exp $ +# $NetBSD: cond-op.mk,v 1.15 2021/12/10 23:12:44 rillig Exp $ # # Tests for operators like &&, ||, ! in .if conditions. # @@ -58,25 +58,34 @@ . error .endif -# As soon as the parser sees the '$', it knows that the condition will -# be malformed. Therefore there is no point in evaluating it. +# In the following malformed conditions, as soon as the parser sees the '$' +# after the '0' or the '1', it knows that the condition will be malformed. +# Therefore there is no point in evaluating the misplaced expression. # -# As of 2021-01-20, that part of the condition is evaluated nevertheless, -# since CondParser_Or just requests the next token, without restricting -# the token to the expected tokens. If the parser were to restrict the -# valid follow tokens for the token "0" to those that can actually produce -# a correct condition (which in this case would be comparison operators, -# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have -# to be evaluated. +# Before cond.c 1.286 from 2021-12-10, the extra expression was evaluated +# nevertheless, since CondParser_Or and CondParser_And asked for the expanded +# next token, even though in this position of the condition, only comparison +# operators, TOK_AND, TOK_OR or TOK_RPAREN are allowed. # -# This would add a good deal of complexity to the code though, for almost -# no benefit, especially since most expressions and conditions are side -# effect free. +# +# +# +# +# +.undef ERR .if 0 ${ERR::=evaluated} . error .endif -.if ${ERR:Uundefined} == evaluated -. info After detecting a parse error, the rest is evaluated. +.if ${ERR:Uundefined} == undefined +. info A misplaced expression after 0 is not evaluated. +.endif + +.undef ERR +.if 1 ${ERR::=evaluated} +. error +.endif +.if ${ERR:Uundefined} == undefined +. info A misplaced expression after 1 is not evaluated. .endif # Just in case that parsing should ever stop on the first error. diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk index 113c3fd08fed..d41c38488fd6 100644 --- a/contrib/bmake/unit-tests/cond-short.mk +++ b/contrib/bmake/unit-tests/cond-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $ +# $NetBSD: cond-short.mk,v 1.18 2021/12/12 09:49:09 rillig Exp $ # # Demonstrates that in conditions, the right-hand side of an && or || # is only evaluated if it can actually influence the result. @@ -12,7 +12,20 @@ # possible to skip evaluation of irrelevant variable expressions and only # parse them. They were still evaluated though, the only difference to # relevant variable expressions was that in the irrelevant variable -# expressions, undefined variables were allowed. +# expressions, undefined variables were allowed. This allowed for conditions +# like 'defined(VAR) && ${VAR:S,from,to,} != ""', which no longer produced an +# error message 'Malformed conditional', but it still evaluated the +# expression, even though the expression was irrelevant. +# +# Since the initial commit on 1993-03-21, the manual page has been saying that +# make 'will only evaluate a conditional as far as is necessary to determine', +# but that was wrong. The code in cond.c 1.1 from 1993-03-21 looks good since +# it calls Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree), but the +# definition of Var_Parse does not call the third parameter 'doEval', as would +# be expected, but instead 'err', accompanied by the comment 'TRUE if +# undefined variables are an error'. This subtle difference between 'do not +# evaluate at all' and 'allow undefined variables' led to the unexpected +# evaluation. # # See also: # var-eval-short.mk, for short-circuited variable modifiers @@ -211,4 +224,56 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo . error .endif + +# Ensure that irrelevant conditions do not influence the result of the whole +# condition. As of cond.c 1.302 from 2021-12-11, an irrelevant function call +# evaluates to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an +# irrelevant comparison evaluates to false (see CondParser_Comparison). +# +# An irrelevant true bubbles up to the outermost CondParser_And, where it is +# ignored. An irrelevant false bubbles up to the outermost CondParser_Or, +# where it is ignored. +# +# If the condition parser should ever be restructured, the bubbling up of the +# irrelevant evaluation results might show up accidentally. Prevent this. +DEF= defined +.undef UNDEF + +.if 0 && defined(DEF) +. error +.endif + +.if 1 && defined(DEF) +.else +. error +.endif + +.if 0 && defined(UNDEF) +. error +.endif + +.if 1 && defined(UNDEF) +. error +.endif + +.if 0 || defined(DEF) +.else +. error +.endif + +.if 1 || defined(DEF) +.else +. error +.endif + +.if 0 || defined(UNDEF) +. error +.endif + +.if 1 || defined(UNDEF) +.else +. error +.endif + + all: diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp index 24cfa6bcbc82..8afde2d41788 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.exp +++ b/contrib/bmake/unit-tests/cond-token-plain.exp @@ -27,28 +27,35 @@ lhs = "var&&name", rhs = "var&&name", op = != CondParser_Eval: ${:Uvar}||name != "var||name" lhs = "var||name", rhs = "var||name", op = != CondParser_Eval: bare -make: "cond-token-plain.mk" line 102: A bare word is treated like defined(...), and the variable 'bare' is not defined. +make: "cond-token-plain.mk" line 106: A bare word is treated like defined(...), and the variable 'bare' is not defined. CondParser_Eval: VAR -make: "cond-token-plain.mk" line 107: A bare word is treated like defined(...). +make: "cond-token-plain.mk" line 111: A bare word is treated like defined(...). CondParser_Eval: V${:UA}R -make: "cond-token-plain.mk" line 114: ok +make: "cond-token-plain.mk" line 118: ok CondParser_Eval: V${UNDEF}AR -make: "cond-token-plain.mk" line 122: Undefined variables in bare words expand to an empty string. +make: "cond-token-plain.mk" line 126: Undefined variables in bare words expand to an empty string. CondParser_Eval: 0${:Ux00} -make: "cond-token-plain.mk" line 130: Numbers can be composed from literals and variable expressions. -CondParser_Eval: 0${:Ux01} make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions. +CondParser_Eval: 0${:Ux01} +make: "cond-token-plain.mk" line 138: Numbers can be composed from literals and variable expressions. CondParser_Eval: "" == -make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '==' +make: "cond-token-plain.mk" line 144: Missing right-hand side of operator '==' CondParser_Eval: == "" -make: "cond-token-plain.mk" line 148: Malformed conditional (== "") +make: "cond-token-plain.mk" line 152: Malformed conditional (== "") CondParser_Eval: \\ -make: "cond-token-plain.mk" line 163: The variable '\\' is not defined. +make: "cond-token-plain.mk" line 167: The variable '\\' is not defined. CondParser_Eval: \\ -make: "cond-token-plain.mk" line 168: Now the variable '\\' is defined. +make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined. CondParser_Eval: "unquoted\"quoted" != unquoted"quoted lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = != CondParser_Eval: $$$$$$$$ != "" +CondParser_Eval: left == right +make: "cond-token-plain.mk" line 195: Malformed conditional (left == right) +CondParser_Eval: ${0:?:} || left == right +CondParser_Eval: 0 +make: "cond-token-plain.mk" line 201: Malformed conditional (${0:?:} || left == right) +CondParser_Eval: left == right || ${0:?:} +make: "cond-token-plain.mk" line 206: Malformed conditional (left == right || ${0:?:}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk index 89f2247e077c..3e59f48bc3c7 100644 --- a/contrib/bmake/unit-tests/cond-token-plain.mk +++ b/contrib/bmake/unit-tests/cond-token-plain.mk @@ -1,17 +1,18 @@ -# $NetBSD: cond-token-plain.mk,v 1.10 2021/01/21 14:08:09 rillig Exp $ +# $NetBSD: cond-token-plain.mk,v 1.14 2021/12/12 09:36:00 rillig Exp $ # # Tests for plain tokens (that is, string literals without quotes) -# in .if conditions. +# in .if conditions. These are also called bare words. .MAKEFLAGS: -dc +# The word 'value' after the '!=' is a bare word. .if ${:Uvalue} != value . error .endif -# Malformed condition since comment parsing is done in an early phase -# and removes the '#' and everything behind it long before the condition -# parser gets to see it. +# Using a '#' in a string literal in a condition leads to a malformed +# condition since comment parsing is done in an early phase and removes the +# '#' and everything after it long before the condition parser gets to see it. # # XXX: The error message is missing for this malformed condition. # The right-hand side of the comparison is just a '"', before unescaping. @@ -32,7 +33,10 @@ # in a very early parsing phase. # # See https://gnats.netbsd.org/19596 for example makefiles demonstrating the -# original problems. This workaround is probably not needed anymore. +# original problems. At that time, the parser didn't recognize the comment in +# the line '.else # comment3'. This workaround is not needed anymore since +# comments are stripped in an earlier phase. See "case '#'" in +# CondParser_Token. # # XXX: Missing error message for the malformed condition. The right-hand # side before unescaping is double-quotes, backslash, backslash. @@ -152,7 +156,7 @@ VAR= defined .endif # The '\\' is not a line continuation. Neither is it an unquoted string -# literal. Instead, it is parsed as a function argument (ParseFuncArg), +# literal. Instead, it is parsed as a bare word (ParseWord), # and in that context, the backslash is just an ordinary character. The # function argument thus stays '\\' (2 backslashes). This string is passed # to FuncDefined, and since there is no variable named '\\', the condition @@ -185,6 +189,23 @@ ${:U\\\\}= backslash . error .endif +# In a condition in an .if directive, the left-hand side must not be an +# unquoted string literal. +# expect+1: Malformed conditional (left == right) +.if left == right +.endif +# Before cond.c 1.276 from 2021-09-21, a variable expression containing the +# modifier ':?:' allowed unquoted string literals for the rest of the +# condition. This was an unintended implementation mistake. +# expect+1: Malformed conditional (${0:?:} || left == right) +.if ${0:?:} || left == right +.endif +# This affected only the comparisons after the expression, so the following +# was still a syntax error. +# expect+1: Malformed conditional (left == right || ${0:?:}) +.if left == right || ${0:?:} +.endif + # See cond-token-string.mk for similar tests where the condition is enclosed # in "quotes". diff --git a/contrib/bmake/unit-tests/deptgt-default.exp b/contrib/bmake/unit-tests/deptgt-default.exp index 39a9383953dd..09fca899f063 100644 --- a/contrib/bmake/unit-tests/deptgt-default.exp +++ b/contrib/bmake/unit-tests/deptgt-default.exp @@ -1 +1,2 @@ +Default command is making 'not-a-target' from 'not-a-target'. exit status 0 diff --git a/contrib/bmake/unit-tests/deptgt-default.mk b/contrib/bmake/unit-tests/deptgt-default.mk index 814eaf72aed3..bf5f16536561 100644 --- a/contrib/bmake/unit-tests/deptgt-default.mk +++ b/contrib/bmake/unit-tests/deptgt-default.mk @@ -1,8 +1,17 @@ -# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: deptgt-default.mk,v 1.3 2021/12/01 23:56:29 rillig Exp $ # -# Tests for the special target .DEFAULT in dependency declarations. +# Tests for the special target .DEFAULT in dependency declarations, which +# attaches its associated commands to all targets that don't specify any way +# to create them. -# TODO: Implementation +all: test-default not-a-target + +test-default: .PHONY + +has-commands: .PHONY + @echo 'Making ${.TARGET} from ${.IMPSRC}.' + +.DEFAULT: dependency-is-ignored + @echo "Default command is making '${.TARGET}' from '${.IMPSRC}'." all: - @:; diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.mk b/contrib/bmake/unit-tests/deptgt-makeflags.mk index 0a0f410e14c4..26f3f5794354 100644 --- a/contrib/bmake/unit-tests/deptgt-makeflags.mk +++ b/contrib/bmake/unit-tests/deptgt-makeflags.mk @@ -1,4 +1,4 @@ -# $NetBSD: deptgt-makeflags.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $ +# $NetBSD: deptgt-makeflags.mk,v 1.7 2021/11/29 00:17:10 rillig Exp $ # # Tests for the special target .MAKEFLAGS in dependency declarations, # which adds command line options later, at parse time. @@ -65,7 +65,7 @@ .endif # Next try at defining another newline variable. Since whitespace around the -# variable value is trimmed, two empty variable expressions surround the +# variable value is trimmed, two empty variable expressions ${:U} surround the # literal newline now. This prevents the newline from being skipped during # parsing. The ':=' assignment operator expands the empty variable # expressions, leaving only the newline as the variable value. @@ -81,6 +81,31 @@ .endif #.MAKEFLAGS: -d0 +# Now do the same for the other escape sequences; see Substring_Words. +.MAKEFLAGS: CHAR_BS:="$${:U}\b$${:U}" +.MAKEFLAGS: CHAR_FF:="$${:U}\f$${:U}" +.MAKEFLAGS: CHAR_NL:="$${:U}\n$${:U}" +.MAKEFLAGS: CHAR_CR:="$${:U}\r$${:U}" +.MAKEFLAGS: CHAR_TAB:="$${:U}\t$${:U}" + +# Note: backspace is not whitespace, it is a control character. +.if ${CHAR_BS:C,^[[:cntrl:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_FF:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_NL:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_CR:C,^[[:space:]]$,found,W} != "found" +. error +.endif +.if ${CHAR_TAB:C,^[[:space:]]$,found,W} != "found" +. error +.endif + + # Unbalanced quotes produce an error message. If they occur anywhere in the # command line, the whole command line is skipped. .MAKEFLAGS: VAR=previous diff --git a/contrib/bmake/unit-tests/directive-else.exp b/contrib/bmake/unit-tests/directive-else.exp index 138e893ffa88..17d5571ba74b 100644 --- a/contrib/bmake/unit-tests/directive-else.exp +++ b/contrib/bmake/unit-tests/directive-else.exp @@ -1,11 +1,11 @@ -make: "directive-else.mk" line 14: The .else directive does not take arguments. +make: "directive-else.mk" line 14: The .else directive does not take arguments make: "directive-else.mk" line 15: ok make: "directive-else.mk" line 19: ok -make: "directive-else.mk" line 21: The .else directive does not take arguments. +make: "directive-else.mk" line 21: The .else directive does not take arguments make: "directive-else.mk" line 26: if-less else make: "directive-else.mk" line 32: ok make: "directive-else.mk" line 33: warning: extra else -make: "directive-else.mk" line 45: The .else directive does not take arguments. +make: "directive-else.mk" line 45: The .else directive does not take arguments make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/directive-endif.exp b/contrib/bmake/unit-tests/directive-endif.exp index 286d85244eae..0de1ecf0bf25 100644 --- a/contrib/bmake/unit-tests/directive-endif.exp +++ b/contrib/bmake/unit-tests/directive-endif.exp @@ -1,7 +1,7 @@ -make: "directive-endif.mk" line 18: The .endif directive does not take arguments. -make: "directive-endif.mk" line 23: The .endif directive does not take arguments. -make: "directive-endif.mk" line 33: The .endif directive does not take arguments. -make: "directive-endif.mk" line 39: The .endif directive does not take arguments. +make: "directive-endif.mk" line 18: The .endif directive does not take arguments +make: "directive-endif.mk" line 23: The .endif directive does not take arguments +make: "directive-endif.mk" line 33: The .endif directive does not take arguments +make: "directive-endif.mk" line 39: The .endif directive does not take arguments make: "directive-endif.mk" line 45: Unknown directive "endifx" make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/contrib/bmake/unit-tests/directive-export-impl.exp b/contrib/bmake/unit-tests/directive-export-impl.exp index 740daa605129..152fc0de4063 100644 --- a/contrib/bmake/unit-tests/directive-export-impl.exp +++ b/contrib/bmake/unit-tests/directive-export-impl.exp @@ -7,7 +7,7 @@ Var_Parse: ${UT_VAR:N*} (eval-defined) Var_Parse: ${REF}> (eval-defined) Evaluating modifier ${UT_VAR:N...} on value "<>" Pattern for ':N' is "*" -ModifyWords: split "<>" into 1 words +ModifyWords: split "<>" into 1 word Result of ${UT_VAR:N*} is "" ParseDependency(: ) CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>" @@ -28,7 +28,7 @@ Var_Parse: ${UT_VAR:N*} (eval-defined) Var_Parse: ${REF}> (eval-defined) Evaluating modifier ${UT_VAR:N...} on value "<>" Pattern for ':N' is "*" -ModifyWords: split "<>" into 1 words +ModifyWords: split "<>" into 1 word Result of ${UT_VAR:N*} is "" ParseDependency(: ) ParseReadLine (54): 'REF= defined' diff --git a/contrib/bmake/unit-tests/directive-for-escape.exp b/contrib/bmake/unit-tests/directive-for-escape.exp index 59d4c2324f15..492f82e16d1f 100644 --- a/contrib/bmake/unit-tests/directive-for-escape.exp +++ b/contrib/bmake/unit-tests/directive-for-escape.exp @@ -11,45 +11,45 @@ make: "directive-for-escape.mk" line 29: !"\\ For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 41: $ +make: "directive-for-escape.mk" line 43: $ For: loop body: . info ${:U${V}} -make: "directive-for-escape.mk" line 41: value +make: "directive-for-escape.mk" line 43: value For: loop body: . info ${:U${V:=-with-modifier}} -make: "directive-for-escape.mk" line 41: value-with-modifier +make: "directive-for-escape.mk" line 43: value-with-modifier For: loop body: . info ${:U$(V)} -make: "directive-for-escape.mk" line 41: value +make: "directive-for-escape.mk" line 43: value For: loop body: . info ${:U$(V:=-with-modifier)} -make: "directive-for-escape.mk" line 41: value-with-modifier +make: "directive-for-escape.mk" line 43: value-with-modifier For: end for 1 For: loop body: . info ${:U\${UNDEF\:U\\$\\$} -make: "directive-for-escape.mk" line 55: ${UNDEF:U\$ +make: "directive-for-escape.mk" line 57: ${UNDEF:U\$ For: loop body: . info ${:U{{\}\}} -make: "directive-for-escape.mk" line 55: {{}} +make: "directive-for-escape.mk" line 57: {{}} For: loop body: . info ${:Uend\}} -make: "directive-for-escape.mk" line 55: end} +make: "directive-for-escape.mk" line 57: end} For: end for 1 For: loop body: . info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end} -make: "directive-for-escape.mk" line 67: beginend +make: "directive-for-escape.mk" line 69: beginend For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 75: $ +make: "directive-for-escape.mk" line 77: $ For: end for 1 For: loop body: . info ${NUMBERS} ${:Ureplaced} -make: "directive-for-escape.mk" line 83: one two three replaced +make: "directive-for-escape.mk" line 85: one two three replaced For: end for 1 For: loop body: . info ${:Ureplaced} -make: "directive-for-escape.mk" line 93: replaced +make: "directive-for-escape.mk" line 95: replaced For: end for 1 For: loop body: . info . $$i: ${:Uinner} @@ -62,14 +62,31 @@ For: loop body: . info . $${i2}: ${i2} . info . $${i,}: ${i,} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} -make: "directive-for-escape.mk" line 101: . $i: inner -make: "directive-for-escape.mk" line 102: . ${i}: inner -make: "directive-for-escape.mk" line 103: . ${i:M*}: inner -make: "directive-for-escape.mk" line 104: . $(i): inner -make: "directive-for-escape.mk" line 105: . $(i:M*): inner -make: "directive-for-escape.mk" line 106: . ${i${:U}}: outer -make: "directive-for-escape.mk" line 107: . ${i\}}: inner} -make: "directive-for-escape.mk" line 108: . ${i2}: two -make: "directive-for-escape.mk" line 109: . ${i,}: comma -make: "directive-for-escape.mk" line 110: . adjacent: innerinnerinnerinner -exit status 0 +make: "directive-for-escape.mk" line 103: . $i: inner +make: "directive-for-escape.mk" line 104: . ${i}: inner +make: "directive-for-escape.mk" line 105: . ${i:M*}: inner +make: "directive-for-escape.mk" line 106: . $(i): inner +make: "directive-for-escape.mk" line 107: . $(i:M*): inner +make: "directive-for-escape.mk" line 108: . ${i${:U}}: outer +make: "directive-for-escape.mk" line 109: . ${i\}}: inner} +make: "directive-for-escape.mk" line 110: . ${i2}: two +make: "directive-for-escape.mk" line 111: . ${i,}: comma +make: "directive-for-escape.mk" line 112: . adjacent: innerinnerinnerinner +For: end for 1 +For: loop body: +. info eight $$$$$$$$ and no cents. +. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents. +make: "directive-for-escape.mk" line 120: eight $$$$ and no cents. +make: "directive-for-escape.mk" line 121: eight dollardollardollardollar and no cents. +make: "directive-for-escape.mk" line 130: eight and no cents. +For: end for 1 +make: "directive-for-escape.mk" line 137: newline in .for value +make: "directive-for-escape.mk" line 137: newline in .for value +For: loop body: +. info short: ${:U" "} +. info long: ${:U" "} +make: "directive-for-escape.mk" line 138: short: " " +make: "directive-for-escape.mk" line 139: long: " " +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/directive-for-escape.mk b/contrib/bmake/unit-tests/directive-for-escape.mk index babc4b8c6e88..725fa85d68c3 100644 --- a/contrib/bmake/unit-tests/directive-for-escape.mk +++ b/contrib/bmake/unit-tests/directive-for-escape.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 rillig Exp $ +# $NetBSD: directive-for-escape.mk,v 1.12 2021/12/05 11:40:03 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 @@ -13,8 +13,8 @@ ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ # XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of -# the loop since it would not need only the escaping for the :U variable -# modifier but also the escaping for the line-end comment. +# 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 @@ -29,19 +29,21 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~ . info ${chars} .endfor -# Cover the code in for_var_len. +# Cover the code in ExprLen. # # XXX: It is unexpected that the variable V gets expanded in the loop body. -# The double '$$' should prevent exactly this. Probably nobody was -# adventurous enough to use literal dollar signs in the values of a .for -# loop. +# The double '$$' should intuitively prevent exactly this. Probably nobody +# was adventurous enough to use literal dollar signs in the values of a .for +# loop, allowing this edge case to go unnoticed for years. +# +# See for.c, function ExprLen. V= value VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier) .for i in ${VALUES} . info $i .endfor -# Try to cover the code for nested '{}' in for_var_len, without success. +# Try to cover the code for nested '{}' in ExprLen, without success. # # The value of the variable VALUES is not meant to be a variable expression. # Instead, it is meant to represent literal text, the only escaping mechanism @@ -55,9 +57,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end} . info $i .endfor -# Second try to cover the code for nested '{}' in for_var_len. +# Second try to cover the code for nested '{}' in ExprLen. # -# XXX: It is wrong that for_var_len requires the braces to be balanced. +# XXX: It is wrong that ExprLen requires the braces to be balanced. # Each variable modifier has its own inconsistent way of parsing nested # variable expressions, braces and parentheses. (Compare ':M', ':S', and # ':D' for details.) The only sensible thing to do is therefore to let @@ -110,4 +112,31 @@ i,= comma . info . adjacent: $i${i}${i:M*}$i .endfor +# 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'. +.for $ in dollar +. info eight $$$$$$$$ and no cents. +. info eight ${$}${$}${$}${$} and no cents. +.endfor +# Outside a .for loop, '${$}' is interpreted differently. The outer '$' starts +# a variable expression. The inner '$' is followed by a '}' and is thus a +# silent syntax error, the '$' is skipped. The variable name is thus '', and +# since since there is never a variable named '', the whole expression '${$}' +# evaluates to an empty string. +closing-brace= } # guard against an +${closing-brace}= # alternative interpretation +.info eight ${$}${$}${$}${$} and no cents. + +# What happens if the values from the .for loop contain a literal newline? +# Before for.c 1.144 from 2021-06-25, the newline was passed verbatim to the +# 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. +.for i in "${.newline}" +. info short: $i +. info long: ${i} +.endfor + all: diff --git a/contrib/bmake/unit-tests/directive-for-if.exp b/contrib/bmake/unit-tests/directive-for-if.exp new file mode 100644 index 000000000000..85bfc484856b --- /dev/null +++ b/contrib/bmake/unit-tests/directive-for-if.exp @@ -0,0 +1,8 @@ +make: "directive-for-if.mk" line 48: if-less endif +make: "directive-for-if.mk" line 48: if-less endif +make: "directive-for-if.mk" line 48: if-less endif +VAR1 +VAR3 +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/directive-for-if.mk b/contrib/bmake/unit-tests/directive-for-if.mk new file mode 100644 index 000000000000..8d73e8ae8c4d --- /dev/null +++ b/contrib/bmake/unit-tests/directive-for-if.mk @@ -0,0 +1,86 @@ +# $NetBSD: directive-for-if.mk,v 1.1 2021/08/30 17:08:13 rillig Exp $ +# +# Test for a .for directive that contains an .if directive. +# +# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for +# loop, their values were placed verbatim in the expanded body. Since then, +# each variable value expands to an expression of the form ${:Uvalue}. +# +# Before that change, the following adventurous code was possible: +# +# .for directive in if ifdef ifndef +# . ${directive} "1" != "0" +# . endif +# .endfor +# +# A more practical usage of the .for loop that often led to surprises was the +# following: +# +# .for var in VAR1 VAR2 VAR3 +# . if ${var} != "VAR2" +# . endif +# .endfor +# +# The .for loop body expanded to this string: +# +# . if VAR1 != "VAR2" +# . endif +# +# Since bare words were not allowed at the left-hand side of a condition, +# make complained about a "Malformed conditional", which was surprising since +# the code before expanding the .for loop body looked quite well. +# +# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for +# loops changed from plain textual value to using expressions of the form +# ${:Uvalue}, this surprising behavior was documented in the code, and a +# workaround was implemented that allowed bare words when they are followed +# by either '!' or '=', as part of the operators '!=' or '=='. +# +# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand +# side of a condition, but that applies only to expression of the form +# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if +# directives. + +# The following snippet worked in 2005, when the variables from the .for loop +# expanded to their bare textual value. +.for directive in if ifdef ifndef +. ${directive} "1" != "0" +. endif +.endfor +# In 2021, the above code does not generate an error message, even though the +# code looks clearly malformed. This is due to the '!', which is interpreted +# as a dependency operator, similar to ':' and '::'. The parser turns this +# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources +# '=' and '"0"'. Since that line is not interpreted as an '.if' directive, +# the error message 'if-less endif' makes sense. + +# In 2005, make complained: +# +# .if line: Malformed conditional (VAR1 != "VAR2") +# .endif line: if-less endif +# .endif line: Need an operator +# +# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}. +.for var in VAR1 VAR2 VAR3 +. if ${var} != "VAR2" +_!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet. +. endif +.endfor + +# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the +# variable expression from the .for loop with '"'. Such a string literal +# has been allowed since cond.c 1.23 from 2004-04-13. Between that commit and +# the one from 2008, the parser would still get confused if the value from the +# .for loop contained '"', which was effectively a code injection. +# +# Surrounding ${var} with quotes disabled the check for typos though. For +# ordinary variables, referring to an undefined variable on the left-hand side +# of the comparison resulted in a "Malformed conditional". Since the .for +# loop was usually close to the .if clause, this was not a problem in +# practice. +.for var in VAR1 VAR2 VAR3 +. if "${var}" != "VAR2" +. endif +.endfor + +all: diff --git a/contrib/bmake/unit-tests/directive-for-null.exp b/contrib/bmake/unit-tests/directive-for-null.exp index 37a7d68925ed..dee26de25e63 100644 --- a/contrib/bmake/unit-tests/directive-for-null.exp +++ b/contrib/bmake/unit-tests/directive-for-null.exp @@ -1,5 +1,5 @@ make: "(stdin)" line 2: Zero byte read from file -make: "(stdin)" line 2: Unexpected end of file in for loop. +make: "(stdin)" line 2: Unexpected end of file in .for loop make: "(stdin)" line 3: Zero byte read from file make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/contrib/bmake/unit-tests/directive-include.exp b/contrib/bmake/unit-tests/directive-include.exp index af56eefb2b88..b339f393e7e8 100755 --- a/contrib/bmake/unit-tests/directive-include.exp +++ b/contrib/bmake/unit-tests/directive-include.exp @@ -3,6 +3,9 @@ lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = != CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null" lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = != make: "directive-include.mk" line 25: Could not find nonexistent.mk +make: "directive-include.mk" line 47: Could not find " +make: "directive-include.mk" line 52: Unknown modifier "Z" +make: "directive-include.mk" line 52: Could not find nonexistent.mk make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/directive-include.mk b/contrib/bmake/unit-tests/directive-include.mk index d36914b25a63..a6b300b3d273 100755 --- a/contrib/bmake/unit-tests/directive-include.mk +++ b/contrib/bmake/unit-tests/directive-include.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-include.mk,v 1.5 2020/11/21 14:59:11 rillig Exp $ +# $NetBSD: directive-include.mk,v 1.7 2021/12/03 22:48:07 rillig Exp $ # # Tests for the .include directive, which includes another file. @@ -30,5 +30,25 @@ # As of 2020-11-21, anything after the delimiter '"' is ignored. .include "/dev/null" and ignore anything in the rest of the line. +# The filename to be included can contain expressions. +DEV= null +.include "/dev/${DEV}" + +# Expressions in double quotes or angle quotes are first parsed naively, to +# find the closing '"'. In a second step, the expressions are expanded. This +# means that the expressions cannot include the characters '"' or '>'. This +# restriction is not practically relevant since the expressions inside +# '.include' directives are typically kept as simple as possible. +# +# If the whole line were expanded before parsing, the filename to be included +# would be empty, and the closing '"' would be in the trailing part of the +# line, which is ignored as of 2021-12-03. +DQUOT= " +.include "${DQUOT}" + +# When the expression in a filename cannot be evaluated, the failing +# expression is skipped and the file is included nevertheless. +# FIXME: Add proper error handling, no file must be included here. +.include "nonexistent${:U123:Z}.mk" + all: - @:; diff --git a/contrib/bmake/unit-tests/export.mk b/contrib/bmake/unit-tests/export.mk index 94e3a862dce1..bab08ee3ea23 100644 --- a/contrib/bmake/unit-tests/export.mk +++ b/contrib/bmake/unit-tests/export.mk @@ -1,4 +1,4 @@ -# $NetBSD: export.mk,v 1.10 2020/10/24 08:50:17 rillig Exp $ +# $NetBSD: export.mk,v 1.11 2021/12/05 14:57:36 rillig Exp $ UT_TEST= export UT_FOO= foo${BAR} @@ -40,7 +40,7 @@ BAR= bar is ${UT_FU} .MAKE.EXPORTED+= UT_ZOO UT_TEST -FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_OPTIONS|PATH|PWD|SHLVL|_|&)=' +FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)=' all: @env | ${FILTER_CMD} | sort diff --git a/contrib/bmake/unit-tests/job-output-null.exp b/contrib/bmake/unit-tests/job-output-null.exp index af9b4e64dba3..631d4862af44 100644 --- a/contrib/bmake/unit-tests/job-output-null.exp +++ b/contrib/bmake/unit-tests/job-output-null.exp @@ -1,4 +1,4 @@ -hello -hello -hello world without newline, hello world without newline, hello world without newline. +1 +2a +3a without newline, 3b without newline. exit status 0 diff --git a/contrib/bmake/unit-tests/job-output-null.mk b/contrib/bmake/unit-tests/job-output-null.mk index 7620bdf6a7ba..1efd9c667980 100644 --- a/contrib/bmake/unit-tests/job-output-null.mk +++ b/contrib/bmake/unit-tests/job-output-null.mk @@ -1,4 +1,4 @@ -# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $ +# $NetBSD: job-output-null.mk,v 1.3 2021/09/12 10:26:49 rillig Exp $ # # Test how null bytes in the output of a command are handled. Make processes # them using null-terminated strings, which may cut off some of the output. @@ -7,20 +7,33 @@ # inconsistently. It's an edge case though since typically the child # processes output text. +# Note: The printf commands used in this test must only use a single format +# string, without parameters. This is because it is implementation-dependent +# how many times the command 'printf "fmt%s" "" "" ""' calls write(2). +# +# NetBSD /bin/sh 1 x write("fmtfmtfmt") +# Dash 1 x write("fmtfmtfmt") +# NetBSD /bin/ksh 3 x write("fmt") (via /bin/printf) +# Bash 5 3 x write("fmt") +# +# In the latter case the output may arrive in parts, which in this test makes +# a crucial difference since the outcome of the test depends on whether there +# is a '\n' in each of the blocks from the output. + .MAKEFLAGS: -j1 # force jobs mode all: .PHONY # The null byte from the command output is kept as-is. # See CollectOutput, which looks like it intended to replace these # null bytes with simple spaces. - @printf 'hello\0world%s\n' '' + @printf '1\0trailing\n' # Give the parent process a chance to see the above output, but not # yet the output from the next printf command. @sleep 1 # All null bytes from the command output are kept as-is. - @printf 'hello\0world%s\n' '' '' '' '' '' '' + @printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n' @sleep 1 @@ -29,4 +42,4 @@ all: .PHONY # # The three null bytes in a row test whether this output is # compressed to a single space like in DebugFailedTarget. It isn't. - @printf 'hello\0world\0without\0\0\0newline%s' ', ' ', ' '.' + @printf '3a\0without\0\0\0newline, 3b\0without\0\0\0newline.' diff --git a/contrib/bmake/unit-tests/lint.exp b/contrib/bmake/unit-tests/lint.exp index d7068b5e006a..db2290c040cd 100755 --- a/contrib/bmake/unit-tests/lint.exp +++ b/contrib/bmake/unit-tests/lint.exp @@ -1,4 +1,4 @@ -make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar. +make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar y@:Q} xvaluey exit status 2 diff --git a/contrib/bmake/unit-tests/objdir-writable.exp b/contrib/bmake/unit-tests/objdir-writable.exp index 9c507f647f8c..e7298a66d369 100644 --- a/contrib/bmake/unit-tests/objdir-writable.exp +++ b/contrib/bmake/unit-tests/objdir-writable.exp @@ -1,5 +1,5 @@ make warning: TMPDIR/roobj: Permission denied. -/tmp +TMPDIR TMPDIR/roobj TMPDIR/roobj exit status 0 diff --git a/contrib/bmake/unit-tests/objdir-writable.mk b/contrib/bmake/unit-tests/objdir-writable.mk index 9fc1c69afb56..b09baa3c32b2 100644 --- a/contrib/bmake/unit-tests/objdir-writable.mk +++ b/contrib/bmake/unit-tests/objdir-writable.mk @@ -1,8 +1,9 @@ -# $NetBSD: objdir-writable.mk,v 1.4 2020/11/14 07:36:00 sjg Exp $ +# $NetBSD: objdir-writable.mk,v 1.5 2021/07/04 01:28:54 sjg Exp $ # test checking for writable objdir -RO_OBJDIR?= ${TMPDIR:U/tmp}/roobj +TMPDIR?= /tmp +RO_OBJDIR?= ${TMPDIR}/roobj .if make(do-objdir) # this should succeed @@ -20,12 +21,12 @@ rm-objdir: @rmdir ${RO_OBJDIR} no-objdir: - @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR + @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR ro-objdir: - @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no + @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no explicit-objdir: - @MAKEOBJDIR=/tmp ${.MAKE} -r -f ${MAKEFILE:tA} -C /tmp do-objdir -V .OBJDIR + @MAKEOBJDIR=${TMPDIR} ${.MAKE} -r -f ${MAKEFILE:tA} -C ${TMPDIR} do-objdir -V .OBJDIR .endif diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp index 25eb2b470b72..c957c7736b32 100644 --- a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp +++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp @@ -24,6 +24,8 @@ line2 *** Failed target: fail-newline *** Failed commands: echo 'line1${.newline}line2'; false + => echo 'line1 +line2'; false *** [fail-newline] Error code 1 make: stopped in unit-tests @@ -44,5 +46,13 @@ word1 word2 echo 'word1' 'word2'; false *** [fail-multiline-intention] Error code 1 +make: stopped in unit-tests + +*** Failed target: fail-vars +*** Failed commands: + @${COMPILE_C} ${COMPILE_C_FLAGS} + => @false c-compiler flag1 -macro="several words" +*** [fail-vars] Error code 1 + make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk index 83b50987a752..007a5f37e08a 100644 --- a/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk +++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk @@ -1,4 +1,4 @@ -# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $ +# $NetBSD: opt-debug-errors-jobs.mk,v 1.2 2021/11/27 23:56:11 rillig Exp $ # # Tests for the -de command line option, which adds debug logging for # failed commands and targets; since 2021-04-27 also in jobs mode. @@ -10,6 +10,7 @@ all: fail-escaped-space all: fail-newline all: fail-multiline all: fail-multiline-intention +all: fail-vars fail-spaces: echo '3 spaces'; false @@ -34,3 +35,14 @@ fail-multiline: fail-multiline-intention: echo 'word1' \ 'word2'; false + +# In makefiles that rely heavily on abstracted variables, it is not possible +# to determine the actual command from the unexpanded command alone. To help +# debugging these issues (for example in NetBSD's build.sh), output the +# expanded command as well whenever it differs from the unexpanded command. +# Since 2021-11-28. +COMPILE_C= false c-compiler +COMPILE_C_DEFS= macro="several words" +COMPILE_C_FLAGS=flag1 ${COMPILE_C_DEFS:@def@-${def}@} +fail-vars: + @${COMPILE_C} ${COMPILE_C_FLAGS} diff --git a/contrib/bmake/unit-tests/opt-debug-graph1.exp b/contrib/bmake/unit-tests/opt-debug-graph1.exp index 4d4aa0c3faea..4049900fee75 100644 --- a/contrib/bmake/unit-tests/opt-debug-graph1.exp +++ b/contrib/bmake/unit-tests/opt-debug-graph1.exp @@ -46,7 +46,6 @@ MFLAGS = -r -k -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/contrib/bmake/unit-tests/opt-debug-graph2.exp b/contrib/bmake/unit-tests/opt-debug-graph2.exp index 03f02719618e..675e5e8cac18 100644 --- a/contrib/bmake/unit-tests/opt-debug-graph2.exp +++ b/contrib/bmake/unit-tests/opt-debug-graph2.exp @@ -81,7 +81,6 @@ MFLAGS = -r -k -d g2 # Stats: 0 hits 4 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/contrib/bmake/unit-tests/opt-debug-graph3.exp b/contrib/bmake/unit-tests/opt-debug-graph3.exp index f2966442eb26..78edb59e4e02 100644 --- a/contrib/bmake/unit-tests/opt-debug-graph3.exp +++ b/contrib/bmake/unit-tests/opt-debug-graph3.exp @@ -81,7 +81,6 @@ MFLAGS = -r -k -d g3 # Stats: 0 hits 4 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 -# 1 0 . #*** Suffixes: #*** Transformations: diff --git a/contrib/bmake/unit-tests/opt-file.mk b/contrib/bmake/unit-tests/opt-file.mk index b7a1c09e6d16..edeff4b9ab11 100644 --- a/contrib/bmake/unit-tests/opt-file.mk +++ b/contrib/bmake/unit-tests/opt-file.mk @@ -1,6 +1,7 @@ -# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $ +# $NetBSD: opt-file.mk,v 1.14 2021/12/09 20:47:33 rillig Exp $ # -# Tests for the -f command line option. +# Tests for the -f command line option, which adds a makefile to the list of +# files that are parsed. # TODO: Implementation @@ -10,7 +11,8 @@ all: file-ending-in-backslash-mmap all: line-with-trailing-whitespace all: file-containing-null-byte -# Passing '-' as the filename reads from stdin. This is unusual but possible. +# When the filename is '-', the input comes from stdin. This is unusual but +# possible. # # In the unlikely case where a file ends in a backslash instead of a newline, # that backslash is trimmed. See ParseGetLine. @@ -19,7 +21,9 @@ all: file-containing-null-byte # outside of the file buffer. # # printf '%s' 'VAR=value\' \ -# | MALLOC_OPTIONS=JA make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \ +# | MALLOC_OPTIONS="JA" \ +# MALLOC_CONF="junk:true" \ +# make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \ # | less # # The debug output shows how make happily uses freshly allocated memory (the diff --git a/contrib/bmake/unit-tests/opt-tracefile.exp b/contrib/bmake/unit-tests/opt-tracefile.exp index 39a9383953dd..0e815606d34f 100644 --- a/contrib/bmake/unit-tests/opt-tracefile.exp +++ b/contrib/bmake/unit-tests/opt-tracefile.exp @@ -1 +1,12 @@ +Making dependency1 from . +Making dependency2 from . +Making trace from dependency1 dependency2. +0 BEG +1 JOB +1 DON +1 JOB +1 DON +1 JOB +1 DON +0 END exit status 0 diff --git a/contrib/bmake/unit-tests/opt-tracefile.mk b/contrib/bmake/unit-tests/opt-tracefile.mk index b62392ca913c..291824680606 100644 --- a/contrib/bmake/unit-tests/opt-tracefile.mk +++ b/contrib/bmake/unit-tests/opt-tracefile.mk @@ -1,8 +1,16 @@ -# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: opt-tracefile.mk,v 1.5 2021/12/06 22:35:20 rillig Exp $ # -# Tests for the -T command line option. +# Tests for the command line option '-T', which in jobs mode appends a trace +# record to a trace log whenever a job is started or completed. -# TODO: Implementation +all: .PHONY + @rm -f opt-tracefile.log + @${MAKE} -f ${MAKEFILE} -j1 -Topt-tracefile.log trace + # Remove timestamps, process IDs and directory paths. + @awk '{ print $$2, $$3 }' opt-tracefile.log + @rm opt-tracefile.log -all: - @:; +trace dependency1 dependency2: .PHONY + @echo 'Making ${.TARGET} from ${.ALLSRC:S,^$,,W}.' + +trace: dependency1 dependency2 diff --git a/contrib/bmake/unit-tests/suff-main-several.exp b/contrib/bmake/unit-tests/suff-main-several.exp index 09fa6d63bffa..b20f5ecf1143 100644 --- a/contrib/bmake/unit-tests/suff-main-several.exp +++ b/contrib/bmake/unit-tests/suff-main-several.exp @@ -111,7 +111,6 @@ MFLAGS = -r -k -d mps -d 0 -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 -# 1 0 . #*** Suffixes: # ".4" (num 1, ref 1) diff --git a/contrib/bmake/unit-tests/suff-transform-debug.exp b/contrib/bmake/unit-tests/suff-transform-debug.exp index 0634ff616d0d..7fec51a1de9d 100644 --- a/contrib/bmake/unit-tests/suff-transform-debug.exp +++ b/contrib/bmake/unit-tests/suff-transform-debug.exp @@ -37,7 +37,6 @@ MFLAGS = -r -k -d g1 # Stats: 0 hits 2 misses 0 near misses 0 losers (0%) # refs hits directory # 1 0 -# 1 0 . #*** Suffixes: # ".a" (num 1, ref 2) diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp index ae0aff7d7c2c..c476c93125e3 100644 --- a/contrib/bmake/unit-tests/var-eval-short.exp +++ b/contrib/bmake/unit-tests/var-eval-short.exp @@ -1,16 +1,16 @@ -make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar. +make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar make: "var-eval-short.mk" line 41: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@}) -make: "var-eval-short.mk" line 79: Invalid time value: ${FAIL}} -make: "var-eval-short.mk" line 79: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}}) -make: "var-eval-short.mk" line 93: Invalid time value: ${FAIL}} -make: "var-eval-short.mk" line 93: Malformed conditional (0 && ${:Uword:localtime=${FAIL}}) +make: "var-eval-short.mk" line 81: Invalid time value at "${FAIL}}" +make: "var-eval-short.mk" line 81: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}}) +make: "var-eval-short.mk" line 95: Invalid time value at "${FAIL}}" +make: "var-eval-short.mk" line 95: Malformed conditional (0 && ${:Uword:localtime=${FAIL}}) CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else} Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only) Parsing modifier ${0:?...} Modifier part: "${FAIL}then" Modifier part: "${FAIL}else" Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined) -ParseReadLine (158): 'DEFINED= defined' +ParseReadLine (160): 'DEFINED= defined' Global: DEFINED = defined CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else} Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only) @@ -20,7 +20,7 @@ Parsing modifier ${DEFINED:?...} Modifier part: "${FAIL}then" Modifier part: "${FAIL}else" Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular) -ParseReadLine (161): '.MAKEFLAGS: -d0' +ParseReadLine (163): '.MAKEFLAGS: -d0' ParseDependency(.MAKEFLAGS: -d0) Global: .MAKEFLAGS = -r -k -d cpv -d Global: .MAKEFLAGS = -r -k -d cpv -d 0 diff --git a/contrib/bmake/unit-tests/var-eval-short.mk b/contrib/bmake/unit-tests/var-eval-short.mk index 41782f0d7823..1b9ccc714736 100644 --- a/contrib/bmake/unit-tests/var-eval-short.mk +++ b/contrib/bmake/unit-tests/var-eval-short.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $ +# $NetBSD: var-eval-short.mk,v 1.7 2021/09/07 20:41:58 rillig Exp $ # # Tests for each variable modifier to ensure that they only do the minimum # necessary computations. If the result of the expression is not needed, they @@ -44,13 +44,13 @@ FAIL= ${:!echo unexpected 1>&2!} .if 0 && ${:Uword:@var@${FAIL}@} .endif -# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand +# Before var.c 1.877 from 2021-03-14, the modifier ':[...]' did not expand # the nested expression ${FAIL} and then tried to parse the unexpanded text, # which failed since '$' is not a valid range character. .if 0 && ${:Uword:[${FAIL}]} .endif -# Before var.c,v 1.867 from 2021-03-14, the modifier ':_' defined the variable +# Before var.c 1.867 from 2021-03-14, the modifier ':_' defined the variable # even though the whole expression should have only been parsed, not # evaluated. .if 0 && ${:Uword:_=VAR} @@ -58,11 +58,13 @@ FAIL= ${:!echo unexpected 1>&2!} . error .endif -# Before var.c,v 1.856 from 2021-03-14, the modifier ':C' did not expand the -# nested expression ${FAIL} and then tried to compile the unexpanded text as a -# regular expression, which failed both because of the '{FAIL}', which is not -# a valid repetition, and because of the '****', which are repeated -# repetitions as well. +# Before var.c 1.856 from 2021-03-14, the modifier ':C' did not expand the +# nested expression ${FAIL}, which is correct, and then tried to compile the +# unexpanded text as a regular expression, which is unnecessary since the +# right-hand side of the '&&' cannot influence the outcome of the condition. +# Compiling the regular expression then failed both because of the '{FAIL}', +# which is not a valid repetition of the form '{1,5}', and because of the +# '****', which are repeated repetitions as well. # '${FAIL}' .if 0 && ${:Uword:C,${FAIL}****,,} .endif diff --git a/contrib/bmake/unit-tests/var-op-expand.exp b/contrib/bmake/unit-tests/var-op-expand.exp index 39a9383953dd..a4ba53942cf7 100644 --- a/contrib/bmake/unit-tests/var-op-expand.exp +++ b/contrib/bmake/unit-tests/var-op-expand.exp @@ -1 +1,7 @@ -exit status 0 +make: "var-op-expand.mk" line 265: Unknown modifier "s,value,replaced," +make: "var-op-expand.mk" line 268: warning: XXX Neither branch should be taken. +make: "var-op-expand.mk" line 273: Unknown modifier "s,value,replaced," +make: "var-op-expand.mk" line 274: warning: XXX Neither branch should be taken. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/var-op-expand.mk b/contrib/bmake/unit-tests/var-op-expand.mk index ff62668a8ada..237f7baf1c62 100644 --- a/contrib/bmake/unit-tests/var-op-expand.mk +++ b/contrib/bmake/unit-tests/var-op-expand.mk @@ -1,8 +1,14 @@ -# $NetBSD: var-op-expand.mk,v 1.11 2021/01/01 23:07:48 sjg Exp $ +# $NetBSD: var-op-expand.mk,v 1.15 2021/11/30 23:52:19 rillig Exp $ # # Tests for the := variable assignment operator, which expands its # right-hand side. +# +# See also: +# varname-dot-make-save_dollars.mk +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS:= yes # If the right-hand side does not contain a dollar sign, the ':=' assignment @@ -174,5 +180,102 @@ VAR_SUBST_${UNDEF}:= assigned by ':=' . error .endif + +# The following test case demonstrates that the variable 'LATER' is preserved +# in the ':=' assignment since the variable 'LATER' is not yet defined. +# After the assignment to 'LATER', evaluating the variable 'INDIRECT' +# evaluates 'LATER' as well. +# +.undef LATER +INDIRECT:= ${LATER:S,value,replaced,} +.if ${INDIRECT} != "" +. error +.endif +LATER= late-value +.if ${INDIRECT} != "late-replaced" +. error +.endif + + +# Same as the test case above, except for the additional modifier ':tl' when +# evaluating the variable 'INDIRECT'. Nothing surprising here. +.undef LATER +.undef later +INDIRECT:= ${LATER:S,value,replaced,} +.if ${INDIRECT:tl} != "" +. error +.endif +LATER= uppercase-value +later= lowercase-value +.if ${INDIRECT:tl} != "uppercase-replaced" +. error +.endif + + +# Similar to the two test cases above, the situation gets a bit more involved +# here, due to the double indirection. The variable 'indirect' is supposed to +# be the lowercase version of the variable 'INDIRECT'. +# +# The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as +# well, it wouldn't make a difference in this case. The crucial detail is the +# assignment operator ':=' for the variable 'indirect'. During this +# assignment, the variable modifier ':S,value,replaced,' is converted to +# lowercase, which turns 'S' into 's', thus producing an unknown modifier. +# In this case, make issues a warning, but in cases where the modifier +# includes a '=', the modifier would be interpreted as a SysV-style +# substitution like '.c=.o', and make would not issue a warning, leading to +# silent unexpected behavior. +# +# As of 2021-11-20, the actual behavior is unexpected. Fixing it is not +# trivial. When the assignment to 'indirect' takes place, the expressions +# from the nested expression could be preserved, like this: +# +# Start with: +# +# indirect:= ${INDIRECT:tl} +# +# Since INDIRECT is defined, expand it, remembering that the modifier +# ':tl' must still be applied to the final result. +# +# indirect:= ${LATER:S,value,replaced,} \ +# OK \ +# ${LATER:value=sysv} +# +# The variable 'LATER' is not defined. An idea may be to append the +# remaining modifier ':tl' to each expression that is starting with an +# undefined variable, resulting in: +# +# indirect:= ${LATER:S,value,replaced,:tl} \ +# OK \ +# ${LATER:value=sysv:tl} +# +# This would work for the first expression. The second expression ends +# with the SysV modifier ':from=to', and when this modifier is parsed, +# it consumes all characters until the end of the expression, which in +# this case would replace the suffix 'value' with the literal 'sysv:tl', +# ignoring that the ':tl' was intended to be an additional modifier. +# +# Due to all of this, this surprising behavior is not easy to fix. +# +.undef LATER +.undef later +INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv} +indirect:= ${INDIRECT:tl} +# expect+1: Unknown modifier "s,value,replaced," +.if ${indirect} != " ok " +. error +.else +. warning XXX Neither branch should be taken. +.endif +LATER= uppercase-value +later= lowercase-value +# expect+1: Unknown modifier "s,value,replaced," +.if ${indirect} != "uppercase-replaced ok uppercase-sysv" +. warning XXX Neither branch should be taken. +.else +. error +.endif + + all: @:; diff --git a/contrib/bmake/unit-tests/vardebug.exp b/contrib/bmake/unit-tests/vardebug.exp index 6d00acc977af..3519bbd0ba1b 100644 --- a/contrib/bmake/unit-tests/vardebug.exp +++ b/contrib/bmake/unit-tests/vardebug.exp @@ -43,11 +43,11 @@ Result of ${:Uvalue} is "value" (eval-defined, defined) Indirect modifier "M*e" from "${:UM*e}" Evaluating modifier ${:M...} on value "value" (eval-defined, defined) Pattern for ':M' is "*e" -ModifyWords: split "value" into 1 words +ModifyWords: split "value" into 1 word Result of ${:M*e} is "value" (eval-defined, defined) Evaluating modifier ${:M...} on value "value" (eval-defined, defined) Pattern for ':M' is "valu[e]" -ModifyWords: split "value" into 1 words +ModifyWords: split "value" into 1 word Result of ${:Mvalu[e]} is "value" (eval-defined, defined) Global:delete VAR Var_Parse: ${:Uvariable:unknown} (eval-defined) diff --git a/contrib/bmake/unit-tests/varmisc.mk b/contrib/bmake/unit-tests/varmisc.mk index e5ab375d8f39..81818f3fb8bb 100644 --- a/contrib/bmake/unit-tests/varmisc.mk +++ b/contrib/bmake/unit-tests/varmisc.mk @@ -1,5 +1,5 @@ -# $Id: varmisc.mk,v 1.23 2021/02/05 20:02:30 sjg Exp $ -# $NetBSD: varmisc.mk,v 1.30 2021/02/04 21:42:47 rillig Exp $ +# $Id: varmisc.mk,v 1.25 2021/12/07 00:03:11 sjg Exp $ +# $NetBSD: varmisc.mk,v 1.32 2021/12/05 10:02:51 rillig Exp $ # # Miscellaneous variable tests. @@ -66,7 +66,7 @@ cmpv: @echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}} @echo We have ${${.TARGET:T}.only} -# catch misshandling of nested vars in .for loop +# catch mishandling of nested variables in .for loop MAN= MAN1= make.1 .for s in 1 2 @@ -78,12 +78,15 @@ MAN+= ${MAN$s} manok: @echo MAN=${MAN} +# Test parsing of boolean values. # begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean. SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off SD_4_DOLLARS= $$$$ .for val in ${SD_VALUES} -.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect. +# The assignment must be done using ':=' since a simple '=' would be +# interpreted as 'yes', due to the leading '$'; see ParseBoolean. +.MAKE.SAVE_DOLLARS:= ${val} SD.${val}:= ${SD_4_DOLLARS} .endfor .MAKE.SAVE_DOLLARS:= yes @@ -92,6 +95,7 @@ save-dollars: .for val in ${SD_VALUES} @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q} .endfor +# end .MAKE.SAVE_DOLLARS # Appending to an undefined variable does not add a space in front. .undef APPENDED diff --git a/contrib/bmake/unit-tests/varmod-assign.exp b/contrib/bmake/unit-tests/varmod-assign.exp index 1e43714d500b..1ad388418ab5 100644 --- a/contrib/bmake/unit-tests/varmod-assign.exp +++ b/contrib/bmake/unit-tests/varmod-assign.exp @@ -12,18 +12,6 @@ Var_Parse: ${${VARNAME}} != "assigned-value" (eval-defined) Var_Parse: ${VARNAME}} != "assigned-value" (eval-defined) Global: .MAKEFLAGS = -r -k -d v -d Global: .MAKEFLAGS = -r -k -d v -d 0 -mod-assign: first=1. -mod-assign: last=3. -mod-assign: appended=1 2 3. -1 -2 -3 -mod-assign: ran:3. -mod-assign: global: 1, 3, 1 2 3, 3. -mod-assign-nested: then1t1 -mod-assign-nested: else2e2 -mod-assign-nested: then3t3 -mod-assign-nested: else4e4 make: Bad modifier ":" for variable "" mod-assign-empty: value} make: Bad modifier ":" for variable "" diff --git a/contrib/bmake/unit-tests/varmod-assign.mk b/contrib/bmake/unit-tests/varmod-assign.mk index f50c654f5bcf..b8559025fbfd 100644 --- a/contrib/bmake/unit-tests/varmod-assign.mk +++ b/contrib/bmake/unit-tests/varmod-assign.mk @@ -1,70 +1,79 @@ -# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $ +# $NetBSD: varmod-assign.mk,v 1.14 2021/12/05 10:13:44 rillig Exp $ # # Tests for the obscure ::= variable modifiers, which perform variable # assignments during evaluation, just like the = operator in C. -all: mod-assign -all: mod-assign-nested all: mod-assign-empty all: mod-assign-parse all: mod-assign-shell-error -mod-assign: - # The ::?= modifier applies the ?= assignment operator 3 times. - # The ?= operator only has an effect for the first time, therefore - # the variable FIRST ends up with the value 1. - @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}. +# The modifier '::?=' applies the assignment operator '?=' 3 times. The +# operator '?=' only has an effect for the first time, therefore the variable +# FIRST ends up with the value 1. +.if "${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}" != " first=1" +. error +.endif - # The ::= modifier applies the = assignment operator 3 times. - # The = operator overwrites the previous value, therefore the - # variable LAST ends up with the value 3. - @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}. +# The modifier '::=' applies the assignment operator '=' 3 times. The +# operator '=' overwrites the previous value, therefore the variable LAST ends +# up with the value 3. +.if "${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}" != " last=3" +. error +.endif - # The ::+= modifier applies the += assignment operator 3 times. - # The += operator appends 3 times to the variable, therefore - # the variable APPENDED ends up with the value "1 2 3". - @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}. +# The modifier '::+=' applies the assignment operator '+=' 3 times. The +# operator '+=' appends 3 times to the variable, therefore the variable +# APPENDED ends up with the value "1 2 3". +.if "${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}" != " appended=1 2 3" +. error +.endif - # The ::!= modifier applies the != assignment operator 3 times. - # The side effects of the shell commands are visible in the output. - # Just as with the ::= modifier, the last value is stored in the - # RAN variable. - @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}. +# The modifier '::!=' applies the assignment operator '!=' 3 times. Just as +# with the modifier '::=', the last value is stored in the RAN variable. +.if "${1 2 3:L:@i@${RAN::!=${i:%=echo '<%>';}}@} ran=${RAN}" != " ran=<3>" +. error +.endif - # The assignments happen in the global scope and thus are - # preserved even after the shell command has been run. - @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}. +# The assignments happen in the global scope and thus are preserved even after +# the shell command has been run and the condition has been evaluated. +.if "${FIRST}, ${LAST}, ${APPENDED}, ${RAN}" != "1, 3, 1 2 3, <3>" +. error +.endif -mod-assign-nested: - # The condition "1" is true, therefore THEN1 gets assigned a value, - # and IT1 as well. Nothing surprising here. - @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1} +# Tests for nested assignments, which are hard to read and therefore seldom +# used in practice. - # The condition "0" is false, therefore ELSE1 gets assigned a value, - # and IE1 as well. Nothing surprising here as well. - @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2} +# The condition "1" is true, therefore THEN1 gets assigned a value, +# and the inner IT1 as well. Nothing surprising here. +.if "${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}} ${THEN1}${ELSE1}${IT1}${IE1}" != " then1t1" +. error +.endif - # The same effects happen when the variables are defined elsewhere. - @echo $@: ${SINK3:Q} - @echo $@: ${SINK4:Q} -SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3} -SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4} +# The condition "0" is false, therefore ELSE2 gets assigned a value, +# and the inner IE2 as well. Nothing surprising here as well. +.if "${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}} ${THEN2}${ELSE2}${IT2}${IE2}" != " else2e2" +. error +.endif + +# The same effects happen when the variables are defined elsewhere. +SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}} ${THEN3}${ELSE3}${IT3}${IE3} +SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}} ${THEN4}${ELSE4}${IT4}${IE4} +.if ${SINK3} != " then3t3" +. error +.endif +.if ${SINK4} != " else4e4" +. error +.endif mod-assign-empty: # Assigning to the empty variable would obviously not work since that # variable is write-protected. Therefore it is rejected early with a # "Bad modifier" message. - # - # XXX: The error message is hard to read since the variable name is - # empty. This leads to a trailing space in the error message. @echo $@: ${::=value} # In this variant, it is not as obvious that the name of the # expression is empty. Assigning to it is rejected as well, with the # same "Bad modifier" message. - # - # XXX: The error message is hard to read since the variable name is - # empty. This leads to a trailing space in the error message. @echo $@: ${:Uvalue::=overwritten} # The :L modifier sets the value of the expression to its variable diff --git a/contrib/bmake/unit-tests/varmod-defined.exp b/contrib/bmake/unit-tests/varmod-defined.exp index 2f7d4dbf4baa..e2ae8d29808c 100644 --- a/contrib/bmake/unit-tests/varmod-defined.exp +++ b/contrib/bmake/unit-tests/varmod-defined.exp @@ -11,7 +11,7 @@ Var_Parse: ${VAR:@var@${8_DOLLARS}@} (eval-keep-dollar-and-undefined) Evaluating modifier ${VAR:@...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular) Modifier part: "var" Modifier part: "${8_DOLLARS}" -ModifyWords: split "$$$$$$$$" into 1 words +ModifyWords: split "$$$$$$$$" into 1 word Global: var = $$$$$$$$ Var_Parse: ${8_DOLLARS} (eval-keep-undefined) ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$" diff --git a/contrib/bmake/unit-tests/varmod-defined.mk b/contrib/bmake/unit-tests/varmod-defined.mk index a44b9f993146..ab5d708cf73f 100644 --- a/contrib/bmake/unit-tests/varmod-defined.mk +++ b/contrib/bmake/unit-tests/varmod-defined.mk @@ -1,8 +1,11 @@ -# $NetBSD: varmod-defined.mk,v 1.11 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: varmod-defined.mk,v 1.12 2021/11/30 23:52:19 rillig Exp $ # # Tests for the :D variable modifier, which returns the given string # if the variable is defined. It is closely related to the :U modifier. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes DEF= defined diff --git a/contrib/bmake/unit-tests/varmod-gmtime.exp b/contrib/bmake/unit-tests/varmod-gmtime.exp index 5d5806b92d26..fdc9a2170e2f 100644 --- a/contrib/bmake/unit-tests/varmod-gmtime.exp +++ b/contrib/bmake/unit-tests/varmod-gmtime.exp @@ -1,12 +1,12 @@ -make: "varmod-gmtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +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: -1} != "" +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: 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: 10000000000000000000000000000000} != "" +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: error} != "" +make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/contrib/bmake/unit-tests/varmod-indirect.exp b/contrib/bmake/unit-tests/varmod-indirect.exp index 63ed988d0c0e..9cff88eb760a 100644 --- a/contrib/bmake/unit-tests/varmod-indirect.exp +++ b/contrib/bmake/unit-tests/varmod-indirect.exp @@ -20,7 +20,7 @@ Indirect modifier "S,a,a," from "${:US,a,a,}" Evaluating modifier ${UNDEF:S...} on value "" (eval-keep-dollar-and-undefined, undefined) Modifier part: "a" Modifier part: "a" -ModifyWords: split "" into 1 words +ModifyWords: split "" into 1 word Result of ${UNDEF:S,a,a,} is "" (eval-keep-dollar-and-undefined, undefined) Global: _ = before ${UNDEF:S,a,a,} after ParseReadLine (179): '_:= before ${UNDEF:${:U}} after' diff --git a/contrib/bmake/unit-tests/varmod-localtime.exp b/contrib/bmake/unit-tests/varmod-localtime.exp index ed4d4f053c61..494f160b766e 100644 --- a/contrib/bmake/unit-tests/varmod-localtime.exp +++ b/contrib/bmake/unit-tests/varmod-localtime.exp @@ -1,12 +1,12 @@ -make: "varmod-localtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +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: -1} != "" +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: 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: 10000000000000000000000000000000} != "" +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: error} != "" +make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """ make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/contrib/bmake/unit-tests/varmod-localtime.mk b/contrib/bmake/unit-tests/varmod-localtime.mk index f2867b61f8e9..ffa09a0bc5fc 100644 --- a/contrib/bmake/unit-tests/varmod-localtime.mk +++ b/contrib/bmake/unit-tests/varmod-localtime.mk @@ -3,7 +3,7 @@ # Tests for the :localtime variable modifier, which formats a timestamp # using strftime(3) in local time. -.if ${TZ} != "Europe/Berlin" # see unit-tests/Makefile +.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile . error .endif diff --git a/contrib/bmake/unit-tests/varmod-loop-delete.exp b/contrib/bmake/unit-tests/varmod-loop-delete.exp new file mode 100644 index 000000000000..aac86ee39061 --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-loop-delete.exp @@ -0,0 +1,4 @@ +make: "varmod-loop-delete.mk" line 19: Cannot delete variable "VAR" while it is used +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-loop-delete.mk b/contrib/bmake/unit-tests/varmod-loop-delete.mk new file mode 100644 index 000000000000..ed145b59ba0f --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-loop-delete.mk @@ -0,0 +1,33 @@ +# $NetBSD: varmod-loop-delete.mk,v 1.2 2021/12/05 15:51:33 rillig Exp $ +# +# Tests for the variable modifier ':@', which as a side effect allows to +# delete an arbitrary variable. + +# A side effect of the modifier ':@' is that the loop variable is created as +# an actual variable in the current evaluation scope (Command/Global/target), +# and at the end of the loop, this variable is deleted. Since var.c 1.204 +# from 2016-02-18 and before var.c 1.963 from 2021-12-05, a variable could be +# deleted while it was in use, leading to a use-after-free bug. +# +# See Var_Parse, comment 'the value of the variable must not change'. + +# Set up the variable that deletes itself when it is evaluated. +VAR= ${:U:@VAR@@} rest of the value + +# In an assignment, the scope is 'Global'. Since the variable 'VAR' is +# defined in the global scope, it deletes itself. +EVAL:= ${VAR} +.if ${EVAL} != " rest of the value" +. error +.endif + +VAR= ${:U:@VAR@@} rest of the value +all: .PHONY + # In the command that is associated with a target, the scope is the + # one from the target. That scope only contains a few variables like + # '.TARGET', '.ALLSRC', '.IMPSRC'. Make does not expect that these + # variables get modified from the outside. + # + # There is no variable named 'VAR' in the local scope, so nothing + # happens. + : $@: '${VAR}' diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.exp b/contrib/bmake/unit-tests/varmod-loop-varname.exp index 9170307bd2a0..4f0379d5ea0a 100644 --- a/contrib/bmake/unit-tests/varmod-loop-varname.exp +++ b/contrib/bmake/unit-tests/varmod-loop-varname.exp @@ -1,11 +1,11 @@ -make: "varmod-loop-varname.mk" line 13: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar. -make: "varmod-loop-varname.mk" line 13: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+") -make: "varmod-loop-varname.mk" line 80: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar. -make: "varmod-loop-varname.mk" line 80: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)") -make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar. -make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()") -make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar. -make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()") +make: "varmod-loop-varname.mk" line 16: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar +make: "varmod-loop-varname.mk" line 16: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+") +make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar +make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)") +make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar +make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()") +make: "varmod-loop-varname.mk" line 95: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar +make: "varmod-loop-varname.mk" line 95: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.mk b/contrib/bmake/unit-tests/varmod-loop-varname.mk index d51e2ba76a42..91f8a4876466 100644 --- a/contrib/bmake/unit-tests/varmod-loop-varname.mk +++ b/contrib/bmake/unit-tests/varmod-loop-varname.mk @@ -1,8 +1,11 @@ -# $NetBSD: varmod-loop-varname.mk,v 1.2 2021/04/04 13:35:26 rillig Exp $ +# $NetBSD: varmod-loop-varname.mk,v 1.4 2021/12/05 15:01:04 rillig Exp $ # # Tests for the first part of the variable modifier ':@var@...@', which # contains the variable name to use during the loop. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes @@ -12,6 +15,8 @@ # variable name. .if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+" . error +.else +. error .endif diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp index a4704973f6e2..d05b870d5b5e 100644 --- a/contrib/bmake/unit-tests/varmod-loop.exp +++ b/contrib/bmake/unit-tests/varmod-loop.exp @@ -1,10 +1,10 @@ -ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$' +ParseReadLine (78): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$' CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$" lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = != -ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}' +ParseReadLine (83): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}' CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$" lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = != -ParseReadLine (105): '.MAKEFLAGS: -d0' +ParseReadLine (108): '.MAKEFLAGS: -d0' ParseDependency(.MAKEFLAGS: -d0) :varname-overwriting-target: :x1y x2y x3y: :: mod-loop-dollar:1: diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk index 4fdaa3ff4e61..82046ff95d79 100644 --- a/contrib/bmake/unit-tests/varmod-loop.mk +++ b/contrib/bmake/unit-tests/varmod-loop.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $ # # Tests for the :@var@...${var}...@ variable modifier. +# Force the test results to be independent of the default value of this +# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake +# distribution and pkgsrc/devel/bmake. .MAKE.SAVE_DOLLARS= yes all: varname-overwriting-target @@ -183,7 +186,4 @@ CMDLINE= global # needed for deleting the environment . error # 'CMDLINE' is gone now from all scopes .endif - -# TODO: Actually trigger the undefined behavior (use after free) that was -# already suspected in Var_Parse, in the comment 'the value of the variable -# must not change'. +all: .PHONY diff --git a/contrib/bmake/unit-tests/varmod-order-numeric.exp b/contrib/bmake/unit-tests/varmod-order-numeric.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-order-numeric.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/contrib/bmake/unit-tests/varmod-order-numeric.mk b/contrib/bmake/unit-tests/varmod-order-numeric.mk new file mode 100644 index 000000000000..40a1d408e9ae --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-order-numeric.mk @@ -0,0 +1,54 @@ +# $NetBSD: varmod-order-numeric.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $ +# +# Tests for the variable modifiers ':On', which returns the words, sorted in +# ascending numeric order, and for ':Orn' and ':Onr', which additionally +# reverse the order. +# +# The variable modifiers ':On', ':Onr' and ':Orn' were added in var.c 1.939 +# from 2021-07-30. + +# This list contains only 32-bit numbers since the make code needs to conform +# to C90, which does not provide integer types larger than 32 bit. It uses +# 'long long' by default, but that type is overridable if necessary to support +# older environments. +# +# To get 53-bit integers even in C90, it would be possible to switch to +# 'double' instead, but that would allow floating-point numbers as well, which +# is out of scope for this variable modifier. +NUMBERS= 3 5 7 1 42 -42 5K -3m 1M 1k -2G + +.if ${NUMBERS:On} != "-2G -3m -42 1 3 5 7 42 1k 5K 1M" +. error ${NUMBERS:On} +.endif + +.if ${NUMBERS:Orn} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" +. error ${NUMBERS:Orn} +.endif + +# Both ':Onr' and ':Orn' have the same effect. +.if ${NUMBERS:Onr} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G" +. error ${NUMBERS:Onr} +.endif + +# Duplicate numbers are preserved in the output. In this case the +# equal-valued numbers are spelled the same, so they are indistinguishable in +# the output. +DUPLICATES= 3 1 2 2 1 1 # https://oeis.org/A034002 +.if ${DUPLICATES:On} != "1 1 1 2 2 3" +. error ${DUPLICATES:On} +.endif + +# If there are several numbers that have the same integer value, they are +# returned in unspecified order. +SAME_VALUE:= ${:U 79 80 0x0050 81 :On} +.if ${SAME_VALUE} != "79 80 0x0050 81" && ${SAME_VALUE} != "79 0x0050 80 81" +. error ${SAME_VALUE} +.endif + +# Hexadecimal and octal numbers are supported as well. +MIXED_BASE= 0 010 0x7 9 +.if ${MIXED_BASE:On} != "0 0x7 010 9" +. error ${MIXED_BASE:On} +.endif + +all: diff --git a/contrib/bmake/unit-tests/varmod-order-reverse.mk b/contrib/bmake/unit-tests/varmod-order-reverse.mk index 1a6d2d766f76..c3be8d0f7817 100644 --- a/contrib/bmake/unit-tests/varmod-order-reverse.mk +++ b/contrib/bmake/unit-tests/varmod-order-reverse.mk @@ -1,13 +1,12 @@ -# $NetBSD: varmod-order-reverse.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: varmod-order-reverse.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $ # # Tests for the :Or variable modifier, which returns the words, sorted in # descending order. -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten -.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight" -. error ${NUMBERS:Or} +.if ${WORDS:Or} != "two three ten six seven one nine four five eight" +. error ${WORDS:Or} .endif all: - @:; diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.mk b/contrib/bmake/unit-tests/varmod-order-shuffle.mk index 185141b6c4a5..16121d7e498f 100644 --- a/contrib/bmake/unit-tests/varmod-order-shuffle.mk +++ b/contrib/bmake/unit-tests/varmod-order-shuffle.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-order-shuffle.mk,v 1.6 2020/11/09 20:16:33 rillig Exp $ +# $NetBSD: varmod-order-shuffle.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $ # # Tests for the :Ox variable modifier, which returns the words of the # variable, shuffled. @@ -11,7 +11,7 @@ # # Tags: probabilistic -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten # Note that 1 in every 10! trials two independently generated # randomized orderings will be the same. The test framework doesn't @@ -20,24 +20,23 @@ NUMBERS= one two three four five six seven eight nine ten # lets the whole test fail once in 1.209.600 runs, on average. # Create two shuffles using the := assignment operator. -shuffled1:= ${NUMBERS:Ox} -shuffled2:= ${NUMBERS:Ox} +shuffled1:= ${WORDS:Ox} +shuffled2:= ${WORDS:Ox} .if ${shuffled1} == ${shuffled2} . error ${shuffled1} == ${shuffled2} .endif # Sorting the list before shuffling it has no effect. -shuffled1:= ${NUMBERS:O:Ox} -shuffled2:= ${NUMBERS:O:Ox} +shuffled1:= ${WORDS:O:Ox} +shuffled2:= ${WORDS:O:Ox} .if ${shuffled1} == ${shuffled2} . error ${shuffled1} == ${shuffled2} .endif # Sorting after shuffling must produce the original numbers. -sorted:= ${NUMBERS:Ox:O} -.if ${sorted} != ${NUMBERS:O} -. error ${sorted} != ${NUMBERS:O} +sorted:= ${WORDS:Ox:O} +.if ${sorted} != ${WORDS:O} +. error ${sorted} != ${WORDS:O} .endif all: - @:; diff --git a/contrib/bmake/unit-tests/varmod-order-string.exp b/contrib/bmake/unit-tests/varmod-order-string.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-order-string.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/contrib/bmake/unit-tests/varmod-order-string.mk b/contrib/bmake/unit-tests/varmod-order-string.mk new file mode 100644 index 000000000000..bb0a145ba825 --- /dev/null +++ b/contrib/bmake/unit-tests/varmod-order-string.mk @@ -0,0 +1,28 @@ +# $NetBSD: varmod-order-string.mk,v 1.2 2021/08/03 04:46:49 rillig Exp $ +# +# Tests for the :O variable modifier, which returns the words, sorted in +# ascending order. + +# Simple words are sorted lexicographically. +WORDS= one two three four five six seven eight nine ten +.if ${WORDS:O} != "eight five four nine one seven six ten three two" +. error ${WORDS:O} +.endif + +# Double quotes and single quotes delimit words, while backticks are just +# regular characters. Therefore '`in' is a separate word from 'backticks`', +# and the additional spaces between them are removed. +QUOTED_WORDS= none "double quoted" 'single quoted' `in backticks` +.if ${QUOTED_WORDS:O} != "\"double quoted\" 'single quoted' `in backticks` none" +. error ${QUOTED_WORDS:O} +.endif + +# Numbers are sorted lexicographically as well. +# To sort the words numerically, use ':On' instead; since var.c 1.939 from +# 2021-07-30. +NUMBERS= -100g -50m -7k -50 -13 0 000 13 50 5k1 7k 50m 100G +.if ${NUMBERS:O} != "-100g -13 -50 -50m -7k 0 000 100G 13 50 50m 5k1 7k" +. error ${NUMBERS:O} +.endif + +all: diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp index 94c3cb694886..46dc45e9f6d6 100644 --- a/contrib/bmake/unit-tests/varmod-order.exp +++ b/contrib/bmake/unit-tests/varmod-order.exp @@ -1,7 +1,24 @@ -make: Bad modifier ":OX" for variable "NUMBERS" -make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX" -make: Bad modifier ":OxXX" for variable "NUMBERS" -make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox" +make: Bad modifier ":OX" for variable "WORDS" +make: "varmod-order.mk" line 14: Undefined variable "${WORDS:OX" +make: Bad modifier ":OxXX" for variable "WORDS" +make: "varmod-order.mk" line 17: Undefined variable "${WORDS:Ox" +make: Unclosed variable expression, expecting '}' for modifier "O" of variable "WORDS" with value "eight five four nine one seven six ten three two" +make: Unclosed variable expression, expecting '}' for modifier "On" of variable "NUMBERS" with value "1 2 3 4 5 6 7 8 9 10" +make: Unclosed variable expression, expecting '}' for modifier "Onr" of variable "NUMBERS" with value "10 9 8 7 6 5 4 3 2 1" +make: Bad modifier ":Oxn" for variable "NUMBERS" +make: "varmod-order.mk" line 29: Malformed conditional (${NUMBERS:Oxn}) +make: Bad modifier ":On_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 39: Malformed conditional (${NUMBERS:On_typo}) +make: Bad modifier ":Onr_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 48: Malformed conditional (${NUMBERS:Onr_typo}) +make: Bad modifier ":Orn_typo" for variable "NUMBERS" +make: "varmod-order.mk" line 57: Malformed conditional (${NUMBERS:Orn_typo}) +make: Bad modifier ":Onn" for variable "NUMBERS" +make: "varmod-order.mk" line 68: Malformed conditional (${NUMBERS:Onn}) +make: Bad modifier ":Onrr" for variable "NUMBERS" +make: "varmod-order.mk" line 77: Malformed conditional (${NUMBERS:Onrr}) +make: Bad modifier ":Orrn" for variable "NUMBERS" +make: "varmod-order.mk" line 86: Malformed conditional (${NUMBERS:Orrn}) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/contrib/bmake/unit-tests/varmod-order.mk b/contrib/bmake/unit-tests/varmod-order.mk index 675b6efec5e7..7f3d485fe8e3 100644 --- a/contrib/bmake/unit-tests/varmod-order.mk +++ b/contrib/bmake/unit-tests/varmod-order.mk @@ -1,19 +1,92 @@ -# $NetBSD: varmod-order.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $ +# $NetBSD: varmod-order.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $ # -# Tests for the :O variable modifier, which returns the words, sorted in -# ascending order. +# Tests for the :O variable modifier and its variants, which either sort the +# words of the value or shuffle them. -NUMBERS= one two three four five six seven eight nine ten +WORDS= one two three four five six seven eight nine ten +NUMBERS= 8 5 4 9 1 7 6 10 3 2 # in English alphabetical order -.if ${NUMBERS:O} != "eight five four nine one seven six ten three two" -. error ${NUMBERS:O} +.if ${WORDS:O} != "eight five four nine one seven six ten three two" +. error ${WORDS:O} .endif # Unknown modifier "OX" -_:= ${NUMBERS:OX} +_:= ${WORDS:OX} # Unknown modifier "OxXX" -_:= ${NUMBERS:OxXX} +_:= ${WORDS:OxXX} + +# Missing closing brace, to cover the error handling code. +_:= ${WORDS:O +_:= ${NUMBERS:On +_:= ${NUMBERS:Onr + +# Shuffling numerically doesn't make sense, so don't allow 'x' and 'n' to be +# combined. +# +# expect-text: Bad modifier ":Oxn" for variable "NUMBERS" +# expect+1: Malformed conditional (${NUMBERS:Oxn}) +.if ${NUMBERS:Oxn} +. error +.else +. error +.endif + +# Extra characters after ':On' are detected and diagnosed. +# TODO: Add line number information to the "Bad modifier" diagnostic. +# +# expect-text: Bad modifier ":On_typo" for variable "NUMBERS" +.if ${NUMBERS:On_typo} +. error +.else +. error +.endif + +# Extra characters after ':Onr' are detected and diagnosed. +# +# expect-text: Bad modifier ":Onr_typo" for variable "NUMBERS" +.if ${NUMBERS:Onr_typo} +. error +.else +. error +.endif + +# Extra characters after ':Orn' are detected and diagnosed. +# +# expect+1: Bad modifier ":Orn_typo" for variable "NUMBERS" +.if ${NUMBERS:Orn_typo} +. error +.else +. error +.endif + +# Repeating the 'n' is not supported. In the typical use cases, the sorting +# criteria are fixed, not computed, therefore allowing this redundancy does +# not make sense. +# +# expect-text: Bad modifier ":Onn" for variable "NUMBERS" +.if ${NUMBERS:Onn} +. error +.else +. error +.endif + +# Repeating the 'r' is not supported as well, for the same reasons as above. +# +# expect-text: Bad modifier ":Onrr" for variable "NUMBERS" +.if ${NUMBERS:Onrr} +. error +.else +. error +.endif + +# Repeating the 'r' is not supported as well, for the same reasons as above. +# +# expect-text: Bad modifier ":Orrn" for variable "NUMBERS" +.if ${NUMBERS:Orrn} +. error +.else +. error +.endif all: - @:; diff --git a/contrib/bmake/unit-tests/varmod-root.exp b/contrib/bmake/unit-tests/varmod-root.exp index 2c99cd3ef4c7..39a9383953dd 100644 --- a/contrib/bmake/unit-tests/varmod-root.exp +++ b/contrib/bmake/unit-tests/varmod-root.exp @@ -1,11 +1 @@ -root of 'a/b/c' is 'a/b/c' -root of 'def' is 'def' -root of 'a.b.c' is 'a.b' -root of 'a.b/c' is 'a' -root of 'a' is 'a' -root of 'a.a' is 'a' -root of '.gitignore' is '' -root of 'a' is 'a' -root of 'a.a' is 'a' -root of 'trailing/' is 'trailing/' exit status 0 diff --git a/contrib/bmake/unit-tests/varmod-root.mk b/contrib/bmake/unit-tests/varmod-root.mk index 1e3159733df0..cf88491df799 100644 --- a/contrib/bmake/unit-tests/varmod-root.mk +++ b/contrib/bmake/unit-tests/varmod-root.mk @@ -1,9 +1,38 @@ -# $NetBSD: varmod-root.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $ +# $NetBSD: varmod-root.mk,v 1.5 2021/12/05 22:31:58 rillig Exp $ # # Tests for the :R variable modifier, which returns the filename root # without the extension. +.if ${a/b/c:L:R} != "a/b/c" +. error +.endif + +.if ${def:L:R} != "def" +. error +.endif + +.if ${a.b.c:L:R} != "a.b" +. error +.endif + +.if ${a.b/c:L:R} != "a" +. error +.endif + +.if ${a:L:R} != "a" +. error +.endif + +.if ${a.a:L:R} != "a" +. error +.endif + +.if ${.gitignore:L:R} != "" +. error +.endif + +.if ${trailing/:L:R} != "trailing/" +. error +.endif + all: -.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/ - @echo "root of '"${path:Q}"' is '"${path:R:Q}"'" -.endfor diff --git a/contrib/bmake/unit-tests/varmod-select-words.mk b/contrib/bmake/unit-tests/varmod-select-words.mk index a9df25f9ff32..ab094bf056b0 100644 --- a/contrib/bmake/unit-tests/varmod-select-words.mk +++ b/contrib/bmake/unit-tests/varmod-select-words.mk @@ -1,7 +1,10 @@ -# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varmod-select-words.mk,v 1.3 2021/12/05 12:06:23 rillig Exp $ # # Tests for the :[...] variable modifier, which selects a single word # or a range of words from a variable. +# +# See also: +# modword.mk (should be migrated here) # TODO: Implementation diff --git a/contrib/bmake/unit-tests/varmod-subst.mk b/contrib/bmake/unit-tests/varmod-subst.mk index 85f41e499ab7..763535ff835a 100644 --- a/contrib/bmake/unit-tests/varmod-subst.mk +++ b/contrib/bmake/unit-tests/varmod-subst.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-subst.mk,v 1.8 2021/05/14 19:37:16 rillig Exp $ +# $NetBSD: varmod-subst.mk,v 1.9 2021/09/06 21:18:55 rillig Exp $ # # Tests for the :S,from,to, variable modifier. @@ -86,6 +86,19 @@ WORDS= sequences of letters . error The '.' seems to be interpreted as a wildcard of some kind. .endif +.if ${:Uvalue:S,^val,&,} != "value" +. error +.endif +.if ${:Uvalue:S,ue$,&,} != "value" +. error +.endif +.if ${:Uvalue:S,^val,&-&-&,} != "val-val-value" +. error +.endif +.if ${:Uvalue:S,ue$,&-&-&,} != "value-ue-ue" +. error +.endif + mod-subst: @echo $@: @echo :${:Ua b b c:S,a b,,:Q}: diff --git a/contrib/bmake/unit-tests/varmod-to-separator.exp b/contrib/bmake/unit-tests/varmod-to-separator.exp index c6e8ce98a21a..3f8f1b2a11eb 100644 --- a/contrib/bmake/unit-tests/varmod-to-separator.exp +++ b/contrib/bmake/unit-tests/varmod-to-separator.exp @@ -1,6 +1,6 @@ -make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu} +make: "varmod-to-separator.mk" line 107: Invalid character number at "400:tu}" make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu}) -make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu} +make: "varmod-to-separator.mk" line 121: Invalid character number at "100:tu}" make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu}) make: Bad modifier ":ts\-300" for variable "WORDS" make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu}) diff --git a/contrib/bmake/unit-tests/varmod-unique.mk b/contrib/bmake/unit-tests/varmod-unique.mk index 04d04a575af1..7fef35b69211 100644 --- a/contrib/bmake/unit-tests/varmod-unique.mk +++ b/contrib/bmake/unit-tests/varmod-unique.mk @@ -1,47 +1,46 @@ -# $NetBSD: varmod-unique.mk,v 1.5 2021/05/30 20:26:41 rillig Exp $ +# $NetBSD: varmod-unique.mk,v 1.6 2021/12/05 22:37:58 rillig Exp $ # # Tests for the :u variable modifier, which discards adjacent duplicate # words. -.if ${:U1 2 1:u} != "1 2 1" -. warning The :u modifier only merges _adjacent_ duplicate words. +.if ${1 2 1:L:u} != "1 2 1" +. warning The modifier ':u' only merges _adjacent_ duplicate words. .endif -.if ${:U1 2 2 3:u} != "1 2 3" -. warning The :u modifier must merge adjacent duplicate words. +.if ${1 2 2 3:L:u} != "1 2 3" +. warning The modifier ':u' must merge adjacent duplicate words. .endif -.if ${:U:u} != "" -. warning The :u modifier must do nothing with an empty word list. +.if ${:L:u} != "" +. warning The modifier ':u' must do nothing with an empty word list. .endif -.if ${:U :u} != "" +.if ${ :L:u} != "" . warning The modifier ':u' must normalize the whitespace. .endif -.if ${:Uword:u} != "word" -. warning The :u modifier must do nothing with a single-element word list. +.if ${word:L:u} != "word" +. warning The modifier ':u' must do nothing with a single-element word list. .endif -.if ${:U word :u} != "word" +.if ${ word :L:u} != "word" . warning The modifier ':u' must normalize the whitespace. .endif -.if ${:U1 1 1 1 1 1 1 1:u} != "1" -. warning The :u modifier must merge _all_ adjacent duplicate words. +.if ${1 1 1 1 1 1 1 1:L:u} != "1" +. warning The modifier ':u' must merge _all_ adjacent duplicate words. .endif -.if ${:U 1 2 1 1 :u} != "1 2 1" -. warning The :u modifier must normalize whitespace between the words. +.if ${ 1 2 1 1 :L:u} != "1 2 1" +. warning The modifier ':u' must normalize whitespace between the words. .endif -.if ${:U1 1 1 1 2:u} != "1 2" +.if ${1 1 1 1 2:L:u} != "1 2" . warning Duplicate words at the beginning must be merged. .endif -.if ${:U1 2 2 2 2:u} != "1 2" +.if ${1 2 2 2 2:L:u} != "1 2" . warning Duplicate words at the end must be merged. .endif all: - @:; diff --git a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk index 97f37a646d2d..31f228c220b2 100644 --- a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk +++ b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk @@ -1,8 +1,130 @@ -# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# $NetBSD: varname-dot-make-save_dollars.mk,v 1.7 2021/12/03 18:43:52 rillig Exp $ # -# Tests for the special .MAKE.SAVE_DOLLARS variable. +# Tests for the special .MAKE.SAVE_DOLLARS variable, which controls whether +# the assignment operator ':=' converts '$$' to a single '$' or keeps it +# as-is. +# +# See also: +# var-op-expand.mk for ':=' in general +# varmisc.mk for parsing the boolean values + +# Initially, the variable .MAKE.SAVE_DOLLARS is undefined. At this point the +# behavior of the assignment operator ':=' depends. NetBSD's usr.bin/make +# preserves the '$$' as-is, while the bmake distribution replaces '$$' with +# '$'. +.if ${.MAKE.SAVE_DOLLARS:Uundefined} != "undefined" +. error +.endif + + +# When dollars are preserved, this setting not only applies to literal +# dollars, but also to those that come indirectly from other expressions. +DOLLARS= $$$$$$$$ +.MAKE.SAVE_DOLLARS= yes +VAR:= ${DOLLARS} +# The reduction from 8 '$' to 4 '$' happens when ${VAR} is evaluated in the +# condition; .MAKE.SAVE_DOLLARS only applies at the moment where the +# assignment is performed using ':='. +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# When dollars are preserved, this setting not only applies to literal +# dollars, but also to those that come indirectly from other expressions. +DOLLARS= $$$$$$$$ +.MAKE.SAVE_DOLLARS= no +VAR:= ${DOLLARS} +.if ${VAR} != "\$\$" +. error +.endif + +# The 'yes' preserves the dollars from the literal. +.MAKE.SAVE_DOLLARS= yes +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# The 'no' converts each '$$' to '$'. +.MAKE.SAVE_DOLLARS= no +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$" +. error +.endif + +# It's even possible to change the dollar interpretation in the middle of +# evaluating an expression, but there is no practical need for it. +.MAKE.SAVE_DOLLARS= no +VAR:= $$$$-${.MAKE.SAVE_DOLLARS::=yes}-$$$$ +.if ${VAR} != "\$--\$\$" +. error +.endif + +# The '$' from the ':U' expressions do not appear as literal '$$' to the +# parser (no matter whether directly or indirectly), they only appear as '$$' +# in the value of an expression, therefore .MAKE.SAVE_DOLLARS doesn't apply +# here. +.MAKE.SAVE_DOLLARS= no +VAR:= ${:U\$\$\$\$}-${.MAKE.SAVE_DOLLARS::=yes}-${:U\$\$\$\$} +.if ${VAR} != "\$\$--\$\$" +. error +.endif + +# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it +# does not restore the default behavior. +.MAKE.SAVE_DOLLARS= no +.undef .MAKE.SAVE_DOLLARS +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$" +. error +.endif + +# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it +# does not restore the default behavior. +.MAKE.SAVE_DOLLARS= yes +.undef .MAKE.SAVE_DOLLARS +VAR:= $$$$$$$$ +.if ${VAR} != "\$\$\$\$" +. error +.endif + +# The variable '.MAKE.SAVE_DOLLARS' not only affects literal '$$' on the +# right-hand side of the assignment operator ':=', it also affects dollars +# in indirect expressions. +# +# In this example, it affects the command in CMD itself, not the result of +# running that command. +.MAKE.SAVE_DOLLARS= no +CMD= echo '$$$$$$$$' +VAR:= ${CMD:sh} +.if ${VAR} != "\$\$" +. error +.endif + +.MAKE.SAVE_DOLLARS= yes +CMD= echo '$$$$$$$$' +VAR:= ${CMD:sh} +.if ${VAR} != "\$\$\$\$" +. error +.endif + + +# In the modifier ':@var@body@', .MAKE.SAVE_DOLLARS does not affect the body. +# In both cases, each '$$' is replaced with a single '$', no matter whether +# directly or indirectly via another expression. +.MAKE.SAVE_DOLLARS= no +DOLLARS= $$$$$$$$ +VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@} +.if ${VAR} != "\$\$-\$\$" +. error +.endif + +.MAKE.SAVE_DOLLARS= yes +DOLLARS= $$$$$$$$ +VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@} +.if ${VAR} != "\$\$-\$\$" +. error +.endif -# TODO: Implementation all: - @:; diff --git a/contrib/bmake/unit-tests/varname-dot-suffixes.exp b/contrib/bmake/unit-tests/varname-dot-suffixes.exp new file mode 100644 index 000000000000..753ce20d9fa8 --- /dev/null +++ b/contrib/bmake/unit-tests/varname-dot-suffixes.exp @@ -0,0 +1,39 @@ +Global:delete .SUFFIXES (not found) +Global: .MAKEFLAGS = -r -k -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 +Global: .SUFFIXES = set ignored (read-only) +Global: .SUFFIXES = append ignored (read-only) +Global: _ = +Var_Parse: ${.SUFFIXES::=assign} (eval-keep-dollar-and-undefined) +Evaluating modifier ${.SUFFIXES::...} on value ".c .o .1 .err .tar.gz" (eval-keep-dollar-and-undefined, regular) +Modifier part: "assign" +Global: .SUFFIXES = assign ignored (read-only) +Result of ${.SUFFIXES::=assign} is "" (eval-keep-dollar-and-undefined, regular) +Global: _ = +Var_Parse: ${preserve:L:_=.SUFFIXES} (eval-keep-dollar-and-undefined) +Evaluating modifier ${preserve:L} on value "" (eval-keep-dollar-and-undefined, undefined) +Result of ${preserve:L} is "preserve" (eval-keep-dollar-and-undefined, defined) +Evaluating modifier ${preserve:_...} on value "preserve" (eval-keep-dollar-and-undefined, defined) +Global: .SUFFIXES = preserve ignored (read-only) +Result of ${preserve:_=.SUFFIXES} is "preserve" (eval-keep-dollar-and-undefined, defined) +Global: _ = preserve +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 +Var_Parse: ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined) +Evaluating modifier ${1 2:L} on value "" (eval-defined, undefined) +Result of ${1 2:L} is "1 2" (eval-defined, defined) +Evaluating modifier ${1 2:@...} on value "1 2" (eval-defined, defined) +Modifier part: ".SUFFIXES" +Modifier part: "${.SUFFIXES}" +ModifyWords: split "1 2" into 2 words +Command: .SUFFIXES = 1 ignored (read-only) +Var_Parse: ${.SUFFIXES} (eval-defined) +ModifyWord_Loop: in "1", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz" +Command: .SUFFIXES = 2 ignored (read-only) +Var_Parse: ${.SUFFIXES} (eval-defined) +ModifyWord_Loop: in "2", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz" +Command:delete .SUFFIXES (not found) +Result of ${1 2:@.SUFFIXES@${.SUFFIXES}@} is ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined, defined) +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d +Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d 0 +exit status 0 diff --git a/contrib/bmake/unit-tests/varname-dot-suffixes.mk b/contrib/bmake/unit-tests/varname-dot-suffixes.mk new file mode 100644 index 000000000000..de8034a172cc --- /dev/null +++ b/contrib/bmake/unit-tests/varname-dot-suffixes.mk @@ -0,0 +1,104 @@ +# $NetBSD: varname-dot-suffixes.mk,v 1.1 2021/12/12 22:16:48 rillig Exp $ +# +# Tests for the special "variable" .SUFFIXES, which lists the suffixes that +# have been registered for use in suffix transformation rules. Suffixes are +# listed even if there is no actual transformation rule that uses them. +# +# The name '.SUFFIXES' does not refer to a real variable, instead it can be +# used as a starting "variable name" for expressions like ${.SUFFIXES} or +# ${.SUFFIXES:M*o}. + +# In the beginning, there are no suffix rules, the expression is thus empty. +.if ${.SUFFIXES} != "" +.endif + +# There is no actual variable named '.SUFFIXES', it is all made up. +.if defined(.SUFFIXES) +. error +.endif + +# The suffixes list is still empty, and so is the "variable" '.SUFFIXES'. +.if !empty(.SUFFIXES) +. error +.endif + +.SUFFIXES: .c .o .1 .err + +# The suffixes are listed in declaration order. +.if ${.SUFFIXES} != ".c .o .1 .err" +. error +.endif + +# There is still no actual variable named '.SUFFIXES', it is all made up. +.if defined(.SUFFIXES) +. error +.endif + +# Now the suffixes list is not empty anymore. It may seem strange that there +# is no variable named '.SUFFIXES' but evaluating '${.SUFFIXES}' nevertheless +# returns something. For all practical use cases, it's good enough though. +.if empty(.SUFFIXES) +. error +.endif + +.SUFFIXES: .tar.gz + +# Changes to the suffixes list are reflected immediately. +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# Deleting .SUFFIXES has no effect since there is no actual variable of that +# name. +.MAKEFLAGS: -dv +# expect: Global:delete .SUFFIXES (not found) +.undef .SUFFIXES +.MAKEFLAGS: -d0 +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# The list of suffixes can only be modified using dependency declarations, any +# attempt at setting the variable named '.SUFFIXES' is rejected. +.MAKEFLAGS: -dv +# expect: Global: .SUFFIXES = set ignored (read-only) +.SUFFIXES= set +# expect: Global: .SUFFIXES = append ignored (read-only) +.SUFFIXES+= append +# expect: Global: .SUFFIXES = assign ignored (read-only) +_:= ${.SUFFIXES::=assign} +# expect: Command: .SUFFIXES = preserve ignored (read-only) +_:= ${preserve:L:_=.SUFFIXES} +.MAKEFLAGS: -d0 + +# Using the name '.SUFFIXES' in a .for loop looks strange because these +# variable names are typically in singular form, and .for loops do not use +# real variables either, they are made up as well, see directive-for.mk. The +# replacement mechanism for the iteration variables takes precedence. +.for .SUFFIXES in .c .o +. if ${.SUFFIXES} != ".c" && ${.SUFFIXES} != ".o" +. error +. endif +.endfor + +# After the .for loop, the expression '${.SUFFIXES}' refers to the list of +# suffixes again. +.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz" +. error +.endif + +# Using the name '.SUFFIXES' in the modifier ':@var@body@' does not create an +# actual variable either. Like in the .for loop, choosing the name +# '.SUFFIXES' for the iteration variable is unusual. In ODE Make, the +# convention for these iteration variables is to have dots at both ends, so +# the name would be '.SUFFIXES.', furthermore the name of the iteration +# variable is typically in singular form. +.MAKEFLAGS: -dv +# expect: Command: .SUFFIXES = 1 ignored (read-only) +# expect: Command: .SUFFIXES = 2 ignored (read-only) +.if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" +. error +.endif +.MAKEFLAGS: -d0 + +all: diff --git a/contrib/bmake/unit-tests/varname-empty.exp b/contrib/bmake/unit-tests/varname-empty.exp index ec225c6973c8..75a3f4151a8c 100644 --- a/contrib/bmake/unit-tests/varname-empty.exp +++ b/contrib/bmake/unit-tests/varname-empty.exp @@ -2,10 +2,6 @@ Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdlin Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored Global: .CURDIR = Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval) -Global: .OBJDIR = -Global:delete .PATH (not found) -Global: .PATH = . -Global: .PATH = . Global: .TARGETS = Internal: MAKEFILE = varname-empty.mk Global: .MAKE.MAKEFILES = varname-empty.mk diff --git a/contrib/bmake/util.c b/contrib/bmake/util.c index 571d187c5fe1..dab18e2ece53 100644 --- a/contrib/bmake/util.c +++ b/contrib/bmake/util.c @@ -3,7 +3,7 @@ /* * Missing stuff from OS's * - * $Id: util.c,v 1.46 2021/02/05 20:02:29 sjg Exp $ + * $Id: util.c,v 1.49 2021/10/14 19:26:52 sjg Exp $ */ #include @@ -340,6 +340,10 @@ getcwd(path, sz) } #endif +#if !defined(HAVE_SIGACTION) +#include "sigact.h" +#endif + /* force posix signals */ SignalProc bmake_signal(int s, SignalProc a) @@ -586,3 +590,79 @@ warnx(const char *fmt, ...) va_end(ap); } #endif + +#ifdef HAVE_INTTYPES_H +#include +#elif defined(HAVE_STDINT_H) +#include +#endif +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifndef NUM_TYPE +# ifdef HAVE_LONG_LONG_INT +# define NUM_TYPE long long +# elif defined(_INT64_T_DECLARED) || defined(int64_t) +# define NUM_TYPE int64_t +# endif +#endif + +#ifdef NUM_TYPE +#if !defined(HAVE_STRTOLL) +#define BCS_ONLY +#define _FUNCNAME strtoll +#define __INT NUM_TYPE +#undef __INT_MIN +#undef __INT_MAX +#ifdef LLONG_MAX +# define __INT_MIN LLONG_MIN +# define __INT_MAX LLONG_MAX +#elif defined(INT64_MAX) +# define __INT_MIN INT64_MIN +# define __INT_MAX INT64_MAX +#endif +#ifndef _DIAGASSERT +# define _DIAGASSERT(e) +#endif +#ifndef __UNCONST +# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif +#include "_strtol.h" +#endif + +#endif + +#if !defined(HAVE_STRTOL) +#define BCS_ONLY +#define _FUNCNAME strtol +#define __INT long +#undef __INT_MIN +#undef __INT_MAX +#define __INT_MIN LONG_MIN +#define __INT_MAX LONG_MAX +#ifndef _DIAGASSERT +# define _DIAGASSERT(e) +#endif +#ifndef __UNCONST +# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif +#include "_strtol.h" +#endif + +#if !defined(HAVE_STRTOUL) +#define BCS_ONLY +#define _FUNCNAME strtoul +#define __INT unsigned long +#undef __INT_MIN +#undef __INT_MAX +#define __INT_MIN 0 +#define __INT_MAX ULONG_MAX +#ifndef _DIAGASSERT +# define _DIAGASSERT(e) +#endif +#ifndef __UNCONST +# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif +#include "_strtol.h" +#endif diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c index 6e5148bba968..4542a2f9a2ed 100644 --- a/contrib/bmake/var.c +++ b/contrib/bmake/var.c @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $ */ +/* $NetBSD: var.c,v 1.973 2021/12/12 20:45:48 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -148,7 +148,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.973 2021/12/12 20:45:48 sjg Exp $"); /* * Variables are defined using one of the VAR=value assignments. Their @@ -326,7 +326,7 @@ GNode *SCOPE_INTERNAL; static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; -static const char *VarEvalMode_Name[] = { +static const char VarEvalMode_Name[][32] = { "parse-only", "eval", "eval-defined", @@ -504,6 +504,12 @@ Var_Delete(GNode *scope, const char *varname) DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); v = he->value; + if (v->inUse) { + Parse_Error(PARSE_FATAL, + "Cannot delete variable \"%s\" while it is used", + v->name.str); + return; + } if (v->exported) unsetenv(v->name.str); if (strcmp(v->name.str, MAKE_EXPORTED) == 0) @@ -856,15 +862,17 @@ GetVarnamesToUnexport(bool isEnv, const char *arg, } static void -UnexportVar(const char *varname, UnexportWhat what) +UnexportVar(Substring varname, UnexportWhat what) { - Var *v = VarFind(varname, SCOPE_GLOBAL, false); + Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false); if (v == NULL) { - DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname); + DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n", + (int)Substring_Length(varname), varname.start); return; } - DEBUG1(VAR, "Unexporting \"%s\"\n", varname); + DEBUG2(VAR, "Unexporting \"%.*s\"\n", + (int)Substring_Length(varname), varname.start); if (what != UNEXPORT_ENV && v->exported && !v->reexport) unsetenv(v->name.str); v->exported = false; @@ -888,17 +896,15 @@ static void UnexportVars(FStr *varnames, UnexportWhat what) { size_t i; - Words words; + SubstringWords words; if (what == UNEXPORT_ENV) ClearEnv(); - words = Str_Words(varnames->str, false); - for (i = 0; i < words.len; i++) { - const char *varname = words.words[i]; - UnexportVar(varname, what); - } - Words_Free(words); + words = Substring_Words(varnames->str, false); + for (i = 0; i < words.len; i++) + UnexportVar(words.words[i], what); + SubstringWords_Free(words); if (what != UNEXPORT_NAMED) Global_Delete(MAKE_EXPORTED); @@ -979,6 +985,12 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val, */ Var_Delete(SCOPE_GLOBAL, name); } + if (strcmp(name, ".SUFFIXES") == 0) { + /* special: treat as readOnly */ + DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n", + scope->name, name, val); + return; + } v = VarAdd(name, val, scope, flags); } else { if (v->readOnly && !(flags & VAR_SET_READONLY)) { @@ -1065,9 +1077,9 @@ Var_Set(GNode *scope, const char *name, const char *val) * Otherwise the new value overwrites and replaces the old value. * * Input: + * scope scope in which to set it * name name of the variable to set, is expanded once * val value to give to the variable - * scope scope in which to set it */ void Var_SetExpand(GNode *scope, const char *name, const char *val) @@ -1143,9 +1155,9 @@ Var_Append(GNode *scope, const char *name, const char *val) * concatenated, with a space in between. * * Input: + * scope scope in which this should occur * name name of the variable to modify, is expanded once * val string to append to it - * scope scope in which this should occur * * Notes: * Only if the variable is being sought in the global scope is the @@ -1204,8 +1216,8 @@ Var_Exists(GNode *scope, const char *name) * fallback scopes. * * Input: - * name Variable to find, is expanded once - * scope Scope in which to start search + * scope scope in which to start search + * name name of the variable to find, is expanded once */ bool Var_ExistsExpand(GNode *scope, const char *name) @@ -1230,8 +1242,8 @@ Var_ExistsExpand(GNode *scope, const char *name) * or the usual scopes. * * Input: - * name name to find, is not expanded any further * scope scope in which to search for it + * name name to find, is not expanded any further * * Results: * The value if the variable exists, NULL if it doesn't. @@ -1593,6 +1605,54 @@ VarREError(int reerr, const regex_t *pat, const char *str) free(errbuf); } +/* + * Replacement of regular expressions is not specified by POSIX, therefore + * re-implement it here. + */ +static void +RegexReplace(const char *replace, SepBuf *buf, const char *wp, + const regmatch_t *m, size_t nsub) +{ + const char *rp; + unsigned int n; + + for (rp = replace; *rp != '\0'; rp++) { + if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { + SepBuf_AddBytes(buf, rp + 1, 1); + rp++; + continue; + } + + if (*rp == '&') { + SepBuf_AddBytesBetween(buf, + wp + (size_t)m[0].rm_so, + wp + (size_t)m[0].rm_eo); + continue; + } + + if (*rp != '\\' || !ch_isdigit(rp[1])) { + SepBuf_AddBytes(buf, rp, 1); + continue; + } + + /* \0 to \9 backreference */ + n = rp[1] - '0'; + rp++; + + if (n >= nsub) { + Error("No subexpression \\%u", n); + } else if (m[n].rm_so == -1) { + if (opts.strict) { + Error("No match for subexpression \\%u", n); + } + } else { + SepBuf_AddBytesBetween(buf, + wp + (size_t)m[n].rm_so, + wp + (size_t)m[n].rm_eo); + } + } +} + struct ModifyWord_SubstRegexArgs { regex_t re; size_t nsub; @@ -1611,87 +1671,42 @@ ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data) struct ModifyWord_SubstRegexArgs *args = data; int xrv; const char *wp; - const char *rp; int flags = 0; regmatch_t m[10]; assert(word.end[0] == '\0'); /* assume null-terminated word */ wp = word.start; if (args->pflags.subOnce && args->matched) - goto nosub; + goto no_match; -tryagain: +again: xrv = regexec(&args->re, wp, args->nsub, m, flags); + if (xrv == 0) + goto ok; + if (xrv != REG_NOMATCH) + VarREError(xrv, &args->re, "Unexpected regex error"); +no_match: + SepBuf_AddStr(buf, wp); + return; - switch (xrv) { - case 0: - args->matched = true; - SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); +ok: + args->matched = true; + SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); - /* - * Replacement of regular expressions is not specified by - * POSIX, therefore implement it here. - */ + RegexReplace(args->replace, buf, wp, m, args->nsub); - for (rp = args->replace; *rp != '\0'; rp++) { - if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { - SepBuf_AddBytes(buf, rp + 1, 1); - rp++; - continue; - } - - if (*rp == '&') { - SepBuf_AddBytesBetween(buf, - wp + m[0].rm_so, wp + m[0].rm_eo); - continue; - } - - if (*rp != '\\' || !ch_isdigit(rp[1])) { - SepBuf_AddBytes(buf, rp, 1); - continue; - } - - { /* \0 to \9 backreference */ - size_t n = (size_t)(rp[1] - '0'); - rp++; - - if (n >= args->nsub) { - Error("No subexpression \\%u", - (unsigned)n); - } else if (m[n].rm_so == -1) { - if (opts.strict) { - Error( - "No match for subexpression \\%u", - (unsigned)n); - } - } else { - SepBuf_AddBytesBetween(buf, - wp + m[n].rm_so, wp + m[n].rm_eo); - } - } - } - - wp += m[0].rm_eo; - if (args->pflags.subGlobal) { - flags |= REG_NOTBOL; - if (m[0].rm_so == 0 && m[0].rm_eo == 0) { - SepBuf_AddBytes(buf, wp, 1); - wp++; - } - if (*wp != '\0') - goto tryagain; + wp += (size_t)m[0].rm_eo; + if (args->pflags.subGlobal) { + flags |= REG_NOTBOL; + if (m[0].rm_so == 0 && m[0].rm_eo == 0) { + SepBuf_AddBytes(buf, wp, 1); + wp++; } if (*wp != '\0') - SepBuf_AddStr(buf, wp); - break; - default: - VarREError(xrv, &args->re, "Unexpected regex error"); - /* FALLTHROUGH */ - case REG_NOMATCH: - nosub: - SepBuf_AddStr(buf, wp); - break; + goto again; } + if (*wp != '\0') + SepBuf_AddStr(buf, wp); } #endif @@ -1740,7 +1755,7 @@ static char * VarSelectWords(const char *str, int first, int last, char sep, bool oneBigWord) { - Words words; + SubstringWords words; int len, start, end, step; int i; @@ -1748,15 +1763,13 @@ VarSelectWords(const char *str, int first, int last, SepBuf_Init(&buf, sep); if (oneBigWord) { - /* fake what Str_Words() would do if there were only one word */ + /* fake what Substring_Words() would do */ words.len = 1; - words.words = bmake_malloc( - (words.len + 1) * sizeof(words.words[0])); - words.freeIt = bmake_strdup(str); - words.words[0] = words.freeIt; - words.words[1] = NULL; + words.words = bmake_malloc(sizeof(words.words[0])); + words.freeIt = NULL; + words.words[0] = Substring_InitStr(str); /* no need to copy */ } else { - words = Str_Words(str, false); + words = Substring_Words(str, false); } /* @@ -1782,11 +1795,11 @@ VarSelectWords(const char *str, int first, int last, } for (i = start; (step < 0) == (i >= end); i += step) { - SepBuf_AddStr(&buf, words.words[i]); + SepBuf_AddSubstring(&buf, words.words[i]); SepBuf_Sep(&buf); } - Words_Free(words); + SubstringWords_Free(words); return SepBuf_DoneData(&buf); } @@ -1814,7 +1827,7 @@ ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) static char * -Words_JoinFree(Words words) +SubstringWords_JoinFree(SubstringWords words) { Buffer buf; size_t i; @@ -1826,10 +1839,11 @@ Words_JoinFree(Words words) /* XXX: Use ch->sep instead of ' ', for consistency. */ Buf_AddByte(&buf, ' '); } - Buf_AddStr(&buf, words.words[i]); + Buf_AddBytesBetween(&buf, + words.words[i].start, words.words[i].end); } - Words_Free(words); + SubstringWords_Free(words); return Buf_DoneData(&buf); } @@ -1980,7 +1994,7 @@ VarStrftime(const char *fmt, bool zulu, time_t tim) * After parsing, the modifier is evaluated. The side effects from evaluating * nested variable expressions in the modifier text often already happen * during parsing though. For most modifiers this doesn't matter since their - * only noticeable effect is that the update the value of the expression. + * only noticeable effect is that they update the value of the expression. * Some modifiers such as ':sh' or '::=' have noticeable side effects though. * * Evaluating the modifier usually takes the current value of the variable @@ -2015,7 +2029,7 @@ typedef enum ExprDefined { DEF_DEFINED } ExprDefined; -static const char *const ExprDefined_Name[] = { +static const char ExprDefined_Name[][10] = { "regular", "undefined", "defined" @@ -2027,7 +2041,7 @@ static const char *const ExprDefined_Name[] = { #define const_member /* no const possible */ #endif -/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */ +/* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */ typedef struct Expr { const char *name; FStr value; @@ -2052,7 +2066,7 @@ typedef struct Expr { * Chain 2 ends at the ':' between ${IND1} and ${IND2}. * Chain 3 starts with all modifiers from ${IND2}. * Chain 3 ends at the ':' after ${IND2}. - * Chain 1 continues with the the 2 modifiers ':O' and ':u'. + * Chain 1 continues with the 2 modifiers ':O' and ':u'. * Chain 1 ends at the final '}' of the expression. * * After such a chain ends, its properties no longer have any effect. @@ -2085,6 +2099,18 @@ Expr_Define(Expr *expr) expr->defined = DEF_DEFINED; } +static const char * +Expr_Str(const Expr *expr) +{ + return expr->value.str; +} + +static SubstringWords +Expr_Words(const Expr *expr) +{ + return Substring_Words(Expr_Str(expr), false); +} + static void Expr_SetValue(Expr *expr, FStr value) { @@ -2143,6 +2169,63 @@ IsEscapedModifierPart(const char *p, char delim, return p[1] == '&' && subst != NULL; } +/* + * In a part of a modifier, parse a subexpression and evaluate it. + */ +static void +ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch, + VarEvalMode emode) +{ + const char *p = *pp; + FStr nested_val; + + (void)Var_Parse(&p, ch->expr->scope, + VarEvalMode_WithoutKeepDollar(emode), &nested_val); + /* TODO: handle errors */ + LazyBuf_AddStr(part, nested_val.str); + FStr_Done(&nested_val); + *pp = p; +} + +/* + * In a part of a modifier, parse a subexpression but don't evaluate it. + * + * XXX: This whole block is very similar to Var_Parse with VARE_PARSE_ONLY. + * There may be subtle edge cases though that are not yet covered in the unit + * tests and that are parsed differently, depending on whether they are + * evaluated or not. + * + * This subtle difference is not documented in the manual page, neither is + * the difference between parsing ':D' and ':M' documented. No code should + * ever depend on these details, but who knows. + */ +static void +ParseModifierPartDollar(const char **pp, LazyBuf *part) +{ + const char *p = *pp; + const char *start = *pp; + + if (p[1] == '(' || p[1] == '{') { + char startc = p[1]; + int endc = startc == '(' ? ')' : '}'; + int depth = 1; + + for (p += 2; *p != '\0' && depth > 0; p++) { + if (p[-1] != '\\') { + if (*p == startc) + depth++; + if (*p == endc) + depth--; + } + } + LazyBuf_AddBytesBetween(part, start, p); + *pp = p; + } else { + LazyBuf_Add(part, *start); + *pp = p + 1; + } +} + /* See ParseModifierPart for the documentation. */ static VarParseResult ParseModifierPartSubst( @@ -2164,88 +2247,26 @@ ParseModifierPartSubst( p = *pp; LazyBuf_Init(part, p); - /* - * Skim through until the matching delimiter is found; pick up - * variable expressions on the way. - */ while (*p != '\0' && *p != delim) { - const char *varstart; - if (IsEscapedModifierPart(p, delim, subst)) { LazyBuf_Add(part, p[1]); p += 2; - continue; - } - - if (*p != '$') { /* Unescaped, simple text */ + } else if (*p != '$') { /* Unescaped, simple text */ if (subst != NULL && *p == '&') LazyBuf_AddSubstring(part, subst->lhs); else LazyBuf_Add(part, *p); p++; - continue; - } - - if (p[1] == delim) { /* Unescaped $ at end of pattern */ + } else if (p[1] == delim) { /* Unescaped '$' at end */ if (out_pflags != NULL) out_pflags->anchorEnd = true; else LazyBuf_Add(part, *p); p++; - continue; - } - - if (VarEvalMode_ShouldEval(emode)) { - /* Nested variable, evaluated */ - const char *nested_p = p; - FStr nested_val; - - (void)Var_Parse(&nested_p, ch->expr->scope, - VarEvalMode_WithoutKeepDollar(emode), &nested_val); - /* TODO: handle errors */ - LazyBuf_AddStr(part, nested_val.str); - FStr_Done(&nested_val); - p += nested_p - p; - continue; - } - - /* - * XXX: This whole block is very similar to Var_Parse without - * VARE_WANTRES. There may be subtle edge cases - * though that are not yet covered in the unit tests and that - * are parsed differently, depending on whether they are - * evaluated or not. - * - * This subtle difference is not documented in the manual - * page, neither is the difference between parsing :D and - * :M documented. No code should ever depend on these - * details, but who knows. - */ - - varstart = p; /* Nested variable, only parsed */ - if (p[1] == '(' || p[1] == '{') { - /* - * Find the end of this variable reference - * and suck it in without further ado. - * It will be interpreted later. - */ - char startc = p[1]; - int endc = startc == '(' ? ')' : '}'; - int depth = 1; - - for (p += 2; *p != '\0' && depth > 0; p++) { - if (p[-1] != '\\') { - if (*p == startc) - depth++; - if (*p == endc) - depth--; - } - } - LazyBuf_AddBytesBetween(part, varstart, p); - } else { - LazyBuf_Add(part, *varstart); - p++; - } + } else if (VarEvalMode_ShouldEval(emode)) + ParseModifierPartExpr(&p, part, ch, emode); + else + ParseModifierPartDollar(&p, part); } if (*p != delim) { @@ -2388,7 +2409,7 @@ ModifyWords(ModChain *ch, bool oneBigWord) { Expr *expr = ch->expr; - const char *val = expr->value.str; + const char *val = Expr_Str(expr); SepBuf result; SubstringWords words; size_t i; @@ -2404,8 +2425,8 @@ ModifyWords(ModChain *ch, words = Substring_Words(val, false); - DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n", - val, (unsigned)words.len); + DEBUG3(VAR, "ModifyWords: split \"%s\" into %u %s\n", + val, (unsigned)words.len, words.len != 1 ? "words" : "word"); SepBuf_Init(&result, ch->sep); for (i = 0; i < words.len; i++) { @@ -2442,7 +2463,7 @@ ApplyModifier_Loop(const char **pp, ModChain *ch) if (strchr(args.var, '$') != NULL) { Parse_Error(PARSE_FATAL, "In the :@ modifier of \"%s\", the variable name \"%s\" " - "must not contain a dollar.", + "must not contain a dollar", expr->name, args.var); return AMR_CLEANUP; } @@ -2569,6 +2590,7 @@ TryParseTime(const char **pp, time_t *out_time) static ApplyModifierResult ApplyModifier_Gmtime(const char **pp, ModChain *ch) { + Expr *expr; time_t utc; const char *mod = *pp; @@ -2579,7 +2601,7 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch) const char *p = mod + 7; if (!TryParseTime(&p, &utc)) { Parse_Error(PARSE_FATAL, - "Invalid time value: %s", mod + 7); + "Invalid time value at \"%s\"", mod + 7); return AMR_CLEANUP; } *pp = p; @@ -2588,9 +2610,10 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch) *pp = mod + 6; } - if (ModChain_ShouldEval(ch)) - Expr_SetValueOwn(ch->expr, - VarStrftime(ch->expr->value.str, true, utc)); + expr = ch->expr; + if (Expr_ShouldEval(expr)) + Expr_SetValueOwn(expr, + VarStrftime(Expr_Str(expr), true, utc)); return AMR_OK; } @@ -2599,6 +2622,7 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch) static ApplyModifierResult ApplyModifier_Localtime(const char **pp, ModChain *ch) { + Expr *expr; time_t utc; const char *mod = *pp; @@ -2609,7 +2633,7 @@ ApplyModifier_Localtime(const char **pp, ModChain *ch) const char *p = mod + 10; if (!TryParseTime(&p, &utc)) { Parse_Error(PARSE_FATAL, - "Invalid time value: %s", mod + 10); + "Invalid time value at \"%s\"", mod + 10); return AMR_CLEANUP; } *pp = p; @@ -2618,9 +2642,10 @@ ApplyModifier_Localtime(const char **pp, ModChain *ch) *pp = mod + 9; } - if (ModChain_ShouldEval(ch)) - Expr_SetValueOwn(ch->expr, - VarStrftime(ch->expr->value.str, false, utc)); + expr = ch->expr; + if (Expr_ShouldEval(expr)) + Expr_SetValueOwn(expr, + VarStrftime(Expr_Str(expr), false, utc)); return AMR_OK; } @@ -2634,7 +2659,7 @@ ApplyModifier_Hash(const char **pp, ModChain *ch) *pp += 4; if (ModChain_ShouldEval(ch)) - Expr_SetValueOwn(ch->expr, VarHash(ch->expr->value.str)); + Expr_SetValueOwn(ch->expr, VarHash(Expr_Str(ch->expr))); return AMR_OK; } @@ -2649,7 +2674,7 @@ ApplyModifier_Path(const char **pp, ModChain *ch) (*pp)++; - if (!ModChain_ShouldEval(ch)) + if (!Expr_ShouldEval(expr)) return AMR_OK; Expr_Define(expr); @@ -2733,9 +2758,9 @@ ApplyModifier_Range(const char **pp, ModChain *ch) return AMR_OK; if (n == 0) { - Words words = Str_Words(ch->expr->value.str, false); + SubstringWords words = Expr_Words(ch->expr); n = words.len; - Words_Free(words); + SubstringWords_Free(words); } Buf_Init(&buf); @@ -3003,7 +3028,7 @@ ApplyModifier_Quote(const char **pp, ModChain *ch) if (!ModChain_ShouldEval(ch)) return AMR_OK; - VarQuote(ch->expr->value.str, quoteDollar, &buf); + VarQuote(Expr_Str(ch->expr), quoteDollar, &buf); if (buf.data != NULL) Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf)); else @@ -3081,7 +3106,7 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch) if (!TryParseChar(&p, base, &ch->sep)) { Parse_Error(PARSE_FATAL, - "Invalid character number: %s", p); + "Invalid character number at \"%s\"", p); return AMR_CLEANUP; } if (!IsDelimiter(*p, ch)) { @@ -3154,15 +3179,15 @@ ApplyModifier_To(const char **pp, ModChain *ch) if (mod[1] == 'u') { /* :tu */ *pp = mod + 2; - if (ModChain_ShouldEval(ch)) - Expr_SetValueOwn(expr, str_toupper(expr->value.str)); + if (Expr_ShouldEval(expr)) + Expr_SetValueOwn(expr, str_toupper(Expr_Str(expr))); return AMR_OK; } if (mod[1] == 'l') { /* :tl */ *pp = mod + 2; - if (ModChain_ShouldEval(ch)) - Expr_SetValueOwn(expr, str_tolower(expr->value.str)); + if (Expr_ShouldEval(expr)) + Expr_SetValueOwn(expr, str_tolower(Expr_Str(expr))); return AMR_OK; } @@ -3211,9 +3236,9 @@ ApplyModifier_Words(const char **pp, ModChain *ch) } else { Buffer buf; - Words words = Str_Words(expr->value.str, false); + SubstringWords words = Expr_Words(expr); size_t ac = words.len; - Words_Free(words); + SubstringWords_Free(words); /* 3 digits + '\0' is usually enough */ Buf_InitSize(&buf, 4); @@ -3267,7 +3292,7 @@ ApplyModifier_Words(const char **pp, ModChain *ch) /* Normal case: select the words described by first and last. */ Expr_SetValueOwn(expr, - VarSelectWords(expr->value.str, first, last, + VarSelectWords(Expr_Str(expr), first, last, ch->sep, ch->oneBigWord)); ok: @@ -3279,62 +3304,140 @@ ApplyModifier_Words(const char **pp, ModChain *ch) return AMR_BAD; } -static int -str_cmp_asc(const void *a, const void *b) +#ifndef NUM_TYPE +# ifdef HAVE_LONG_LONG_INT +# define NUM_TYPE long long +# elif defined(_INT64_T_DECLARED) || defined(int64_t) +# define NUM_TYPE int64_t +# else +# define NUM_TYPE long +# define strtoll strtol +# endif +#endif + +static NUM_TYPE +num_val(Substring s) { - return strcmp(*(const char *const *)a, *(const char *const *)b); + NUM_TYPE val; + char *ep; + + val = strtoll(s.start, &ep, 0); + if (ep != s.start) { + switch (*ep) { + case 'K': + case 'k': + val <<= 10; + break; + case 'M': + case 'm': + val <<= 20; + break; + case 'G': + case 'g': + val <<= 30; + break; + } + } + return val; } static int -str_cmp_desc(const void *a, const void *b) +SubNumAsc(const void *sa, const void *sb) { - return strcmp(*(const char *const *)b, *(const char *const *)a); + NUM_TYPE a, b; + + a = num_val(*((const Substring *)sa)); + b = num_val(*((const Substring *)sb)); + return (a > b) ? 1 : (b > a) ? -1 : 0; +} + +static int +SubNumDesc(const void *sa, const void *sb) +{ + return SubNumAsc(sb, sa); +} + +static int +SubStrAsc(const void *sa, const void *sb) +{ + return strcmp( + ((const Substring *)sa)->start, ((const Substring *)sb)->start); +} + +static int +SubStrDesc(const void *sa, const void *sb) +{ + return SubStrAsc(sb, sa); } static void -ShuffleStrings(char **strs, size_t n) +ShuffleSubstrings(Substring *strs, size_t n) { size_t i; for (i = n - 1; i > 0; i--) { size_t rndidx = (size_t)random() % (i + 1); - char *t = strs[i]; + Substring t = strs[i]; strs[i] = strs[rndidx]; strs[rndidx] = t; } } -/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ +/* + * :O order ascending + * :Or order descending + * :Ox shuffle + * :On numeric ascending + * :Onr, :Orn numeric descending + */ static ApplyModifierResult ApplyModifier_Order(const char **pp, ModChain *ch) { - const char *mod = (*pp)++; /* skip past the 'O' in any case */ - Words words; - enum SortMode { - ASC, DESC, SHUFFLE - } mode; + const char *mod = *pp; + SubstringWords words; + int (*cmp)(const void *, const void *); - if (IsDelimiter(mod[1], ch)) { - mode = ASC; - } else if ((mod[1] == 'r' || mod[1] == 'x') && - IsDelimiter(mod[2], ch)) { + if (IsDelimiter(mod[1], ch) || mod[1] == '\0') { + cmp = SubStrAsc; (*pp)++; - mode = mod[1] == 'r' ? DESC : SHUFFLE; - } else - return AMR_BAD; + } else if (IsDelimiter(mod[2], ch) || mod[2] == '\0') { + if (mod[1] == 'n') + cmp = SubNumAsc; + else if (mod[1] == 'r') + cmp = SubStrDesc; + else if (mod[1] == 'x') + cmp = NULL; + else + goto bad; + *pp += 2; + } else if (IsDelimiter(mod[3], ch) || mod[3] == '\0') { + if ((mod[1] == 'n' && mod[2] == 'r') || + (mod[1] == 'r' && mod[2] == 'n')) + cmp = SubNumDesc; + else + goto bad; + *pp += 3; + } else { + goto bad; + } if (!ModChain_ShouldEval(ch)) return AMR_OK; - words = Str_Words(ch->expr->value.str, false); - if (mode == SHUFFLE) - ShuffleStrings(words.words, words.len); - else - qsort(words.words, words.len, sizeof words.words[0], - mode == ASC ? str_cmp_asc : str_cmp_desc); - Expr_SetValueOwn(ch->expr, Words_JoinFree(words)); + words = Expr_Words(ch->expr); + if (cmp == NULL) + ShuffleSubstrings(words.words, words.len); + else { + assert(words.words[0].end[0] == '\0'); + qsort(words.words, words.len, sizeof(words.words[0]), cmp); + } + Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words)); return AMR_OK; + +bad: + (*pp)++; + return AMR_BAD; } /* :? then : else */ @@ -3350,7 +3453,7 @@ ApplyModifier_IfElse(const char **pp, ModChain *ch) VarEvalMode then_emode = VARE_PARSE_ONLY; VarEvalMode else_emode = VARE_PARSE_ONLY; - int cond_rc = COND_PARSE; /* anything other than COND_INVALID */ + CondEvalResult cond_rc = COND_PARSE; /* just not COND_INVALID */ if (Expr_ShouldEval(expr)) { cond_rc = Cond_EvalCondition(expr->name, &value); if (cond_rc != COND_INVALID && value) @@ -3377,10 +3480,12 @@ ApplyModifier_IfElse(const char **pp, ModChain *ch) if (cond_rc == COND_INVALID) { Error("Bad conditional expression '%s' in '%s?%s:%s'", expr->name, expr->name, then_expr.str, else_expr.str); + FStr_Done(&then_expr); + FStr_Done(&else_expr); return AMR_CLEANUP; } - if (!ModChain_ShouldEval(ch)) { + if (!Expr_ShouldEval(expr)) { FStr_Done(&then_expr); FStr_Done(&else_expr); } else if (value) { @@ -3528,7 +3633,7 @@ ApplyModifier_Remember(const char **pp, ModChain *ch) *pp = mod + 1; if (Expr_ShouldEval(expr)) - Var_Set(expr->scope, name.str, expr->value.str); + Var_Set(expr->scope, name.str, Expr_Str(expr)); FStr_Done(&name); return AMR_OK; @@ -3556,7 +3661,7 @@ ApplyModifier_WordFunc(const char **pp, ModChain *ch, static ApplyModifierResult ApplyModifier_Unique(const char **pp, ModChain *ch) { - Words words; + SubstringWords words; if (!IsDelimiter((*pp)[1], ch)) return AMR_UNKNOWN; @@ -3565,14 +3670,14 @@ ApplyModifier_Unique(const char **pp, ModChain *ch) if (!ModChain_ShouldEval(ch)) return AMR_OK; - words = Str_Words(ch->expr->value.str, false); + words = Expr_Words(ch->expr); if (words.len > 1) { size_t si, di; di = 0; for (si = 1; si < words.len; si++) { - if (strcmp(words.words[si], words.words[di]) != 0) { + if (!Substring_Eq(words.words[si], words.words[di])) { di++; if (di != si) words.words[di] = words.words[si]; @@ -3581,7 +3686,7 @@ ApplyModifier_Unique(const char **pp, ModChain *ch) words.len = di + 1; } - Expr_SetValueOwn(ch->expr, Words_JoinFree(words)); + Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words)); return AMR_OK; } @@ -3637,7 +3742,7 @@ ApplyModifier_SysV(const char **pp, ModChain *ch) (*pp)--; /* Go back to the ch->endc. */ /* Do not turn an empty expression into non-empty. */ - if (lhsBuf.len == 0 && expr->value.str[0] == '\0') + if (lhsBuf.len == 0 && Expr_Str(expr)[0] == '\0') goto done; lhs = LazyBuf_Get(&lhsBuf); @@ -3671,9 +3776,9 @@ ApplyModifier_SunShell(const char **pp, ModChain *ch) if (Expr_ShouldEval(expr)) { const char *errfmt; - char *output = Cmd_Exec(expr->value.str, &errfmt); + char *output = Cmd_Exec(Expr_Str(expr), &errfmt); if (errfmt != NULL) - Error(errfmt, expr->value.str); + Error(errfmt, Expr_Str(expr)); Expr_SetValueOwn(expr, output); } @@ -3703,13 +3808,13 @@ LogBeforeApply(const ModChain *ch, const char *mod) debug_printf( "Evaluating modifier ${%s:%c%s} on value \"%s\"\n", expr->name, mod[0], is_single_char ? "" : "...", - expr->value.str); + Expr_Str(expr)); return; } debug_printf( "Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n", - expr->name, mod[0], is_single_char ? "" : "...", expr->value.str, + expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr), VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]); } @@ -3717,7 +3822,7 @@ static void LogAfterApply(const ModChain *ch, const char *p, const char *mod) { const Expr *expr = ch->expr; - const char *value = expr->value.str; + const char *value = Expr_Str(expr); const char *quot = value == var_Error ? "" : "\""; if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) && @@ -3757,6 +3862,7 @@ ApplyModifier(const char **pp, ModChain *ch) return ApplyModifier_Regex(pp, ch); #endif case 'D': + case 'U': return ApplyModifier_Defined(pp, ch); case 'E': return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix); @@ -3794,8 +3900,6 @@ ApplyModifier(const char **pp, ModChain *ch) return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail); case 't': return ApplyModifier_To(pp, ch); - case 'U': - return ApplyModifier_Defined(pp, ch); case 'u': return ApplyModifier_Unique(pp, ch); default: @@ -3848,7 +3952,7 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp) if (mods.str[0] != '\0') { const char *modsp = mods.str; ApplyModifiers(expr, &modsp, '\0', '\0'); - if (expr->value.str == var_Error || *modsp != '\0') { + if (Expr_Str(expr) == var_Error || *modsp != '\0') { FStr_Done(&mods); *pp = p; return AMIR_OUT; /* error already reported */ @@ -3916,7 +4020,7 @@ ApplySingleModifier(const char **pp, ModChain *ch) "modifier \"%.*s\" of variable \"%s\" with value \"%s\"", ch->endc, (int)(p - mod), mod, - ch->expr->name, ch->expr->value.str); + ch->expr->name, Expr_Str(ch->expr)); } else if (*p == ':') { p++; } else if (opts.strict && *p != '\0' && *p != ch->endc) { @@ -3964,7 +4068,7 @@ ApplyModifiers( assert(startc == '(' || startc == '{' || startc == '\0'); assert(endc == ')' || endc == '}' || endc == '\0'); - assert(expr->value.str != NULL); + assert(Expr_Str(expr) != NULL); p = *pp; @@ -4002,7 +4106,7 @@ ApplyModifiers( } *pp = p; - assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */ + assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */ return; bad_modifier: @@ -4284,6 +4388,7 @@ ParseVarnameLong( ) { LazyBuf varname; + Substring name; Var *v; bool haveModifier; bool dynamic = false; @@ -4294,13 +4399,13 @@ ParseVarnameLong( p += 2; /* skip "${" or "$(" or "y(" */ ParseVarname(&p, startc, endc, scope, emode, &varname); + name = LazyBuf_Get(&varname); if (*p == ':') { haveModifier = true; } else if (*p == endc) { haveModifier = false; } else { - Substring name = LazyBuf_Get(&varname); Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"", (int)Substring_Length(name), name.start); LazyBuf_Done(&varname); @@ -4310,14 +4415,18 @@ ParseVarnameLong( return false; } - v = VarFindSubstring(LazyBuf_Get(&varname), scope, true); + v = VarFindSubstring(name, scope, true); /* At this point, p points just after the variable name, * either at ':' or at endc. */ if (v == NULL) { - v = FindLocalLegacyVar(LazyBuf_Get(&varname), scope, - out_true_extraModifiers); + if (Substring_Equals(name, ".SUFFIXES")) + v = VarNew(Substring_Str(name), + Suff_NamesStr(), false, true); + else + v = FindLocalLegacyVar(name, scope, + out_true_extraModifiers); } if (v == NULL) { @@ -4325,14 +4434,14 @@ ParseVarnameLong( * Defer expansion of dynamic variables if they appear in * non-local scope since they are not defined there. */ - dynamic = VarnameIsDynamic(LazyBuf_Get(&varname)) && + dynamic = VarnameIsDynamic(name) && (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL); if (!haveModifier) { p++; /* skip endc */ *out_false_pp = p; *out_false_res = EvalUndefined(dynamic, start, p, - LazyBuf_Get(&varname), emode, out_false_val); + name, emode, out_false_val); return false; } @@ -4365,11 +4474,11 @@ ParseVarnameLong( /* Free the environment variable now since we own it. */ static void -FreeEnvVar(Var *v, FStr *inout_val) +FreeEnvVar(Var *v, Expr *expr) { char *varValue = Buf_DoneData(&v->val); - if (inout_val->str == varValue) - inout_val->freeIt = varValue; + if (expr->value.str == varValue) + expr->value.freeIt = varValue; else free(varValue); @@ -4434,9 +4543,9 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value) * * Input: * *pp The string to parse. - * When parsing a condition in ParseEmptyArg, it may also - * point to the "y" of "empty(VARNAME:Modifiers)", which - * is syntactically the same. + * In CondParser_FuncCallEmpty, it may also point to the + * "y" of "empty(VARNAME:Modifiers)", which is + * syntactically the same. * scope The scope for finding variables * emode Controls the exact details of parsing and evaluation * @@ -4535,14 +4644,14 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val) * Before applying any modifiers, expand any nested expressions from * the variable value. */ - if (strchr(expr.value.str, '$') != NULL && + if (strchr(Expr_Str(&expr), '$') != NULL && VarEvalMode_ShouldEval(emode)) { char *expanded; VarEvalMode nested_emode = emode; if (opts.strict) nested_emode = VarEvalMode_UndefOk(nested_emode); v->inUse = true; - (void)Var_Subst(expr.value.str, scope, nested_emode, + (void)Var_Subst(Expr_Str(&expr), scope, nested_emode, &expanded); v->inUse = false; /* TODO: handle errors */ @@ -4565,7 +4674,7 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val) *pp = p; if (v->fromEnv) { - FreeEnvVar(v, &expr.value); + FreeEnvVar(v, &expr); } else if (expr.defined != DEF_REGULAR) { if (expr.defined == DEF_UNDEF) { @@ -4732,6 +4841,14 @@ Var_Stats(void) HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); } +static int +StrAsc(const void *sa, const void *sb) +{ + return strcmp( + *((const char *const *)sa), *((const char *const *)sb)); +} + + /* Print all variables in a scope, sorted by name. */ void Var_Dump(GNode *scope) @@ -4748,7 +4865,7 @@ Var_Dump(GNode *scope) *(const char **)Vector_Push(&vec) = hi.entry->key; varnames = vec.items; - qsort(varnames, vec.len, sizeof varnames[0], str_cmp_asc); + qsort(varnames, vec.len, sizeof varnames[0], StrAsc); for (i = 0; i < vec.len; i++) { const char *varname = varnames[i];