diff options
| author | Simon J. Gerraty <sjg@FreeBSD.org> | 2026-07-05 18:22:31 +0000 |
|---|---|---|
| committer | Simon J. Gerraty <sjg@FreeBSD.org> | 2026-07-05 18:22:31 +0000 |
| commit | e988c5eab5231646c612d35ff5b16122cebfbf6a (patch) | |
| tree | 307239878c1f97050623742f4663c68476e81e4f | |
| parent | ef402bba84260816d3e8d6e2439b0bc7eddc9446 (diff) | |
| -rw-r--r-- | ChangeLog | 34 | ||||
| -rw-r--r-- | VERSION | 2 | ||||
| -rw-r--r-- | bmake.1 | 40 | ||||
| -rw-r--r-- | bmake.cat1 | 37 | ||||
| -rw-r--r-- | dir.c | 21 | ||||
| -rw-r--r-- | dir.h | 3 | ||||
| -rw-r--r-- | filemon/filemon_dev.c | 15 | ||||
| -rw-r--r-- | make.1 | 40 | ||||
| -rw-r--r-- | meta.c | 367 | ||||
| -rw-r--r-- | mk/ChangeLog | 34 | ||||
| -rw-r--r-- | mk/dirdeps.mk | 6 | ||||
| -rw-r--r-- | mk/gendirdeps.mk | 20 | ||||
| -rw-r--r-- | mk/install-mk | 4 | ||||
| -rw-r--r-- | mk/install-new.mk | 4 | ||||
| -rw-r--r-- | mk/meta.autodep.mk | 4 | ||||
| -rwxr-xr-x | mk/meta2deps.py | 20 | ||||
| -rwxr-xr-x | mk/meta2deps.sh | 15 | ||||
| -rw-r--r-- | mk/sys.dirdeps.mk | 4 | ||||
| -rwxr-xr-x[-rw-r--r--] | os.sh | 0 | ||||
| -rw-r--r-- | parse.c | 33 | ||||
| -rw-r--r-- | unit-tests/dep.exp | 20 | ||||
| -rw-r--r-- | unit-tests/dep.mk | 54 | ||||
| -rw-r--r-- | unit-tests/dir.mk | 4 | ||||
| -rw-r--r-- | unit-tests/varmod-match.mk | 12 | ||||
| -rw-r--r-- | var.c | 52 |
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 @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20260508 +_MAKE_VERSION=20260704 @@ -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) @@ -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; } @@ -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; @@ -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 @@ -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 @@ -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 @@ -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); |
