Import bmake-20240108

Interesting/relevant changes since bmake-20230909

	* VERSION (_MAKE_VERSION): 20240106
	Merge with NetBSD make, pick up
	o fix duplicate progname when reporting an unknown target
	o unit tests for Cmd_Exec using temp file

	* VERSION (_MAKE_VERSION): 20240105
	Merge with NetBSD make, pick up
	o main.c: Cmd_Exec write cmd to a file if too big
	avoid blowing commandline/env limits

	* VERSION (_MAKE_VERSION): 20240101
	o util.c: flesh out more of strftime
	* configure.in: add --with-bmake-strftime
	it is not a full implementation but enough to pass all
	the unit-tests.
	* parse.c: LoadFile do not append \n to empty buffer.

	* VERSION (_MAKE_VERSION): 20231230
	Merge with NetBSD make, pick up
	o simplify memory allocation for string buffers
	o fix declared types of list nodes
	o suff.c: clean up freeing of suffixes
	o var.c: simplify debug message for the ':@var@...@' modifier
	clean up variable handling

	* VERSION (_MAKE_VERSION): 20231226
	Merge with NetBSD make, pick up
	o compat.c: ensure make's output is correctly ordered with that of
	the target when not going to a tty
	o main.c: check for shellPath whether to call Shell_Init()

	* VERSION (_MAKE_VERSION): 20231224
	Merge with NetBSD make, pick up
	o compat.c: check for shellPath whether to call Shell_Init()
	tweak the unit test to detect the bug thus fixed.
	o make.1: do not claim .SHELL is only used by jobs mode.

	* VERSION (_MAKE_VERSION): 20231220
	Merge with NetBSD make, pick up
	o str.c: speed up pattern matching in the ':M' modifier
	o var.c: fix confusing debug logging when deleting a variable
	use consistent debug messages style when ignoring variables

	* VERSION (_MAKE_VERSION): 20231210
	Merge with NetBSD make, pick up
	o var.c: avoid segfault on empty :C match expression
	explain in debug log why variable assignment is ignored.

	* VERSION (_MAKE_VERSION): 20231208
	Merge with NetBSD make, pick up
	o var.c: ensure fromCmd is set correctly for variables set on
	command line.

	* VERSION (_MAKE_VERSION): 20231124
	Merge with NetBSD make, pick up
	o main.c: cleanup processing of -j
	fix lint warning about strchr
	o var.c: more accurate error message for invalid ':mtime' argument
	cleanup :[...] modifier
	avoid reading beyond substring when comparing
	o unit-tests cover all cases of :mtime, test and explain exporting
	of variables
	o cleanup comments

	* bsd.after-import.mk (ECHO_TAG): FreeBSD no longer uses
	$FreeBSD$ tag, so avoid adding it.

mk/ChangeLog since bmake-20230909

	* dirdeps.mk: for MAKE_VERSION 20240105 we do not have the same
	limits on command line length, so skip export of lists to env.

	* jobs.mk: avoid C suffix in JOB_MAX_C if factor is floating
	point.  This keeps JOB_MAX numeric incase another makefile does
	comparisons.

	* gendirdeps.mk: if META_XTRAS is passed to us, add to META_FILES
This commit is contained in:
Simon J. Gerraty 2024-01-13 17:16:25 -08:00
parent 1012cf15f7
commit 7a05a7153a
181 changed files with 2598 additions and 2190 deletions

102
ChangeLog
View File

@ -1,3 +1,105 @@
2024-01-08 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20240108
Merge with NetBSD make, pick up
o miscellaneous cleanups
2024-01-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20240106
Merge with NetBSD make, pick up
o fix duplicate progname when reporting an unknown target
o unit tests for Cmd_Exec using temp file
2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20240105
Merge with NetBSD make, pick up
o main.c: Cmd_Exec write cmd to a file if too big
avoid blowing commandline/env limits
2024-01-02 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20240101
o util.c: flesh out more of strftime
* configure.in: add --with-bmake-strftime
it is not a full implementation but enough to pass all
the unit-tests.
* parse.c: LoadFile do not append \n to empty buffer.
2023-12-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231230
Merge with NetBSD make, pick up
o simplify memory allocation for string buffers
o fix declared types of list nodes
o suff.c: clean up freeing of suffixes
o var.c: simplify debug message for the ':@var@...@' modifier
clean up variable handling
2023-12-26 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231226
Merge with NetBSD make, pick up
o compat.c: ensure make's output is correctly ordered with that of
the target when not going to a tty
o main.c: check for shellPath whether to call Shell_Init()
2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231224
Merge with NetBSD make, pick up
o compat.c: check for shellPath whether to call Shell_Init()
tweak the unit test to detect the bug thus fixed.
o make.1: do not claim .SHELL is only used by jobs mode.
2023-12-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231220
Merge with NetBSD make, pick up
o str.c: speed up pattern matching in the ':M' modifier
o var.c: fix confusing debug logging when deleting a variable
use consistent debug messages style when ignoring variables
2023-12-10 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231210
Merge with NetBSD make, pick up
o var.c: avoid segfault on empty :C match expression
explain in debug log why variable assignment is ignored.
2023-12-08 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231208
Merge with NetBSD make, pick up
o var.c: ensure fromCmd is set correctly for variables set on
command line.
2023-11-26 Simon J Gerraty <sjg@beast.crufty.net>
* configure.in: disable generation of 'makefile' for
Darwin by default.
* boot-strap: docuement --without-makefile
2023-11-24 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20231124
Merge with NetBSD make, pick up
o main.c: cleanup processing of -j
fix lint warning about strchr
o var.c: more accurate error message for invalid ':mtime' argument
cleanup :[...] modifier
avoid reading beyond substring when comparing
o unit-tests cover all cases of :mtime, test and explain exporting
of variables
o cleanup comments
2023-09-17 Simon J Gerraty <sjg@beast.crufty.net>
* bsd.after-import.mk (ECHO_TAG): FreeBSD no longer uses
$FreeBSD$ tag, so avoid adding it.
2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230909

17
README
View File

@ -6,12 +6,12 @@ Since 1993 I have run it on AIX, BSDi, Darwin, FreeBSD, HP-UX, IRIX,
Linux, Minix, OSF, Solaris, SunOS and even UTS.
Others have run it on many more systems.
Currently each release is tested on NetBSD, FreeBSD, Solaris and Linux.
Currently each release is tested on Darwin, NetBSD, FreeBSD and Linux.
Since 2003 bmake switched to a date based version (first was 20030714)
which generally represents the date it was last merged with NetBSD's
make. Since then, NetBSD's make is imported within a week of any
interesting changes, so that bmake tracks it very closely.
*interesting* changes, so that bmake tracks it very closely.
Building
========
@ -33,20 +33,27 @@ the GNU standard process of::
./configure; make; make install
This will *not* work on Darwin or any other system with a case
insensitive filesystem. It depends on a generated ``makefile`` which
is disabled by default on Darwin.
To make much use of bmake you will need the bsd.*.mk macros or my
portable *.mk macros which are included with bmake since 20121212
and separately available from
http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
which will be links to the latest versions.
https://www.crufty.net/ftp/pub/sjg/mk.tar.gz
both that and
https://www.crufty.net/ftp/pub/sjg/bmake.tar.gz
will be links to the latest versions.
Porting
=======
If you encounter a system that bmake does not build or work on *out of
the box*, I welcome patches.
Even a report of unit tests which fail is appreciated.
If you can provide access to a suitable machine - even better.
More info can be found at http://www.crufty.net/help/sjg/bmake.htm
More info can be found at https://www.crufty.net/help/sjg/bmake.htm
--sjg <sjg@crufty.net>

View File

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

10
arch.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: arch.c,v 1.213 2023/02/14 21:08:00 rillig Exp $ */
/* $NetBSD: arch.c,v 1.214 2023/11/19 22:50:11 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.213 2023/02/14 21:08:00 rillig Exp $");
MAKE_RCSID("$NetBSD: arch.c,v 1.214 2023/11/19 22:50:11 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@ -253,7 +253,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
FStr lib; /* Library-part of specification */
FStr mem; /* Member-part of specification */
char saveChar; /* Ending delimiter of member-name */
bool expandLib; /* Whether the parsed lib contains variable
bool expandLib; /* Whether the parsed lib contains
* expressions that need to be expanded */
spec = *pp;
@ -262,7 +262,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
for (cp = lib.str; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
/* Expand nested variable expressions. */
/* Expand nested expressions. */
/* XXX: This code can probably be shortened. */
const char *nested_p = cp;
FStr result;
@ -299,7 +299,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
mem = FStr_InitRefer(cp);
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
if (*cp == '$') {
/* Expand nested variable expressions. */
/* Expand nested expressions. */
/*
* XXX: This code can probably be shortened.
*/

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.371 2023/09/10 21:52:36 rillig Exp $
.\" $NetBSD: make.1,v 1.372 2023/12/24 16:48:30 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 September 9, 2023
.Dd December 24, 2023
.Dt BMAKE 1
.Os
.Sh NAME
@ -2523,7 +2523,7 @@ set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
uses to execute commands in jobs mode.
uses to execute commands.
The sources are a set of
.Ar field\| Ns Cm \&= Ns Ar value
pairs.

View File

@ -1599,8 +1599,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
set the read-only attribute on the global variables specified as
sources.
.SHELL Sets the shell that bmake uses to execute commands in jobs mode.
The sources are a set of field=value pairs.
.SHELL Sets the shell that bmake uses to execute commands. The sources
are a set of field=value pairs.
name This is the minimal specification, used to select
one of the built-in shell specs; sh, ksh, and csh.
@ -1759,4 +1759,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
attempt to suppress a cascade of unnecessary errors, can result in a
seemingly unexplained `*** Error code 6'
FreeBSD 13.0 September 9, 2023 FreeBSD 13.0
FreeBSD 13.0 December 24, 2023 FreeBSD 13.0

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -82,9 +82,23 @@
#
# Possibly useful configure_args:
#
# --without-makefile
# do not generate 'makefile'.
#
# 'makefile' is used to enable the classic
# './configure; make; make install' dance, but on
# systems with case insensitive filesystems it can lead
# to infinite recursion.
#
# It is disabled by default on Darwin, and Cygwin.
#
# --without-meta
# disable use of meta mode.
#
# Even without filemon(9) meta mode is very useful
# both for debugging build and improving reliability of
# update builds.
#
# --without-filemon
# disable use of filemon(9) which is currently only
# available for NetBSD and FreeBSD.
@ -119,7 +133,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: boot-strap,v 1.58 2023/06/27 21:02:19 sjg Exp $
# $Id: boot-strap,v 1.59 2023/11/26 18:19:43 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#

View File

@ -1,4 +1,4 @@
# $Id: bsd.after-import.mk,v 1.17 2021/10/22 06:31:32 sjg Exp $
# $Id: bsd.after-import.mk,v 1.18 2023/09/18 05:29:23 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@ -68,18 +68,25 @@ MAKEFILE_SED = sed -e '/^MACHINE/d' \
# These are the simple files we want to capture
configured_files= config.h Makefile.config unit-tests/Makefile.config
# FreeBSD has dropped their tag with svn
.if ${HOST_OS:NFreeBSD} == ""
ECHO_TAG= :
.else
ECHO_TAG?= echo
.endif
after-import: bootstrap ${MAKEFILE}
.for f in ${configured_files:M*.[ch]}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
@(echo '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
@(${ECHO_TAG} '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
.endfor
.for f in ${configured_files:M*Makefile*}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
echo '#'; echo '# $$${HOST_OS}$$'; echo; \
echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; echo; \
echo 'SRCTOP?= $${.CURDIR:${${.CURDIR}/$f:L:H:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; echo; \
${MAKEFILE_SED} ${HOST_OS}/$f ) > ${.CURDIR}/$f
.endfor
@ -89,7 +96,7 @@ _makefile: bootstrap ${MAKEFILE}
@echo Generating ${.CURDIR}/Makefile
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
echo '#'; echo '# $$${HOST_OS}$$'; \
echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
echo; echo 'SRCTOP?= $${.CURDIR:${.CURDIR:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; \
echo; echo '# look here first for config.h'; \
echo 'CFLAGS+= -I$${.CURDIR}'; echo; \
@ -115,7 +122,7 @@ _utmakefile: bootstrap ${MAKEFILE}
@mkdir -p ${.CURDIR}/unit-tests
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
echo '#'; echo '# $$${HOST_OS}$$'; \
echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
${MAKEFILE_SED} \
-e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \
${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET}

6
buf.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.c,v 1.56 2023/06/01 07:44:10 rillig Exp $ */
/* $NetBSD: buf.c,v 1.57 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,13 +69,13 @@
* SUCH DAMAGE.
*/
/* Automatically-expanding null-terminated character buffers. */
/* Automatically growing null-terminated buffers of characters. */
#include <limits.h>
#include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: buf.c,v 1.56 2023/06/01 07:44:10 rillig Exp $");
MAKE_RCSID("$NetBSD: buf.c,v 1.57 2023/12/19 19:33:39 rillig Exp $");
/* Make space in the buffer for adding at least 16 more bytes. */
void

4
buf.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.h,v 1.48 2023/06/01 07:44:10 rillig Exp $ */
/* $NetBSD: buf.h,v 1.49 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -96,7 +96,7 @@ Buf_Clear(Buffer *buf)
buf->data[0] = '\0';
}
/* Buf_AddByte adds a single byte to a buffer. */
/* Adds a single byte to a buffer. */
MAKE_INLINE void
Buf_AddByte(Buffer *buf, char byte)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $ */
/* $NetBSD: compat.c,v 1.252 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -94,7 +94,7 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $");
MAKE_RCSID("$NetBSD: compat.c,v 1.252 2024/01/05 23:22:06 rillig Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
@ -110,10 +110,8 @@ CompatDeleteTarget(GNode *gn)
if (gn != NULL && !GNode_IsPrecious(gn) &&
(gn->type & OP_PHONY) == 0) {
const char *file = GNode_VarTarget(gn);
if (!opts.noExecute && unlink_file(file) == 0) {
if (!opts.noExecute && unlink_file(file) == 0)
Error("*** %s removed", file);
}
}
}
@ -132,14 +130,11 @@ CompatInterrupt(int signo)
CompatDeleteTarget(curTarg);
if (curTarg != NULL && !GNode_IsPrecious(curTarg)) {
/*
* Run .INTERRUPT only if hit with interrupt signal
*/
/* Run .INTERRUPT only if hit with interrupt signal. */
if (signo == SIGINT) {
GNode *gn = Targ_FindNode(".INTERRUPT");
if (gn != NULL) {
if (gn != NULL)
Compat_Make(gn, gn);
}
}
}
@ -280,11 +275,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
silent = !DEBUG(LOUD);
else if (*cmd == '-')
errCheck = false;
else if (*cmd == '+') {
else if (*cmd == '+')
doIt = true;
if (shellName == NULL) /* we came here from jobs */
Shell_Init();
} else if (!ch_isspace(*cmd))
else if (!ch_isspace(*cmd))
/* Ignore whitespace for compatibility with gnu make */
break;
cmd++;
@ -292,37 +285,25 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
while (ch_isspace(*cmd))
cmd++;
/*
* If we did not end up with a command, just skip it.
*/
if (cmd[0] == '\0')
return true;
useShell = UseShell(cmd);
/*
* Print the command before echoing if we're not supposed to be quiet
* for this one. We also print the command if -n given.
*/
if (!silent || !GNode_ShouldExecute(gn)) {
printf("%s\n", cmd);
fflush(stdout);
}
/*
* If we're not supposed to execute any commands, this is as far as
* we go...
*/
if (!doIt && !GNode_ShouldExecute(gn))
return true;
DEBUG1(JOB, "Execute: '%s'\n", cmd);
if (useShell && shellPath == NULL)
Shell_Init(); /* we need shellPath */
if (useShell) {
/*
* We need to pass the command off to the shell, typically
* because the command contains a "meta" character.
*/
static const char *shargv[5];
/* The following work for any of the builtin shell specs. */
@ -337,11 +318,6 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
bp = NULL;
mav = NULL;
} else {
/*
* No meta-characters, so no need to exec a shell. Break the
* command into words to form an argument vector we can
* execute.
*/
Words words = Str_Words(cmd, false);
mav = words.words;
bp = words.freeIt;
@ -380,24 +356,21 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
meta_compat_parent(cpid);
#endif
/*
* The child is off and running. Now all we can do is wait...
*/
/* The child is off and running. Now all we can do is wait... */
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
JobReapChild(retstat, reason, false); /* not ours? */
if (retstat == -1 && errno != EINTR) {
if (retstat == -1 && errno != EINTR)
break;
}
}
if (retstat < 0)
Fatal("error in wait: %d: %s", retstat, strerror(errno));
if (WIFSTOPPED(reason)) {
status = WSTOPSIG(reason); /* stopped */
status = WSTOPSIG(reason);
} else if (WIFEXITED(reason)) {
status = WEXITSTATUS(reason); /* exited */
status = WEXITSTATUS(reason);
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
if (useMeta)
meta_cmd_finish(NULL);
@ -408,7 +381,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf("*** Error code %d", status);
}
} else {
status = WTERMSIG(reason); /* signaled */
status = WTERMSIG(reason);
printf("*** Signal %d", status);
}
@ -439,6 +412,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf(" (ignored)\n");
status = 0;
}
fflush(stdout);
}
free(cmdStart);
@ -594,10 +568,6 @@ MakeUnmade(GNode *gn, GNode *pgn)
gn->type |= OP_SILENT;
if (Job_CheckCommands(gn, Fatal)) {
/*
* Our commands are ok, but we still have to worry about
* the -t flag.
*/
if (!opts.touch || (gn->type & OP_MAKE)) {
curTarg = gn;
#ifdef USE_META
@ -781,7 +751,6 @@ Compat_MakeAll(GNodeList *targs)
errorNode = gn;
}
/* If the user has defined a .END target, run its commands. */
if (errorNode == NULL) {
GNode *endNode = Targ_GetEndNode();
Compat_Make(endNode, endNode);

111
cond.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: cond.c,v 1.354 2023/08/11 04:56:31 rillig Exp $ */
/* $NetBSD: cond.c,v 1.359 2023/12/29 12:59:43 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -81,8 +81,7 @@
* of one of the .if directives or the condition in a
* ':?then:else' variable modifier.
*
* Cond_EndFile
* At the end of reading a makefile, ensure that the
* Cond_EndFile At the end of reading a makefile, ensure that the
* conditional directives are well-balanced.
*/
@ -92,7 +91,7 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: cond.c,v 1.354 2023/08/11 04:56:31 rillig Exp $");
MAKE_RCSID("$NetBSD: cond.c,v 1.359 2023/12/29 12:59:43 rillig Exp $");
/*
* Conditional expressions conform to this grammar:
@ -154,10 +153,10 @@ typedef struct CondParser {
* 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.
* been an expression or a plain word.
*
* In conditional directives like '.if', the left-hand side must
* either be a variable expression, a quoted string or a number.
* either be an expression, a quoted string or a number.
*/
bool leftUnquotedOK;
@ -166,9 +165,7 @@ typedef struct CondParser {
/*
* Whether an error message has already been printed for this
* condition. The first available error message is usually the most
* specific one, therefore it makes sense to suppress the standard
* "Malformed conditional" message.
* condition.
*/
bool printedError;
} CondParser;
@ -212,16 +209,16 @@ ParseWord(const char **pp, bool doEval)
{
const char *p = *pp;
Buffer word;
int paren_depth;
int depth;
Buf_InitSize(&word, 16);
Buf_Init(&word);
paren_depth = 0;
depth = 0;
for (;;) {
char ch = *p;
if (ch == '\0' || ch == ' ' || ch == '\t')
break;
if ((ch == '&' || ch == '|') && paren_depth == 0)
if ((ch == '&' || ch == '|') && depth == 0)
break;
if (ch == '$') {
VarEvalMode emode = doEval
@ -238,8 +235,8 @@ ParseWord(const char **pp, bool doEval)
continue;
}
if (ch == '(')
paren_depth++;
else if (ch == ')' && --paren_depth < 0)
depth++;
else if (ch == ')' && --depth < 0)
break;
Buf_AddByte(&word, ch);
p++;
@ -258,7 +255,7 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
const char *p = *pp;
char *res;
p++; /* Skip opening '(' - verified by caller */
p++; /* skip the '(' */
cpp_skip_hspace(&p);
res = ParseWord(&p, doEval);
cpp_skip_hspace(&p);
@ -383,7 +380,7 @@ is_separator(char ch)
}
/*
* In a quoted or unquoted string literal or a number, parse a variable
* In a quoted or unquoted string literal or a number, parse an
* expression and add its value to the buffer.
*
* Return whether to continue parsing the leaf.
@ -429,7 +426,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
}
/*
* Parse a string from a variable expression or an optionally quoted string,
* Parse a string from an expression or an optionally quoted string,
* on the left-hand and right-hand sides of comparisons.
*
* Results:
@ -487,10 +484,6 @@ CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
default:
if (!unquotedOK && !quoted && *start != '$' &&
!ch_isdigit(*start)) {
/*
* The left-hand side must be quoted,
* a variable expression or a number.
*/
str = FStr_InitRefer(NULL);
goto return_str;
}
@ -635,7 +628,6 @@ CondParser_Comparison(CondParser *par, bool doEval)
CondParser_SkipWhitespace(par);
if (!CondParser_ComparisonOp(par, &op)) {
/* Unknown operator, compare against an empty string or 0. */
t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted));
goto done_lhs;
}
@ -667,19 +659,19 @@ done_lhs:
static bool
CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
{
const char *cp = par->p;
const char *p = par->p;
Token tok;
FStr val;
if (!skip_string(&cp, "empty"))
if (!skip_string(&p, "empty"))
return false;
cpp_skip_whitespace(&cp);
if (*cp != '(')
cpp_skip_whitespace(&p);
if (*p != '(')
return false;
cp--; /* Make cp[1] point to the '('. */
val = Var_Parse(&cp, SCOPE_CMDLINE,
p--; /* Make p[1] point to the '('. */
val = Var_Parse(&p, SCOPE_CMDLINE,
doEval ? VARE_WANTRES : VARE_PARSE_ONLY);
/* TODO: handle errors */
@ -692,7 +684,7 @@ CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
FStr_Done(&val);
*out_token = tok;
par->p = cp;
par->p = p;
return true;
}
@ -734,7 +726,7 @@ CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
/*
* 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.
* operator, which is a number, an expression or a string literal.
*
* TODO: Can this be merged into CondParser_Comparison?
*/
@ -743,37 +735,36 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
{
Token t;
char *arg;
const char *cp;
const char *p;
/* Push anything numeric through the compare expression */
cp = par->p;
if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
p = par->p;
if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+')
return CondParser_Comparison(par, doEval);
/*
* Most likely we have a naked token to apply the default function to.
* However ".if a == b" gets here when the "a" is unquoted and doesn't
* start with a '$'. This surprises people.
* Most likely we have a bare word to apply the default function to.
* However, ".if a == b" gets here when the "a" is unquoted and
* doesn't start with a '$'. This surprises people.
* If what follows the function argument is a '=' or '!' then the
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
/*
* XXX: In edge cases, a variable expression may be evaluated twice,
* XXX: In edge cases, an expression may be evaluated twice,
* see cond-token-plain.mk, keyword 'twice'.
*/
arg = ParseWord(&cp, doEval);
arg = ParseWord(&p, doEval);
assert(arg[0] != '\0');
if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
if (*p == '=' || *p == '!' || *p == '<' || *p == '>')
return CondParser_Comparison(par, doEval);
par->p = cp;
par->p = p;
/*
* Evaluate the argument using the default function.
* This path always treats .if as .ifdef. To get here, the character
* after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion.
* be empty - even if it contained an expression.
*/
t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
free(arg);
@ -961,7 +952,7 @@ CondParser_Eval(CondParser *par)
}
/*
* Evaluate the condition, including any side effects from the variable
* Evaluate the condition, including any side effects from the
* expressions in the condition. The condition consists of &&, ||, !,
* function(arg), comparisons and parenthetical groupings thereof.
*/
@ -1034,12 +1025,6 @@ DetermineKindOfConditional(const char **pp, bool *out_plain,
return true;
unknown_directive:
/*
* TODO: Add error message about unknown directive, since there is no
* other known directive that starts with 'el' or 'if'.
*
* Example: .elifx 123
*/
return false;
}
@ -1068,7 +1053,7 @@ unknown_directive:
* conditional (when <cond> evaluates to true)
* CR_FALSE to skip the lines after the conditional
* (when <cond> evaluates to false, or when a previous
* branch has already been taken)
* branch was already taken)
* CR_ERROR if the conditional was not valid, either because of
* a syntax error or because some variable was undefined
* or because the condition could not be evaluated
@ -1115,7 +1100,7 @@ Cond_EvalLine(const char *line)
p++; /* skip the leading '.' */
cpp_skip_hspace(&p);
if (IsEndif(p)) { /* It is an '.endif'. */
if (IsEndif(p)) {
if (p[5] != '\0') {
Parse_Error(PARSE_FATAL,
"The .endif directive does not take arguments");
@ -1135,14 +1120,8 @@ Cond_EvalLine(const char *line)
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
if (p[0] == 'e') {
if (p[1] != 'l') {
/*
* Unknown directive. It might still be a
* transformation rule like '.err.txt',
* therefore no error message here.
*/
if (p[1] != 'l')
return CR_ERROR;
}
/* Quite likely this is 'else' or 'elif' */
p += 2;
@ -1176,13 +1155,8 @@ Cond_EvalLine(const char *line)
} else
isElif = false;
if (p[0] != 'i' || p[1] != 'f') {
/*
* Unknown directive. It might still be a transformation rule
* like '.elisp.scm', therefore no error message here.
*/
return CR_ERROR; /* Not an ifxxx or elifxxx line */
}
if (p[0] != 'i' || p[1] != 'f')
return CR_ERROR;
if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
return CR_ERROR;
@ -1219,16 +1193,11 @@ Cond_EvalLine(const char *line)
state = cond_states[cond_depth];
cond_depth++;
if (!(state & IFS_ACTIVE)) {
/*
* If we aren't parsing the data,
* treat as always false.
*/
cond_states[cond_depth] = IFS_WAS_ACTIVE;
return CR_FALSE;
}
}
/* And evaluate the conditional expression */
res = CondEvalExpression(p, plain, evalBare, negate, true, false);
if (res == CR_ERROR) {
/* Syntax error, error message already output. */

34
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for bmake 20230723.
# Generated by GNU Autoconf 2.71 for bmake 20240101.
#
# Report bugs to <sjg@NetBSD.org>.
#
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
PACKAGE_VERSION='20230723'
PACKAGE_STRING='bmake 20230723'
PACKAGE_VERSION='20240101'
PACKAGE_STRING='bmake 20240101'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
@ -724,6 +724,7 @@ with_defshell
with_makefile
with_meta
with_filemon
with_bmake_strftime
with_machine
with_force_machine
with_force_machine_arch
@ -1290,7 +1291,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 20230723 to adapt to many kinds of systems.
\`configure' configures bmake 20240101 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1352,7 +1353,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of bmake 20230723:";;
short | recursive ) echo "Configuration of bmake 20240101:";;
esac
cat <<\_ACEOF
@ -1374,6 +1375,7 @@ Optional Packages:
--without-makefile disable use of generated makefile
--without-meta disable use of meta-mode
--with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev
--with-bmake-strftime force use of bmake strftime
--with-machine=MACHINE explicitly set MACHINE
--with-force-machine=MACHINE set FORCE_MACHINE
--with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH
@ -1461,7 +1463,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
bmake configure 20230723
bmake configure 20240101
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@ -1968,7 +1970,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 20230723, which was
It was created by bmake $as_me 20240101, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@ -2783,7 +2785,7 @@ esac
fi
case "$OS" in
CYGWIN*|MINGW*) use_makefile=no;;
CYGWIN*|Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
@ -2846,6 +2848,15 @@ esac
fi
# Check whether --with-bmake_strftime was given.
if test ${with_bmake_strftime+y}
then :
withval=$with_bmake_strftime; case "${withval}" in
yes|no) bmake_strftime=$withval;;
esac
fi
case "$use_meta" in
yes)
case "$use_filemon" in
@ -4897,6 +4908,9 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
if test ${bmake_strftime:-no} = yes; then
CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
# if type does not work which(1) had better!
# note we cannot rely on type returning non-zero on failure
@ -7574,7 +7588,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 20230723, which was
This file was extended by bmake $as_me 20240101, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -7638,7 +7652,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
bmake config.status 20230723
bmake config.status 20240101
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

@ -1,11 +1,11 @@
dnl
dnl RCSid:
dnl $Id: configure.in,v 1.97 2023/07/25 20:12:32 sjg Exp $
dnl $Id: configure.in,v 1.101 2024/01/04 23:04:07 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ([2.71])
AC_INIT([bmake],[20230723],[sjg@NetBSD.org])
AC_INIT([bmake],[20240101],[sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@ -62,7 +62,7 @@ no) ;;
esac])
dnl
case "$OS" in
CYGWIN*|MINGW*) use_makefile=no;;
CYGWIN*|Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
AC_ARG_WITH(makefile,
@ -114,6 +114,13 @@ dev) ;;
*) filemon_h=no;;
esac
])
dnl some systems have broken/incomplete strftime
AC_ARG_WITH(bmake_strftime,
[ --with-bmake-strftime force use of bmake strftime],
[case "${withval}" in
yes|no) bmake_strftime=$withval;;
esac])
dnl
dnl echo "Note: use_meta=$use_meta use_filemon=$use_filemon filemon_h=$filemon_h" >&6
case "$use_meta" in
yes)
@ -189,6 +196,9 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
if test ${bmake_strftime:-no} = yes; then
CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
AC_SUBST(bmake_path_max)dnl
dnl

330
dir.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $ */
/* $NetBSD: dir.c,v 1.286 2023/12/29 18:53:24 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -83,7 +83,7 @@
*
* Dir_End Clean up the module.
*
* Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
* Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath.
*
* Dir_HasWildcards
* Returns true if the name given it needs to
@ -94,13 +94,12 @@
* from the search path.
*
* Dir_FindFile Searches for a file on a given search path.
* If it exists, the entire path is returned.
* Otherwise NULL is returned.
* If it exists, returns the entire path, otherwise NULL.
*
* Dir_FindHereOrAbove
* Search for a path in the current directory and
* then all the directories above it in turn until
* the path is found or we reach the root ("/").
* Search for a path in the current directory and then
* all the directories above it in turn, until the path
* is found or the root directory ("/") is reached.
*
* Dir_UpdateMTime
* Update the modification time and path of a node with
@ -114,11 +113,6 @@
* preceded by the command flag and all of them
* separated by a space.
*
* Dir_Destroy Destroy an element of a search path. Frees up all
* things that can be freed for the element as long
* as the element is no longer referenced by any other
* search path.
*
* SearchPath_Clear
* Resets a search path to the empty list.
*
@ -138,7 +132,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
MAKE_RCSID("$NetBSD: dir.c,v 1.286 2023/12/29 18:53:24 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@ -152,26 +146,21 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
* All previously-read directories are kept in openDirs, which is checked
* first before a directory is opened.
*
* The need for the caching of whole directories is brought about by the
* multi-level transformation code in suff.c, which tends to search for far
* more files than regular make does. In the initial implementation, the
* amount of time spent performing "stat" calls was truly astronomical.
* The problem with caching at the start is, of course, that pmake doesn't
* then detect changes to these directories during the course of the make.
* Three possibilities suggest themselves:
* This cache is used by the multi-level transformation code in suff.c, which
* tends to search for far more files than in regular explicit targets. After
* a directory has been cached, any later changes to that directory are not
* reflected in the cache. To keep the cache up to date, there are several
* ideas:
*
* 1) just use stat to test for a file's existence. As mentioned above,
* this is very inefficient due to the number of checks engendered by
* this is very inefficient due to the number of checks performed by
* the multi-level transformation code.
*
* 2) use readdir() and company to search the directories, keeping them
* open between checks. I have tried this and while it didn't slow down
* the process too much, it could severely affect the amount of
* parallelism available as each directory open would take another file
* descriptor out of play for handling I/O for another job. Given that
* it is only recently (as of 1993 or earlier) that UNIX OS's have taken
* to allowing more than 20 or 32 file descriptors for a process, this
* doesn't seem acceptable to me.
* 2) use readdir() to search the directories, keeping them open between
* checks. Around 1993 or earlier, this didn't slow down the process too
* much, but it consumed one file descriptor per open directory, which
* was critical on the then-current operating systems, as many limited
* the number of open file descriptors to 20 or 32.
*
* 3) record the mtime of the directory in the CachedDir structure and
* verify the directory hasn't changed since the contents were cached.
@ -184,9 +173,9 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
* number of reloadings and if the number goes over a (small) limit,
* resort to using stat in its place.
*
* An additional thing to consider is that pmake is used primarily to create
* C programs and until recently (as of 1993 or earlier) pcc-based compilers
* refused to allow you to specify where the resulting object file should be
* An additional thing to consider is that make is used primarily to create
* C programs and until recently (as of 1993 or earlier), pcc-based compilers
* didn't have an option to specify where the resulting object file should be
* placed. This forced all objects to be created in the current directory.
* This isn't meant as a full excuse, just an explanation of some of the
* reasons for the caching used here.
@ -212,7 +201,7 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
/* A cache for the filenames in a directory. */
struct CachedDir {
/*
* Name of directory, either absolute or relative to the current
* Name of the directory, either absolute or relative to the current
* directory. The name is not normalized in any way, that is, "."
* and "./." are different.
*
@ -238,8 +227,6 @@ struct CachedDir {
typedef List CachedDirList;
typedef ListNode CachedDirListNode;
typedef ListNode SearchPathNode;
/* A list of cached directories, with fast lookup by directory name. */
typedef struct OpenDirs {
CachedDirList list;
@ -273,8 +260,7 @@ static CachedDir *dotLast = NULL;
*
* XXX: If this is done way early, there's a chance other rules will have
* already updated the file, in which case we'll update it again. Generally,
* there won't be two rules to update a single file, so this should be ok,
* but...
* there won't be two rules to update a single file, so this should be ok.
*/
static HashTable mtimes;
@ -338,7 +324,7 @@ CachedDir_Unref(CachedDir *dir)
free(dir);
}
/* Update the value of the CachedDir variable, updating the reference counts. */
/* Update the value of 'var', updating the reference counts. */
static void
CachedDir_Assign(CachedDir **var, CachedDir *dir)
{
@ -475,9 +461,7 @@ Dir_Init(void)
CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST"));
}
/*
* Called by Dir_InitDir and whenever .CURDIR is assigned to.
*/
/* Called by Dir_InitDir and whenever .CURDIR is assigned to. */
void
Dir_InitCur(const char *newCurdir)
{
@ -487,7 +471,7 @@ Dir_InitCur(const char *newCurdir)
return;
/*
* Our build directory is not the same as our source directory.
* The build directory is not the same as the source directory.
* Keep this one around too.
*/
dir = SearchPath_Add(NULL, newCurdir);
@ -498,7 +482,7 @@ Dir_InitCur(const char *newCurdir)
}
/*
* (Re)initialize "dot" (current/object directory) path hash.
* (Re)initialize "dot" (the current/object directory).
* Some directories may be cached.
*/
void
@ -599,8 +583,6 @@ Dir_SetSYSPATH(void)
* XXX: This code is not 100% correct ([^]] fails etc.). I really don't think
* that make(1) should be expanding patterns, because then you have to set a
* mechanism for escaping the expansion!
*
* Return true if the word should be expanded, false otherwise.
*/
bool
Dir_HasWildcards(const char *name)
@ -637,20 +619,14 @@ Dir_HasWildcards(const char *name)
}
/*
* See if any files match the pattern and add their names to the 'expansions'
* list if they do.
* See if any files as seen from 'dir' match 'pattern', and add their names
* to 'expansions' if they do.
*
* This is incomplete -- wildcards are only expanded in the final path
* component, but not in directories like src/lib*c/file*.c, but it
* will do for now (now being 1993 until at least 2020). To expand these,
* Wildcards are only expanded in the final path component, but not in
* directories like src/lib*c/file*.c. To expand these wildcards,
* delegate the work to the shell, using the '!=' variable assignment
* operator, the ':sh' variable modifier or the ':!...!' variable modifier,
* such as in ${:!echo src/lib*c/file*.c!}.
*
* Input:
* pattern Pattern to look for
* dir Directory to search
* expansion Place to store the results
*/
static void
DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
@ -696,21 +672,18 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
}
}
/*
* Find the next closing brace in the string, taking nested braces into
* account.
*/
/* Find the next closing brace in 'p', taking nested braces into account. */
static const char *
closing_brace(const char *p)
{
int nest = 0;
int depth = 0;
while (*p != '\0') {
if (*p == '}' && nest == 0)
if (*p == '}' && depth == 0)
break;
if (*p == '{')
nest++;
depth++;
if (*p == '}')
nest--;
depth--;
p++;
}
return p;
@ -723,14 +696,14 @@ closing_brace(const char *p)
static const char *
separator_comma(const char *p)
{
int nest = 0;
int depth = 0;
while (*p != '\0') {
if ((*p == '}' || *p == ',') && nest == 0)
if ((*p == '}' || *p == ',') && depth == 0)
break;
if (*p == '{')
nest++;
depth++;
if (*p == '}')
nest--;
depth--;
p++;
}
return p;
@ -787,7 +760,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
const char *prefix, *middle, *piece, *middle_end, *suffix;
size_t prefix_len, suffix_len;
/* Split the word into prefix '{' middle '}' suffix. */
/* Split the word into prefix, '{', middle, '}' and suffix. */
middle = brace + 1;
middle_end = closing_brace(middle);
@ -824,11 +797,11 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
}
/* Expand the pattern in each of the directories from the path. */
/* Expand 'pattern' in each of the directories from 'path'. */
static void
DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions)
{
SearchPathNode *ln;
CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
DirMatchFiles(pattern, dir, expansions);
@ -860,11 +833,6 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
SearchPath *partPath;
prefix = bmake_strsedup(pattern, wildcardComponent + 1);
/*
* XXX: Check the "the directory is added to the path" part.
* It is probably surprising that the directory before a
* wildcard gets added to the path.
*/
/*
* XXX: Only the first match of the prefix in the path is
* taken, any others are ignored. The expectation may be
@ -880,7 +848,7 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
* path contains ../Etc/Object and we're looking for Etc, it won't
* be found. Ah well. Probably not important.
*
* XXX: Check whether the above comment is still true.
* TODO: Check whether the above comment is still true.
*/
if (dirpath == NULL)
return;
@ -921,15 +889,9 @@ SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
goto done;
}
/* At this point, the pattern does not contain '{'. */
slash = strchr(pattern, '/');
if (slash == NULL) {
/* The pattern has no directory component. */
/* First the files in dot. */
DirMatchFiles(pattern, dot, expansions);
/* Then the files in every other directory on the path. */
DirExpandPath(pattern, path, expansions);
goto done;
}
@ -972,13 +934,13 @@ done:
}
/*
* Find if the file with the given name exists in the given path.
* Find if 'base' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
DirLookup(CachedDir *dir, const char *base)
{
char *file; /* the current filename to check */
char *file;
DEBUG1(DIR, " %s ...\n", dir->name);
@ -994,7 +956,7 @@ DirLookup(CachedDir *dir, const char *base)
/*
* Find if the file with the given name exists in the given directory.
* Find if 'name' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
@ -1016,12 +978,12 @@ DirLookupSubdir(CachedDir *dir, const char *name)
}
/*
* Find if the file with the given name exists in the given path.
* Return the freshly allocated path to the file, the empty string, or NULL.
* Returning the empty string means that the search should be terminated.
* Find if 'name' (which has basename 'base') exists in 'dir'.
* Return the freshly allocated path to the file, an empty string, or NULL.
* Returning an empty string means that the search should be terminated.
*/
static char *
DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
DirLookupAbs(CachedDir *dir, const char *name, const char *base)
{
const char *dnp; /* pointer into dir->name */
const char *np; /* pointer into name */
@ -1037,10 +999,10 @@ DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
for (dnp = dir->name, np = name;
*dnp != '\0' && *dnp == *np; dnp++, np++)
continue;
if (*dnp != '\0' || np != cp - 1)
if (*dnp != '\0' || np != base - 1)
return NULL;
if (!HashSet_Contains(&dir->files, cp)) {
if (!HashSet_Contains(&dir->files, base)) {
DEBUG0(DIR, " must be here but isn't -- returning\n");
return bmake_strdup(""); /* to terminate the search */
}
@ -1080,7 +1042,7 @@ static bool
FindFileRelative(SearchPath *path, bool seenDotLast,
const char *name, char **out_file)
{
SearchPathNode *ln;
CachedDirListNode *ln;
bool checkedDot = false;
char *file;
@ -1090,11 +1052,11 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
if (dot != NULL) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
goto found;
goto done;
}
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@ -1107,18 +1069,18 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
checkedDot = true;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
goto found;
goto done;
}
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
goto found;
goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
goto found;
goto done;
}
if (checkedDot) {
@ -1128,12 +1090,12 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
*/
DEBUG0(DIR, " Checked . already, returning NULL\n");
file = NULL;
goto found;
goto done;
}
return false;
found:
done:
*out_file = file;
return true;
}
@ -1143,18 +1105,8 @@ FindFileAbsolute(SearchPath *path, bool seenDotLast,
const char *name, const char *base, char **out_file)
{
char *file;
SearchPathNode *ln;
CachedDirListNode *ln;
/*
* For absolute names, compare directory path prefix against
* the the directory path of each member on the search path
* for an exact match. If we have an exact match on any member
* of the search path, use the cached contents of that member
* to lookup the final file component. If that lookup fails we
* can safely assume that the file does not exist at all.
* This is signified by DirLookupAbs() returning an empty
* string.
*/
DEBUG0(DIR, " Trying exact path matches...\n");
if (!seenDotLast && cur != NULL &&
@ -1187,13 +1139,6 @@ found:
/*
* Find the file with the given name along the given search path.
*
* If the file is found in a directory that is not on the path
* already (either 'name' is absolute or it is a relative path
* [ dir1/.../dirn/file ] which exists below one of the directories
* already on the search path), its directory is added to the end
* of the path, on the assumption that there will be more files in
* that directory later on. Sometimes this is true. Sometimes not.
*
* Input:
* name the file to find
* path the directories to search, or NULL
@ -1206,7 +1151,7 @@ Dir_FindFile(const char *name, SearchPath *path)
{
char *file; /* the current filename to check */
bool seenDotLast = false; /* true if we should search dot last */
struct cached_stat cst; /* Buffer for stat, if necessary */
struct cached_stat cst;
const char *trailing_dot = ".";
const char *base = str_basename(name);
@ -1233,21 +1178,19 @@ Dir_FindFile(const char *name, SearchPath *path)
* of each of the directories on the search path.
*/
if (base == name || (base - name == 2 && *name == '.')) {
SearchPathNode *ln;
CachedDirListNode *ln;
/*
* We look through all the directories on the path seeking one
* Look through all the directories on the path seeking one
* which contains the final component of the given name. If
* such a file is found, we concatenate the directory name
* and the final component and return the resulting string.
* If we don't find any such thing, we go on to phase two.
* such a file is found, return its pathname.
* If there is no such file, go on to phase two.
*
* No matter what, we always look for the file in the current
* directory before anywhere else (unless we found the magic
* DOTLAST path, in which case we search it last) and we *do
* not* add the ./ to it if it exists.
* No matter what, always look for the file in the current
* directory before anywhere else (unless the path contains
* the magic '.DOTLAST', in which case search it last).
* This is so there are no conflicts between what the user
* specifies (fish.c) and what pmake finds (./fish.c).
* specifies (fish.c) and what make finds (./fish.c).
*/
if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)
return file;
@ -1264,30 +1207,14 @@ Dir_FindFile(const char *name, SearchPath *path)
return file;
}
/*
* We didn't find the file on any directory in the search path.
* If the name doesn't contain a slash, that means it doesn't exist.
* If it *does* contain a slash, however, there is still hope: it
* could be in a subdirectory of one of the members of the search
* path. (eg. /usr/include and sys/types.h. The above search would
* fail to turn up types.h in /usr/include, but it *is* in
* /usr/include/sys/types.h).
* [ This no longer applies: If we find such a file, we assume there
* will be more (what else can we assume?) and add all but the last
* component of the resulting name onto the search path (at the
* end).]
* This phase is only performed if the file is *not* absolute.
*/
if (base == name) {
DEBUG0(DIR, " failed.\n");
misses++;
return NULL;
}
if (*base == '\0') {
/* we were given a trailing "/" */
base = trailing_dot;
}
if (*base == '\0')
base = trailing_dot; /* we were given a trailing "/" */
if (name[0] != '/') {
if (FindFileRelative(path, seenDotLast, name, &file))
@ -1298,16 +1225,7 @@ Dir_FindFile(const char *name, SearchPath *path)
}
/*
* Didn't find it that way, either. Sigh. Phase 3. Add its directory
* onto the search path in any case, just in case, then look for the
* thing in the hash table. If we find it, grand. We return a new
* copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
* Note that if the directory holding the file doesn't exist, this
* will do an extra search of the final directory on the path. Unless
* something weird happens, this search won't succeed and life will
* be groovy.
*
* Sigh. We cannot add the directory onto the search path because
* We cannot add the directory onto the search path because
* of this amusing case:
* $(INSTALLDIR)/$(FILE): $(FILE)
*
@ -1315,75 +1233,40 @@ Dir_FindFile(const char *name, SearchPath *path)
* When searching for $(FILE), we will find it in $(INSTALLDIR)
* b/c we added it here. This is not good...
*/
#if 0
{
CachedDir *dir;
char *prefix;
if (base == trailing_dot) {
base = strrchr(name, '/');
base++;
}
prefix = bmake_strsedup(name, base - 1);
(void)SearchPath_Add(path, prefix);
free(prefix);
bigmisses++;
if (path->last == NULL)
return NULL;
dir = path->last->datum;
if (HashSet_Contains(&dir->files, base))
return bmake_strdup(name);
return NULL;
}
#else
DEBUG1(DIR, " Looking for \"%s\" ...\n", name);
bigmisses++;
if (cached_stat(name, &cst) == 0) {
if (cached_stat(name, &cst) == 0)
return bmake_strdup(name);
}
DEBUG0(DIR, " failed. Returning NULL\n");
return NULL;
#endif
}
/*
* Search for a path starting at a given directory and then working our way
* up towards the root.
*
* Input:
* here starting directory
* search_path the relative path we are looking for
*
* Results:
* The found path, or NULL.
* Search for 'needle' starting at the directory 'here' and then working our
* way up towards the root directory. Return the allocated path, or NULL.
*/
char *
Dir_FindHereOrAbove(const char *here, const char *search_path)
Dir_FindHereOrAbove(const char *here, const char *needle)
{
struct cached_stat cst;
char *dirbase, *dirbase_end;
char *try, *try_end;
/* copy out our starting point */
dirbase = bmake_strdup(here);
dirbase_end = dirbase + strlen(dirbase);
/* loop until we determine a result */
for (;;) {
/* try and stat(2) it ... */
try = str_concat3(dirbase, "/", search_path);
try = str_concat3(dirbase, "/", needle);
if (cached_stat(try, &cst) != -1) {
/*
* success! if we found a file, chop off
* the filename so we return a directory.
*/
if ((cst.cst_mode & S_IFMT) != S_IFDIR) {
/*
* Chop off the filename, to return a
* directory.
*/
try_end = try + strlen(try);
while (try_end > try && *try_end != '/')
try_end--;
@ -1396,16 +1279,10 @@ Dir_FindHereOrAbove(const char *here, const char *search_path)
}
free(try);
/*
* nope, we didn't find it. if we used up dirbase we've
* reached the root and failed.
*/
if (dirbase_end == dirbase)
break; /* failed! */
/*
* truncate dirbase from the end to move up a dir
*/
/* Truncate dirbase from the end to move up a dir. */
while (dirbase_end > dirbase && *dirbase_end != '/')
dirbase_end--;
*dirbase_end = '\0'; /* chop! */
@ -1476,10 +1353,10 @@ ResolveFullName(GNode *gn)
}
/*
* Search gn along dirSearchPath and store its modification time in gn->mtime.
* If no file is found, store 0 instead.
* Search 'gn' along 'dirSearchPath' and store its modification time in
* 'gn->mtime'. If no file is found, store 0 instead.
*
* The found file is stored in gn->path, unless the node already had a path.
* The found file is stored in 'gn->path', unless the node already had a path.
*/
void
Dir_UpdateMTime(GNode *gn, bool forceRefresh)
@ -1562,14 +1439,14 @@ CacheNewDir(const char *name, SearchPath *path)
}
/*
* Read the list of filenames in the directory and store the result
* in openDirs.
* Read the list of filenames in the directory 'name' and store the result
* in 'openDirs'.
*
* If a path is given, append the directory to that path.
* If a search path is given, append the directory to that path.
*
* Input:
* path The path to which the directory should be
* added, or NULL to only add the directory to openDirs
* added, or NULL to only add the directory to openDirs.
* name The name of the directory to add.
* The name is not normalized in any way.
* Output:
@ -1584,7 +1461,7 @@ SearchPath_Add(SearchPath *path, const char *name)
{
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
SearchPathNode *ln;
CachedDirListNode *ln;
/* XXX: Linear search gets slow with thousands of entries. */
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@ -1617,7 +1494,7 @@ SearchPath *
Dir_CopyDirSearchPath(void)
{
SearchPath *path = SearchPath_New();
SearchPathNode *ln;
CachedDirListNode *ln;
for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
Lst_Append(&path->dirs, CachedDir_Ref(dir));
@ -1628,22 +1505,14 @@ Dir_CopyDirSearchPath(void)
/*
* Make a string by taking all the directories in the given search path and
* preceding them by the given flag. Used by the suffix module to create
* variables for compilers based on suffix search paths.
*
* Input:
* flag flag which should precede each directory
* path list of directories
*
* Results:
* The string mentioned above. Note that there is no space between the
* given flag and each directory. The empty string is returned if things
* don't go well.
* variables for compilers based on suffix search paths. Note that there is no
* space between the given flag and each directory.
*/
char *
SearchPath_ToFlags(SearchPath *path, const char *flag)
{
Buffer buf;
SearchPathNode *ln;
CachedDirListNode *ln;
Buf_Init(&buf);
@ -1663,7 +1532,7 @@ SearchPath_ToFlags(SearchPath *path, const char *flag)
void
SearchPath_Free(SearchPath *path)
{
SearchPathNode *ln;
CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@ -1694,7 +1563,7 @@ SearchPath_Clear(SearchPath *path)
void
SearchPath_AddAll(SearchPath *dst, SearchPath *src)
{
SearchPathNode *ln;
CachedDirListNode *ln;
for (ln = src->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@ -1709,7 +1578,6 @@ percentage(int num, int den)
return den != 0 ? num * 100 / den : 0;
}
/********** DEBUG INFO **********/
void
Dir_PrintDirectories(void)
{
@ -1732,7 +1600,7 @@ Dir_PrintDirectories(void)
void
SearchPath_Print(const SearchPath *path)
{
SearchPathNode *ln;
CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
const CachedDir *dir = ln->datum;

14
for.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.176 2023/06/01 09:02:14 rillig Exp $ */
/* $NetBSD: for.c,v 1.177 2023/11/19 22:50:11 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@ -45,7 +45,7 @@
*
* 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
* body is scanned for 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.
*
@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.176 2023/06/01 09:02:14 rillig Exp $");
MAKE_RCSID("$NetBSD: for.c,v 1.177 2023/11/19 22:50:11 rillig Exp $");
typedef struct ForLoop {
@ -298,8 +298,8 @@ For_Accum(const char *line, int *forLevel)
/*
* When the body of a '.for i' loop is prepared for an iteration, each
* occurrence of $i in the body is replaced with ${:U...}, inserting the
* value of the item. If this item contains a '$', it may be the start of a
* variable expression. This expression is copied verbatim, its length is
* value of the item. If this item contains a '$', it may be the start of an
* expression. This expression is copied verbatim, its length is
* determined here, in a rather naive way, ignoring escape characters and
* funny delimiters in modifiers like ':S}from}to}'.
*/
@ -428,7 +428,7 @@ ForLoop_SubstVarLong(ForLoop *f, unsigned int firstItem, Buffer *body,
/*
* While expanding the body of a .for loop, replace single-character
* variable expressions like $i with their ${:U...} expansion.
* expressions like $i with their ${:U...} expansion.
*/
static void
ForLoop_SubstVarShort(ForLoop *f, unsigned int firstItem, Buffer *body,
@ -464,7 +464,7 @@ found:
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*
* Using variable expressions ensures that the .for loop can't generate
* Using expressions ensures that the .for loop can't generate
* syntax, and that the later parsing will still see an expression.
* This code assumes that the variable with the empty name is never defined,
* see unit-tests/varname-empty.mk.

28
hash.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.c,v 1.72 2022/02/09 21:09:24 rillig Exp $ */
/* $NetBSD: hash.c,v 1.74 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,12 +69,12 @@
* SUCH DAMAGE.
*/
/* Hash tables with string keys. */
/* Hash tables with string keys and pointer values. */
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: hash.c,v 1.72 2022/02/09 21:09:24 rillig Exp $");
MAKE_RCSID("$NetBSD: hash.c,v 1.74 2023/12/19 19:33:39 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@ -113,7 +113,7 @@ Hash_Substring(Substring key)
static HashEntry *
HashTable_Find(HashTable *t, Substring key, unsigned int h)
{
HashEntry *e;
HashEntry *he;
unsigned int chainlen = 0;
size_t keyLen = Substring_Length(key);
@ -122,18 +122,18 @@ HashTable_Find(HashTable *t, Substring key, unsigned int h)
t, h, (int)keyLen, key.start);
#endif
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
for (he = t->buckets[h & t->bucketsMask]; he != NULL; he = he->next) {
chainlen++;
if (e->key_hash == h &&
strncmp(e->key, key.start, keyLen) == 0 &&
e->key[keyLen] == '\0')
if (he->hash == h &&
strncmp(he->key, key.start, keyLen) == 0 &&
he->key[keyLen] == '\0')
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
return e;
return he;
}
/* Set up the hash table. */
@ -207,7 +207,7 @@ HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
/*
* Make the hash table larger. Any bucket numbers from the old table become
* invalid; the hash codes stay valid though.
* invalid; the hash values stay valid though.
*/
static void
HashTable_Enlarge(HashTable *t)
@ -226,8 +226,8 @@ HashTable_Enlarge(HashTable *t)
HashEntry *he = oldBuckets[i];
while (he != NULL) {
HashEntry *next = he->next;
he->next = newBuckets[he->key_hash & newMask];
newBuckets[he->key_hash & newMask] = he;
he->next = newBuckets[he->hash & newMask];
newBuckets[he->hash & newMask] = he;
he = next;
}
}
@ -264,7 +264,7 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
he = bmake_malloc(sizeof *he + (size_t)(keyEnd - key));
he->value = NULL;
he->key_hash = h;
he->hash = h;
memcpy(he->key, key, (size_t)(keyEnd - key) + 1);
he->next = t->buckets[h & t->bucketsMask];
@ -287,7 +287,7 @@ HashTable_Set(HashTable *t, const char *key, void *value)
void
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
{
HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
HashEntry **ref = &t->buckets[he->hash & t->bucketsMask];
HashEntry *p;
for (; (p = *ref) != NULL; ref = &p->next) {

21
hash.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.h,v 1.46 2022/01/31 22:58:26 rillig Exp $ */
/* $NetBSD: hash.h,v 1.48 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -72,7 +72,7 @@
* from: @(#)hash.h 8.1 (Berkeley) 6/6/93
*/
/* Hash tables with strings as keys and arbitrary pointers as values. */
/* Hash tables with string keys and pointer values. */
#ifndef MAKE_HASH_H
#define MAKE_HASH_H
@ -82,18 +82,17 @@ typedef struct HashEntry {
struct HashEntry *next; /* Used to link together all the entries
* associated with the same bucket. */
void *value;
unsigned int key_hash; /* hash value of the key */
unsigned int hash; /* hash value of the key */
char key[1]; /* key string, variable length */
} HashEntry;
/* The hash table containing the entries. */
typedef struct HashTable {
HashEntry **buckets; /* Pointers to HashEntry, one for each bucket
* in the table. */
HashEntry **buckets;
unsigned int bucketsSize;
unsigned int numEntries; /* Number of entries in the table. */
unsigned int numEntries;
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
unsigned int maxchain; /* max length of chain detected */
unsigned int maxchain; /* Maximum length of chain seen. */
} HashTable;
/* State of an iteration over all entries in a table. */
@ -109,15 +108,15 @@ typedef struct HashSet {
} HashSet;
MAKE_INLINE void * MAKE_ATTR_USE
HashEntry_Get(HashEntry *h)
HashEntry_Get(HashEntry *he)
{
return h->value;
return he->value;
}
MAKE_INLINE void
HashEntry_Set(HashEntry *h, void *datum)
HashEntry_Set(HashEntry *he, void *datum)
{
h->value = datum;
he->value = datum;
}
/* Set things up for iterating over all entries in the hash table. */

139
job.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: job.c,v 1.459 2023/02/15 06:52:58 rillig Exp $ */
/* $NetBSD: job.c,v 1.465 2024/01/07 11:39:04 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -70,12 +70,11 @@
*/
/*
* job.c --
* handle the creation etc. of our child processes.
* Create child processes and collect their output.
*
* Interface:
* Job_Init Called to initialize this module. In addition,
* the .BEGIN target is made including all of its
* the .BEGIN target is made, including all of its
* dependencies before this function returns.
* Hence, the makefiles must have been parsed
* before this function is called.
@ -155,7 +154,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: job.c,v 1.459 2023/02/15 06:52:58 rillig Exp $");
MAKE_RCSID("$NetBSD: job.c,v 1.465 2024/01/07 11:39:04 rillig Exp $");
/*
* A shell defines how the commands are run. All commands for a target are
@ -258,9 +257,7 @@ typedef struct ShellWriter {
} ShellWriter;
/*
* error handling variables
*/
/* error handling variables */
static int job_errors = 0; /* number of errors reported */
static enum { /* Why is the make aborting? */
ABORT_NONE,
@ -270,9 +267,7 @@ static enum { /* Why is the make aborting? */
} aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
/*
* this tracks the number of tokens currently "out" to build jobs.
*/
/* Tracks the number of tokens currently "out" to build jobs. */
int jobTokensRunning = 0;
typedef enum JobStartResult {
@ -301,15 +296,15 @@ typedef enum JobStartResult {
#define DEFSHELL_INDEX_SH 1
#define DEFSHELL_INDEX_KSH 2
#define DEFSHELL_INDEX_CSH 3
#else /* !DEFSHELL_CUSTOM */
#else
#define DEFSHELL_INDEX_SH 0
#define DEFSHELL_INDEX_KSH 1
#define DEFSHELL_INDEX_CSH 2
#endif /* !DEFSHELL_CUSTOM */
#endif
#ifndef DEFSHELL_INDEX
#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
#endif /* !DEFSHELL_INDEX */
#endif
static Shell shells[] = {
#ifdef DEFSHELL_CUSTOM
@ -425,7 +420,7 @@ static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
static Job *job_table; /* The structures that describe them */
static Job *job_table_end; /* job_table + maxJobs */
static unsigned int wantToken; /* we want a token */
static unsigned int wantToken;
static bool lurking_children = false;
static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */
@ -560,7 +555,7 @@ JobCreatePipe(Job *job, int minfd)
Punt("Cannot create pipe: %s", strerror(errno));
for (i = 0; i < 2; i++) {
/* Avoid using low numbered fds */
/* Avoid using low-numbered fds */
fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
if (fd != -1) {
close(pipe_fds[i]);
@ -571,7 +566,6 @@ JobCreatePipe(Job *job, int minfd)
job->inPipe = pipe_fds[0];
job->outPipe = pipe_fds[1];
/* Set close-on-exec flag for both */
if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
Punt("Cannot set close-on-exec: %s", strerror(errno));
if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
@ -694,10 +688,10 @@ JobPassSig_suspend(int signo)
/*
* We've been continued.
*
* A whole host of signals continue to happen!
* A whole host of signals is going to happen!
* SIGCHLD for any processes that actually suspended themselves.
* SIGCHLD for any processes that exited while we were alseep.
* The SIGCONT that actually caused us to wakeup.
* SIGCHLD for any processes that exited while we were asleep.
* The SIGCONT that actually caused us to wake up.
*
* Since we defer passing the SIGCONT on to our children until
* the main processing loop, we can be sure that all the SIGCHLD
@ -746,7 +740,7 @@ ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags)
else if (*p == '+')
out_cmdFlags->always = true;
else if (!ch_isspace(*p))
/* Ignore whitespace for compatibility with gnu make */
/* Ignore whitespace for compatibility with GNU make */
break;
p++;
}
@ -876,13 +870,9 @@ static void
JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run,
CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate)
{
if (!run) {
/*
* If there is no command to run, there is no need to switch
* error checking off and on again for nothing.
*/
if (!run)
inout_cmdFlags->ignerr = false;
} else if (shell->hasErrCtl)
else if (shell->hasErrCtl)
ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo);
else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') {
JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
@ -951,12 +941,10 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
escCmd = shell->hasErrCtl ? NULL : EscapeShellDblQuot(xcmd);
if (!cmdFlags.echo) {
if (job->echo && run && shell->hasEchoCtl) {
if (job->echo && run && shell->hasEchoCtl)
ShellWriter_EchoOff(wr);
} else {
if (shell->hasErrCtl)
cmdFlags.echo = true;
}
else if (shell->hasErrCtl)
cmdFlags.echo = true;
}
if (cmdFlags.ignerr) {
@ -1404,7 +1392,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
return false;
}
abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
abortProc("don't know how to make %s. Stop", gn->name);
return false;
}
@ -1431,7 +1419,7 @@ JobExec(Job *job, char **argv)
}
/*
* Some jobs produce no output and it's disconcerting to have
* Some jobs produce no output, and it's disconcerting to have
* no feedback of their running (since they produce no output, the
* banner with their name in it never appears). This is an attempt to
* provide that feedback, even if nothing follows it.
@ -1483,9 +1471,7 @@ JobExec(Job *job, char **argv)
execDie("lseek to 0", "stdin");
if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
/*
* Pass job token pipe to submakes.
*/
/* Pass job token pipe to submakes. */
if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
execDie("clear close-on-exec",
"tokenWaitJob.inPipe");
@ -1781,12 +1767,12 @@ JobStart(GNode *gn, bool special)
* itself.
*/
static char *
PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
PrintFilteredOutput(char *p, char *endp) /* XXX: should all be const */
{
char *ecp; /* XXX: should be const */
char *ep; /* XXX: should be const */
if (shell->noPrint == NULL || shell->noPrint[0] == '\0')
return cp;
return p;
/*
* XXX: What happens if shell->noPrint occurs on the boundary of
@ -1794,9 +1780,9 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* be a proper stream filter instead of doing string matching on
* selected chunks of the output.
*/
while ((ecp = strstr(cp, shell->noPrint)) != NULL) {
if (ecp != cp) {
*ecp = '\0'; /* XXX: avoid writing to the buffer */
while ((ep = strstr(p, shell->noPrint)) != NULL) {
if (ep != p) {
*ep = '\0'; /* XXX: avoid writing to the buffer */
/*
* The only way there wouldn't be a newline after
* this line is if it were the last in the buffer.
@ -1804,16 +1790,16 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* there must be a newline, so we don't print one.
*/
/* XXX: What about null bytes in the output? */
(void)fprintf(stdout, "%s", cp);
(void)fprintf(stdout, "%s", p);
(void)fflush(stdout);
}
cp = ecp + shell->noPrintLen;
if (cp == endp)
p = ep + shell->noPrintLen;
if (p == endp)
break;
cp++; /* skip over the (XXX: assumed) newline */
pp_skip_whitespace(&cp);
p++; /* skip over the (XXX: assumed) newline */
pp_skip_whitespace(&p);
}
return cp;
return p;
}
/*
@ -1908,7 +1894,7 @@ again:
*/
job->outBuf[i] = '\0';
if (i >= job->curPos) {
char *cp;
char *p;
/*
* FIXME: SwitchOutputTo should be here, according to
@ -1916,23 +1902,23 @@ again:
* do anything in the default shell, this bug has gone
* unnoticed until now.
*/
cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
p = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
/*
* There's still more in the output buffer. This time,
* though, we know there's no newline at the end, so
* we add one of our own free will.
*/
if (*cp != '\0') {
if (*p != '\0') {
if (!opts.silent)
SwitchOutputTo(job->node);
#ifdef USE_META
if (useMeta) {
meta_job_output(job, cp,
meta_job_output(job, p,
gotNL ? "\n" : "");
}
#endif
(void)fprintf(stdout, "%s%s", cp,
(void)fprintf(stdout, "%s%s", p,
gotNL ? "\n" : "");
(void)fflush(stdout);
}
@ -2212,11 +2198,10 @@ Shell_GetNewline(void)
void
Job_SetPrefix(void)
{
if (targPrefix != NULL) {
if (targPrefix != NULL)
free(targPrefix);
} else if (!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOB.PREFIX")) {
else if (!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOB.PREFIX"))
Global_Set(".MAKE.JOB.PREFIX", "---");
}
targPrefix = Var_Subst("${.MAKE.JOB.PREFIX}",
SCOPE_GLOBAL, VARE_WANTRES);
@ -2283,9 +2268,7 @@ Job_Init(void)
watchfd(&childExitJob);
sigemptyset(&caught_signals);
/*
* Install a SIGCHLD handler.
*/
/* Install a SIGCHLD handler. */
(void)bmake_signal(SIGCHLD, JobChildSig);
sigaddset(&caught_signals, SIGCHLD);
@ -2416,9 +2399,7 @@ Job_ParseShell(char *line)
memset(&newShell, 0, sizeof newShell);
/*
* Parse the specification by keyword
*/
/* Parse the specification by keyword. */
wordsList = Str_Words(line, true);
words = wordsList.words;
argc = wordsList.len;
@ -2513,25 +2494,9 @@ Job_ParseShell(char *line)
}
}
} else {
/*
* The user provided a path. If s/he gave nothing else
* (fullSpec is false), try and find a matching shell in the
* ones we know of. Else we just take the specification at
* its word and copy it to a new location. In either case,
* we need to record the path the user gave for the shell.
*/
shellPath = path;
path = strrchr(path, '/');
if (path == NULL) {
path = UNCONST(shellPath);
} else {
path++;
}
if (newShell.name != NULL) {
shellName = newShell.name;
} else {
shellName = path;
}
shellName = newShell.name != NULL ? newShell.name
: str_basename(path);
if (!fullSpec) {
if ((sh = FindShellByName(shellName)) == NULL) {
Parse_Error(PARSE_WARNING,
@ -2628,11 +2593,10 @@ Job_Finish(void)
GNode *endNode = Targ_GetEndNode();
if (!Lst_IsEmpty(&endNode->commands) ||
!Lst_IsEmpty(&endNode->children)) {
if (job_errors != 0) {
if (job_errors != 0)
Error("Errors reported so .END ignored");
} else {
else
JobRun(endNode);
}
}
return job_errors;
}
@ -2772,9 +2736,7 @@ clearfd(Job *job)
fdsLen--;
}
#endif
/*
* Move last job in table into hole made by dead job.
*/
/* Move last job in table into hole made by dead job. */
if (fdsLen != i) {
fds[i] = fds[fdsLen];
jobByFdIndex[i] = jobByFdIndex[fdsLen];
@ -2905,9 +2867,8 @@ Job_TokenWithdraw(void)
if (count == 0)
Fatal("eof on job pipe!");
if (count < 0 && jobTokensRunning != 0) {
if (errno != EAGAIN) {
if (errno != EAGAIN)
Fatal("job pipe read: %s", strerror(errno));
}
DEBUG1(JOB, "(%d) blocked for token\n", getpid());
wantToken = 1;
return false;

6
job.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: job.h,v 1.77 2021/12/15 12:58:01 rillig Exp $ */
/* $NetBSD: job.h,v 1.78 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -73,9 +73,7 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
/*
* Running of jobs in parallel mode.
*/
/* Run jobs in parallel mode. */
#ifndef MAKE_JOB_H
#define MAKE_JOB_H

22
lst.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.c,v 1.106 2022/02/26 11:57:21 rillig Exp $ */
/* $NetBSD: lst.c,v 1.107 2023/12/29 20:43:58 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -34,7 +34,7 @@
#include "make.h"
MAKE_RCSID("$NetBSD: lst.c,v 1.106 2022/02/26 11:57:21 rillig Exp $");
MAKE_RCSID("$NetBSD: lst.c,v 1.107 2023/12/29 20:43:58 rillig Exp $");
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@ -48,15 +48,6 @@ LstNodeNew(ListNode *prev, ListNode *next, void *datum)
return ln;
}
/* Create and initialize a new, empty list. */
List *
Lst_New(void)
{
List *list = bmake_malloc(sizeof *list);
Lst_Init(list);
return list;
}
void
Lst_Done(List *list)
{
@ -80,15 +71,6 @@ Lst_DoneCall(List *list, LstFreeProc freeProc)
}
}
/* Free a list and all its nodes. The node data are not freed though. */
void
Lst_Free(List *list)
{
Lst_Done(list);
free(list);
}
/* Insert a new node with the datum before the given node. */
void
Lst_InsertBefore(List *list, ListNode *ln, void *datum)

8
lst.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.h,v 1.103 2022/03/03 19:55:27 rillig Exp $ */
/* $NetBSD: lst.h,v 1.104 2023/12/29 20:43:58 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -106,16 +106,10 @@ struct List {
/* Free the datum of a node, called before freeing the node itself. */
typedef void LstFreeProc(void *);
/* Create or destroy a list */
/* Create a new list. */
List *Lst_New(void) MAKE_ATTR_USE;
/* Free the list nodes, but not the list itself. */
void Lst_Done(List *);
/* Free the list nodes, freeing the node data using the given function. */
void Lst_DoneCall(List *, LstFreeProc);
/* Free the list, leaving the node data unmodified. */
void Lst_Free(List *);
#define LST_INIT { NULL, NULL }

156
main.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $ */
/* $NetBSD: main.c,v 1.609 2024/01/07 01:33:57 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -111,8 +111,8 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
MAKE_RCSID("$NetBSD: main.c,v 1.609 2024/01/07 01:33:57 sjg Exp $");
#if defined(MAKE_NATIVE)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
"All rights reserved.");
@ -125,7 +125,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
CmdOpts opts;
time_t now; /* Time at start of make */
GNode *defaultNode; /* .DEFAULT node */
bool allPrecious; /* .PRECIOUS given on line by itself */
bool allPrecious; /* .PRECIOUS given on a line by itself */
bool deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */
@ -152,7 +152,7 @@ static HashTable cached_realpaths;
/*
* For compatibility with the POSIX version of MAKEFLAGS that includes
* all the options without '-', convert 'flags' to '-f -l -a -g -s'.
* all the options without '-', convert 'flags' to '-f -l -a -g -s '.
*/
static char *
explode(const char *flags)
@ -226,8 +226,7 @@ MainParseArgDebugFile(const char *arg)
opts.debug_file = fopen(fname, mode);
if (opts.debug_file == NULL) {
fprintf(stderr, "Cannot open debug file \"%s\"\n",
fname);
fprintf(stderr, "Cannot open debug file \"%s\"\n", fname);
exit(2);
}
free(fname);
@ -407,19 +406,19 @@ MainParseArgJobs(const char *arg)
forceJobs = true;
opts.maxJobs = (int)strtol(arg, &end, 0);
p = arg + (end - arg);
p = end;
#ifdef _SC_NPROCESSORS_ONLN
if (*p != '\0') {
double d;
if (*p == 'C') {
if (*p == 'C')
d = (opts.maxJobs > 0) ? opts.maxJobs : 1;
} else if (*p == '.') {
else if (*p == '.') {
d = strtod(arg, &end);
p = arg + (end - arg);
p = end;
} else
d = 0;
if (d > 0) {
d = 0.0;
if (d > 0.0) {
p = "";
opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN);
opts.maxJobs = (int)(d * (double)opts.maxJobs);
@ -443,8 +442,7 @@ MainParseArgJobs(const char *arg)
static void
MainParseArgSysInc(const char *argvalue)
{
/* look for magic parent directory search string */
if (strncmp(".../", argvalue, 4) == 0) {
if (strncmp(argvalue, ".../", 4) == 0) {
char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4);
if (found_path == NULL)
return;
@ -480,7 +478,7 @@ MainParseOption(char c, const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
break;
case 'I':
Parse_AddIncludeDir(argvalue);
SearchPath_Add(parseIncPath, argvalue);
Global_Append(MAKEFLAGS, "-I");
Global_Append(MAKEFLAGS, argvalue);
break;
@ -691,13 +689,13 @@ Main_ParseArgLine(const char *line)
{
Words words;
char *buf;
const char *p;
if (line == NULL)
return;
/* XXX: don't use line as an iterator variable */
for (; *line == ' '; line++)
for (p = line; *p == ' '; p++)
continue;
if (line[0] == '\0')
if (p[0] == '\0')
return;
#ifndef POSIX
@ -716,7 +714,7 @@ Main_ParseArgLine(const char *line)
#endif
{
FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE");
buf = str_concat3(argv0.str, " ", line);
buf = str_concat3(argv0.str, " ", p);
FStr_Done(&argv0);
}
@ -791,22 +789,17 @@ SetVarObjdir(bool writable, const char *var, const char *suffix)
}
/*
* Splits str into words, adding them to the list.
* Splits str into words (in-place, modifying it), adding them to the list.
* The string must be kept alive as long as the list.
*/
int
str2Lst_Append(StringList *lp, char *str)
void
AppendWords(StringList *lp, char *str)
{
char *cp;
int n;
char *p;
const char *sep = " \t";
for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) {
Lst_Append(lp, cp);
n++;
}
return n;
for (p = strtok(str, sep); p != NULL; p = strtok(NULL, sep))
Lst_Append(lp, p);
}
#ifdef SIGINFO
@ -1042,20 +1035,14 @@ InitVarMachineArch(void)
#ifndef NO_PWD_OVERRIDE
/*
* All this code is so that we know where we are when we start up
* on a different machine with pmake.
*
* XXX: Make no longer has "local" and "remote" mode. Is this code still
* necessary?
*
* Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX
* since the value of curdir can vary depending on how we got
* here. Ie sitting at a shell prompt (shell that provides $PWD)
* or via subdir.mk in which case its likely a shell which does
* here. That is, sitting at a shell prompt (shell that provides $PWD)
* or via subdir.mk, in which case it's likely a shell which does
* not provide it.
*
* So, to stop it breaking this case only, we ignore PWD if
* MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression.
* MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains an expression.
*/
static void
HandlePWD(const struct stat *curdir_st)
@ -1189,7 +1176,7 @@ static void
InitDefSysIncPath(char *syspath)
{
static char defsyspath[] = _PATH_DEFSYSPATH;
char *start, *cp;
char *start, *p;
/*
* If no user-supplied system path was given (through the -m option)
@ -1201,11 +1188,11 @@ InitDefSysIncPath(char *syspath)
else
syspath = bmake_strdup(syspath);
for (start = syspath; *start != '\0'; start = cp) {
for (cp = start; *cp != '\0' && *cp != ':'; cp++)
for (start = syspath; *start != '\0'; start = p) {
for (p = start; *p != '\0' && *p != ':'; p++)
continue;
if (*cp == ':')
*cp++ = '\0';
if (*p == ':')
*p++ = '\0';
/* look for magic parent directory search string */
if (strncmp(start, ".../", 4) == 0) {
@ -1296,17 +1283,17 @@ InitVpath(void)
/* TODO: handle errors */
path = vpath;
do {
char *cp;
char *p;
/* skip to end of directory */
for (cp = path; *cp != ':' && *cp != '\0'; cp++)
for (p = path; *p != ':' && *p != '\0'; p++)
continue;
/* Save terminator character so know when to stop */
savec = *cp;
*cp = '\0';
savec = *p;
*p = '\0';
/* Add directory to search path */
(void)SearchPath_Add(&dirSearchPath, path);
*cp = savec;
path = cp + 1;
*p = savec;
path = p + 1;
} while (savec == ':');
free(vpath);
}
@ -1332,7 +1319,7 @@ ReadFirstDefaultMakefile(void)
SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
(void)str2Lst_Append(&makefiles, prefs);
AppendWords(&makefiles, prefs);
for (ln = makefiles.first; ln != NULL; ln = ln->next)
if (ReadMakefile(ln->datum))
@ -1378,22 +1365,12 @@ main_Init(int argc, char **argv)
exit(2);
}
/*
* Get the name of this type of MACHINE from utsname
* so we can share an executable for similar machines.
* (i.e. m68k: amiga hp300, mac68k, sun3, ...)
*
* Note that both MACHINE and MACHINE_ARCH are decided at
* run-time.
*/
machine = InitVarMachine(&utsname);
machine_arch = InitVarMachineArch();
myPid = getpid(); /* remember this for vFork() */
/*
* Just in case MAKEOBJDIR wants us to do something tricky.
*/
/* Just in case MAKEOBJDIR wants us to do something tricky. */
Targ_Init();
Var_Init();
Global_Set_ReadOnly(".MAKE.OS", utsname.sysname);
@ -1402,7 +1379,7 @@ main_Init(int argc, char **argv)
#ifdef MAKE_VERSION
Global_Set("MAKE_VERSION", MAKE_VERSION);
#endif
Global_Set_ReadOnly(".newline", "\n"); /* handy for :@ loops */
Global_Set_ReadOnly(".newline", "\n");
#ifndef MAKEFILE_PREFERENCE_LIST
/* This is the traditional preference for makefiles. */
# define MAKEFILE_PREFERENCE_LIST "makefile Makefile"
@ -1757,16 +1734,40 @@ Cmd_Exec(const char *cmd, char **error)
Buffer buf; /* buffer to store the result */
ssize_t bytes_read;
char *output;
char *cp;
char *p;
int saved_errno;
char cmd_file[MAXPATHLEN];
size_t cmd_len;
int cmd_fd = -1;
if (shellName == NULL)
if (shellPath == NULL)
Shell_Init();
cmd_len = strlen(cmd);
if (cmd_len > 1000) {
cmd_fd = mkTempFile(NULL, cmd_file, sizeof(cmd_file));
if (cmd_fd >= 0) {
ssize_t n;
n = write(cmd_fd, cmd, cmd_len);
close(cmd_fd);
if (n < (ssize_t)cmd_len) {
unlink(cmd_file);
cmd_fd = -1;
}
}
}
args[0] = shellName;
args[1] = "-c";
args[2] = cmd;
args[3] = NULL;
if (cmd_fd >= 0) {
args[1] = cmd_file;
args[2] = NULL;
} else {
cmd_file[0] = '\0';
args[1] = "-c";
args[2] = cmd;
args[3] = NULL;
}
DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd);
if (pipe(pipefds) == -1) {
@ -1815,9 +1816,9 @@ Cmd_Exec(const char *cmd, char **error)
buf.data[buf.len - 1] = '\0';
output = Buf_DoneData(&buf);
for (cp = output; *cp != '\0'; cp++)
if (*cp == '\n')
*cp = ' ';
for (p = output; *p != '\0'; p++)
if (*p == '\n')
*p = ' ';
if (WIFSIGNALED(status))
*error = str_concat3("\"", cmd, "\" exited on a signal");
@ -1829,6 +1830,8 @@ Cmd_Exec(const char *cmd, char **error)
"Couldn't read shell's output for \"", cmd, "\"");
else
*error = NULL;
if (cmd_file[0] != '\0')
unlink(cmd_file);
return output;
}
@ -1880,6 +1883,7 @@ Fatal(const char *fmt, ...)
Job_Wait();
(void)fflush(stdout);
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
@ -2003,13 +2007,13 @@ execDie(const char *af, const char *av)
static void
purge_relative_cached_realpaths(void)
{
HashEntry *he, *nhe;
HashEntry *he, *next;
HashIter hi;
HashIter_Init(&hi, &cached_realpaths);
he = HashIter_Next(&hi);
while (he != NULL) {
nhe = HashIter_Next(&hi);
next = HashIter_Next(&hi);
if (he->key[0] != '/') {
DEBUG1(DIR, "cached_realpath: purging %s\n", he->key);
HashTable_DeleteEntry(&cached_realpaths, he);
@ -2018,7 +2022,7 @@ purge_relative_cached_realpaths(void)
* free them or document why they cannot be freed.
*/
}
he = nhe;
he = next;
}
}
@ -2183,7 +2187,7 @@ getTmpdir(void)
/*
* Create and open a temp file using "pattern".
* If out_fname is provided, set it to a copy of the filename created.
* If tfile is provided, set it to a copy of the filename created.
* Otherwise unlink the file once open.
*/
int

6
make.1
View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.371 2023/09/10 21:52:36 rillig Exp $
.\" $NetBSD: make.1,v 1.372 2023/12/24 16:48:30 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 September 9, 2023
.Dd December 24, 2023
.Dt MAKE 1
.Os
.Sh NAME
@ -2523,7 +2523,7 @@ set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
uses to execute commands in jobs mode.
uses to execute commands.
The sources are a set of
.Ar field\| Ns Cm \&= Ns Ar value
pairs.

28
make.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.c,v 1.259 2023/02/14 21:38:31 rillig Exp $ */
/* $NetBSD: make.c,v 1.262 2024/01/05 23:22:06 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.259 2023/02/14 21:38:31 rillig Exp $");
MAKE_RCSID("$NetBSD: make.c,v 1.262 2024/01/05 23:22:06 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@ -132,7 +132,7 @@ GNodeType_ToString(GNodeType type, void **freeIt)
{
Buffer buf;
Buf_InitSize(&buf, 32);
Buf_Init(&buf);
#define ADD(flag) Buf_AddFlag(&buf, (type & (flag)) != OP_NONE, #flag)
ADD(OP_DEPENDS);
ADD(OP_FORCE);
@ -174,7 +174,7 @@ GNodeFlags_ToString(GNodeFlags flags, void **freeIt)
{
Buffer buf;
Buf_InitSize(&buf, 32);
Buf_Init(&buf);
Buf_AddFlag(&buf, flags.remake, "REMAKE");
Buf_AddFlag(&buf, flags.childMade, "CHILDMADE");
Buf_AddFlag(&buf, flags.force, "FORCE");
@ -330,13 +330,12 @@ GNode_IsOODate(GNode *gn)
* out-of-date.
*/
if (DEBUG(MAKE)) {
if (gn->type & OP_FORCE) {
if (gn->type & OP_FORCE)
debug_printf("! operator...");
} else if (gn->type & OP_PHONY) {
else if (gn->type & OP_PHONY)
debug_printf(".PHONY node...");
} else {
else
debug_printf(".EXEC node...");
}
}
oodate = true;
} else if (IsOODateRegular(gn)) {
@ -440,11 +439,10 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
* We don't need to do this for commands.
* They get expanded properly when we execute.
*/
if (gn->uname == NULL) {
if (gn->uname == NULL)
gn->uname = gn->name;
} else {
else
free(gn->name);
}
gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES);
/* TODO: handle errors */
if (gn->uname != NULL && strcmp(gn->name, gn->uname) != 0) {
@ -546,9 +544,8 @@ Make_Recheck(GNode *gn)
* depend on FRC to be made, so we have to check for gn->children
* being empty as well.
*/
if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children))
gn->mtime = now;
}
#else
/*
* This is what Make does and it's actually a good thing, as it
@ -689,9 +686,8 @@ Make_Update(GNode *cgn)
* now -- some rules won't actually update the file. If the file
* still doesn't exist, make its mtime now.
*/
if (cgn->made != UPTODATE) {
if (cgn->made != UPTODATE)
mtime = Make_Recheck(cgn);
}
/*
* If this is a `::' node, we must consult its first instance
@ -1224,7 +1220,7 @@ MakePrintStatusList(GNodeList *gnodes, int *errors)
static void
ExamineLater(GNodeList *examine, GNodeList *toBeExamined)
{
ListNode *ln;
GNodeListNode *ln;
for (ln = toBeExamined->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;

5
make.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.325 2023/09/10 11:52:29 rillig Exp $ */
/* $NetBSD: make.h,v 1.327 2023/12/17 09:02:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -882,7 +882,6 @@ void PrintLocation(FILE *, bool, const GNode *);
void PrintStackTrace(bool);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE;
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_PushInput(const char *, unsigned, unsigned, Buffer,
struct ForLoop *);
@ -1064,7 +1063,7 @@ void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(bool);
bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE;
int str2Lst_Append(StringList *, char *);
void AppendWords(StringList *, char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE;

6
meta.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.206 2023/08/19 00:09:17 sjg Exp $ */
/* $NetBSD: meta.c,v 1.207 2023/12/17 09:02:26 rillig Exp $ */
/*
* Implement 'meta' mode.
@ -620,7 +620,7 @@ meta_mode_init(const char *make_mode)
metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
str2Lst_Append(&metaBailiwick, metaBailiwickStr);
AppendWords(&metaBailiwick, metaBailiwickStr);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
@ -629,7 +629,7 @@ meta_mode_init(const char *make_mode)
metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
AppendWords(&metaIgnorePaths, metaIgnorePathsStr);
/*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}

View File

@ -1,3 +1,45 @@
2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20240105
* dirdeps.mk: for MAKE_VERSION 20240105 we do not have the same
limits on command line length, so skip export of lists to env.
2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
* man.mk: add logic for staging man pages
2023-11-28 Simon J Gerraty <sjg@beast.crufty.net>
* jobs.mk: avoid C suffix in JOB_MAX_C if factor is floating
point. This keeps JOB_MAX numeric incase another makefile does
comparisons.
2023-11-04 Simon J Gerraty <sjg@beast.crufty.net>
* dpadd.mk: add support for DPLIBS_QUALIFIER_LIST
* gendirdeps.mk: if META_XTRAS is passed to us, add to META_FILES
2023-10-03 Simon J Gerraty <sjg@beast.crufty.net>
* compiler.mk (COMPILER_VERSION): clang at least is into
double digit major versions.
2023-10-02 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20231001
* set _CCLINK in init.mk so lib.mk can use it for default SHLIB_LD
* lib.mk (cleanlib): use LD_solink so we remove all the right files.
Use -Wl for -soname since we now default to linking with CC
We should not need SHLIB_LDSTARTFILE or SHLIB_LDENDFILE when linking
with CC.
2023-09-24 Simon J Gerraty <sjg@beast.crufty.net>
* init.mk (QUALIFIED_VAR_LIST): Add SRCS
2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
* jobs.mk (JOB_MAX): use -jC if we can

View File

@ -1,4 +1,4 @@
# $Id: compiler.mk,v 1.11 2022/09/09 17:44:29 sjg Exp $
# $Id: compiler.mk,v 1.12 2023/10/03 18:47:48 sjg Exp $
#
# @(#) Copyright (c) 2019, Simon J. Gerraty
#
@ -32,7 +32,7 @@ COMPILER_TYPE = gcc
.endif
.endif
.if empty(COMPILER_VERSION)
COMPILER_VERSION != echo "${_v:M[1-9].[0-9]*}:[1]" | \
COMPILER_VERSION != echo "${_v:M[1-9][0-9]*.[0-9]*}:[1]" | \
awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3;}'
.endif
.undef _v

View File

@ -1,4 +1,4 @@
# $Id: dirdeps.mk,v 1.165 2023/08/19 17:35:32 sjg Exp $
# $Id: dirdeps.mk,v 1.166 2024/01/05 23:16:34 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@ -754,7 +754,6 @@ _cache_script = echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}';
# guard against _new_dirdeps being too big for a single command line
_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,}
_cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,}
.export _cache_xtra_deps _new_dirdeps
.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
@ -804,15 +803,27 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q}
.if ${BUILD_DIRDEPS_CACHE} == "yes"
.if !empty(_build_dirs)
_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,}
# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
.if ${MAKE_VERSION} < 20240105
.if !empty(_cache_deps)
.export _cache_deps
_cache_script += for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done;
.endif
# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
.export _cache_xtra_deps _new_dirdeps
x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \
echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
.else
# we do not have the same limits on command lines
.if !empty(_cache_deps)
_cache_script += for x in ${_cache_deps}; do echo " _{SRCTOP}/$$x \\"; done;
.endif
x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
for x in ${_cache_xtra_deps}; do echo " _{SRCTOP}/$$x \\"; done; \
echo; for x in ${_new_dirdeps}; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
.endif
.endif
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}

View File

@ -1,6 +1,6 @@
# $Id: dpadd.mk,v 1.30 2021/12/08 05:56:50 sjg Exp $
# $Id: dpadd.mk,v 1.31 2023/11/25 01:07:49 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
# @(#) Copyright (c) 2004-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@ -75,6 +75,10 @@
# and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should
# have no need of anything else.
#
# Sometimes things are more complicated so allow for
# DPLIBS to be qualified with each of the variables in
# DPLIBS_QUALIFIER_LIST (default is VAR_QUALIFIER_LIST same as
# init.mk)
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@ -110,7 +114,9 @@ CXXFLAGS_LAST += ${CXXFLAGS_DEBUG_XTRA}
.-include <local.dpadd.mk>
# DPLIBS helps us ensure we keep DPADD and LDADD in sync
DPLIBS+= ${DPLIBS_LAST}
DPLIBS_QUALIFIER_LIST ?= ${VAR_QUALIFIER_LIST}
DPLIBS += ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS.$Q:U}@}
DPLIBS+= ${DPLIBS_LAST} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_LAST.$Q:U}@}
DPADD+= ${DPLIBS:N-*}
.for __lib in ${DPLIBS}
.if "${__lib:M-*}" != ""
@ -131,7 +137,7 @@ __dpadd_libs := ${DPADD:M*/lib*}
# dups will be dealt with later.
# Note: libfoo_pic uses DPLIBS_libfoo
__ldadd_all_xtras=
.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}}@}
.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_${d:T:R:S,_pic,,}.$Q:U}@}@}
__ldadd_all_xtras+= ${LDADD_${__lib}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}}
.if "${DPADD:M${__lib}}" == ""
DPADD+= ${__lib}

View File

@ -1,4 +1,4 @@
# $Id: gendirdeps.mk,v 1.49 2023/04/20 17:45:03 sjg Exp $
# $Id: gendirdeps.mk,v 1.50 2023/11/04 16:47:34 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@ -102,6 +102,10 @@ _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
# caller should have set this
META_FILES ?= ${.MAKE.META.FILES}
# this sometimes needs to be passed separately
.if !empty(META_XTRAS)
META_FILES += ${META_XTRAS:N\*.meta}
.endif
.if !empty(META_FILES)

View File

@ -1,5 +1,5 @@
# RCSid:
# $Id: host-target.mk,v 1.18 2023/05/22 23:08:31 sjg Exp $
# $Id: host-target.mk,v 1.19 2023/09/21 06:44:53 sjg Exp $
# Host platform information; may be overridden
.if !target(__${.PARSEFILE}__)
@ -48,9 +48,14 @@ HOST_TARGET := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH}
# sometimes we want HOST_TARGET32
MACHINE32.amd64 = i386
MACHINE32.x86_64 = i386
.if !defined(_HOST_ARCH32)
_HOST_ARCH32 := ${MACHINE32.${_HOST_ARCH}:U${_HOST_ARCH:S,64$,,}}
.export _HOST_ARCH32
.endif
HOST_TARGET32 := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH32}
.export HOST_TARGET HOST_TARGET32
# tr is insanely non-portable, accommodate the lowest common denominator
TR ?= tr
toLower = ${TR} 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'

View File

@ -1,4 +1,4 @@
# $Id: init.mk,v 1.27 2022/01/01 17:32:18 sjg Exp $
# $Id: init.mk,v 1.30 2023/10/03 16:25:01 sjg Exp $
#
# @(#) Copyright (c) 2002, Simon J. Gerraty
#
@ -32,6 +32,11 @@ _this_mk_dir := ${.PARSEDIR}
# should have been set by sys.mk
CXX_SUFFIXES?= .cc .cpp .cxx .C
.if defined(PROG_CXX) || ${SRCS:Uno:${CXX_SUFFIXES:S,^,N*,:ts:}} != ${SRCS:Uno:N/}
_CCLINK ?= ${CXX}
.endif
_CCLINK ?= ${CC}
.if !empty(WARNINGS_SET) || !empty(WARNINGS_SET_${MACHINE_ARCH})
.include <warnings.mk>
.endif
@ -51,6 +56,7 @@ QUALIFIED_VAR_LIST += \
CPPFLAGS \
CPUFLAGS \
LDFLAGS \
SRCS \
# a final :U avoids errors if someone uses :=
.for V in ${QUALIFIED_VAR_LIST:O:u:@q@$q $q_LAST@}

4
mk/install-mk Normal file → Executable file
View File

@ -59,7 +59,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: install-mk,v 1.242 2023/09/09 16:00:16 sjg Exp $
# $Id: install-mk,v 1.244 2024/01/05 23:16:34 sjg Exp $
#
# @(#) Copyright (c) 1994-2023 Simon J. Gerraty
#
@ -74,7 +74,7 @@
# sjg@crufty.net
#
MK_VERSION=20230909
MK_VERSION=20240105
OWNER=
GROUP=
MODE=444

View File

@ -1,4 +1,4 @@
# $Id: jobs.mk,v 1.14 2023/09/11 16:52:44 sjg Exp $
# $Id: jobs.mk,v 1.16 2023/11/29 15:59:50 sjg Exp $
#
# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
#
@ -78,7 +78,8 @@ JOB_MAX = ${.MAKE.JOBS}
# This should be derrived from number of cpu's
.if ${.MAKE.JOBS.C:Uno} == "yes"
# 1.2 - 1.5 times nCPU works well on most machines that support -jC
JOB_MAX_C ?= 1.33C
# if the factor is floating point, the C suffix isn't needed
JOB_MAX_C ?= 1.33
JOB_MAX ?= ${JOB_MAX_C}
.endif
JOB_MAX ?= 8

View File

@ -1,4 +1,4 @@
# $Id: lib.mk,v 1.75 2023/09/11 05:20:23 sjg Exp $
# $Id: lib.mk,v 1.81 2023/10/03 18:18:57 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@ -62,6 +62,10 @@ META_NOECHO?= echo
# (usually just ${CPPPICFLAGS} ${CPICFLAGS})
# APICFLAGS: flags for ${AS} to assemble .[sS] to ${PICO} objects.
# we simplify life by letting the toolchain do most of the work
# _CCLINK is set by init.mk based on whether we are doing C++ or not
SHLIB_LD ?= ${_CCLINK}
.if ${TARGET_OSNAME} == "NetBSD"
.if ${MACHINE_ARCH} == "alpha"
# Alpha-specific shared library flags
@ -111,7 +115,7 @@ APICFLAGS?= -k
# Platform-independent linker flags for ELF shared libraries
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
SHLIB_LDSTARTFILE?= /usr/lib/crtbeginS.o
SHLIB_LDENDFILE?= /usr/lib/crtendS.o
.endif
@ -125,7 +129,7 @@ LD_shared=${SHLIB_SHFLAGS}
.if ${TARGET_OSNAME} == "FreeBSD"
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
.else
SHLIB_SHFLAGS= -assert pure-text
.endif
@ -168,7 +172,6 @@ AR_cq= -cqs
.elif ${TARGET_OSNAME} == "FreeBSD"
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_solib= -Wl,--whole-archive lib${LIB}_pic.a -Wl,--no-whole-archive
@ -180,7 +183,6 @@ LD_pobjs = ${POBJS}
LD_sobjs = ${SOBJS}
.endif
.elif ${TARGET_OSNAME} == "Darwin"
SHLIB_LD = ${CC}
SHLIB_INSTALL_VERSION ?= ${SHLIB_MAJOR}
SHLIB_COMPATABILITY_VERSION ?= ${SHLIB_MAJOR}.${SHLIB_MINOR:U0}
SHLIB_COMPATABILITY ?= \
@ -213,8 +215,6 @@ PICFLAG ?= -fPIC -fno-common
RANLIB = :
.endif
SHLIB_LD ?= ${LD}
.if !empty(SHLIB_MAJOR)
.if ${NEED_SOLINKS} && empty(SHLIB_LINKS)
.if ${MK_LINKLIB} != "no"
@ -441,8 +441,6 @@ lib${LIB}_pic.a: ${SOBJS}
@${AR} ${AR_cq} ${.TARGET} ${LD_sobjs}
${RANLIB} ${.TARGET}
#SHLIB_LDADD?= ${LDADD}
# bound to be non-portable...
# this is known to work for NetBSD 1.6 and FreeBSD 4.2
lib${LIB}.${LD_so}: ${SOLIB} ${DPADD}
@ -450,10 +448,9 @@ lib${LIB}.${LD_so}: ${SOLIB} ${DPADD}
@rm -f ${.TARGET}
.if ${TARGET_OSNAME:NFreeBSD:NNetBSD} == ""
.if ${OBJECT_FMT} == "ELF"
${SHLIB_LD} -x -shared ${SHLIB_SHFLAGS} ${LDFLAGS} -o ${.TARGET} \
${SHLIB_LDSTARTFILE} \
--whole-archive ${SOLIB} --no-whole-archive \
${LDADD} ${SHLIB_LDADD} ${SHLIB_LDENDFILE}
${SHLIB_LD} -shared -Wl,-x ${SHLIB_SHFLAGS} ${LDFLAGS} -o ${.TARGET} \
-Wl,--whole-archive ${SOLIB} -Wl,--no-whole-archive \
${LDADD} ${SHLIB_LDADD}
.else
${SHLIB_LD} ${LD_x} ${LD_shared} ${LDFLAGS} \
-o ${.TARGET} ${SOLIB} ${LDADD} ${SHLIB_LDADD}
@ -480,7 +477,7 @@ cleanlib: .PHONY
rm -f a.out [Ee]rrs mklog core *.core ${CLEANFILES}
rm -f lib${LIB}.a ${OBJS}
rm -f lib${LIB}_p.a ${POBJS}
rm -f lib${LIB}_pic.a lib${LIB}.so.*.* ${SOBJS}
rm -f lib${LIB}_pic.a lib${LIB}*${LD_solink} lib${LIB}*${LD_solink}.* ${SOBJS}
rm -f llib-l${LIB}.ln ${LOBJS}
.if !empty(SHLIB_LINKS)
rm -f ${SHLIB_LINKS}

View File

@ -1,4 +1,4 @@
# $Id: man.mk,v 1.25 2021/10/31 03:03:14 sjg Exp $
# $Id: man.mk,v 1.26 2023/12/30 02:10:38 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@ -16,13 +16,13 @@ OPTIONS_DEFAULT_NO += CMT2DOC
# so we have to use sed(1).
# set MANTARGET=cat for formatted pages
MANTARGET?= man
MANTARGET ?= man
# set this to .0 for same behavior as bsd.man.mk
MCATEXT?=
MCATEXT ?=
NROFF?= nroff
MANDIR?= /usr/share/man
MANDOC?= man
NROFF ?= nroff
MANDIR ?= /usr/share/man
MANDOC ?= man
MAN_SUFFIXES?= .1 .2 .3 .4 .5 .6 .7 .8 .9
.SUFFIXES: ${MAN_SUFFIXES}
@ -35,8 +35,24 @@ ${MAN_SUFFIXES:@s@$s${s:S,.,.cat,}@}:
@${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}.new && \
mv ${.TARGET:T}.new ${.TARGET:T}
.if !empty(MANOWN)
MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
MAN_CHOWN ?= chown
.else
MAN_CHOWN = :
.endif
MINSTALL = ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
.if defined(MAN) && !empty(MAN)
.if ${MANTARGET} == "cat"
MANALL ?= ${MAN:T:@p@${p:R}.cat${p:E}@}
.else
MANALL ?= ${MAN}
.endif
.if ${MK_CMT2DOC} == "yes"
# use cmt2doc.py to extract manpages from source
CMT2DOC?= cmt2doc.py
@ -52,36 +68,65 @@ ${CMT2DOC_SUFFIXES:@s@${MAN_SUFFIXES:@m@$s$m@}@}:
.endif
_mandir=${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
# none of this is relevant unless doing maninstall
.if make(*install)
_mandir = ${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
.if ${MANTARGET} == "cat"
_mfromdir?=.
MANALL= ${MAN:${MAN_SUFFIXES:S,.,,:@m@S/.$m/.cat$m/@:ts:}}
_mfromdir ?= .
.if ${MCATEXT} == ""
_minstpage=`echo $$page | sed 's/\.cat/./'`
_minstpage = `echo $$page | sed 's/\.cat/./'`
.else
_minstpage=`echo $$page | sed 's/\.cat.*//'`${MCATEXT}
_minstpage = `echo $$page | sed 's/\.cat.*//'`${MCATEXT}
.endif
.endif
.if target(${MAN:[1]})
_mfromdir?=.
_mfromdir ?= .
.endif
_mfromdir?=${.CURDIR}
MANALL?= ${MAN}
_minstpage?=$${page}
_mfromdir ?= ${.CURDIR}
_minstpage ?= $${page}
.endif
.if !empty(MANOWN)
MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
MAN_CHOWN ?= chown
.else
MAN_CHOWN = :
.endif
MINSTALL= ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
.if defined(MANZ)
# chown and chmod are done afterward automatically
MCOMPRESS= gzip -cf
MCOMPRESSSUFFIX= .gz
MCOMPRESS_CMD ?= gzip -cf
MCOMPRESS_EXT ?= .gz
_MANZ_USE: .USE
@${MCOMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
.for _page in ${MANALL}
${_page:T}${MCOMPRESS_EXT}: ${_page} _MANZ_USE
.endfor
.endif
.if ${MK_STAGING_MAN} == "yes"
_mansets := ${MAN:E:O:u:M*[1-9]:@s@man$s@}
.if ${MANTARGET} == "cat"
STAGE_AS_SETS += ${_mansets}
_stage_man = stage_as
.else
STAGE_SETS += ${_mansets}
_stage_man = stage_files
.endif
STAGE_TARGETS += ${_stage_man}
.for _page _as in ${MANALL:@x@$x ${x:T:S/.cat/./}@}
${_stage_man}.man${_as:E}: ${_page}
.if target(${_page:T}${MCOMPRESS_EXT})
${_man_stage}.man${_as:E}: ${_page:T}${MCOMPRESS_EXT}
.endif
STAGE_DIR.man${_as:E} ?= ${STAGE_OBJTOP}${MANDIR}/${MANTARGET}${_as:E}${MANSUBDIR}
.if ${MANTARGET} == "cat"
STAGE_AS_${_page} = ${_as}
.endif
.endfor
.if !defined(NO_MLINKS) && !empty(MLINKS)
STAGE_SETS += mlinks
STAGE_TARGETS += stage_links
STAGE_LINKS.mlinks := ${MLINKS:M*.[1-9]:@f@${f:S,^,${MANDIR}/${MANTARGET}${f:E}${MANSUBDIR}/,}@}
stage_links.mlinks: ${_mansets:@s@stage_files.$s@}
.endif
.endif
.endif
maninstall:

View File

@ -1,4 +1,4 @@
# $Id: meta.autodep.mk,v 1.59 2023/08/19 17:35:32 sjg Exp $
# $Id: meta.autodep.mk,v 1.60 2024/01/09 23:42:22 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@ -305,7 +305,7 @@ ${_DEPENDFILE}: .PRECIOUS
CLEANFILES += *.meta filemon.* *.db
# these make it easy to gather some stats
now_utc = ${%s:L:localtime}
now_utc ?= ${%s:L:localtime}
start_utc := ${now_utc}
meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \

View File

@ -1,4 +1,4 @@
# $Id: own.mk,v 1.44 2021/12/08 05:56:50 sjg Exp $
# $Id: own.mk,v 1.45 2023/12/30 02:10:38 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@ -119,6 +119,7 @@ OPTIONS_DEFAULT_DEPENDENT+= \
PICINSTALL/LINKLIB \
PICLIB/PIC \
PROFILE/LINKLIB \
STAGING_MAN/STAGING \
STAGING_PROG/STAGING \
.include <options.mk>

View File

@ -1,4 +1,4 @@
# $Id: prog.mk,v 1.39 2023/04/20 23:45:56 sjg Exp $
# $Id: prog.mk,v 1.40 2023/10/02 21:35:43 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@ -75,12 +75,9 @@ ${CXX_SUFFIXES:%=%.o}:
.if defined(PROG_CXX)
PROG= ${PROG_CXX}
_CCLINK= ${CXX}
_SUPCXX?= -lstdc++ -lm
.endif
_CCLINK?= ${CC}
.if defined(PROG)
BINDIR ?= ${prefix}/bin

View File

@ -1,4 +1,4 @@
# $Id: rst2htm.mk,v 1.13 2023/09/13 18:55:42 sjg Exp $
# $Id: rst2htm.mk,v 1.14 2023/09/19 22:27:28 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@ -44,6 +44,7 @@ RST_SUFFIXES ?= .rst .txt
CLEANFILES += ${HTMFILES} ${PDFFILES}
html: ${HTMFILES}
pdf: ${PDFFILES}
.SUFFIXES: ${RST_SUFFIXES} .htm .pdf

0
os.sh Normal file → Executable file
View File

279
parse.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: parse.c,v 1.706 2023/08/19 11:09:02 rillig Exp $ */
/* $NetBSD: parse.c,v 1.716 2024/01/07 11:39:04 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -121,7 +121,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: parse.c,v 1.706 2023/08/19 11:09:02 rillig Exp $");
MAKE_RCSID("$NetBSD: parse.c,v 1.716 2024/01/07 11:39:04 rillig Exp $");
/* Detects a multiple-inclusion guard in a makefile. */
typedef enum {
@ -131,9 +131,7 @@ typedef enum {
GS_NO /* the file is not guarded */
} GuardState;
/*
* A file being read.
*/
/* A file being parsed. */
typedef struct IncludedFile {
FStr name; /* absolute or relative to the cwd */
unsigned lineno; /* 1-based */
@ -251,7 +249,7 @@ static StringList targCmds = LST_INIT;
*/
static GNode *order_pred;
static int parseErrors = 0;
static int parseErrors;
/*
* The include chain of makefiles. At index 0 is the top-level makefile from
@ -328,6 +326,23 @@ enum PosixState posix_state = PS_NOT_YET;
static HashTable /* full file name -> Guard */ guards;
static List *
Lst_New(void)
{
List *list = bmake_malloc(sizeof *list);
Lst_Init(list);
return list;
}
static void
Lst_Free(List *list)
{
Lst_Done(list);
free(list);
}
static IncludedFile *
GetInclude(size_t i)
{
@ -335,7 +350,7 @@ GetInclude(size_t i)
return Vector_Get(&includes, i);
}
/* The makefile that is currently being read. */
/* The makefile or the body of a .for loop that is currently being read. */
static IncludedFile *
CurFile(void)
{
@ -382,7 +397,7 @@ LoadFile(const char *path, int fd)
}
assert(buf.len <= buf.cap);
if (!Buf_EndsWith(&buf, '\n'))
if (buf.len > 0 && !Buf_EndsWith(&buf, '\n'))
Buf_AddByte(&buf, '\n');
return buf; /* may not be null-terminated */
@ -442,8 +457,8 @@ IsEscaped(const char *line, const char *p)
}
/*
* Add the filename and lineno to the GNode so that we remember where its
* last command was added or where it was mentioned in a .depend file.
* Remember the location (filename and lineno) where the last command was
* added or where the node was mentioned in a .depend file.
*/
static void
RememberLocation(GNode *gn)
@ -566,7 +581,7 @@ ParseErrorInternal(const GNode *gn,
}
/*
* Print a parse error message, including location information.
* Print a message, including location information.
*
* If the level is PARSE_FATAL, continue parsing until the end of the
* current top-level makefile, then exit (see Parse_File).
@ -621,8 +636,7 @@ HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
/*
* Add the child to the parent's children, and for non-special targets, vice
* versa. Special targets such as .END do not need to be informed once the
* child target has been made.
* versa.
*/
static void
LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
@ -633,7 +647,10 @@ LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
Lst_Append(&pgn->children, cgn);
pgn->unmade++;
/* Special targets like .END don't need any children. */
/*
* Special targets like .END do not need to be informed once the child
* target has been made.
*/
if (!isSpecial)
Lst_Append(&cgn->parents, pgn);
@ -672,10 +689,9 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
/*
* If the node was on the left-hand side of a '::' operator,
* we need to create a new instance of it for the children
* and commands on this dependency line since each of these
* dependency groups has its own attributes and commands,
* separate from the others.
* create a new node for the children and commands on this
* dependency line, since each of these dependency groups has
* its own attributes and commands, separate from the others.
*
* The new instance is placed on the 'cohorts' list of the
* initial one (note the initial one is not on its own
@ -694,7 +710,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (doing_depend)
RememberLocation(cohort);
/*
* Make the cohort invisible as well to avoid duplicating it
* Make the cohort invisible to avoid duplicating it
* into other variables. True, parents of this target won't
* tend to do anything with their local variables, but better
* safe than sorry.
@ -709,11 +725,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
(unsigned int)gn->unmade_cohorts % 1000000);
} else {
/*
* We don't want to nuke any previous flags (whatever they
* were) so we just OR the new operator into the old.
*/
gn->type |= op;
gn->type |= op; /* preserve any previous flags */
}
return true;
@ -730,12 +742,12 @@ ApplyDependencyOperator(GNodeType op)
}
/*
* We add a .WAIT node in the dependency list. After any dynamic dependencies
* Add a .WAIT node in the dependency list. After any dynamic dependencies
* (and filename globbing) have happened, it is given a dependency on each
* previous child, back until the previous .WAIT node. The next child won't
* be scheduled until the .WAIT node is built.
*
* We give each .WAIT node a unique name (mainly for diagnostics).
* Give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
ApplyDependencySourceWait(bool isSpecial)
@ -819,9 +831,7 @@ ApplyDependencySourceOrder(const char *src)
Targ_PrintNode(gn, 0);
}
}
/*
* The current source now becomes the predecessor for the next one.
*/
/* The current source now becomes the predecessor for the next one. */
order_pred = gn;
}
@ -879,7 +889,8 @@ MaybeUpdateMainTarget(void)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
if (GNode_IsMainCandidate(gn)) {
DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
DEBUG1(MAKE, "Setting main node to \"%s\"\n",
gn->name);
mainNode = gn;
return;
}
@ -901,38 +912,31 @@ InvalidLineType(const char *line, const char *unexpanded_line)
} else if (strcmp(line, unexpanded_line) == 0)
Parse_Error(PARSE_FATAL, "Invalid line '%s'", line);
else
Parse_Error(PARSE_FATAL, "Invalid line '%s', expanded to '%s'",
Parse_Error(PARSE_FATAL,
"Invalid line '%s', expanded to '%s'",
unexpanded_line, line);
}
static void
ParseDependencyTargetWord(char **pp, const char *lstart)
{
const char *cp = *pp;
const char *p = *pp;
while (*cp != '\0') {
if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' ||
*cp == '(') &&
!IsEscaped(lstart, cp))
while (*p != '\0') {
if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(')
&& !IsEscaped(lstart, p))
break;
if (*cp == '$') {
/*
* Must be a dynamic source (would have been expanded
* otherwise).
*
* There should be no errors in this, as they would
* have been discovered in the initial Var_Subst and
* we wouldn't be here.
*/
FStr val = Var_Parse(&cp, SCOPE_CMDLINE,
if (*p == '$') {
FStr val = Var_Parse(&p, SCOPE_CMDLINE,
VARE_PARSE_ONLY);
/* TODO: handle errors */
FStr_Done(&val);
} else
cp++;
p++;
}
*pp += cp - *pp;
*pp += p - *pp;
}
/*
@ -1359,6 +1363,7 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
Suff_AddSuffix(word);
break;
case SP_PATH:
case SP_SYSPATH:
AddToPaths(word, paths);
break;
case SP_INCLUDES:
@ -1379,9 +1384,6 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
case SP_READONLY:
Var_ReadOnly(word, true);
break;
case SP_SYSPATH:
AddToPaths(word, paths);
break;
default:
break;
}
@ -1392,7 +1394,7 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
GNodeType *inout_targetAttr,
SearchPathList **inout_paths)
{
char savec = *nameEnd;
char savedNameEnd = *nameEnd;
*nameEnd = '\0';
if (!HandleDependencyTarget(name, inout_special,
@ -1404,7 +1406,7 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
else if (*inout_special == SP_PATH && *name != '.' && *name != '\0')
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name);
*nameEnd = savec;
*nameEnd = savedNameEnd;
return true;
}
@ -1466,17 +1468,17 @@ static void
ParseDependencySourcesSpecial(char *start,
ParseSpecial special, SearchPathList *paths)
{
char savec;
while (*start != '\0') {
char savedEnd;
char *end = start;
while (*end != '\0' && !ch_isspace(*end))
end++;
savec = *end;
savedEnd = *end;
*end = '\0';
ParseDependencySourceSpecial(special, start, paths);
*end = savec;
if (savec != '\0')
*end = savedEnd;
if (savedEnd != '\0')
end++;
pp_skip_whitespace(&end);
start = end;
@ -1505,17 +1507,13 @@ ParseDependencySourcesMundane(char *start,
* rest of the line is the value.
*/
if (Parse_IsVar(start, &var)) {
/*
* Check if this makefile has disabled
* setting local variables.
*/
bool target_vars = GetBooleanExpr(
bool targetVarsEnabled = GetBooleanExpr(
"${.MAKE.TARGET_LOCAL_VARIABLES}", true);
if (target_vars)
if (targetVarsEnabled)
LinkVarToTargets(&var);
free(var.varname);
if (target_vars)
if (targetVarsEnabled)
return true;
}
@ -1594,7 +1592,6 @@ ParseDependencySources(char *p, GNodeType targetAttr,
return;
}
/* Now go for the sources. */
switch (special) {
case SP_INCLUDES:
case SP_LIBS:
@ -1766,10 +1763,7 @@ Parse_IsVar(const char *p, VarAssign *out_var)
nameStart = p;
firstSpace = NULL;
/*
* Scan for one of the assignment operators outside a variable
* expansion.
*/
/* Scan for one of the assignment operators outside an expression. */
while (*p != '\0') {
char ch = *p++;
if (ch == '(' || ch == '{') {
@ -1821,14 +1815,14 @@ Parse_IsVar(const char *p, VarAssign *out_var)
* Check for syntax errors such as unclosed expressions or unknown modifiers.
*/
static void
VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope)
VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope)
{
if (opts.strict) {
if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
char *expandedValue = Var_Subst(uvalue,
if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) {
char *parsedValue = Var_Subst(uvalue,
scope, VARE_PARSE_ONLY);
/* TODO: handle errors */
free(expandedValue);
free(parsedValue);
}
}
}
@ -1841,7 +1835,7 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
char *evalue;
/*
* make sure that we set the variable the first time to nothing
* Make sure that we set the variable the first time to nothing
* so that it gets substituted.
*
* TODO: Add a test that demonstrates why this code is needed,
@ -1952,7 +1946,7 @@ Parse_Var(VarAssign *var, GNode *scope)
/*
* See if the command possibly calls a sub-make by using the variable
* See if the command possibly calls a sub-make by using the
* expressions ${.MAKE}, ${MAKE} or the plain word "make".
*/
static bool
@ -1991,16 +1985,10 @@ MaybeSubMake(const char *cmd)
return false;
}
/*
* Append the command to the target node.
*
* The node may be marked as a submake node if the command is determined to
* be that.
*/
/* Append the command to the target node. */
static void
GNode_AddCommand(GNode *gn, char *cmd)
{
/* Add to last (ie current) cohort for :: targets */
if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL)
gn = gn->cohorts.last->datum;
@ -2011,35 +1999,15 @@ GNode_AddCommand(GNode *gn, char *cmd)
gn->type |= OP_SUBMAKE;
RememberLocation(gn);
} else {
#if 0
/* XXX: We cannot do this until we fix the tree */
Lst_Append(&gn->commands, cmd);
Parse_Error(PARSE_WARNING,
"overriding commands for target \"%s\"; "
"previous commands defined at %s: %u ignored",
gn->name, gn->fname, gn->lineno);
#else
Parse_Error(PARSE_WARNING,
"duplicate script for target \"%s\" ignored",
gn->name);
ParseErrorInternal(gn, PARSE_WARNING,
"using previous script for \"%s\" defined here",
gn->name);
#endif
}
}
/*
* Add a directory to the path searched for included makefiles bracketed
* by double-quotes.
*/
void
Parse_AddIncludeDir(const char *dir)
{
(void)SearchPath_Add(parseIncPath, dir);
}
/*
* Parse a directive like '.include' or '.-include'.
*
@ -2063,13 +2031,9 @@ ParseInclude(char *directive)
return;
}
if (*p++ == '<')
endc = '>';
else
endc = '"';
endc = *p++ == '<' ? '>' : '"';
file = FStr_InitRefer(p);
/* Skip to matching delimiter */
while (*p != '\0' && *p != endc)
p++;
@ -2281,7 +2245,7 @@ IsSysVInclude(const char *line)
static void
ParseTraditionalInclude(char *line)
{
char *cp; /* current position in file spec */
char *p; /* current position in file spec */
bool done = false;
bool silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
@ -2294,13 +2258,13 @@ ParseTraditionalInclude(char *line)
all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
for (file = all_files; !done; file = cp + 1) {
for (file = all_files; !done; file = p + 1) {
/* Skip to end of line or next whitespace */
for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++)
for (p = file; *p != '\0' && !ch_isspace(*p); p++)
continue;
if (*cp != '\0')
*cp = '\0';
if (*p != '\0')
*p = '\0';
else
done = true;
@ -2345,9 +2309,8 @@ ParseGmakeExport(char *line)
#endif
/*
* Called when EOF is reached in the current file. If we were reading an
* include file or a .for loop, the includes stack is popped and things set
* up to go back to reading the previous file at the previous location.
* When the end of the current file or .for loop is reached, continue reading
* the previous file at the previous location.
*
* Results:
* true to continue parsing, i.e. it had only reached the end of an
@ -2610,23 +2573,9 @@ SkipIrrelevantBranches(void)
{
const char *line;
while ((line = ReadLowLevelLine(LK_DOT)) != NULL) {
while ((line = ReadLowLevelLine(LK_DOT)) != NULL)
if (Cond_EvalLine(line) == CR_TRUE)
return true;
/*
* TODO: Check for typos in .elif directives such as .elsif
* or .elseif.
*
* This check will probably duplicate some of the code in
* ParseLine. Most of the code there cannot apply, only
* ParseVarassign and ParseDependencyLine can, and to prevent
* code duplication, these would need to be called with a
* flag called onlyCheckSyntax.
*
* See directive-elif.mk for details.
*/
}
return false;
}
@ -2665,9 +2614,9 @@ ParseForLoop(const char *line)
/*
* Read an entire line from the input file.
*
* Empty lines, .if and .for are completely handled by this function,
* leaving only variable assignments, other directives, dependency lines
* and shell commands to the caller.
* Empty lines, .if and .for are handled by this function, while variable
* assignments, other directives, dependency lines and shell commands are
* handled by the caller.
*
* Return a line without trailing whitespace, or NULL for EOF. The returned
* string will be freed at the end of including the file.
@ -2800,26 +2749,26 @@ HandleBreak(const char *arg)
static bool
ParseDirective(char *line)
{
char *cp = line + 1;
char *p = line + 1;
const char *arg;
Substring dir;
pp_skip_whitespace(&cp);
if (IsInclude(cp, false)) {
ParseInclude(cp);
pp_skip_whitespace(&p);
if (IsInclude(p, false)) {
ParseInclude(p);
return true;
}
dir.start = cp;
while (ch_islower(*cp) || *cp == '-')
cp++;
dir.end = cp;
dir.start = p;
while (ch_islower(*p) || *p == '-')
p++;
dir.end = p;
if (*cp != '\0' && !ch_isspace(*cp))
if (*p != '\0' && !ch_isspace(*p))
return false;
pp_skip_whitespace(&cp);
arg = cp;
pp_skip_whitespace(&p);
arg = p;
if (Substring_Equals(dir, "break"))
HandleBreak(arg);
@ -2880,7 +2829,7 @@ Parse_GuardEndif(void)
static char *
FindSemicolon(char *p)
{
int level = 0;
int depth = 0;
for (; *p != '\0'; p++) {
if (*p == '\\' && p[1] != '\0') {
@ -2889,19 +2838,15 @@ FindSemicolon(char *p)
}
if (*p == '$' && (p[1] == '(' || p[1] == '{'))
level++;
else if (level > 0 && (*p == ')' || *p == '}'))
level--;
else if (level == 0 && *p == ';')
depth++;
else if (depth > 0 && (*p == ')' || *p == '}'))
depth--;
else if (depth == 0 && *p == ';')
break;
}
return p;
}
/*
* dependency -> [target...] op [source...] [';' command]
* op -> ':' | '::' | '!'
*/
static void
ParseDependencyLine(char *line)
{
@ -2909,11 +2854,6 @@ ParseDependencyLine(char *line)
char *expanded_line;
const char *shellcmd = NULL;
/*
* For some reason - probably to make the parser impossible -
* a ';' can be used to separate commands from dependencies.
* Attempt to skip over ';' inside substitution patterns.
*/
{
char *semicolon = FindSemicolon(line);
if (*semicolon != '\0') {
@ -2924,7 +2864,7 @@ ParseDependencyLine(char *line)
}
/*
* We now know it's a dependency line so it needs to have all
* We now know it's a dependency line, so it needs to have all
* variables expanded before being parsed.
*
* XXX: Ideally the dependency line would first be split into
@ -2935,7 +2875,7 @@ ParseDependencyLine(char *line)
* as well.
*
* Parsing the line first would also prevent that targets
* generated from variable expressions are interpreted as the
* generated from expressions are interpreted as the
* dependency operator, such as in "target${:U\:} middle: source",
* in which the middle is interpreted as a source, not a target.
*/
@ -2975,13 +2915,6 @@ ParseDependencyLine(char *line)
static void
ParseLine(char *line)
{
/*
* Lines that begin with '.' can be pretty much anything:
* - directives like '.include' or '.if',
* - suffix rules like '.c.o:',
* - dependencies for filenames that start with '.',
* - variable assignments like '.tmp=value'.
*/
if (line[0] == '.' && ParseDirective(line))
return;
@ -2992,9 +2925,6 @@ ParseLine(char *line)
#ifdef SYSVINCLUDE
if (IsSysVInclude(line)) {
/*
* It's an S3/S5-style "include".
*/
ParseTraditionalInclude(line);
return;
}
@ -3003,9 +2933,6 @@ ParseLine(char *line)
#ifdef GMAKEEXPORT
if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
strchr(line, ':') == NULL) {
/*
* It's a Gmake "export".
*/
ParseGmakeExport(line);
return;
}
@ -3019,10 +2946,7 @@ ParseLine(char *line)
ParseDependencyLine(line);
}
/*
* Parse a top-level makefile, incorporating its content into the global
* dependency graph.
*/
/* Interpret a top-level makefile. */
void
Parse_File(const char *name, int fd)
{
@ -3043,7 +2967,6 @@ Parse_File(const char *name, int fd)
CurFile()->lineno, line);
ParseLine(line);
}
/* Reached EOF, but it may be just EOF of an include file. */
} while (ParseEOF());
FinishDependencyGroup();

83
str.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: str.c,v 1.99 2023/06/23 05:03:04 rillig Exp $ */
/* $NetBSD: str.c,v 1.102 2024/01/05 23:22:06 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.99 2023/06/23 05:03:04 rillig Exp $");
MAKE_RCSID("$NetBSD: str.c,v 1.102 2024/01/05 23:22:06 rillig Exp $");
static HashTable interned_strings;
@ -107,6 +107,10 @@ str_concat3(const char *s1, const char *s2, const char *s3)
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account.
*
* A string that is empty or only contains whitespace nevertheless results in
* a single word. This is unexpected in many places, and the caller needs to
* correct for this edge case.
*
* If expand is true, quotes are removed and escape sequences such as \r, \t,
* etc... are expanded. In this case, return NULL on parse errors.
*
@ -322,17 +326,13 @@ StrMatchResult
Str_Match(const char *str, const char *pat)
{
StrMatchResult res = { NULL, false };
const char *fixed_str, *fixed_pat;
bool asterisk, matched;
asterisk = false;
fixed_str = str;
fixed_pat = pat;
bool asterisk = false;
const char *fixed_str = str;
const char *fixed_pat = pat;
match_fixed_length:
str = fixed_str;
pat = fixed_pat;
matched = false;
for (; *pat != '\0' && *pat != '*'; str++, pat++) {
if (*str == '\0')
return res;
@ -350,7 +350,7 @@ match_fixed_length:
if (*pat == ']' || *pat == '\0') {
if (neg)
goto end_of_char_list;
goto match_done;
goto no_match;
}
if (*pat == *str)
goto end_of_char_list;
@ -369,7 +369,7 @@ match_fixed_length:
end_of_char_list:
if (neg && *pat != ']' && *pat != '\0')
goto match_done;
goto no_match;
while (*pat != ']' && *pat != '\0')
pat++;
if (*pat == '\0')
@ -379,43 +379,40 @@ match_fixed_length:
if (*pat == '\\') /* match the next character exactly */
pat++;
if (*pat != *str)
goto match_done;
}
matched = true;
match_done:
if (!asterisk) {
if (!matched)
return res;
if (*pat == '\0') {
res.matched = *str == '\0';
return res;
}
asterisk = true;
} else {
if (!matched) {
fixed_str++;
goto match_fixed_length;
}
if (*pat == '\0') {
if (*str == '\0') {
res.matched = true;
return res;
if (*pat != *str) {
if (asterisk && str == fixed_str) {
while (*str != '\0' && *str != *pat)
str++;
fixed_str = str;
goto match_fixed_length;
}
fixed_str += strlen(str);
goto match_fixed_length;
goto no_match;
}
}
while (*pat == '*')
pat++;
if (*pat == '\0') {
res.matched = true;
return res;
if (*pat == '*') {
asterisk = true;
while (*pat == '*')
pat++;
if (*pat == '\0') {
res.matched = true;
return res;
}
fixed_str = str;
fixed_pat = pat;
goto match_fixed_length;
}
fixed_str = str;
fixed_pat = pat;
if (asterisk && *str != '\0') {
fixed_str += strlen(str);
goto match_fixed_length;
}
res.matched = *str == '\0';
return res;
no_match:
if (!asterisk)
return res;
fixed_str++;
goto match_fixed_length;
}

31
str.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: str.h,v 1.17 2023/06/23 04:56:54 rillig Exp $ */
/* $NetBSD: str.h,v 1.19 2024/01/05 21:56:55 rillig Exp $ */
/*
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
@ -76,27 +76,24 @@ typedef struct StrMatchResult {
} StrMatchResult;
MAKE_INLINE FStr
FStr_Init(const char *str, void *freeIt)
{
FStr fstr;
fstr.str = str;
fstr.freeIt = freeIt;
return fstr;
}
/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
FStr_InitOwn(char *str)
{
return FStr_Init(str, str);
FStr fstr;
fstr.str = str;
fstr.freeIt = str;
return fstr;
}
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
return FStr_Init(str, NULL);
FStr fstr;
fstr.str = str;
fstr.freeIt = NULL;
return fstr;
}
MAKE_INLINE void
@ -154,14 +151,6 @@ Substring_Eq(Substring sub, Substring str)
memcmp(sub.start, str.start, len) == 0;
}
MAKE_STATIC Substring
Substring_Sub(Substring sub, size_t start, size_t end)
{
assert(start <= Substring_Length(sub));
assert(end <= Substring_Length(sub));
return Substring_Init(sub.start + start, sub.start + end);
}
MAKE_STATIC bool
Substring_HasPrefix(Substring sub, Substring prefix)
{
@ -198,7 +187,7 @@ Substring_SkipFirst(Substring sub, char ch)
}
MAKE_STATIC const char *
Substring_LastIndex(Substring sub, char ch)
Substring_FindLast(Substring sub, char ch)
{
const char *p;

168
suff.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: suff.c,v 1.368 2023/02/14 21:38:31 rillig Exp $ */
/* $NetBSD: suff.c,v 1.377 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -115,7 +115,7 @@
#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
MAKE_RCSID("$NetBSD: suff.c,v 1.368 2023/02/14 21:38:31 rillig Exp $");
MAKE_RCSID("$NetBSD: suff.c,v 1.377 2024/01/05 23:22:06 rillig Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@ -142,8 +142,6 @@ static GNodeList transforms = LST_INIT;
*/
static int sNum = 0;
typedef List SuffixListList;
/*
* A suffix such as ".c" or ".o" that may be used in suffix transformation
* rules such as ".c.o:".
@ -185,14 +183,6 @@ typedef struct Suffix {
SuffixList parents;
/* Suffixes we have a transformation from */
SuffixList children;
/*
* 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.
*/
SuffixListList ref;
} Suffix;
/*
@ -374,7 +364,6 @@ SuffixList_Unref(SuffixList *list, Suffix *suff)
}
}
/* Free up all memory associated with the given suffix structure. */
static void
Suffix_Free(Suffix *suff)
{
@ -392,7 +381,6 @@ Suffix_Free(Suffix *suff)
suff->name, suff->refCount);
#endif
Lst_Done(&suff->ref);
Lst_Done(&suff->children);
Lst_Done(&suff->parents);
SearchPath_Free(suff->searchPath);
@ -401,12 +389,6 @@ Suffix_Free(Suffix *suff)
free(suff);
}
static void
SuffFree(void *p)
{
Suffix_Free(p);
}
/* Remove the suffix from the list, and free if it is otherwise unused. */
static void
SuffixList_Remove(SuffixList *list, Suffix *suff)
@ -416,7 +398,7 @@ SuffixList_Remove(SuffixList *list, Suffix *suff)
/* XXX: can lead to suff->refCount == -1 */
SuffixList_Unref(&sufflist, suff);
DEBUG1(SUFF, "Removing suffix \"%s\"\n", suff->name);
SuffFree(suff);
Suffix_Free(suff);
}
}
@ -440,12 +422,10 @@ SuffixList_Insert(SuffixList *list, Suffix *suff)
DEBUG2(SUFF, "inserting \"%s\" (%d) at end of list\n",
suff->name, suff->sNum);
Lst_Append(list, Suffix_Ref(suff));
Lst_Append(&suff->ref, list);
} else if (listSuff->sNum != suff->sNum) {
DEBUG4(SUFF, "inserting \"%s\" (%d) before \"%s\" (%d)\n",
suff->name, suff->sNum, listSuff->name, listSuff->sNum);
Lst_InsertBefore(list, ln, Suffix_Ref(suff));
Lst_Append(&suff->ref, list);
} else {
DEBUG2(SUFF, "\"%s\" (%d) is already there\n",
suff->name, suff->sNum);
@ -469,7 +449,6 @@ Suffix_New(const char *name)
suff->searchPath = SearchPath_New();
Lst_Init(&suff->children);
Lst_Init(&suff->parents);
Lst_Init(&suff->ref);
suff->sNum = sNum++;
suff->include = false;
suff->library = false;
@ -496,7 +475,7 @@ Suff_ClearSuffixes(void)
Lst_Init(&sufflist);
sNum = 0;
if (nullSuff != NULL)
SuffFree(nullSuff);
Suffix_Free(nullSuff);
emptySuff = nullSuff = Suffix_New("");
SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath);
@ -821,19 +800,7 @@ UpdateTargets(Suffix *suff)
}
}
/*
* Add the suffix to the end of the list of known suffixes.
* Should we restructure the suffix graph? Make doesn't.
*
* A GNode is created for the suffix (XXX: this sounds completely wrong) and
* a Suffix structure is created and added to the suffixes list unless the
* suffix was already known.
* The mainNode passed can be modified if a target mutated into a
* transform and that target happened to be the main target.
*
* Input:
* name the name of the suffix to add
*/
/* Add the suffix to the end of the list of known suffixes. */
void
Suff_AddSuffix(const char *name)
{
@ -1247,9 +1214,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
if (!Dir_HasWildcards(cgn->name))
return;
/*
* Expand the word along the chosen path
*/
/* Expand the word along the chosen path. */
Lst_Init(&expansions);
SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
@ -1258,10 +1223,10 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
/*
* Fetch next expansion off the list and find its GNode
*/
char *cp = Lst_Dequeue(&expansions);
char *name = Lst_Dequeue(&expansions);
DEBUG1(SUFF, "%s...", cp);
gn = Targ_GetNode(cp);
DEBUG1(SUFF, "%s...", name);
gn = Targ_GetNode(name);
/* Insert gn before the original child. */
Lst_InsertBefore(&pgn->children, cln, gn);
@ -1274,8 +1239,8 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
DEBUG0(SUFF, "\n");
/*
* Now the source is expanded, remove it from the list of children to
* keep it from being processed.
* Now that the source is expanded, remove it from the list of
* children, to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@ -1287,57 +1252,56 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* add those nodes to the members list.
*
* Unfortunately, we can't use Str_Words because it doesn't understand about
* variable expressions with spaces in them.
* expressions with spaces in them.
*/
static void
ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
ExpandChildrenRegular(char *p, GNode *pgn, GNodeList *members)
{
char *start;
pp_skip_hspace(&cp);
start = cp;
while (*cp != '\0') {
if (*cp == ' ' || *cp == '\t') {
pp_skip_hspace(&p);
start = p;
while (*p != '\0') {
if (*p == ' ' || *p == '\t') {
GNode *gn;
/*
* White-space -- terminate element, find the node,
* add it, skip any further spaces.
*/
*cp++ = '\0';
*p++ = '\0';
gn = Targ_GetNode(start);
Lst_Append(members, gn);
pp_skip_hspace(&cp);
pp_skip_hspace(&p);
/* Continue at the next non-space. */
start = cp;
} else if (*cp == '$') {
/* Skip over the variable expression. */
const char *nested_p = cp;
start = p;
} else if (*p == '$') {
/* Skip over the expression. */
const char *nested_p = p;
FStr junk = Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY);
/* TODO: handle errors */
if (junk.str == var_Error) {
Parse_Error(PARSE_FATAL,
"Malformed variable expression at \"%s\"",
cp);
cp++;
"Malformed expression at \"%s\"", p);
p++;
} else {
cp += nested_p - cp;
p += nested_p - p;
}
FStr_Done(&junk);
} else if (cp[0] == '\\' && cp[1] != '\0') {
} else if (p[0] == '\\' && p[1] != '\0') {
/* Escaped something -- skip over it. */
/*
* XXX: In other places, escaping at this syntactical
* position is done by a '$', not a '\'. The '\' is
* only used in variable modifiers.
*/
cp += 2;
p += 2;
} else {
cp++;
p++;
}
}
if (cp != start) {
if (p != start) {
/*
* Stuff left over -- add it to the list too
*/
@ -1347,7 +1311,7 @@ ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
}
/*
* Expand the names of any children of a given node that contain variable
* Expand the names of any children of a given node that contain
* expressions or file wildcards into actual targets.
*
* The expanded node is removed from the parent's list of children, and the
@ -1361,7 +1325,7 @@ static void
ExpandChildren(GNodeListNode *cln, GNode *pgn)
{
GNode *cgn = cln->datum;
char *cp; /* Expanded value */
char *expanded;
if (!Lst_IsEmpty(&cgn->order_pred) || !Lst_IsEmpty(&cgn->order_succ))
/* It is all too hard to process the result of .ORDER */
@ -1383,7 +1347,7 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
}
DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
cp = Var_Subst(cgn->name, pgn, VARE_UNDEFERR);
expanded = Var_Subst(cgn->name, pgn, VARE_UNDEFERR);
/* TODO: handle errors */
{
@ -1395,39 +1359,32 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
* call on the Arch module to find the nodes for us,
* expanding variables in the parent's scope.
*/
char *p = cp;
(void)Arch_ParseArchive(&p, &members, pgn);
char *ap = expanded;
(void)Arch_ParseArchive(&ap, &members, pgn);
} else {
ExpandChildrenRegular(cp, pgn, &members);
ExpandChildrenRegular(expanded, pgn, &members);
}
/*
* Add all elements of the members list to the parent node.
*/
/* Add all members to the parent node. */
while (!Lst_IsEmpty(&members)) {
GNode *gn = Lst_Dequeue(&members);
DEBUG1(SUFF, "%s...", gn->name);
/*
* Add gn to the parents child list before the
* original child.
*/
Lst_InsertBefore(&pgn->children, cln, gn);
Lst_Append(&gn->parents, pgn);
pgn->unmade++;
/* Expand wildcards on new node */
ExpandWildcards(cln->prev, pgn);
}
Lst_Done(&members);
free(cp);
free(expanded);
}
DEBUG0(SUFF, "\n");
/*
* Now the source is expanded, remove it from the list of children to
* keep it from being processed.
* The source is expanded now, so remove it from the list of children,
* to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@ -1446,16 +1403,10 @@ ExpandAllChildren(GNode *gn)
}
/*
* Find a path along which to expand the node.
* Find a path along which to search or expand the node.
*
* If the node has a known suffix, use that path.
* If it has no known suffix, use the default system search path.
*
* Input:
* gn Node being examined
*
* Results:
* The appropriate path to search for the GNode.
* If the node has a known suffix, use that path,
* otherwise use the default system search path.
*/
SearchPath *
Suff_FindPath(GNode *gn)
@ -1529,7 +1480,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
/* Apply the rule. */
Make_HandleUse(gn, tgn);
/* Deal with wildcards and variables in any acquired sources. */
/* Deal with wildcards and expressions in any acquired sources. */
ln = ln != NULL ? ln->next : NULL;
while (ln != NULL) {
GNodeListNode *nln = ln->next;
@ -1556,7 +1507,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
static void
ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
{
GNodeListNode *ln;
SuffixListNode *ln;
size_t nameLen = (size_t)(eoarch - gn->name);
/* Use first matching suffix... */
@ -1565,7 +1516,6 @@ ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
break;
if (ln != NULL) {
/* Got one -- apply it */
Suffix *suff = ln->datum;
if (!ApplyTransform(gn, mem, suff, memSuff)) {
DEBUG2(SUFF, "\tNo transformation from %s -> %s\n",
@ -1579,9 +1529,6 @@ static void FindDeps(GNode *, CandidateSearcher *);
/*
* Locate dependencies for an OP_ARCHV node.
*
* Input:
* gn Node for which to locate dependencies
*
* Side Effects:
* Same as Suff_FindDeps
*/
@ -1595,7 +1542,7 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
const char *name; /* Start of member's name */
/*
* The node is an archive(member) pair. so we must find a
* The node is an 'archive(member)' pair, so we must find a
* suffix for both of them.
*/
eoarch = strchr(gn->name, '(');
@ -1969,8 +1916,7 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
if (targ->node == NULL)
targ->node = Targ_GetNode(targ->file);
ApplyTransform(targ->node, src->node,
targ->suff, src->suff);
ApplyTransform(targ->node, src->node, targ->suff, src->suff);
if (targ->node != gn) {
/*
@ -2068,9 +2014,6 @@ FindDeps(GNode *gn, CandidateSearcher *cs)
*
* Need to handle the changing of the null suffix gracefully so the old
* transformation rules don't just go away.
*
* Input:
* name Name of null suffix
*/
void
Suff_SetNull(const char *name)
@ -2102,16 +2045,21 @@ Suff_Init(void)
Suff_ClearSuffixes();
}
/* Clean up the suffixes module. */
void
Suff_End(void)
{
#ifdef CLEANUP
Lst_DoneCall(&sufflist, SuffFree);
Lst_DoneCall(&suffClean, SuffFree);
SuffixListNode *ln;
for (ln = sufflist.first; ln != NULL; ln = ln->next)
Suffix_Free(ln->datum);
Lst_Done(&sufflist);
for (ln = suffClean.first; ln != NULL; ln = ln->next)
Suffix_Free(ln->datum);
Lst_Done(&suffClean);
if (nullSuff != NULL)
SuffFree(nullSuff);
Suffix_Free(nullSuff);
Lst_Done(&transforms);
#endif
}
@ -2135,7 +2083,7 @@ Suffix_Print(const Suffix *suff)
{
Buffer buf;
Buf_InitSize(&buf, 16);
Buf_Init(&buf);
Buf_AddFlag(&buf, suff->include, "SUFF_INCLUDE");
Buf_AddFlag(&buf, suff->library, "SUFF_LIBRARY");
Buf_AddFlag(&buf, suff->isNull, "SUFF_NULL");
@ -2191,7 +2139,7 @@ Suff_NamesStr(void)
SuffixListNode *ln;
Suffix *suff;
Buf_InitSize(&buf, 16);
Buf_Init(&buf);
for (ln = sufflist.first; ln != NULL; ln = ln->next) {
suff = ln->datum;
if (ln != sufflist.first)

View File

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.207 2023/09/09 16:44:03 sjg Exp $
# $Id: Makefile,v 1.210 2024/01/08 18:28:08 sjg Exp $
#
# $NetBSD: Makefile,v 1.341 2023/09/09 16:41:04 sjg Exp $
# $NetBSD: Makefile,v 1.342 2024/01/07 02:07:44 sjg Exp $
#
# Unit tests for make(1)
#
@ -217,7 +217,7 @@ TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
#TESTS+= job-output-long-lines
TESTS+= job-output-long-lines
TESTS+= job-output-null
TESTS+= jobs-empty-commands
TESTS+= jobs-empty-commands-error
@ -249,7 +249,7 @@ TESTS+= opt-debug-graph1
TESTS+= opt-debug-graph2
TESTS+= opt-debug-graph3
TESTS+= opt-debug-hash
#TESTS+= opt-debug-jobs
TESTS+= opt-debug-jobs
TESTS+= opt-debug-lint
TESTS+= opt-debug-loud
TESTS+= opt-debug-meta
@ -821,6 +821,7 @@ _SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g' -e 's,${.OBJDIR:tA},<curdir>,g'
_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
_SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}\(\[[1-9][0-9]*\]:\),make\1,'
_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
_SED_CMDS+= -e '/MAKE_VERSION/d'

View File

@ -1,7 +1,7 @@
: undefined--eol
make: Unclosed variable "UNCLOSED"
: unclosed-variable-
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier-
make: Unknown modifier "Z"
: unknown-modifier--eol

View File

@ -1,7 +1,7 @@
: undefined
make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier "Z"
: unknown-modifier

View File

@ -1,7 +1,7 @@
: undefined--eol
make: Unclosed variable "UNCLOSED"
: unclosed-variable-
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier-
make: Unknown modifier "Z"
: unknown-modifier--eol

View File

@ -1,6 +1,6 @@
# $NetBSD: cmdline-undefined.mk,v 1.3 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cmdline-undefined.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
# Tests for undefined variable expressions in the command line.
# Tests for undefined expressions in the command line.
all:
# When the command line is parsed, variable assignments using the

View File

@ -1,4 +1,4 @@
# $NetBSD: comment.mk,v 1.5 2022/05/08 06:51:27 rillig Exp $
# $NetBSD: comment.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Demonstrate how comments are written in makefiles.
@ -55,7 +55,7 @@ VAR= \# # Both in the assignment.
# Since 2012-03-24 the variable modifier :[#] does not need to be escaped.
# To keep the parsing code simple, any "[#" does not start a comment, even
# outside of a variable expression.
# outside of an expression.
WORDS= ${VAR:[#]} [#
.if ${WORDS} != "1 [#"
. error

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-cmp-string.mk,v 1.17 2023/03/28 14:38:29 rillig Exp $
# $NetBSD: cond-cmp-string.mk,v 1.18 2023/11/19 21:47:52 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@ -20,11 +20,11 @@
. error
.endif
# The left-hand side of the comparison requires that any variable expression
# The left-hand side of the comparison requires that any expression
# is defined.
#
# The variable named "" is never defined, nevertheless it can be used as a
# starting point for variable expressions. Applying the :U modifier to such
# starting point for expressions. Applying the :U modifier to such
# an undefined expression turns it into a defined expression.
#
# See ApplyModifier_Defined and DEF_DEFINED.
@ -63,13 +63,13 @@
. error
.endif
# A variable expression can be enclosed in double quotes.
# An expression can be enclosed in double quotes.
.if ${:Uword} != "${:Uword}"
. error
.endif
# Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the
# characters " \t!=><" directly after a variable expression resulted in a
# characters " \t!=><" directly after an expression resulted in a
# "Malformed conditional", even though the string was well-formed.
.if ${:Uword } != "${:Uword} "
. error
@ -89,12 +89,12 @@
. error
.endif
# Adding another variable expression to the string literal works though.
# Adding another expression to the string literal works though.
.if ${:Uword} != "${:Uwo}${:Urd}"
. error
.endif
# Adding a space at the beginning of the quoted variable expression works
# Adding a space at the beginning of the quoted expression works
# though.
.if ${:U word } != " ${:Uword} "
. error

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-cmp-unary.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-cmp-unary.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for unary comparisons in .if conditions, that is, comparisons with
# a single operand. If the operand is a number, it is compared to zero,
@ -24,7 +24,7 @@
. error
.endif
# The empty string may come from a variable expression.
# The empty string may come from an expression.
#
# XXX: As of 2023-06-01, this empty string is interpreted "as a number" in
# EvalTruthy, which is plain wrong. The bug is in TryParseNumber.
@ -32,13 +32,13 @@
. error
.endif
# A variable expression that is not surrounded by quotes is interpreted
# An expression that is not surrounded by quotes is interpreted
# as a number if possible, otherwise as a string.
.if ${:U0}
. error
.endif
# A non-zero number from a variable expression evaluates to true.
# A non-zero number from an expression evaluates to true.
.if !${:U12345}
. error
.endif

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-eof.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-eof.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for parsing the end of '.if' conditions, which are represented as the
# token TOK_EOF.
@ -9,7 +9,7 @@ SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
# In the following conditions, ${SIDE_EFFECT} is the position of the first
# 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.
# evaluated, even if it was not necessary to expand the 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.

View File

@ -1,6 +1,6 @@
make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 47: In .for loops, variable expressions for the loop variables are
make: "cond-func-defined.mk" line 47: In .for loops, expressions for the loop variables are
make: "cond-func-defined.mk" line 49: substituted at evaluation time. There is no actual variable
make: "cond-func-defined.mk" line 51: involved, even if it feels like it.
make: Fatal errors encountered -- cannot continue

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-defined.mk,v 1.10 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-func-defined.mk,v 1.11 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@ -25,7 +25,7 @@ ${:UA B}= variable name with spaces
. error
.endif
# If necessary, the whitespace can be generated by a variable expression.
# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif
@ -43,8 +43,8 @@ ${:UA B}= variable name with spaces
. if defined(var)
. error
. else
# expect+1: In .for loops, variable expressions for the loop variables are
. info In .for loops, variable expressions for the loop variables are
# expect+1: In .for loops, expressions for the loop variables are
. info In .for loops, expressions for the loop variables are
# expect+1: substituted at evaluation time. There is no actual variable
. info substituted at evaluation time. There is no actual variable
# expect+1: involved, even if it feels like it.

View File

@ -1,9 +1,9 @@
# $NetBSD: cond-func-empty.mk,v 1.22 2023/08/11 05:01:12 rillig Exp $
# $NetBSD: cond-func-empty.mk,v 1.24 2023/12/19 19:33:40 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# Tests for the empty() function in .if conditions, which tests an
# expression for emptiness.
#
# Note that the argument in the parentheses is a variable name, not a variable
# Note that the argument in the parentheses is a variable name, not an
# expression. That name may be followed by ':...' modifiers.
#
@ -120,7 +120,7 @@ ${:U }= space
. error
.endif
# The :L modifier creates a variable expression that has the same value as
# The :L modifier creates an expression that has the same value as
# its name, which both are "VAR" in this case. The value is therefore not
# empty.
.if empty(VAR:L)
@ -138,7 +138,7 @@ ${:U }= space
. error
.endif
# Ensure that variable expressions that appear as part of the function call
# Ensure that expressions that appear as part of the function call
# argument are properly parsed. Typical use cases for this are .for loops,
# which are expanded to exactly these ${:U} expressions.
#
@ -188,20 +188,20 @@ ${:U WORD }= variable name with spaces
# 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
# "VARNAME${:U2}", but without expanding any nested expression, in
# 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
# The expression was expanded, and this was wrong. The
# expansion was done without VARE_WANTRES (called VARF_WANTRES back then)
# though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be
# expanded.
#
# This was fixed by expanding nested variable expressions in the variable name
# This was fixed by expanding nested expressions in the variable name
# only if the flag VARE_WANTRES is given.
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
# $NetBSD: cond-func-exists.mk,v 1.7 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the exists() function in .if conditions.
@ -17,7 +17,7 @@
.endif
# The only way to escape characters that would otherwise influence the parser
# is to enclose them in a variable expression. For function arguments,
# is to enclose them in an expression. For function arguments,
# neither the backslash nor the dollar sign act as escape character.
.if exists(\.)
. error
@ -27,7 +27,7 @@
. error
.endif
# The argument to the function can have several variable expressions.
# The argument to the function can have several expressions.
# See cond-func.mk for the characters that cannot be used directly.
.if !exists(${.PARSEDIR}/${.PARSEFILE})
. error

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func.mk,v 1.13 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-func.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for those parts of the functions in .if conditions that are common
# among several functions.
@ -38,7 +38,7 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
. error
.endif
# If necessary, the whitespace can be generated by a variable expression.
# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif

View File

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

View File

@ -1,6 +1,6 @@
# $NetBSD: cond-late.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $
# $NetBSD: cond-late.mk,v 1.6 2023/12/10 20:12:28 rillig Exp $
#
# Using the :? modifier, variable expressions can contain conditional
# Using the :? modifier, expressions can contain conditional
# expressions that are evaluated late, at expansion time.
#
# Any expressions appearing in these conditions are expanded before parsing
@ -31,6 +31,6 @@ cond-literal:
VAR= ${${UNDEF} != "no":?:}
# expect-reset
# expect: make: Bad conditional expression ' != "no"' in ' != "no"?:'
# expect: make: Bad conditional expression ' != "no"' before '?:'
.if empty(VAR:Mpattern)
.endif

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op-and.mk,v 1.8 2023/08/15 21:27:09 rillig Exp $
# $NetBSD: cond-op-and.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the && operator in .if conditions.
@ -33,15 +33,15 @@
# Test combinations of outer '||' with inner '&&', to ensure that the operands
# of the inner '&&' are only evaluated if necessary.
DEF= defined
# expect+1: Malformed conditional (0 || (${DEF} && ${UNDEF})
# expect+1: Malformed conditional (0 || (${DEF} && ${UNDEF}))
.if 0 || (${DEF} && ${UNDEF})
.endif
.if 0 || (!${DEF} && ${UNDEF})
.endif
# expect+1: Malformed conditional (0 || (${UNDEF} && ${UNDEF})
# expect+1: Malformed conditional (0 || (${UNDEF} && ${UNDEF}))
.if 0 || (${UNDEF} && ${UNDEF})
.endif
# expect+1: Malformed conditional (0 || (!${UNDEF} && ${UNDEF})
# expect+1: Malformed conditional (0 || (!${UNDEF} && ${UNDEF}))
.if 0 || (!${UNDEF} && ${UNDEF})
.endif
.if 1 || (${DEF} && ${UNDEF})

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op-or.mk,v 1.10 2023/08/15 21:27:09 rillig Exp $
# $NetBSD: cond-op-or.mk,v 1.11 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the || operator in .if conditions.
@ -43,13 +43,13 @@ DEF= defined
.endif
.if 1 && (${DEF} || ${UNDEF})
.endif
# expect+1: Malformed conditional (1 && (!${DEF} || ${UNDEF})
# expect+1: Malformed conditional (1 && (!${DEF} || ${UNDEF}))
.if 1 && (!${DEF} || ${UNDEF})
.endif
# expect+1: Malformed conditional (1 && (${UNDEF} || ${UNDEF})
# expect+1: Malformed conditional (1 && (${UNDEF} || ${UNDEF}))
.if 1 && (${UNDEF} || ${UNDEF})
.endif
# expect+1: Malformed conditional (1 && (!${UNDEF} || ${UNDEF})
# expect+1: Malformed conditional (1 && (!${UNDEF} || ${UNDEF}))
.if 1 && (!${UNDEF} || ${UNDEF})
.endif

View File

@ -7,7 +7,7 @@ expected M pattern
expected or
expected or exists
expected or empty
make: "cond-short.mk" line 214: Comparison with '<' requires both operands '' and '42' to be numeric
make: "cond-short.mk" line 231: Comparison with '<' requires both operands '' and '42' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-short.mk,v 1.20 2023/03/04 13:42:36 rillig Exp $
# $NetBSD: cond-short.mk,v 1.23 2023/11/19 22:32:44 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@ -9,9 +9,9 @@
# Before 2020-06-28, the right-hand side of an && or || operator was always
# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
# Var_Parse got a new parameter named 'wantit'. Since then it would have been
# possible to skip evaluation of irrelevant variable expressions and only
# possible to skip evaluation of irrelevant expressions and only
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
# relevant expressions was that in the irrelevant
# 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 the irrelevant expression was
@ -178,6 +178,23 @@ INDIR_UNDEF= ${UNDEF}
. error
.endif
# Since cond.c 1.76 from 2020.06.28 and before var.c 1.225 from 2020.07.01,
# the following snippet resulted in the error message 'Variable VAR is
# recursive'. The condition '0' evaluated to false, which made the right-hand
# side of the '&&' irrelevant. Back then, irrelevant condition parts were
# still evaluated, but in "irrelevant mode", which allowed undefined variables
# to occur in expressions. In this mode, the variable name 'VAR' was
# unnecessarily evaluated, resulting in the expression '${VAR${:U1}}'. In
# this expression, the variable name was 'VAR${:U1}', and of this variable
# name, only the fixed part 'VAR' was evaluated, without the part '${:U1}'.
# This partial evaluation led to the wrong error message about 'VAR' being
# recursive.
VAR= ${VAR${:U1}}
.if 0 && !empty(VAR)
.endif
# Enclosing the expression in double quotes changes how that expression is
# evaluated. In irrelevant expressions that are enclosed in double quotes,
# expressions based on undefined variables are allowed and evaluate to an

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-number.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-token-number.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for number tokens in .if conditions.
#
@ -52,13 +52,13 @@
. error
.endif
# When the number comes from a variable expression though, it may be signed.
# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if ${:U+0}
. error
.endif
# When the number comes from a variable expression though, it may be signed.
# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if !${:U+1}
. error

View File

@ -35,9 +35,9 @@ make: "cond-token-plain.mk" line 121: ok
CondParser_Eval: V${UNDEF}AR
make: "cond-token-plain.mk" line 130: Undefined variables in bare words expand to an empty string.
CondParser_Eval: 0${:Ux00}
make: "cond-token-plain.mk" line 139: Numbers can be composed from literals and variable expressions.
make: "cond-token-plain.mk" line 139: Numbers can be composed from literals and expressions.
CondParser_Eval: 0${:Ux01}
make: "cond-token-plain.mk" line 144: Numbers can be composed from literals and variable expressions.
make: "cond-token-plain.mk" line 144: Numbers can be composed from literals and expressions.
CondParser_Eval: "" ==
make: "cond-token-plain.mk" line 151: Missing right-hand side of operator '=='
CondParser_Eval: == ""

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-plain.mk,v 1.18 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-token-plain.mk,v 1.19 2023/11/19 21:47:52 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions. These are also called bare words.
@ -115,7 +115,7 @@ VAR= defined
. error
.endif
# Bare words may be intermixed with variable expressions.
# Bare words may be intermixed with expressions.
.if V${:UA}R
# expect+1: ok
. info ok
@ -135,13 +135,13 @@ VAR= defined
.if 0${:Ux00}
. error
.else
# expect+1: Numbers can be composed from literals and variable expressions.
. info Numbers can be composed from literals and variable expressions.
# expect+1: Numbers can be composed from literals and expressions.
. info Numbers can be composed from literals and expressions.
.endif
.if 0${:Ux01}
# expect+1: Numbers can be composed from literals and variable expressions.
. info Numbers can be composed from literals and variable expressions.
# expect+1: Numbers can be composed from literals and expressions.
. info Numbers can be composed from literals and expressions.
.else
. error
.endif
@ -205,7 +205,7 @@ ${:U\\\\}= backslash
# expect+1: Malformed conditional (left == right)
.if left == right
.endif
# Before cond.c 1.276 from 2021-09-21, a variable expression containing the
# Before cond.c 1.276 from 2021-09-21, an 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)
@ -245,7 +245,7 @@ ${:U\\\\}= backslash
# A different situation is when CondParser.leftUnquotedOK is true. This
# situation arises in expressions of the form ${cond:?yes:no}. As of
# 2021-12-30, the condition in such an expression is evaluated before parsing
# the condition, see varmod-ifelse.mk. To pass a variable expression to the
# the condition, see varmod-ifelse.mk. To pass an expression to the
# condition parser, it needs to be escaped. This rarely happens in practice,
# in most cases the conditions are simple enough that it doesn't matter
# whether the condition is first evaluated and then parsed, or vice versa.

View File

@ -10,7 +10,7 @@ make: "cond-token-string.mk" line 61: The string literal " " is not empty, even
CondParser_Eval: "${UNDEF}"
make: "cond-token-string.mk" line 71: An undefined variable in quotes expands to an empty string, which then evaluates to false.
CondParser_Eval: "${:Uvalue}"
make: "cond-token-string.mk" line 77: A nonempty variable expression evaluates to true.
make: "cond-token-string.mk" line 77: A nonempty expression evaluates to true.
CondParser_Eval: "${:U}"
make: "cond-token-string.mk" line 86: An empty variable evaluates to false.
CondParser_Eval: ("${VAR}")

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-string.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-token-string.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for quoted string literals in .if conditions.
#
@ -26,7 +26,7 @@
.endif
# The 'x' produces a "Malformed conditional" since the left-hand side of a
# comparison in an .if directive must be either a variable expression, a
# comparison in an .if directive must be either an expression, a
# quoted string literal or a number that starts with a digit.
# expect+1: Malformed conditional (x${:Uvalue} == "")
.if x${:Uvalue} == ""
@ -73,8 +73,8 @@
.endif
.if "${:Uvalue}"
# expect+1: A nonempty variable expression evaluates to true.
. info A nonempty variable expression evaluates to true.
# expect+1: A nonempty expression evaluates to true.
. info A nonempty expression evaluates to true.
.else
. error
.endif
@ -87,7 +87,7 @@
.endif
# A non-empty string evaluates to true, no matter if it's a literal string or
# if it contains variable expressions. The parentheses are not necessary for
# if it contains expressions. The parentheses are not necessary for
# the parser, in this case their only purpose is to make the code harder to
# read for humans.
VAR= value

View File

@ -1,11 +1,11 @@
# $NetBSD: cond-token-var.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: cond-token-var.mk,v 1.8 2023/11/19 21:47:52 rillig Exp $
#
# Tests for variable expressions in .if conditions.
# Tests for expressions in .if conditions.
#
# Note the fine distinction between a variable and a variable expression.
# A variable has a name and a value. To access the value, one writes a
# variable expression of the form ${VAR}. This is a simple variable
# expression. Variable expressions can get more complicated by adding
# Note the fine distinction between a variable and an expression.
# A variable has a name and a value. To access the value, one writes an
# expression of the form ${VAR}. This is a simple
# expression. Expressions can get more complicated by adding
# variable modifiers such as in ${VAR:Mpattern}.
#
# XXX: Strictly speaking, variable modifiers should be called expression
@ -49,7 +49,7 @@ DEF= defined
.if ${UNDEF:U}
.endif
# If the value of the variable expression is a number, it is compared against
# If the value of the expression is a number, it is compared against
# zero.
.if ${:U0}
. error
@ -58,7 +58,7 @@ DEF= defined
. error
.endif
# If the value of the variable expression is not a number, any non-empty
# If the value of the expression is not a number, any non-empty
# value evaluates to true, even if there is only whitespace.
.if ${:U}
. error

View File

@ -22,7 +22,7 @@ Var_Parse: $): (parse-only)
Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 $)
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Malformed variable expression at "$)"
make: Malformed expression at "$)"
def2
a-def2-b
1-2-NDIRECT_2-2-1

View File

@ -1,4 +1,4 @@
# $NetBSD: dep-var.mk,v 1.8 2023/05/10 15:53:32 rillig Exp $
# $NetBSD: dep-var.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
@ -9,11 +9,11 @@
# expect: Var_Parse: ${UNDEF1} (eval-defined)
# Even though undefined expressions should lead to errors, no error message is
# generated for this line. The variable expression ${UNDEF1} simply expands
# generated for this line. The expression ${UNDEF1} simply expands
# to an empty string.
all: ${UNDEF1}
# Using a double dollar in order to circumvent immediate variable expansion
# Using a double dollar in order to circumvent immediate expression expansion
# feels like unintended behavior. At least the manual page says nothing at
# all about defined or undefined variables in dependency lines.
#
@ -25,7 +25,7 @@ all: $${DEF2} a-$${DEF2}-b
# XXX: The -dv log says later when expanding the sources of 'all':
# Var_Parse: ${UNDEF3} (eval-defined)
# but no error message is generated for this line, just like for UNDEF1.
# The variable expression ${UNDEF3} simply expands to an empty string.
# The expression ${UNDEF3} simply expands to an empty string.
all: $${UNDEF3}
# Try out how many levels of indirection are really expanded in dependency
@ -63,7 +63,7 @@ INDIRECT_3= indirect
UNDEF1= undef1
DEF2= def2
# Cover the code in SuffExpandChildren that deals with malformed variable
# Cover the code in SuffExpandChildren that deals with malformed
# expressions.
#
# This seems to be an edge case that never happens in practice, and it would

View File

@ -1,8 +1,8 @@
ignore-errors begin
false ignore-errors
*** Error code 1 (ignored)
ignore-errors end
all begin
*** Error code 1 (ignored)
false all
*** Error code 1 (continuing)

View File

@ -16,6 +16,7 @@ make: *** deptgt-delete_on_error-regular-delete removed
Stop.
make: stopped in unit-tests
*** Error code 1 (ignored)
Parallel mode
> deptgt-delete_on_error-regular; false
@ -45,5 +46,4 @@ make: stopped in unit-tests
make: stopped in unit-tests
*** Error code 1 (ignored)
*** Error code 1 (ignored)
exit status 0

View File

@ -1,4 +1,4 @@
Global: delete DOLLAR (not found)
Global: ignoring delete 'DOLLAR' as it is not found
Command: DOLLAR = $$$$
Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt-makeflags.mk,v 1.7 2021/11/29 00:17:10 rillig Exp $
# $NetBSD: deptgt-makeflags.mk,v 1.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the special target .MAKEFLAGS in dependency declarations,
# which adds command line options later, at parse time.
@ -65,9 +65,9 @@
.endif
# Next try at defining another newline variable. Since whitespace around the
# variable value is trimmed, two empty variable expressions ${:U} surround the
# variable value is trimmed, two empty expressions ${:U} surround the
# literal newline now. This prevents the newline from being skipped during
# parsing. The ':=' assignment operator expands the empty variable
# parsing. The ':=' assignment operator expands the empty
# expressions, leaving only the newline as the variable value.
#
# This is one of the very few ways (maybe even the only one) to inject literal

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt.mk,v 1.14 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: deptgt.mk,v 1.16 2023/12/17 09:44:00 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@ -30,7 +30,7 @@ VAR=value # targets := NULL
: command3 # parse error, since targets == NULL
# In a dependency declaration, the list of targets can be empty.
# It doesn't matter whether the empty string is generated by a variable
# It doesn't matter whether the empty string is generated by an
# expression or whether it is just omitted.
.MAKEFLAGS: -dp
${:U}: empty-source
@ -54,7 +54,7 @@ $$$$$$$${:U:Z}:
# expect+1: warning: Extra target (ordinary) ignored
.PATH ordinary:
# expect+1: Special and mundane targets don't mix. Mundane ones ignored
# expect+1: warning: Special and mundane targets don't mix. Mundane ones ignored
ordinary .PATH:
all:

View File

@ -1,4 +1,4 @@
# $NetBSD: dir.mk,v 1.10 2023/01/24 00:24:02 sjg Exp $
# $NetBSD: dir.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for dir.c.
@ -67,7 +67,7 @@ fetch fetch-post extract extract-post:
# The expansions may have duplicates.
# When the source of the dependency line is expanded later, each of the
# expanded words will be the same.
# expanded words resolves to the same node.
all: dup-{1,1,1,1,1,1,1}
dup-1:

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-else.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-else.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .else directive.
#
@ -45,7 +45,7 @@
.else # comment
.endif
# A variable expression does count as an argument, even if it is empty.
# An expression does count as an argument, even if it is empty.
.if 0
# expect+1: The .else directive does not take arguments
.else ${:U}

View File

@ -1,5 +1,6 @@
make: "directive-export-gmake.mk" line 71: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
make: "directive-export-gmake.mk" line 85: 16:00:00
make: "directive-export-gmake.mk" line 92: Variable/Value missing from "export"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-export-gmake.mk,v 1.7 2023/08/20 20:48:32 rillig Exp $
# $NetBSD: directive-export-gmake.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the export directive (without leading dot), as in GNU make.
@ -67,7 +67,7 @@ export VAR=an ${UNDEF} variable
.for value in 1
# XXX: The ':' in this line is inside an expression and should thus not be
# interpreted as a dependency operator.
# expect+1: Invalid line 'export VAR=${:U1}'
# expect+1: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
export VAR=${value}
.endfor
@ -83,3 +83,24 @@ INDIRECT_TZ= ${:UAmerica/Los_Angeles}
export TZ=${INDIRECT_TZ}
# expect+1: 16:00:00
.info ${%T:L:localtime=86400}
# The '=' must be present in the unexpanded line, it cannot be generated by
# an expression.
EQ= =
# expect+1: Variable/Value missing from "export"
export EQ_VAR${EQ}eq-value
.if ${:!env!:MEQ_VAR=*}
. error
.endif
# The variable name must be given directly, it is not expanded. The name of
# the exported variable thus starts with a '$', and that name may be filtered
# out by the platform.
INDIRECT_NAME= I_NAME
INDIRECT_VALUE= indirect value
export ${INDIRECT_NAME}=${INDIRECT_VALUE}
.if ${:!env!:MI_NAME=*}
. error
.endif

View File

@ -1,4 +1,4 @@
make: "directive-export.mk" line 50: 00:00:00
make: "directive-export.mk" line 55: 00:00:00
make: "directive-export.mk" line 58: 16:00:00
make: "directive-export.mk" line 56: 00:00:00
make: "directive-export.mk" line 61: 00:00:00
make: "directive-export.mk" line 64: 16:00:00
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-export.mk,v 1.9 2023/08/20 20:48:32 rillig Exp $
# $NetBSD: directive-export.mk,v 1.10 2023/11/19 09:45:19 rillig Exp $
#
# Tests for the .export directive.
#
@ -35,7 +35,13 @@ VAR= value $$ ${INDIRECT}
.export ${:U}
# Trigger the "This isn't going to end well" in ExportVarEnv.
# Before a child process is started, whether for the '!=' assignment operator
# or for the ':sh' modifier, all variables that were marked for being exported
# are expanded and then exported. If expanding such a variable requires
# running a child command, the marked-as-exported variables would need to be
# exported first, ending in an endless loop. To avoid this endless loop,
# don't export the variables while preparing a child process, see
# ExportVarEnv.
EMPTY_SHELL= ${:sh}
.export EMPTY_SHELL # only marked for export at this point
_!= :;: # Force the variable to be actually exported.

View File

@ -1,12 +1,12 @@
# $NetBSD: directive-for-empty.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-for-empty.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Tests for .for loops containing conditions of the form 'empty(var:...)'.
#
# When a .for loop is expanded, variable expressions in the body of the loop
# When a .for loop is expanded, expressions in the body of the loop
# are replaced with expressions containing the variable values. This
# replacement is a bit naive but covers most of the practical cases. The one
# popular exception is the condition 'empty(var:Modifiers)', which does not
# look like a variable expression and is thus not replaced.
# look like an expression and is thus not replaced.
#
# See also:
# https://gnats.netbsd.org/43821
@ -24,7 +24,7 @@
.endfor
# In conditions, the function call to 'empty' does not look like a variable
# In conditions, the function call to 'empty' does not look like an
# expression, therefore it is not replaced. Since there is no global variable
# named 'i', this expression makes for a leaky abstraction. If the .for
# variables were real variables, calling 'empty' would work on them as well.
@ -51,8 +51,8 @@
# loop would be naive and require many special cases, as there are many cases
# that need to be considered when deciding whether the token 'empty' is a
# function call or not, as demonstrated by the following examples. For
# variable expressions like '${i:Modifiers}', this is simpler as a single
# dollar almost always starts a variable expression. For counterexamples and
# expressions like '${i:Modifiers}', this is simpler as a single
# dollar almost always starts an expression. For counterexamples and
# edge cases, see directive-for-escape.mk. Adding another such tricky detail
# is out of the question.
.MAKEFLAGS: -df

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-errors.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-for-errors.mk,v 1.9 2023/12/19 19:33:40 rillig Exp $
#
# Tests for error handling in .for loops.
@ -9,7 +9,7 @@
.fori in 1 2 3
. warning <${i}>
.endfor
# expect-2: <>
# expect-2: warning: <>
# expect-2: for-less endfor
@ -35,7 +35,7 @@
#
# The '$$' was not replaced with the values '1' or '3' from the .for loop,
# instead it was kept as-is, and when the .info directive expanded its
# argument, each '$$' got replaced with a single '$'. The "long variable
# argument, each '$$' got replaced with a single '$'. The "long
# expression" ${$} got replaced though, even though this would be a parse
# error everywhere outside a .for loop.
${:U\$}= dollar # see whether the "variable" '$' is local
@ -67,7 +67,7 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
# The list of values after the 'in' may be empty, no matter if this emptiness
# comes from an empty expansion or even from a syntactically empty line.
# comes from an expanded expression or from a syntactically empty line.
.for i in
. info Would be reached if there were items to loop over.
.endfor
@ -89,6 +89,6 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
.for i in 1 2 ${:U3:Z} 4
. warning Should not be reached.
.endfor
# expect-2: Should not be reached.
# expect-3: Should not be reached.
# expect-4: Should not be reached.
# expect-2: warning: Should not be reached.
# expect-3: warning: Should not be reached.
# expect-4: warning: Should not be reached.

View File

@ -1,12 +1,12 @@
For: end for 1
For: loop body with chars = !"#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: Unclosed expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
For: loop body with chars = !"\\#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: Unclosed expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 30: !"\\
For: end for 1
For: loop body with i = $:

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-escape.mk,v 1.21 2023/06/23 06:11:06 rillig Exp $
# $NetBSD: directive-for-escape.mk,v 1.23 2023/11/19 22:32:44 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
@ -69,7 +69,7 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
# 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.
# The value of the variable VALUES is not meant to be an expression.
# Instead, it is meant to represent literal text, the only escaping mechanism
# being that each '$' is written as '$$'.
VALUES= $${UNDEF:U\$$\$$ {{}} end}
@ -128,7 +128,7 @@ ${:U\\}= backslash
# XXX: It is not the job of ExprLen to parse an expression, it is naive to
# expect ExprLen to get all the details right in just a few lines of code.
# Each variable modifier has its own inconsistent way of parsing nested
# variable expressions, braces and parentheses. (Compare ':M', ':S', and
# expressions, braces and parentheses. (Compare ':M', ':S', and
# ':D' for details.) The only sensible thing to do is therefore to let
# Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
@ -147,7 +147,7 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
# expect-2: $
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# could contain colons, which affected variable expressions having this exact
# could contain colons, which affected expressions having this exact
# modifier. This possibility was neither intended nor documented.
NUMBERS= one two three
# expect+1: invalid character ':' in .for loop variable name
@ -156,7 +156,7 @@ NUMBERS= one two three
.endfor
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
# could contain braces, which allowed to replace sequences of variable
# could contain braces, which allowed to replace sequences of
# expressions. This possibility was neither intended nor documented.
BASENAME= one
EXT= .c
@ -203,7 +203,7 @@ i,= comma
. 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
# an 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.

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for-if.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Test for a .for directive that contains an .if directive.
#
@ -71,7 +71,7 @@ _!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
.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
# 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.

View File

@ -17,14 +17,15 @@ make: "directive-for.mk" line 146: }{ }{ }{
make: "directive-for.mk" line 166: invalid character ':' in .for loop variable name
make: "directive-for.mk" line 173: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 185: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 196: Unknown modifier "Z"
make: "directive-for.mk" line 197: XXX: Not reached word1
make: "directive-for.mk" line 197: XXX: Not reached word3
make: "directive-for.mk" line 204: no iteration variables in for
make: "directive-for.mk" line 230: 1 open conditional
make: "directive-for.mk" line 246: for-less endfor
make: "directive-for.mk" line 247: if-less endif
make: "directive-for.mk" line 255: if-less endif
make: "directive-for.mk" line 210: Unknown modifier "Z"
make: "directive-for.mk" line 211: XXX: Should not reach word1
make: "directive-for.mk" line 211: XXX: Should not reach before--after
make: "directive-for.mk" line 211: XXX: Should not reach word3
make: "directive-for.mk" line 219: no iteration variables in for
make: "directive-for.mk" line 245: 1 open conditional
make: "directive-for.mk" line 261: for-less endfor
make: "directive-for.mk" line 262: if-less endif
make: "directive-for.mk" line 270: if-less endif
For: new loop 2
For: end for 2
For: end for 1
@ -35,7 +36,7 @@ For: loop body with outer = o:
endfor
For: end for 1
For: loop body with inner = i:
make: "directive-for.mk" line 303: newline-item=(a)
make: "directive-for.mk" line 318: newline-item=(a)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-for.mk,v 1.22 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-for.mk,v 1.24 2023/12/06 22:28:20 rillig Exp $
#
# Tests for the .for directive.
#
@ -25,7 +25,7 @@ NUMBERS+= ${num}
# The .for loop also works for multiple iteration variables.
# This is something that the modifier :@ cannot do.
# This is something that the modifier :@ cannot do as easily.
.for name value in VARNAME value NAME2 value2
${name}= ${value}
.endfor
@ -189,15 +189,30 @@ INDIRECT= direct
.endfor
# Regular global variables and the "variables" from the .for loop don't
# interfere with each other. In the following snippet, the variable 'DIRECT'
# is used both as a global variable, as well as an iteration variable in the
# .for loop. The expression '${INDIRECT}' refers to the global variable, not
# to the one from the .for loop.
DIRECT= global
INDIRECT= ${DIRECT}
.for DIRECT in iteration
. if "${DIRECT} ${INDIRECT}" != "iteration global"
. error
. endif
.endfor
# XXX: A parse error or evaluation error in the items of the .for loop
# should skip the whole loop. As of 2023-05-09, the loop is expanded as
# usual.
# expect+1: Unknown modifier "Z"
.for var in word1 ${:Uword2:Z} word3
. info XXX: Not reached ${var}
.for var in word1 before-${:Uword2:Z}-after word3
. info XXX: Should not reach ${var}
.endfor
# expect-2: XXX: Not reached word1
# expect-3: XXX: Not reached word3
# expect-2: XXX: Should not reach word1
# expect-3: XXX: Should not reach before--after
# expect-4: XXX: Should not reach word3
# An empty list of variables to the left of the 'in' is a parse error.

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-ifmake.mk,v 1.11 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-ifmake.mk,v 1.12 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .ifmake directive, which provides a shortcut for asking
# whether a certain target is requested to be made from the command line.
@ -75,7 +75,7 @@
. error
.endif
# A condition that consists of a variable expression only (without any
# A condition that consists of an expression only (without any
# comparison operator) can be used with .if and the other .ifxxx directives.
.ifmake ${:Ufirst}
# expect+1: ok

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-ifndef.mk,v 1.8 2023/06/19 20:44:06 rillig Exp $
# $NetBSD: directive-ifndef.mk,v 1.9 2023/10/19 18:24:33 rillig Exp $
#
# Tests for the .ifndef directive, which can be used for multiple-inclusion
# guards. In contrast to C, where #ifndef and #define nicely line up the
@ -50,4 +50,38 @@ DEFINED=
. error
.endif
# The negation from the 'if-not-defined' directive only applies to bare words,
# but not to numbers, quoted strings or expressions. Those are evaluated
# without extra negation, just like in a plain '.if' directive.
.ifndef 0
. error
.endif
.ifndef 1
.else
. error
.endif
.ifndef ""
. error
.endif
.ifndef "word"
.else
. error
.endif
.ifndef ${:UUNDEFINED}
.else
. error
.endif
.ifndef ${:UDEFINED}
. error
.endif
.ifndef ${:U0}
. error
.endif
.ifndef ${:U1}
.else
. error
.endif
all:

View File

@ -2,6 +2,10 @@ Parse_PushInput: file variable-ifndef.tmp, line 1
Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
Parse_PushInput: file variable-ifndef-zero.tmp, line 1
Parse_PushInput: file variable-ifndef-zero.tmp, line 1
Parse_PushInput: file variable-ifndef-one.tmp, line 1
Parse_PushInput: file variable-ifndef-one.tmp, line 1
Parse_PushInput: file comments.tmp, line 1
Skipping 'comments.tmp' because 'COMMENTS' is defined
Parse_PushInput: file variable-if.tmp, line 1
@ -10,10 +14,16 @@ Parse_PushInput: file variable-if-reuse.tmp, line 1
Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
Parse_PushInput: file variable-if-triple-negation.tmp, line 1
Parse_PushInput: file variable-if-triple-negation.tmp, line 1
Parse_PushInput: file variable-if-spaced.tmp, line 1
Parse_PushInput: file variable-if-spaced.tmp, line 1
Parse_PushInput: file variable-if-parenthesized.tmp, line 1
Parse_PushInput: file variable-if-parenthesized.tmp, line 1
Parse_PushInput: file variable-ifdef-negated.tmp, line 1
Parse_PushInput: file variable-ifdef-negated.tmp, line 1
Parse_PushInput: file variable-name-mismatch.tmp, line 1
Parse_PushInput: file variable-name-mismatch.tmp, line 1
Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
Parse_PushInput: file variable-name-exclamation.tmp, line 1
Parse_PushInput: file variable-name-exclamation.tmp, line 1
Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
@ -85,4 +95,10 @@ Parse_PushInput: file target-already-defined.tmp, line 1
Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
Parse_PushInput: file target-name-exclamation.tmp, line 1
Parse_PushInput: file target-name-exclamation.tmp, line 1
Parse_PushInput: file target-name-parenthesized.tmp, line 1
Parse_PushInput: file target-name-parenthesized.tmp, line 1
Parse_PushInput: file target-call-parenthesized.tmp, line 1
Parse_PushInput: file target-call-parenthesized.tmp, line 1
Parse_PushInput: file multiline.tmp, line 1
Skipping 'multiline.tmp' because 'MULTILINE' is defined
exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-include-guard.mk,v 1.12 2023/08/11 04:56:31 rillig Exp $
# $NetBSD: directive-include-guard.mk,v 1.16 2023/12/17 14:07:22 rillig Exp $
#
# Tests for multiple-inclusion guards in makefiles.
#
@ -15,8 +15,9 @@
# .endif
#
# When such a file is included for the second or later time, and the guard
# variable or the guard target is defined, including the file has no effect,
# as all its content is skipped.
# variable or the guard target is defined, the file is skipped completely, as
# including it would not have any effect, not even on the special variable
# '.MAKE.MAKEFILES', as that variable skips duplicate pathnames.
#
# See also:
# https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
@ -27,7 +28,7 @@
# This is the canonical form of a variable-based multiple-inclusion guard.
INCS+= variable-ifndef
CASES+= variable-ifndef
LINES.variable-ifndef= \
'.ifndef VARIABLE_IFNDEF' \
'VARIABLE_IFNDEF=' \
@ -38,7 +39,7 @@ LINES.variable-ifndef= \
# A file that reuses a guard from a previous file (or whose guard is defined
# for any other reason) is only processed once, to see whether it is guarded.
# Its content is skipped, therefore the syntax error is not detected.
INCS+= variable-ifndef-reuse
CASES+= variable-ifndef-reuse
LINES.variable-ifndef-reuse= \
'.ifndef VARIABLE_IFNDEF' \
'syntax error' \
@ -46,8 +47,27 @@ LINES.variable-ifndef-reuse= \
# expect: Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
# expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
# The guard variable cannot be a number, as numbers are interpreted
# differently from bare words.
CASES+= variable-ifndef-zero
LINES.variable-ifndef-zero= \
'.ifndef 0e0' \
'syntax error' \
'.endif'
# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
# The guard variable cannot be a number, as numbers are interpreted
# differently from bare words.
CASES+= variable-ifndef-one
LINES.variable-ifndef-one= \
'.ifndef 1' \
'.endif'
# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
# Comments and empty lines do not affect the multiple-inclusion guard.
INCS+= comments
CASES+= comments
LINES.comments= \
'\# comment' \
'' \
@ -62,7 +82,7 @@ LINES.comments= \
# An alternative form uses the 'defined' function. It is more verbose than
# the canonical form but avoids the '.ifndef' directive, as that directive is
# not commonly used.
INCS+= variable-if
CASES+= variable-if
LINES.variable-if= \
'.if !defined(VARIABLE_IF)' \
'VARIABLE_IF=' \
@ -73,7 +93,7 @@ LINES.variable-if= \
# A file that reuses a guard from a previous file (or whose guard is defined
# for any other reason) is only processed once, to see whether it is guarded.
# Its content is skipped, therefore the syntax error is not detected.
INCS+= variable-if-reuse
CASES+= variable-if-reuse
LINES.variable-if-reuse= \
'.if !defined(VARIABLE_IF)' \
'syntax error' \
@ -83,7 +103,7 @@ LINES.variable-if-reuse= \
# Triple negation is so uncommon that it's not recognized, even though it has
# the same effect as a single negation.
INCS+= variable-if-triple-negation
CASES+= variable-if-triple-negation
LINES.variable-if-triple-negation= \
'.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \
'VARIABLE_IF_TRIPLE_NEGATION=' \
@ -91,9 +111,29 @@ LINES.variable-if-triple-negation= \
# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
# If the guard variable is enclosed in spaces, it does not have an effect, as
# that form is not common in practice.
CASES+= variable-if-spaced
LINES.variable-if-spaced= \
'.if !defined( VARIABLE_IF_SPACED )' \
'VARIABLE_IF_SPACED=' \
'.endif'
# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
# If the guard variable condition is enclosed in parentheses, it does not have
# an effect, as that form is not common in practice.
CASES+= variable-if-parenthesized
LINES.variable-if-parenthesized= \
'.if (!defined(VARIABLE_IF_PARENTHESIZED))' \
'VARIABLE_IF_PARENTHESIZED=' \
'.endif'
# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
# A conditional other than '.if' or '.ifndef' does not guard the file, even if
# it is otherwise equivalent to the above accepted forms.
INCS+= variable-ifdef-negated
CASES+= variable-ifdef-negated
LINES.variable-ifdef-negated= \
'.ifdef !VARIABLE_IFDEF_NEGATED' \
'VARIABLE_IFDEF_NEGATED=' \
@ -102,7 +142,7 @@ LINES.variable-ifdef-negated= \
# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
# The variable names in the '.if' and the assignment must be the same.
INCS+= variable-name-mismatch
CASES+= variable-name-mismatch
LINES.variable-name-mismatch= \
'.ifndef VARIABLE_NAME_MISMATCH' \
'VARIABLE_NAME_DIFFERENT=' \
@ -110,13 +150,23 @@ LINES.variable-name-mismatch= \
# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
# If the guard variable condition is enclosed in parentheses, it does not have
# an effect, as that form is not common in practice.
CASES+= variable-ifndef-parenthesized
LINES.variable-ifndef-parenthesized= \
'.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \
'VARIABLE_IFNDEF_PARENTHESIZED=' \
'.endif'
# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
# The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
# the '!' would be a negation. It is syntactically valid in a '.if !defined'
# condition, but this case is so uncommon that the guard mechanism doesn't
# accept '!' in the guard variable name. Furthermore, when defining the
# variable, the character '!' has to be escaped, to prevent it from being
# interpreted as the '!' dependency operator.
INCS+= variable-name-exclamation
CASES+= variable-name-exclamation
LINES.variable-name-exclamation= \
'.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
'${:U!}VARIABLE_NAME_EXCLAMATION=' \
@ -124,11 +174,11 @@ LINES.variable-name-exclamation= \
# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
# A variable name can contain a '!' in the middle, as that character is
# interpreted as an ordinary character in conditions as well as on the left
# side of a variable assignment. For guard variable names, the '!' is not
# supported in any place, though.
INCS+= variable-name-exclamation-middle
# In general, a variable name can contain a '!' in the middle, as that
# character is interpreted as an ordinary character in conditions as well as
# on the left side of a variable assignment. For guard variable names, the
# '!' is not supported in any place, though.
CASES+= variable-name-exclamation-middle
LINES.variable-name-exclamation-middle= \
'.ifndef VARIABLE_NAME!MIDDLE' \
'VARIABLE_NAME!MIDDLE=' \
@ -141,7 +191,7 @@ LINES.variable-name-exclamation-middle= \
# where parentheses or braces are handled inconsistently to make this naming
# choice a bad idea, therefore these characters are not allowed in guard
# variable names.
INCS+= variable-name-parentheses
CASES+= variable-name-parentheses
LINES.variable-name-parentheses= \
'.ifndef VARIABLE_NAME(&)PARENTHESES' \
'VARIABLE_NAME(&)PARENTHESES=' \
@ -150,7 +200,7 @@ LINES.variable-name-parentheses= \
# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
# The guard condition must consist of only the guard variable, nothing else.
INCS+= variable-ifndef-plus
CASES+= variable-ifndef-plus
LINES.variable-ifndef-plus= \
'.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
'VARIABLE_IFNDEF_PLUS=' \
@ -160,7 +210,7 @@ LINES.variable-ifndef-plus= \
# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
# The guard condition must consist of only the guard variable, nothing else.
INCS+= variable-if-plus
CASES+= variable-if-plus
LINES.variable-if-plus= \
'.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
'VARIABLE_IF_PLUS=' \
@ -171,7 +221,7 @@ LINES.variable-if-plus= \
# The variable name in an '.ifndef' guard must be given directly, it must not
# contain any '$' expression.
INCS+= variable-ifndef-indirect
CASES+= variable-ifndef-indirect
LINES.variable-ifndef-indirect= \
'.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
'VARIABLE_IFNDEF_INDIRECT=' \
@ -181,7 +231,7 @@ LINES.variable-ifndef-indirect= \
# The variable name in an '.if' guard must be given directly, it must not
# contain any '$' expression.
INCS+= variable-if-indirect
CASES+= variable-if-indirect
LINES.variable-if-indirect= \
'.if !defined($${VARIABLE_IF_INDIRECT:L})' \
'VARIABLE_IF_INDIRECT=' \
@ -193,7 +243,7 @@ LINES.variable-if-indirect= \
# characters and underscores. The place where the guard variable is defined
# is more flexible, as long as the variable is defined at the point where the
# file is included the next time.
INCS+= variable-assign-indirect
CASES+= variable-assign-indirect
LINES.variable-assign-indirect= \
'.ifndef VARIABLE_ASSIGN_INDIRECT' \
'$${VARIABLE_ASSIGN_INDIRECT:L}=' \
@ -203,7 +253,7 @@ LINES.variable-assign-indirect= \
# The time at which the guard variable is defined doesn't matter, as long as
# it is defined at the point where the file is included the next time.
INCS+= variable-assign-late
CASES+= variable-assign-late
LINES.variable-assign-late= \
'.ifndef VARIABLE_ASSIGN_LATE' \
'VARIABLE_ASSIGN_LATE_OTHER=' \
@ -214,7 +264,7 @@ LINES.variable-assign-late= \
# The time at which the guard variable is defined doesn't matter, as long as
# it is defined at the point where the file is included the next time.
INCS+= variable-assign-nested
CASES+= variable-assign-nested
LINES.variable-assign-nested= \
'.ifndef VARIABLE_ASSIGN_NESTED' \
'. if 1' \
@ -231,7 +281,7 @@ LINES.variable-assign-nested= \
# skips almost all lines, as they are irrelevant, but the structure of the
# top-level '.if/.endif' conditional can be determined reliably enough to
# decide whether the file is guarded.
INCS+= variable-already-defined
CASES+= variable-already-defined
LINES.variable-already-defined= \
'.ifndef VARIABLE_ALREADY_DEFINED' \
'VARIABLE_ALREADY_DEFINED=' \
@ -244,7 +294,7 @@ VARIABLE_ALREADY_DEFINED=
# the file is processed but its content is skipped. If that same guard
# variable is undefined when the file is included the second time, the file is
# processed as usual.
INCS+= variable-defined-then-undefined
CASES+= variable-defined-then-undefined
LINES.variable-defined-then-undefined= \
'.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
'.endif'
@ -258,7 +308,7 @@ UNDEF_BETWEEN.variable-defined-then-undefined= \
# several, as each of these conditionals would require its separate guard.
# This case is not expected to occur in practice, as the two parts would
# rather be split into separate files.
INCS+= variable-two-times
CASES+= variable-two-times
LINES.variable-two-times= \
'.ifndef VARIABLE_TWO_TIMES_1' \
'VARIABLE_TWO_TIMES_1=' \
@ -275,7 +325,7 @@ LINES.variable-two-times= \
# Choosing unique guard names is the responsibility of the makefile authors.
# A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
# System-provided files typically start the guard names with '_'.
INCS+= variable-clash
CASES+= variable-clash
LINES.variable-clash= \
${LINES.variable-if}
# expect: Parse_PushInput: file variable-clash.tmp, line 1
@ -283,7 +333,7 @@ LINES.variable-clash= \
# The conditional must come before the assignment, otherwise the conditional
# is useless, as it always evaluates to false.
INCS+= variable-swapped
CASES+= variable-swapped
LINES.variable-swapped= \
'SWAPPED=' \
'.ifndef SWAPPED' \
@ -294,7 +344,7 @@ LINES.variable-swapped= \
# If the guard variable is undefined between the first and the second time the
# file is included, the guarded file is included again.
INCS+= variable-undef-between
CASES+= variable-undef-between
LINES.variable-undef-between= \
'.ifndef VARIABLE_UNDEF_BETWEEN' \
'VARIABLE_UNDEF_BETWEEN=' \
@ -306,7 +356,7 @@ UNDEF_BETWEEN.variable-undef-between= \
# If the guard variable is undefined while the file is included the first
# time, the guard does not have an effect, and the file is included again.
INCS+= variable-undef-inside
CASES+= variable-undef-inside
LINES.variable-undef-inside= \
'.ifndef VARIABLE_UNDEF_INSIDE' \
'VARIABLE_UNDEF_INSIDE=' \
@ -317,7 +367,7 @@ LINES.variable-undef-inside= \
# If the file does not define the guard variable, the guard does not have an
# effect, and the file is included again.
INCS+= variable-not-defined
CASES+= variable-not-defined
LINES.variable-not-defined= \
'.ifndef VARIABLE_NOT_DEFINED' \
'.endif'
@ -325,7 +375,7 @@ LINES.variable-not-defined= \
# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
# The outermost '.if' must not have an '.elif' branch.
INCS+= elif
CASES+= elif
LINES.elif= \
'.ifndef ELIF' \
'ELIF=' \
@ -336,7 +386,7 @@ LINES.elif= \
# When a file with an '.if/.elif/.endif' conditional at the top level is
# included, it is never optimized, as one of its branches is taken.
INCS+= elif-reuse
CASES+= elif-reuse
LINES.elif-reuse= \
'.ifndef ELIF' \
'syntax error' \
@ -346,7 +396,7 @@ LINES.elif-reuse= \
# expect: Parse_PushInput: file elif-reuse.tmp, line 1
# The outermost '.if' must not have an '.else' branch.
INCS+= else
CASES+= else
LINES.else= \
'.ifndef ELSE' \
'ELSE=' \
@ -357,7 +407,7 @@ LINES.else= \
# When a file with an '.if/.else/.endif' conditional at the top level is
# included, it is never optimized, as one of its branches is taken.
INCS+= else-reuse
CASES+= else-reuse
LINES.else-reuse= \
'.ifndef ELSE' \
'syntax error' \
@ -368,7 +418,7 @@ LINES.else-reuse= \
# The inner '.if' directives may have an '.elif' or '.else', and it doesn't
# matter which of their branches are taken.
INCS+= inner-if-elif-else
CASES+= inner-if-elif-else
LINES.inner-if-elif-else= \
'.ifndef INNER_IF_ELIF_ELSE' \
'INNER_IF_ELIF_ELSE=' \
@ -394,7 +444,7 @@ LINES.inner-if-elif-else= \
# usually chosen according to a pattern that doesn't interfere with real
# target names, they don't need to be declared '.PHONY' as they don't generate
# filesystem operations.
INCS+= target
CASES+= target
LINES.target= \
'.if !target(__target.tmp__)' \
'__target.tmp__: .NOTMAIN' \
@ -405,7 +455,7 @@ LINES.target= \
# When used for system files, the target name may include '<' and '>', for
# symmetry with the '.include <sys.mk>' directive. The characters '<' and '>'
# are ordinary characters.
INCS+= target-sys
CASES+= target-sys
LINES.target-sys= \
'.if !target(__<target-sys.tmp>__)' \
'__<target-sys.tmp>__: .NOTMAIN' \
@ -419,7 +469,7 @@ LINES.target-sys= \
# and once for determining the guard name. This double evaluation should not
# matter in practice, as guard expressions are expected to be simple,
# deterministic and without side effects.
INCS+= target-indirect
CASES+= target-indirect
LINES.target-indirect= \
'.if !target($${target-indirect.tmp:L})' \
'target-indirect.tmp: .NOTMAIN' \
@ -432,7 +482,7 @@ LINES.target-indirect= \
# pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
# This form does not work when the basename contains whitespace characters, as
# it is not possible to define a target with whitespace, not even by cheating.
INCS+= target-indirect-PARSEFILE
CASES+= target-indirect-PARSEFILE
LINES.target-indirect-PARSEFILE= \
'.if !target(__$${.PARSEFILE}__)' \
'__$${.PARSEFILE}__: .NOTMAIN' \
@ -442,7 +492,7 @@ LINES.target-indirect-PARSEFILE= \
# Two files with different basenames can both use the same syntactic pattern
# for the target guard name, as the expressions expand to different strings.
INCS+= target-indirect-PARSEFILE2
CASES+= target-indirect-PARSEFILE2
LINES.target-indirect-PARSEFILE2= \
'.if !target(__$${.PARSEFILE}__)' \
'__$${.PARSEFILE}__: .NOTMAIN' \
@ -453,8 +503,8 @@ LINES.target-indirect-PARSEFILE2= \
# Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include
# guard is the same as in the test case 'target-indirect-PARSEFILE', as the
# guard name only contains the basename but not the directory name. So even
# without defining the guard variable, the file is considered guarded.
INCS+= subdir/target-indirect-PARSEFILE
# without defining the guard target, the file is considered guarded.
CASES+= subdir/target-indirect-PARSEFILE
LINES.subdir/target-indirect-PARSEFILE= \
'.if !target(__$${.PARSEFILE}__)' \
'.endif'
@ -463,7 +513,7 @@ LINES.subdir/target-indirect-PARSEFILE= \
# Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
# or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
INCS+= target-indirect-PARSEDIR-PARSEFILE
CASES+= target-indirect-PARSEDIR-PARSEFILE
LINES.target-indirect-PARSEDIR-PARSEFILE= \
'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
@ -475,7 +525,7 @@ LINES.target-indirect-PARSEDIR-PARSEFILE= \
# Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
# subdirectory gets a different guard target name than the previous one.
INCS+= subdir/target-indirect-PARSEDIR-PARSEFILE
CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE
LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
@ -487,7 +537,7 @@ LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
# If the guard target is not defined when including the file the next time,
# the file is processed again.
INCS+= target-unguarded
CASES+= target-unguarded
LINES.target-unguarded= \
'.if !target(target-unguarded)' \
'.endif'
@ -495,7 +545,7 @@ LINES.target-unguarded= \
# expect: Parse_PushInput: file target-unguarded.tmp, line 1
# The guard condition must consist of only the guard target, nothing else.
INCS+= target-plus
CASES+= target-plus
LINES.target-plus= \
'.if !target(target-plus) && 1' \
'target-plus: .NOTMAIN' \
@ -505,7 +555,7 @@ LINES.target-plus= \
# If the guard target is defined before the file is included the first time,
# the file is read once and then considered guarded.
INCS+= target-already-defined
CASES+= target-already-defined
LINES.target-already-defined= \
'.if !target(target-already-defined)' \
'target-already-defined: .NOTMAIN' \
@ -523,7 +573,7 @@ target-already-defined: .NOTMAIN
# the '\' escapes the '!' from being a dependency operator, but when reading
# the target name, the '\' is kept, resulting in the target name
# '\!target-name-exclamation' instead of '!target-name-exclamation'.
INCS+= target-name-exclamation
CASES+= target-name-exclamation
LINES.target-name-exclamation= \
'.if !target(!target-name-exclamation)' \
'\!target-name-exclamation: .NOTMAIN' \
@ -531,13 +581,47 @@ LINES.target-name-exclamation= \
# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
# If the guard target name is enclosed in spaces, it does not have an effect,
# as that form is not common in practice.
CASES+= target-name-parenthesized
LINES.target-name-parenthesized= \
'.if !target( target-name-parenthesized )' \
'target-name-parenthesized: .NOTMAIN' \
'.endif'
# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
# If the guard target condition is enclosed in parentheses, it does not have
# an effect, as that form is not common in practice.
CASES+= target-call-parenthesized
LINES.target-call-parenthesized= \
'.if (!target(target-call-parenthesized))' \
'target-call-parenthesized: .NOTMAIN' \
'.endif'
# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
# If the '.if' or '.ifndef' directive spans more than a single line, it is
# still recognized as a guard condition. This case is entirely uncommon, but
# at the point where the guard condition is checked, line continuations have
# already been converted to spaces.
CASES+= multiline
LINES.multiline= \
'.\' \
' ifndef \' \
' MULTILINE' \
'MULTILINE=' \
'.endif'
# expect: Parse_PushInput: file multiline.tmp, line 1
# expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined
# Now run all test cases by including each of the files twice and looking at
# the debug output. The files that properly guard against multiple inclusion
# generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
#
# Some debug output lines are suppressed in the .exp file, see ./Makefile.
.for i in ${INCS}
.for i in ${CASES}
. for fname in $i.tmp
_:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
_!= printf '%s\n' ${LINES.$i} > ${fname}

View File

@ -1,4 +1,4 @@
# $NetBSD: directive-warning.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
# $NetBSD: directive-warning.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the .warning directive.
#
@ -16,7 +16,7 @@
.warnin message # misspelled
# expect+1: Missing argument for ".warning"
.warning # "Missing argument"
.warning message # expect+0: message
.warning message # expect+0: warning: message
# expect+1: Unknown directive "warnings"
.warnings # misspelled
# expect+1: Unknown directive "warnings"

View File

@ -1,4 +1,4 @@
# $NetBSD: directive.mk,v 1.8 2023/08/19 11:09:02 rillig Exp $
# $NetBSD: directive.mk,v 1.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the preprocessing directives, such as .if or .info.
@ -13,7 +13,7 @@
# expect+1: Unknown directive "indented"
. indented tab
# Directives must be written directly, not indirectly via variable
# Directives must be written directly, not indirectly via
# expressions.
# expect+1: Unknown directive ""
.${:Uinfo} directives cannot be indirect

View File

@ -1,5 +1,4 @@
var-1bs
printf "%s=:%s:\n" VAR1BS 111\\111; printf "%s=:%s:\n" VAR1BSa 111\\aaa; printf "%s=:%s:\n" VAR1BSA 111\\aaa; printf "%s=:%s:\n" VAR1BSda 111\\\$\{a\}; printf "%s=:%s:\n" VAR1BSdA 111\\\$\{A\}; printf "%s=:%s:\n" VAR1BSc 111\#\ backslash\ escapes\ comment\ char,\ so\ this\ is\ part\ of\ the\ value; printf "%s=:%s:\n" VAR1BSsc 111\\\ ;
VAR1BS=:111\111:
VAR1BSa=:111\aaa:
VAR1BSA=:111\aaa:
@ -8,25 +7,22 @@ VAR1BSdA=:111\${A}:
VAR1BSc=:111# backslash escapes comment char, so this is part of the value:
VAR1BSsc=:111\ :
var-2bs
printf "%s=:%s:\n" VAR2BS 222\\\\222; printf "%s=:%s:\n" VAR2BSa 222\\\\aaa; printf "%s=:%s:\n" VAR2BSA 222\\\\aaa; printf "%s=:%s:\n" VAR2BSda 222\\\\\$\{a\}; printf "%s=:%s:\n" VAR2BSdA 222\\\\\$\{A\}; printf "%s=:%s:\n" VAR2BSc 222\\\\; printf "%s=:%s:\n" VAR2BSsc 222\\\\;
VAR2BS=:222\\222:
VAR2BSa=:222\\aaa:
VAR2BSA=:222\\aaa:
VAR2BSda=:222\\${a}:
VAR2BSdA=:222\\${A}:
VAR2BSc=:222\\:
VAR2BSsc=:222\\:
var-1bsnl
printf "%s=:%s:\n" VAR1BSNL 111\ 111; printf "%s=:%s:\n" VAR1BSNLa 111\ aaa; printf "%s=:%s:\n" VAR1BSNLA 111\ aaa; printf "%s=:%s:\n" VAR1BSNLda 111\ \$\{a\}; printf "%s=:%s:\n" VAR1BSNLdA 111\ \$\{A\}; printf "%s=:%s:\n" VAR1BSNLc 111; printf "%s=:%s:\n" VAR1BSNLsc 111;
VAR1BSNL=:111 111:
VAR1BSNLa=:111 aaa:
VAR1BSNLA=:111 aaa:
VAR1BSNLda=:111 ${a}:
VAR1BSNLdA=:111 ${A}:
VAR1BSNLc=:111:
VAR1BSNLsc=:111:
VAR2.BS=:222\\222:
VAR2.BS.a=:222\\aaa:
VAR2.BS.A=:222\\aaa:
VAR2.BS.d.a=:222\\${a}:
VAR2.BS.d.A=:222\\${A}:
VAR2.BS.c=:222\\:
VAR2.BS.s.c=:222\\:
var-1bs-nl
VAR1.BS-NL=:111 111:
VAR1.BS-NL.a=:111 aaa:
VAR1.BS-NL.A=:111 aaa:
VAR1.BS-NL.d-a=:111 ${a}:
VAR1.BS-NL.d-A=:111 ${A}:
VAR1.BS-NL.c=:111:
VAR1.BS-NL.s-c=:111:
var-2bsnl
printf "%s=:%s:\n" VAR2BSNL 222\\\\; printf "%s=:%s:\n" VAR2BSNLa 222\\\\; printf "%s=:%s:\n" VAR2BSNLA 222\\\\; printf "%s=:%s:\n" VAR2BSNLda 222\\\\; printf "%s=:%s:\n" VAR2BSNLdA 222\\\\; printf "%s=:%s:\n" VAR2BSNLc 222\\\\; printf "%s=:%s:\n" VAR2BSNLsc 222\\\\;
VAR2BSNL=:222\\:
VAR2BSNLa=:222\\:
VAR2BSNLA=:222\\:
@ -35,7 +31,6 @@ VAR2BSNLdA=:222\\:
VAR2BSNLc=:222\\:
VAR2BSNLsc=:222\\:
var-3bsnl
printf "%s=:%s:\n" VAR3BSNL 333\\\\\ 333=; printf "%s=:%s:\n" VAR3BSNLa 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLA 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLda 333\\\\\ \$\{a\}=; printf "%s=:%s:\n" VAR3BSNLdA 333\\\\\ \$\{A\}=; printf "%s=:%s:\n" VAR3BSNLc 333\\\\; printf "%s=:%s:\n" VAR3BSNLsc 333\\\\;
VAR3BSNL=:333\\ 333=:
VAR3BSNLa=:333\\ aaa=:
VAR3BSNLA=:333\\ aaa=:
@ -44,7 +39,6 @@ VAR3BSNLdA=:333\\ ${A}=:
VAR3BSNLc=:333\\:
VAR3BSNLsc=:333\\:
var-1bsnl-space
printf "%s=:%s:\n" VAR1BSNL00 first\ line; printf "%s=:%s:\n" VAR1BSNL0 first\ line\ no\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLs first\ line\ one\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLss first\ line\ two\ spaces\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLt first\ line\ one\ tab\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLtt first\ line\ two\ tabs\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLxx first\ line\ many\ spaces\ and\ tabs\ \[\ \ \ \ \]\ on\ second\ line;
VAR1BSNL00=:first line:
VAR1BSNL0=:first line no space on second line:
VAR1BSNLs=:first line one space on second line:

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