mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-27 00:33:30 +00:00
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:
parent
1012cf15f7
commit
7a05a7153a
102
ChangeLog
102
ChangeLog
@ -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
17
README
@ -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>
|
||||
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20230909
|
||||
_MAKE_VERSION=20240108
|
||||
|
10
arch.c
10
arch.c
@ -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.
|
||||
*/
|
||||
|
6
bmake.1
6
bmake.1
@ -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.
|
||||
|
@ -1599,8 +1599,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
set the read-only attribute on the global variables specified as
|
||||
sources.
|
||||
|
||||
[1m.SHELL [22mSets the shell that [1mbmake [22muses to execute commands in jobs mode.
|
||||
The sources are a set of [4mfield[24m[1m=[4m[22mvalue[24m pairs.
|
||||
[1m.SHELL [22mSets the shell that [1mbmake [22muses to execute commands. The sources
|
||||
are a set of [4mfield[24m[1m=[4m[22mvalue[24m 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 |
16
boot-strap
16
boot-strap
@ -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
|
||||
#
|
||||
|
@ -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
6
buf.c
@ -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
4
buf.h
@ -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)
|
||||
{
|
||||
|
65
compat.c
65
compat.c
@ -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
111
cond.c
@ -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
34
configure
vendored
@ -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\\"
|
||||
|
||||
|
16
configure.in
16
configure.in
@ -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
330
dir.c
@ -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
14
for.c
@ -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
28
hash.c
@ -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
21
hash.h
@ -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
139
job.c
@ -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
6
job.h
@ -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
22
lst.c
@ -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
8
lst.h
@ -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
156
main.c
@ -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
6
make.1
@ -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
28
make.c
@ -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
5
make.h
@ -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
6
meta.c
@ -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}
|
||||
|
42
mk/ChangeLog
42
mk/ChangeLog
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
14
mk/dpadd.mk
14
mk/dpadd.mk
@ -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}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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
4
mk/install-mk
Normal file → Executable 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
|
||||
|
@ -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
|
||||
|
25
mk/lib.mk
25
mk/lib.mk
@ -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}
|
||||
|
95
mk/man.mk
95
mk/man.mk
@ -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:
|
||||
|
@ -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:[#]}} \
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
279
parse.c
279
parse.c
@ -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
83
str.c
@ -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
31
str.h
@ -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
168
suff.c
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
make: Bad conditional expression ' != "no"' in ' != "no"?:'
|
||||
make: Bad conditional expression ' != "no"' before '?:'
|
||||
yes
|
||||
no
|
||||
exit status 0
|
||||
|
@ -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
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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: == ""
|
||||
|
@ -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.
|
||||
|
@ -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}")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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} != "\$\$"
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 = $:
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user