aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2026-07-05 18:22:31 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2026-07-05 18:22:31 +0000
commite988c5eab5231646c612d35ff5b16122cebfbf6a (patch)
tree307239878c1f97050623742f4663c68476e81e4f
parentef402bba84260816d3e8d6e2439b0bc7eddc9446 (diff)
-rw-r--r--ChangeLog34
-rw-r--r--VERSION2
-rw-r--r--bmake.140
-rw-r--r--bmake.cat137
-rw-r--r--dir.c21
-rw-r--r--dir.h3
-rw-r--r--filemon/filemon_dev.c15
-rw-r--r--make.140
-rw-r--r--meta.c367
-rw-r--r--mk/ChangeLog34
-rw-r--r--mk/dirdeps.mk6
-rw-r--r--mk/gendirdeps.mk20
-rw-r--r--mk/install-mk4
-rw-r--r--mk/install-new.mk4
-rw-r--r--mk/meta.autodep.mk4
-rwxr-xr-xmk/meta2deps.py20
-rwxr-xr-xmk/meta2deps.sh15
-rw-r--r--mk/sys.dirdeps.mk4
-rwxr-xr-x[-rw-r--r--]os.sh0
-rw-r--r--parse.c33
-rw-r--r--unit-tests/dep.exp20
-rw-r--r--unit-tests/dep.mk54
-rw-r--r--unit-tests/dir.mk4
-rw-r--r--unit-tests/varmod-match.mk12
-rw-r--r--var.c52
25 files changed, 645 insertions, 200 deletions
diff --git a/ChangeLog b/ChangeLog
index 5189ec11ca1a..c4908f97a494 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2026-07-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20260704
+
+2026-07-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20260703
+ Merge with NetBSD make, pick up
+ o meta.c: do a better job of resolving relative paths
+ o var.c: add alternation support to :M
+ eg. :M*{Makefile*,.mk} matches any *Makefile* or *.mk
+
+2026-07-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20260702
+ Merge with NetBSD make, pick up
+ o meta.c: .MAKE.META.FILTER can be used to more generally filter
+ relevant content of .meta file in meta_oodate.
+
+2026-06-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20260619
+ Merge with NetBSD make, pick up
+ o make.1: fix description of .MADE it is a source not a target.
+
+ * update mk files
+
+2026-06-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20260609
+ Merge with NetBSD make, pick up
+ o parse.c: allow semicolons and parentheses in dependency line
+ expressions
+
2026-05-08 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20260508
diff --git a/VERSION b/VERSION
index c20e42a05834..d2504154fadb 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20260508
+_MAKE_VERSION=20260704
diff --git a/bmake.1 b/bmake.1
index 8190a574da98..4ada0b2628da 100644
--- a/bmake.1
+++ b/bmake.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.391 2026/05/08 16:11:21 sjg Exp $
+.\" $NetBSD: make.1,v 1.394 2026/07/03 15:31:34 sjg Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd May 8, 2026
+.Dd July 3, 2026
.Dt BMAKE 1
.Os
.Sh NAME
@@ -1038,11 +1038,26 @@ the current target is considered out-of-date.
.It Va .MAKE.META.CMP_FILTER
In
.Dq meta
-mode, it can (very rarely!) be useful to filter command
+mode, it can sometimes be useful to filter command
lines before comparison.
-This variable can be set to a set of modifiers that are applied to
-each line of the old and new command that differ, if the filtered
-commands still differ, the target is considered out-of-date.
+This variable can contain a list of modifiers that will be joined with
+.Ql \&:
+and applied to each line of the old and new command that differ.
+If the filtered commands still differ,
+the target is considered out-of-date.
+.It Va .MAKE.META.FILTER
+In
+.Dq meta
+mode, it can be useful to filter most of the data in a meta file.
+For example, when a pre-built tree is relocated or cloned,
+all the targets would be considered out-of-date without a filter
+to replace the old location with the new.
+This variable is similar to
+.Va .MAKE.META.CMP_FILTER
+but is appliled to commands, cwd and the syscall records from filemon.
+In the case of commands, this is done prior to any
+.Va .MAKE.META.CMP_FILTER
+modifiers are applied during comparison.
.It Va .MAKE.META.CREATED
In
.Dq meta
@@ -1506,7 +1521,16 @@ and
.Ql \&[] )
may
be used.
-The wildcard characters may be escaped with a backslash
+Braces can be used to list alternate patterns.
+For example,
+.Ql ${.ALLSRC:M*{Makefile*,.mk}}
+could be used to match any
+.Ql *Makefile*
+or
+.Ql *.mk
+in
+.Va .ALLSRC .
+The wildcard characters and braces may be escaped with a backslash
.Pq Ql \e .
As a consequence of the way values are split into words, matched,
and then joined, the construct
@@ -2317,7 +2341,7 @@ as if they all were preceded by a dash
.\" .It Ic .JOIN
.\" XXX
.It Ic .MADE
-Mark all sources of this target as being up to date.
+Mark the target(s) up to date.
.It Ic .MAKE
Execute the commands associated with this target even if the
.Fl n
diff --git a/bmake.cat1 b/bmake.cat1
index 32fbf8979d8b..d034f33f3941 100644
--- a/bmake.cat1
+++ b/bmake.cat1
@@ -675,11 +675,21 @@ VVAARRIIAABBLLEE AASSSSIIGGNNMMEENNTTSS
current target is considered out-of-date.
_._M_A_K_E_._M_E_T_A_._C_M_P___F_I_L_T_E_R
- In "meta" mode, it can (very rarely!) be useful to filter command
- lines before comparison. This variable can be set to a set of
- modifiers that are applied to each line of the old and new
- command that differ, if the filtered commands still differ, the
- target is considered out-of-date.
+ In "meta" mode, it can sometimes be useful to filter command
+ lines before comparison. This variable can contain a list of
+ modifiers that will be joined with `:' and applied to each line
+ of the old and new command that differ. If the filtered commands
+ still differ, the target is considered out-of-date.
+
+ _._M_A_K_E_._M_E_T_A_._F_I_L_T_E_R
+ In "meta" mode, it can be useful to filter most of the data in a
+ meta file. For example, when a pre-built tree is relocated or
+ cloned, all the targets would be considered out-of-date without a
+ filter to replace the old location with the new. This variable
+ is similar to _._M_A_K_E_._M_E_T_A_._C_M_P___F_I_L_T_E_R but is appliled to commands,
+ cwd and the syscall records from filemon. In the case of
+ commands, this is done prior to any _._M_A_K_E_._M_E_T_A_._C_M_P___F_I_L_T_E_R
+ modifiers are applied during comparison.
_._M_A_K_E_._M_E_T_A_._C_R_E_A_T_E_D
In "meta" mode, this variable contains a list of all the meta
@@ -978,11 +988,14 @@ VVAARRIIAABBLLEE AASSSSIIGGNNMMEENNTTSS
::MM_p_a_t_t_e_r_n
Selects only those words that match _p_a_t_t_e_r_n. The standard shell
- wildcard characters (`*', `?', and `[]') may be used. The wildcard
- characters may be escaped with a backslash (`\'). As a consequence
- of the way values are split into words, matched, and then joined,
- the construct `${VAR:M*}' removes all leading and trailing
- whitespace and normalizes the inter-word spacing to a single space.
+ wildcard characters (`*', `?', and `[]') may be used. Braces can be
+ used to list alternate patterns. For example,
+ `${.ALLSRC:M*{Makefile*,.mk}}' could be used to match any
+ `*Makefile*' or `*.mk' in _._A_L_L_S_R_C. The wildcard characters and
+ braces may be escaped with a backslash (`\'). As a consequence of
+ the way values are split into words, matched, and then joined, the
+ construct `${VAR:M*}' removes all leading and trailing whitespace
+ and normalizes the inter-word spacing to a single space.
::NN_p_a_t_t_e_r_n
This is the opposite of `::MM', selecting all words which do _n_o_t match
@@ -1489,7 +1502,7 @@ SSPPEECCIIAALL SSOOUURRCCEESS ((AATTTTRRIIBBUUTTEESS))
..IIGGNNOORREE Ignore any errors from the commands associated with this
target, exactly as if they all were preceded by a dash (`-').
- ..MMAADDEE Mark all sources of this target as being up to date.
+ ..MMAADDEE Mark the target(s) up to date.
..MMAAKKEE Execute the commands associated with this target even if the --nn
or --tt options were specified. Normally used to mark recursive
@@ -1882,4 +1895,4 @@ BBUUGGSS
attempt to suppress a cascade of unnecessary errors, can result in a
seemingly unexplained `*** Error code 6'
-FreeBSD 14.3-RELEASE-p9 May 8, 2026 FreeBSD 14.3-RELEASE-p9
+FreeBSD 14.4-RELEASE-p3 July 3, 2026 BMAKE(1)
diff --git a/dir.c b/dir.c
index df30038f3edd..ac84c3997ef9 100644
--- a/dir.c
+++ b/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.298 2026/04/06 17:13:54 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.299 2026/07/03 15:31:34 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -89,6 +89,9 @@
* Returns true if the name given it needs to
* be wildcard-expanded.
*
+ * ExpandCurly
+ * Expand a pattern containing braces to a StringList.
+ *
* SearchPath_Expand
* Expand a filename pattern to find all matching files
* from the search path.
@@ -132,7 +135,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.298 2026/04/06 17:13:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.299 2026/07/03 15:31:34 sjg Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -752,8 +755,8 @@ concat3(const char *a, size_t a_len, const char *b, size_t b_len,
/*
* Expand curly braces like the C shell. Brace expansion by itself is purely
* textual, the expansions are not looked up in the file system. But if an
- * expanded word contains wildcard characters, it is expanded further,
- * matching only the actually existing files.
+ * expanded word contains wildcard characters, and path is provided,
+ * it is expanded further, matching only the actually existing files.
*
* Example: "{a{b,c}}" expands to "ab" and "ac".
* Example: "{a}" expands to "a".
@@ -762,11 +765,11 @@ concat3(const char *a, size_t a_len, const char *b, size_t b_len,
* Input:
* word Entire word to expand
* brace First curly brace in it
- * path Search path to use
+ * path Search path to use (NULL to skip wildcard expansion)
* expansions Place to store the expansions
*/
-static void
-DirExpandCurly(const char *word, const char *brace, SearchPath *path,
+void
+ExpandCurly(const char *word, const char *brace, SearchPath *path,
StringList *expansions)
{
const char *prefix, *middle, *piece, *middle_end, *suffix;
@@ -796,7 +799,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
char *file = concat3(prefix, prefix_len, piece, piece_len,
suffix, suffix_len);
- if (contains_wildcard(file)) {
+ if (path != NULL && contains_wildcard(file)) {
SearchPath_Expand(path, file, expansions);
free(file);
} else {
@@ -898,7 +901,7 @@ SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
brace = strchr(pattern, '{');
if (brace != NULL) {
- DirExpandCurly(pattern, brace, path, expansions);
+ ExpandCurly(pattern, brace, path, expansions);
goto done;
}
diff --git a/dir.h b/dir.h
index 79c75598f68b..ff303b3d4ded 100644
--- a/dir.h
+++ b/dir.h
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.h,v 1.49 2024/07/07 07:50:57 rillig Exp $ */
+/* $NetBSD: dir.h,v 1.50 2026/07/03 15:31:34 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -86,6 +86,7 @@ void Dir_End(void);
void Dir_SetPATH(void);
void Dir_SetSYSPATH(void);
bool Dir_HasWildcards(const char *) MAKE_ATTR_USE;
+void ExpandCurly(const char *, const char *, SearchPath *, StringList *);
void SearchPath_Expand(SearchPath *, const char *, StringList *);
char *Dir_FindFile(const char *, SearchPath *) MAKE_ATTR_USE;
char *Dir_FindInclude(const char *, SearchPath *) MAKE_ATTR_USE;
diff --git a/filemon/filemon_dev.c b/filemon/filemon_dev.c
index 741edc7c25ea..f605061f5c67 100644
--- a/filemon/filemon_dev.c
+++ b/filemon/filemon_dev.c
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon_dev.c,v 1.9 2022/03/04 23:17:16 sjg Exp $ */
+/* $NetBSD: filemon_dev.c,v 1.10 2026/07/03 03:34:25 sjg Exp $ */
/*
* Copyright (c) 2020 The NetBSD Foundation, Inc.
@@ -123,6 +123,19 @@ filemon_close(struct filemon *F)
{
int error = 0;
+#ifdef FILEMON_FLUSH_IOCTL
+ /*
+ * Ask the kernel to write the footer and flush the trailing records to
+ * the output file *now*, synchronously, before we close the device.
+ * Otherwise finalization happens only at device release, which the
+ * kernel defers until the last reference to the device is dropped; a
+ * child that transiently inherited this fd across a fork->exec window
+ * can hold that reference past our read of the output file, yielding a
+ * truncated .meta (records + "# Bye bye" footer missing).
+ */
+ (void)ioctl(F->fd, FILEMON_FLUSH_IOCTL);
+#endif
+
/* Close the filemon device fd. */
if (close(F->fd) == -1 && error == 0)
error = errno;
diff --git a/make.1 b/make.1
index 3529a5e71a53..3a2db8bc72a2 100644
--- a/make.1
+++ b/make.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.391 2026/05/08 16:11:21 sjg Exp $
+.\" $NetBSD: make.1,v 1.394 2026/07/03 15:31:34 sjg Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd May 8, 2026
+.Dd July 3, 2026
.Dt MAKE 1
.Os
.Sh NAME
@@ -1038,11 +1038,26 @@ the current target is considered out-of-date.
.It Va .MAKE.META.CMP_FILTER
In
.Dq meta
-mode, it can (very rarely!) be useful to filter command
+mode, it can sometimes be useful to filter command
lines before comparison.
-This variable can be set to a set of modifiers that are applied to
-each line of the old and new command that differ, if the filtered
-commands still differ, the target is considered out-of-date.
+This variable can contain a list of modifiers that will be joined with
+.Ql \&:
+and applied to each line of the old and new command that differ.
+If the filtered commands still differ,
+the target is considered out-of-date.
+.It Va .MAKE.META.FILTER
+In
+.Dq meta
+mode, it can be useful to filter most of the data in a meta file.
+For example, when a pre-built tree is relocated or cloned,
+all the targets would be considered out-of-date without a filter
+to replace the old location with the new.
+This variable is similar to
+.Va .MAKE.META.CMP_FILTER
+but is appliled to commands, cwd and the syscall records from filemon.
+In the case of commands, this is done prior to any
+.Va .MAKE.META.CMP_FILTER
+modifiers are applied during comparison.
.It Va .MAKE.META.CREATED
In
.Dq meta
@@ -1506,7 +1521,16 @@ and
.Ql \&[] )
may
be used.
-The wildcard characters may be escaped with a backslash
+Braces can be used to list alternate patterns.
+For example,
+.Ql ${.ALLSRC:M*{Makefile*,.mk}}
+could be used to match any
+.Ql *Makefile*
+or
+.Ql *.mk
+in
+.Va .ALLSRC .
+The wildcard characters and braces may be escaped with a backslash
.Pq Ql \e .
As a consequence of the way values are split into words, matched,
and then joined, the construct
@@ -2317,7 +2341,7 @@ as if they all were preceded by a dash
.\" .It Ic .JOIN
.\" XXX
.It Ic .MADE
-Mark all sources of this target as being up to date.
+Mark the target(s) up to date.
.It Ic .MAKE
Execute the commands associated with this target even if the
.Fl n
diff --git a/meta.c b/meta.c
index 9f9f4fe04663..e19a0a9e23db 100644
--- a/meta.c
+++ b/meta.c
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.c,v 1.221 2026/04/06 17:13:54 rillig Exp $ */
+/* $NetBSD: meta.c,v 1.223 2026/07/03 18:58:40 sjg Exp $ */
/*
* Implement 'meta' mode.
@@ -64,18 +64,11 @@ static char *metaBailiwickStr; /* string storage for the list */
static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */
static char *metaIgnorePathsStr; /* string storage for the list */
-#ifndef MAKE_META_IGNORE_PATHS
#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
-#endif
-#ifndef MAKE_META_IGNORE_PATTERNS
#define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS"
-#endif
-#ifndef MAKE_META_IGNORE_FILTER
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
-#endif
-#ifndef MAKE_META_CMP_FILTER
#define MAKE_META_CMP_FILTER ".MAKE.META.CMP_FILTER"
-#endif
+#define MAKE_META_FILTER ".MAKE.META.FILTER"
bool useMeta = false;
static bool useFilemon = false;
@@ -88,6 +81,7 @@ static bool metaIgnoreCMDs = false; /* ignore CMDs in .meta files */
static bool metaIgnorePatterns = false; /* do we need to do pattern matches */
static bool metaIgnoreFilter = false; /* do we have more complex filtering? */
static bool metaCmpFilter = false; /* do we have CMP_FILTER ? */
+static bool metaFilter = false; /* do we have FILTER */
static bool metaCurdirOk = false; /* write .meta in .CURDIR Ok? */
static bool metaSilent = false; /* if we have a .meta be SILENT */
@@ -606,6 +600,7 @@ meta_mode_init(const char *make_mode)
metaIgnorePatterns = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS);
metaIgnoreFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER);
metaCmpFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_CMP_FILTER);
+ metaFilter = Var_Exists(SCOPE_GLOBAL, MAKE_META_FILTER);
}
MAKE_INLINE BuildMon *
@@ -1001,16 +996,86 @@ append_if_new(StringList *list, const char *str)
Lst_Append(list, bmake_strdup(str));
}
-/* A "reserved" variable to store the command to be filtered */
-#define META_CMD_FILTER_VAR ".MAKE.cmd_filtered"
+/* remove any missingFiles entries that match p */
+static void
+remove_missing(StringList *missingFiles, const char *p)
+{
+ StringListNode *ln = missingFiles->first;
+
+ while (ln != NULL) {
+ StringListNode *next = ln->next;
+ if (path_starts_with(ln->datum, p)) {
+ free(ln->datum);
+ Lst_Remove(missingFiles, ln);
+ }
+ ln = next;
+ }
+}
+
+static void
+remove_relative_missing(StringList *missingFiles, const char *p, char *sdirs[])
+{
+ char buf[MAXPATHLEN];
+ char **sdp;
+
+ for (sdp = sdirs; *sdp != NULL; sdp++) {
+ snprintf(buf, sizeof(buf), "%s/%s", *sdp, p);
+ remove_missing(missingFiles, buf);
+ }
+}
+
+/* try to find a relative path */
+static char *
+meta_resolve_path(char *buf, size_t bufsz, const char *p,
+ char *cwd, struct cached_stat *cstp, char *sdirs[])
+{
+ char **sdp;
+
+ if (strcmp(p, ".") == 0)
+ return cwd;
+
+ for (sdp = sdirs; *sdp != NULL; sdp++) {
+ snprintf(buf, bufsz, "%s/%s", *sdp, p);
+ if (cached_stat(buf, cstp) == 0)
+ return buf;
+ }
+ return NULL;
+}
+
+/*
+ * Update sdirs to contain a NULL terminated list of
+ * unique dirs to try and resolve relative paths against.
+ */
+static void
+set_sdirs(char *sdirs[], size_t sdsz,
+ char *latestdir, char *lcwd, char *cwd)
+{
+ size_t i = 0;
+
+ sdirs[i++] = latestdir;
+ if (i < sdsz && strcmp(latestdir, lcwd) != 0)
+ sdirs[i++] = lcwd;
+ if (i < sdsz && strcmp(lcwd, cwd) != 0)
+ sdirs[i++] = cwd;
+ if (i < sdsz)
+ sdirs[i] = NULL;
+ else
+ sdirs[i - 1] = NULL; /* should never happen */
+}
+
+/* A "reserved" variable to store the string to be filtered */
+#define META_FILTER_VAR ".MAKE.META._filtered_"
static char *
-meta_filter_cmd(GNode *gn, char *s)
+meta_filter(GNode *gn, char *s, const char *filter)
{
- Var_Set(gn, META_CMD_FILTER_VAR, s);
- s = Var_Subst(
- "${" META_CMD_FILTER_VAR ":${" MAKE_META_CMP_FILTER ":ts:}}",
- gn, VARE_EVAL);
+ char buf[sizeof(META_FILTER_VAR) + sizeof(MAKE_META_CMP_FILTER) + 24];
+
+ Var_Set(gn, META_FILTER_VAR, s);
+ /* ":N" is a harmless do nothing */
+ snprintf(buf, sizeof(buf), "${%s:${%s:UN:ts:}}",
+ META_FILTER_VAR, filter);
+ s = Var_Subst(buf, gn, VARE_EVAL);
return s;
}
@@ -1022,15 +1087,25 @@ meta_cmd_cmp(GNode *gn, char *a, char *b, bool filter)
rc = strcmp(a, b);
if (rc == 0 || !filter)
return rc;
- a = meta_filter_cmd(gn, a);
- b = meta_filter_cmd(gn, b);
+ a = meta_filter(gn, a, MAKE_META_CMP_FILTER);
+ b = meta_filter(gn, b, MAKE_META_CMP_FILTER);
rc = strcmp(a, b);
free(a);
free(b);
- Var_Delete(gn, META_CMD_FILTER_VAR);
return rc;
}
+typedef enum MetaSection {
+ META_HEADER,
+ META_CMD,
+ META_CWD,
+ META_TARGET,
+ META_OODATE,
+ META_ENV,
+ META_OUTPUT,
+ META_FILEMON
+} MetaSection;
+
bool
meta_oodate(GNode *gn, bool oodate)
{
@@ -1043,7 +1118,6 @@ meta_oodate(GNode *gn, bool oodate)
char fname[MAXPATHLEN];
char fname1[MAXPATHLEN];
char fname2[MAXPATHLEN];
- char fname3[MAXPATHLEN];
FStr dname;
const char *tname;
char *p;
@@ -1054,7 +1128,6 @@ meta_oodate(GNode *gn, bool oodate)
FILE *fp;
bool needOODATE = false;
StringList missingFiles;
- bool have_filemon = false;
bool cmp_filter;
if (oodate)
@@ -1063,10 +1136,10 @@ meta_oodate(GNode *gn, bool oodate)
dname = Var_Value(gn, ".OBJDIR");
tname = GNode_VarTarget(gn);
- /* if this succeeds fname3 is realpath of dname */
- if (!meta_needed(gn, dname.str, fname3, false))
+ /* if this succeeds fname2 is realpath of dname */
+ if (!meta_needed(gn, dname.str, fname2, false))
goto oodate_out;
- dname.str = fname3;
+ dname.str = fname2;
Lst_Init(&missingFiles);
@@ -1087,12 +1160,17 @@ meta_oodate(GNode *gn, bool oodate)
if ((fp = fopen(fname, "r")) != NULL) {
static char *buf = NULL;
static size_t bufsz;
+ char *rp;
+ char *freeit = NULL;
+ char *sdirs[4];
unsigned lineno = 0;
int lastpid = 0;
int pid;
int x;
+ bool found;
StringListNode *cmdNode;
struct cached_stat cst;
+ MetaSection section = META_HEADER;
if (buf == NULL) {
bufsz = 8 * BUFSIZ;
@@ -1107,6 +1185,10 @@ meta_oodate(GNode *gn, bool oodate)
strlcpy(lcwd, cwd, sizeof lcwd);
strlcpy(latestdir, cwd, sizeof latestdir);
+ /* adjust this when lcwd or latestdir change */
+ sdirs[0] = cwd;
+ sdirs[1] = NULL;
+
if (tmpdir == NULL) {
tmpdir = getTmpdir();
tmplen = strlen(tmpdir);
@@ -1115,10 +1197,14 @@ meta_oodate(GNode *gn, bool oodate)
/* we want to track all the .meta we read */
Global_Append(".MAKE.META.FILES", fname);
- cmp_filter = metaCmpFilter || Var_Exists(gn, MAKE_META_CMP_FILTER);
+ cmp_filter = metaFilter || metaCmpFilter || Var_Exists(gn, MAKE_META_CMP_FILTER);
cmdNode = gn->commands.first;
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
+ if (freeit != NULL) {
+ free(freeit);
+ freeit = NULL;
+ }
lineno++;
if (buf[x - 1] == '\n')
buf[x - 1] = '\0';
@@ -1129,16 +1215,37 @@ meta_oodate(GNode *gn, bool oodate)
}
link_src = NULL;
move_target = NULL;
- /* Find the start of the build monitor section. */
- if (!have_filemon) {
- if (strncmp(buf, "-- filemon", 10) == 0) {
- have_filemon = true;
- continue;
- }
- if (strncmp(buf, "# buildmon", 10) == 0) {
- have_filemon = true;
- continue;
- }
+ /*
+ * Track which section of the meta file we are in.
+ * For CMD, CWD, and FILEMON we may have to do filtering
+ * the rest are of no interest here.
+ */
+ switch (section) {
+ case META_HEADER:
+ section = META_CMD;
+ continue;
+ case META_CMD:
+ if (strncmp(buf, "CWD", 3) == 0)
+ section = META_CWD;
+ break;
+ case META_CWD:
+ if (strncmp(buf, "TARG", 4) == 0)
+ section = META_TARGET;
+ break;
+ case META_TARGET:
+ case META_OODATE:
+ case META_ENV:
+ if (strncmp(buf, "-- command", 10) == 0)
+ section = META_OUTPUT;
+ continue;
+ case META_OUTPUT:
+ if (strncmp(buf, "-- filemon", 10) == 0)
+ section = META_FILEMON;
+ if (strncmp(buf, "# buildmon", 10) == 0)
+ section = META_FILEMON;
+ continue;
+ case META_FILEMON:
+ break;
}
/* Delimit the record type. */
@@ -1146,8 +1253,35 @@ meta_oodate(GNode *gn, bool oodate)
#ifdef DEBUG_META_MODE
DEBUG3(META, "%s:%u: %s\n", fname, lineno, buf);
#endif
+ if (metaFilter) {
+ switch (buf[0]) {
+ case '#': /* these do not need filtering */
+ case 'F':
+ case 'V':
+ case 'X':
+ break;
+ default:
+ switch (section) {
+ default:
+ break;
+ case META_CMD:
+ case META_CWD:
+ case META_FILEMON:
+ freeit = p = meta_filter(gn, p, MAKE_META_FILTER);
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: filtered: %s\n", fname, lineno, p);
+#endif
+ break;
+ }
+ break;
+ }
+ }
+
strsep(&p, " ");
- if (have_filemon) {
+ switch (section) {
+ default:
+ break;
+ case META_FILEMON:
/*
* We are in the 'filemon' output section.
* Each record from filemon follows the general form:
@@ -1206,6 +1340,7 @@ meta_oodate(GNode *gn, bool oodate)
strlcpy(lcwd, ldir.str, sizeof lcwd);
FStr_Done(&ldir);
}
+ set_sdirs(sdirs, 4, latestdir, lcwd, cwd);
}
/* Skip past the pid. */
if (strsep(&p, " ") == NULL)
@@ -1261,6 +1396,7 @@ meta_oodate(GNode *gn, bool oodate)
DEBUG4(META, "%s:%u: cwd=%s ldir=%s\n",
fname, lineno, cwd, lcwd);
#endif
+ set_sdirs(sdirs, 4, latestdir, lcwd, cwd);
break;
case 'M': /* renaMe */
@@ -1283,18 +1419,26 @@ meta_oodate(GNode *gn, bool oodate)
DEQUOTE(move_target);
/* FALLTHROUGH */
case 'D': /* unlink */
- if (*p == '/') {
- /* remove any missingFiles entries that match p */
- StringListNode *ln = missingFiles.first;
- while (ln != NULL) {
- StringListNode *next = ln->next;
- if (path_starts_with(ln->datum, p)) {
- free(ln->datum);
- Lst_Remove(&missingFiles, ln);
- }
- ln = next;
+ if (*p != '/') {
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: looking for: %s ...\n",
+ fname, lineno, p);
+#endif
+ rp = meta_resolve_path(fname1, sizeof(fname1), p,
+ lcwd, &cst, sdirs);
+ if (rp != NULL) {
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: found: %s\n",
+ fname, lineno, rp);
+#endif
+ p = rp;
}
}
+ if (*p == '/') {
+ remove_missing(&missingFiles, p);
+ } else {
+ remove_relative_missing(&missingFiles, p, sdirs);
+ }
if (buf[0] == 'M') {
/* the target of the mv is a file 'W'ritten */
#ifdef DEBUG_META_MODE
@@ -1330,10 +1474,34 @@ meta_oodate(GNode *gn, bool oodate)
* but outside of .OBJDIR is missing,
* we need to do it again.
*/
- /* ignore non-absolute paths */
- if (*p != '/')
+ if (*p != '/') {
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: looking for: %s ...\n",
+ fname, lineno, p);
+#endif
+ rp = meta_resolve_path(fname1, sizeof(fname1), p,
+ lcwd, &cst, sdirs);
+ if (rp != NULL) {
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: found: %s\n",
+ fname, lineno, rp);
+#endif
+ p = rp;
+ }
+ }
+ if (*p != '/') {
+ /*
+ * it is missing or we should have found it
+ */
+ snprintf(fname1, sizeof(fname1), "%s/%s",
+ lcwd, p);
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: maybe missing: %s\n",
+ fname, lineno, fname1);
+#endif
+ append_if_new(&missingFiles, fname1);
break;
-
+ }
if (Lst_IsEmpty(&metaBailiwick))
break;
@@ -1378,73 +1546,59 @@ meta_oodate(GNode *gn, bool oodate)
/*
* The rest of the record is the file name.
- * Check if it's not an absolute path.
*/
- {
- char *sdirs[4];
- char **sdp;
- int sdx = 0;
- bool found = false;
-
- if (*p == '/') {
- sdirs[sdx++] = p; /* done */
- } else {
- if (strcmp(".", p) == 0)
- continue; /* no point */
-
- /* Check vs latestdir */
- if (snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p) < (int)(sizeof fname1))
- sdirs[sdx++] = fname1;
-
- if (strcmp(latestdir, lcwd) != 0) {
- /* Check vs lcwd */
- if (snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p) < (int)(sizeof fname2))
- sdirs[sdx++] = fname2;
- }
- if (strcmp(lcwd, cwd) != 0) {
- /* Check vs cwd */
- if (snprintf(fname3, sizeof fname3, "%s/%s", cwd, p) < (int)(sizeof fname3))
- sdirs[sdx++] = fname3;
- }
- }
- sdirs[sdx++] = NULL;
-
- for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
+ if (*p == '/') {
+ if (cached_stat(p, &cst) == 0) {
+ found = true;
#ifdef DEBUG_META_MODE
- DEBUG3(META, "%s:%u: looking for: %s\n",
- fname, lineno, *sdp);
+ DEBUG3(META, "%s:%u: found: %s\n",
+ fname, lineno, p);
#endif
- if (cached_stat(*sdp, &cst) == 0) {
- found = true;
- p = *sdp;
- }
}
- if (found) {
+ } else {
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: looking for: %s ...\n",
+ fname, lineno, p);
+#endif
+ rp = meta_resolve_path(fname1, sizeof(fname1), p,
+ lcwd, &cst, sdirs);
+ if (rp != NULL) {
#ifdef DEBUG_META_MODE
DEBUG3(META, "%s:%u: found: %s\n",
- fname, lineno, p);
+ fname, lineno, rp);
#endif
- if (!S_ISDIR(cst.cst_mode) &&
- cst.cst_mtime > gn->mtime) {
- DEBUG3(META, "%s:%u: file '%s' is newer than the target...\n",
- fname, lineno, p);
- oodate = true;
- } else if (S_ISDIR(cst.cst_mode)) {
- /* Update the latest directory. */
- cached_realpath(p, latestdir);
- }
- } else if (errno == ENOENT && *p == '/' &&
- strncmp(p, cwd, cwdlen) != 0) {
+ p = rp;
+ found = true;
+ }
+ }
+ if (found) {
+ if (!S_ISDIR(cst.cst_mode) &&
+ cst.cst_mtime > gn->mtime) {
+ DEBUG3(META, "%s:%u: file '%s' is newer than the target...\n",
+ fname, lineno, p);
+ oodate = true;
+ } else if (S_ISDIR(cst.cst_mode)) {
+ /* Update the latest directory. */
+ cached_realpath(p, latestdir);
+ set_sdirs(sdirs, 4, latestdir, lcwd, cwd);
+ }
+ } else if (errno == ENOENT) {
+ if (*p != '/' || strncmp(p, cwd, cwdlen) != 0) {
/*
* A referenced file outside of CWD is missing.
* We cannot catch every eventuality here...
*/
+#ifdef DEBUG_META_MODE
+ DEBUG3(META, "%s:%u: maybe missing: %s\n",
+ fname, lineno, p);
+#endif
append_if_new(&missingFiles, p);
}
}
if (buf[0] == 'E') {
/* previous latestdir is no longer relevant */
strlcpy(latestdir, lcwd, sizeof latestdir);
+ set_sdirs(sdirs, 4, latestdir, lcwd, cwd);
}
break;
default:
@@ -1452,7 +1606,8 @@ meta_oodate(GNode *gn, bool oodate)
}
if (!oodate && buf[0] == 'L' && link_src != NULL)
goto check_link_src;
- } else if (strcmp(buf, "CMD") == 0) {
+ break;
+ case META_CMD:
/*
* Compare the current command with the one in the
* meta data file.
@@ -1518,7 +1673,8 @@ meta_oodate(GNode *gn, bool oodate)
free(cmd);
cmdNode = cmdNode->next;
}
- } else if (strcmp(buf, "CWD") == 0) {
+ break;
+ case META_CWD:
/*
* Check if there are extra commands now
* that weren't in the meta data file.
@@ -1534,8 +1690,13 @@ meta_oodate(GNode *gn, bool oodate)
fname, lineno, p, curdir);
oodate = true;
}
+ break;
}
}
+ free(freeit);
+
+ if (metaFilter || metaCmpFilter)
+ Var_Delete(gn, META_FILTER_VAR);
fclose(fp);
if (!Lst_IsEmpty(&missingFiles)) {
@@ -1543,10 +1704,12 @@ meta_oodate(GNode *gn, bool oodate)
fname, (char *)missingFiles.first->datum);
oodate = true;
}
- if (!oodate && !have_filemon && filemonMissing) {
+#ifdef USE_FILEMON
+ if (!oodate && section != META_FILEMON && filemonMissing) {
DEBUG1(META, "%s: missing filemon data\n", fname);
oodate = true;
}
+#endif
} else {
if (writeMeta && (metaMissing || (gn->type & OP_META))) {
const char *cp = NULL;
diff --git a/mk/ChangeLog b/mk/ChangeLog
index a8805fa774bf..e904175f354d 100644
--- a/mk/ChangeLog
+++ b/mk/ChangeLog
@@ -1,3 +1,37 @@
+2026-07-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20260704
+
+2026-07-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: make use of alternation support in latest bmake
+ when setting .MAKE.META.IGNORE_FILTER so we can get both
+ Makefile.depend* and *.mk to make cache out-of-date.
+
+2026-07-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta2deps.py: detect corrupted filemon data on Linux,
+ caused by lost newlines.
+
+2026-06-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20260621
+
+ * gendirdeps.mk: more debug output
+
+ * meta.autodep.mk: remove extra } from GENDIRDEPS_ENV
+
+2026-06-19 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20260619
+
+ * install-new.mk: for POSIX_SHELL we need .new*
+ rather than just .new
+
+2026-05-21 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta2deps.{py,sh}: skip pathnames with spaces - not worth the pain.
+
2026-05-09 Simon J Gerraty <sjg@beast.crufty.net>
* gendirdeps.mk: also apply ':S,/$,,' and ':C,/(\.[^.]*)$,\1,'
diff --git a/mk/dirdeps.mk b/mk/dirdeps.mk
index abd3407eddf4..f4cf1a7f4d54 100644
--- a/mk/dirdeps.mk
+++ b/mk/dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: dirdeps.mk,v 1.178 2026/05/09 20:33:46 sjg Exp $
+# $Id: dirdeps.mk,v 1.179 2026/07/03 15:46:16 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -570,7 +570,11 @@ M_oneperline = @x@\\${.newline} $$x@
BUILD_DIRDEPS = no
# ignore anything but these
+.if ${MAKE_VERSION} > 20260702
+.MAKE.META.IGNORE_FILTER = M*{/${.MAKE.DEPENDFILE_PREFIX}*,.mk}
+.else
.MAKE.META.IGNORE_FILTER = M*/${.MAKE.DEPENDFILE_PREFIX}*
+.endif
dirdeps: dirdeps-cached
dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
diff --git a/mk/gendirdeps.mk b/mk/gendirdeps.mk
index 85af65668407..ad964e062205 100644
--- a/mk/gendirdeps.mk
+++ b/mk/gendirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: gendirdeps.mk,v 1.55 2026/05/09 20:46:13 sjg Exp $
+# $Id: gendirdeps.mk,v 1.57 2026/06/21 19:04:57 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -87,6 +87,12 @@ _debug.gendirdeps = 0
all:
+.if ${_debug.gendirdeps}
+.if ${DEBUG_GENDIRDEPS:M-d*}
+.MAKEFLAGS: ${DEBUG_GENDIRDEPS:M-d*}
+.endif
+.endif
+
_CURDIR ?= ${.CURDIR}
_OBJDIR ?= ${.OBJDIR}
_OBJTOP ?= ${OBJTOP}
@@ -157,10 +163,12 @@ META2DEPS := ${META2DEPS}
.if ${_debug.gendirdeps} && ${DEBUG_GENDIRDEPS:Mmeta2d*} != ""
_time = time
+_set_x = set -x;
_sh_x = sh -x
_py_d = -ddd
.else
_time =
+_set_x =
_sh_x =
_py_d =
.endif
@@ -238,7 +246,11 @@ x != echo; for m in $$_meta_files; do echo $$m; done > meta.list
_meta_files_arg:= ${_meta_files}
.endif
-dir_list != cd ${_OBJDIR} && \
+.if ${_debug.gendirdeps}
+.info ${RELDIR}: _meta_files_arg='${_meta_files_arg}'
+.endif
+
+dir_list != cd ${_OBJDIR} && ${_set_x} \
${META2DEPS_CMD} MACHINE=${MACHINE} \
SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \
${META2DEPS_ARGS} \
@@ -250,6 +262,9 @@ dir_list != cd ${_OBJDIR} && \
.warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,}
# we are not going to update anything
.else
+.if ${_debug.gendirdeps}
+.info ${RELDIR}: raw_dir_list='${dir_list}'
+.endif
dpadd_dir_list=
.if !empty(DPADD)
_nonlibs := ${DPADD:T:Nlib*:N*include}
@@ -270,7 +285,6 @@ ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \
sed ${GENDIRDEPS_SEDCMDS}
.if ${_debug.gendirdeps}
-.info ${RELDIR}: raw_dir_list='${dir_list}'
.info ${RELDIR}: ddeps='${ddeps}'
.endif
dir_list += ${ddeps}
diff --git a/mk/install-mk b/mk/install-mk
index c272b63f2b54..1f4c2acee964 100644
--- a/mk/install-mk
+++ b/mk/install-mk
@@ -59,7 +59,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: install-mk,v 1.275 2026/04/24 19:56:48 sjg Exp $
+# $Id: install-mk,v 1.278 2026/07/04 16:44:08 sjg Exp $
#
# @(#) Copyright (c) 1994-2025 Simon J. Gerraty
#
@@ -69,7 +69,7 @@
# sjg@crufty.net
#
-MK_VERSION=20260424
+MK_VERSION=20260704
OWNER=
GROUP=
MODE=444
diff --git a/mk/install-new.mk b/mk/install-new.mk
index 2fb9725a7a47..001460439ca8 100644
--- a/mk/install-new.mk
+++ b/mk/install-new.mk
@@ -1,4 +1,4 @@
-# $Id: install-new.mk,v 1.9 2025/11/19 17:44:15 sjg Exp $
+# $Id: install-new.mk,v 1.10 2026/06/19 19:47:03 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@@ -47,7 +47,7 @@ InstallNew= ${CmpCpMv}; InstallNew() { \
for new in "$$@"; do \
if test $$_t $$new; then \
if ${isPOSIX_SHELL:Ufalse}; then \
- target=$${new%.new}; \
+ target=$${new%.new*}; \
else \
target=`expr $$new : '\(.*\).new'`; \
fi; \
diff --git a/mk/meta.autodep.mk b/mk/meta.autodep.mk
index 1998689306d4..a4d43f19fec1 100644
--- a/mk/meta.autodep.mk
+++ b/mk/meta.autodep.mk
@@ -1,4 +1,4 @@
-# $Id: meta.autodep.mk,v 1.72 2026/01/11 05:32:29 sjg Exp $
+# $Id: meta.autodep.mk,v 1.73 2026/06/21 19:01:21 sjg Exp $
#
# @(#) Copyright (c) 2010-2025, Simon J. Gerraty
#
@@ -275,7 +275,7 @@ META_FILES = ${.MAKE.META.FILES:N.depend*}
# objects in subdirs
META_FILE_FILTER += S,${_OBJDIR}/,,:O:u
# we have to defer evaluation until the target script runs
-GENDIRDEPS_ENV += META_FILES="${META_FILES:${META_FILE_FILTER:O:u:ts:}}}"
+GENDIRDEPS_ENV += META_FILES="${META_FILES:${META_FILE_FILTER:O:u:ts:}}"
.if ${_debug.autodep}
.info ${_DEPENDFILE:S,${SRCTOP}/,,}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} xtras=${META_XTRAS}
diff --git a/mk/meta2deps.py b/mk/meta2deps.py
index 42085e9e7f7d..8543ec4049ea 100755
--- a/mk/meta2deps.py
+++ b/mk/meta2deps.py
@@ -39,7 +39,7 @@ We only pay attention to a subset of the information in the
SPDX-License-Identifier: BSD-2-Clause
RCSid:
- $Id: meta2deps.py,v 1.55 2026/01/13 04:32:45 sjg Exp $
+ $Id: meta2deps.py,v 1.57 2026/07/03 15:45:29 sjg Exp $
Copyright (c) 2011-2025, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@@ -73,6 +73,13 @@ import re
import sys
import stat
+# Attempt to match bogus filemon data that could potentitally be truncated by
+# the unreliable Linux filemon such as:
+# R 2712488 /X 2712597 0
+# or
+# R 1096904 /path/foo-linuR 1096904 /path/bar.py
+filemon_fused_re = re.compile(r'[CDEFLMRSVWX] [0-9]+ ')
+
def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
"""
Return an absolute path, resolving via cwd or last_dir if needed.
@@ -515,6 +522,17 @@ class MetaFile:
if self.debug > 2:
print('op={} elen={} wlen={} line="{}"'.format(w[0], elen, wlen, line.strip()), file=self.debug_out)
if wlen != elen:
+ if wlen > elen and w[0] in 'CELMRW':
+ # Before assuming spaces in a pathname, make sure this
+ # isn't two records fused onto one line because of
+ # dropped data (unreliable Linux filemon).
+ if filemon_fused_re.search(line.split(None, 2)[2]):
+ raise AssertionError('corrupted filemon data: fused records (dropped newline): {} in: {}'.format(error_name, line.strip()))
+ # It could just be spaces in pathname.
+ # More trouble than its worth, just skip it.
+ if self.debug > 0:
+ print('Skipping: wrong number of words: expected {} got {} in: {}'.format(elen, wlen, line), file=self.debug_out)
+ continue
raise AssertionError('corrupted filemon data: wrong number of words: expected {} got {} in: {}'.format(elen, wlen, line))
pid = int(w[1])
diff --git a/mk/meta2deps.sh b/mk/meta2deps.sh
index 293ebdab6e7c..454b8eeb861c 100755
--- a/mk/meta2deps.sh
+++ b/mk/meta2deps.sh
@@ -77,7 +77,7 @@
# RCSid:
-# $Id: meta2deps.sh,v 1.26 2025/12/08 17:34:02 sjg Exp $
+# $Id: meta2deps.sh,v 1.27 2026/05/22 00:33:26 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -162,6 +162,10 @@ error() {
exit 1
}
+warning() {
+ echo "WARNING: $@" >&2
+}
+
meta2deps() {
DPDEPS=
SRCTOPS=$SRCTOP
@@ -273,8 +277,15 @@ meta2deps() {
;;
\#*) ;; # ok
[LM],) error "missing path2 in: '$op $pid $path'";;
- [LMX],*" "*) error "wrong number of words in: '$op $pid $path $path2'";;
+ [LMX],*" "*)
+ warning "wrong number of words in: '$op $pid $path $path2'"
+ continue
+ ;;
*,|[LMX],*) ;; # ok
+ [CELMRW],*)
+ warning "wrong number of words in: '$op $pid $path $path2'"
+ continue
+ ;;
*) error "wrong number of words in: '$op $pid $path $path2'";;
esac
# we track cwd and ldir (of interest) per pid
diff --git a/mk/sys.dirdeps.mk b/mk/sys.dirdeps.mk
index bba3bfb9c678..46e76ce4e825 100644
--- a/mk/sys.dirdeps.mk
+++ b/mk/sys.dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: sys.dirdeps.mk,v 1.20 2026/04/24 19:56:01 sjg Exp $
+# $Id: sys.dirdeps.mk,v 1.21 2026/05/22 00:32:40 sjg Exp $
#
# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
#
@@ -15,8 +15,8 @@
# Include from [local.]sys.mk - if doing DIRDEPS_BUILD
# we should not be here otherwise
MK_DIRDEPS_BUILD ?= yes
-MK_META_AUTODEP ?= yes
# these are all implied
+MK_META_AUTODEP ?= yes
MK_AUTO_OBJ ?= yes
MK_META_MODE ?= yes
MK_STAGING ?= yes
diff --git a/os.sh b/os.sh
index f854e83967bb..f854e83967bb 100644..100755
--- a/os.sh
+++ b/os.sh
diff --git a/parse.c b/parse.c
index d08727253d5d..8b047a569054 100644
--- a/parse.c
+++ b/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.756 2026/04/06 17:13:55 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.757 2026/06/09 08:27:08 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -110,7 +110,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.756 2026/04/06 17:13:55 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.757 2026/06/09 08:27:08 rillig Exp $");
/* Detects a multiple-inclusion guard in a makefile. */
typedef enum {
@@ -2838,22 +2838,23 @@ Parse_GuardEndif(void)
static char *
FindSemicolon(char *p)
{
- int depth = 0;
+ if (strchr(p, ';') == NULL)
+ return NULL;
- for (; *p != '\0'; p++) {
- if (*p == '\\' && p[1] != '\0') {
+ while (*p != '\0') {
+ if (*p == ';')
+ return p;
+ if (*p == '$' && p[1] == '$')
+ p += 2;
+ else if (*p == '$') {
+ const char *cp = p;
+ FStr value = Var_Parse(&cp, SCOPE_GLOBAL, VARE_PARSE);
+ FStr_Done(&value);
+ p += cp - p;
+ } else
p++;
- continue;
- }
-
- if (*p == '$' && (p[1] == '(' || p[1] == '{'))
- depth++;
- else if (depth > 0 && (*p == ')' || *p == '}'))
- depth--;
- else if (depth == 0 && *p == ';')
- break;
}
- return p;
+ return NULL;
}
static void
@@ -2864,7 +2865,7 @@ ParseDependencyLine(char *line)
{
char *semicolon = FindSemicolon(line);
- if (*semicolon != '\0') {
+ if (semicolon != NULL) {
/* Terminate the dependency list at the ';' */
*semicolon = '\0';
shellcmd = semicolon + 1;
diff --git a/unit-tests/dep.exp b/unit-tests/dep.exp
index c08f86bc73db..99295ee12b4f 100644
--- a/unit-tests/dep.exp
+++ b/unit-tests/dep.exp
@@ -1,5 +1,17 @@
-make: dep.mk:11: Inconsistent operator for only-colon
-make: dep.mk:13: Inconsistent operator for only-colon
+make: dep.mk:18: Inconsistent operator for only-colon
+ in make[1] in directory "<curdir>"
+make: dep.mk:20: Inconsistent operator for only-colon
+ in make[1] in directory "<curdir>"
make: Fatal errors encountered -- cannot continue
-make: stopped making "all" in unit-tests
-exit status 1
+make: stopped making "parse-only-colon" in unit-tests
+sub exit status 1
+Making renamed-parse-semicolon-in-source out of nothing.
+Making parse-semicolon-in-source from renamed-parse-semicolon-in-source.
+make: Malformed expression at "$$-rest"
+ in make[1] in directory "<curdir>"
+semicolon-in-modifier.param: after semicolon
+parentheses-in-modifier.1-12-23-3: after semicolon
+var-semicolon.: after semicolon
+double-dollar.$-rest: after semicolon
+sub exit status 2
+exit status 0
diff --git a/unit-tests/dep.mk b/unit-tests/dep.mk
index 53fadc789b13..259085e27b1d 100644
--- a/unit-tests/dep.mk
+++ b/unit-tests/dep.mk
@@ -1,8 +1,15 @@
-# $NetBSD: dep.mk,v 1.4 2023/06/01 07:27:30 rillig Exp $
+# $NetBSD: dep.mk,v 1.6 2026/06/09 08:27:08 rillig Exp $
#
# Tests for dependency declarations, such as "target: sources".
-.MAIN: all
+all: .PHONY
+ @${MAKE} -f ${MAKEFILE} parse-only-colon || echo "sub exit status $$?"
+ @${MAKE} -f ${MAKEFILE} parse-semicolon-in-source
+ @${MAKE} -f ${MAKEFILE} parse-semicolon || echo "sub exit status $$?"
+
+
+.if make(parse-only-colon)
+parse-only-colon: .PHONY
# As soon as a target is defined using one of the dependency operators, it is
# restricted to this dependency operator and cannot use the others anymore.
@@ -15,16 +22,47 @@ only-colon::
# would be another error message.
only-colon:
+.endif
+
# Before parse.c 1.158 from 2009-10-07, the parser broke dependency lines at
# the first ';', without parsing expressions as such. It interpreted the
# first ';' as the separator between the dependency and its commands, and the
# '^' as a shell command.
-all: for-subst
-.for file in ${.PARSEFILE}
-for-subst: ${file:S;^;./;g}
- @echo ".for with :S;... OK"
-.endfor
+.if make(parse-semicolon-in-source)
+parse-semicolon-in-source: .PHONY
+
+. for file in parse-semicolon-in-source
+parse-semicolon-in-source: ${file:S;^;renamed-;g}
+ @echo "Making ${.TARGET} from ${.ALLSRC}."
+renamed-parse-semicolon-in-source:
+ @echo "Making ${.TARGET} out of nothing."
+. endfor
+
+.endif
+
+
+# Before parse.c 1.757 from 2026-06-09, the parser counted "$(" and "${" as
+# opening a brace level, and ")" and "}" as closing it. It didn't count a
+# plain "(" or "{" as opening, though. This simple counting resulted in
+# parse errors on modifiers containing parentheses or braces, as well as
+# semicolons.
+.if make(parse-semicolon)
+parse-semicolon: .PHONY
+
+semicolon-in-modifier.${:Utarget:S;target;param;}:; @echo '$@: after semicolon'
+parse-semicolon: semicolon-in-modifier.param
+
+parentheses-in-modifier.${:U123:C;(.);\1-\1;g}:; @echo '$@: after semicolon'
+parse-semicolon: parentheses-in-modifier.1-12-23-3
+
+var-semicolon.$;:; @echo '$@: after semicolon'
+parse-semicolon: var-semicolon.
+# Using actual dollars in target names is still tricky and not recommended,
+# due to multiple levels of expansion, that's where the 8 dollars come from.
+# expect: make: Malformed expression at "$$-rest"
+double-dollar.$$$;-$$$$-rest:; @echo '$@: after semicolon'
+parse-semicolon: double-dollar.$$$$-$$$$$$$$-rest
-all:
+.endif
diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk
index 956285393489..685a4660f01c 100644
--- a/unit-tests/dir.mk
+++ b/unit-tests/dir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dir.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
+# $NetBSD: dir.mk,v 1.12 2026/07/03 15:31:35 sjg Exp $
#
# Tests for dir.c.
@@ -21,7 +21,7 @@ all: {one,two,three}
.if target({one,two,three})
. error
.endif
-.if ${.ALLTARGETS:M{one,two,three}} != "{one,two,three}"
+.if ${.ALLTARGETS:M\{one,two,three\}} != "{one,two,three}"
. error
.endif
diff --git a/unit-tests/varmod-match.mk b/unit-tests/varmod-match.mk
index 5894196c9cd5..0e5664bb3870 100644
--- a/unit-tests/varmod-match.mk
+++ b/unit-tests/varmod-match.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-match.mk,v 1.32 2025/06/29 09:40:13 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.33 2026/07/03 15:31:35 sjg Exp $
#
# Tests for the ':M' modifier, which keeps only those words that match the
# given pattern.
@@ -388,3 +388,13 @@ WORDS= [x- x x- y yyyyy
.if ${:U:${:UM\\\:\\}}
. error
.endif
+
+# :M supports alternation
+.if ${/bin/sh ../Makefile.inc cat .../mk/sys.mk:L:M*{Makefile*,.mk}} != "../Makefile.inc .../mk/sys.mk"
+. error
+.endif
+
+# :M supports alternation
+.if ${{a} one and two {o} yes:L:M\{{a,e,i,o,u}\}} != "{a} {o}"
+. error
+.endif
diff --git a/var.c b/var.c
index 44d1433b464f..b7fe618de948 100644
--- a/var.c
+++ b/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.1180 2026/04/06 17:13:55 rillig Exp $ */
+/* $NetBSD: var.c,v 1.1181 2026/07/03 15:31:35 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -145,7 +145,7 @@
#endif
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.1180 2026/04/06 17:13:55 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.1181 2026/07/03 15:31:35 sjg Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -2822,7 +2822,7 @@ ParseModifier_Match(const char **pp, const ModChain *ch)
}
struct ModifyWord_MatchArgs {
- const char *pattern;
+ StringList patterns;
bool neg;
bool error_reported;
};
@@ -2831,17 +2831,25 @@ static void
ModifyWord_Match(Substring word, SepBuf *buf, void *data)
{
struct ModifyWord_MatchArgs *args = data;
+ StringListNode *ln;
StrMatchResult res;
+ const char *pattern;
+
assert(word.end[0] == '\0'); /* assume null-terminated word */
- res = Str_Match(word.start, args->pattern);
- if (res.error != NULL && !args->error_reported) {
- args->error_reported = true;
- Parse_Error(PARSE_FATAL,
- "%s in pattern \"%s\" of modifier \"%s\"",
- res.error, args->pattern, args->neg ? ":N" : ":M");
+ for (ln = args->patterns.first; ln != NULL; ln = ln->next) {
+ pattern = ln->datum;
+ res = Str_Match(word.start, pattern);
+ if (res.error != NULL && !args->error_reported) {
+ args->error_reported = true;
+ Parse_Error(PARSE_FATAL,
+ "%s in pattern \"%s\" of modifier \"%s\"",
+ res.error, pattern, args->neg ? ":N" : ":M");
+ }
+ if (res.matched != args->neg) {
+ SepBuf_AddSubstring(buf, word);
+ break;
+ }
}
- if (res.matched != args->neg)
- SepBuf_AddSubstring(buf, word);
}
/* :Mpattern or :Npattern */
@@ -2855,10 +2863,30 @@ ApplyModifier_Match(const char **pp, ModChain *ch)
if (ModChain_ShouldEval(ch)) {
struct ModifyWord_MatchArgs args;
- args.pattern = pattern;
+ const char *brace;
+
+ Lst_Init(&args.patterns);
+ if (mod == 'N')
+ brace = NULL; /* not supported */
+ else
+ for (brace = strchr(pattern, '{'); brace != NULL;
+ brace = strchr(++brace, '{')) {
+ if (brace == pattern
+ || (brace[-1] != '\\' && brace[-1] != '$'))
+ break;
+ }
+
+ if (brace)
+ ExpandCurly(pattern, brace, NULL, &args.patterns);
+ else
+ Lst_Append(&args.patterns, pattern);
args.neg = mod == 'N';
args.error_reported = false;
ModifyWords(ch, ModifyWord_Match, &args, ch->oneBigWord);
+ if (brace)
+ Lst_DoneFree(&args.patterns);
+ else
+ Lst_Done(&args.patterns);
}
free(pattern);