Import bmake-20201117
o allow env var MAKE_OBJDIR_CHECK_WRITABLE=no to skip writable checks in InitObjdir. Explicit .OBJDIR target always allows read-only directory. o Fix building and unit-tests on non-BSD. o More code cleanup and refactoring. o More unit tests
This commit is contained in:
parent
302da1a3d3
commit
1b65f0bd2b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/NetBSD/bmake/dist/; revision=367860 svn path=/vendor/NetBSD/bmake/20201117/; revision=367861; tag=vendor/NetBSD/bmake/20201117
75
ChangeLog
75
ChangeLog
@ -1,3 +1,78 @@
|
||||
2020-11-17 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201117
|
||||
Merge with NetBSD make, pick up
|
||||
o fix some unit-tests when dash is .SHELL
|
||||
o rename Targ_NewGN to GNode_New
|
||||
o make some GNode functions const
|
||||
o main.c: call Targ_Init before Var_Init
|
||||
cleanup PrintOnError, getTmpdir and ParseBoolean
|
||||
o var.c: fix error message of failed :!cmd! modifier
|
||||
|
||||
2020-11-14 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201114
|
||||
Merge with NetBSD make, pick up
|
||||
o replace a few HashTable_CreateEntry with HashTable_Set
|
||||
o clean up cached_stats
|
||||
o rename DEFAULT to defaultNode
|
||||
o remove redundant struct make_stat
|
||||
o cond.c: in lint mode, check for ".else <cond>"
|
||||
use bitset for IfState
|
||||
replace large switch with if-else in Cond_EvalLine
|
||||
o job.c: clean up JobExec, JobStart, JobDoOutput
|
||||
use stderr for error message about failed touch
|
||||
clean up Job_Touch
|
||||
replace macro DBPRINTF with JobPrintln
|
||||
rename JobState to JobStatus
|
||||
main.c: switch cache for realpath from GNode to HashTable
|
||||
clean up Fatal
|
||||
clean up InitDefSysIncPath
|
||||
use progname instead of hard-coded 'make' in warning
|
||||
rename Main_SetVarObjdir to SetVarObjdir
|
||||
make.1: document the -S option
|
||||
make.c: fix debug output for GNode details
|
||||
use symbolic names in debug output of GNodes
|
||||
|
||||
2020-11-12 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* configure.in: fix --with-force-machine-arch
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201112
|
||||
Merge with NetBSD make, pick up
|
||||
o allow env var MAKE_OBJDIR_CHECK_WRITABLE=no to skip writable
|
||||
checks in InitObjdir. Explicit .OBJDIR target always allows
|
||||
read-only directory.
|
||||
o cond.c: clean up Cond_EvalLine
|
||||
|
||||
2020-11-11 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201111
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit-tests
|
||||
o style cleanup
|
||||
remove redundant parentheses from sizeof operator
|
||||
replace character literal 0 with '\0'.
|
||||
replace pointer literal 0 with NULL.
|
||||
remove redundant parentheses.
|
||||
replace (expr & mask) == 0 with !(expr & mask).
|
||||
use strict typing in conditions of the form !var
|
||||
o rename Make_OODate to GNode_IsOODate
|
||||
o rename Make_TimeStamp to GNode_UpdateYoungestChild
|
||||
o rename Var_Set_with_flags to Var_SetWithFlags
|
||||
o rename dieQuietly to shouldDieQuietly
|
||||
o buf.c: make API of Buf_Init simpler
|
||||
o compat.c: clean up Compat_Make, Compat_RunCommand,
|
||||
CompatDeleteTarget and CompatInterrupt
|
||||
o cond.c: in lint mode, only allow '&&' and '||', not '&' and '|'
|
||||
clean up CondParser_Comparison
|
||||
o main.c: rename getBoolean and s2Boolean
|
||||
rename MAKEFILE_PREFERENCE for consistency
|
||||
o parse.c: replace strstr in ParseMaybeSubMake with optimized code
|
||||
o var.c: rename VARE_ASSIGN to VARE_KEEP_DOLLAR
|
||||
replace emptyString with allocated empty string
|
||||
error out on unclosed expressions after the colon
|
||||
|
||||
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201101
|
||||
|
28
FILES
28
FILES
@ -75,8 +75,14 @@ unit-tests/archive-suffix.exp
|
||||
unit-tests/archive-suffix.mk
|
||||
unit-tests/archive.exp
|
||||
unit-tests/archive.mk
|
||||
unit-tests/cmd-errors-lint.exp
|
||||
unit-tests/cmd-errors-lint.mk
|
||||
unit-tests/cmd-errors.exp
|
||||
unit-tests/cmd-errors.mk
|
||||
unit-tests/cmd-interrupt.exp
|
||||
unit-tests/cmd-interrupt.mk
|
||||
unit-tests/cmdline-undefined.exp
|
||||
unit-tests/cmdline-undefined.mk
|
||||
unit-tests/cmdline.exp
|
||||
unit-tests/cmdline.mk
|
||||
unit-tests/comment.exp
|
||||
@ -115,10 +121,14 @@ unit-tests/cond-func.exp
|
||||
unit-tests/cond-func.mk
|
||||
unit-tests/cond-late.exp
|
||||
unit-tests/cond-late.mk
|
||||
unit-tests/cond-op-and-lint.exp
|
||||
unit-tests/cond-op-and-lint.mk
|
||||
unit-tests/cond-op-and.exp
|
||||
unit-tests/cond-op-and.mk
|
||||
unit-tests/cond-op-not.exp
|
||||
unit-tests/cond-op-not.mk
|
||||
unit-tests/cond-op-or-lint.exp
|
||||
unit-tests/cond-op-or-lint.mk
|
||||
unit-tests/cond-op-or.exp
|
||||
unit-tests/cond-op-or.mk
|
||||
unit-tests/cond-op-parentheses.exp
|
||||
@ -287,6 +297,8 @@ unit-tests/directive-for.exp
|
||||
unit-tests/directive-for.mk
|
||||
unit-tests/directive-hyphen-include.exp
|
||||
unit-tests/directive-hyphen-include.mk
|
||||
unit-tests/directive-if-nested.exp
|
||||
unit-tests/directive-if-nested.mk
|
||||
unit-tests/directive-if.exp
|
||||
unit-tests/directive-if.mk
|
||||
unit-tests/directive-ifdef.exp
|
||||
@ -315,8 +327,6 @@ unit-tests/directive-warning.exp
|
||||
unit-tests/directive-warning.mk
|
||||
unit-tests/directive.exp
|
||||
unit-tests/directive.mk
|
||||
unit-tests/directives.exp
|
||||
unit-tests/directives.mk
|
||||
unit-tests/dollar.exp
|
||||
unit-tests/dollar.mk
|
||||
unit-tests/doterror.exp
|
||||
@ -341,6 +351,8 @@ unit-tests/forloop.exp
|
||||
unit-tests/forloop.mk
|
||||
unit-tests/forsubst.exp
|
||||
unit-tests/forsubst.mk
|
||||
unit-tests/gnode-submake.exp
|
||||
unit-tests/gnode-submake.mk
|
||||
unit-tests/hanoi-include.exp
|
||||
unit-tests/hanoi-include.mk
|
||||
unit-tests/impsrc.exp
|
||||
@ -349,6 +361,8 @@ unit-tests/include-main.exp
|
||||
unit-tests/include-main.mk
|
||||
unit-tests/include-sub.mk
|
||||
unit-tests/include-subsub.mk
|
||||
unit-tests/job-flags.exp
|
||||
unit-tests/job-flags.mk
|
||||
unit-tests/job-output-long-lines.exp
|
||||
unit-tests/job-output-long-lines.mk
|
||||
unit-tests/lint.exp
|
||||
@ -365,6 +379,8 @@ unit-tests/modts.exp
|
||||
unit-tests/modts.mk
|
||||
unit-tests/modword.exp
|
||||
unit-tests/modword.mk
|
||||
unit-tests/objdir-writable.exp
|
||||
unit-tests/objdir-writable.mk
|
||||
unit-tests/opt-backwards.exp
|
||||
unit-tests/opt-backwards.mk
|
||||
unit-tests/opt-chdir.exp
|
||||
@ -447,6 +463,8 @@ unit-tests/opt-raw.exp
|
||||
unit-tests/opt-raw.mk
|
||||
unit-tests/opt-silent.exp
|
||||
unit-tests/opt-silent.mk
|
||||
unit-tests/opt-touch-jobs.exp
|
||||
unit-tests/opt-touch-jobs.mk
|
||||
unit-tests/opt-touch.exp
|
||||
unit-tests/opt-touch.mk
|
||||
unit-tests/opt-tracefile.exp
|
||||
@ -517,6 +535,8 @@ unit-tests/suff-main.exp
|
||||
unit-tests/suff-main.mk
|
||||
unit-tests/suff-rebuild.exp
|
||||
unit-tests/suff-rebuild.mk
|
||||
unit-tests/suff-self.exp
|
||||
unit-tests/suff-self.mk
|
||||
unit-tests/suff-transform-endless.exp
|
||||
unit-tests/suff-transform-endless.mk
|
||||
unit-tests/suff-transform-expand.exp
|
||||
@ -737,14 +757,14 @@ unit-tests/varname.exp
|
||||
unit-tests/varname.mk
|
||||
unit-tests/varparse-dynamic.exp
|
||||
unit-tests/varparse-dynamic.mk
|
||||
unit-tests/varparse-errors.exp
|
||||
unit-tests/varparse-errors.mk
|
||||
unit-tests/varparse-mod.exp
|
||||
unit-tests/varparse-mod.mk
|
||||
unit-tests/varparse-undef-partial.exp
|
||||
unit-tests/varparse-undef-partial.mk
|
||||
unit-tests/varquote.exp
|
||||
unit-tests/varquote.mk
|
||||
unit-tests/varshell.exp
|
||||
unit-tests/varshell.mk
|
||||
util.c
|
||||
var.c
|
||||
wait.h
|
||||
|
8
Makefile
8
Makefile
@ -1,4 +1,4 @@
|
||||
# $Id: Makefile,v 1.113 2020/10/26 17:55:09 sjg Exp $
|
||||
# $Id: Makefile,v 1.114 2020/11/13 21:47:25 sjg Exp $
|
||||
|
||||
PROG= bmake
|
||||
|
||||
@ -49,6 +49,12 @@ CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE
|
||||
CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}}
|
||||
COPTS.main.c+= "-DMAKE_VERSION=\"${_MAKE_VERSION}\""
|
||||
|
||||
.for x in FORCE_MACHINE FORCE_MACHINE_ARCH
|
||||
.ifdef $x
|
||||
COPTS.main.c+= "-D$x=\"${$x}\""
|
||||
.endif
|
||||
.endfor
|
||||
|
||||
# meta mode can be useful even without filemon
|
||||
# should be set by now
|
||||
USE_FILEMON ?= no
|
||||
|
@ -5,8 +5,8 @@ _MAKE_VERSION?=@_MAKE_VERSION@
|
||||
prefix?= @prefix@
|
||||
srcdir= @srcdir@
|
||||
CC?= @CC@
|
||||
MACHINE?= @machine@
|
||||
MACHINE_ARCH?= @machine_arch@
|
||||
@force_machine@MACHINE?= @machine@
|
||||
@force_machine_arch@MACHINE_ARCH?= @machine_arch@
|
||||
DEFAULT_SYS_PATH?= @default_sys_path@
|
||||
|
||||
CPPFLAGS+= @CPPFLAGS@
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20201101
|
||||
_MAKE_VERSION=20201117
|
||||
|
553
arch.c
553
arch.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 rillig Exp $ */
|
||||
/* $NetBSD: arch.c,v 1.177 2020/11/14 21:29:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,38 +68,38 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* arch.c --
|
||||
* Functions to manipulate libraries, archives and their members.
|
||||
/* Manipulate libraries, archives and their members.
|
||||
*
|
||||
* Once again, cacheing/hashing comes into play in the manipulation
|
||||
* of archives. The first time an archive is referenced, all of its members'
|
||||
* headers are read and hashed and the archive closed again. All hashed
|
||||
* archives are kept on a list which is searched each time an archive member
|
||||
* is referenced.
|
||||
* The first time an archive is referenced, all of its members' headers are
|
||||
* read and cached and the archive closed again. All cached archives are kept
|
||||
* on a list which is searched each time an archive member is referenced.
|
||||
*
|
||||
* The interface to this module is:
|
||||
*
|
||||
* Arch_Init Initialize this module.
|
||||
*
|
||||
* Arch_End Clean up this module.
|
||||
*
|
||||
* Arch_ParseArchive
|
||||
* Given an archive specification, return a list
|
||||
* of GNode's, one for each member in the spec.
|
||||
* FALSE is returned if the specification is
|
||||
* invalid for some reason.
|
||||
* Parse an archive specification such as
|
||||
* "archive.a(member1 member2)".
|
||||
*
|
||||
* Arch_Touch Alter the modification time of the archive
|
||||
* member described by the given node to be
|
||||
* the current time.
|
||||
* the time when make was started.
|
||||
*
|
||||
* Arch_TouchLib Update the modification time of the library
|
||||
* described by the given node. This is special
|
||||
* because it also updates the modification time
|
||||
* of the library's table of contents.
|
||||
*
|
||||
* Arch_MTime Find the modification time of a member of
|
||||
* an archive *in the archive*. The time is also
|
||||
* placed in the member's GNode. Returns the
|
||||
* modification time.
|
||||
* Arch_UpdateMTime
|
||||
* Find the modification time of a member of
|
||||
* an archive *in the archive* and place it in the
|
||||
* member's GNode.
|
||||
*
|
||||
* Arch_MemTime Find the modification time of a member of
|
||||
* Arch_UpdateMemberMTime
|
||||
* Find the modification time of a member of
|
||||
* an archive. Called when the member doesn't
|
||||
* already exist. Looks in the archive for the
|
||||
* modification time. Returns the modification
|
||||
@ -109,12 +109,7 @@
|
||||
* library name in the GNode should be in
|
||||
* -l<name> format.
|
||||
*
|
||||
* Arch_LibOODate Special function to decide if a library node
|
||||
* is out-of-date.
|
||||
*
|
||||
* Arch_Init Initialize this module.
|
||||
*
|
||||
* Arch_End Clean up this module.
|
||||
* Arch_LibOODate Decide if a library node is out-of-date.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
@ -151,16 +146,7 @@ struct ar_hdr {
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: arch.c,v 1.151 2020/10/31 18:41:07 rillig Exp $");
|
||||
|
||||
#ifdef TARGET_MACHINE
|
||||
#undef MAKE_MACHINE
|
||||
#define MAKE_MACHINE TARGET_MACHINE
|
||||
#endif
|
||||
#ifdef TARGET_MACHINE_ARCH
|
||||
#undef MAKE_MACHINE_ARCH
|
||||
#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH
|
||||
#endif
|
||||
MAKE_RCSID("$NetBSD: arch.c,v 1.177 2020/11/14 21:29:44 rillig Exp $");
|
||||
|
||||
typedef struct List ArchList;
|
||||
typedef struct ListNode ArchListNode;
|
||||
@ -230,38 +216,36 @@ ArchFree(void *ap)
|
||||
#endif
|
||||
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* Arch_ParseArchive --
|
||||
* Parse the archive specification in the given line and find/create
|
||||
* the nodes for the specified archive members, placing their nodes
|
||||
* on the given list.
|
||||
/*
|
||||
* Parse an archive specification such as "archive.a(member1 member2.${EXT})",
|
||||
* adding nodes for the expanded members to nodeLst. Nodes are created as
|
||||
* necessary.
|
||||
*
|
||||
* Input:
|
||||
* linePtr Pointer to start of specification
|
||||
* nodeLst Lst on which to place the nodes
|
||||
* ctxt Context in which to expand variables
|
||||
* pp The start of the specification.
|
||||
* nodeLst The list on which to place the nodes.
|
||||
* ctxt The context in which to expand variables.
|
||||
*
|
||||
* Results:
|
||||
* TRUE if it was a valid specification. The linePtr is updated
|
||||
* to point to the first non-space after the archive spec. The
|
||||
* nodes for the members are placed on the given list.
|
||||
*-----------------------------------------------------------------------
|
||||
* Output:
|
||||
* return TRUE if it was a valid specification.
|
||||
* *pp Points to the first non-space after the archive spec.
|
||||
* *nodeLst Nodes for the members have been added.
|
||||
*/
|
||||
Boolean
|
||||
Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
{
|
||||
char *cp; /* Pointer into line */
|
||||
GNode *gn; /* New node */
|
||||
char *libName; /* Library-part of specification */
|
||||
char *libName_freeIt = NULL;
|
||||
char *memName; /* Member-part of specification */
|
||||
char saveChar; /* Ending delimiter of member-name */
|
||||
Boolean subLibName; /* TRUE if libName should have/had
|
||||
* variable substitution performed on it */
|
||||
Boolean expandLibName; /* Whether the parsed libName contains
|
||||
* variable expressions that need to be
|
||||
* expanded */
|
||||
|
||||
libName = *linePtr;
|
||||
|
||||
subLibName = FALSE;
|
||||
libName = *pp;
|
||||
expandLibName = FALSE;
|
||||
|
||||
for (cp = libName; *cp != '(' && *cp != '\0';) {
|
||||
if (*cp == '$') {
|
||||
@ -274,7 +258,8 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
const char *result;
|
||||
Boolean isError;
|
||||
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
|
||||
/* XXX: is expanded twice: once here and once below */
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
&result, &result_freeIt);
|
||||
/* TODO: handle errors */
|
||||
isError = result == var_Error;
|
||||
@ -282,16 +267,17 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
if (isError)
|
||||
return FALSE;
|
||||
|
||||
subLibName = TRUE;
|
||||
expandLibName = TRUE;
|
||||
cp += nested_p - cp;
|
||||
} else
|
||||
cp++;
|
||||
}
|
||||
|
||||
*cp++ = '\0';
|
||||
if (subLibName) {
|
||||
(void)Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES, &libName);
|
||||
if (expandLibName) {
|
||||
(void)Var_Subst(libName, ctxt, VARE_WANTRES | VARE_UNDEFERR, &libName);
|
||||
/* TODO: handle errors */
|
||||
libName_freeIt = libName;
|
||||
}
|
||||
|
||||
|
||||
@ -317,7 +303,7 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
Boolean isError;
|
||||
const char *nested_p = cp;
|
||||
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_UNDEFERR|VARE_WANTRES,
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
&result, &freeIt);
|
||||
/* TODO: handle errors */
|
||||
isError = result == var_Error;
|
||||
@ -339,7 +325,7 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
* so it's better to return failure than allow such things to happen
|
||||
*/
|
||||
if (*cp == '\0') {
|
||||
printf("No closing parenthesis in archive specification\n");
|
||||
Parse_Error(PARSE_FATAL, "No closing parenthesis in archive specification");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -370,7 +356,7 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
char *sacrifice;
|
||||
char *oldMemName = memName;
|
||||
|
||||
(void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES,
|
||||
(void)Var_Subst(memName, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
&memName);
|
||||
/* TODO: handle errors */
|
||||
|
||||
@ -381,7 +367,8 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
*/
|
||||
buf = sacrifice = str_concat4(libName, "(", memName, ")");
|
||||
|
||||
if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
|
||||
if (strchr(memName, '$') != NULL &&
|
||||
strcmp(memName, oldMemName) == 0) {
|
||||
/*
|
||||
* Must contain dynamic sources, so we can't deal with it now.
|
||||
* Just create an ARCHV node for the thing and let
|
||||
@ -437,17 +424,12 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
*cp = saveChar;
|
||||
}
|
||||
|
||||
/*
|
||||
* If substituted libName, free it now, since we need it no longer.
|
||||
*/
|
||||
if (subLibName) {
|
||||
free(libName);
|
||||
}
|
||||
free(libName_freeIt);
|
||||
|
||||
cp++; /* skip the ')' */
|
||||
/* We promised that linePtr would be set up at the next non-space. */
|
||||
/* We promised that pp would be set up at the next non-space. */
|
||||
pp_skip_whitespace(&cp);
|
||||
*linePtr = cp;
|
||||
*pp = cp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -457,15 +439,17 @@ Arch_ParseArchive(char **linePtr, GNodeList *nodeLst, GNode *ctxt)
|
||||
* Input:
|
||||
* archive Path to the archive
|
||||
* member Name of member; only its basename is used.
|
||||
* hash TRUE if archive should be hashed if not already so.
|
||||
* addToCache TRUE if archive should be cached if not already so.
|
||||
*
|
||||
* Results:
|
||||
* The ar_hdr for the member.
|
||||
* The ar_hdr for the member, or NULL.
|
||||
*
|
||||
* See ArchFindMember for an almost identical copy of this code.
|
||||
*/
|
||||
static struct ar_hdr *
|
||||
ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
{
|
||||
#define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME) - 1)
|
||||
#define AR_MAX_NAME_LEN (sizeof arh.AR_NAME - 1)
|
||||
FILE *arch; /* Stream to archive */
|
||||
size_t size; /* Size of archive member */
|
||||
char magic[SARMAG];
|
||||
@ -484,8 +468,8 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
member = lastSlash + 1;
|
||||
|
||||
for (ln = archives->first; ln != NULL; ln = ln->next) {
|
||||
const Arch *archPtr = ln->datum;
|
||||
if (strcmp(archPtr->name, archive) == 0)
|
||||
const Arch *a = ln->datum;
|
||||
if (strcmp(a->name, archive) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -505,17 +489,17 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
if (len > AR_MAX_NAME_LEN) {
|
||||
len = AR_MAX_NAME_LEN;
|
||||
snprintf(copy, sizeof copy, "%s", member);
|
||||
hdr = HashTable_FindValue(&ar->members, copy);
|
||||
}
|
||||
hdr = HashTable_FindValue(&ar->members, copy);
|
||||
return hdr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hash) {
|
||||
if (!addToCache) {
|
||||
/*
|
||||
* Caller doesn't want the thing hashed, just use ArchFindMember
|
||||
* Caller doesn't want the thing cached, just use ArchFindMember
|
||||
* to read the header for the member out and close down the stream
|
||||
* again. Since the archive is not to be hashed, we assume there's
|
||||
* again. Since the archive is not to be cached, we assume there's
|
||||
* no need to allocate extra room for the header we're returning,
|
||||
* so just declare it static.
|
||||
*/
|
||||
@ -541,98 +525,92 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
* We use the ARMAG string to make sure this is an archive we
|
||||
* can handle...
|
||||
*/
|
||||
if ((fread(magic, SARMAG, 1, arch) != 1) ||
|
||||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
|
||||
fclose(arch);
|
||||
if (fread(magic, SARMAG, 1, arch) != 1 ||
|
||||
strncmp(magic, ARMAG, SARMAG) != 0) {
|
||||
(void)fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ar = bmake_malloc(sizeof(Arch));
|
||||
ar = bmake_malloc(sizeof *ar);
|
||||
ar->name = bmake_strdup(archive);
|
||||
ar->fnametab = NULL;
|
||||
ar->fnamesize = 0;
|
||||
HashTable_Init(&ar->members);
|
||||
memName[AR_MAX_NAME_LEN] = '\0';
|
||||
|
||||
while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
|
||||
if (strncmp(arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) {
|
||||
/*
|
||||
* The header is bogus, so the archive is bad
|
||||
* and there's no way we can recover...
|
||||
*/
|
||||
while (fread(&arh, sizeof arh, 1, arch) == 1) {
|
||||
char *nameend;
|
||||
|
||||
/* If the header is bogus, there's no way we can recover. */
|
||||
if (strncmp(arh.AR_FMAG, ARFMAG, sizeof arh.AR_FMAG) != 0)
|
||||
goto badarch;
|
||||
} else {
|
||||
char *nameend;
|
||||
|
||||
/*
|
||||
* We need to advance the stream's pointer to the start of the
|
||||
* next header. Files are padded with newlines to an even-byte
|
||||
* boundary, so we need to extract the size of the file from the
|
||||
* 'size' field of the header and round it up during the seek.
|
||||
*/
|
||||
arh.AR_SIZE[sizeof(arh.AR_SIZE) - 1] = '\0';
|
||||
size = (size_t)strtol(arh.ar_size, NULL, 10);
|
||||
/*
|
||||
* We need to advance the stream's pointer to the start of the
|
||||
* next header. Files are padded with newlines to an even-byte
|
||||
* boundary, so we need to extract the size of the file from the
|
||||
* 'size' field of the header and round it up during the seek.
|
||||
*/
|
||||
arh.AR_SIZE[sizeof arh.AR_SIZE - 1] = '\0';
|
||||
size = (size_t)strtol(arh.AR_SIZE, NULL, 10);
|
||||
|
||||
memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
|
||||
nameend = memName + AR_MAX_NAME_LEN;
|
||||
while (*nameend == ' ') {
|
||||
nameend--;
|
||||
}
|
||||
nameend[1] = '\0';
|
||||
memcpy(memName, arh.AR_NAME, sizeof arh.AR_NAME);
|
||||
nameend = memName + AR_MAX_NAME_LEN;
|
||||
while (nameend > memName && *nameend == ' ')
|
||||
nameend--;
|
||||
nameend[1] = '\0';
|
||||
|
||||
#ifdef SVR4ARCHIVES
|
||||
/*
|
||||
* svr4 names are slash terminated. Also svr4 extended AR format.
|
||||
*/
|
||||
if (memName[0] == '/') {
|
||||
/*
|
||||
* svr4 names are slash terminated. Also svr4 extended AR format.
|
||||
* svr4 magic mode; handle it
|
||||
*/
|
||||
if (memName[0] == '/') {
|
||||
/*
|
||||
* svr4 magic mode; handle it
|
||||
*/
|
||||
switch (ArchSVR4Entry(ar, memName, size, arch)) {
|
||||
case -1: /* Invalid data */
|
||||
goto badarch;
|
||||
case 0: /* List of files entry */
|
||||
continue;
|
||||
default: /* Got the entry */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (nameend[0] == '/')
|
||||
nameend[0] = '\0';
|
||||
switch (ArchSVR4Entry(ar, memName, size, arch)) {
|
||||
case -1: /* Invalid data */
|
||||
goto badarch;
|
||||
case 0: /* List of files entry */
|
||||
continue;
|
||||
default: /* Got the entry */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (nameend[0] == '/')
|
||||
nameend[0] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AR_EFMT1
|
||||
/*
|
||||
* BSD 4.4 extended AR format: #1/<namelen>, with name as the
|
||||
* first <namelen> bytes of the file
|
||||
*/
|
||||
if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
|
||||
ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) {
|
||||
/*
|
||||
* BSD 4.4 extended AR format: #1/<namelen>, with name as the
|
||||
* first <namelen> bytes of the file
|
||||
*/
|
||||
if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
|
||||
ch_isdigit(memName[sizeof AR_EFMT1 - 1])) {
|
||||
|
||||
int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]);
|
||||
int elen = atoi(memName + sizeof AR_EFMT1 - 1);
|
||||
|
||||
if ((unsigned int)elen > MAXPATHLEN)
|
||||
goto badarch;
|
||||
if (fread(memName, (size_t)elen, 1, arch) != 1)
|
||||
goto badarch;
|
||||
memName[elen] = '\0';
|
||||
if (fseek(arch, -elen, SEEK_CUR) != 0)
|
||||
goto badarch;
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE)) {
|
||||
debug_printf("ArchStat: Extended format entry for %s\n",
|
||||
memName);
|
||||
}
|
||||
}
|
||||
if ((unsigned int)elen > MAXPATHLEN)
|
||||
goto badarch;
|
||||
if (fread(memName, (size_t)elen, 1, arch) != 1)
|
||||
goto badarch;
|
||||
memName[elen] = '\0';
|
||||
if (fseek(arch, -elen, SEEK_CUR) != 0)
|
||||
goto badarch;
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("ArchStatMember: Extended format entry for %s\n",
|
||||
memName);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
HashEntry *he;
|
||||
he = HashTable_CreateEntry(&ar->members, memName, NULL);
|
||||
HashEntry_Set(he, bmake_malloc(sizeof(struct ar_hdr)));
|
||||
memcpy(HashEntry_Get(he), &arh, sizeof(struct ar_hdr));
|
||||
}
|
||||
{
|
||||
struct ar_hdr *cached_hdr = bmake_malloc(sizeof *cached_hdr);
|
||||
memcpy(cached_hdr, &arh, sizeof arh);
|
||||
HashTable_Set(&ar->members, memName, cached_hdr);
|
||||
}
|
||||
|
||||
if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0)
|
||||
goto badarch;
|
||||
}
|
||||
@ -643,7 +621,7 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
|
||||
/*
|
||||
* Now that the archive has been read and cached, we can look into
|
||||
* the hash table to find the desired member's header.
|
||||
* the addToCache table to find the desired member's header.
|
||||
*/
|
||||
return HashTable_FindValue(&ar->members, member);
|
||||
|
||||
@ -674,15 +652,15 @@ ArchStatMember(const char *archive, const char *member, Boolean hash)
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
static int
|
||||
ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
|
||||
ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
{
|
||||
#define ARLONGNAMES1 "//"
|
||||
#define ARLONGNAMES2 "/ARFILENAMES"
|
||||
size_t entry;
|
||||
char *ptr, *eptr;
|
||||
|
||||
if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 ||
|
||||
strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) {
|
||||
if (strncmp(inout_name, ARLONGNAMES1, sizeof ARLONGNAMES1 - 1) == 0 ||
|
||||
strncmp(inout_name, ARLONGNAMES2, sizeof ARLONGNAMES2 - 1) == 0) {
|
||||
|
||||
if (ar->fnametab != NULL) {
|
||||
DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n");
|
||||
@ -711,51 +689,74 @@ ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (name[1] == ' ' || name[1] == '\0')
|
||||
if (inout_name[1] == ' ' || inout_name[1] == '\0')
|
||||
return 2;
|
||||
|
||||
entry = (size_t)strtol(&name[1], &eptr, 0);
|
||||
if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
|
||||
DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
|
||||
entry = (size_t)strtol(&inout_name[1], &eptr, 0);
|
||||
if ((*eptr != ' ' && *eptr != '\0') || eptr == &inout_name[1]) {
|
||||
DEBUG1(ARCH, "Could not parse SVR4 name %s\n", inout_name);
|
||||
return 2;
|
||||
}
|
||||
if (entry >= ar->fnamesize) {
|
||||
DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
|
||||
name, (unsigned long)ar->fnamesize);
|
||||
inout_name, (unsigned long)ar->fnamesize);
|
||||
return 2;
|
||||
}
|
||||
|
||||
DEBUG2(ARCH, "Replaced %s with %s\n", name, &ar->fnametab[entry]);
|
||||
DEBUG2(ARCH, "Replaced %s with %s\n", inout_name, &ar->fnametab[entry]);
|
||||
|
||||
snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
|
||||
snprintf(inout_name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* ArchFindMember --
|
||||
* Locate a member of an archive, given the path of the archive and
|
||||
* the path of the desired member. If the archive is to be modified,
|
||||
* the mode should be "r+", if not, it should be "r".
|
||||
* The passed struct ar_hdr structure is filled in.
|
||||
static Boolean
|
||||
ArchiveMember_HasName(const struct ar_hdr *hdr,
|
||||
const char *name, size_t namelen)
|
||||
{
|
||||
const size_t ar_name_len = sizeof hdr->AR_NAME;
|
||||
const char *ar_name = hdr->AR_NAME;
|
||||
|
||||
if (strncmp(ar_name, name, namelen) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (namelen >= ar_name_len)
|
||||
return namelen == ar_name_len;
|
||||
|
||||
/* hdr->AR_NAME is space-padded to the right. */
|
||||
if (ar_name[namelen] == ' ')
|
||||
return TRUE;
|
||||
|
||||
/* In archives created by GNU binutils 2.27, the member names end with
|
||||
* a slash. */
|
||||
if (ar_name[namelen] == '/' &&
|
||||
(namelen == ar_name_len || ar_name[namelen + 1] == ' '))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Locate a member of an archive, given the path of the archive and the path
|
||||
* of the desired member.
|
||||
*
|
||||
* Input:
|
||||
* archive Path to the archive
|
||||
* member Name of member. If it is a path, only the last
|
||||
* component is used.
|
||||
* arhPtr Pointer to header structure to be filled in
|
||||
* mode The mode for opening the stream
|
||||
* out_arh Archive header to be filled in
|
||||
* mode "r" for read-only access, "r+" for read-write access
|
||||
*
|
||||
* Results:
|
||||
* An FILE *, opened for reading and writing, positioned at the
|
||||
* start of the member's struct ar_hdr, or NULL if the member was
|
||||
* nonexistent. The current struct ar_hdr for member.
|
||||
*-----------------------------------------------------------------------
|
||||
* Output:
|
||||
* return The archive file, positioned at the start of the
|
||||
* member's struct ar_hdr, or NULL if the member doesn't
|
||||
* exist.
|
||||
* *out_arh The current struct ar_hdr for member.
|
||||
*
|
||||
* See ArchStatMember for an almost identical copy of this code.
|
||||
*/
|
||||
static FILE *
|
||||
ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
const char *mode)
|
||||
{
|
||||
FILE *arch; /* Stream to archive */
|
||||
@ -772,8 +773,8 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
* We use the ARMAG string to make sure this is an archive we
|
||||
* can handle...
|
||||
*/
|
||||
if ((fread(magic, SARMAG, 1, arch) != 1) ||
|
||||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
|
||||
if (fread(magic, SARMAG, 1, arch) != 1 ||
|
||||
strncmp(magic, ARMAG, SARMAG) != 0) {
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
@ -787,13 +788,13 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
member = lastSlash + 1;
|
||||
|
||||
len = tlen = strlen(member);
|
||||
if (len > sizeof(arhPtr->AR_NAME)) {
|
||||
tlen = sizeof(arhPtr->AR_NAME);
|
||||
if (len > sizeof out_arh->AR_NAME) {
|
||||
tlen = sizeof out_arh->AR_NAME;
|
||||
}
|
||||
|
||||
while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
|
||||
while (fread(out_arh, sizeof *out_arh, 1, arch) == 1) {
|
||||
|
||||
if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG)) != 0) {
|
||||
if (strncmp(out_arh->AR_FMAG, ARFMAG, sizeof out_arh->AR_FMAG) != 0) {
|
||||
/*
|
||||
* The header is bogus, so the archive is bad
|
||||
* and there's no way we can recover...
|
||||
@ -802,25 +803,21 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strncmp(member, arhPtr->AR_NAME, tlen) == 0) {
|
||||
/*
|
||||
* If the member's name doesn't take up the entire 'name' field,
|
||||
* we have to be careful of matching prefixes. Names are space-
|
||||
* padded to the right, so if the character in 'name' at the end
|
||||
* of the matched string is anything but a space, this isn't the
|
||||
* member we sought.
|
||||
*/
|
||||
if (tlen != sizeof arhPtr->AR_NAME && arhPtr->AR_NAME[tlen] != ' ')
|
||||
goto skip;
|
||||
DEBUG5(ARCH, "Reading archive %s member %.*s mtime %.*s\n",
|
||||
archive,
|
||||
(int)sizeof out_arh->AR_NAME, out_arh->AR_NAME,
|
||||
(int)sizeof out_arh->ar_date, out_arh->ar_date);
|
||||
|
||||
if (ArchiveMember_HasName(out_arh, member, len)) {
|
||||
/*
|
||||
* To make life easier, we reposition the file at the start
|
||||
* To make life easier for callers that want to update the
|
||||
* archive, we reposition the file at the start
|
||||
* of the header we just read before we return the stream.
|
||||
* In a more general situation, it might be better to leave
|
||||
* the file at the actual member, rather than its header, but
|
||||
* not here...
|
||||
* not here.
|
||||
*/
|
||||
if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) {
|
||||
if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) != 0) {
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
@ -832,10 +829,10 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
* BSD 4.4 extended AR format: #1/<namelen>, with name as the
|
||||
* first <namelen> bytes of the file
|
||||
*/
|
||||
if (strncmp(arhPtr->AR_NAME, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
|
||||
ch_isdigit(arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]))
|
||||
if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
|
||||
ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))
|
||||
{
|
||||
int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1]);
|
||||
int elen = atoi(&out_arh->AR_NAME[sizeof AR_EFMT1 - 1]);
|
||||
char ename[MAXPATHLEN + 1];
|
||||
|
||||
if ((unsigned int)elen > MAXPATHLEN) {
|
||||
@ -847,9 +844,9 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
return NULL;
|
||||
}
|
||||
ename[elen] = '\0';
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE)) {
|
||||
debug_printf("ArchFind: Extended format entry for %s\n", ename);
|
||||
}
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("ArchFindMember: Extended format entry for %s\n",
|
||||
ename);
|
||||
if (strncmp(ename, member, len) == 0) {
|
||||
/* Found as extended name */
|
||||
if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
|
||||
@ -866,7 +863,6 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
}
|
||||
#endif
|
||||
|
||||
skip:
|
||||
/*
|
||||
* This isn't the member we're after, so we need to advance the
|
||||
* stream's pointer to the start of the next header. Files are
|
||||
@ -874,113 +870,89 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
|
||||
* extract the size of the file from the 'size' field of the
|
||||
* header and round it up during the seek.
|
||||
*/
|
||||
arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
|
||||
size = (int)strtol(arhPtr->ar_size, NULL, 10);
|
||||
out_arh->AR_SIZE[sizeof out_arh->AR_SIZE - 1] = '\0';
|
||||
size = (int)strtol(out_arh->AR_SIZE, NULL, 10);
|
||||
if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We've looked everywhere, but the member is not to be found. Close the
|
||||
* archive and return NULL -- an error.
|
||||
*/
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* Arch_Touch --
|
||||
* Touch a member of an archive.
|
||||
* The modification time of the entire archive is also changed.
|
||||
* For a library, this could necessitate the re-ranlib'ing of the
|
||||
* whole thing.
|
||||
/* Touch a member of an archive, on disk.
|
||||
* The GNode's modification time is left as-is.
|
||||
*
|
||||
* The st_mtime of the entire archive is also changed.
|
||||
* For a library, it may be required to run ranlib after this.
|
||||
*
|
||||
* Input:
|
||||
* gn Node of member to touch
|
||||
*
|
||||
* Results:
|
||||
* The 'time' field of the member's header is updated.
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
Arch_Touch(GNode *gn)
|
||||
{
|
||||
FILE *arch; /* Stream open to archive, positioned properly */
|
||||
struct ar_hdr arh; /* Current header describing member */
|
||||
FILE *f;
|
||||
struct ar_hdr arh;
|
||||
|
||||
arch = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn),
|
||||
&arh, "r+");
|
||||
f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh, "r+");
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long)now);
|
||||
|
||||
if (arch != NULL) {
|
||||
(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
|
||||
fclose(arch);
|
||||
}
|
||||
snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
|
||||
(void)fwrite(&arh, sizeof arh, 1, f);
|
||||
fclose(f); /* TODO: handle errors */
|
||||
}
|
||||
|
||||
/* Given a node which represents a library, touch the thing, making sure that
|
||||
* the table of contents also is touched.
|
||||
* the table of contents is also touched.
|
||||
*
|
||||
* Both the modification time of the library and of the RANLIBMAG member are
|
||||
* set to 'now'.
|
||||
*
|
||||
* Input:
|
||||
* gn The node of the library to touch
|
||||
*/
|
||||
* set to 'now'. */
|
||||
void
|
||||
Arch_TouchLib(GNode *gn)
|
||||
Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
|
||||
{
|
||||
#ifdef RANLIBMAG
|
||||
FILE * arch; /* Stream open to archive */
|
||||
struct ar_hdr arh; /* Header describing table of contents */
|
||||
struct utimbuf times; /* Times for utime() call */
|
||||
FILE *f;
|
||||
struct ar_hdr arh; /* Header describing table of contents */
|
||||
struct utimbuf times;
|
||||
|
||||
arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
|
||||
snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now);
|
||||
f = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+");
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
if (arch != NULL) {
|
||||
(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
|
||||
fclose(arch);
|
||||
snprintf(arh.ar_date, sizeof arh.ar_date, "%-ld", (unsigned long)now);
|
||||
(void)fwrite(&arh, sizeof arh, 1, f);
|
||||
fclose(f); /* TODO: handle errors */
|
||||
|
||||
times.actime = times.modtime = now;
|
||||
utime(gn->path, ×);
|
||||
}
|
||||
#else
|
||||
(void)gn;
|
||||
times.actime = times.modtime = now;
|
||||
utime(gn->path, ×); /* TODO: handle errors */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return the modification time of a member of an archive. The mtime field
|
||||
* of the given node is filled in with the value returned by the function.
|
||||
*
|
||||
* Input:
|
||||
* gn Node describing archive member
|
||||
*/
|
||||
time_t
|
||||
Arch_MTime(GNode *gn)
|
||||
/* Update the mtime of the GNode with the mtime from the archive member on
|
||||
* disk (or in the cache). */
|
||||
void
|
||||
Arch_UpdateMTime(GNode *gn)
|
||||
{
|
||||
struct ar_hdr *arhPtr; /* Header of desired member */
|
||||
time_t modTime; /* Modification time as an integer */
|
||||
struct ar_hdr *arh;
|
||||
|
||||
arhPtr = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
|
||||
if (arhPtr != NULL) {
|
||||
modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10);
|
||||
} else {
|
||||
modTime = 0;
|
||||
}
|
||||
|
||||
gn->mtime = modTime;
|
||||
return modTime;
|
||||
arh = ArchStatMember(GNode_VarArchive(gn), GNode_VarMember(gn), TRUE);
|
||||
if (arh != NULL)
|
||||
gn->mtime = (time_t)strtol(arh->ar_date, NULL, 10);
|
||||
else
|
||||
gn->mtime = 0;
|
||||
}
|
||||
|
||||
/* Given a non-existent archive member's node, get its modification time from
|
||||
* its archived form, if it exists. gn->mtime is filled in as well. */
|
||||
time_t
|
||||
Arch_MemMTime(GNode *gn)
|
||||
/* Given a non-existent archive member's node, update gn->mtime from its
|
||||
* archived form, if it exists. */
|
||||
void
|
||||
Arch_UpdateMemberMTime(GNode *gn)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
@ -1001,7 +973,8 @@ Arch_MemMTime(GNode *gn)
|
||||
|
||||
if ((pgn->flags & REMAKE) &&
|
||||
strncmp(nameStart, gn->name, nameLen) == 0) {
|
||||
gn->mtime = Arch_MTime(pgn);
|
||||
Arch_UpdateMTime(pgn);
|
||||
gn->mtime = pgn->mtime;
|
||||
}
|
||||
} else if (pgn->flags & REMAKE) {
|
||||
/*
|
||||
@ -1012,8 +985,6 @@ Arch_MemMTime(GNode *gn)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return gn->mtime;
|
||||
}
|
||||
|
||||
/* Search for a library along the given search path.
|
||||
@ -1045,13 +1016,15 @@ Arch_FindLib(GNode *gn, SearchPath *path)
|
||||
}
|
||||
|
||||
/* Decide if a node with the OP_LIB attribute is out-of-date. Called from
|
||||
* Make_OODate to make its life easier.
|
||||
* The library will be hashed if it hasn't been already.
|
||||
* GNode_IsOODate to make its life easier.
|
||||
* The library is cached if it hasn't been already.
|
||||
*
|
||||
* There are several ways for a library to be out-of-date that are
|
||||
* not available to ordinary files. In addition, there are ways
|
||||
* that are open to regular files that are not available to
|
||||
* libraries. A library that is only used as a source is never
|
||||
* libraries.
|
||||
*
|
||||
* A library that is only used as a source is never
|
||||
* considered out-of-date by itself. This does not preclude the
|
||||
* library's modification time from making its parent be out-of-date.
|
||||
* A library will be considered out-of-date for any of these reasons,
|
||||
@ -1066,16 +1039,10 @@ Arch_FindLib(GNode *gn, SearchPath *path)
|
||||
*
|
||||
* The modification time of one of its sources is greater than the one
|
||||
* of its RANLIBMAG member (i.e. its table of contents is out-of-date).
|
||||
* We don't compare of the archive time vs. TOC time because they can be
|
||||
* We don't compare the archive time vs. TOC time because they can be
|
||||
* too close. In my opinion we should not bother with the TOC at all
|
||||
* since this is used by 'ar' rules that affect the data contents of the
|
||||
* archive, not by ranlib rules, which affect the TOC.
|
||||
*
|
||||
* Input:
|
||||
* gn The library's graph node
|
||||
*
|
||||
* Results:
|
||||
* TRUE if the library is out-of-date. FALSE otherwise.
|
||||
*/
|
||||
Boolean
|
||||
Arch_LibOODate(GNode *gn)
|
||||
@ -1093,25 +1060,23 @@ Arch_LibOODate(GNode *gn)
|
||||
oodate = TRUE;
|
||||
} else {
|
||||
#ifdef RANLIBMAG
|
||||
struct ar_hdr *arhPtr; /* Header for __.SYMDEF */
|
||||
struct ar_hdr *arh; /* Header for __.SYMDEF */
|
||||
int modTimeTOC; /* The table-of-contents's mod time */
|
||||
|
||||
arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE);
|
||||
arh = ArchStatMember(gn->path, RANLIBMAG, FALSE);
|
||||
|
||||
if (arhPtr != NULL) {
|
||||
modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10);
|
||||
if (arh != NULL) {
|
||||
modTimeTOC = (int)strtol(arh->ar_date, NULL, 10);
|
||||
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE)) {
|
||||
debug_printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
|
||||
}
|
||||
oodate = (gn->youngestChild == NULL || gn->youngestChild->mtime > modTimeTOC);
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("%s modified %s...",
|
||||
RANLIBMAG, Targ_FmtTime(modTimeTOC));
|
||||
oodate = gn->youngestChild == NULL ||
|
||||
gn->youngestChild->mtime > modTimeTOC;
|
||||
} else {
|
||||
/*
|
||||
* A library w/o a table of contents is out-of-date
|
||||
*/
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE)) {
|
||||
debug_printf("No t.o.c....");
|
||||
}
|
||||
/* A library without a table of contents is out-of-date. */
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("no toc...");
|
||||
oodate = TRUE;
|
||||
}
|
||||
#else
|
||||
|
19
bmake.1
19
bmake.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig 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 November 1, 2020
|
||||
.Dd November 14, 2020
|
||||
.Dt BMAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -37,7 +37,7 @@
|
||||
.Nd maintain program dependencies
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl BeikNnqrstWwX
|
||||
.Op Fl BeikNnqrSstWwX
|
||||
.Op Fl C Ar directory
|
||||
.Op Fl D Ar variable
|
||||
.Op Fl d Ar flags
|
||||
@ -329,6 +329,10 @@ Do not execute any commands, but exit 0 if the specified targets are
|
||||
up-to-date and 1, otherwise.
|
||||
.It Fl r
|
||||
Do not use the built-in rules specified in the system makefile.
|
||||
.It Fl S
|
||||
Stop processing if an error is encountered.
|
||||
This is the default behavior and the opposite of
|
||||
.Fl k .
|
||||
.It Fl s
|
||||
Do not echo any commands as they are executed.
|
||||
Equivalent to specifying
|
||||
@ -1090,6 +1094,15 @@ to the specified directory if it exists, and set
|
||||
and
|
||||
.Ql Ev PWD
|
||||
to that directory before executing any targets.
|
||||
.Pp
|
||||
Except in the case of an explicit
|
||||
.Ql Ic .OBJDIR
|
||||
target,
|
||||
.Nm
|
||||
will check that the specified directory is writable and ignore it if not.
|
||||
This check can be skipped by setting the environment variable
|
||||
.Ql Ev MAKE_OBJDIR_CHECK_WRITABLE
|
||||
to "no".
|
||||
.
|
||||
.It Va .PARSEDIR
|
||||
A path to the directory of the current
|
||||
|
13
bmake.cat1
13
bmake.cat1
@ -4,7 +4,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[1mbmake [22m-- maintain program dependencies
|
||||
|
||||
[1mSYNOPSIS[0m
|
||||
[1mbmake [22m[[1m-BeikNnqrstWwX[22m] [[1m-C [4m[22mdirectory[24m] [[1m-D [4m[22mvariable[24m] [[1m-d [4m[22mflags[24m]
|
||||
[1mbmake [22m[[1m-BeikNnqrSstWwX[22m] [[1m-C [4m[22mdirectory[24m] [[1m-D [4m[22mvariable[24m] [[1m-d [4m[22mflags[24m]
|
||||
[[1m-f [4m[22mmakefile[24m] [[1m-I [4m[22mdirectory[24m] [[1m-J [4m[22mprivate[24m] [[1m-j [4m[22mmax_jobs[24m]
|
||||
[[1m-m [4m[22mdirectory[24m] [[1m-T [4m[22mfile[24m] [[1m-V [4m[22mvariable[24m] [[1m-v [4m[22mvariable[24m]
|
||||
[[4mvariable=value[24m] [[4mtarget[24m [4m...[24m]
|
||||
@ -205,6 +205,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m-r [22mDo not use the built-in rules specified in the system makefile.
|
||||
|
||||
[1m-S [22mStop processing if an error is encountered. This is the default
|
||||
behavior and the opposite of [1m-k[22m.
|
||||
|
||||
[1m-s [22mDo not echo any commands as they are executed. Equivalent to
|
||||
specifying `[1m@[22m' before each command line in the makefile.
|
||||
|
||||
@ -718,6 +721,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
the specified directory if it exists, and set `[4m.OBJDIR[24m'
|
||||
and `PWD' to that directory before executing any targets.
|
||||
|
||||
Except in the case of an explicit `[1m.OBJDIR[22m' target, [1mbmake[0m
|
||||
will check that the specified directory is writable and
|
||||
ignore it if not. This check can be skipped by setting
|
||||
the environment variable `MAKE_OBJDIR_CHECK_WRITABLE' to
|
||||
"no".
|
||||
|
||||
[4m.PARSEDIR[24m A path to the directory of the current `[4mMakefile[24m' being
|
||||
parsed.
|
||||
|
||||
@ -1568,4 +1577,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
There is no way of escaping a space character in a filename.
|
||||
|
||||
FreeBSD 11.3 November 1, 2020 FreeBSD 11.3
|
||||
FreeBSD 11.3 November 14, 2020 FreeBSD 11.3
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
@ -107,6 +107,10 @@
|
||||
# set "machine_arch" to override that determined by
|
||||
# machine.sh
|
||||
#
|
||||
# --with-force_machine_arch="machine_arch"
|
||||
# force "machine_arch" to override that determined by
|
||||
# machine.sh
|
||||
#
|
||||
# --with-default-sys-path="syspath"
|
||||
# set an explicit default "syspath" which is where bmake
|
||||
# will look for sys.mk and friends.
|
||||
@ -115,7 +119,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: boot-strap,v 1.53 2020/09/16 02:12:01 sjg Exp $
|
||||
# $Id: boot-strap,v 1.54 2020/11/13 21:47:25 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2001 Simon J. Gerraty
|
||||
#
|
||||
|
17
buf.c
17
buf.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $ */
|
||||
/* $NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -75,7 +75,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $");
|
||||
|
||||
/* Make space in the buffer for adding a single byte. */
|
||||
void
|
||||
@ -155,19 +155,22 @@ Buf_Empty(Buffer *buf)
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Initialize a buffer.
|
||||
* If the given initial capacity is 0, a reasonable default is used. */
|
||||
/* Initialize a buffer. */
|
||||
void
|
||||
Buf_Init(Buffer *buf, size_t cap)
|
||||
Buf_InitSize(Buffer *buf, size_t cap)
|
||||
{
|
||||
if (cap <= 0)
|
||||
cap = 256;
|
||||
buf->cap = cap;
|
||||
buf->len = 0;
|
||||
buf->data = bmake_malloc(cap);
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
Buf_Init(Buffer *buf)
|
||||
{
|
||||
Buf_InitSize(buf, 256);
|
||||
}
|
||||
|
||||
/* Reset the buffer.
|
||||
* If freeData is TRUE, the data from the buffer is freed as well.
|
||||
* Otherwise it is kept and returned. */
|
||||
|
11
buf.h
11
buf.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.h,v 1.34 2020/09/27 16:59:02 rillig Exp $ */
|
||||
/* $NetBSD: buf.h,v 1.36 2020/11/10 00:32:12 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -94,7 +94,7 @@ typedef struct Buffer {
|
||||
void Buf_Expand_1(Buffer *);
|
||||
|
||||
/* Buf_AddByte adds a single byte to a buffer. */
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
Buf_AddByte(Buffer *buf, char byte)
|
||||
{
|
||||
size_t old_len = buf->len++;
|
||||
@ -106,13 +106,13 @@ Buf_AddByte(Buffer *buf, char byte)
|
||||
end[1] = '\0';
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED size_t
|
||||
MAKE_INLINE size_t
|
||||
Buf_Len(const Buffer *buf)
|
||||
{
|
||||
return buf->len;
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED Boolean
|
||||
MAKE_INLINE Boolean
|
||||
Buf_EndsWith(const Buffer *buf, char ch)
|
||||
{
|
||||
return buf->len > 0 && buf->data[buf->len - 1] == ch;
|
||||
@ -124,7 +124,8 @@ void Buf_AddStr(Buffer *, const char *);
|
||||
void Buf_AddInt(Buffer *, int);
|
||||
char *Buf_GetAll(Buffer *, size_t *);
|
||||
void Buf_Empty(Buffer *);
|
||||
void Buf_Init(Buffer *, size_t);
|
||||
void Buf_Init(Buffer *);
|
||||
void Buf_InitSize(Buffer *, size_t);
|
||||
char *Buf_Destroy(Buffer *, Boolean);
|
||||
char *Buf_DestroyCompact(Buffer *);
|
||||
|
||||
|
99
compat.c
99
compat.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $ */
|
||||
/* $NetBSD: compat.c,v 1.183 2020/11/15 22:31:03 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -99,15 +99,15 @@
|
||||
#include "pathnames.h"
|
||||
|
||||
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.183 2020/11/15 22:31:03 rillig Exp $");
|
||||
|
||||
static GNode *curTarg = NULL;
|
||||
static pid_t compatChild;
|
||||
static int compatSigno;
|
||||
|
||||
/*
|
||||
* CompatDeleteTarget -- delete a failed, interrupted, or otherwise
|
||||
* duffed target if not inhibited by .PRECIOUS.
|
||||
* CompatDeleteTarget -- delete the file of a failed, interrupted, or
|
||||
* otherwise duffed target if not inhibited by .PRECIOUS.
|
||||
*/
|
||||
static void
|
||||
CompatDeleteTarget(GNode *gn)
|
||||
@ -132,8 +132,6 @@ CompatDeleteTarget(GNode *gn)
|
||||
static void
|
||||
CompatInterrupt(int signo)
|
||||
{
|
||||
GNode *gn;
|
||||
|
||||
CompatDeleteTarget(curTarg);
|
||||
|
||||
if (curTarg != NULL && !Targ_Precious(curTarg)) {
|
||||
@ -141,7 +139,7 @@ CompatInterrupt(int signo)
|
||||
* Run .INTERRUPT only if hit with interrupt signal
|
||||
*/
|
||||
if (signo == SIGINT) {
|
||||
gn = Targ_FindNode(".INTERRUPT");
|
||||
GNode *gn = Targ_FindNode(".INTERRUPT");
|
||||
if (gn != NULL) {
|
||||
Compat_Make(gn, gn);
|
||||
}
|
||||
@ -206,7 +204,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
|
||||
/* TODO: handle errors */
|
||||
|
||||
if (*cmdStart == '\0') {
|
||||
if (cmdStart[0] == '\0') {
|
||||
free(cmdStart);
|
||||
return 0;
|
||||
}
|
||||
@ -225,20 +223,17 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
|
||||
switch (*cmd) {
|
||||
case '@':
|
||||
for (;;) {
|
||||
if (*cmd == '@')
|
||||
silent = !DEBUG(LOUD);
|
||||
break;
|
||||
case '-':
|
||||
else if (*cmd == '-')
|
||||
errCheck = FALSE;
|
||||
break;
|
||||
case '+':
|
||||
else if (*cmd == '+') {
|
||||
doIt = TRUE;
|
||||
if (!shellName) /* we came here from jobs */
|
||||
if (!shellName) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
cmd++;
|
||||
}
|
||||
|
||||
@ -248,7 +243,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
/*
|
||||
* If we did not end up with a command, just skip it.
|
||||
*/
|
||||
if (!*cmd)
|
||||
if (cmd[0] == '\0')
|
||||
return 0;
|
||||
|
||||
#if !defined(MAKE_NATIVE)
|
||||
@ -286,9 +281,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
* If we're not supposed to execute any commands, this is as far as
|
||||
* we go...
|
||||
*/
|
||||
if (!doIt && !GNode_ShouldExecute(gn)) {
|
||||
if (!doIt && !GNode_ShouldExecute(gn))
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG1(JOB, "Execute: '%s'\n", cmd);
|
||||
|
||||
if (useShell) {
|
||||
@ -297,20 +292,13 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
* because the command contains a "meta" character.
|
||||
*/
|
||||
static const char *shargv[5];
|
||||
int shargc;
|
||||
|
||||
shargc = 0;
|
||||
/* The following work for any of the builtin shell specs. */
|
||||
int shargc = 0;
|
||||
shargv[shargc++] = shellPath;
|
||||
/*
|
||||
* The following work for any of the builtin shell specs.
|
||||
*/
|
||||
if (errCheck && shellErrFlag) {
|
||||
if (errCheck && shellErrFlag)
|
||||
shargv[shargc++] = shellErrFlag;
|
||||
}
|
||||
if (DEBUG(SHELL))
|
||||
shargv[shargc++] = "-xc";
|
||||
else
|
||||
shargv[shargc++] = "-c";
|
||||
shargv[shargc++] = DEBUG(SHELL) ? "-xc" : "-c";
|
||||
shargv[shargc++] = cmd;
|
||||
shargv[shargc] = NULL;
|
||||
av = shargv;
|
||||
@ -389,17 +377,19 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
#endif
|
||||
if (status != 0) {
|
||||
if (DEBUG(ERROR)) {
|
||||
const char *cp;
|
||||
const char *p = cmd;
|
||||
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
for (cp = cmd; *cp; ) {
|
||||
if (ch_isspace(*cp)) {
|
||||
|
||||
/* Replace runs of whitespace with a single space, to reduce
|
||||
* the amount of whitespace for multi-line command lines. */
|
||||
while (*p != '\0') {
|
||||
if (ch_isspace(*p)) {
|
||||
debug_printf(" ");
|
||||
while (ch_isspace(*cp))
|
||||
cp++;
|
||||
cpp_skip_whitespace(&p);
|
||||
} else {
|
||||
debug_printf("%c", *cp);
|
||||
cp++;
|
||||
debug_printf("%c", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
@ -480,16 +470,17 @@ MakeNodes(GNodeList *gnodes, GNode *pgn)
|
||||
void
|
||||
Compat_Make(GNode *gn, GNode *pgn)
|
||||
{
|
||||
if (!shellName) /* we came here from jobs */
|
||||
if (shellName == NULL) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
|
||||
if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
|
||||
/*
|
||||
* First mark ourselves to be made, then apply whatever transformations
|
||||
* the suffix module thinks are necessary. Once that's done, we can
|
||||
* descend and make all our children. If any of them has an error
|
||||
* but the -k flag was given, our 'make' field will be set FALSE again.
|
||||
* This is our signal to not attempt to do anything but abort our
|
||||
* parent as well.
|
||||
* but the -k flag was given, our 'make' field will be set to FALSE
|
||||
* again. This is our signal to not attempt to do anything but abort
|
||||
* our parent as well.
|
||||
*/
|
||||
gn->flags |= REMAKE;
|
||||
gn->made = BEINGMADE;
|
||||
@ -509,10 +500,10 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
* All the children were made ok. Now youngestChild->mtime contains the
|
||||
* modification time of the newest child, we need to find out if we
|
||||
* exist and when we were modified last. The criteria for datedness
|
||||
* are defined by the Make_OODate function.
|
||||
* are defined by GNode_IsOODate.
|
||||
*/
|
||||
DEBUG1(MAKE, "Examining %s...", gn->name);
|
||||
if (!Make_OODate(gn)) {
|
||||
if (!GNode_IsOODate(gn)) {
|
||||
gn->made = UPTODATE;
|
||||
DEBUG0(MAKE, "up-to-date.\n");
|
||||
goto cohorts;
|
||||
@ -523,9 +514,8 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
* If the user is just seeing if something is out-of-date, exit now
|
||||
* to tell him/her "yes".
|
||||
*/
|
||||
if (opts.queryFlag) {
|
||||
if (opts.queryFlag)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to be re-made. We also have to make sure we've got a $?
|
||||
@ -573,15 +563,15 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
if (gn->made != ERROR) {
|
||||
/*
|
||||
* If the node was made successfully, mark it so, update
|
||||
* its modification time and timestamp all its parents. Note
|
||||
* that for .ZEROTIME targets, the timestamping isn't done.
|
||||
* its modification time and timestamp all its parents.
|
||||
* This is to keep its state from affecting that of its parent.
|
||||
*/
|
||||
gn->made = MADE;
|
||||
pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0;
|
||||
if (Make_Recheck(gn) == 0)
|
||||
pgn->flags |= FORCE;
|
||||
if (!(gn->type & OP_EXEC)) {
|
||||
pgn->flags |= CHILDMADE;
|
||||
Make_TimeStamp(pgn, gn);
|
||||
GNode_UpdateYoungestChild(pgn, gn);
|
||||
}
|
||||
} else if (opts.keepgoing) {
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
@ -604,15 +594,14 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
break;
|
||||
case MADE:
|
||||
if ((gn->type & OP_EXEC) == 0) {
|
||||
if (!(gn->type & OP_EXEC)) {
|
||||
pgn->flags |= CHILDMADE;
|
||||
Make_TimeStamp(pgn, gn);
|
||||
GNode_UpdateYoungestChild(pgn, gn);
|
||||
}
|
||||
break;
|
||||
case UPTODATE:
|
||||
if ((gn->type & OP_EXEC) == 0) {
|
||||
Make_TimeStamp(pgn, gn);
|
||||
}
|
||||
if (!(gn->type & OP_EXEC))
|
||||
GNode_UpdateYoungestChild(pgn, gn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
563
cond.c
563
cond.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 rillig Exp $ */
|
||||
/* $NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -72,7 +72,8 @@
|
||||
/* Handling of conditionals in a makefile.
|
||||
*
|
||||
* Interface:
|
||||
* Cond_EvalLine Evaluate the conditional.
|
||||
* Cond_EvalLine Evaluate the conditional directive, such as
|
||||
* '.if <cond>', '.elifnmake <cond>', '.else', '.endif'.
|
||||
*
|
||||
* Cond_EvalCondition
|
||||
* Evaluate the conditional, which is either the argument
|
||||
@ -93,7 +94,7 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.173 2020/10/30 20:30:44 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The parsing of conditional expressions is based on this grammar:
|
||||
@ -162,7 +163,7 @@ static unsigned int cond_min_depth = 0; /* depth at makefile open */
|
||||
*
|
||||
* TRUE when CondEvalExpression is called from Cond_EvalLine (.if etc)
|
||||
* FALSE when CondEvalExpression is called from ApplyModifier_IfElse
|
||||
* since lhs is already expanded and we cannot tell if
|
||||
* since lhs is already expanded, and at that point we cannot tell if
|
||||
* it was a variable reference or not.
|
||||
*/
|
||||
static Boolean lhsStrict;
|
||||
@ -173,6 +174,12 @@ is_token(const char *str, const char *tok, size_t len)
|
||||
return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
|
||||
}
|
||||
|
||||
static Token
|
||||
ToToken(Boolean cond)
|
||||
{
|
||||
return cond ? TOK_TRUE : TOK_FALSE;
|
||||
}
|
||||
|
||||
/* Push back the most recent token read. We only need one level of this. */
|
||||
static void
|
||||
CondParser_PushBack(CondParser *par, Token t)
|
||||
@ -200,7 +207,7 @@ CondParser_SkipWhitespace(CondParser *par)
|
||||
* func says whether the argument belongs to an actual function, or
|
||||
* whether the parsed argument is passed to the default function.
|
||||
*
|
||||
* Return the length of the argument. */
|
||||
* Return the length of the argument, or 0 on error. */
|
||||
static size_t
|
||||
ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
char **out_arg) {
|
||||
@ -213,26 +220,18 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
p++; /* Skip opening '(' - verified by caller */
|
||||
|
||||
if (*p == '\0') {
|
||||
/*
|
||||
* No arguments whatsoever. Because 'make' and 'defined' aren't really
|
||||
* "reserved words", we don't print a message. I think this is better
|
||||
* than hitting the user with a warning message every time s/he uses
|
||||
* the word 'make' or 'defined' at the beginning of a symbol...
|
||||
*/
|
||||
*out_arg = NULL;
|
||||
return 0;
|
||||
*out_arg = NULL; /* Missing closing parenthesis: */
|
||||
return 0; /* .if defined( */
|
||||
}
|
||||
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
Buf_Init(&argBuf, 16);
|
||||
Buf_InitSize(&argBuf, 16);
|
||||
|
||||
paren_depth = 0;
|
||||
for (;;) {
|
||||
char ch = *p;
|
||||
if (ch == 0 || ch == ' ' || ch == '\t')
|
||||
if (ch == '\0' || ch == ' ' || ch == '\t')
|
||||
break;
|
||||
if ((ch == '&' || ch == '|') && paren_depth == 0)
|
||||
break;
|
||||
@ -244,7 +243,8 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
* though perhaps we should...
|
||||
*/
|
||||
void *nestedVal_freeIt;
|
||||
VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0);
|
||||
VarEvalFlags eflags = doEval ? VARE_WANTRES | VARE_UNDEFERR
|
||||
: VARE_NONE;
|
||||
const char *nestedVal;
|
||||
(void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal,
|
||||
&nestedVal_freeIt);
|
||||
@ -264,9 +264,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
*out_arg = Buf_GetAll(&argBuf, &argLen);
|
||||
Buf_Destroy(&argBuf, FALSE);
|
||||
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
if (func != NULL && *p++ != ')') {
|
||||
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
|
||||
@ -309,13 +307,10 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
char *path;
|
||||
|
||||
path = Dir_FindFile(arg, dirSearchPath);
|
||||
DEBUG2(COND, "exists(%s) result is \"%s\"\n", arg, path ? path : "");
|
||||
if (path != NULL) {
|
||||
result = TRUE;
|
||||
free(path);
|
||||
} else {
|
||||
result = FALSE;
|
||||
}
|
||||
DEBUG2(COND, "exists(%s) result is \"%s\"\n",
|
||||
arg, path != NULL ? path : "");
|
||||
result = path != NULL;
|
||||
free(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -336,40 +331,41 @@ FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(gn->commands);
|
||||
}
|
||||
|
||||
/*-
|
||||
/*
|
||||
* Convert the given number into a double.
|
||||
* We try a base 10 or 16 integer conversion first, if that fails
|
||||
* then we try a floating point conversion instead.
|
||||
*
|
||||
* Results:
|
||||
* Sets 'value' to double value of string.
|
||||
* Returns TRUE if the conversion succeeded.
|
||||
* Sets 'out_value' to the converted number.
|
||||
*/
|
||||
static Boolean
|
||||
TryParseNumber(const char *str, double *value)
|
||||
TryParseNumber(const char *str, double *out_value)
|
||||
{
|
||||
char *eptr, ech;
|
||||
unsigned long l_val;
|
||||
double d_val;
|
||||
char *end;
|
||||
unsigned long ul_val;
|
||||
double dbl_val;
|
||||
|
||||
errno = 0;
|
||||
if (!*str) {
|
||||
*value = 0.0;
|
||||
if (str[0] == '\0') { /* XXX: why is an empty string a number? */
|
||||
*out_value = 0.0;
|
||||
return TRUE;
|
||||
}
|
||||
l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10);
|
||||
ech = *eptr;
|
||||
if (ech == '\0' && errno != ERANGE) {
|
||||
d_val = str[0] == '-' ? -(double)-l_val : (double)l_val;
|
||||
} else {
|
||||
if (ech != '\0' && ech != '.' && ech != 'e' && ech != 'E')
|
||||
return FALSE;
|
||||
d_val = strtod(str, &eptr);
|
||||
if (*eptr)
|
||||
return FALSE;
|
||||
|
||||
ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
|
||||
if (*end == '\0' && errno != ERANGE) {
|
||||
*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*value = d_val;
|
||||
if (*end != '\0' && *end != '.' && *end != 'e' && *end != 'E')
|
||||
return FALSE; /* skip the expensive strtod call */
|
||||
dbl_val = strtod(str, &end);
|
||||
if (*end != '\0')
|
||||
return FALSE;
|
||||
|
||||
*out_value = dbl_val;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -385,31 +381,31 @@ is_separator(char ch)
|
||||
*
|
||||
* Results:
|
||||
* Returns the string, absent any quotes, or NULL on error.
|
||||
* Sets quoted if the string was quoted.
|
||||
* Sets freeIt if needed.
|
||||
* Sets out_quoted if the string was quoted.
|
||||
* Sets out_freeIt.
|
||||
*/
|
||||
/* coverity:[+alloc : arg-*4] */
|
||||
static const char *
|
||||
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
Boolean *quoted, void **freeIt)
|
||||
Boolean *out_quoted, void **out_freeIt)
|
||||
{
|
||||
Buffer buf;
|
||||
const char *str;
|
||||
Boolean atStart;
|
||||
const char *nested_p;
|
||||
Boolean qt;
|
||||
Boolean quoted;
|
||||
const char *start;
|
||||
VarEvalFlags eflags;
|
||||
VarParseResult parseResult;
|
||||
|
||||
Buf_Init(&buf, 0);
|
||||
Buf_Init(&buf);
|
||||
str = NULL;
|
||||
*freeIt = NULL;
|
||||
*quoted = qt = par->p[0] == '"' ? 1 : 0;
|
||||
*out_freeIt = NULL;
|
||||
*out_quoted = quoted = par->p[0] == '"';
|
||||
start = par->p;
|
||||
if (qt)
|
||||
if (quoted)
|
||||
par->p++;
|
||||
while (par->p[0] && str == NULL) {
|
||||
while (par->p[0] != '\0' && str == NULL) {
|
||||
switch (par->p[0]) {
|
||||
case '\\':
|
||||
par->p++;
|
||||
@ -419,40 +415,44 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
}
|
||||
continue;
|
||||
case '"':
|
||||
if (qt) {
|
||||
par->p++; /* we don't want the quotes */
|
||||
if (quoted) {
|
||||
par->p++; /* skip the closing quote */
|
||||
goto got_str;
|
||||
}
|
||||
Buf_AddByte(&buf, par->p[0]); /* likely? */
|
||||
par->p++;
|
||||
continue;
|
||||
case ')':
|
||||
case ')': /* see is_separator */
|
||||
case '!':
|
||||
case '=':
|
||||
case '>':
|
||||
case '<':
|
||||
case ' ':
|
||||
case '\t':
|
||||
if (!qt)
|
||||
if (!quoted)
|
||||
goto got_str;
|
||||
Buf_AddByte(&buf, par->p[0]);
|
||||
par->p++;
|
||||
continue;
|
||||
case '$':
|
||||
/* if we are in quotes, an undefined variable is ok */
|
||||
eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) |
|
||||
(doEval ? VARE_WANTRES : 0);
|
||||
eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
|
||||
doEval ? VARE_WANTRES :
|
||||
VARE_NONE;
|
||||
|
||||
nested_p = par->p;
|
||||
atStart = nested_p == start;
|
||||
parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, &str,
|
||||
freeIt);
|
||||
out_freeIt);
|
||||
/* TODO: handle errors */
|
||||
if (str == var_Error) {
|
||||
if (parseResult & VPR_ANY_MSG)
|
||||
par->printedError = TRUE;
|
||||
if (*freeIt) {
|
||||
free(*freeIt);
|
||||
*freeIt = NULL;
|
||||
if (*out_freeIt != NULL) {
|
||||
/* XXX: Can there be any situation in which a returned
|
||||
* var_Error requires freeIt? */
|
||||
free(*out_freeIt);
|
||||
*out_freeIt = NULL;
|
||||
}
|
||||
/*
|
||||
* Even if !doEval, we still report syntax errors, which
|
||||
@ -473,19 +473,15 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
goto cleanup;
|
||||
|
||||
Buf_AddStr(&buf, str);
|
||||
if (*freeIt) {
|
||||
free(*freeIt);
|
||||
*freeIt = NULL;
|
||||
if (*out_freeIt) {
|
||||
free(*out_freeIt);
|
||||
*out_freeIt = NULL;
|
||||
}
|
||||
str = NULL; /* not finished yet */
|
||||
continue;
|
||||
default:
|
||||
if (strictLHS && !qt && *start != '$' && !ch_isdigit(*start)) {
|
||||
if (strictLHS && !quoted && *start != '$' && !ch_isdigit(*start)) {
|
||||
/* lhs must be quoted, a variable reference or number */
|
||||
if (*freeIt) {
|
||||
free(*freeIt);
|
||||
*freeIt = NULL;
|
||||
}
|
||||
str = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -495,20 +491,22 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
}
|
||||
}
|
||||
got_str:
|
||||
*freeIt = Buf_GetAll(&buf, NULL);
|
||||
str = *freeIt;
|
||||
*out_freeIt = Buf_GetAll(&buf, NULL);
|
||||
str = *out_freeIt;
|
||||
cleanup:
|
||||
Buf_Destroy(&buf, FALSE);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* The different forms of .if directives. */
|
||||
static const struct If {
|
||||
struct If {
|
||||
const char *form; /* Form of if */
|
||||
size_t formlen; /* Length of form */
|
||||
Boolean doNot; /* TRUE if default function should be negated */
|
||||
Boolean (*defProc)(size_t, const char *); /* Default function to apply */
|
||||
} ifs[] = {
|
||||
};
|
||||
|
||||
/* The different forms of .if directives. */
|
||||
static const struct If ifs[] = {
|
||||
{ "def", 3, FALSE, FuncDefined },
|
||||
{ "ndef", 4, TRUE, FuncDefined },
|
||||
{ "make", 4, FALSE, FuncMake },
|
||||
@ -516,28 +514,39 @@ static const struct If {
|
||||
{ "", 0, FALSE, FuncDefined },
|
||||
{ NULL, 0, FALSE, NULL }
|
||||
};
|
||||
enum { PLAIN_IF_INDEX = 4 };
|
||||
|
||||
static Boolean
|
||||
If_Eval(const struct If *if_info, const char *arg, size_t arglen)
|
||||
{
|
||||
Boolean res = if_info->defProc(arglen, arg);
|
||||
return if_info->doNot ? !res : res;
|
||||
}
|
||||
|
||||
/* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
|
||||
* ".if 0". */
|
||||
static Token
|
||||
EvalNotEmpty(CondParser *par, const char *lhs, Boolean lhsQuoted)
|
||||
static Boolean
|
||||
EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
|
||||
{
|
||||
double left;
|
||||
double num;
|
||||
|
||||
/* For .ifxxx "..." check for non-empty string. */
|
||||
if (lhsQuoted)
|
||||
return lhs[0] != '\0';
|
||||
/* For .ifxxx "...", check for non-empty string. */
|
||||
if (quoted)
|
||||
return value[0] != '\0';
|
||||
|
||||
/* For .ifxxx <number> compare against zero */
|
||||
if (TryParseNumber(lhs, &left))
|
||||
return left != 0.0;
|
||||
/* For .ifxxx <number>, compare against zero */
|
||||
if (TryParseNumber(value, &num))
|
||||
return num != 0.0;
|
||||
|
||||
/* For .if ${...} check for non-empty string (defProc is ifdef). */
|
||||
/* For .if ${...}, check for non-empty string. This is different from
|
||||
* the evaluation function from that .if variant, which would test
|
||||
* whether a variable of the given name were defined. */
|
||||
/* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
|
||||
if (par->if_info->form[0] == '\0')
|
||||
return lhs[0] != 0;
|
||||
return value[0] != '\0';
|
||||
|
||||
/* Otherwise action default test ... */
|
||||
return par->if_info->defProc(strlen(lhs), lhs) == !par->if_info->doNot;
|
||||
/* For the other variants of .ifxxx ${...}, use its default function. */
|
||||
return If_Eval(par->if_info, value, strlen(value));
|
||||
}
|
||||
|
||||
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
|
||||
@ -553,18 +562,18 @@ EvalCompareNum(double lhs, const char *op, double rhs)
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return lhs != rhs;
|
||||
return ToToken(lhs != rhs);
|
||||
case '=':
|
||||
if (op[1] != '=') {
|
||||
Parse_Error(PARSE_WARNING, "Unknown operator");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return lhs == rhs;
|
||||
return ToToken(lhs == rhs);
|
||||
case '<':
|
||||
return op[1] == '=' ? lhs <= rhs : lhs < rhs;
|
||||
return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs);
|
||||
case '>':
|
||||
return op[1] == '=' ? lhs >= rhs : lhs > rhs;
|
||||
return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs);
|
||||
}
|
||||
return TOK_ERROR;
|
||||
}
|
||||
@ -580,7 +589,7 @@ EvalCompareStr(const char *lhs, const char *op, const char *rhs)
|
||||
}
|
||||
|
||||
DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op);
|
||||
return (*op == '=') == (strcmp(lhs, rhs) == 0);
|
||||
return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0));
|
||||
}
|
||||
|
||||
/* Evaluate a comparison, such as "${VAR} == 12345". */
|
||||
@ -609,43 +618,34 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
|
||||
{
|
||||
Token t = TOK_ERROR;
|
||||
const char *lhs, *op, *rhs;
|
||||
void *lhsFree, *rhsFree;
|
||||
void *lhs_freeIt, *rhs_freeIt;
|
||||
Boolean lhsQuoted, rhsQuoted;
|
||||
|
||||
rhs = NULL;
|
||||
lhsFree = rhsFree = NULL;
|
||||
lhsQuoted = rhsQuoted = FALSE;
|
||||
|
||||
/*
|
||||
* Parse the variable spec and skip over it, saving its
|
||||
* value in lhs.
|
||||
*/
|
||||
lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhsFree);
|
||||
if (!lhs)
|
||||
goto done;
|
||||
lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhs_freeIt);
|
||||
if (lhs == NULL)
|
||||
goto done_lhs;
|
||||
|
||||
CondParser_SkipWhitespace(par);
|
||||
|
||||
/*
|
||||
* Make sure the operator is a valid one. If it isn't a
|
||||
* known relational operator, pretend we got a
|
||||
* != 0 comparison.
|
||||
*/
|
||||
op = par->p;
|
||||
switch (par->p[0]) {
|
||||
case '!':
|
||||
case '=':
|
||||
case '<':
|
||||
case '>':
|
||||
if (par->p[1] == '=') {
|
||||
if (par->p[1] == '=')
|
||||
par->p += 2;
|
||||
} else {
|
||||
else
|
||||
par->p++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
t = doEval ? EvalNotEmpty(par, lhs, lhsQuoted) : TOK_FALSE;
|
||||
goto done;
|
||||
/* Unknown operator, compare against an empty string or 0. */
|
||||
t = ToToken(doEval && EvalNotEmpty(par, lhs, lhsQuoted));
|
||||
goto done_lhs;
|
||||
}
|
||||
|
||||
CondParser_SkipWhitespace(par);
|
||||
@ -653,42 +653,45 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
|
||||
if (par->p[0] == '\0') {
|
||||
Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
goto done;
|
||||
goto done_lhs;
|
||||
}
|
||||
|
||||
rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhsFree);
|
||||
rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhs_freeIt);
|
||||
if (rhs == NULL)
|
||||
goto done;
|
||||
goto done_rhs;
|
||||
|
||||
if (!doEval) {
|
||||
t = TOK_FALSE;
|
||||
goto done;
|
||||
goto done_rhs;
|
||||
}
|
||||
|
||||
t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
|
||||
|
||||
done:
|
||||
free(lhsFree);
|
||||
free(rhsFree);
|
||||
done_rhs:
|
||||
free(rhs_freeIt);
|
||||
done_lhs:
|
||||
free(lhs_freeIt);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* The argument to empty() is a variable name, optionally followed by
|
||||
* variable modifiers. */
|
||||
static size_t
|
||||
ParseEmptyArg(const char **linePtr, Boolean doEval,
|
||||
const char *func MAKE_ATTR_UNUSED, char **argPtr)
|
||||
ParseEmptyArg(const char **pp, Boolean doEval,
|
||||
const char *func MAKE_ATTR_UNUSED, char **out_arg)
|
||||
{
|
||||
void *val_freeIt;
|
||||
const char *val;
|
||||
size_t magic_res;
|
||||
|
||||
/* We do all the work here and return the result as the length */
|
||||
*argPtr = NULL;
|
||||
*out_arg = NULL;
|
||||
|
||||
(*linePtr)--; /* Make (*linePtr)[1] point to the '('. */
|
||||
(void)Var_Parse(linePtr, VAR_CMDLINE, doEval ? VARE_WANTRES : 0,
|
||||
(*pp)--; /* Make (*pp)[1] point to the '('. */
|
||||
(void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
|
||||
&val, &val_freeIt);
|
||||
/* TODO: handle errors */
|
||||
/* If successful, *linePtr points beyond the closing ')' now. */
|
||||
/* If successful, *pp points beyond the closing ')' now. */
|
||||
|
||||
if (val == var_Error) {
|
||||
free(val_freeIt);
|
||||
@ -714,54 +717,71 @@ FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
|
||||
return arglen == 1;
|
||||
}
|
||||
|
||||
static Token
|
||||
CondParser_Func(CondParser *par, Boolean doEval)
|
||||
static Boolean
|
||||
CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
|
||||
{
|
||||
static const struct fn_def {
|
||||
const char *fn_name;
|
||||
size_t fn_name_len;
|
||||
size_t (*fn_parse)(const char **, Boolean, const char *, char **);
|
||||
Boolean (*fn_eval)(size_t, const char *);
|
||||
} fn_defs[] = {
|
||||
} fns[] = {
|
||||
{ "defined", 7, ParseFuncArg, FuncDefined },
|
||||
{ "make", 4, ParseFuncArg, FuncMake },
|
||||
{ "exists", 6, ParseFuncArg, FuncExists },
|
||||
{ "empty", 5, ParseEmptyArg, FuncEmpty },
|
||||
{ "target", 6, ParseFuncArg, FuncTarget },
|
||||
{ "commands", 8, ParseFuncArg, FuncCommands },
|
||||
{ NULL, 0, NULL, NULL },
|
||||
{ "commands", 8, ParseFuncArg, FuncCommands }
|
||||
};
|
||||
const struct fn_def *fn_def;
|
||||
const struct fn_def *fn;
|
||||
char *arg = NULL;
|
||||
size_t arglen;
|
||||
const char *cp = par->p;
|
||||
const struct fn_def *fns_end = fns + sizeof fns / sizeof fns[0];
|
||||
|
||||
for (fn = fns; fn != fns_end; fn++) {
|
||||
if (!is_token(cp, fn->fn_name, fn->fn_name_len))
|
||||
continue;
|
||||
|
||||
cp += fn->fn_name_len;
|
||||
cpp_skip_whitespace(&cp);
|
||||
if (*cp != '(')
|
||||
break;
|
||||
|
||||
arglen = fn->fn_parse(&cp, doEval, fn->fn_name, &arg);
|
||||
if (arglen == 0 || arglen == (size_t)-1) {
|
||||
par->p = cp;
|
||||
*out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Evaluate the argument using the required function. */
|
||||
*out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
|
||||
free(arg);
|
||||
par->p = cp;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Parse a function call, a number, a variable expression or a string
|
||||
* literal. */
|
||||
static Token
|
||||
CondParser_LeafToken(CondParser *par, Boolean doEval)
|
||||
{
|
||||
Token t;
|
||||
char *arg = NULL;
|
||||
size_t arglen;
|
||||
const char *cp = par->p;
|
||||
const char *cp1;
|
||||
|
||||
for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) {
|
||||
if (!is_token(cp, fn_def->fn_name, fn_def->fn_name_len))
|
||||
continue;
|
||||
cp += fn_def->fn_name_len;
|
||||
/* There can only be whitespace before the '(' */
|
||||
cpp_skip_whitespace(&cp);
|
||||
if (*cp != '(')
|
||||
break;
|
||||
|
||||
arglen = fn_def->fn_parse(&cp, doEval, fn_def->fn_name, &arg);
|
||||
if (arglen == 0 || arglen == (size_t)-1) {
|
||||
par->p = cp;
|
||||
return arglen == 0 ? TOK_FALSE : TOK_ERROR;
|
||||
}
|
||||
/* Evaluate the argument using the required function. */
|
||||
t = !doEval || fn_def->fn_eval(arglen, arg);
|
||||
free(arg);
|
||||
par->p = cp;
|
||||
if (CondParser_Func(par, doEval, &t))
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Push anything numeric through the compare expression */
|
||||
cp = par->p;
|
||||
if (ch_isdigit(cp[0]) || strchr("+-", cp[0]))
|
||||
if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
|
||||
return CondParser_Comparison(par, doEval);
|
||||
|
||||
/*
|
||||
@ -785,7 +805,7 @@ CondParser_Func(CondParser *par, Boolean doEval)
|
||||
* after .if must have been taken literally, so the argument cannot
|
||||
* be empty - even if it contained a variable expansion.
|
||||
*/
|
||||
t = !doEval || par->if_info->defProc(arglen, arg) == !par->if_info->doNot;
|
||||
t = ToToken(!doEval || If_Eval(par->if_info, arg, arglen));
|
||||
free(arg);
|
||||
return t;
|
||||
}
|
||||
@ -802,9 +822,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
return t;
|
||||
}
|
||||
|
||||
while (par->p[0] == ' ' || par->p[0] == '\t') {
|
||||
par->p++;
|
||||
}
|
||||
cpp_skip_hspace(&par->p);
|
||||
|
||||
switch (par->p[0]) {
|
||||
|
||||
@ -818,15 +836,23 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
|
||||
case '|':
|
||||
par->p++;
|
||||
if (par->p[0] == '|') {
|
||||
if (par->p[0] == '|')
|
||||
par->p++;
|
||||
else if (opts.lint) {
|
||||
Parse_Error(PARSE_FATAL, "Unknown operator '|'");
|
||||
par->printedError = TRUE;
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return TOK_OR;
|
||||
|
||||
case '&':
|
||||
par->p++;
|
||||
if (par->p[0] == '&') {
|
||||
if (par->p[0] == '&')
|
||||
par->p++;
|
||||
else if (opts.lint) {
|
||||
Parse_Error(PARSE_FATAL, "Unknown operator '&'");
|
||||
par->printedError = TRUE;
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return TOK_AND;
|
||||
|
||||
@ -834,8 +860,9 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
par->p++;
|
||||
return TOK_NOT;
|
||||
|
||||
case '#':
|
||||
case '\n':
|
||||
case '#': /* XXX: see unit-tests/cond-token-plain.mk */
|
||||
case '\n': /* XXX: why should this end the condition? */
|
||||
/* Probably obsolete now, from 1993-03-21. */
|
||||
case '\0':
|
||||
return TOK_EOF;
|
||||
|
||||
@ -844,7 +871,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
return CondParser_Comparison(par, doEval);
|
||||
|
||||
default:
|
||||
return CondParser_Func(par, doEval);
|
||||
return CondParser_LeafToken(par, doEval);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1003,25 +1030,14 @@ static CondEvalResult
|
||||
CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
|
||||
Boolean eprint, Boolean strictLHS)
|
||||
{
|
||||
static const struct If *dflt_info;
|
||||
CondParser par;
|
||||
int rval;
|
||||
CondEvalResult rval;
|
||||
|
||||
lhsStrict = strictLHS;
|
||||
|
||||
while (*cond == ' ' || *cond == '\t')
|
||||
cond++;
|
||||
cpp_skip_hspace(&cond);
|
||||
|
||||
if (info == NULL && (info = dflt_info) == NULL) {
|
||||
/* Scan for the entry for .if - it can't be first */
|
||||
for (info = ifs;; info++)
|
||||
if (info->form[0] == 0)
|
||||
break;
|
||||
dflt_info = info;
|
||||
}
|
||||
assert(info != NULL);
|
||||
|
||||
par.if_info = info;
|
||||
par.if_info = info != NULL ? info : ifs + PLAIN_IF_INDEX;
|
||||
par.p = cond;
|
||||
par.curr = TOK_NONE;
|
||||
par.printedError = FALSE;
|
||||
@ -1034,123 +1050,154 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* Evaluate a condition in a :? modifier, such as
|
||||
* ${"${VAR}" == value:?yes:no}. */
|
||||
CondEvalResult
|
||||
Cond_EvalCondition(const char *cond, Boolean *out_value)
|
||||
{
|
||||
return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/* Evaluate the conditional in the passed line. The line looks like this:
|
||||
* .<cond-type> <expr>
|
||||
* In this line, <cond-type> is any of if, ifmake, ifnmake, ifdef, ifndef,
|
||||
* elif, elifmake, elifnmake, elifdef, elifndef.
|
||||
* In this line, <expr> consists of &&, ||, !, function(arg), comparisons
|
||||
* and parenthetical groupings thereof.
|
||||
/* Evaluate the conditional directive in the line, which is one of:
|
||||
*
|
||||
* Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order
|
||||
* to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF),
|
||||
* otherwise .else could be treated as '.elif 1'.
|
||||
* .if <cond>
|
||||
* .ifmake <cond>
|
||||
* .ifnmake <cond>
|
||||
* .ifdef <cond>
|
||||
* .ifndef <cond>
|
||||
* .elif <cond>
|
||||
* .elifmake <cond>
|
||||
* .elifnmake <cond>
|
||||
* .elifdef <cond>
|
||||
* .elifndef <cond>
|
||||
* .else
|
||||
* .endif
|
||||
*
|
||||
* In these directives, <cond> consists of &&, ||, !, function(arg),
|
||||
* comparisons, expressions, bare words, numbers and strings, and
|
||||
* parenthetical groupings thereof.
|
||||
*
|
||||
* Results:
|
||||
* COND_PARSE to continue parsing the lines after the conditional
|
||||
* (when .if or .else returns TRUE)
|
||||
* COND_PARSE to continue parsing the lines that follow the
|
||||
* conditional (when <cond> evaluates to TRUE)
|
||||
* COND_SKIP to skip the lines after the conditional
|
||||
* (when .if or .elif returns FALSE, or when a previous
|
||||
* (when <cond> evaluates to FALSE, or when a previous
|
||||
* branch has already been taken)
|
||||
* COND_INVALID 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
|
||||
*/
|
||||
CondEvalResult
|
||||
Cond_EvalLine(const char *line)
|
||||
Cond_EvalLine(const char *const line)
|
||||
{
|
||||
enum { MAXIF = 128 }; /* maximum depth of .if'ing */
|
||||
enum { MAXIF_BUMP = 32 }; /* how much to grow by */
|
||||
enum if_states {
|
||||
IF_ACTIVE, /* .if or .elif part active */
|
||||
ELSE_ACTIVE, /* .else part active */
|
||||
SEARCH_FOR_ELIF, /* searching for .elif/else to execute */
|
||||
SKIP_TO_ELSE, /* has been true, but not seen '.else' */
|
||||
SKIP_TO_ENDIF /* nothing else to execute */
|
||||
};
|
||||
static enum if_states *cond_state = NULL;
|
||||
static unsigned int max_if_depth = MAXIF;
|
||||
typedef enum IfState {
|
||||
|
||||
/* None of the previous <cond> evaluated to TRUE. */
|
||||
IFS_INITIAL = 0,
|
||||
|
||||
/* The previous <cond> evaluated to TRUE.
|
||||
* The lines following this condition are interpreted. */
|
||||
IFS_ACTIVE = 1 << 0,
|
||||
|
||||
/* The previous directive was an '.else'. */
|
||||
IFS_SEEN_ELSE = 1 << 1,
|
||||
|
||||
/* One of the previous <cond> evaluated to TRUE. */
|
||||
IFS_WAS_ACTIVE = 1 << 2
|
||||
|
||||
} IfState;
|
||||
|
||||
static enum IfState *cond_states = NULL;
|
||||
static unsigned int cond_states_cap = 128;
|
||||
|
||||
const struct If *ifp;
|
||||
Boolean isElif;
|
||||
Boolean value;
|
||||
enum if_states state;
|
||||
IfState state;
|
||||
const char *p = line;
|
||||
|
||||
if (!cond_state) {
|
||||
cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state));
|
||||
cond_state[0] = IF_ACTIVE;
|
||||
if (cond_states == NULL) {
|
||||
cond_states = bmake_malloc(cond_states_cap * sizeof *cond_states);
|
||||
cond_states[0] = IFS_ACTIVE;
|
||||
}
|
||||
/* skip leading character (the '.') and any whitespace */
|
||||
for (line++; *line == ' ' || *line == '\t'; line++)
|
||||
continue;
|
||||
|
||||
/* Find what type of if we're dealing with. */
|
||||
if (line[0] == 'e') {
|
||||
if (line[1] != 'l') {
|
||||
if (!is_token(line + 1, "ndif", 4))
|
||||
p++; /* skip the leading '.' */
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
|
||||
if (p[0] == 'e') {
|
||||
if (p[1] != 'l') {
|
||||
if (!is_token(p + 1, "ndif", 4)) {
|
||||
/* Unknown directive. It might still be a transformation
|
||||
* rule like '.elisp.scm', therefore no error message here. */
|
||||
return COND_INVALID;
|
||||
/* End of conditional section */
|
||||
}
|
||||
|
||||
/* It is an '.endif'. */
|
||||
/* TODO: check for extraneous <cond> */
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less endif");
|
||||
return COND_PARSE;
|
||||
}
|
||||
|
||||
/* Return state for previous conditional */
|
||||
cond_depth--;
|
||||
return cond_state[cond_depth] <= ELSE_ACTIVE
|
||||
return cond_states[cond_depth] & IFS_ACTIVE
|
||||
? COND_PARSE : COND_SKIP;
|
||||
}
|
||||
|
||||
/* Quite likely this is 'else' or 'elif' */
|
||||
line += 2;
|
||||
if (is_token(line, "se", 2)) {
|
||||
/* It is else... */
|
||||
p += 2;
|
||||
if (is_token(p, "se", 2)) { /* It is an 'else'. */
|
||||
|
||||
if (opts.lint && p[2] != '\0')
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"The .else directive does not take arguments.");
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less else");
|
||||
return COND_PARSE;
|
||||
}
|
||||
|
||||
state = cond_state[cond_depth];
|
||||
switch (state) {
|
||||
case SEARCH_FOR_ELIF:
|
||||
state = ELSE_ACTIVE;
|
||||
break;
|
||||
case ELSE_ACTIVE:
|
||||
case SKIP_TO_ENDIF:
|
||||
Parse_Error(PARSE_WARNING, "extra else");
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
case IF_ACTIVE:
|
||||
case SKIP_TO_ELSE:
|
||||
state = SKIP_TO_ENDIF;
|
||||
break;
|
||||
state = cond_states[cond_depth];
|
||||
if (state == IFS_INITIAL) {
|
||||
state = IFS_ACTIVE | IFS_SEEN_ELSE;
|
||||
} else {
|
||||
if (state & IFS_SEEN_ELSE)
|
||||
Parse_Error(PARSE_WARNING, "extra else");
|
||||
state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
}
|
||||
cond_state[cond_depth] = state;
|
||||
return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP;
|
||||
cond_states[cond_depth] = state;
|
||||
|
||||
return state & IFS_ACTIVE ? COND_PARSE : COND_SKIP;
|
||||
}
|
||||
/* Assume for now it is an elif */
|
||||
isElif = TRUE;
|
||||
} else
|
||||
isElif = FALSE;
|
||||
|
||||
if (line[0] != 'i' || line[1] != 'f')
|
||||
/* Not an ifxxx or elifxxx line */
|
||||
return COND_INVALID;
|
||||
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 COND_INVALID; /* Not an ifxxx or elifxxx line */
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out what sort of conditional it is -- what its default
|
||||
* function is, etc. -- by looking in the table of valid "ifs"
|
||||
*/
|
||||
line += 2;
|
||||
p += 2;
|
||||
for (ifp = ifs;; ifp++) {
|
||||
if (ifp->form == NULL)
|
||||
if (ifp->form == NULL) {
|
||||
/* TODO: Add error message about unknown directive,
|
||||
* since there is no other known directive that starts with 'el'
|
||||
* or 'if'.
|
||||
* Example: .elifx 123 */
|
||||
return COND_INVALID;
|
||||
if (is_token(ifp->form, line, ifp->formlen)) {
|
||||
line += ifp->formlen;
|
||||
}
|
||||
if (is_token(p, ifp->form, ifp->formlen)) {
|
||||
p += ifp->formlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1162,51 +1209,51 @@ Cond_EvalLine(const char *line)
|
||||
Parse_Error(PARSE_FATAL, "if-less elif");
|
||||
return COND_PARSE;
|
||||
}
|
||||
state = cond_state[cond_depth];
|
||||
if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) {
|
||||
state = cond_states[cond_depth];
|
||||
if (state & IFS_SEEN_ELSE) {
|
||||
Parse_Error(PARSE_WARNING, "extra elif");
|
||||
cond_state[cond_depth] = SKIP_TO_ENDIF;
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
if (state != SEARCH_FOR_ELIF) {
|
||||
/* Either just finished the 'true' block, or already SKIP_TO_ELSE */
|
||||
cond_state[cond_depth] = SKIP_TO_ELSE;
|
||||
if (state != IFS_INITIAL) {
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
} else {
|
||||
/* Normal .if */
|
||||
if (cond_depth + 1 >= max_if_depth) {
|
||||
if (cond_depth + 1 >= cond_states_cap) {
|
||||
/*
|
||||
* This is rare, but not impossible.
|
||||
* In meta mode, dirdeps.mk (only runs at level 0)
|
||||
* can need more than the default.
|
||||
*/
|
||||
max_if_depth += MAXIF_BUMP;
|
||||
cond_state = bmake_realloc(cond_state,
|
||||
max_if_depth * sizeof(*cond_state));
|
||||
cond_states_cap += 32;
|
||||
cond_states = bmake_realloc(cond_states,
|
||||
cond_states_cap * sizeof *cond_states);
|
||||
}
|
||||
state = cond_state[cond_depth];
|
||||
state = cond_states[cond_depth];
|
||||
cond_depth++;
|
||||
if (state > ELSE_ACTIVE) {
|
||||
if (!(state & IFS_ACTIVE)) {
|
||||
/* If we aren't parsing the data, treat as always false */
|
||||
cond_state[cond_depth] = SKIP_TO_ELSE;
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
/* And evaluate the conditional expression */
|
||||
if (CondEvalExpression(ifp, line, &value, TRUE, TRUE) == COND_INVALID) {
|
||||
if (CondEvalExpression(ifp, p, &value, TRUE, TRUE) == COND_INVALID) {
|
||||
/* Syntax error in conditional, error message already output. */
|
||||
/* Skip everything to matching .endif */
|
||||
cond_state[cond_depth] = SKIP_TO_ELSE;
|
||||
/* XXX: An extra '.else' is not detected in this case. */
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
cond_state[cond_depth] = SEARCH_FOR_ELIF;
|
||||
cond_states[cond_depth] = IFS_INITIAL;
|
||||
return COND_SKIP;
|
||||
}
|
||||
cond_state[cond_depth] = IF_ACTIVE;
|
||||
cond_states[cond_depth] = IFS_ACTIVE;
|
||||
return COND_PARSE;
|
||||
}
|
||||
|
||||
|
30
configure
vendored
30
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for bmake 20201018.
|
||||
# Generated by GNU Autoconf 2.69 for bmake 20201112.
|
||||
#
|
||||
# Report bugs to <sjg@NetBSD.org>.
|
||||
#
|
||||
@ -580,8 +580,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='bmake'
|
||||
PACKAGE_TARNAME='bmake'
|
||||
PACKAGE_VERSION='20201018'
|
||||
PACKAGE_STRING='bmake 20201018'
|
||||
PACKAGE_VERSION='20201112'
|
||||
PACKAGE_STRING='bmake 20201112'
|
||||
PACKAGE_BUGREPORT='sjg@NetBSD.org'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -631,6 +631,7 @@ GCC
|
||||
INSTALL
|
||||
default_sys_path
|
||||
mksrc
|
||||
force_machine_arch
|
||||
machine_arch
|
||||
force_machine
|
||||
machine
|
||||
@ -1254,7 +1255,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 20201018 to adapt to many kinds of systems.
|
||||
\`configure' configures bmake 20201112 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1315,7 +1316,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of bmake 20201018:";;
|
||||
short | recursive ) echo "Configuration of bmake 20201112:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1331,9 +1332,9 @@ Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions
|
||||
--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
|
||||
--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-machine=MACHINE explicitly set MACHINE
|
||||
--with-force-machine=MACHINE set FORCE_MACHINE
|
||||
--with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH
|
||||
@ -1421,7 +1422,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
bmake configure 20201018
|
||||
bmake configure 20201112
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -2001,7 +2002,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 20201018, which was
|
||||
It was created by bmake $as_me 20201112, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -6045,7 +6046,7 @@ if test "${with_force_machine_arch+set}" = set; then :
|
||||
withval=$with_force_machine_arch; case "${withval}" in
|
||||
yes) force_machine_arch=FORCE_;;
|
||||
no) ;;
|
||||
*) force_machine_arch=FORCE_; machine_arch=$with_force_machine;;
|
||||
*) force_machine_arch=FORCE_; machine_arch=$with_force_machine_arch;;
|
||||
esac
|
||||
fi
|
||||
|
||||
@ -6059,7 +6060,7 @@ no) ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "Using: ${force_machine}MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6
|
||||
echo "Using: ${force_machine}MACHINE=$machine, ${force_machine_arch}MACHINE_ARCH=$machine_arch" 1>&6
|
||||
default_sys_path=\${prefix}/share/mk
|
||||
|
||||
# Check whether --with-default-sys-path was given.
|
||||
@ -6151,6 +6152,7 @@ fi
|
||||
|
||||
|
||||
|
||||
|
||||
bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh"
|
||||
if test $use_makefile = yes; then
|
||||
bm_outfiles="makefile $bm_outfiles"
|
||||
@ -6664,7 +6666,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 20201018, which was
|
||||
This file was extended by bmake $as_me 20201112, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -6726,7 +6728,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
bmake config.status 20201018
|
||||
bmake config.status 20201112
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
15
configure.in
15
configure.in
@ -1,11 +1,11 @@
|
||||
dnl
|
||||
dnl RCSid:
|
||||
dnl $Id: configure.in,v 1.67 2020/10/19 19:47:50 sjg Exp $
|
||||
dnl $Id: configure.in,v 1.69 2020/11/14 07:40:43 sjg Exp $
|
||||
dnl
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
dnl
|
||||
AC_PREREQ(2.50)
|
||||
AC_INIT([bmake], [20201018], [sjg@NetBSD.org])
|
||||
AC_INIT([bmake], [20201112], [sjg@NetBSD.org])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
dnl make srcdir absolute
|
||||
@ -38,7 +38,7 @@ CYGWIN*|MINGW*) use_makefile=no;;
|
||||
*) use_makefile=yes;;
|
||||
esac
|
||||
AC_ARG_WITH(makefile,
|
||||
[ --without-makefile disable use of generated makefile],
|
||||
[ --without-makefile disable use of generated makefile],
|
||||
[case "${withval}" in
|
||||
yes|no) use_makefile=${withval};;
|
||||
*) AC_MSG_ERROR(bad value ${withval} given for makefile) ;;
|
||||
@ -46,14 +46,14 @@ esac])
|
||||
dnl
|
||||
use_meta=yes
|
||||
AC_ARG_WITH(meta,
|
||||
[ --without-meta disable use of meta-mode],
|
||||
[ --without-meta disable use of meta-mode],
|
||||
[case "${withval}" in
|
||||
yes|no) use_meta=${withval};;
|
||||
*) AC_MSG_ERROR(bad value ${withval} given for meta) ;;
|
||||
esac])
|
||||
dnl
|
||||
AC_ARG_WITH(filemon,
|
||||
[ --with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev],
|
||||
[ --with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev],
|
||||
[ case "/${withval}" in
|
||||
/no) use_filemon=no;;
|
||||
/*trace) filemon_h=no use_filemon="${withval}";;
|
||||
@ -308,7 +308,7 @@ AC_ARG_WITH(force_machine_arch,
|
||||
[case "${withval}" in
|
||||
yes) force_machine_arch=FORCE_;;
|
||||
no) ;;
|
||||
*) force_machine_arch=FORCE_; machine_arch=$with_force_machine;;
|
||||
*) force_machine_arch=FORCE_; machine_arch=$with_force_machine_arch;;
|
||||
esac])
|
||||
dnl
|
||||
AC_ARG_WITH(machine_arch,
|
||||
@ -321,7 +321,7 @@ esac])
|
||||
dnl
|
||||
dnl Tell them what we ended up with
|
||||
dnl
|
||||
echo "Using: ${force_machine}MACHINE=$machine, MACHINE_ARCH=$machine_arch" 1>&6
|
||||
echo "Using: ${force_machine}MACHINE=$machine, ${force_machine_arch}MACHINE_ARCH=$machine_arch" 1>&6
|
||||
dnl
|
||||
dnl Allow folk to control _PATH_DEFSYSPATH
|
||||
dnl
|
||||
@ -407,6 +407,7 @@ dnl
|
||||
AC_SUBST(machine)
|
||||
AC_SUBST(force_machine)
|
||||
AC_SUBST(machine_arch)
|
||||
AC_SUBST(force_machine_arch)
|
||||
AC_SUBST(mksrc)
|
||||
AC_SUBST(default_sys_path)
|
||||
AC_SUBST(INSTALL)
|
||||
|
244
dir.c
244
dir.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $ */
|
||||
/* $NetBSD: dir.c,v 1.210 2020/11/14 21:29:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -100,9 +100,9 @@
|
||||
* then all the directories above it in turn until
|
||||
* the path is found or we reach the root ("/").
|
||||
*
|
||||
* Dir_MTime Return the modification time of a node. The file
|
||||
* is searched for along the default search path.
|
||||
* The path and mtime fields of the node are filled in.
|
||||
* Dir_UpdateMTime
|
||||
* Update the modification time and path of a node with
|
||||
* data from the file corresponding to the node.
|
||||
*
|
||||
* Dir_AddDir Add a directory to a search path.
|
||||
*
|
||||
@ -134,7 +134,7 @@
|
||||
#include "job.h"
|
||||
|
||||
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: dir.c,v 1.210 2020/11/14 21:29:44 rillig Exp $");
|
||||
|
||||
#define DIR_DEBUG0(text) DEBUG0(DIR, text)
|
||||
#define DIR_DEBUG1(fmt, arg1) DEBUG1(DIR, fmt, arg1)
|
||||
@ -168,9 +168,9 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $");
|
||||
* 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 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.
|
||||
* 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.
|
||||
*
|
||||
* 3) record the mtime of the directory in the CachedDir structure and
|
||||
* verify the directory hasn't changed since the contents were cached.
|
||||
@ -184,11 +184,11 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $");
|
||||
* 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 pcc-based compilers refused to allow you 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.
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* One more note: the location of a target's file is only performed on the
|
||||
* downward traversal of the graph and then only for terminal nodes in the
|
||||
@ -204,7 +204,7 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.193 2020/10/31 17:39:20 rillig Exp $");
|
||||
* Given that an access() is essentially a stat() without the copyout() call,
|
||||
* and that the same filesystem overhead would have to be incurred in
|
||||
* Dir_MTime, it made sense to replace the access() with a stat() and record
|
||||
* the mtime in a cache for when Dir_MTime was actually called.
|
||||
* the mtime in a cache for when Dir_UpdateMTime was actually called.
|
||||
*/
|
||||
|
||||
typedef List CachedDirList;
|
||||
@ -253,12 +253,10 @@ OpenDirs_Find(OpenDirs *odirs, const char *name)
|
||||
static void
|
||||
OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir)
|
||||
{
|
||||
HashEntry *he = HashTable_FindEntry(&odirs->table, cdir->name);
|
||||
if (he != NULL)
|
||||
if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL)
|
||||
return;
|
||||
he = HashTable_CreateEntry(&odirs->table, cdir->name, NULL);
|
||||
Lst_Append(odirs->list, cdir);
|
||||
HashEntry_Set(he, odirs->list->last);
|
||||
HashTable_Set(&odirs->table, cdir->name, odirs->list->last);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -273,10 +271,10 @@ OpenDirs_Remove(OpenDirs *odirs, const char *name)
|
||||
Lst_Remove(odirs->list, ln);
|
||||
}
|
||||
|
||||
static OpenDirs openDirs; /* the list of all open directories */
|
||||
static OpenDirs openDirs; /* all cached directories */
|
||||
|
||||
/*
|
||||
* Variables for gathering statistics on the efficiency of the cashing
|
||||
* Variables for gathering statistics on the efficiency of the caching
|
||||
* mechanism.
|
||||
*/
|
||||
static int hits; /* Found in directory cache */
|
||||
@ -300,74 +298,50 @@ static HashTable mtimes;
|
||||
|
||||
static HashTable lmtimes; /* same as mtimes but for lstat */
|
||||
|
||||
/*
|
||||
* We use stat(2) a lot, cache the results.
|
||||
* mtime and mode are all we care about.
|
||||
*/
|
||||
struct cache_st {
|
||||
time_t lmtime; /* lstat */
|
||||
time_t mtime; /* stat */
|
||||
mode_t mode;
|
||||
};
|
||||
|
||||
/* minimize changes below */
|
||||
typedef enum CachedStatsFlags {
|
||||
CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */
|
||||
CST_UPDATE = 0x02 /* ignore existing cached entry */
|
||||
CST_NONE = 0,
|
||||
CST_LSTAT = 1 << 0, /* call lstat(2) instead of stat(2) */
|
||||
CST_UPDATE = 1 << 1 /* ignore existing cached entry */
|
||||
} CachedStatsFlags;
|
||||
|
||||
/* Returns 0 and the result of stat(2) or lstat(2) in *mst, or -1 on error. */
|
||||
/* Returns 0 and the result of stat(2) or lstat(2) in *out_cst,
|
||||
* or -1 on error. */
|
||||
static int
|
||||
cached_stats(HashTable *htp, const char *pathname, struct make_stat *mst,
|
||||
cached_stats(const char *pathname, struct cached_stat *out_cst,
|
||||
CachedStatsFlags flags)
|
||||
{
|
||||
HashEntry *entry;
|
||||
HashTable *tbl = flags & CST_LSTAT ? &lmtimes : &mtimes;
|
||||
struct stat sys_st;
|
||||
struct cache_st *cst;
|
||||
struct cached_stat *cst;
|
||||
int rc;
|
||||
|
||||
if (!pathname || !pathname[0])
|
||||
return -1;
|
||||
if (pathname == NULL || pathname[0] == '\0')
|
||||
return -1; /* This can happen in meta mode. */
|
||||
|
||||
entry = HashTable_FindEntry(htp, pathname);
|
||||
|
||||
if (entry && !(flags & CST_UPDATE)) {
|
||||
cst = HashEntry_Get(entry);
|
||||
|
||||
mst->mst_mode = cst->mode;
|
||||
mst->mst_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime;
|
||||
if (mst->mst_mtime) {
|
||||
DIR_DEBUG2("Using cached time %s for %s\n",
|
||||
Targ_FmtTime(mst->mst_mtime), pathname);
|
||||
return 0;
|
||||
}
|
||||
cst = HashTable_FindValue(tbl, pathname);
|
||||
if (cst != NULL && !(flags & CST_UPDATE)) {
|
||||
*out_cst = *cst;
|
||||
DIR_DEBUG2("Using cached time %s for %s\n",
|
||||
Targ_FmtTime(cst->cst_mtime), pathname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = (flags & CST_LSTAT)
|
||||
? lstat(pathname, &sys_st)
|
||||
: stat(pathname, &sys_st);
|
||||
rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st);
|
||||
if (rc == -1)
|
||||
return -1;
|
||||
return -1; /* don't cache negative lookups */
|
||||
|
||||
if (sys_st.st_mtime == 0)
|
||||
sys_st.st_mtime = 1; /* avoid confusion with missing file */
|
||||
|
||||
mst->mst_mode = sys_st.st_mode;
|
||||
mst->mst_mtime = sys_st.st_mtime;
|
||||
if (cst == NULL) {
|
||||
cst = bmake_malloc(sizeof *cst);
|
||||
HashTable_Set(tbl, pathname, cst);
|
||||
}
|
||||
|
||||
if (entry == NULL)
|
||||
entry = HashTable_CreateEntry(htp, pathname, NULL);
|
||||
if (HashEntry_Get(entry) == NULL) {
|
||||
HashEntry_Set(entry, bmake_malloc(sizeof(*cst)));
|
||||
memset(HashEntry_Get(entry), 0, sizeof(*cst));
|
||||
}
|
||||
cst = HashEntry_Get(entry);
|
||||
if (flags & CST_LSTAT) {
|
||||
cst->lmtime = sys_st.st_mtime;
|
||||
} else {
|
||||
cst->mtime = sys_st.st_mtime;
|
||||
}
|
||||
cst->mode = sys_st.st_mode;
|
||||
cst->cst_mtime = sys_st.st_mtime;
|
||||
cst->cst_mode = sys_st.st_mode;
|
||||
|
||||
*out_cst = *cst;
|
||||
DIR_DEBUG2(" Caching %s for %s\n",
|
||||
Targ_FmtTime(sys_st.st_mtime), pathname);
|
||||
|
||||
@ -375,15 +349,15 @@ cached_stats(HashTable *htp, const char *pathname, struct make_stat *mst,
|
||||
}
|
||||
|
||||
int
|
||||
cached_stat(const char *pathname, struct make_stat *st)
|
||||
cached_stat(const char *pathname, struct cached_stat *cst)
|
||||
{
|
||||
return cached_stats(&mtimes, pathname, st, 0);
|
||||
return cached_stats(pathname, cst, CST_NONE);
|
||||
}
|
||||
|
||||
int
|
||||
cached_lstat(const char *pathname, struct make_stat *st)
|
||||
cached_lstat(const char *pathname, struct cached_stat *cst)
|
||||
{
|
||||
return cached_stats(&lmtimes, pathname, st, CST_LSTAT);
|
||||
return cached_stats(pathname, cst, CST_LSTAT);
|
||||
}
|
||||
|
||||
/* Initialize the directories module. */
|
||||
@ -401,7 +375,7 @@ Dir_InitDir(const char *cdname)
|
||||
{
|
||||
Dir_InitCur(cdname);
|
||||
|
||||
dotLast = bmake_malloc(sizeof(CachedDir));
|
||||
dotLast = bmake_malloc(sizeof *dotLast);
|
||||
dotLast->refCount = 1;
|
||||
dotLast->hits = 0;
|
||||
dotLast->name = bmake_strdup(".DOTLAST");
|
||||
@ -416,23 +390,31 @@ Dir_InitCur(const char *cdname)
|
||||
{
|
||||
CachedDir *dir;
|
||||
|
||||
if (cdname != NULL) {
|
||||
if (cdname == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Our build directory is not the same as our source directory.
|
||||
* Keep this one around too.
|
||||
*/
|
||||
dir = Dir_AddDir(NULL, cdname);
|
||||
if (dir == NULL)
|
||||
return;
|
||||
|
||||
/* XXX: Reference counting is wrong here.
|
||||
* If this function is called repeatedly with the same directory name,
|
||||
* its reference count increases each time even though the number of
|
||||
* actual references stays the same. */
|
||||
|
||||
dir->refCount++;
|
||||
if (cur != NULL && cur != dir) {
|
||||
/*
|
||||
* Our build directory is not the same as our source directory.
|
||||
* Keep this one around too.
|
||||
* We've been here before, clean up.
|
||||
*/
|
||||
if ((dir = Dir_AddDir(NULL, cdname))) {
|
||||
dir->refCount++;
|
||||
if (cur && cur != dir) {
|
||||
/*
|
||||
* We've been here before, clean up.
|
||||
*/
|
||||
cur->refCount--;
|
||||
Dir_Destroy(cur);
|
||||
}
|
||||
cur = dir;
|
||||
}
|
||||
cur->refCount--;
|
||||
Dir_Destroy(cur);
|
||||
}
|
||||
cur = dir;
|
||||
}
|
||||
|
||||
/* (Re)initialize "dot" (current/object directory) path hash.
|
||||
@ -588,6 +570,9 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
|
||||
Boolean isDot = dirName[0] == '.' && dirName[1] == '\0';
|
||||
HashIter hi;
|
||||
|
||||
/* XXX: Iterating over all hash entries is inefficient. If the pattern
|
||||
* is a plain string without any wildcards, a direct lookup is faster. */
|
||||
|
||||
HashIter_Init(&hi, &dir->files);
|
||||
while (HashIter_Next(&hi) != NULL) {
|
||||
const char *base = hi.entry->key;
|
||||
@ -879,13 +864,13 @@ DirLookup(CachedDir *dir, const char *base)
|
||||
static char *
|
||||
DirLookupSubdir(CachedDir *dir, const char *name)
|
||||
{
|
||||
struct make_stat mst;
|
||||
struct cached_stat cst;
|
||||
char *file = dir == dot ? bmake_strdup(name)
|
||||
: str_concat3(dir->name, "/", name);
|
||||
|
||||
DIR_DEBUG1("checking %s ...\n", file);
|
||||
|
||||
if (cached_stat(file, &mst) == 0) {
|
||||
if (cached_stat(file, &cst) == 0) {
|
||||
nearmisses++;
|
||||
return file;
|
||||
}
|
||||
@ -974,7 +959,7 @@ Dir_FindFile(const char *name, SearchPath *path)
|
||||
const char *base; /* Terminal name of file */
|
||||
Boolean hasLastDot = FALSE; /* true if we should search dot last */
|
||||
Boolean hasSlash; /* true if 'name' contains a / */
|
||||
struct make_stat mst; /* Buffer for stat, if necessary */
|
||||
struct cached_stat cst; /* Buffer for stat, if necessary */
|
||||
const char *trailing_dot = ".";
|
||||
|
||||
/*
|
||||
@ -1176,7 +1161,7 @@ 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...
|
||||
*/
|
||||
#ifdef notdef
|
||||
#if 0
|
||||
if (base == trailing_dot) {
|
||||
base = strrchr(name, '/');
|
||||
base++;
|
||||
@ -1198,17 +1183,17 @@ Dir_FindFile(const char *name, SearchPath *path)
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
#else /* !notdef */
|
||||
#else
|
||||
DIR_DEBUG1(" Looking for \"%s\" ...\n", name);
|
||||
|
||||
bigmisses++;
|
||||
if (cached_stat(name, &mst) == 0) {
|
||||
if (cached_stat(name, &cst) == 0) {
|
||||
return bmake_strdup(name);
|
||||
}
|
||||
|
||||
DIR_DEBUG0(" failed. Returning NULL\n");
|
||||
return NULL;
|
||||
#endif /* notdef */
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -1225,7 +1210,7 @@ Dir_FindFile(const char *name, SearchPath *path)
|
||||
char *
|
||||
Dir_FindHereOrAbove(const char *here, const char *search_path)
|
||||
{
|
||||
struct make_stat mst;
|
||||
struct cached_stat cst;
|
||||
char *dirbase, *dirbase_end;
|
||||
char *try, *try_end;
|
||||
|
||||
@ -1238,12 +1223,12 @@ Dir_FindHereOrAbove(const char *here, const char *search_path)
|
||||
|
||||
/* try and stat(2) it ... */
|
||||
try = str_concat3(dirbase, "/", search_path);
|
||||
if (cached_stat(try, &mst) != -1) {
|
||||
if (cached_stat(try, &cst) != -1) {
|
||||
/*
|
||||
* success! if we found a file, chop off
|
||||
* the filename so we return a directory.
|
||||
*/
|
||||
if ((mst.mst_mode & S_IFMT) != S_IFDIR) {
|
||||
if ((cst.cst_mode & S_IFMT) != S_IFDIR) {
|
||||
try_end = try + strlen(try);
|
||||
while (try_end > try && *try_end != '/')
|
||||
try_end--;
|
||||
@ -1275,36 +1260,27 @@ Dir_FindHereOrAbove(const char *here, const char *search_path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* Dir_MTime --
|
||||
* Find the modification time of the file described by gn along the
|
||||
* search path dirSearchPath.
|
||||
/* Search gn along dirSearchPath and store its modification time in gn->mtime.
|
||||
* If no file is found, store 0 instead.
|
||||
*
|
||||
* Input:
|
||||
* gn the file whose modification time is desired
|
||||
*
|
||||
* Results:
|
||||
* The modification time or 0 if it doesn't exist
|
||||
*
|
||||
* Side Effects:
|
||||
* The modification time is placed in the node's mtime slot.
|
||||
* If the node didn't have a path entry before, and Dir_FindFile
|
||||
* found one for it, the full name is placed in the path slot.
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
time_t
|
||||
Dir_MTime(GNode *gn, Boolean recheck)
|
||||
* The found file is stored in gn->path, unless the node already had a path. */
|
||||
void
|
||||
Dir_UpdateMTime(GNode *gn, Boolean recheck)
|
||||
{
|
||||
char *fullName; /* the full pathname of name */
|
||||
struct make_stat mst; /* buffer for finding the mod time */
|
||||
char *fullName;
|
||||
struct cached_stat cst;
|
||||
|
||||
if (gn->type & OP_ARCHV) {
|
||||
return Arch_MTime(gn);
|
||||
} else if (gn->type & OP_PHONY) {
|
||||
Arch_UpdateMTime(gn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gn->type & OP_PHONY) {
|
||||
gn->mtime = 0;
|
||||
return 0;
|
||||
} else if (gn->path == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gn->path == NULL) {
|
||||
if (gn->type & OP_NOPATH)
|
||||
fullName = NULL;
|
||||
else {
|
||||
@ -1344,25 +1320,24 @@ Dir_MTime(GNode *gn, Boolean recheck)
|
||||
fullName = gn->path;
|
||||
}
|
||||
|
||||
if (fullName == NULL) {
|
||||
if (fullName == NULL)
|
||||
fullName = bmake_strdup(gn->name);
|
||||
}
|
||||
|
||||
if (cached_stats(&mtimes, fullName, &mst, recheck ? CST_UPDATE : 0) < 0) {
|
||||
if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) {
|
||||
if (gn->type & OP_MEMBER) {
|
||||
if (fullName != gn->path)
|
||||
free(fullName);
|
||||
return Arch_MemMTime(gn);
|
||||
} else {
|
||||
mst.mst_mtime = 0;
|
||||
Arch_UpdateMemberMTime(gn);
|
||||
return;
|
||||
}
|
||||
|
||||
cst.cst_mtime = 0;
|
||||
}
|
||||
|
||||
if (fullName != NULL && gn->path == NULL)
|
||||
gn->path = fullName;
|
||||
|
||||
gn->mtime = mst.mst_mtime;
|
||||
return gn->mtime;
|
||||
gn->mtime = cst.cst_mtime;
|
||||
}
|
||||
|
||||
/* Read the list of filenames in the directory and store the result
|
||||
@ -1387,6 +1362,7 @@ Dir_AddDir(SearchPath *path, const char *name)
|
||||
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
|
||||
SearchPathNode *ln;
|
||||
|
||||
/* XXX: Linear search gets slow with thousands of entries. */
|
||||
for (ln = path->first; ln != NULL; ln = ln->next) {
|
||||
CachedDir *pathDir = ln->datum;
|
||||
if (strcmp(pathDir->name, name) == 0)
|
||||
@ -1410,7 +1386,7 @@ Dir_AddDir(SearchPath *path, const char *name)
|
||||
DIR_DEBUG1("Caching %s ...", name);
|
||||
|
||||
if ((d = opendir(name)) != NULL) {
|
||||
dir = bmake_malloc(sizeof(CachedDir));
|
||||
dir = bmake_malloc(sizeof *dir);
|
||||
dir->name = bmake_strdup(name);
|
||||
dir->hits = 0;
|
||||
dir->refCount = 1;
|
||||
@ -1480,7 +1456,7 @@ Dir_MakeFlags(const char *flag, SearchPath *path)
|
||||
Buffer buf;
|
||||
SearchPathNode *ln;
|
||||
|
||||
Buf_Init(&buf, 0);
|
||||
Buf_Init(&buf);
|
||||
|
||||
if (path != NULL) {
|
||||
for (ln = path->first; ln != NULL; ln = ln->next) {
|
||||
|
14
dir.h
14
dir.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.h,v 1.32 2020/10/25 10:00:20 rillig Exp $ */
|
||||
/* $NetBSD: dir.h,v 1.34 2020/11/14 19:24:24 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -101,7 +101,7 @@ Boolean Dir_HasWildcards(const char *);
|
||||
void Dir_Expand(const char *, SearchPath *, StringList *);
|
||||
char *Dir_FindFile(const char *, SearchPath *);
|
||||
char *Dir_FindHereOrAbove(const char *, const char *);
|
||||
time_t Dir_MTime(GNode *, Boolean);
|
||||
void Dir_UpdateMTime(GNode *, Boolean);
|
||||
CachedDir *Dir_AddDir(SearchPath *, const char *);
|
||||
char *Dir_MakeFlags(const char *, SearchPath *);
|
||||
void Dir_ClearPath(SearchPath *);
|
||||
@ -112,12 +112,12 @@ void Dir_Destroy(void *);
|
||||
SearchPath *Dir_CopyDirSearchPath(void);
|
||||
|
||||
/* Stripped-down variant of struct stat. */
|
||||
struct make_stat {
|
||||
time_t mst_mtime;
|
||||
mode_t mst_mode;
|
||||
struct cached_stat {
|
||||
time_t cst_mtime;
|
||||
mode_t cst_mode;
|
||||
};
|
||||
|
||||
int cached_lstat(const char *, struct make_stat *);
|
||||
int cached_stat(const char *, struct make_stat *);
|
||||
int cached_lstat(const char *, struct cached_stat *);
|
||||
int cached_stat(const char *, struct cached_stat *);
|
||||
|
||||
#endif /* MAKE_DIR_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon_dev.c,v 1.3 2020/07/10 15:53:30 sjg Exp $ */
|
||||
/* $NetBSD: filemon_dev.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2020 The NetBSD Foundation, Inc.
|
||||
@ -65,7 +65,7 @@ filemon_open(void)
|
||||
int error;
|
||||
|
||||
/* Allocate and zero a struct filemon object. */
|
||||
F = calloc(1, sizeof(*F));
|
||||
F = calloc(1, sizeof *F);
|
||||
if (F == NULL)
|
||||
return NULL;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.3 2020/10/18 11:54:43 rillig Exp $ */
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
@ -198,7 +198,7 @@ filemon_open(void)
|
||||
int error;
|
||||
|
||||
/* Allocate and zero a struct filemon object. */
|
||||
F = calloc(1, sizeof(*F));
|
||||
F = calloc(1, sizeof *F);
|
||||
if (F == NULL)
|
||||
return NULL;
|
||||
|
||||
|
68
for.c
68
for.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $ */
|
||||
/* $NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1992, The Regents of the University of California.
|
||||
@ -60,15 +60,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $");
|
||||
|
||||
/* The .for loop substitutes the items as ${:U<value>...}, which means
|
||||
* that characters that break this syntax must be backslash-escaped. */
|
||||
typedef enum ForEscapes {
|
||||
FOR_SUB_ESCAPE_CHAR = 0x0001,
|
||||
FOR_SUB_ESCAPE_BRACE = 0x0002,
|
||||
FOR_SUB_ESCAPE_PAREN = 0x0004
|
||||
} ForEscapes;
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $");
|
||||
|
||||
static int forLevel = 0; /* Nesting level */
|
||||
|
||||
@ -120,30 +112,6 @@ For_Free(For *f)
|
||||
free(f);
|
||||
}
|
||||
|
||||
static ForEscapes
|
||||
GetEscapes(const char *word)
|
||||
{
|
||||
const char *p;
|
||||
ForEscapes escapes = 0;
|
||||
|
||||
for (p = word; *p != '\0'; p++) {
|
||||
switch (*p) {
|
||||
case ':':
|
||||
case '$':
|
||||
case '\\':
|
||||
escapes |= FOR_SUB_ESCAPE_CHAR;
|
||||
break;
|
||||
case ')':
|
||||
escapes |= FOR_SUB_ESCAPE_PAREN;
|
||||
break;
|
||||
case '}':
|
||||
escapes |= FOR_SUB_ESCAPE_BRACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return escapes;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
IsFor(const char *p)
|
||||
{
|
||||
@ -191,11 +159,11 @@ For_Eval(const char *line)
|
||||
*/
|
||||
|
||||
f = bmake_malloc(sizeof *f);
|
||||
Buf_Init(&f->body, 0);
|
||||
Buf_Init(&f->body);
|
||||
Vector_Init(&f->vars, sizeof(ForVar));
|
||||
f->items.words = NULL;
|
||||
f->items.freeIt = NULL;
|
||||
Buf_Init(&f->curBody, 0);
|
||||
Buf_Init(&f->curBody);
|
||||
f->short_var = FALSE;
|
||||
f->sub_next = 0;
|
||||
|
||||
@ -302,7 +270,7 @@ for_var_len(const char *var)
|
||||
size_t len;
|
||||
|
||||
var_start = *var;
|
||||
if (var_start == 0)
|
||||
if (var_start == '\0')
|
||||
/* just escape the $ */
|
||||
return 0;
|
||||
|
||||
@ -315,7 +283,7 @@ for_var_len(const char *var)
|
||||
return 1;
|
||||
|
||||
depth = 1;
|
||||
for (len = 1; (ch = var[len++]) != 0;) {
|
||||
for (len = 1; (ch = var[len++]) != '\0';) {
|
||||
if (ch == var_start)
|
||||
depth++;
|
||||
else if (ch == var_end && --depth == 0)
|
||||
@ -326,18 +294,30 @@ for_var_len(const char *var)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The .for loop substitutes the items as ${:U<value>...}, which means
|
||||
* that characters that break this syntax must be backslash-escaped. */
|
||||
static Boolean
|
||||
NeedsEscapes(const char *word, char endc)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
for (p = word; *p != '\0'; p++) {
|
||||
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* While expanding the body of a .for loop, write the item in the ${:U...}
|
||||
* expression, escaping characters as needed. See ApplyModifier_Defined. */
|
||||
* expression, escaping characters as needed.
|
||||
*
|
||||
* The result is later unescaped by ApplyModifier_Defined. */
|
||||
static void
|
||||
Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
|
||||
{
|
||||
ForEscapes escapes = GetEscapes(item);
|
||||
char ch;
|
||||
|
||||
/* If there were no escapes, or the only escape is the other variable
|
||||
* terminator, then just substitute the full string */
|
||||
if (!(escapes & (ech == ')' ? ~(unsigned)FOR_SUB_ESCAPE_BRACE
|
||||
: ~(unsigned)FOR_SUB_ESCAPE_PAREN))) {
|
||||
if (!NeedsEscapes(item, ech)) {
|
||||
Buf_AddStr(cmds, item);
|
||||
return;
|
||||
}
|
||||
|
18
hash.c
18
hash.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $ */
|
||||
/* $NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -74,7 +74,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The ratio of # entries to # buckets at which we rebuild the table to
|
||||
@ -128,7 +128,7 @@ void
|
||||
HashTable_Init(HashTable *t)
|
||||
{
|
||||
unsigned int n = 16, i;
|
||||
HashEntry **buckets = bmake_malloc(sizeof(*buckets) * n);
|
||||
HashEntry **buckets = bmake_malloc(sizeof *buckets * n);
|
||||
for (i = 0; i < n; i++)
|
||||
buckets[i] = NULL;
|
||||
|
||||
@ -195,7 +195,7 @@ HashTable_Enlarge(HashTable *t)
|
||||
HashEntry **oldBuckets = t->buckets;
|
||||
unsigned int newSize = 2 * oldSize;
|
||||
unsigned int newMask = newSize - 1;
|
||||
HashEntry **newBuckets = bmake_malloc(sizeof(*newBuckets) * newSize);
|
||||
HashEntry **newBuckets = bmake_malloc(sizeof *newBuckets * newSize);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < newSize; i++)
|
||||
@ -239,7 +239,7 @@ HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
|
||||
if (t->numEntries >= rebuildLimit * t->bucketsSize)
|
||||
HashTable_Enlarge(t);
|
||||
|
||||
he = bmake_malloc(sizeof(*he) + keylen);
|
||||
he = bmake_malloc(sizeof *he + keylen);
|
||||
he->value = NULL;
|
||||
he->key_hash = h;
|
||||
memcpy(he->key, key, keylen + 1);
|
||||
@ -253,6 +253,14 @@ HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
|
||||
return he;
|
||||
}
|
||||
|
||||
HashEntry *
|
||||
HashTable_Set(HashTable *t, const char *key, void *value)
|
||||
{
|
||||
HashEntry *he = HashTable_CreateEntry(t, key, NULL);
|
||||
HashEntry_Set(he, value);
|
||||
return he;
|
||||
}
|
||||
|
||||
/* Delete the entry from the table and free the associated memory. */
|
||||
void
|
||||
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
|
||||
|
7
hash.h
7
hash.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.h,v 1.31 2020/10/25 19:19:07 rillig Exp $ */
|
||||
/* $NetBSD: hash.h,v 1.33 2020/11/14 21:29:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -103,13 +103,13 @@ typedef struct HashIter {
|
||||
HashEntry *entry; /* Next entry to check in current bucket. */
|
||||
} HashIter;
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void *
|
||||
MAKE_INLINE void *
|
||||
HashEntry_Get(HashEntry *h)
|
||||
{
|
||||
return h->value;
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
HashEntry_Set(HashEntry *h, void *datum)
|
||||
{
|
||||
h->value = datum;
|
||||
@ -122,6 +122,7 @@ void *HashTable_FindValue(HashTable *, const char *);
|
||||
unsigned int Hash_Hash(const char *);
|
||||
void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
|
||||
HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
|
||||
HashEntry *HashTable_Set(HashTable *, const char *, void *);
|
||||
void HashTable_DeleteEntry(HashTable *, HashEntry *);
|
||||
void HashTable_DebugStats(HashTable *, const char *);
|
||||
|
||||
|
24
job.h
24
job.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.h,v 1.58 2020/10/26 21:34:10 rillig Exp $ */
|
||||
/* $NetBSD: job.h,v 1.63 2020/11/14 13:27:01 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -117,25 +117,25 @@ struct pollfd;
|
||||
# include "meta.h"
|
||||
#endif
|
||||
|
||||
typedef enum JobState {
|
||||
typedef enum JobStatus {
|
||||
JOB_ST_FREE = 0, /* Job is available */
|
||||
JOB_ST_SETUP = 1, /* Job is allocated but otherwise invalid */
|
||||
JOB_ST_SET_UP = 1, /* Job is allocated but otherwise invalid */
|
||||
/* XXX: What about the 2? */
|
||||
JOB_ST_RUNNING = 3, /* Job is running, pid valid */
|
||||
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
|
||||
} JobState;
|
||||
} JobStatus;
|
||||
|
||||
typedef enum JobFlags {
|
||||
JOB_NONE = 0,
|
||||
/* Ignore non-zero exits */
|
||||
JOB_IGNERR = 0x001,
|
||||
JOB_IGNERR = 1 << 0,
|
||||
/* no output */
|
||||
JOB_SILENT = 0x002,
|
||||
JOB_SILENT = 1 << 1,
|
||||
/* Target is a special one. i.e. run it locally
|
||||
* if we can't export it and maxLocal is 0 */
|
||||
JOB_SPECIAL = 0x004,
|
||||
/* Ignore "..." lines when processing commands */
|
||||
JOB_IGNDOTS = 0x008,
|
||||
JOB_SPECIAL = 1 << 2,
|
||||
/* we've sent 'set -x' */
|
||||
JOB_TRACED = 0x400
|
||||
JOB_TRACED = 1 << 10
|
||||
} JobFlags;
|
||||
|
||||
/* A Job manages the shell commands that are run to create a single target.
|
||||
@ -167,9 +167,9 @@ typedef struct Job {
|
||||
|
||||
int exit_status; /* from wait4() in signal handler */
|
||||
|
||||
JobState job_state; /* status of the job entry */
|
||||
JobStatus status;
|
||||
|
||||
char job_suspended;
|
||||
Boolean suspended;
|
||||
|
||||
JobFlags flags; /* Flags to control treatment of job */
|
||||
|
||||
|
144
lst.c
144
lst.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $ */
|
||||
/* $NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $");
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
@ -45,11 +45,11 @@ MAKE_RCSID("$NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $");
|
||||
static ListNode *
|
||||
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
|
||||
{
|
||||
ListNode *node = bmake_malloc(sizeof *node);
|
||||
node->prev = prev;
|
||||
node->next = next;
|
||||
node->datum = datum;
|
||||
return node;
|
||||
ListNode *ln = bmake_malloc(sizeof *ln);
|
||||
ln->prev = prev;
|
||||
ln->next = next;
|
||||
ln->datum = datum;
|
||||
return ln;
|
||||
}
|
||||
|
||||
/* Create and initialize a new, empty list. */
|
||||
@ -68,12 +68,11 @@ Lst_New(void)
|
||||
void
|
||||
Lst_Free(List *list)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *next;
|
||||
ListNode *ln, *next;
|
||||
|
||||
for (node = list->first; node != NULL; node = next) {
|
||||
next = node->next;
|
||||
free(node);
|
||||
for (ln = list->first; ln != NULL; ln = next) {
|
||||
next = ln->next;
|
||||
free(ln);
|
||||
}
|
||||
|
||||
free(list);
|
||||
@ -84,37 +83,32 @@ Lst_Free(List *list)
|
||||
void
|
||||
Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *next;
|
||||
ListNode *ln, *next;
|
||||
|
||||
for (node = list->first; node != NULL; node = next) {
|
||||
next = node->next;
|
||||
freeProc(node->datum);
|
||||
free(node);
|
||||
for (ln = list->first; ln != NULL; ln = next) {
|
||||
next = ln->next;
|
||||
freeProc(ln->datum);
|
||||
free(ln);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions to modify a list
|
||||
*/
|
||||
|
||||
/* Insert a new node with the datum before the given node. */
|
||||
void
|
||||
Lst_InsertBefore(List *list, ListNode *node, void *datum)
|
||||
Lst_InsertBefore(List *list, ListNode *ln, void *datum)
|
||||
{
|
||||
ListNode *newNode;
|
||||
|
||||
assert(datum != NULL);
|
||||
|
||||
newNode = LstNodeNew(node->prev, node, datum);
|
||||
newNode = LstNodeNew(ln->prev, ln, datum);
|
||||
|
||||
if (node->prev != NULL)
|
||||
node->prev->next = newNode;
|
||||
node->prev = newNode;
|
||||
if (ln->prev != NULL)
|
||||
ln->prev->next = newNode;
|
||||
ln->prev = newNode;
|
||||
|
||||
if (node == list->first)
|
||||
if (ln == list->first)
|
||||
list->first = newNode;
|
||||
}
|
||||
|
||||
@ -122,18 +116,18 @@ Lst_InsertBefore(List *list, ListNode *node, void *datum)
|
||||
void
|
||||
Lst_Prepend(List *list, void *datum)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *ln;
|
||||
|
||||
assert(datum != NULL);
|
||||
|
||||
node = LstNodeNew(NULL, list->first, datum);
|
||||
ln = LstNodeNew(NULL, list->first, datum);
|
||||
|
||||
if (list->first == NULL) {
|
||||
list->first = node;
|
||||
list->last = node;
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->first->prev = node;
|
||||
list->first = node;
|
||||
list->first->prev = ln;
|
||||
list->first = ln;
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,71 +135,69 @@ Lst_Prepend(List *list, void *datum)
|
||||
void
|
||||
Lst_Append(List *list, void *datum)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *ln;
|
||||
|
||||
assert(datum != NULL);
|
||||
|
||||
node = LstNodeNew(list->last, NULL, datum);
|
||||
ln = LstNodeNew(list->last, NULL, datum);
|
||||
|
||||
if (list->last == NULL) {
|
||||
list->first = node;
|
||||
list->last = node;
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->last->next = node;
|
||||
list->last = node;
|
||||
list->last->next = ln;
|
||||
list->last = ln;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the given node from the given list.
|
||||
* The datum stored in the node must be freed by the caller, if necessary. */
|
||||
void
|
||||
Lst_Remove(List *list, ListNode *node)
|
||||
Lst_Remove(List *list, ListNode *ln)
|
||||
{
|
||||
/* unlink it from its neighbors */
|
||||
if (node->next != NULL)
|
||||
node->next->prev = node->prev;
|
||||
if (node->prev != NULL)
|
||||
node->prev->next = node->next;
|
||||
if (ln->next != NULL)
|
||||
ln->next->prev = ln->prev;
|
||||
if (ln->prev != NULL)
|
||||
ln->prev->next = ln->next;
|
||||
|
||||
/* unlink it from the list */
|
||||
if (list->first == node)
|
||||
list->first = node->next;
|
||||
if (list->last == node)
|
||||
list->last = node->prev;
|
||||
if (list->first == ln)
|
||||
list->first = ln->next;
|
||||
if (list->last == ln)
|
||||
list->last = ln->prev;
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node with the new datum. */
|
||||
void
|
||||
LstNode_Set(ListNode *node, void *datum)
|
||||
LstNode_Set(ListNode *ln, void *datum)
|
||||
{
|
||||
assert(datum != NULL);
|
||||
|
||||
node->datum = datum;
|
||||
ln->datum = datum;
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node to NULL.
|
||||
/* Replace the datum in the given node with NULL.
|
||||
* Having NULL values in a list is unusual though. */
|
||||
void
|
||||
LstNode_SetNull(ListNode *node)
|
||||
LstNode_SetNull(ListNode *ln)
|
||||
{
|
||||
node->datum = NULL;
|
||||
ln->datum = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for entire lists
|
||||
*/
|
||||
|
||||
/* Return the first node that contains the given datum, or NULL. */
|
||||
/* Return the first node that contains the given datum, or NULL.
|
||||
*
|
||||
* Time complexity: O(length(list)) */
|
||||
ListNode *
|
||||
Lst_FindDatum(List *list, const void *datum)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *ln;
|
||||
|
||||
assert(datum != NULL);
|
||||
|
||||
for (node = list->first; node != NULL; node = node->next)
|
||||
if (node->datum == datum)
|
||||
return node;
|
||||
for (ln = list->first; ln != NULL; ln = ln->next)
|
||||
if (ln->datum == datum)
|
||||
return ln;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -213,32 +205,32 @@ Lst_FindDatum(List *list, const void *datum)
|
||||
int
|
||||
Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
|
||||
{
|
||||
ListNode *node;
|
||||
ListNode *ln;
|
||||
int result = 0;
|
||||
|
||||
for (node = list->first; node != NULL; node = node->next) {
|
||||
result = proc(node->datum, procData);
|
||||
for (ln = list->first; ln != NULL; ln = ln->next) {
|
||||
result = proc(ln->datum, procData);
|
||||
if (result != 0)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Move all nodes from list2 to the end of list1.
|
||||
* List2 is destroyed and freed. */
|
||||
/* Move all nodes from src to the end of dst.
|
||||
* The source list is destroyed and freed. */
|
||||
void
|
||||
Lst_MoveAll(List *list1, List *list2)
|
||||
Lst_MoveAll(List *dst, List *src)
|
||||
{
|
||||
if (list2->first != NULL) {
|
||||
list2->first->prev = list1->last;
|
||||
if (list1->last != NULL)
|
||||
list1->last->next = list2->first;
|
||||
if (src->first != NULL) {
|
||||
src->first->prev = dst->last;
|
||||
if (dst->last != NULL)
|
||||
dst->last->next = src->first;
|
||||
else
|
||||
list1->first = list2->first;
|
||||
dst->first = src->first;
|
||||
|
||||
list1->last = list2->last;
|
||||
dst->last = src->last;
|
||||
}
|
||||
free(list2);
|
||||
free(src);
|
||||
}
|
||||
|
||||
/* Copy the element data from src to the start of dst. */
|
||||
|
6
lst.h
6
lst.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.h,v 1.84 2020/10/28 02:43:16 rillig Exp $ */
|
||||
/* $NetBSD: lst.h,v 1.85 2020/11/10 00:32:12 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -118,7 +118,7 @@ void Lst_Destroy(List *, LstFreeProc);
|
||||
|
||||
/* Get information about a list */
|
||||
|
||||
static inline MAKE_ATTR_UNUSED Boolean
|
||||
MAKE_INLINE Boolean
|
||||
Lst_IsEmpty(List *list) { return list->first == NULL; }
|
||||
|
||||
/* Find the first node that contains the given datum, or NULL. */
|
||||
@ -173,7 +173,7 @@ void Vector_Init(Vector *, size_t);
|
||||
|
||||
/* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
static inline MAKE_ATTR_UNUSED void *
|
||||
MAKE_INLINE void *
|
||||
Vector_Get(Vector *v, size_t i)
|
||||
{
|
||||
unsigned char *items = v->items;
|
||||
|
@ -16,7 +16,8 @@ CFLAGS="@CFLAGS@ -I. -I${srcdir} @DEFS@ @CPPFLAGS@ -DMAKE_NATIVE ${XDEFS} -DBMAK
|
||||
MAKE_VERSION=@_MAKE_VERSION@
|
||||
|
||||
MDEFS="-DMAKE_VERSION=\"$MAKE_VERSION\" \
|
||||
-D@force_machine@MACHINE=\"@machine@\" -DMACHINE_ARCH=\"@machine_arch@\" \
|
||||
-D@force_machine@MACHINE=\"@machine@\" \
|
||||
-D@force_machine_arch@MACHINE_ARCH=\"@machine_arch@\" \
|
||||
-D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\""
|
||||
|
||||
|
||||
@ -59,7 +60,7 @@ do_link() {
|
||||
}
|
||||
|
||||
BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \
|
||||
lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
|
||||
lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o \
|
||||
suff.o targ.o trace.o var.o util.o"
|
||||
|
||||
LIB_OBJECTS="@LIBOBJS@"
|
||||
|
19
make.1
19
make.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig 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 November 1, 2020
|
||||
.Dd November 14, 2020
|
||||
.Dt MAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -37,7 +37,7 @@
|
||||
.Nd maintain program dependencies
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl BeikNnqrstWwX
|
||||
.Op Fl BeikNnqrSstWwX
|
||||
.Op Fl C Ar directory
|
||||
.Op Fl D Ar variable
|
||||
.Op Fl d Ar flags
|
||||
@ -329,6 +329,10 @@ Do not execute any commands, but exit 0 if the specified targets are
|
||||
up-to-date and 1, otherwise.
|
||||
.It Fl r
|
||||
Do not use the built-in rules specified in the system makefile.
|
||||
.It Fl S
|
||||
Stop processing if an error is encountered.
|
||||
This is the default behavior and the opposite of
|
||||
.Fl k .
|
||||
.It Fl s
|
||||
Do not echo any commands as they are executed.
|
||||
Equivalent to specifying
|
||||
@ -1090,6 +1094,15 @@ to the specified directory if it exists, and set
|
||||
and
|
||||
.Ql Ev PWD
|
||||
to that directory before executing any targets.
|
||||
.Pp
|
||||
Except in the case of an explicit
|
||||
.Ql Ic .OBJDIR
|
||||
target,
|
||||
.Nm
|
||||
will check that the specified directory is writable and ignore it if not.
|
||||
This check can be skipped by setting the environment variable
|
||||
.Ql Ev MAKE_OBJDIR_CHECK_WRITABLE
|
||||
to "no".
|
||||
.
|
||||
.It Va .PARSEDIR
|
||||
A path to the directory of the current
|
||||
|
345
make.c
345
make.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.c,v 1.186 2020/11/01 17:47:26 rillig Exp $ */
|
||||
/* $NetBSD: make.c,v 1.209 2020/11/16 22:31:42 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,33 +68,28 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* make.c --
|
||||
* The functions which perform the examination of targets and
|
||||
* their suitability for creation
|
||||
/* Examination of targets and their suitability for creation.
|
||||
*
|
||||
* Interface:
|
||||
* Make_Run Initialize things for the module and recreate
|
||||
* whatever needs recreating. Returns TRUE if
|
||||
* work was (or would have been) done and FALSE
|
||||
* otherwise.
|
||||
* Make_Run Initialize things for the module. Returns TRUE if
|
||||
* work was (or would have been) done.
|
||||
*
|
||||
* Make_Update Update all parents of a given child. Performs
|
||||
* various bookkeeping chores like the updating
|
||||
* Make_Update After a target is made, update all its parents.
|
||||
* Perform various bookkeeping chores like the updating
|
||||
* of the youngestChild field of the parent, filling
|
||||
* of the IMPSRC context variable, etc. It will
|
||||
* place the parent on the toBeMade queue if it
|
||||
* should be.
|
||||
* of the IMPSRC context variable, etc. Place the parent
|
||||
* on the toBeMade queue if it should be.
|
||||
*
|
||||
* Make_TimeStamp Function to set the parent's youngestChild field
|
||||
* based on a child's modification time.
|
||||
* GNode_UpdateYoungestChild
|
||||
* Update the node's youngestChild field based on the
|
||||
* child's modification time.
|
||||
*
|
||||
* Make_DoAllVar Set up the various local variables for a
|
||||
* target, including the .ALLSRC variable, making
|
||||
* sure that any variable that needs to exist
|
||||
* at the very least has the empty value.
|
||||
*
|
||||
* Make_OODate Determine if a target is out-of-date.
|
||||
* GNode_IsOODate Determine if a target is out-of-date.
|
||||
*
|
||||
* Make_HandleUse See if a child is a .USE node for a parent
|
||||
* and perform the .USE actions if so.
|
||||
@ -107,17 +102,16 @@
|
||||
#include "job.h"
|
||||
|
||||
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: make.c,v 1.186 2020/11/01 17:47:26 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: make.c,v 1.209 2020/11/16 22:31:42 rillig Exp $");
|
||||
|
||||
/* Sequence # to detect recursion. */
|
||||
static unsigned int checked = 1;
|
||||
static unsigned int checked_seqno = 1;
|
||||
|
||||
/* The current fringe of the graph.
|
||||
* These are nodes which await examination by MakeOODate.
|
||||
* It is added to by Make_Update and subtracted from by MakeStartJobs */
|
||||
static GNodeList *toBeMade;
|
||||
|
||||
static int MakeCheckOrder(void *, void *);
|
||||
static int MakeBuildParent(void *, void *);
|
||||
|
||||
void
|
||||
@ -185,11 +179,37 @@ GNode_ShouldExecute(GNode *gn)
|
||||
|
||||
/* Update the youngest child of the node, according to the given child. */
|
||||
void
|
||||
Make_TimeStamp(GNode *pgn, GNode *cgn)
|
||||
GNode_UpdateYoungestChild(GNode *gn, GNode *cgn)
|
||||
{
|
||||
if (pgn->youngestChild == NULL || cgn->mtime > pgn->youngestChild->mtime) {
|
||||
pgn->youngestChild = cgn;
|
||||
if (gn->youngestChild == NULL || cgn->mtime > gn->youngestChild->mtime)
|
||||
gn->youngestChild = cgn;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
IsOODateRegular(GNode *gn)
|
||||
{
|
||||
/* These rules are inherited from the original Make. */
|
||||
|
||||
if (gn->youngestChild != NULL) {
|
||||
if (gn->mtime < gn->youngestChild->mtime) {
|
||||
DEBUG1(MAKE, "modified before source \"%s\"...",
|
||||
GNode_Path(gn->youngestChild));
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) {
|
||||
DEBUG0(MAKE, "non-existent and no sources...");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (gn->type & OP_DOUBLEDEP) {
|
||||
DEBUG0(MAKE, ":: operator and no sources...");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* See if the node is out of date with respect to its sources.
|
||||
@ -204,7 +224,7 @@ Make_TimeStamp(GNode *pgn, GNode *cgn)
|
||||
* may be changed.
|
||||
*/
|
||||
Boolean
|
||||
Make_OODate(GNode *gn)
|
||||
GNode_IsOODate(GNode *gn)
|
||||
{
|
||||
Boolean oodate;
|
||||
|
||||
@ -212,14 +232,13 @@ Make_OODate(GNode *gn)
|
||||
* Certain types of targets needn't even be sought as their datedness
|
||||
* doesn't depend on their modification time...
|
||||
*/
|
||||
if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) {
|
||||
(void)Dir_MTime(gn, 1);
|
||||
if (!(gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
|
||||
Dir_UpdateMTime(gn, TRUE);
|
||||
if (DEBUG(MAKE)) {
|
||||
if (gn->mtime != 0) {
|
||||
if (gn->mtime != 0)
|
||||
debug_printf("modified %s...", Targ_FmtTime(gn->mtime));
|
||||
} else {
|
||||
else
|
||||
debug_printf("non-existent...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,8 +263,7 @@ Make_OODate(GNode *gn)
|
||||
*/
|
||||
DEBUG0(MAKE, ".USE node...");
|
||||
oodate = FALSE;
|
||||
} else if ((gn->type & OP_LIB) &&
|
||||
((gn->mtime==0) || Arch_IsLib(gn))) {
|
||||
} else if ((gn->type & OP_LIB) && (gn->mtime == 0 || Arch_IsLib(gn))) {
|
||||
DEBUG0(MAKE, "library...");
|
||||
|
||||
/*
|
||||
@ -261,7 +279,7 @@ Make_OODate(GNode *gn)
|
||||
*/
|
||||
DEBUG0(MAKE, ".JOIN node...");
|
||||
DEBUG1(MAKE, "source %smade...", gn->flags & CHILDMADE ? "" : "not ");
|
||||
oodate = (gn->flags & CHILDMADE) ? TRUE : FALSE;
|
||||
oodate = (gn->flags & CHILDMADE) != 0;
|
||||
} else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) {
|
||||
/*
|
||||
* A node which is the object of the force (!) operator or which has
|
||||
@ -277,30 +295,7 @@ Make_OODate(GNode *gn)
|
||||
}
|
||||
}
|
||||
oodate = TRUE;
|
||||
} else if ((gn->youngestChild != NULL &&
|
||||
gn->mtime < gn->youngestChild->mtime) ||
|
||||
(gn->youngestChild == NULL &&
|
||||
((gn->mtime == 0 && !(gn->type & OP_OPTIONAL))
|
||||
|| gn->type & OP_DOUBLEDEP)))
|
||||
{
|
||||
/*
|
||||
* A node whose modification time is less than that of its
|
||||
* youngest child or that has no children (youngestChild == NULL) and
|
||||
* either doesn't exist (mtime == 0) and it isn't optional
|
||||
* or was the object of a * :: operator is out-of-date.
|
||||
* Why? Because that's the way Make does it.
|
||||
*/
|
||||
if (DEBUG(MAKE)) {
|
||||
if (gn->youngestChild != NULL &&
|
||||
gn->mtime < gn->youngestChild->mtime) {
|
||||
debug_printf("modified before source %s...",
|
||||
GNode_Path(gn->youngestChild));
|
||||
} else if (gn->mtime == 0) {
|
||||
debug_printf("non-existent and no sources...");
|
||||
} else {
|
||||
debug_printf(":: operator and no sources...");
|
||||
}
|
||||
}
|
||||
} else if (IsOODateRegular(gn)) {
|
||||
oodate = TRUE;
|
||||
} else {
|
||||
/*
|
||||
@ -314,7 +309,7 @@ Make_OODate(GNode *gn)
|
||||
if (gn->flags & FORCE)
|
||||
debug_printf("non existing child...");
|
||||
}
|
||||
oodate = (gn->flags & FORCE) ? TRUE : FALSE;
|
||||
oodate = (gn->flags & FORCE) != 0;
|
||||
}
|
||||
|
||||
#ifdef USE_META
|
||||
@ -333,46 +328,24 @@ Make_OODate(GNode *gn)
|
||||
if (!oodate) {
|
||||
GNodeListNode *ln;
|
||||
for (ln = gn->parents->first; ln != NULL; ln = ln->next)
|
||||
Make_TimeStamp(ln->datum, gn);
|
||||
GNode_UpdateYoungestChild(ln->datum, gn);
|
||||
}
|
||||
|
||||
return oodate;
|
||||
}
|
||||
|
||||
/* Add the node to the list if it needs to be examined. */
|
||||
static int
|
||||
MakeAddChild(void *gnp, void *lp)
|
||||
static void
|
||||
PretendAllChildrenAreMade(GNode *pgn)
|
||||
{
|
||||
GNode *gn = gnp;
|
||||
GNodeList *l = lp;
|
||||
GNodeListNode *ln;
|
||||
|
||||
if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) {
|
||||
DEBUG2(MAKE, "MakeAddChild: need to examine %s%s\n",
|
||||
gn->name, gn->cohort_num);
|
||||
Lst_Enqueue(l, gn);
|
||||
for (ln = pgn->children->first; ln != NULL; ln = ln->next) {
|
||||
GNode *cgn = ln->datum;
|
||||
|
||||
Dir_UpdateMTime(cgn, FALSE); /* cgn->path may get updated as well */
|
||||
GNode_UpdateYoungestChild(pgn, cgn);
|
||||
pgn->unmade--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the pathname of a child that was already made.
|
||||
*
|
||||
* The path and mtime of the node and the youngestChild of the parent are
|
||||
* updated; the unmade children count of the parent is decremented.
|
||||
*
|
||||
* Input:
|
||||
* gnp the node to find
|
||||
*/
|
||||
static int
|
||||
MakeFindChild(void *gnp, void *pgnp)
|
||||
{
|
||||
GNode *gn = gnp;
|
||||
GNode *pgn = pgnp;
|
||||
|
||||
(void)Dir_MTime(gn, 0);
|
||||
Make_TimeStamp(pgn, gn);
|
||||
pgn->unmade--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called by Make_Run and SuffApplyTransform on the downward pass to handle
|
||||
@ -394,9 +367,9 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
|
||||
GNodeListNode *ln; /* An element in the children list */
|
||||
|
||||
#ifdef DEBUG_SRC
|
||||
if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) {
|
||||
if (!(cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM))) {
|
||||
debug_printf("Make_HandleUse: called for plain node %s\n", cgn->name);
|
||||
return;
|
||||
return; /* XXX: debug mode should not affect control flow */
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -457,10 +430,10 @@ MakeHandleUse(GNode *cgn, GNode *pgn, GNodeListNode *ln)
|
||||
{
|
||||
Boolean unmarked;
|
||||
|
||||
unmarked = ((cgn->type & OP_MARK) == 0);
|
||||
unmarked = !(cgn->type & OP_MARK);
|
||||
cgn->type |= OP_MARK;
|
||||
|
||||
if ((cgn->type & (OP_USE|OP_USEBEFORE)) == 0)
|
||||
if (!(cgn->type & (OP_USE|OP_USEBEFORE)))
|
||||
return;
|
||||
|
||||
if (unmarked)
|
||||
@ -493,7 +466,10 @@ HandleUseNodes(GNode *gn)
|
||||
time_t
|
||||
Make_Recheck(GNode *gn)
|
||||
{
|
||||
time_t mtime = Dir_MTime(gn, 1);
|
||||
time_t mtime;
|
||||
|
||||
Dir_UpdateMTime(gn, TRUE);
|
||||
mtime = gn->mtime;
|
||||
|
||||
#ifndef RECHECK
|
||||
/*
|
||||
@ -512,13 +488,11 @@ Make_Recheck(GNode *gn)
|
||||
* In this case, if the definitions produced by yacc haven't changed
|
||||
* from before, parse.h won't have been updated and gn->mtime will
|
||||
* reflect the current modification time for parse.h. This is
|
||||
* something of a kludge, I admit, but it's a useful one..
|
||||
* XXX: People like to use a rule like
|
||||
* something of a kludge, I admit, but it's a useful one.
|
||||
*
|
||||
* FRC:
|
||||
*
|
||||
* To force things that depend on FRC to be made, so we have to
|
||||
* check for gn->children being empty as well...
|
||||
* XXX: People like to use a rule like "FRC:" to force things that
|
||||
* 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)) {
|
||||
gn->mtime = now;
|
||||
@ -535,7 +509,7 @@ Make_Recheck(GNode *gn)
|
||||
* using the same file from a common server), there are times
|
||||
* when the modification time of a file created on a remote
|
||||
* machine will not be modified before the local stat() implied by
|
||||
* the Dir_MTime occurs, thus leading us to believe that the file
|
||||
* the Dir_UpdateMTime occurs, thus leading us to believe that the file
|
||||
* is unchanged, wreaking havoc with files that depend on this one.
|
||||
*
|
||||
* I have decided it is better to make too much than to make too
|
||||
@ -543,8 +517,8 @@ Make_Recheck(GNode *gn)
|
||||
* -- ardeb 1/12/88
|
||||
*/
|
||||
/*
|
||||
* Christos, 4/9/92: If we are saving commands pretend that
|
||||
* the target is made now. Otherwise archives with ... rules
|
||||
* Christos, 4/9/92: If we are saving commands, pretend that
|
||||
* the target is made now. Otherwise archives with '...' rules
|
||||
* don't work!
|
||||
*/
|
||||
if (!GNode_ShouldExecute(gn) || (gn->type & OP_SAVE_CMDS) ||
|
||||
@ -552,12 +526,14 @@ Make_Recheck(GNode *gn)
|
||||
DEBUG2(MAKE, " recheck(%s): update time from %s to now\n",
|
||||
gn->name, Targ_FmtTime(gn->mtime));
|
||||
gn->mtime = now;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
DEBUG2(MAKE, " recheck(%s): current update time: %s\n",
|
||||
gn->name, Targ_FmtTime(gn->mtime));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: The returned mtime may differ from gn->mtime.
|
||||
* Intentionally? */
|
||||
return mtime;
|
||||
}
|
||||
|
||||
@ -581,6 +557,25 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname)
|
||||
}
|
||||
}
|
||||
|
||||
/* See if a .ORDER rule stops us from building this node. */
|
||||
static Boolean
|
||||
IsWaitingForOrder(GNode *gn)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = gn->order_pred->first; ln != NULL; ln = ln->next) {
|
||||
GNode *ogn = ln->datum;
|
||||
|
||||
if (ogn->made >= MADE || !(ogn->flags & REMAKE))
|
||||
continue;
|
||||
|
||||
DEBUG2(MAKE, "IsWaitingForOrder: Waiting for .ORDER node \"%s%s\"\n",
|
||||
ogn->name, ogn->cohort_num);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Perform update on the parents of a node. Used by JobFinish once
|
||||
* a node has been dealt with and by MakeStartJobs if it finds an
|
||||
* up-to-date node.
|
||||
@ -610,7 +605,7 @@ Make_Update(GNode *cgn)
|
||||
GNode *centurion;
|
||||
|
||||
/* It is save to re-examine any nodes again */
|
||||
checked++;
|
||||
checked_seqno++;
|
||||
|
||||
cname = GNode_VarTarget(cgn);
|
||||
|
||||
@ -647,11 +642,11 @@ Make_Update(GNode *cgn)
|
||||
for (ln = parents->first; ln != NULL; ln = ln->next) {
|
||||
GNode *pgn = ln->datum;
|
||||
|
||||
if (DEBUG(MAKE))
|
||||
debug_printf("inspect parent %s%s: flags %x, "
|
||||
"type %x, made %d, unmade %d ",
|
||||
pgn->name, pgn->cohort_num, pgn->flags,
|
||||
pgn->type, pgn->made, pgn->unmade - 1);
|
||||
if (DEBUG(MAKE)) {
|
||||
debug_printf("inspect parent %s%s: ", pgn->name, pgn->cohort_num);
|
||||
GNode_FprintDetails(opts.debug_file, "", pgn, "");
|
||||
debug_printf(", unmade %d ", pgn->unmade - 1);
|
||||
}
|
||||
|
||||
if (!(pgn->flags & REMAKE)) {
|
||||
/* This parent isn't needed */
|
||||
@ -674,10 +669,10 @@ Make_Update(GNode *cgn)
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) {
|
||||
if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE))) {
|
||||
if (cgn->made == MADE)
|
||||
pgn->flags |= CHILDMADE;
|
||||
(void)Make_TimeStamp(pgn, cgn);
|
||||
GNode_UpdateYoungestChild(pgn, cgn);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -716,11 +711,10 @@ Make_Update(GNode *cgn)
|
||||
DEBUG0(MAKE, "- not deferred\n");
|
||||
continue;
|
||||
}
|
||||
assert(pgn->order_pred != NULL);
|
||||
if (Lst_ForEachUntil(pgn->order_pred, MakeCheckOrder, 0)) {
|
||||
/* A .ORDER rule stops us building this */
|
||||
|
||||
if (IsWaitingForOrder(pgn))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DEBUG(MAKE)) {
|
||||
debug_printf("- %s%s made, schedule %s%s (made %d)\n",
|
||||
cgn->name, cgn->cohort_num,
|
||||
@ -771,7 +765,7 @@ MakeAddAllSrc(GNode *cgn, GNode *pgn)
|
||||
return;
|
||||
cgn->type |= OP_MARK;
|
||||
|
||||
if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) {
|
||||
if (!(cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE))) {
|
||||
const char *child, *allsrc;
|
||||
|
||||
if (cgn->type & OP_ARCHV)
|
||||
@ -798,9 +792,9 @@ MakeAddAllSrc(GNode *cgn, GNode *pgn)
|
||||
* the start of the make. This is to keep pmake from getting
|
||||
* confused if something else updates the parent after the
|
||||
* make starts (shouldn't happen, I know, but sometimes it
|
||||
* does). In such a case, if we've updated the kid, the parent
|
||||
* does). In such a case, if we've updated the child, the parent
|
||||
* is likely to have a modification time later than that of
|
||||
* the kid and anything that relies on the OODATE variable will
|
||||
* the child and anything that relies on the OODATE variable will
|
||||
* be hosed.
|
||||
*
|
||||
* XXX: This will cause all made children to go in the OODATE
|
||||
@ -838,44 +832,31 @@ Make_DoAllVar(GNode *gn)
|
||||
for (ln = gn->children->first; ln != NULL; ln = ln->next)
|
||||
MakeAddAllSrc(ln->datum, gn);
|
||||
|
||||
if (!Var_Exists(OODATE, gn)) {
|
||||
if (!Var_Exists(OODATE, gn))
|
||||
Var_Set(OODATE, "", gn);
|
||||
}
|
||||
if (!Var_Exists(ALLSRC, gn)) {
|
||||
if (!Var_Exists(ALLSRC, gn))
|
||||
Var_Set(ALLSRC, "", gn);
|
||||
}
|
||||
|
||||
if (gn->type & OP_JOIN)
|
||||
Var_Set(TARGET, GNode_VarAllsrc(gn), gn);
|
||||
gn->flags |= DONE_ALLSRC;
|
||||
}
|
||||
|
||||
static int
|
||||
MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED)
|
||||
{
|
||||
GNode *bn = v_bn;
|
||||
|
||||
if (bn->made >= MADE || !(bn->flags & REMAKE))
|
||||
return 0;
|
||||
|
||||
DEBUG2(MAKE, "MakeCheckOrder: Waiting for .ORDER node %s%s\n",
|
||||
bn->name, bn->cohort_num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
MakeBuildChild(void *v_cn, void *toBeMade_next)
|
||||
{
|
||||
GNode *cn = v_cn;
|
||||
|
||||
DEBUG4(MAKE, "MakeBuildChild: inspect %s%s, made %d, type %x\n",
|
||||
cn->name, cn->cohort_num, cn->made, cn->type);
|
||||
if (DEBUG(MAKE)) {
|
||||
debug_printf("MakeBuildChild: inspect %s%s, ",
|
||||
cn->name, cn->cohort_num);
|
||||
GNode_FprintDetails(opts.debug_file, "", cn, "\n");
|
||||
}
|
||||
if (cn->made > DEFERRED)
|
||||
return 0;
|
||||
|
||||
/* If this node is on the RHS of a .ORDER, check LHSs. */
|
||||
assert(cn->order_pred);
|
||||
if (Lst_ForEachUntil(cn->order_pred, MakeCheckOrder, 0)) {
|
||||
if (IsWaitingForOrder(cn)) {
|
||||
/* Can't build this (or anything else in this child list) yet */
|
||||
cn->made = DEFERRED;
|
||||
return 0; /* but keep looking */
|
||||
@ -899,7 +880,7 @@ MakeBuildChild(void *v_cn, void *toBeMade_next)
|
||||
return cn->type & OP_WAIT && cn->unmade > 0;
|
||||
}
|
||||
|
||||
/* When a .ORDER LHS node completes we do this on each RHS */
|
||||
/* When a .ORDER LHS node completes, we do this on each RHS. */
|
||||
static int
|
||||
MakeBuildParent(void *v_pn, void *toBeMade_next)
|
||||
{
|
||||
@ -918,21 +899,21 @@ MakeBuildParent(void *v_pn, void *toBeMade_next)
|
||||
|
||||
/* Start as many jobs as possible, taking them from the toBeMade queue.
|
||||
*
|
||||
* If the query flag was given to pmake, no job will be started,
|
||||
* If the -q option was given, no job will be started,
|
||||
* but as soon as an out-of-date target is found, this function
|
||||
* returns TRUE. At all other times, this function returns FALSE.
|
||||
* returns TRUE. In all other cases, this function returns FALSE.
|
||||
*/
|
||||
static Boolean
|
||||
MakeStartJobs(void)
|
||||
{
|
||||
GNode *gn;
|
||||
int have_token = 0;
|
||||
GNode *gn;
|
||||
Boolean have_token = FALSE;
|
||||
|
||||
while (!Lst_IsEmpty(toBeMade)) {
|
||||
/* Get token now to avoid cycling job-list when we only have 1 token */
|
||||
if (!have_token && !Job_TokenWithdraw())
|
||||
break;
|
||||
have_token = 1;
|
||||
have_token = TRUE;
|
||||
|
||||
gn = Lst_Dequeue(toBeMade);
|
||||
DEBUG2(MAKE, "Examining %s%s...\n", gn->name, gn->cohort_num);
|
||||
@ -943,13 +924,13 @@ MakeStartJobs(void)
|
||||
make_abort(gn, __LINE__);
|
||||
}
|
||||
|
||||
if (gn->checked_seqno == checked) {
|
||||
if (gn->checked_seqno == checked_seqno) {
|
||||
/* We've already looked at this node since a job finished... */
|
||||
DEBUG2(MAKE, "already checked %s%s\n", gn->name, gn->cohort_num);
|
||||
gn->made = DEFERRED;
|
||||
continue;
|
||||
}
|
||||
gn->checked_seqno = checked;
|
||||
gn->checked_seqno = checked_seqno;
|
||||
|
||||
if (gn->unmade != 0) {
|
||||
/*
|
||||
@ -964,14 +945,13 @@ MakeStartJobs(void)
|
||||
}
|
||||
|
||||
gn->made = BEINGMADE;
|
||||
if (Make_OODate(gn)) {
|
||||
if (GNode_IsOODate(gn)) {
|
||||
DEBUG0(MAKE, "out-of-date\n");
|
||||
if (opts.queryFlag) {
|
||||
if (opts.queryFlag)
|
||||
return TRUE;
|
||||
}
|
||||
Make_DoAllVar(gn);
|
||||
Job_Make(gn);
|
||||
have_token = 0;
|
||||
have_token = FALSE;
|
||||
} else {
|
||||
DEBUG0(MAKE, "up-to-date\n");
|
||||
gn->made = UPTODATE;
|
||||
@ -994,6 +974,7 @@ MakeStartJobs(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Print the status of a .ORDER node. */
|
||||
static void
|
||||
MakePrintStatusOrderNode(GNode *ogn, GNode *gn)
|
||||
{
|
||||
@ -1074,7 +1055,7 @@ MakePrintStatus(GNode *gn, int *errors)
|
||||
* print out the cycle by recursing on its children.
|
||||
*/
|
||||
if (!(gn->flags & CYCLE)) {
|
||||
/* Fist time we've seen this node, check all children */
|
||||
/* First time we've seen this node, check all children */
|
||||
gn->flags |= CYCLE;
|
||||
MakePrintStatusList(gn->children, errors);
|
||||
/* Mark that this node needn't be processed again */
|
||||
@ -1103,6 +1084,25 @@ MakePrintStatusList(GNodeList *gnodes, int *errors)
|
||||
break;
|
||||
}
|
||||
|
||||
static void
|
||||
ExamineLater(GNodeList *examine, GNodeList *toBeExamined)
|
||||
{
|
||||
ListNode *ln;
|
||||
|
||||
for (ln = toBeExamined->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
|
||||
if (gn->flags & REMAKE)
|
||||
continue;
|
||||
if (gn->type & (OP_USE | OP_USEBEFORE))
|
||||
continue;
|
||||
|
||||
DEBUG2(MAKE, "ExamineLater: need to examine \"%s%s\"\n",
|
||||
gn->name, gn->cohort_num);
|
||||
Lst_Enqueue(examine, gn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand .USE nodes and create a new targets list.
|
||||
*
|
||||
* Input:
|
||||
@ -1111,17 +1111,8 @@ MakePrintStatusList(GNodeList *gnodes, int *errors)
|
||||
void
|
||||
Make_ExpandUse(GNodeList *targs)
|
||||
{
|
||||
GNodeList *examine; /* List of targets to examine */
|
||||
|
||||
{
|
||||
/* XXX: Why is it necessary to copy the list? There shouldn't be
|
||||
* any modifications to the list, at least the function name
|
||||
* ExpandUse doesn't suggest that. */
|
||||
GNodeListNode *ln;
|
||||
examine = Lst_New();
|
||||
for (ln = targs->first; ln != NULL; ln = ln->next)
|
||||
Lst_Append(examine, ln->datum);
|
||||
}
|
||||
GNodeList *examine = Lst_New(); /* Queue of targets to examine */
|
||||
Lst_AppendAll(examine, targs);
|
||||
|
||||
/*
|
||||
* Make an initial downward pass over the graph, marking nodes to be made
|
||||
@ -1151,9 +1142,8 @@ Make_ExpandUse(GNodeList *targs)
|
||||
* expansions.
|
||||
*/
|
||||
if (gn->type & OP_ARCHV) {
|
||||
char *eoa, *eon;
|
||||
eoa = strchr(gn->name, '(');
|
||||
eon = strchr(gn->name, ')');
|
||||
char *eoa = strchr(gn->name, '(');
|
||||
char *eon = strchr(gn->name, ')');
|
||||
if (eoa == NULL || eon == NULL)
|
||||
continue;
|
||||
*eoa = '\0';
|
||||
@ -1164,23 +1154,22 @@ Make_ExpandUse(GNodeList *targs)
|
||||
*eon = ')';
|
||||
}
|
||||
|
||||
(void)Dir_MTime(gn, 0);
|
||||
Dir_UpdateMTime(gn, FALSE);
|
||||
Var_Set(TARGET, GNode_Path(gn), gn);
|
||||
UnmarkChildren(gn);
|
||||
HandleUseNodes(gn);
|
||||
|
||||
if ((gn->type & OP_MADE) == 0)
|
||||
if (!(gn->type & OP_MADE))
|
||||
Suff_FindDeps(gn);
|
||||
else {
|
||||
/* Pretend we made all this node's children */
|
||||
Lst_ForEachUntil(gn->children, MakeFindChild, gn);
|
||||
PretendAllChildrenAreMade(gn);
|
||||
if (gn->unmade != 0)
|
||||
printf("Warning: %s%s still has %d unmade children\n",
|
||||
gn->name, gn->cohort_num, gn->unmade);
|
||||
printf("Warning: %s%s still has %d unmade children\n",
|
||||
gn->name, gn->cohort_num, gn->unmade);
|
||||
}
|
||||
|
||||
if (gn->unmade != 0)
|
||||
Lst_ForEachUntil(gn->children, MakeAddChild, examine);
|
||||
ExamineLater(examine, gn->children);
|
||||
}
|
||||
|
||||
Lst_Free(examine);
|
||||
@ -1218,7 +1207,7 @@ Make_ProcessWait(GNodeList *targs)
|
||||
* Perhaps this should be done earlier...
|
||||
*/
|
||||
|
||||
pgn = Targ_NewGN(".MAIN");
|
||||
pgn = GNode_New(".MAIN");
|
||||
pgn->flags = REMAKE;
|
||||
pgn->type = OP_PHONY | OP_DEPENDS;
|
||||
/* Get it displayed in the diag dumps */
|
||||
@ -1353,9 +1342,9 @@ Make_Run(GNodeList *targs)
|
||||
MakePrintStatusList(targs, &errors);
|
||||
if (DEBUG(MAKE)) {
|
||||
debug_printf("done: errors %d\n", errors);
|
||||
if (errors)
|
||||
if (errors > 0)
|
||||
Targ_PrintGraph(4);
|
||||
}
|
||||
}
|
||||
return errors != 0;
|
||||
return errors > 0;
|
||||
}
|
||||
|
272
make.h
272
make.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.h,v 1.179 2020/11/01 17:47:26 rillig Exp $ */
|
||||
/* $NetBSD: make.h,v 1.210 2020/11/16 21:53:10 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -135,6 +135,8 @@
|
||||
#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */
|
||||
#endif
|
||||
|
||||
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
|
||||
|
||||
/*
|
||||
* A boolean type is defined as an integer, not an enum, for historic reasons.
|
||||
* The only allowed values are the constants TRUE and FALSE (1 and 0).
|
||||
@ -187,7 +189,7 @@ typedef int Boolean;
|
||||
#define POSIX_SIGNALS
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
typedef enum GNodeMade {
|
||||
UNMADE, /* Not examined yet */
|
||||
DEFERRED, /* Examined once (building child) */
|
||||
REQUESTED, /* on toBeMade list */
|
||||
@ -207,6 +209,8 @@ typedef enum {
|
||||
*
|
||||
* Some of the OP_ constants can be combined, others cannot. */
|
||||
typedef enum GNodeType {
|
||||
OP_NONE = 0,
|
||||
|
||||
/* The dependency operator ':' is the most common one. The commands of
|
||||
* this node are executed if any child is out-of-date. */
|
||||
OP_DEPENDS = 1 << 0,
|
||||
@ -215,7 +219,8 @@ typedef enum GNodeType {
|
||||
OP_FORCE = 1 << 1,
|
||||
/* The dependency operator '::' behaves like ':', except that it allows
|
||||
* multiple dependency groups to be defined. Each of these groups is
|
||||
* executed on its own, independently from the others. */
|
||||
* executed on its own, independently from the others. Each individual
|
||||
* dependency group is called a cohort. */
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
@ -246,7 +251,7 @@ typedef enum GNodeType {
|
||||
/* Like .USE, only prepend commands */
|
||||
OP_USEBEFORE = 1 << 13,
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in the
|
||||
* parents' local variables. */
|
||||
* parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
OP_INVISIBLE = 1 << 14,
|
||||
/* The node is exempt from normal 'main target' processing in parse.c */
|
||||
OP_NOTMAIN = 1 << 15,
|
||||
@ -254,7 +259,10 @@ typedef enum GNodeType {
|
||||
OP_PHONY = 1 << 16,
|
||||
/* Don't search for file in the path */
|
||||
OP_NOPATH = 1 << 17,
|
||||
/* .WAIT phony node */
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is made
|
||||
* first, including its children. Once that is finished, source2 is made,
|
||||
* including its children. The .WAIT keyword may appear more than once in
|
||||
* a single dependency declaration. */
|
||||
OP_WAIT = 1 << 18,
|
||||
/* .NOMETA do not create a .meta file */
|
||||
OP_NOMETA = 1 << 19,
|
||||
@ -267,7 +275,7 @@ typedef enum GNodeType {
|
||||
|
||||
/* Attributes applied by PMake */
|
||||
|
||||
/* The node is a transformation rule */
|
||||
/* The node is a transformation rule, such as ".c.o". */
|
||||
OP_TRANSFORM = 1 << 31,
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
@ -337,7 +345,7 @@ typedef struct GNode {
|
||||
int unmade; /* The number of unmade children */
|
||||
|
||||
/* The modification time; 0 means the node does not have a corresponding
|
||||
* file; see Make_OODate. */
|
||||
* file; see GNode_IsOODate. */
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
|
||||
@ -346,9 +354,6 @@ typedef struct GNode {
|
||||
* file.c has the node for file.o in this list. */
|
||||
GNodeList *implicitParents;
|
||||
|
||||
/* Other nodes of the same name, for the '::' operator. */
|
||||
GNodeList *cohorts;
|
||||
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
GNodeList *parents;
|
||||
@ -364,6 +369,8 @@ typedef struct GNode {
|
||||
* in the normal sense. */
|
||||
GNodeList *order_succ;
|
||||
|
||||
/* Other nodes of the same name, for the '::' dependency operator. */
|
||||
GNodeList *cohorts;
|
||||
/* The "#n" suffix for this cohort, or "" for other nodes */
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
@ -389,20 +396,20 @@ typedef struct GNode {
|
||||
* but the Suff module) */
|
||||
struct Suff *suffix;
|
||||
|
||||
/* filename where the GNode got defined */
|
||||
/* Filename where the GNode got defined */
|
||||
/* XXX: What is the lifetime of this string? */
|
||||
const char *fname;
|
||||
/* line number where the GNode got defined */
|
||||
/* Line number where the GNode got defined */
|
||||
int lineno;
|
||||
} GNode;
|
||||
|
||||
/*
|
||||
* Error levels for parsing. PARSE_FATAL means the process cannot continue
|
||||
* once the top-level makefile has been parsed. PARSE_WARNING and PARSE_INFO
|
||||
* mean it can.
|
||||
*/
|
||||
/* Error levels for diagnostics during parsing. */
|
||||
typedef enum ParseErrorLevel {
|
||||
/* Exit when the current top-level makefile has been parsed completely. */
|
||||
PARSE_FATAL = 1,
|
||||
/* Print "warning"; may be upgraded to fatal by the -w option. */
|
||||
PARSE_WARNING,
|
||||
/* Informational, mainly used during development of makefiles. */
|
||||
PARSE_INFO
|
||||
} ParseErrorLevel;
|
||||
|
||||
@ -415,9 +422,7 @@ typedef enum CondEvalResult {
|
||||
COND_INVALID /* Not a conditional statement */
|
||||
} CondEvalResult;
|
||||
|
||||
/*
|
||||
* Definitions for the "local" variables. Used only for clarity.
|
||||
*/
|
||||
/* Names of the variables that are "local" to a specific target. */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
@ -426,58 +431,77 @@ typedef enum CondEvalResult {
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
|
||||
#define FTARGET "@F" /* file part of TARGET */
|
||||
#define DTARGET "@D" /* directory part of TARGET */
|
||||
#define FIMPSRC "<F" /* file part of IMPSRC */
|
||||
#define DIMPSRC "<D" /* directory part of IMPSRC */
|
||||
#define FPREFIX "*F" /* file part of PREFIX */
|
||||
#define DPREFIX "*D" /* directory part of PREFIX */
|
||||
|
||||
/*
|
||||
* Global Variables
|
||||
*/
|
||||
|
||||
/* True if every target is precious */
|
||||
extern Boolean allPrecious;
|
||||
/* True if failed targets should be deleted */
|
||||
extern Boolean deleteOnError;
|
||||
/* TRUE while processing .depend */
|
||||
extern Boolean doing_depend;
|
||||
/* .DEFAULT rule */
|
||||
extern GNode *defaultNode;
|
||||
|
||||
/* Variables defined internally by make which should not override those set
|
||||
* by makefiles. */
|
||||
extern GNode *VAR_INTERNAL;
|
||||
/* Variables defined in a global context, e.g in the Makefile itself. */
|
||||
extern GNode *VAR_GLOBAL;
|
||||
/* Variables defined on the command line. */
|
||||
extern GNode *VAR_CMDLINE;
|
||||
|
||||
/* Value returned by Var_Parse when an error is encountered. It actually
|
||||
* points to an empty string, so naive callers needn't worry about it. */
|
||||
extern char var_Error[];
|
||||
|
||||
/* The time at the start of this whole process */
|
||||
extern time_t now;
|
||||
|
||||
/*
|
||||
* If FALSE (the default behavior), undefined subexpressions in a variable
|
||||
* expression are discarded. If TRUE (only during variable assignments using
|
||||
* the ':=' assignment operator, no matter how deeply nested), they are
|
||||
* preserved and possibly expanded later when the variable from the
|
||||
* subexpression has been defined.
|
||||
*
|
||||
* Example for a ':=' assignment:
|
||||
* CFLAGS = $(.INCLUDES)
|
||||
* CFLAGS := -I.. $(CFLAGS)
|
||||
* # If .INCLUDES (an undocumented special variable, by the way) is
|
||||
* # still undefined, the updated CFLAGS becomes "-I.. $(.INCLUDES)".
|
||||
*/
|
||||
extern Boolean preserveUndefined;
|
||||
|
||||
/* The list of directories to search when looking for targets (set by the
|
||||
* special target .PATH). */
|
||||
extern SearchPath *dirSearchPath;
|
||||
/* The list of directories to search when
|
||||
* looking for targets */
|
||||
extern Boolean allPrecious; /* True if every target is precious */
|
||||
extern Boolean deleteOnError; /* True if failed targets should be deleted */
|
||||
extern Boolean doing_depend; /* TRUE if processing .depend */
|
||||
/* Used for .include "...". */
|
||||
extern SearchPath *parseIncPath;
|
||||
/* Used for .include <...>, for the built-in sys.mk and makefiles from the
|
||||
* command line arguments. */
|
||||
extern SearchPath *sysIncPath;
|
||||
/* The default for sysIncPath. */
|
||||
extern SearchPath *defSysIncPath;
|
||||
|
||||
extern GNode *DEFAULT; /* .DEFAULT rule */
|
||||
/* Startup directory */
|
||||
extern char curdir[];
|
||||
/* The basename of the program name, suffixed with [n] for sub-makes. */
|
||||
extern char *progname;
|
||||
/* Name of the .depend makefile */
|
||||
extern char *makeDependfile;
|
||||
/* If we replaced environ, this will be non-NULL. */
|
||||
extern char **savedEnv;
|
||||
|
||||
extern GNode *VAR_INTERNAL; /* Variables defined internally by make
|
||||
* which should not override those set by
|
||||
* makefiles.
|
||||
*/
|
||||
extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
|
||||
* in the Makefile itself */
|
||||
extern GNode *VAR_CMDLINE; /* Variables defined on the command line */
|
||||
extern char var_Error[]; /* Value returned by Var_Parse when an error
|
||||
* is encountered. It actually points to
|
||||
* an empty string, so naive callers needn't
|
||||
* worry about it. */
|
||||
|
||||
extern time_t now; /* The time at the start of this whole
|
||||
* process */
|
||||
|
||||
extern Boolean oldVars; /* Do old-style variable substitution */
|
||||
|
||||
extern SearchPath *sysIncPath; /* The system include path. */
|
||||
extern SearchPath *defSysIncPath; /* The default system include path. */
|
||||
|
||||
extern char curdir[]; /* Startup directory */
|
||||
extern char *progname; /* The program name */
|
||||
extern char *makeDependfile; /* .depend */
|
||||
extern char **savedEnv; /* if we replaced environ this will be non-NULL */
|
||||
|
||||
extern int makelevel;
|
||||
extern int makelevel;
|
||||
|
||||
/*
|
||||
* We cannot vfork() in a child of vfork().
|
||||
* Most systems do not enforce this but some do.
|
||||
*/
|
||||
#define vFork() ((getpid() == myPid) ? vfork() : fork())
|
||||
extern pid_t myPid;
|
||||
extern pid_t myPid;
|
||||
|
||||
#define MAKEFLAGS ".MAKEFLAGS"
|
||||
#define MAKEOVERRIDES ".MAKEOVERRIDES"
|
||||
@ -485,7 +509,7 @@ extern pid_t myPid;
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */
|
||||
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
|
||||
#define MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
|
||||
#define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
|
||||
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
|
||||
#define MAKE_MODE ".MAKE.MODE"
|
||||
#ifndef MAKE_LEVEL_ENV
|
||||
@ -493,29 +517,28 @@ extern pid_t myPid;
|
||||
#endif
|
||||
|
||||
typedef enum DebugFlags {
|
||||
DEBUG_NONE = 0,
|
||||
DEBUG_ARCH = 1 << 0,
|
||||
DEBUG_COND = 1 << 1,
|
||||
DEBUG_DIR = 1 << 2,
|
||||
DEBUG_GRAPH1 = 1 << 3,
|
||||
DEBUG_GRAPH2 = 1 << 4,
|
||||
DEBUG_JOB = 1 << 5,
|
||||
DEBUG_MAKE = 1 << 6,
|
||||
DEBUG_SUFF = 1 << 7,
|
||||
DEBUG_TARG = 1 << 8,
|
||||
DEBUG_VAR = 1 << 9,
|
||||
DEBUG_FOR = 1 << 10,
|
||||
DEBUG_SHELL = 1 << 11,
|
||||
DEBUG_ERROR = 1 << 12,
|
||||
DEBUG_LOUD = 1 << 13,
|
||||
DEBUG_META = 1 << 14,
|
||||
DEBUG_HASH = 1 << 15,
|
||||
|
||||
DEBUG_GRAPH3 = 1 << 16,
|
||||
DEBUG_SCRIPT = 1 << 17,
|
||||
DEBUG_PARSE = 1 << 18,
|
||||
DEBUG_CWD = 1 << 19,
|
||||
|
||||
DEBUG_LINT = 1 << 20
|
||||
DEBUG_CWD = 1 << 2,
|
||||
DEBUG_DIR = 1 << 3,
|
||||
DEBUG_ERROR = 1 << 4,
|
||||
DEBUG_FOR = 1 << 5,
|
||||
DEBUG_GRAPH1 = 1 << 6,
|
||||
DEBUG_GRAPH2 = 1 << 7,
|
||||
DEBUG_GRAPH3 = 1 << 8,
|
||||
DEBUG_HASH = 1 << 9,
|
||||
DEBUG_JOB = 1 << 10,
|
||||
DEBUG_LOUD = 1 << 11,
|
||||
DEBUG_MAKE = 1 << 12,
|
||||
DEBUG_META = 1 << 13,
|
||||
DEBUG_PARSE = 1 << 14,
|
||||
DEBUG_SCRIPT = 1 << 15,
|
||||
DEBUG_SHELL = 1 << 16,
|
||||
DEBUG_SUFF = 1 << 17,
|
||||
DEBUG_TARG = 1 << 18,
|
||||
DEBUG_VAR = 1 << 19,
|
||||
DEBUG_ALL = (1 << 20) - 1
|
||||
} DebugFlags;
|
||||
|
||||
#define CONCAT(a,b) a##b
|
||||
@ -549,8 +572,9 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
|
||||
|
||||
typedef enum PrintVarsMode {
|
||||
COMPAT_VARS = 1,
|
||||
EXPAND_VARS
|
||||
PVM_NONE,
|
||||
PVM_UNEXPANDED,
|
||||
PVM_EXPANDED
|
||||
} PrintVarsMode;
|
||||
|
||||
/* Command line options */
|
||||
@ -565,6 +589,12 @@ typedef struct CmdOpts {
|
||||
/* -df: debug output is written here - default stderr */
|
||||
FILE *debug_file;
|
||||
|
||||
/* -dL: lint mode
|
||||
*
|
||||
* Runs make in strict mode, with additional checks and better error
|
||||
* handling. */
|
||||
Boolean lint;
|
||||
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
Boolean debugVflag;
|
||||
|
||||
@ -630,49 +660,49 @@ extern CmdOpts opts;
|
||||
|
||||
#include "nonints.h"
|
||||
|
||||
void Make_TimeStamp(GNode *, GNode *);
|
||||
Boolean Make_OODate(GNode *);
|
||||
void GNode_UpdateYoungestChild(GNode *, GNode *);
|
||||
Boolean GNode_IsOODate(GNode *);
|
||||
void Make_ExpandUse(GNodeList *);
|
||||
time_t Make_Recheck(GNode *);
|
||||
void Make_HandleUse(GNode *, GNode *);
|
||||
void Make_Update(GNode *);
|
||||
void Make_DoAllVar(GNode *);
|
||||
Boolean Make_Run(GNodeList *);
|
||||
int dieQuietly(GNode *, int);
|
||||
Boolean shouldDieQuietly(GNode *, int);
|
||||
void PrintOnError(GNode *, const char *);
|
||||
void Main_ExportMAKEFLAGS(Boolean);
|
||||
Boolean Main_SetObjdir(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
int mkTempFile(const char *, char **);
|
||||
int str2Lst_Append(StringList *, char *, const char *);
|
||||
int str2Lst_Append(StringList *, char *);
|
||||
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
|
||||
Boolean GNode_ShouldExecute(GNode *gn);
|
||||
|
||||
/* See if the node was seen on the left-hand side of a dependency operator. */
|
||||
static MAKE_ATTR_UNUSED Boolean
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsTarget(const GNode *gn)
|
||||
{
|
||||
return (gn->type & OP_OPMASK) != 0;
|
||||
}
|
||||
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_Path(const GNode *gn)
|
||||
{
|
||||
return gn->path != NULL ? gn->path : gn->name;
|
||||
}
|
||||
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarAllsrc(GNode *gn) { return Var_ValueDirect(ALLSRC, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarPrefix(GNode *gn) { return Var_ValueDirect(PREFIX, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
|
||||
|
||||
#ifdef __GNUC__
|
||||
@ -703,35 +733,49 @@ GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
|
||||
#define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
#endif
|
||||
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isalnum(char ch)
|
||||
{ return isalnum((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isalpha(char ch)
|
||||
{ return isalpha((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isdigit(char ch)
|
||||
{ return isdigit((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isspace(char ch)
|
||||
{ return isspace((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isupper(char ch)
|
||||
{ return isupper((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED char ch_tolower(char ch)
|
||||
{ return (char)tolower((unsigned char)ch); }
|
||||
static inline MAKE_ATTR_UNUSED char ch_toupper(char ch)
|
||||
{ return (char)toupper((unsigned char)ch); }
|
||||
MAKE_INLINE Boolean
|
||||
ch_isalnum(char ch) { return isalnum((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE Boolean
|
||||
ch_isalpha(char ch) { return isalpha((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE Boolean
|
||||
ch_isdigit(char ch) { return isdigit((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE Boolean
|
||||
ch_isspace(char ch) { return isspace((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE Boolean
|
||||
ch_isupper(char ch) { return isupper((unsigned char)ch) != 0; }
|
||||
MAKE_INLINE char
|
||||
ch_tolower(char ch) { return (char)tolower((unsigned char)ch); }
|
||||
MAKE_INLINE char
|
||||
ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
cpp_skip_whitespace(const char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
cpp_skip_hspace(const char **pp)
|
||||
{
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
pp_skip_whitespace(char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
pp_skip_hspace(char **pp)
|
||||
{
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
#ifdef MAKE_NATIVE
|
||||
# include <sys/cdefs.h>
|
||||
# ifndef lint
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.h,v 1.12 2020/10/19 23:43:55 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.h,v 1.13 2020/11/10 00:32:12 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -46,7 +46,7 @@ char *bmake_strsedup(const char *, const char *);
|
||||
*
|
||||
* The case of a NULL pointer happens especially often after Var_Value,
|
||||
* since only environment variables need to be freed, but not others. */
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
bmake_free(void *p)
|
||||
{
|
||||
if (p != NULL)
|
||||
|
118
meta.c
118
meta.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.c,v 1.136 2020/10/31 12:04:24 rillig Exp $ */
|
||||
/* $NetBSD: meta.c,v 1.144 2020/11/15 12:02:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Implement 'meta' mode.
|
||||
@ -84,7 +84,6 @@ static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
|
||||
static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
|
||||
|
||||
extern Boolean forceJobs;
|
||||
extern Boolean comatMake;
|
||||
extern char **environ;
|
||||
|
||||
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
|
||||
@ -97,7 +96,7 @@ extern char **environ;
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_STRSEP)
|
||||
# define strsep(s, d) stresep((s), (d), 0)
|
||||
# define strsep(s, d) stresep((s), (d), '\0')
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -184,7 +183,7 @@ filemon_read(FILE *mfp, int fd)
|
||||
error = 0;
|
||||
fprintf(mfp, "\n-- filemon acquired metadata --\n");
|
||||
|
||||
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||
while ((n = read(fd, buf, sizeof buf)) > 0) {
|
||||
if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n)
|
||||
error = EIO;
|
||||
}
|
||||
@ -276,12 +275,12 @@ meta_name(char *mname, size_t mnamelen,
|
||||
* next time through.
|
||||
*/
|
||||
if (tname[0] == '/') {
|
||||
strlcpy(buf, tname, sizeof(buf));
|
||||
strlcpy(buf, tname, sizeof buf);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "%s/%s", cwd, tname);
|
||||
snprintf(buf, sizeof buf, "%s/%s", cwd, tname);
|
||||
}
|
||||
eat_dots(buf, sizeof(buf), 1); /* ./ */
|
||||
eat_dots(buf, sizeof(buf), 2); /* ../ */
|
||||
eat_dots(buf, sizeof buf, 1); /* ./ */
|
||||
eat_dots(buf, sizeof buf, 2); /* ../ */
|
||||
tname = buf;
|
||||
}
|
||||
}
|
||||
@ -329,7 +328,7 @@ is_submake(void *cmdp, void *gnp)
|
||||
char *cp2;
|
||||
int rc = 0; /* keep looking */
|
||||
|
||||
if (!p_make) {
|
||||
if (p_make == NULL) {
|
||||
void *dontFreeIt;
|
||||
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
|
||||
p_len = strlen(p_make);
|
||||
@ -341,7 +340,7 @@ is_submake(void *cmdp, void *gnp)
|
||||
cmd = mp;
|
||||
}
|
||||
cp2 = strstr(cmd, p_make);
|
||||
if ((cp2)) {
|
||||
if (cp2 != NULL) {
|
||||
switch (cp2[p_len]) {
|
||||
case '\0':
|
||||
case ' ':
|
||||
@ -415,7 +414,7 @@ static Boolean
|
||||
meta_needed(GNode *gn, const char *dname,
|
||||
char *objdir, int verbose)
|
||||
{
|
||||
struct make_stat mst;
|
||||
struct cached_stat cst;
|
||||
|
||||
if (verbose)
|
||||
verbose = DEBUG(META);
|
||||
@ -446,7 +445,7 @@ meta_needed(GNode *gn, const char *dname,
|
||||
}
|
||||
|
||||
/* The object directory may not exist. Check it.. */
|
||||
if (cached_stat(dname, &mst) != 0) {
|
||||
if (cached_stat(dname, &cst) != 0) {
|
||||
if (verbose)
|
||||
debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
|
||||
return FALSE;
|
||||
@ -513,7 +512,7 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
/* Don't create meta data. */
|
||||
goto out;
|
||||
|
||||
fname = meta_name(pbm->meta_fname, sizeof(pbm->meta_fname),
|
||||
fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname,
|
||||
dname, tname, objdir);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
@ -529,7 +528,7 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
|
||||
printCMDs(gn, &mf);
|
||||
|
||||
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
|
||||
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof buf));
|
||||
fprintf(mf.fp, "TARGET %s\n", tname);
|
||||
cp = GNode_VarOodate(gn);
|
||||
if (cp && *cp) {
|
||||
@ -585,7 +584,7 @@ meta_init(void)
|
||||
|
||||
#define get_mode_bf(bf, token) \
|
||||
if ((cp = strstr(make_mode, token))) \
|
||||
bf = boolValue(&cp[sizeof(token) - 1])
|
||||
bf = boolValue(cp + sizeof (token) - 1)
|
||||
|
||||
/*
|
||||
* Initialization we need after reading makefiles.
|
||||
@ -630,7 +629,7 @@ meta_mode_init(const char *make_mode)
|
||||
if (once)
|
||||
return;
|
||||
once = 1;
|
||||
memset(&Mybm, 0, sizeof(Mybm));
|
||||
memset(&Mybm, 0, sizeof Mybm);
|
||||
/*
|
||||
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
|
||||
*/
|
||||
@ -638,7 +637,7 @@ meta_mode_init(const char *make_mode)
|
||||
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
|
||||
str2Lst_Append(metaBailiwick, metaBailiwickStr);
|
||||
/*
|
||||
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
|
||||
*/
|
||||
@ -648,7 +647,7 @@ meta_mode_init(const char *make_mode)
|
||||
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
|
||||
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr);
|
||||
|
||||
/*
|
||||
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
||||
@ -784,7 +783,7 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
|
||||
if (job != NULL) {
|
||||
pbm = &job->bm;
|
||||
if (!gn)
|
||||
if (gn == NULL)
|
||||
gn = job->node;
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
@ -798,9 +797,9 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
if (gn) {
|
||||
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
|
||||
}
|
||||
getcwd(cwd, sizeof(cwd));
|
||||
getcwd(cwd, sizeof cwd);
|
||||
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL);
|
||||
if (pbm->meta_fname[0]) {
|
||||
if (pbm->meta_fname[0] != '\0') {
|
||||
Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL);
|
||||
}
|
||||
meta_job_finish(job);
|
||||
@ -821,7 +820,7 @@ meta_job_output(Job *job, char *cp, const char *nl)
|
||||
static char *meta_prefix = NULL;
|
||||
static size_t meta_prefix_len;
|
||||
|
||||
if (!meta_prefix) {
|
||||
if (meta_prefix == NULL) {
|
||||
char *cp2;
|
||||
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}",
|
||||
@ -851,7 +850,7 @@ meta_cmd_finish(void *pbmp)
|
||||
int x;
|
||||
#endif
|
||||
|
||||
if (!pbm)
|
||||
if (pbm == NULL)
|
||||
pbm = &Mybm;
|
||||
|
||||
#ifdef USE_FILEMON
|
||||
@ -865,9 +864,11 @@ meta_cmd_finish(void *pbmp)
|
||||
error = x;
|
||||
pbm->mon_fd = -1;
|
||||
pbm->filemon = NULL;
|
||||
} else
|
||||
return error;
|
||||
}
|
||||
#endif
|
||||
fprintf(pbm->mfp, "\n"); /* ensure end with newline */
|
||||
|
||||
fprintf(pbm->mfp, "\n"); /* ensure end with newline */
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -941,7 +942,7 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
*bufp = buf = p;
|
||||
*szp = bufsz = newsz;
|
||||
/* fetch the rest */
|
||||
if (!fgets(&buf[x], (int)bufsz - x, fp))
|
||||
if (fgets(&buf[x], (int)bufsz - x, fp) == NULL)
|
||||
return x; /* truncated! */
|
||||
goto check_newline;
|
||||
}
|
||||
@ -1012,7 +1013,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
char *fm;
|
||||
|
||||
/* skip if filter result is empty */
|
||||
snprintf(fname, sizeof(fname),
|
||||
snprintf(fname, sizeof fname,
|
||||
"${%s:L:${%s:ts:}}",
|
||||
p, MAKE_META_IGNORE_FILTER);
|
||||
(void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
|
||||
@ -1090,7 +1091,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
FILE *fp;
|
||||
Boolean needOODATE = FALSE;
|
||||
StringList *missingFiles;
|
||||
int have_filemon = FALSE;
|
||||
Boolean have_filemon = FALSE;
|
||||
void *objdir_freeIt;
|
||||
|
||||
if (oodate)
|
||||
@ -1114,7 +1115,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
*/
|
||||
Make_DoAllVar(gn);
|
||||
|
||||
meta_name(fname, sizeof(fname), dname, tname, dname);
|
||||
meta_name(fname, sizeof fname, dname, tname, dname);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: %s\n", fname);
|
||||
@ -1128,22 +1129,22 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
int pid;
|
||||
int x;
|
||||
StringListNode *cmdNode;
|
||||
struct make_stat mst;
|
||||
struct cached_stat cst;
|
||||
|
||||
if (!buf) {
|
||||
if (buf == NULL) {
|
||||
bufsz = 8 * BUFSIZ;
|
||||
buf = bmake_malloc(bufsz);
|
||||
}
|
||||
|
||||
if (!cwdlen) {
|
||||
if (getcwd(cwd, sizeof(cwd)) == NULL)
|
||||
if (cwdlen == 0) {
|
||||
if (getcwd(cwd, sizeof cwd) == NULL)
|
||||
err(1, "Could not get current working directory");
|
||||
cwdlen = strlen(cwd);
|
||||
}
|
||||
strlcpy(lcwd, cwd, sizeof(lcwd));
|
||||
strlcpy(latestdir, cwd, sizeof(latestdir));
|
||||
strlcpy(lcwd, cwd, sizeof lcwd);
|
||||
strlcpy(latestdir, cwd, sizeof latestdir);
|
||||
|
||||
if (!tmpdir) {
|
||||
if (tmpdir == NULL) {
|
||||
tmpdir = getTmpdir();
|
||||
tmplen = strlen(tmpdir);
|
||||
}
|
||||
@ -1228,17 +1229,17 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
|
||||
Var_Set(ldir_vname, latestdir, VAR_GLOBAL);
|
||||
}
|
||||
snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid);
|
||||
snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid);
|
||||
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
|
||||
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
|
||||
lastpid = pid;
|
||||
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(latestdir, ldir, sizeof(latestdir));
|
||||
strlcpy(latestdir, ldir, sizeof latestdir);
|
||||
bmake_free(tp);
|
||||
}
|
||||
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(lcwd, ldir, sizeof(lcwd));
|
||||
strlcpy(lcwd, ldir, sizeof lcwd);
|
||||
bmake_free(tp);
|
||||
}
|
||||
}
|
||||
@ -1271,9 +1272,9 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
child = atoi(p);
|
||||
if (child > 0) {
|
||||
snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child);
|
||||
snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child);
|
||||
Var_Set(cldir, lcwd, VAR_GLOBAL);
|
||||
snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
|
||||
snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child);
|
||||
Var_Set(cldir, latestdir, VAR_GLOBAL);
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
@ -1288,8 +1289,8 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
case 'C': /* Chdir */
|
||||
/* Update lcwd and latest directory. */
|
||||
strlcpy(latestdir, p, sizeof(latestdir));
|
||||
strlcpy(lcwd, p, sizeof(lcwd));
|
||||
strlcpy(latestdir, p, sizeof latestdir);
|
||||
strlcpy(lcwd, p, sizeof lcwd);
|
||||
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
|
||||
Var_Set(ldir_vname, lcwd, VAR_GLOBAL);
|
||||
#ifdef DEBUG_META_MODE
|
||||
@ -1382,11 +1383,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
break;
|
||||
|
||||
/* ignore anything containing the string "tmp" */
|
||||
/* XXX: The arguments to strstr must be swapped. */
|
||||
if ((strstr("tmp", p)))
|
||||
break;
|
||||
|
||||
if ((link_src != NULL && cached_lstat(p, &mst) < 0) ||
|
||||
(link_src == NULL && cached_stat(p, &mst) < 0)) {
|
||||
if ((link_src != NULL && cached_lstat(p, &cst) < 0) ||
|
||||
(link_src == NULL && cached_stat(p, &cst) < 0)) {
|
||||
if (!meta_ignore(gn, p))
|
||||
append_if_new(missingFiles, p);
|
||||
}
|
||||
@ -1425,17 +1427,17 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
continue; /* no point */
|
||||
|
||||
/* Check vs latestdir */
|
||||
snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p);
|
||||
snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p);
|
||||
sdirs[sdx++] = fname1;
|
||||
|
||||
if (strcmp(latestdir, lcwd) != 0) {
|
||||
/* Check vs lcwd */
|
||||
snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p);
|
||||
snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p);
|
||||
sdirs[sdx++] = fname2;
|
||||
}
|
||||
if (strcmp(lcwd, cwd) != 0) {
|
||||
/* Check vs cwd */
|
||||
snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p);
|
||||
snprintf(fname3, sizeof fname3, "%s/%s", cwd, p);
|
||||
sdirs[sdx++] = fname3;
|
||||
}
|
||||
}
|
||||
@ -1446,7 +1448,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
DEBUG3(META, "%s: %d: looking for: %s\n",
|
||||
fname, lineno, *sdp);
|
||||
#endif
|
||||
if (cached_stat(*sdp, &mst) == 0) {
|
||||
if (cached_stat(*sdp, &cst) == 0) {
|
||||
found = 1;
|
||||
p = *sdp;
|
||||
}
|
||||
@ -1456,12 +1458,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
DEBUG3(META, "%s: %d: found: %s\n",
|
||||
fname, lineno, p);
|
||||
#endif
|
||||
if (!S_ISDIR(mst.mst_mode) &&
|
||||
mst.mst_mtime > gn->mtime) {
|
||||
if (!S_ISDIR(cst.cst_mode) &&
|
||||
cst.cst_mtime > gn->mtime) {
|
||||
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
|
||||
fname, lineno, p);
|
||||
oodate = TRUE;
|
||||
} else if (S_ISDIR(mst.mst_mode)) {
|
||||
} else if (S_ISDIR(cst.cst_mode)) {
|
||||
/* Update the latest directory. */
|
||||
cached_realpath(p, latestdir);
|
||||
}
|
||||
@ -1476,7 +1478,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
/* previous latestdir is no longer relevant */
|
||||
strlcpy(latestdir, lcwd, sizeof(latestdir));
|
||||
strlcpy(latestdir, lcwd, sizeof latestdir);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1537,10 +1539,10 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
if (buf[x - 1] == '\n')
|
||||
buf[x - 1] = '\0';
|
||||
}
|
||||
if (p &&
|
||||
if (p != NULL &&
|
||||
!hasOODATE &&
|
||||
!(gn->type & OP_NOMETA_CMP) &&
|
||||
strcmp(p, cmd) != 0) {
|
||||
(strcmp(p, cmd) != 0)) {
|
||||
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
|
||||
fname, lineno, p, cmd);
|
||||
if (!metaIgnoreCMDs)
|
||||
@ -1588,7 +1590,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
cp = NULL; /* not in .CURDIR */
|
||||
}
|
||||
}
|
||||
if (!cp) {
|
||||
if (cp == NULL) {
|
||||
DEBUG1(META, "%s: required but missing\n", fname);
|
||||
oodate = TRUE;
|
||||
needOODATE = TRUE; /* assume the worst */
|
||||
@ -1686,7 +1688,7 @@ meta_compat_parent(pid_t child)
|
||||
|
||||
if (outfd != -1 && FD_ISSET(outfd, &readfds)) do {
|
||||
/* XXX this is not line-buffered */
|
||||
ssize_t nread = read(outfd, buf, sizeof(buf) - 1);
|
||||
ssize_t nread = read(outfd, buf, sizeof buf - 1);
|
||||
if (nread == -1)
|
||||
err(1, "read");
|
||||
if (nread == 0) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: metachar.h,v 1.11 2020/10/31 18:20:00 rillig Exp $ */
|
||||
/* $NetBSD: metachar.h,v 1.12 2020/11/10 00:32:12 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2015 The NetBSD Foundation, Inc.
|
||||
@ -37,7 +37,7 @@ extern unsigned char _metachar[];
|
||||
|
||||
#define is_shell_metachar(c) _metachar[(c) & 0x7f]
|
||||
|
||||
static inline MAKE_ATTR_UNUSED int
|
||||
MAKE_INLINE int
|
||||
needshell(const char *cmd)
|
||||
{
|
||||
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
|
||||
|
186
missing/sys/cdefs.h
Normal file
186
missing/sys/cdefs.h
Normal file
@ -0,0 +1,186 @@
|
||||
/* $NetBSD: cdefs.h,v 1.18 1997/06/18 19:09:50 christos Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Berkeley Software Design, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)cdefs.h 8.7 (Berkeley) 1/21/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_CDEFS_H_
|
||||
|
||||
#if defined(NEED_HOST_CDEFS_H)
|
||||
/*
|
||||
* make sure we don't come past here again.
|
||||
*/
|
||||
#undef NEED_HOST_CDEFS_H
|
||||
/*
|
||||
* Some systems - notably linux, have sys/cdefs.h
|
||||
* which is not really compatible with our's.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
# include_next <sys/cdefs.h>
|
||||
#else
|
||||
/*
|
||||
* It sucks that we have to hard code a path like this.
|
||||
* But systems that have a sys/cdefs.h that don't use gcc
|
||||
* should be few.
|
||||
*/
|
||||
# include "/usr/include/sys/cdefs.h"
|
||||
#endif
|
||||
/*
|
||||
* We are about to [re]define these
|
||||
*/
|
||||
#undef __P
|
||||
#undef _SYS_CDEFS_H_
|
||||
#endif
|
||||
|
||||
#define _SYS_CDEFS_H_
|
||||
|
||||
#ifdef NetBSD
|
||||
#include <machine/cdefs.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
# ifndef __BEGIN_DECLS
|
||||
# define __BEGIN_DECLS extern "C" {
|
||||
# endif
|
||||
# ifndef __END_DECLS
|
||||
# define __END_DECLS };
|
||||
# endif
|
||||
#else
|
||||
# ifndef __BEGIN_DECLS
|
||||
# define __BEGIN_DECLS
|
||||
# endif
|
||||
# ifndef __END_DECLS
|
||||
# define __END_DECLS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The __CONCAT macro is used to concatenate parts of symbol names, e.g.
|
||||
* with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
|
||||
* The __CONCAT macro is a bit tricky -- make sure you don't put spaces
|
||||
* in between its arguments. __CONCAT can also concatenate double-quoted
|
||||
* strings produced by the __STRING macro, but this only works with ANSI C.
|
||||
*/
|
||||
#if defined(__STDC__) || defined(__cplusplus)
|
||||
#define __P(protos) protos /* full-blown ANSI C */
|
||||
#ifndef __CONCAT
|
||||
#define __CONCAT(x,y) x ## y
|
||||
#endif
|
||||
#define __STRING(x) #x
|
||||
|
||||
#define __const const /* define reserved names to standard */
|
||||
#define __signed signed
|
||||
#define __volatile volatile
|
||||
#if defined(__cplusplus)
|
||||
#define __inline inline /* convert to C++ keyword */
|
||||
#else
|
||||
#ifndef __GNUC__
|
||||
#define __inline /* delete GCC keyword */
|
||||
#endif /* !__GNUC__ */
|
||||
#endif /* !__cplusplus */
|
||||
|
||||
#else /* !(__STDC__ || __cplusplus) */
|
||||
#define __P(protos) () /* traditional C preprocessor */
|
||||
#define __CONCAT(x,y) x/**/y
|
||||
#define __STRING(x) "x"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#define __const /* delete pseudo-ANSI C keywords */
|
||||
#define __inline
|
||||
#define __signed
|
||||
#define __volatile
|
||||
#endif /* !__GNUC__ */
|
||||
|
||||
/*
|
||||
* In non-ANSI C environments, new programs will want ANSI-only C keywords
|
||||
* deleted from the program and old programs will want them left alone.
|
||||
* Programs using the ANSI C keywords const, inline etc. as normal
|
||||
* identifiers should define -DNO_ANSI_KEYWORDS.
|
||||
*/
|
||||
#ifndef NO_ANSI_KEYWORDS
|
||||
#define const __const /* convert ANSI C keywords */
|
||||
#define inline __inline
|
||||
#define signed __signed
|
||||
#define volatile __volatile
|
||||
#endif /* !NO_ANSI_KEYWORDS */
|
||||
#endif /* !(__STDC__ || __cplusplus) */
|
||||
|
||||
/*
|
||||
* GCC1 and some versions of GCC2 declare dead (non-returning) and
|
||||
* pure (no side effects) functions using "volatile" and "const";
|
||||
* unfortunately, these then cause warnings under "-ansi -pedantic".
|
||||
* GCC2 uses a new, peculiar __attribute__((attrs)) style. All of
|
||||
* these work for GNU C++ (modulo a slight glitch in the C++ grammar
|
||||
* in the distribution version of 2.5.5).
|
||||
*/
|
||||
#if !defined(__GNUC__) || __GNUC__ < 2 || \
|
||||
(__GNUC__ == 2 && __GNUC_MINOR__ < 5)
|
||||
#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
#define __dead __volatile
|
||||
#define __pure __const
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef sun386
|
||||
# define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#ifdef __KPRINTF_ATTRIBUTE__
|
||||
#define __kprintf_attribute__(a) __attribute__(a)
|
||||
#else
|
||||
#define __kprintf_attribute__(a)
|
||||
#endif
|
||||
|
||||
/* Delete pseudo-keywords wherever they are not available or needed. */
|
||||
#ifndef __dead
|
||||
#define __dead
|
||||
#define __pure
|
||||
#endif
|
||||
|
||||
#define __IDSTRING(name,string) \
|
||||
static const char name[] __attribute__((__unused__)) = string
|
||||
|
||||
#ifndef __RCSID
|
||||
#define __RCSID(s) __IDSTRING(rcsid,s)
|
||||
#endif
|
||||
|
||||
#ifndef __COPYRIGHT
|
||||
#define __COPYRIGHT(s) __IDSTRING(copyright,s)
|
||||
#endif
|
||||
|
||||
#endif /* !_SYS_CDEFS_H_ */
|
@ -1,3 +1,10 @@
|
||||
2020-11-06 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201106
|
||||
|
||||
* meta.autodep.mk: use OBJ_EXTENSIONS rather than hardcode sed
|
||||
args to tweak extensions for local deps.
|
||||
|
||||
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201101
|
||||
|
@ -55,7 +55,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: install-mk,v 1.183 2020/11/02 16:34:12 sjg Exp $
|
||||
# $Id: install-mk,v 1.184 2020/11/08 05:47:56 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 1994 Simon J. Gerraty
|
||||
#
|
||||
@ -70,7 +70,7 @@
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
MK_VERSION=20201101
|
||||
MK_VERSION=20201106
|
||||
OWNER=
|
||||
GROUP=
|
||||
MODE=444
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: meta.autodep.mk,v 1.52 2020/07/18 05:57:57 sjg Exp $
|
||||
# $Id: meta.autodep.mk,v 1.53 2020/11/08 05:47:56 sjg Exp $
|
||||
|
||||
#
|
||||
# @(#) Copyright (c) 2010, Simon J. Gerraty
|
||||
@ -178,7 +178,8 @@ DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh
|
||||
@case "${.MAKE.META.FILES:T:M*.po.*}" in \
|
||||
*.po.*) mv $@.${.MAKE.PID} $@;; \
|
||||
*) { cat $@.${.MAKE.PID}; \
|
||||
sed 's,\${PICO}:,.o:,;s,\.o:,.po:,' $@.${.MAKE.PID}; } | sort -u > $@; \
|
||||
sed ${OBJ_EXTENSIONS:N.o:N.po:@o@-e 's,\$o:,.o:,'@} \
|
||||
-e 's,\.o:,.po:,' $@.${.MAKE.PID}; } | sort -u > $@; \
|
||||
rm -f $@.${.MAKE.PID};; \
|
||||
esac
|
||||
.else
|
||||
|
@ -77,7 +77,7 @@
|
||||
|
||||
|
||||
# RCSid:
|
||||
# $Id: meta2deps.sh,v 1.14 2020/10/02 03:11:17 sjg Exp $
|
||||
# $Id: meta2deps.sh,v 1.15 2020/11/08 06:31:08 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2013, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
@ -259,7 +259,7 @@ meta2deps() {
|
||||
0) error "no filemon data";;
|
||||
*) ;;
|
||||
esac
|
||||
version=0
|
||||
version=0
|
||||
continue
|
||||
;;
|
||||
$pid,$pid) ;;
|
||||
|
63
nonints.h
63
nonints.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: nonints.h,v 1.149 2020/11/01 00:24:57 rillig Exp $ */
|
||||
/* $NetBSD: nonints.h,v 1.162 2020/11/16 21:48:18 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -79,8 +79,8 @@ void Arch_End(void);
|
||||
Boolean Arch_ParseArchive(char **, GNodeList *, GNode *);
|
||||
void Arch_Touch(GNode *);
|
||||
void Arch_TouchLib(GNode *);
|
||||
time_t Arch_MTime(GNode *);
|
||||
time_t Arch_MemMTime(GNode *);
|
||||
void Arch_UpdateMTime(GNode *gn);
|
||||
void Arch_UpdateMemberMTime(GNode *gn);
|
||||
void Arch_FindLib(GNode *, SearchPath *);
|
||||
Boolean Arch_LibOODate(GNode *);
|
||||
Boolean Arch_IsLib(GNode *);
|
||||
@ -107,6 +107,7 @@ void JobReapChild(pid_t, WAIT_T, Boolean);
|
||||
#endif
|
||||
|
||||
/* main.c */
|
||||
Boolean GetBooleanVar(const char *, Boolean);
|
||||
void Main_ParseArgLine(const char *);
|
||||
void MakeMode(const char *);
|
||||
char *Cmd_Exec(const char *, const char **);
|
||||
@ -118,8 +119,7 @@ void Finish(int) MAKE_ATTR_DEAD;
|
||||
int eunlink(const char *);
|
||||
void execDie(const char *, const char *);
|
||||
char *getTmpdir(void);
|
||||
Boolean s2Boolean(const char *, Boolean);
|
||||
Boolean getBoolean(const char *, Boolean);
|
||||
Boolean ParseBoolean(const char *, Boolean);
|
||||
char *cached_realpath(const char *, char *);
|
||||
|
||||
/* parse.c */
|
||||
@ -159,7 +159,7 @@ typedef struct Words {
|
||||
} Words;
|
||||
|
||||
Words Str_Words(const char *, Boolean);
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
MAKE_INLINE void
|
||||
Words_Free(Words w) {
|
||||
free(w.words);
|
||||
free(w.freeIt);
|
||||
@ -199,15 +199,15 @@ void Targ_End(void);
|
||||
|
||||
void Targ_Stats(void);
|
||||
GNodeList *Targ_List(void);
|
||||
GNode *Targ_NewGN(const char *);
|
||||
GNode *GNode_New(const char *);
|
||||
GNode *Targ_FindNode(const char *);
|
||||
GNode *Targ_GetNode(const char *);
|
||||
GNode *Targ_NewInternalNode(const char *);
|
||||
GNode *Targ_GetEndNode(void);
|
||||
GNodeList *Targ_FindList(StringList *);
|
||||
Boolean Targ_Ignore(GNode *);
|
||||
Boolean Targ_Silent(GNode *);
|
||||
Boolean Targ_Precious(GNode *);
|
||||
Boolean Targ_Ignore(const GNode *);
|
||||
Boolean Targ_Silent(const GNode *);
|
||||
Boolean Targ_Precious(const GNode *);
|
||||
void Targ_SetMain(GNode *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
@ -223,21 +223,40 @@ void Var_End(void);
|
||||
|
||||
typedef enum VarEvalFlags {
|
||||
VARE_NONE = 0,
|
||||
/* Treat undefined variables as errors. */
|
||||
VARE_UNDEFERR = 0x01,
|
||||
/* Expand and evaluate variables during parsing. */
|
||||
VARE_WANTRES = 0x02,
|
||||
/* In an assignment using the ':=' operator, keep '$$' as '$$' instead
|
||||
* of reducing it to a single '$'. */
|
||||
VARE_ASSIGN = 0x04
|
||||
|
||||
/* Expand and evaluate variables during parsing.
|
||||
*
|
||||
* TODO: Document what Var_Parse and Var_Subst return when this flag
|
||||
* is not set. */
|
||||
VARE_WANTRES = 1 << 0,
|
||||
|
||||
/* Treat undefined variables as errors.
|
||||
* Must only be used in combination with VARE_WANTRES. */
|
||||
VARE_UNDEFERR = 1 << 1,
|
||||
|
||||
/* Keep '$$' as '$$' instead of reducing it to a single '$'.
|
||||
*
|
||||
* Used in variable assignments using the ':=' operator. It allows
|
||||
* multiple such assignments to be chained without accidentally expanding
|
||||
* '$$file' to '$file' in the first assignment and interpreting it as
|
||||
* '${f}' followed by 'ile' in the next assignment.
|
||||
*
|
||||
* See also preserveUndefined, which preserves subexpressions that are
|
||||
* based on undefined variables; maybe that can be converted to a flag
|
||||
* as well. */
|
||||
VARE_KEEP_DOLLAR = 1 << 2
|
||||
} VarEvalFlags;
|
||||
|
||||
typedef enum VarSet_Flags {
|
||||
VAR_NO_EXPORT = 0x01, /* do not export */
|
||||
typedef enum VarSetFlags {
|
||||
VAR_SET_NONE = 0,
|
||||
|
||||
/* do not export */
|
||||
VAR_SET_NO_EXPORT = 1 << 0,
|
||||
|
||||
/* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag. */
|
||||
VAR_SET_READONLY = 0x02
|
||||
} VarSet_Flags;
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
} VarSetFlags;
|
||||
|
||||
/* The state of error handling returned by Var_Parse.
|
||||
*
|
||||
@ -296,7 +315,7 @@ typedef enum VarParseResult {
|
||||
|
||||
void Var_Delete(const char *, GNode *);
|
||||
void Var_Set(const char *, const char *, GNode *);
|
||||
void Var_Set_with_flags(const char *, const char *, GNode *, VarSet_Flags);
|
||||
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags);
|
||||
void Var_Append(const char *, const char *, GNode *);
|
||||
Boolean Var_Exists(const char *, GNode *);
|
||||
const char *Var_Value(const char *, GNode *, void **);
|
||||
|
60
str.c
60
str.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: str.c,v 1.70 2020/10/24 20:51:49 rillig Exp $ */
|
||||
/* $NetBSD: str.c,v 1.74 2020/11/16 18:28:27 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.70 2020/10/24 20:51:49 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.74 2020/11/16 18:28:27 rillig Exp $");
|
||||
|
||||
/* Return the concatenation of s1 and s2, freshly allocated. */
|
||||
char *
|
||||
@ -116,15 +116,13 @@ str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
|
||||
}
|
||||
|
||||
/* Fracture a string into an array of words (as delineated by tabs or spaces)
|
||||
* taking quotation marks into account. Leading tabs/spaces are ignored.
|
||||
* taking quotation marks into account.
|
||||
*
|
||||
* If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
|
||||
* etc... are expanded. In this case, the return value is NULL on parse
|
||||
* errors.
|
||||
* etc... are expanded. In this case, return NULL on parse errors.
|
||||
*
|
||||
* Returns the fractured words, which must be freed later using Words_Free.
|
||||
* If expand was TRUE and there was a parse error, words is NULL, and in that
|
||||
* case, nothing needs to be freed.
|
||||
* Returns the fractured words, which must be freed later using Words_Free,
|
||||
* unless the returned Words.words was NULL.
|
||||
*/
|
||||
Words
|
||||
Str_Words(const char *str, Boolean expand)
|
||||
@ -139,9 +137,8 @@ Str_Words(const char *str, Boolean expand)
|
||||
char *word_end;
|
||||
const char *str_p;
|
||||
|
||||
/* skip leading space chars. */
|
||||
for (; *str == ' ' || *str == '\t'; ++str)
|
||||
continue;
|
||||
/* XXX: why only hspace, not whitespace? */
|
||||
cpp_skip_hspace(&str); /* skip leading space chars. */
|
||||
|
||||
/* words_buf holds the words, separated by '\0'. */
|
||||
str_len = strlen(str);
|
||||
@ -239,7 +236,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
case '\n':
|
||||
/* hmmm; fix it up as best we can */
|
||||
ch = '\\';
|
||||
--str_p;
|
||||
str_p--;
|
||||
break;
|
||||
case 'b':
|
||||
ch = '\b';
|
||||
@ -264,21 +261,15 @@ Str_Words(const char *str, Boolean expand)
|
||||
*word_end++ = ch;
|
||||
}
|
||||
done:
|
||||
words[words_len] = NULL;
|
||||
words[words_len] = NULL; /* useful for argv */
|
||||
return (Words){ words, words_len, words_buf };
|
||||
}
|
||||
|
||||
/*
|
||||
* Str_Match -- Test if a string matches a pattern like "*.[ch]".
|
||||
* The following special characters are known *?\[] (as in fnmatch(3)).
|
||||
*
|
||||
* XXX this function does not detect or report malformed patterns.
|
||||
*
|
||||
* Results:
|
||||
* Non-zero is returned if string matches the pattern, 0 otherwise. The
|
||||
* matching operation permits the following special characters in the
|
||||
* pattern: *?\[] (as in fnmatch(3)).
|
||||
*
|
||||
* Side effects: None.
|
||||
* XXX: this function does not detect or report malformed patterns.
|
||||
*/
|
||||
Boolean
|
||||
Str_Match(const char *str, const char *pat)
|
||||
@ -286,12 +277,12 @@ Str_Match(const char *str, const char *pat)
|
||||
for (;;) {
|
||||
/*
|
||||
* See if we're at the end of both the pattern and the
|
||||
* string. If, we succeeded. If we're at the end of the
|
||||
* string. If so, we succeeded. If we're at the end of the
|
||||
* pattern but not at the end of the string, we failed.
|
||||
*/
|
||||
if (*pat == 0)
|
||||
return *str == 0;
|
||||
if (*str == 0 && *pat != '*')
|
||||
if (*pat == '\0')
|
||||
return *str == '\0';
|
||||
if (*str == '\0' && *pat != '*')
|
||||
return FALSE;
|
||||
|
||||
/*
|
||||
@ -302,9 +293,9 @@ Str_Match(const char *str, const char *pat)
|
||||
pat++;
|
||||
while (*pat == '*')
|
||||
pat++;
|
||||
if (*pat == 0)
|
||||
if (*pat == '\0')
|
||||
return TRUE;
|
||||
while (*str != 0) {
|
||||
while (*str != '\0') {
|
||||
if (Str_Match(str, pat))
|
||||
return TRUE;
|
||||
str++;
|
||||
@ -327,15 +318,18 @@ Str_Match(const char *str, const char *pat)
|
||||
pat += neg ? 2 : 1;
|
||||
|
||||
for (;;) {
|
||||
if (*pat == ']' || *pat == 0) {
|
||||
if (*pat == ']' || *pat == '\0') {
|
||||
if (neg)
|
||||
break;
|
||||
return FALSE;
|
||||
}
|
||||
/* XXX: This naive comparison makes the parser
|
||||
* for the pattern dependent on the actual of
|
||||
* the string. This is unpredictable. */
|
||||
if (*pat == *str)
|
||||
break;
|
||||
if (pat[1] == '-') {
|
||||
if (pat[2] == 0)
|
||||
if (pat[2] == '\0')
|
||||
return neg;
|
||||
if (*pat <= *str && pat[2] >= *str)
|
||||
break;
|
||||
@ -345,11 +339,11 @@ Str_Match(const char *str, const char *pat)
|
||||
}
|
||||
pat++;
|
||||
}
|
||||
if (neg && *pat != ']' && *pat != 0)
|
||||
if (neg && *pat != ']' && *pat != '\0')
|
||||
return FALSE;
|
||||
while (*pat != ']' && *pat != 0)
|
||||
while (*pat != ']' && *pat != '\0')
|
||||
pat++;
|
||||
if (*pat == 0)
|
||||
if (*pat == '\0')
|
||||
pat--;
|
||||
goto thisCharOK;
|
||||
}
|
||||
@ -360,7 +354,7 @@ Str_Match(const char *str, const char *pat)
|
||||
*/
|
||||
if (*pat == '\\') {
|
||||
pat++;
|
||||
if (*pat == 0)
|
||||
if (*pat == '\0')
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
166
targ.c
166
targ.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $ */
|
||||
/* $NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,19 +68,17 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*-
|
||||
* targ.c --
|
||||
* Functions for maintaining the Lst allTargets. Target nodes are
|
||||
* kept in two structures: a Lst and a hash table.
|
||||
/*
|
||||
* Maintaining the targets and sources, which are both implemented as GNode.
|
||||
*
|
||||
* Interface:
|
||||
* Targ_Init Initialization procedure.
|
||||
* Targ_Init Initialize the module.
|
||||
*
|
||||
* Targ_End Clean up the module
|
||||
* Targ_End Clean up the module.
|
||||
*
|
||||
* Targ_List Return the list of all targets so far.
|
||||
*
|
||||
* Targ_NewGN Create a new GNode for the passed target
|
||||
* GNode_New Create a new GNode for the passed target
|
||||
* (string). The node is *not* placed in the
|
||||
* hash table, though all its fields are
|
||||
* initialized.
|
||||
@ -121,23 +119,26 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $");
|
||||
|
||||
static GNodeList *allTargets; /* the list of all targets found so far */
|
||||
#ifdef CLEANUP
|
||||
static GNodeList *allGNs; /* List of all the GNodes */
|
||||
#endif
|
||||
static HashTable targets; /* a hash table of same */
|
||||
/* All target nodes found so far, but not the source nodes. */
|
||||
static GNodeList *allTargets;
|
||||
static HashTable allTargetsByName;
|
||||
|
||||
#ifdef CLEANUP
|
||||
static void TargFreeGN(void *);
|
||||
static GNodeList *allNodes;
|
||||
|
||||
static void GNode_Free(void *);
|
||||
#endif
|
||||
|
||||
void
|
||||
Targ_Init(void)
|
||||
{
|
||||
allTargets = Lst_New();
|
||||
HashTable_Init(&targets);
|
||||
HashTable_Init(&allTargetsByName);
|
||||
#ifdef CLEANUP
|
||||
allNodes = Lst_New();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -146,56 +147,67 @@ Targ_End(void)
|
||||
Targ_Stats();
|
||||
#ifdef CLEANUP
|
||||
Lst_Free(allTargets);
|
||||
if (allGNs != NULL)
|
||||
Lst_Destroy(allGNs, TargFreeGN);
|
||||
HashTable_Done(&targets);
|
||||
HashTable_Done(&allTargetsByName);
|
||||
Lst_Destroy(allNodes, GNode_Free);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Targ_Stats(void)
|
||||
{
|
||||
HashTable_DebugStats(&targets, "targets");
|
||||
HashTable_DebugStats(&allTargetsByName, "targets");
|
||||
}
|
||||
|
||||
/* Return the list of all targets. */
|
||||
/*
|
||||
* Return the list of all targets, which are all nodes that appear on the
|
||||
* left-hand side of a dependency declaration such as "target: source".
|
||||
* The returned list does not contain pure sources.
|
||||
*/
|
||||
GNodeList *
|
||||
Targ_List(void)
|
||||
{
|
||||
return allTargets;
|
||||
}
|
||||
|
||||
/* Create and initialize a new graph node. The gnode is added to the list of
|
||||
* all gnodes.
|
||||
/* Create a new graph node, but don't register it anywhere.
|
||||
*
|
||||
* Input:
|
||||
* name the name of the node, such as "clean", "src.c", ".END"
|
||||
* Graph nodes that appear on the left-hand side of a dependency line such
|
||||
* as "target: source" are called targets. XXX: In some cases (like the
|
||||
* .ALLTARGETS variable), all nodes are called targets as well, even if they
|
||||
* never appear on the left-hand side. This is a mistake.
|
||||
*
|
||||
* Typical names for graph nodes are:
|
||||
* "src.c" (an ordinary file)
|
||||
* "clean" (a .PHONY target)
|
||||
* ".END" (a special hook target)
|
||||
* "-lm" (a library)
|
||||
* "libc.a(isspace.o)" (an archive member)
|
||||
*/
|
||||
GNode *
|
||||
Targ_NewGN(const char *name)
|
||||
GNode_New(const char *name)
|
||||
{
|
||||
GNode *gn;
|
||||
|
||||
gn = bmake_malloc(sizeof(GNode));
|
||||
gn = bmake_malloc(sizeof *gn);
|
||||
gn->name = bmake_strdup(name);
|
||||
gn->uname = NULL;
|
||||
gn->path = NULL;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
|
||||
gn->unmade = 0;
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->centurion = NULL;
|
||||
gn->made = UNMADE;
|
||||
gn->flags = 0;
|
||||
gn->checked_seqno = 0;
|
||||
gn->made = UNMADE;
|
||||
gn->unmade = 0;
|
||||
gn->mtime = 0;
|
||||
gn->youngestChild = NULL;
|
||||
gn->implicitParents = Lst_New();
|
||||
gn->cohorts = Lst_New();
|
||||
gn->parents = Lst_New();
|
||||
gn->children = Lst_New();
|
||||
gn->order_pred = Lst_New();
|
||||
gn->order_succ = Lst_New();
|
||||
gn->cohorts = Lst_New();
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->centurion = NULL;
|
||||
gn->checked_seqno = 0;
|
||||
HashTable_Init(&gn->context);
|
||||
gn->commands = Lst_New();
|
||||
gn->suffix = NULL;
|
||||
@ -203,9 +215,7 @@ Targ_NewGN(const char *name)
|
||||
gn->lineno = 0;
|
||||
|
||||
#ifdef CLEANUP
|
||||
if (allGNs == NULL)
|
||||
allGNs = Lst_New();
|
||||
Lst_Append(allGNs, gn);
|
||||
Lst_Append(allNodes, gn);
|
||||
#endif
|
||||
|
||||
return gn;
|
||||
@ -213,24 +223,29 @@ Targ_NewGN(const char *name)
|
||||
|
||||
#ifdef CLEANUP
|
||||
static void
|
||||
TargFreeGN(void *gnp)
|
||||
GNode_Free(void *gnp)
|
||||
{
|
||||
GNode *gn = gnp;
|
||||
|
||||
free(gn->name);
|
||||
free(gn->uname);
|
||||
free(gn->path);
|
||||
|
||||
Lst_Free(gn->implicitParents);
|
||||
Lst_Free(gn->cohorts);
|
||||
Lst_Free(gn->parents);
|
||||
Lst_Free(gn->children);
|
||||
Lst_Free(gn->order_succ);
|
||||
Lst_Free(gn->order_pred);
|
||||
HashTable_Done(&gn->context);
|
||||
Lst_Free(gn->commands);
|
||||
|
||||
/* XXX: does gn->suffix need to be freed? It is reference-counted. */
|
||||
/* gn->youngestChild is not owned by this node. */
|
||||
Lst_Free(gn->implicitParents); /* ... but not the nodes themselves, */
|
||||
Lst_Free(gn->parents); /* as they are not owned by this node. */
|
||||
Lst_Free(gn->children); /* likewise */
|
||||
Lst_Free(gn->order_pred); /* likewise */
|
||||
Lst_Free(gn->order_succ); /* likewise */
|
||||
Lst_Free(gn->cohorts); /* likewise */
|
||||
HashTable_Done(&gn->context); /* ... but not the variables themselves,
|
||||
* even though they are owned by this node.
|
||||
* XXX: they should probably be freed. */
|
||||
Lst_Free(gn->commands); /* ... but not the commands themselves,
|
||||
* as they may be shared with other nodes. */
|
||||
/* gn->suffix is not owned by this node. */
|
||||
/* XXX: gn->suffix should be unreferenced here. This requires a thorough
|
||||
* check that the reference counting is done correctly in all places,
|
||||
* otherwise a suffix might be freed too early. */
|
||||
|
||||
free(gn);
|
||||
}
|
||||
@ -240,7 +255,7 @@ TargFreeGN(void *gnp)
|
||||
GNode *
|
||||
Targ_FindNode(const char *name)
|
||||
{
|
||||
return HashTable_FindValue(&targets, name);
|
||||
return HashTable_FindValue(&allTargetsByName, name);
|
||||
}
|
||||
|
||||
/* Get the existing global node, or create it. */
|
||||
@ -248,7 +263,7 @@ GNode *
|
||||
Targ_GetNode(const char *name)
|
||||
{
|
||||
Boolean isNew;
|
||||
HashEntry *he = HashTable_CreateEntry(&targets, name, &isNew);
|
||||
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
|
||||
if (!isNew)
|
||||
return HashEntry_Get(he);
|
||||
|
||||
@ -259,14 +274,16 @@ Targ_GetNode(const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a node, register it in .ALLTARGETS but don't store it in the
|
||||
/*
|
||||
* Create a node, register it in .ALLTARGETS but don't store it in the
|
||||
* table of global nodes. This means it cannot be found by name.
|
||||
*
|
||||
* This is used for internal nodes, such as cohorts or .WAIT nodes. */
|
||||
* This is used for internal nodes, such as cohorts or .WAIT nodes.
|
||||
*/
|
||||
GNode *
|
||||
Targ_NewInternalNode(const char *name)
|
||||
{
|
||||
GNode *gn = Targ_NewGN(name);
|
||||
GNode *gn = GNode_New(name);
|
||||
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
|
||||
Lst_Append(allTargets, gn);
|
||||
if (doing_depend)
|
||||
@ -274,8 +291,10 @@ Targ_NewInternalNode(const char *name)
|
||||
return gn;
|
||||
}
|
||||
|
||||
/* Return the .END node, which contains the commands to be executed when
|
||||
* everything else is done. */
|
||||
/*
|
||||
* Return the .END node, which contains the commands to be run when
|
||||
* everything else has been made.
|
||||
*/
|
||||
GNode *Targ_GetEndNode(void)
|
||||
{
|
||||
/* Save the node locally to avoid having to search for it all the time. */
|
||||
@ -303,31 +322,33 @@ Targ_FindList(StringList *names)
|
||||
|
||||
/* Return true if should ignore errors when creating gn. */
|
||||
Boolean
|
||||
Targ_Ignore(GNode *gn)
|
||||
Targ_Ignore(const GNode *gn)
|
||||
{
|
||||
return opts.ignoreErrors || gn->type & OP_IGNORE;
|
||||
}
|
||||
|
||||
/* Return true if be silent when creating gn. */
|
||||
Boolean
|
||||
Targ_Silent(GNode *gn)
|
||||
Targ_Silent(const GNode *gn)
|
||||
{
|
||||
return opts.beSilent || gn->type & OP_SILENT;
|
||||
}
|
||||
|
||||
/* See if the given target is precious. */
|
||||
Boolean
|
||||
Targ_Precious(GNode *gn)
|
||||
Targ_Precious(const GNode *gn)
|
||||
{
|
||||
/* XXX: Why are '::' targets precious? */
|
||||
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
|
||||
}
|
||||
|
||||
/******************* DEBUG INFO PRINTING ****************/
|
||||
/*
|
||||
* The main target to be made; only for debugging output.
|
||||
* See mainNode in parse.c for the definitive source.
|
||||
*/
|
||||
static GNode *mainTarg;
|
||||
|
||||
static GNode *mainTarg; /* the main target, as set by Targ_SetMain */
|
||||
|
||||
/* Set our idea of the main target we'll be creating. Used for debugging
|
||||
* output. */
|
||||
/* Remember the main target to make; only used for debugging. */
|
||||
void
|
||||
Targ_SetMain(GNode *gn)
|
||||
{
|
||||
@ -459,12 +480,12 @@ Targ_PrintNode(GNode *gn, int pass)
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
}
|
||||
if (pass >= 2) {
|
||||
if (gn->unmade) {
|
||||
if (gn->unmade > 0) {
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
} else {
|
||||
debug_printf("# No unmade children\n");
|
||||
}
|
||||
if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
|
||||
if (!(gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
|
||||
if (gn->mtime != 0) {
|
||||
debug_printf("# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
@ -532,20 +553,27 @@ Targ_PrintGraph(int pass)
|
||||
{
|
||||
debug_printf("#*** Input graph:\n");
|
||||
Targ_PrintNodes(allTargets, pass);
|
||||
debug_printf("\n\n");
|
||||
debug_printf("#\n# Files that are only sources:\n");
|
||||
debug_printf("\n");
|
||||
debug_printf("\n");
|
||||
|
||||
debug_printf("#\n");
|
||||
debug_printf("# Files that are only sources:\n");
|
||||
PrintOnlySources();
|
||||
|
||||
debug_printf("#*** Global Variables:\n");
|
||||
Var_Dump(VAR_GLOBAL);
|
||||
|
||||
debug_printf("#*** Command-line Variables:\n");
|
||||
Var_Dump(VAR_CMDLINE);
|
||||
|
||||
debug_printf("\n");
|
||||
Dir_PrintDirectories();
|
||||
debug_printf("\n");
|
||||
|
||||
Suff_PrintAll();
|
||||
}
|
||||
|
||||
/* Propagate some type information to cohort nodes (those from the ::
|
||||
/* Propagate some type information to cohort nodes (those from the '::'
|
||||
* dependency operator).
|
||||
*
|
||||
* Should be called after the makefiles are parsed but before any action is
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $Id: Makefile,v 1.107 2020/11/02 00:40:25 sjg Exp $
|
||||
# $Id: Makefile,v 1.115 2020/11/18 04:01:07 sjg Exp $
|
||||
#
|
||||
# $NetBSD: Makefile,v 1.181 2020/11/01 19:02:22 rillig Exp $
|
||||
# $NetBSD: Makefile,v 1.206 2020/11/18 01:12:00 sjg Exp $
|
||||
#
|
||||
# Unit tests for make(1)
|
||||
#
|
||||
@ -36,8 +36,11 @@
|
||||
# src/tests/usr.bin/make/t_make.sh as well.
|
||||
#TESTS+= archive
|
||||
TESTS+= archive-suffix
|
||||
TESTS+= cmd-errors
|
||||
TESTS+= cmd-errors-lint
|
||||
TESTS+= cmd-interrupt
|
||||
TESTS+= cmdline
|
||||
TESTS+= cmdline-undefined
|
||||
TESTS+= comment
|
||||
TESTS+= cond-cmp-numeric
|
||||
TESTS+= cond-cmp-numeric-eq
|
||||
@ -58,8 +61,10 @@ TESTS+= cond-func-target
|
||||
TESTS+= cond-late
|
||||
TESTS+= cond-op
|
||||
TESTS+= cond-op-and
|
||||
TESTS+= cond-op-and-lint
|
||||
TESTS+= cond-op-not
|
||||
TESTS+= cond-op-or
|
||||
TESTS+= cond-op-or-lint
|
||||
TESTS+= cond-op-parentheses
|
||||
TESTS+= cond-short
|
||||
TESTS+= cond-token-number
|
||||
@ -144,6 +149,7 @@ TESTS+= directive-for
|
||||
TESTS+= directive-for-generating-endif
|
||||
TESTS+= directive-hyphen-include
|
||||
TESTS+= directive-if
|
||||
TESTS+= directive-if-nested
|
||||
TESTS+= directive-ifdef
|
||||
TESTS+= directive-ifmake
|
||||
TESTS+= directive-ifndef
|
||||
@ -156,7 +162,6 @@ TESTS+= directive-undef
|
||||
TESTS+= directive-unexport
|
||||
TESTS+= directive-unexport-env
|
||||
TESTS+= directive-warning
|
||||
TESTS+= directives
|
||||
TESTS+= dollar
|
||||
TESTS+= doterror
|
||||
TESTS+= dotwait
|
||||
@ -169,9 +174,11 @@ TESTS+= export-env
|
||||
TESTS+= export-variants
|
||||
TESTS+= forloop
|
||||
TESTS+= forsubst
|
||||
TESTS+= gnode-submake
|
||||
TESTS+= hanoi-include
|
||||
TESTS+= impsrc
|
||||
TESTS+= include-main
|
||||
TESTS+= job-flags
|
||||
#TESTS+= job-output-long-lines
|
||||
TESTS+= lint
|
||||
TESTS+= make-exported
|
||||
@ -180,6 +187,7 @@ TESTS+= modmatch
|
||||
TESTS+= modmisc
|
||||
TESTS+= modts
|
||||
TESTS+= modword
|
||||
TESTS+= objdir-writable
|
||||
TESTS+= opt
|
||||
TESTS+= opt-backwards
|
||||
TESTS+= opt-chdir
|
||||
@ -223,6 +231,7 @@ TESTS+= opt-query
|
||||
TESTS+= opt-raw
|
||||
TESTS+= opt-silent
|
||||
TESTS+= opt-touch
|
||||
TESTS+= opt-touch-jobs
|
||||
TESTS+= opt-tracefile
|
||||
TESTS+= opt-var-expanded
|
||||
TESTS+= opt-var-literal
|
||||
@ -248,7 +257,9 @@ TESTS+= sh-multi-line
|
||||
TESTS+= sh-single-line
|
||||
TESTS+= shell-csh
|
||||
TESTS+= shell-custom
|
||||
.if exists(/bin/ksh)
|
||||
TESTS+= shell-ksh
|
||||
.endif
|
||||
TESTS+= shell-sh
|
||||
TESTS+= suff-add-later
|
||||
TESTS+= suff-clear-regular
|
||||
@ -256,6 +267,7 @@ TESTS+= suff-clear-single
|
||||
TESTS+= suff-lookup
|
||||
TESTS+= suff-main
|
||||
TESTS+= suff-rebuild
|
||||
TESTS+= suff-self
|
||||
TESTS+= suff-transform-endless
|
||||
TESTS+= suff-transform-expand
|
||||
TESTS+= suff-transform-select
|
||||
@ -366,38 +378,54 @@ TESTS+= varname-makeflags
|
||||
TESTS+= varname-pwd
|
||||
TESTS+= varname-vpath
|
||||
TESTS+= varparse-dynamic
|
||||
TESTS+= varparse-errors
|
||||
TESTS+= varparse-mod
|
||||
TESTS+= varparse-undef-partial
|
||||
TESTS+= varquote
|
||||
TESTS+= varshell
|
||||
|
||||
# Ideas for more tests:
|
||||
# char-0020-space.mk
|
||||
# char-005C-backslash.mk
|
||||
# escape-cond-str.mk
|
||||
# escape-cond-func-arg.mk
|
||||
# escape-cond-func-arg.mk
|
||||
# escape-varmod.mk
|
||||
# escape-varmod-define.mk
|
||||
# escape-varmod-match.mk
|
||||
# escape-varname.mk
|
||||
# escape-varassign-varname.mk
|
||||
# escape-varassign-varname-cmdline.mk
|
||||
# escape-varassign-value.mk
|
||||
# escape-varassign-value-cmdline.mk
|
||||
# escape-dependency-source.mk
|
||||
# escape-dependency-target.mk
|
||||
# escape-for-varname.mk
|
||||
# escape-for-item.mk
|
||||
# posix-*.mk (see posix.mk and posix1.mk)
|
||||
|
||||
.if ${.OBJDIR} != ${.CURDIR}
|
||||
RO_OBJDIR:= ${.OBJDIR}/roobj
|
||||
.else
|
||||
RO_OBJDIR:= ${TMPDIR:U/tmp}/roobj
|
||||
.endif
|
||||
# Additional environment variables for some of the tests.
|
||||
# The base environment is -i PATH="$PATH".
|
||||
ENV.depsrc-optional+= TZ=UTC
|
||||
ENV.envfirst= FROM_ENV=value-from-env
|
||||
ENV.objdir-writable+= RO_OBJDIR=${RO_OBJDIR}
|
||||
ENV.varmisc= FROM_ENV=env
|
||||
ENV.varmisc+= FROM_ENV_BEFORE=env
|
||||
ENV.varmisc+= FROM_ENV_AFTER=env
|
||||
ENV.varmod-localtime+= TZ=Europe/Berlin
|
||||
ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
|
||||
|
||||
# Override make flags for some of the tests; default is -k.
|
||||
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
|
||||
# settings FLAGS.test=-dv here, since that is closer to the test code.
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none
|
||||
FLAGS.envfirst= -e
|
||||
FLAGS.export= # none
|
||||
FLAGS.opt-ignore= -i
|
||||
FLAGS.opt-keep-going= -k
|
||||
FLAGS.opt-no-action= -n
|
||||
FLAGS.opt-query= -q
|
||||
FLAGS.opt-var-expanded= -v VAR -v VALUE
|
||||
FLAGS.opt-var-literal= -V VAR -V VALUE
|
||||
FLAGS.opt-warnings-as-errors= -W
|
||||
FLAGS.order= -j1
|
||||
FLAGS.recursive= -dL
|
||||
FLAGS.sh-leading-plus= -n
|
||||
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain'
|
||||
FLAGS.doterror= # none, especially not -k
|
||||
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
|
||||
|
||||
# Some tests need extra postprocessing.
|
||||
SED_CMDS.export= \
|
||||
@ -406,6 +434,9 @@ SED_CMDS.export= \
|
||||
.for t in export-all export-env
|
||||
SED_CMDS.$t= ${SED_CMDS.export}
|
||||
.endfor
|
||||
SED_CMDS.directive-export-gmake= \
|
||||
${:D dash is a pain } \
|
||||
-e /non-zero/d
|
||||
SED_CMDS.job-output-long-lines= \
|
||||
${:D Job separators on their own line are ok. } \
|
||||
-e '/^--- job-[ab] ---$$/d' \
|
||||
@ -417,6 +448,7 @@ SED_CMDS.job-output-long-lines= \
|
||||
${:D marker should always be at the beginning of the line. } \
|
||||
-e '/^aa*--- job-b ---$$/d' \
|
||||
-e '/^bb*--- job-a ---$$/d'
|
||||
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
|
||||
SED_CMDS.opt-debug-graph1= \
|
||||
-e 's,${.CURDIR},CURDIR,'
|
||||
SED_CMDS.opt-debug-graph1+= \
|
||||
@ -428,11 +460,14 @@ SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
|
||||
# The "-q" may be there or not, see jobs.c, variable shells.
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: sh\) -q,\1,'
|
||||
SED_CMDS.var-op-shell+= -e 's,^${.SHELL:T}: ,,'
|
||||
SED_CMDS.var-op-shell+= -e '/command/{ s,^[1-9]: ,,;s,No such.*,not found,; }'
|
||||
SED_CMDS.vardebug= \
|
||||
${:D canonicalize .SHELL } \
|
||||
-e 's,${.SHELL},</path/to/shell>,'
|
||||
SED_CMDS.varmod-subst-regex+= \
|
||||
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
|
||||
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
|
||||
SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,'
|
||||
SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
|
||||
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
|
||||
@ -442,7 +477,7 @@ SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
|
||||
# Some tests need an additional round of postprocessing.
|
||||
POSTPROC.deptgt-suffixes= \
|
||||
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
|
||||
POSTPROC.varname= ${TOOL_SED} -n -e '/^MAGIC/p' -e '/^ORDER_/p'
|
||||
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
|
||||
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
|
||||
|
||||
# Some tests reuse other tests, which makes them unnecessarily fragile.
|
||||
@ -519,6 +554,8 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
|
||||
# always pretend .MAKE was called 'make'
|
||||
_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,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,'
|
||||
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
|
||||
# replace anything after 'stopped in' with unit-tests
|
||||
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
|
||||
# strip ${.CURDIR}/ from the output
|
||||
|
@ -1,11 +1,11 @@
|
||||
# $NetBSD: archive-suffix.mk,v 1.1 2020/08/29 14:47:26 rillig Exp $
|
||||
# $NetBSD: archive-suffix.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Between 2020-08-23 and 2020-08-30, the below code produced an assertion
|
||||
# failure in Var_Set_with_flags, triggered by Compat_Make, when setting the
|
||||
# failure in Var_SetWithFlags, triggered by Compat_Make, when setting the
|
||||
# .IMPSRC of an archive node to its .TARGET.
|
||||
#
|
||||
# The code assumed that the .TARGET variable of every node would be set, but
|
||||
# but that is not guaranteed.
|
||||
# that is not guaranteed.
|
||||
#
|
||||
# Between 2016-03-15 and 2016-03-16 the behavior of the below code changed.
|
||||
# Until 2016-03-15, it remade the target, starting with 2016-03-16 it says
|
||||
|
@ -1,18 +1,15 @@
|
||||
# $NetBSD: archive.mk,v 1.10 2020/10/09 06:44:42 rillig Exp $
|
||||
# $NetBSD: archive.mk,v 1.11 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Very basic demonstration of handling archives, based on the description
|
||||
# in PSD.doc/tutorial.ms.
|
||||
#
|
||||
# This test aims at covering the code, not at being an introduction to
|
||||
# archive handling. That's why it is more complicated and detailed than
|
||||
# strictly necessary.
|
||||
# archive handling. That's why it deviates from the tutorial style of
|
||||
# several other tests.
|
||||
|
||||
ARCHIVE= libprog.a
|
||||
FILES= archive.mk modmisc.mk varmisc.mk
|
||||
|
||||
MAKE_CMD= ${.MAKE} -f ${MAKEFILE}
|
||||
RUN?= @set -eu;
|
||||
|
||||
all:
|
||||
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
|
||||
@cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
|
||||
@ -20,13 +17,13 @@ all:
|
||||
# The following targets create and remove files. The filesystem cache in
|
||||
# dir.c would probably not handle this correctly, therefore each of the
|
||||
# targets is run in its separate sub-make.
|
||||
${RUN} ${MAKE_CMD} remove-archive
|
||||
${RUN} ${MAKE_CMD} create-archive
|
||||
${RUN} ${MAKE_CMD} list-archive
|
||||
${RUN} ${MAKE_CMD} list-archive-wildcard
|
||||
${RUN} ${MAKE_CMD} depend-on-existing-member
|
||||
${RUN} ${MAKE_CMD} depend-on-nonexistent-member
|
||||
${RUN} ${MAKE_CMD} remove-archive
|
||||
@${MAKE} -f ${MAKEFILE} remove-archive
|
||||
@${MAKE} -f ${MAKEFILE} create-archive
|
||||
@${MAKE} -f ${MAKEFILE} list-archive
|
||||
@${MAKE} -f ${MAKEFILE} list-archive-wildcard
|
||||
@${MAKE} -f ${MAKEFILE} depend-on-existing-member
|
||||
@${MAKE} -f ${MAKEFILE} depend-on-nonexistent-member
|
||||
@${MAKE} -f ${MAKEFILE} remove-archive
|
||||
|
||||
create-archive: ${ARCHIVE} pre post
|
||||
|
||||
@ -43,15 +40,16 @@ list-archive: ${ARCHIVE} pre post
|
||||
|
||||
# XXX: I had expected that this dependency would select all *.mk files from
|
||||
# the archive. Instead, the globbing is done in the current directory.
|
||||
#
|
||||
# To prevent an overly long file list, the pattern is restricted to [at]*.mk.
|
||||
list-archive-wildcard: ${ARCHIVE}([at]*.mk) pre post
|
||||
${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
|
||||
@printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
|
||||
|
||||
depend-on-existing-member: ${ARCHIVE}(archive.mk) pre post
|
||||
${RUN} echo $@
|
||||
@echo $@
|
||||
|
||||
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) pre post
|
||||
${RUN} echo $@
|
||||
@echo $@
|
||||
|
||||
remove-archive: pre post
|
||||
rm -f ${ARCHIVE}
|
||||
|
9
unit-tests/cmd-errors-lint.exp
Normal file
9
unit-tests/cmd-errors-lint.exp
Normal file
@ -0,0 +1,9 @@
|
||||
: undefined
|
||||
make: Unclosed variable "UNCLOSED"
|
||||
: unclosed-variable
|
||||
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
|
||||
: unclosed-modifier
|
||||
make: Unknown modifier 'Z'
|
||||
: unknown-modifier
|
||||
: end
|
||||
exit status 2
|
32
unit-tests/cmd-errors-lint.mk
Normal file
32
unit-tests/cmd-errors-lint.mk
Normal file
@ -0,0 +1,32 @@
|
||||
# $NetBSD: cmd-errors-lint.mk,v 1.1 2020/11/02 20:43:27 rillig Exp $
|
||||
#
|
||||
# Demonstrate how errors in variable expansions affect whether the commands
|
||||
# are actually executed.
|
||||
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
|
||||
|
||||
# Undefined variables are not an error. They expand to empty strings.
|
||||
undefined:
|
||||
: $@ ${UNDEFINED}
|
||||
|
||||
# XXX: As of 2020-11-01, this obvious syntax error is not detected.
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-variable:
|
||||
: $@ ${UNCLOSED
|
||||
|
||||
# XXX: As of 2020-11-01, this obvious syntax error is not detected.
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-modifier:
|
||||
: $@ ${UNCLOSED:
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unknown-modifier:
|
||||
: $@ ${UNKNOWN:Z}
|
||||
|
||||
end:
|
||||
: $@
|
9
unit-tests/cmd-errors.exp
Normal file
9
unit-tests/cmd-errors.exp
Normal file
@ -0,0 +1,9 @@
|
||||
: undefined eol
|
||||
make: Unclosed variable "UNCLOSED"
|
||||
: unclosed-variable
|
||||
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
|
||||
: unclosed-modifier
|
||||
make: Unknown modifier 'Z'
|
||||
: unknown-modifier eol
|
||||
: end eol
|
||||
exit status 0
|
30
unit-tests/cmd-errors.mk
Normal file
30
unit-tests/cmd-errors.mk
Normal file
@ -0,0 +1,30 @@
|
||||
# $NetBSD: cmd-errors.mk,v 1.3 2020/11/09 23:36:34 rillig Exp $
|
||||
#
|
||||
# Demonstrate how errors in variable expansions affect whether the commands
|
||||
# are actually executed.
|
||||
|
||||
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
|
||||
|
||||
# Undefined variables are not an error. They expand to empty strings.
|
||||
undefined:
|
||||
: $@ ${UNDEFINED} eol
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-variable:
|
||||
: $@ ${UNCLOSED
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-modifier:
|
||||
: $@ ${UNCLOSED:
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unknown-modifier:
|
||||
: $@ ${UNKNOWN:Z} eol
|
||||
|
||||
end:
|
||||
: $@ eol
|
||||
|
||||
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cmd-interrupt.mk,v 1.2 2020/08/28 18:16:22 rillig Exp $
|
||||
# $NetBSD: cmd-interrupt.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for interrupting a command.
|
||||
#
|
||||
@ -22,7 +22,7 @@ all: clean-before interrupt-ordinary interrupt-phony interrupt-precious clean-af
|
||||
clean-before clean-after: .PHONY
|
||||
@rm -f cmd-interrupt-ordinary cmd-interrupt-phony cmd-interrupt-precious
|
||||
|
||||
interrupt-ordinary: .PHONY
|
||||
interrupt-ordinary:
|
||||
@${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-ordinary || true
|
||||
# The ././ is necessary to work around the file cache.
|
||||
@echo ${.TARGET}: ${exists(././cmd-interrupt-ordinary) :? error : ok }
|
||||
|
17
unit-tests/cmdline-undefined.exp
Normal file
17
unit-tests/cmdline-undefined.exp
Normal file
@ -0,0 +1,17 @@
|
||||
The = assignment operator
|
||||
make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
|
||||
make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
|
||||
make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
|
||||
make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
|
||||
make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
|
||||
make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
|
||||
|
||||
The := assignment operator
|
||||
make: "cmdline-undefined.mk" line 29: From the command line: Undefined is .
|
||||
make: "cmdline-undefined.mk" line 30: From .MAKEFLAGS '=': Undefined is .
|
||||
make: "cmdline-undefined.mk" line 31: From .MAKEFLAGS ':=': Undefined is .
|
||||
make: "cmdline-undefined.mk" line 35: From the command line: Undefined is now defined.
|
||||
make: "cmdline-undefined.mk" line 36: From .MAKEFLAGS '=': Undefined is now defined.
|
||||
make: "cmdline-undefined.mk" line 37: From .MAKEFLAGS ':=': Undefined is now defined.
|
||||
|
||||
exit status 0
|
40
unit-tests/cmdline-undefined.mk
Normal file
40
unit-tests/cmdline-undefined.mk
Normal file
@ -0,0 +1,40 @@
|
||||
# $NetBSD: cmdline-undefined.mk,v 1.2 2020/11/04 04:49:33 rillig Exp $
|
||||
#
|
||||
# Tests for undefined variable expressions in the command line.
|
||||
|
||||
all:
|
||||
# When the command line is parsed, variable assignments using the
|
||||
# '=' assignment operator do get their variable name expanded
|
||||
# (which probably occurs rarely in practice, if at all), but their
|
||||
# variable value is not expanded, as usual.
|
||||
#
|
||||
@echo 'The = assignment operator'
|
||||
@${.MAKE} -f ${MAKEFILE} print-undefined \
|
||||
CMDLINE='Undefined is $${UNDEFINED}.'
|
||||
@echo
|
||||
|
||||
# The interesting case is using the ':=' assignment operator, which
|
||||
# expands its right-hand side. But only those variables that are
|
||||
# defined.
|
||||
@echo 'The := assignment operator'
|
||||
@${.MAKE} -f ${MAKEFILE} print-undefined \
|
||||
CMDLINE:='Undefined is $${UNDEFINED}.'
|
||||
@echo
|
||||
|
||||
.if make(print-undefined)
|
||||
|
||||
.MAKEFLAGS: MAKEFLAGS_ASSIGN='Undefined is $${UNDEFINED}.'
|
||||
.MAKEFLAGS: MAKEFLAGS_SUBST:='Undefined is $${UNDEFINED}.'
|
||||
|
||||
.info From the command line: ${CMDLINE}
|
||||
.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
|
||||
.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
|
||||
|
||||
UNDEFINED?= now defined
|
||||
|
||||
.info From the command line: ${CMDLINE}
|
||||
.info From .MAKEFLAGS '=': ${MAKEFLAGS_ASSIGN}
|
||||
.info From .MAKEFLAGS ':=': ${MAKEFLAGS_SUBST}
|
||||
|
||||
print-undefined:
|
||||
.endif
|
@ -1,8 +1,7 @@
|
||||
# $NetBSD: cmdline.mk,v 1.1 2020/07/28 22:44:44 rillig Exp $
|
||||
# $NetBSD: cmdline.mk,v 1.2 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for command line parsing and related special variables.
|
||||
|
||||
RUN?= @set -eu;
|
||||
TMPBASE?= /tmp
|
||||
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
|
||||
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
|
||||
@ -14,14 +13,14 @@ all: prepare-dirs
|
||||
all: makeobjdir-direct makeobjdir-indirect
|
||||
|
||||
prepare-dirs:
|
||||
${RUN} rm -rf ${DIR2} ${DIR12}
|
||||
${RUN} mkdir -p ${DIR2} ${DIR12}
|
||||
@rm -rf ${DIR2} ${DIR12}
|
||||
@mkdir -p ${DIR2} ${DIR12}
|
||||
|
||||
# The .OBJDIR can be set via the MAKEOBJDIR command line variable.
|
||||
# It must be a command line variable; an environment variable would not work.
|
||||
makeobjdir-direct:
|
||||
@echo $@:
|
||||
${RUN} ${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
|
||||
@${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir
|
||||
|
||||
# The .OBJDIR can be set via the MAKEOBJDIR command line variable,
|
||||
# and that variable could even contain the usual modifiers.
|
||||
@ -31,7 +30,7 @@ makeobjdir-direct:
|
||||
# see MAKE_CMD.
|
||||
makeobjdir-indirect:
|
||||
@echo $@:
|
||||
${RUN} ${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
|
||||
@${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir
|
||||
|
||||
show-objdir:
|
||||
@echo $@: ${.OBJDIR:Q}
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: comment.mk,v 1.2 2020/09/07 19:17:36 rillig Exp $
|
||||
# $NetBSD: comment.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Demonstrate how comments are written in makefiles.
|
||||
|
||||
@ -12,7 +12,7 @@ that \
|
||||
goes \
|
||||
on and on.
|
||||
|
||||
# Comments can be indented, but that is rather unusual.
|
||||
# Comments can be indented with spaces, but that is rather unusual.
|
||||
|
||||
# Comments can be indented with a tab.
|
||||
# These are not shell commands, they are just makefile comments.
|
||||
@ -21,6 +21,8 @@ on and on.
|
||||
.endif # And after the closing directive.
|
||||
|
||||
VAR= # This comment makes the variable value empty.
|
||||
# ParseGetLine removes any whitespace before the
|
||||
# comment.
|
||||
.if ${VAR} != ""
|
||||
. error
|
||||
.endif
|
||||
@ -35,7 +37,9 @@ VAR= value
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This is NOT an escaped comment due to the double backslashes \\
|
||||
# This comment ends with 2 backslashes. An even number of backslashes does
|
||||
# not count as a line continuation, therefore the variable assignment that
|
||||
# follows is actively interpreted. \\
|
||||
VAR= not part of the comment
|
||||
.if ${VAR} != "not part of the comment"
|
||||
. error
|
||||
@ -55,7 +59,7 @@ WORDS= ${VAR:[#]} [#
|
||||
. error
|
||||
.endif
|
||||
|
||||
# An odd number of comment signs makes a line continuation, \\\
|
||||
# An odd number of backslashes makes a line continuation, \\\
|
||||
no matter if it is 3 or 5 \\\\\
|
||||
or 9 backslashes. \\\\\\\\\
|
||||
This is the last line of the comment.
|
||||
|
@ -1,6 +1,6 @@
|
||||
make: "cond-cmp-numeric-eq.mk" line 54: warning: Unknown operator
|
||||
make: "cond-cmp-numeric-eq.mk" line 54: Malformed conditional (!(12345 = 12345))
|
||||
make: "cond-cmp-numeric-eq.mk" line 61: Malformed conditional (!(12345 === 12345))
|
||||
make: "cond-cmp-numeric-eq.mk" line 67: warning: Unknown operator
|
||||
make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345))
|
||||
make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345))
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.5 2020/11/08 21:47:59 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the == operator in .if conditions.
|
||||
|
||||
@ -49,6 +49,19 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Because an IEEE 754 double can only hold integers with a mantissa of 53
|
||||
# bits, these two numbers are considered the same. The 993 is rounded down
|
||||
# to the 992.
|
||||
.if 9007199254740993 == 9007199254740992
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
# The 995 is rounded up, the 997 is rounded down.
|
||||
.if 9007199254740995 == 9007199254740997
|
||||
.else
|
||||
. error Probably a misconfiguration in the floating point environment, \
|
||||
or maybe a machine without IEEE 754 floating point support.
|
||||
.endif
|
||||
|
||||
# There is no = operator for numbers.
|
||||
.if !(12345 = 12345)
|
||||
|
@ -6,6 +6,10 @@ make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be
|
||||
make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN)
|
||||
CondParser_Eval: !(${:UNaN} == NaN)
|
||||
lhs = "NaN", rhs = "NaN", op = ==
|
||||
CondParser_Eval: 123 ! 123
|
||||
lhs = 123.000000, rhs = 123.000000, op = !
|
||||
make: "cond-cmp-numeric.mk" line 34: warning: Unknown operator
|
||||
make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-cmp-numeric.mk,v 1.3 2020/09/12 18:01:51 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric.mk,v 1.4 2020/11/08 22:56:16 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons in .if conditions.
|
||||
|
||||
@ -25,5 +25,17 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The parsing code in CondParser_Comparison only performs a light check on
|
||||
# whether the operator is valid, leaving the rest of the work to the
|
||||
# evaluation functions EvalCompareNum and EvalCompareStr. Ensure that this
|
||||
# parse error is properly reported.
|
||||
#
|
||||
# XXX: The warning message does not mention the actual operator.
|
||||
.if 123 ! 123
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,8 +1,8 @@
|
||||
make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
|
||||
make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing")
|
||||
make: "cond-cmp-string.mk" line 42: warning: String comparison operator must be either == or !=
|
||||
make: "cond-cmp-string.mk" line 42: Malformed conditional (!("value" = "value"))
|
||||
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" === "value"))
|
||||
make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing")
|
||||
make: "cond-cmp-string.mk" line 49: warning: String comparison operator must be either == or !=
|
||||
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value"))
|
||||
make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value"))
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-cmp-string.mk,v 1.11 2020/10/30 14:53:31 rillig Exp $
|
||||
# $NetBSD: cond-cmp-string.mk,v 1.13 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for string comparisons in .if conditions.
|
||||
|
||||
@ -19,9 +19,14 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The left-hand side of the comparison requires a defined variable.
|
||||
# The variable named "" is not defined, but applying the :U modifier to it
|
||||
# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here.
|
||||
# The left-hand side of the comparison requires that any variable 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
|
||||
# an undefined expression turns it into a defined expression.
|
||||
#
|
||||
# See ApplyModifier_Defined and VEF_DEF.
|
||||
.if ${:Ustr} != "str"
|
||||
. error
|
||||
.endif
|
||||
@ -33,9 +38,11 @@
|
||||
.endif
|
||||
|
||||
# It is not possible to concatenate two string literals to form a single
|
||||
# string.
|
||||
# string. In C, Python and the shell this is possible, but not in make.
|
||||
.if "string" != "str""ing"
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no = operator for strings.
|
||||
@ -88,3 +95,16 @@
|
||||
.if ${:U word } != " ${:Uword} "
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If at least one side of the comparison is a string literal, the string
|
||||
# comparison is performed.
|
||||
.if 12345 != "12345"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If at least one side of the comparison is a string literal, the string
|
||||
# comparison is performed. The ".0" in the left-hand side makes the two
|
||||
# sides of the equation unequal.
|
||||
.if 12345.0 == "12345"
|
||||
. error
|
||||
.endif
|
||||
|
@ -1 +1,2 @@
|
||||
make: "cond-cmp-unary.mk" line 53: This is only reached because of a bug in EvalNotEmpty.
|
||||
exit status 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-cmp-unary.mk,v 1.1 2020/09/14 06:22:59 rillig Exp $
|
||||
# $NetBSD: cond-cmp-unary.mk,v 1.2 2020/11/11 07:30:11 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,
|
||||
@ -25,6 +25,9 @@
|
||||
.endif
|
||||
|
||||
# The empty string may come from a variable expression.
|
||||
#
|
||||
# XXX: As of 2020-11-11, this empty string is interpreted "as a number" in
|
||||
# EvalNotEmpty, which is plain wrong. The bug is in TryParseNumber.
|
||||
.if ${:U}
|
||||
. error
|
||||
.endif
|
||||
@ -40,4 +43,16 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A string of whitespace should evaluate to false.
|
||||
#
|
||||
# XXX: As of 2020-11-11, the implementation in EvalNotEmpty does not skip
|
||||
# whitespace before testing for the end. This was probably an oversight in
|
||||
# a commit from 1992-04-15 saying "A variable is empty when it just contains
|
||||
# spaces".
|
||||
.if ${:U }
|
||||
. info This is only reached because of a bug in EvalNotEmpty.
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all: # nothing
|
||||
|
@ -1,10 +1,11 @@
|
||||
# $NetBSD: cond-func-commands.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-func-commands.mk,v 1.5 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for the commands() function in .if conditions.
|
||||
|
||||
.MAIN: all
|
||||
|
||||
# The target "target" does not exist yet, therefore it cannot have commands.
|
||||
# At this point, the target 'target' does not exist yet, therefore it cannot
|
||||
# have commands. Sounds obvious, but good to know that it is really so.
|
||||
.if commands(target)
|
||||
. error
|
||||
.endif
|
||||
|
@ -1,5 +1,10 @@
|
||||
make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B))
|
||||
make: "cond-func-defined.mk" line 33: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func-defined.mk" line 33: Malformed conditional (defined(DEF)
|
||||
make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are
|
||||
make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable
|
||||
make: "cond-func-defined.mk" line 47: involved, even if it feels like it.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-defined.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-func-defined.mk,v 1.7 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for the defined() function in .if conditions.
|
||||
|
||||
@ -29,5 +29,24 @@ ${:UA B}= variable name with spaces
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Parse error: missing closing parenthesis; see ParseFuncArg.
|
||||
.if defined(DEF
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Variables from .for loops are not defined.
|
||||
# See directive-for.mk for more details.
|
||||
.for var in value
|
||||
. if defined(var)
|
||||
. error
|
||||
. else
|
||||
. info In .for loops, variable expressions for the loop variables are
|
||||
. info substituted at evaluation time. There is no actual variable
|
||||
. info involved, even if it feels like it.
|
||||
. endif
|
||||
.endfor
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1 +1,5 @@
|
||||
exit status 0
|
||||
make: "cond-func-empty.mk" line 152: Unclosed variable "WORD"
|
||||
make: "cond-func-empty.mk" line 152: Malformed conditional (empty(WORD)
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,11 +1,10 @@
|
||||
# $NetBSD: cond-func-empty.mk,v 1.8 2020/09/23 08:11:28 rillig Exp $
|
||||
# $NetBSD: cond-func-empty.mk,v 1.10 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for the empty() function in .if conditions, which tests a variable
|
||||
# expression for emptiness.
|
||||
#
|
||||
# Note that the argument in the parentheses is indeed a variable name,
|
||||
# optionally followed by variable modifiers. This is like the defined()
|
||||
# function.
|
||||
# optionally followed by variable modifiers.
|
||||
#
|
||||
|
||||
.undef UNDEF
|
||||
@ -25,13 +24,15 @@ WORD= word
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The :S modifier replaces the empty value with an actual word, and
|
||||
# after that the expression is no longer empty. Because the variable
|
||||
# was undefined in the first place, the expression has the flag VAR_JUNK
|
||||
# but not VAR_KEEP, therefore it is still considered undefined.
|
||||
# Only very few variable modifiers turn an undefined variable expression
|
||||
# into a defined variable expression. The :U and :D modifiers belong to
|
||||
# that group, but :S doesn't (see VAR_KEEP).
|
||||
# The :S modifier replaces the empty value with an actual word. The
|
||||
# expression is now no longer empty, but it is still possible to see whether
|
||||
# the expression was based on an undefined variable. The expression has the
|
||||
# flag VEF_UNDEF.
|
||||
#
|
||||
# The expression does not have the flag VEF_DEF though, therefore it is still
|
||||
# considered undefined. Yes, indeed, undefined but not empty. There are a
|
||||
# few variable modifiers that turn an undefined expression into a defined
|
||||
# expression, among them :U and :D, but not :S.
|
||||
#
|
||||
# XXX: This is hard to explain to someone who doesn't know these
|
||||
# implementation details.
|
||||
@ -49,13 +50,14 @@ WORD= word
|
||||
.endif
|
||||
|
||||
# And now to the surprising part. Applying the following :S modifier to the
|
||||
# undefined variable makes it non-empty, but the marker VAR_JUNK is preserved
|
||||
# nevertheless. The :U modifier that follows only looks at VAR_JUNK to decide
|
||||
# whether the variable is defined or not. This kind of makes sense since the
|
||||
# :U modifier tests the _variable_, not the _expression_.
|
||||
# undefined expression makes it non-empty, but the marker VEF_UNDEF is
|
||||
# preserved nevertheless. The :U modifier that follows only looks at the
|
||||
# VEF_UNDEF flag to decide whether the variable is defined or not. This kind
|
||||
# of makes sense since the :U modifier tests the _variable_, not the
|
||||
# _expression_.
|
||||
#
|
||||
# But since the variable was undefined to begin with, the fallback value is
|
||||
# used in this expression.
|
||||
# But since the variable was undefined to begin with, the fallback value from
|
||||
# the :U modifier is used in this expression.
|
||||
#
|
||||
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
|
||||
. error
|
||||
@ -128,7 +130,7 @@ ${:U }= space
|
||||
# If everything goes well, the argument expands to "WORD", and that variable
|
||||
# is defined at the beginning of this file. The surrounding 'W' and 'D'
|
||||
# ensure that the parser in ParseEmptyArg has the correct position, both
|
||||
# before and after the call to Var_ParsePP.
|
||||
# before and after the call to Var_Parse.
|
||||
.if empty(W${:UOR}D)
|
||||
. error
|
||||
.endif
|
||||
@ -146,5 +148,12 @@ ${:U WORD }= variable name with spaces
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Parse error: missing closing parenthesis.
|
||||
.if empty(WORD
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,9 +1,15 @@
|
||||
make: "cond-func.mk" line 29: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 29: Malformed conditional (!defined(A B))
|
||||
make: "cond-func.mk" line 44: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 44: Malformed conditional (!defined(A&B))
|
||||
make: "cond-func.mk" line 47: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 47: Malformed conditional (!defined(A|B))
|
||||
make: "cond-func.mk" line 36: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 36: Malformed conditional (!defined(A B))
|
||||
make: "cond-func.mk" line 51: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 51: Malformed conditional (!defined(A&B))
|
||||
make: "cond-func.mk" line 54: warning: Missing closing parenthesis for defined()
|
||||
make: "cond-func.mk" line 54: Malformed conditional (!defined(A|B))
|
||||
make: "cond-func.mk" line 94: The empty variable is never defined.
|
||||
make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
|
||||
make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).
|
||||
make: "cond-func.mk" line 119: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 124: Symbols may start with a function name.
|
||||
make: "cond-func.mk" line 130: Malformed conditional (defined()
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,15 +1,22 @@
|
||||
# $NetBSD: cond-func.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-func.mk,v 1.9 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Tests for those parts of the functions in .if conditions that are common
|
||||
# among several functions.
|
||||
#
|
||||
# The below test uses the function defined(...) since it has no side-effects,
|
||||
# the other functions (except empty(...)) would work equally well.
|
||||
# the other functions (except empty(...)) would work equally well. The
|
||||
# function empty is special because it uses a different parsing algorithm for
|
||||
# its argument.
|
||||
|
||||
DEF= defined
|
||||
${:UA B}= variable name with spaces
|
||||
${:UVAR(value)}= variable name with parentheses
|
||||
${:UVAR{value}}= variable name with braces
|
||||
${:UVAR{value}}= variable name with balanced braces
|
||||
|
||||
# Really strange variable names must be given indirectly via another variable,
|
||||
# so that no unbalanced braces appear in the top-level expression.
|
||||
VARNAME_UNBALANCED_BRACES= VAR{{{value
|
||||
${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
|
||||
|
||||
.if !defined(DEF)
|
||||
. error
|
||||
@ -59,6 +66,12 @@ ${:UVAR{value}}= variable name with braces
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Braces do not have any special meaning when parsing arguments.
|
||||
# They don't need to be balanced.
|
||||
.if !defined(VAR{{{value)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There may be spaces around the operators and parentheses, and even
|
||||
# inside the parentheses. The spaces inside the parentheses are not
|
||||
# allowed for the empty() function (see cond-func-empty.mk), therefore
|
||||
@ -67,5 +80,58 @@ ${:UVAR{value}}= variable name with braces
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The following condition is interpreted as defined(A) && defined(B).
|
||||
# In lack of a function call expression, each kind of .if directive has a
|
||||
# default function that is called when a bare word is parsed. For the plain
|
||||
# .if directive, this function is defined(); see "struct If ifs" in cond.c.
|
||||
.if A&B
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if defined()
|
||||
. error
|
||||
.else
|
||||
. info The empty variable is never defined.
|
||||
.endif
|
||||
|
||||
# The plain word 'defined' is interpreted as '!empty(defined)'.
|
||||
# That variable is not defined (yet).
|
||||
.if defined
|
||||
. error
|
||||
.else
|
||||
. info A plain function name is parsed as !empty(...).
|
||||
.endif
|
||||
|
||||
# If a variable named 'defined' is actually defined and not empty, the plain
|
||||
# symbol 'defined' evaluates to true.
|
||||
defined= non-empty
|
||||
.if defined
|
||||
. info A plain function name is parsed as !empty(...).
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A plain symbol name may start with one of the function names, in this case
|
||||
# 'defined'.
|
||||
.if defined-var
|
||||
. error
|
||||
.else
|
||||
. info Symbols may start with a function name.
|
||||
.endif
|
||||
|
||||
defined-var= non-empty
|
||||
.if defined-var
|
||||
. info Symbols may start with a function name.
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Missing closing parenthesis when parsing the function argument.
|
||||
.if defined(
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,7 +1,9 @@
|
||||
# $NetBSD: cond-late.mk,v 1.2 2020/07/25 20:37:46 rillig Exp $
|
||||
# $NetBSD: cond-late.mk,v 1.3 2020/11/15 14:07:53 rillig Exp $
|
||||
#
|
||||
# Using the :? modifier, variable expressions can contain conditional
|
||||
# expressions that are evaluated late. Any variables appearing in these
|
||||
# expressions that are evaluated late, at expansion time.
|
||||
#
|
||||
# Any variables appearing in these
|
||||
# conditions are expanded before parsing the condition. This is
|
||||
# different from many other places.
|
||||
#
|
||||
@ -11,15 +13,15 @@
|
||||
# They should also not contain operators like == or <, since these are
|
||||
# actually interpreted as these operators. This is demonstrated below.
|
||||
#
|
||||
# If the order of evaluation were to change to first parse the condition
|
||||
# and then expand the variables, the output would change from the
|
||||
# current "yes no" to "yes yes", since both variables are non-empty.
|
||||
|
||||
all: cond-literal
|
||||
|
||||
COND.true= "yes" == "yes"
|
||||
COND.false= "yes" != "yes"
|
||||
|
||||
# If the order of evaluation were to change to first parse the condition
|
||||
# and then expand the variables, the output would change from the
|
||||
# current "yes no" to "yes yes", since both variables are non-empty.
|
||||
cond-literal:
|
||||
@echo ${ ${COND.true} :?yes:no}
|
||||
@echo ${ ${COND.false} :?yes:no}
|
||||
|
4
unit-tests/cond-op-and-lint.exp
Normal file
4
unit-tests/cond-op-and-lint.exp
Normal file
@ -0,0 +1,4 @@
|
||||
make: "cond-op-and-lint.mk" line 9: Unknown operator '&'
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
13
unit-tests/cond-op-and-lint.mk
Normal file
13
unit-tests/cond-op-and-lint.mk
Normal file
@ -0,0 +1,13 @@
|
||||
# $NetBSD: cond-op-and-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
|
||||
#
|
||||
# Tests for the && operator in .if conditions, in lint mode.
|
||||
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
# The '&' operator is not allowed in lint mode.
|
||||
# It is not used in practice anyway.
|
||||
.if 0 & 0
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
@ -1 +1,6 @@
|
||||
make: "cond-op-not.mk" line 29: Not empty evaluates to true.
|
||||
make: "cond-op-not.mk" line 37: Not space evaluates to false.
|
||||
make: "cond-op-not.mk" line 41: Not 0 evaluates to true.
|
||||
make: "cond-op-not.mk" line 49: Not 1 evaluates to false.
|
||||
make: "cond-op-not.mk" line 55: Not word evaluates to false.
|
||||
exit status 0
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $NetBSD: cond-op-not.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-op-not.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for the ! operator in .if conditions.
|
||||
# Tests for the ! operator in .if conditions, which negates its argument.
|
||||
|
||||
# The exclamation mark negates its operand.
|
||||
.if !1
|
||||
@ -18,5 +18,42 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The operator '==' binds more tightly than '!'.
|
||||
# This is unusual since most other programming languages define the precedence
|
||||
# to be the other way round.
|
||||
.if !${:Uexpression} == "expression"
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !${:U}
|
||||
. info Not empty evaluates to true.
|
||||
.else
|
||||
. info Not empty evaluates to false.
|
||||
.endif
|
||||
|
||||
.if !${:U }
|
||||
. info Not space evaluates to true.
|
||||
.else
|
||||
. info Not space evaluates to false.
|
||||
.endif
|
||||
|
||||
.if !${:U0}
|
||||
. info Not 0 evaluates to true.
|
||||
.else
|
||||
. info Not 0 evaluates to false.
|
||||
.endif
|
||||
|
||||
.if !${:U1}
|
||||
. info Not 1 evaluates to true.
|
||||
.else
|
||||
. info Not 1 evaluates to false.
|
||||
.endif
|
||||
|
||||
.if !${:Uword}
|
||||
. info Not word evaluates to true.
|
||||
.else
|
||||
. info Not word evaluates to false.
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
4
unit-tests/cond-op-or-lint.exp
Normal file
4
unit-tests/cond-op-or-lint.exp
Normal file
@ -0,0 +1,4 @@
|
||||
make: "cond-op-or-lint.mk" line 9: Unknown operator '|'
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
13
unit-tests/cond-op-or-lint.mk
Normal file
13
unit-tests/cond-op-or-lint.mk
Normal file
@ -0,0 +1,13 @@
|
||||
# $NetBSD: cond-op-or-lint.mk,v 1.1 2020/11/08 23:54:28 rillig Exp $
|
||||
#
|
||||
# Tests for the || operator in .if conditions, in lint mode.
|
||||
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
# The '|' operator is not allowed in lint mode.
|
||||
# It is not used in practice anyway.
|
||||
.if 0 | 0
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
@ -1 +1,2 @@
|
||||
make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112.
|
||||
exit status 0
|
||||
|
@ -1,8 +1,19 @@
|
||||
# $NetBSD: cond-op-parentheses.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-op-parentheses.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for parentheses in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
# Test for deeply nested conditions.
|
||||
.if (((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
|
||||
(((((((((((((((((((((((((((((((((((((((((((((((((((((((( \
|
||||
1 \
|
||||
)))))))))))))))))))))))))))))))))))))))))))))))))))))))) \
|
||||
))))))))))))))))))))))))))))))))))))))))))))))))))))))))
|
||||
. info Parentheses can be nested at least to depth 112.
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,7 +1,16 @@
|
||||
make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word)
|
||||
make: "cond-op.mk" line 70: Malformed conditional (0 ${ERR::=evaluated})
|
||||
make: "cond-op.mk" line 74: warning: After detecting a parse error, the rest is evaluated.
|
||||
make: "cond-op.mk" line 78: Parsing continues until here.
|
||||
make: "cond-op.mk" line 50: Malformed conditional ("!word" == !word)
|
||||
make: "cond-op.mk" line 75: Malformed conditional (0 ${ERR::=evaluated})
|
||||
make: "cond-op.mk" line 79: After detecting a parse error, the rest is evaluated.
|
||||
make: "cond-op.mk" line 83: Parsing continues until here.
|
||||
make: "cond-op.mk" line 86: A B C => (A || B) && C A || B && C A || (B && C)
|
||||
make: "cond-op.mk" line 93: 0 0 0 => 0 0 0
|
||||
make: "cond-op.mk" line 93: 0 0 1 => 0 0 0
|
||||
make: "cond-op.mk" line 93: 0 1 0 => 0 0 0
|
||||
make: "cond-op.mk" line 93: 0 1 1 => 1 1 1
|
||||
make: "cond-op.mk" line 93: 1 0 0 => 0 1 1
|
||||
make: "cond-op.mk" line 93: 1 0 1 => 1 1 1
|
||||
make: "cond-op.mk" line 93: 1 1 0 => 0 1 1
|
||||
make: "cond-op.mk" line 93: 1 1 1 => 1 1 1
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-op.mk,v 1.8 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-op.mk,v 1.10 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for operators like &&, ||, ! in .if conditions.
|
||||
#
|
||||
@ -9,8 +9,8 @@
|
||||
# cond-op-parentheses.mk
|
||||
|
||||
# In make, && binds more tightly than ||, like in C.
|
||||
# If make had the same precedence for both && and ||, the result would be
|
||||
# different.
|
||||
# If make had the same precedence for both && and ||, like in the shell,
|
||||
# the result would be different.
|
||||
# If || were to bind more tightly than &&, the result would be different
|
||||
# as well.
|
||||
.if !(1 || 1 && 0)
|
||||
@ -18,13 +18,17 @@
|
||||
.endif
|
||||
|
||||
# If make were to interpret the && and || operators like the shell, the
|
||||
# implicit binding would be this:
|
||||
# previous condition would be interpreted as:
|
||||
.if (1 || 1) && 0
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The precedence of the ! operator is different from C though. It has a
|
||||
# lower precedence than the comparison operators.
|
||||
# lower precedence than the comparison operators. Negating a condition
|
||||
# does not need parentheses.
|
||||
#
|
||||
# This kind of condition looks so unfamiliar that it doesn't occur in
|
||||
# practice.
|
||||
.if !"word" == "word"
|
||||
. error
|
||||
.endif
|
||||
@ -36,7 +40,8 @@
|
||||
|
||||
# TODO: Demonstrate that the precedence of the ! and == operators actually
|
||||
# makes a difference. There is a simple example for sure, I just cannot
|
||||
# wrap my head around it.
|
||||
# wrap my head around it right now. See the truth table generator below
|
||||
# for an example that doesn't require much thought.
|
||||
|
||||
# This condition is malformed because the '!' on the right-hand side must not
|
||||
# appear unquoted. If any, it must be enclosed in quotes.
|
||||
@ -71,11 +76,27 @@
|
||||
. error
|
||||
.endif
|
||||
.if ${ERR:Uundefined} == evaluated
|
||||
. warning After detecting a parse error, the rest is evaluated.
|
||||
. info After detecting a parse error, the rest is evaluated.
|
||||
.endif
|
||||
|
||||
# Just in case that parsing should ever stop on the first error.
|
||||
.info Parsing continues until here.
|
||||
|
||||
# Demonstration that '&&' has higher precedence than '||'.
|
||||
.info A B C => (A || B) && C A || B && C A || (B && C)
|
||||
.for a in 0 1
|
||||
. for b in 0 1
|
||||
. for c in 0 1
|
||||
. for r1 in ${ ($a || $b) && $c :?1:0}
|
||||
. for r2 in ${ $a || $b && $c :?1:0}
|
||||
. for r3 in ${ $a || ($b && $c) :?1:0}
|
||||
. info $a $b $c => ${r1} ${r2} ${r3}
|
||||
. endfor
|
||||
. endfor
|
||||
. endfor
|
||||
. endfor
|
||||
. endfor
|
||||
.endfor
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,11 +1,14 @@
|
||||
# $NetBSD: cond-short.mk,v 1.11 2020/10/24 08:50:17 rillig Exp $
|
||||
# $NetBSD: cond-short.mk,v 1.12 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Demonstrates that in conditions, the right-hand side of an && or ||
|
||||
# is only evaluated if it can actually influence the result.
|
||||
# This is called 'short-circuit evaluation' and is the usual evaluation
|
||||
# mode in most programming languages. A notable exception is Ada, which
|
||||
# distinguishes between the operators 'And', 'And Then', 'Or', 'Or Else'.
|
||||
#
|
||||
# Between 2015-10-11 and 2020-06-28, the right-hand side of an && or ||
|
||||
# operator was always evaluated, which was wrong.
|
||||
#
|
||||
# TODO: Had the evaluation been correct at some time before 2015-11-12?
|
||||
|
||||
# The && operator.
|
||||
|
||||
@ -113,6 +116,9 @@ VAR= # empty again, for the following tests
|
||||
# make sure these do not cause complaint
|
||||
#.MAKEFLAGS: -dc
|
||||
|
||||
# TODO: Rewrite this whole section and check all the conditions and variables.
|
||||
# Several of the assumptions are probably wrong here.
|
||||
# TODO: replace 'x=' with '.info' or '.error'.
|
||||
V42= 42
|
||||
iV1= ${V42}
|
||||
iV2= ${V66}
|
||||
@ -167,5 +173,16 @@ x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
|
||||
# TODO: Has this always worked? There may have been a time, maybe around
|
||||
# 2000, when make would complain about the "Malformed conditional" because
|
||||
# UNDEF is not defined.
|
||||
.if defined(UNDEF) && ${UNDEF} != "undefined"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# TODO: Test each modifier to make sure it is skipped when it is irrelevant
|
||||
# for the result. Since this test is already quite long, do that in another
|
||||
# test.
|
||||
|
||||
all:
|
||||
@:;:
|
||||
|
@ -1,8 +1,8 @@
|
||||
make: "cond-token-number.mk" line 13: Malformed conditional (-0)
|
||||
make: "cond-token-number.mk" line 21: Malformed conditional (+0)
|
||||
make: "cond-token-number.mk" line 29: Malformed conditional (!-1)
|
||||
make: "cond-token-number.mk" line 37: Malformed conditional (!+1)
|
||||
make: "cond-token-number.mk" line 54: End of the tests.
|
||||
make: "cond-token-number.mk" line 15: Malformed conditional (-0)
|
||||
make: "cond-token-number.mk" line 25: Malformed conditional (+0)
|
||||
make: "cond-token-number.mk" line 35: Malformed conditional (!-1)
|
||||
make: "cond-token-number.mk" line 45: Malformed conditional (!+1)
|
||||
make: "cond-token-number.mk" line 80: End of the tests.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,6 +1,8 @@
|
||||
# $NetBSD: cond-token-number.mk,v 1.3 2020/09/14 06:22:59 rillig Exp $
|
||||
# $NetBSD: cond-token-number.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for number tokens in .if conditions.
|
||||
#
|
||||
# TODO: Add introduction.
|
||||
|
||||
.if 0
|
||||
. error
|
||||
@ -12,6 +14,8 @@
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if -0
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though +0 is a number and would be accepted by strtod, it is not
|
||||
@ -20,6 +24,8 @@
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if +0
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though -1 is a number and would be accepted by strtod, it is not
|
||||
@ -28,6 +34,8 @@
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if !-1
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though +1 is a number and would be accepted by strtod, it is not
|
||||
@ -36,6 +44,8 @@
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if !+1
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When the number comes from a variable expression though, it may be signed.
|
||||
@ -50,6 +60,22 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Hexadecimal numbers are accepted.
|
||||
.if 0x0
|
||||
. error
|
||||
.endif
|
||||
.if 0x1
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This is not a hexadecimal number, even though it has an x.
|
||||
# It is interpreted as a string instead, effectively meaning defined(3x4).
|
||||
.if 3x4
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Ensure that parsing continues until here.
|
||||
.info End of the tests.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-token-plain.mk,v 1.4 2020/09/12 17:47:24 rillig Exp $
|
||||
# $NetBSD: cond-token-plain.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for plain tokens (that is, string literals without quotes)
|
||||
# in .if conditions.
|
||||
@ -14,7 +14,7 @@
|
||||
# parser gets to see it.
|
||||
#
|
||||
# XXX: The error message is missing for this malformed condition.
|
||||
# The right-hand side of the comparison is just a '"'.
|
||||
# The right-hand side of the comparison is just a '"', before unescaping.
|
||||
.if ${:U} != "#hash"
|
||||
. error
|
||||
.endif
|
||||
@ -31,16 +31,19 @@
|
||||
# comment handling anymore. The comments are supposed to be stripped off
|
||||
# in a very early parsing phase.
|
||||
#
|
||||
# See https://gnats.netbsd.org/19596 for example makefiles demonstrating the
|
||||
# original problems. This workaround is probably not needed anymore.
|
||||
#
|
||||
# XXX: Missing error message for the malformed condition. The right-hand
|
||||
# side is double-quotes, backslash, backslash.
|
||||
# XXX: It is unexpected that the right-hand side evaluates to a single
|
||||
# backslash.
|
||||
# side before unescaping is double-quotes, backslash, backslash.
|
||||
.if ${:U\\} != "\\#hash"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The right-hand side of a comparison is not parsed as a token, therefore
|
||||
# the code from CondParser_Token does not apply to it.
|
||||
# TODO: Explain the consequences.
|
||||
# TODO: Does this mean that more syntactic variants are allowed here?
|
||||
.if ${:U\#hash} != \#hash
|
||||
. error
|
||||
.endif
|
||||
|
@ -1 +1,8 @@
|
||||
exit status 0
|
||||
make: Unknown modifier 'Z'
|
||||
make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}")
|
||||
make: "cond-token-string.mk" line 18: xvalue is not defined.
|
||||
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")
|
||||
make: "cond-token-string.mk" line 33: Expected.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,39 @@
|
||||
# $NetBSD: cond-token-string.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-token-string.mk,v 1.3 2020/11/10 22:23:37 rillig Exp $
|
||||
#
|
||||
# Tests for quoted and unquoted string literals in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
# Cover the code in CondParser_String that frees the memory after parsing
|
||||
# a variable expression based on an undefined variable.
|
||||
.if "" != "${:Uvalue:Z}"
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if x${:Uvalue}
|
||||
. error
|
||||
.else
|
||||
. info xvalue is not defined.
|
||||
.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
|
||||
# quoted string literal or a number that starts with a digit.
|
||||
.if x${:Uvalue} == ""
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# In plain words, a '\' can be used to escape any character, just as in
|
||||
# double-quoted string literals. See CondParser_String.
|
||||
.if \x${:Uvalue} == "xvalue"
|
||||
. info Expected.
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,7 +1,7 @@
|
||||
make: "cond-token-var.mk" line 9: ok
|
||||
make: "cond-token-var.mk" line 15: Malformed conditional (${UNDEF} == ${DEF})
|
||||
make: "cond-token-var.mk" line 20: Malformed conditional (${DEF} == ${UNDEF})
|
||||
make: "cond-token-var.mk" line 29: Malformed conditional (${UNDEF})
|
||||
make: "cond-token-var.mk" line 20: ok
|
||||
make: "cond-token-var.mk" line 27: Malformed conditional (${UNDEF} == ${DEF})
|
||||
make: "cond-token-var.mk" line 33: Malformed conditional (${DEF} == ${UNDEF})
|
||||
make: "cond-token-var.mk" line 42: Malformed conditional (${UNDEF})
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,6 +1,17 @@
|
||||
# $NetBSD: cond-token-var.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-token-var.mk,v 1.5 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for variables in .if conditions.
|
||||
# Tests for variable 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
|
||||
# variable modifiers such as in ${VAR:Mpattern}.
|
||||
#
|
||||
# XXX: Strictly speaking, variable modifiers should be called expression
|
||||
# modifiers instead since they only modify the expression, not the variable.
|
||||
# Well, except for the assignment modifiers, these do indeed change the value
|
||||
# of the variable.
|
||||
|
||||
DEF= defined
|
||||
|
||||
@ -12,11 +23,13 @@ DEF= defined
|
||||
.endif
|
||||
|
||||
# A variable that appears on the left-hand side must be defined.
|
||||
# The following line thus generates a parse error.
|
||||
.if ${UNDEF} == ${DEF}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A variable that appears on the right-hand side must be defined.
|
||||
# The following line thus generates a parse error.
|
||||
.if ${DEF} == ${UNDEF}
|
||||
. error
|
||||
.endif
|
||||
@ -25,10 +38,11 @@ DEF= defined
|
||||
.if ${DEF}
|
||||
.endif
|
||||
|
||||
# An undefined variable generates a warning.
|
||||
# An undefined variable on its own generates a parse error.
|
||||
.if ${UNDEF}
|
||||
.endif
|
||||
|
||||
# The :U modifier turns an undefined variable into an ordinary expression.
|
||||
# The :U modifier turns an undefined expression into a defined expression.
|
||||
# Since the expression is defined now, it doesn't generate any parse error.
|
||||
.if ${UNDEF:U}
|
||||
.endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
|
||||
make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
|
||||
make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
|
||||
make: "cond-undef-lint.mk" line 45: Variable "VAR.defined" is undefined
|
||||
make: "cond-undef-lint.mk" line 49: Variable "VAR.defined" is undefined
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-undef-lint.mk,v 1.2 2020/09/14 07:13:29 rillig Exp $
|
||||
# $NetBSD: cond-undef-lint.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
|
||||
#
|
||||
# Tests for defined and undefined variables in .if conditions, in lint mode.
|
||||
#
|
||||
@ -42,6 +42,10 @@ DEF= defined
|
||||
.endif
|
||||
|
||||
# The variable VAR.defined is not defined and thus generates an error message.
|
||||
#
|
||||
# TODO: This pattern looks a lot like CFLAGS.${OPSYS}, which is at least
|
||||
# debatable. Or would any practical use of CFLAGS.${OPSYS} be via an indirect
|
||||
# expression, as in the next example?
|
||||
.if ${VAR.${DEF}}
|
||||
. error
|
||||
.else
|
||||
|
@ -1,5 +1,5 @@
|
||||
make: "cond1.mk" line 75: warning: extra else
|
||||
make: "cond1.mk" line 85: warning: extra else
|
||||
make: "cond1.mk" line 80: warning: extra else
|
||||
make: "cond1.mk" line 90: warning: extra else
|
||||
2 is prime
|
||||
A='other' B='unknown' C='clever' o='no,no'
|
||||
Passed:
|
||||
|
@ -1,4 +1,9 @@
|
||||
# $NetBSD: cond1.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
|
||||
# $NetBSD: cond1.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $
|
||||
|
||||
# TODO: Convert these tests into tutorial form.
|
||||
# TODO: Split these tests by topic.
|
||||
# TODO: Use better variable names and expression values that actually express
|
||||
# the intended behavior. uname(1) has nothing to do with conditions.
|
||||
|
||||
# hard code these!
|
||||
TEST_UNAME_S= NetBSD
|
||||
|
@ -1,6 +1,9 @@
|
||||
# $NetBSD: dep-double-colon.mk,v 1.4 2020/09/26 15:41:53 rillig Exp $
|
||||
# $NetBSD: dep-double-colon.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
|
||||
#
|
||||
# Tests for the :: operator in dependency declarations.
|
||||
# Tests for the '::' operator in dependency declarations, which allows
|
||||
# several dependency groups for a single node, each having its own attributes
|
||||
# and dependencies. In the code, the additional dependency groups are called
|
||||
# cohorts.
|
||||
|
||||
all::
|
||||
@echo 'command 1a'
|
||||
|
@ -1,6 +1,11 @@
|
||||
# $NetBSD: dep-exclam.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: dep-exclam.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
#
|
||||
# Tests for the ! operator in dependency declarations.
|
||||
# Tests for the ! operator in dependency declarations, which always re-creates
|
||||
# the target, whether or not it is out of date.
|
||||
#
|
||||
# TODO: Is this related to OP_PHONY?
|
||||
# TODO: Is this related to OP_EXEC?
|
||||
# TODO: Is this related to OP_MAKE?
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
# $NetBSD: depsrc-ignore.mk,v 1.4 2020/08/29 16:13:27 rillig Exp $
|
||||
# $NetBSD: depsrc-ignore.mk,v 1.5 2020/11/15 20:20:58 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .IGNORE in dependency declarations,
|
||||
# which ignores any command failures for that target.
|
||||
#
|
||||
# Even though ignore-errors fails, the all target is still made.
|
||||
# Since the all target is not marked with .IGNORE, it stops at the
|
||||
# first failing command.
|
||||
# Even though 'ignore-errors' fails, 'all' is still made. Since 'all' is
|
||||
# not marked with .IGNORE, it stops at the first failing command.
|
||||
#
|
||||
# XXX: The ordering of the messages in the output is confusing.
|
||||
# The "ignored" comes much too late to be related to the "false
|
||||
@ -24,8 +23,8 @@
|
||||
# This is what actually happens, as of 2020-08-29. To verify it, set the
|
||||
# following breakpoints in CompatRunCommand:
|
||||
#
|
||||
# * the "!silent" line, to see all commands.
|
||||
# * the "fflush" line, to see stdout being flushed.
|
||||
# * the "!silent" line, to see all commands
|
||||
# * the "fflush" line, to see stdout being flushed
|
||||
# * the "status = WEXITSTATUS" line
|
||||
# * the "(continuing)" line
|
||||
# * the "(ignored)" line
|
||||
|
@ -1,9 +1,11 @@
|
||||
# $NetBSD: depsrc-make.mk,v 1.3 2020/09/05 15:57:12 rillig Exp $
|
||||
# $NetBSD: depsrc-make.mk,v 1.4 2020/11/15 20:20:58 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .MAKE in dependency declarations, which
|
||||
# executes the commands of the target even if the -n or -t command line
|
||||
# options are given.
|
||||
|
||||
# TODO: Add a test for the -t command line option.
|
||||
|
||||
.MAKEFLAGS: -n
|
||||
|
||||
all: this-is-made
|
||||
|
@ -1,2 +1,20 @@
|
||||
`all' is up to date.
|
||||
Make_ExpandUse: examine all
|
||||
ExamineLater: need to examine "important"
|
||||
Make_ExpandUse: examine important
|
||||
ExamineLater: need to examine "optional"
|
||||
ExamineLater: need to examine "optional-cohort"
|
||||
Make_ExpandUse: examine optional
|
||||
Make_ExpandUse: examine optional-cohort
|
||||
Examining optional...non-existent...up-to-date.
|
||||
Examining optional-cohort...non-existent...:: operator and no sources...out-of-date.
|
||||
: A leaf node using '::' is considered out-of-date.
|
||||
recheck(optional-cohort): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining important...non-existent...modified before source "optional-cohort"...out-of-date.
|
||||
: important is made.
|
||||
recheck(important): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining all...non-existent...modified before source "important"...out-of-date.
|
||||
: all is made.
|
||||
recheck(all): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining .END...non-existent...non-existent and no sources...out-of-date.
|
||||
recheck(.END): update time from 0:00:00 Jan 01, 1970 to now
|
||||
exit status 0
|
||||
|
@ -1,18 +1,21 @@
|
||||
# $NetBSD: depsrc-optional.mk,v 1.3 2020/09/05 15:57:12 rillig Exp $
|
||||
# $NetBSD: depsrc-optional.mk,v 1.5 2020/11/08 10:33:47 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .OPTIONAL in dependency declarations,
|
||||
# which ignores the target if make cannot find out how to create it.
|
||||
#
|
||||
# TODO: Describe practical use cases for this feature.
|
||||
|
||||
# TODO: Explain why the commands for "important" are not executed.
|
||||
# I had thought that only the "optional" commands were skipped.
|
||||
|
||||
all: important
|
||||
: ${.TARGET} is made.
|
||||
|
||||
important: optional
|
||||
important: optional optional-cohort
|
||||
: ${.TARGET} is made.
|
||||
|
||||
optional: .OPTIONAL
|
||||
: This is not executed.
|
||||
: An optional leaf node is not executed.
|
||||
|
||||
# See IsOODateRegular.
|
||||
optional-cohort:: .OPTIONAL
|
||||
: A leaf node using '::' is considered out-of-date.
|
||||
|
||||
.MAKEFLAGS: -dm
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user