aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2024-05-31 10:27:18 +0100
committerRon Yorston <rmy@pobox.com>2024-05-31 10:27:18 +0100
commitf9d10b2b6314ea2a80515112498aaa919ad81c97 (patch)
tree3f199636f02b16f94f12cd9fe65b04fd087123f8
parent2a0923c400fe5df140e1c5aad8dc59f4733e8598 (diff)
downloadbusybox-w32-f9d10b2b6314ea2a80515112498aaa919ad81c97.tar.gz
busybox-w32-f9d10b2b6314ea2a80515112498aaa919ad81c97.tar.bz2
busybox-w32-f9d10b2b6314ea2a80515112498aaa919ad81c97.zip
make: fix detection of target rules (take 2)
Commit d6b764116 (make: fix detection of target rules) checked for target rules before macro assignments. This failed for some Makefiles generated by autotools because partially defined macros were expanded while testing for a target rule. Revert to checking for macro assignments first, but try to detect if the proposed left hand side of the assignment might form part of a target rule with an inline command. Also handle the case where the ';' separator of the inline command has been obfuscated by putting it in a macro. Saves 128-160 bytes. (GitHub pdpmake issues 31, 44)
-rw-r--r--miscutils/make.c338
-rwxr-xr-xtestsuite/make.tests23
2 files changed, 177 insertions, 184 deletions
diff --git a/miscutils/make.c b/miscutils/make.c
index 8d5664500..02da3611c 100644
--- a/miscutils/make.c
+++ b/miscutils/make.c
@@ -1223,40 +1223,6 @@ modify_words(const char *val, int modifier, size_t lenf, size_t lenr,
1223} 1223}
1224 1224
1225/* 1225/*
1226 * Try to detect a target rule by searching for a colon that isn't part
1227 * of a macro assignment. Macros must have been expanded already. Return
1228 * a pointer to the colon or NULL.
1229 */
1230static char *
1231find_colon(char *p)
1232{
1233 char *q;
1234
1235#if ENABLE_PLATFORM_MINGW32
1236 for (q = p; (q = strchr(q, ':')); ++q) {
1237 if (posix && !(pragma & P_WINDOWS))
1238 break;
1239 if (q == p || !isalpha(q[-1]) || q[1] != '/')
1240 break;
1241 }
1242 if (q != NULL) {
1243#else
1244 if ((q = strchr(p, ':')) != NULL) {
1245#endif
1246 // Skip ':=', '::=' and ':::=' macro assignments
1247 if (
1248 // '::=' and ':::=' are from POSIX 202X.
1249 !(!POSIX_2017 && q[1] == ':' && q[2] == ':' && q[3] == '=') &&
1250 !(!POSIX_2017 && q[1] == ':' && q[2] == '=') &&
1251 // ':=' is a non-POSIX extension
1252 !(!posix && q[1] == '=')
1253 )
1254 return q;
1255 }
1256 return NULL;
1257}
1258
1259/*
1260 * Return a pointer to the next instance of a given character. Macro 1226 * Return a pointer to the next instance of a given character. Macro
1261 * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't 1227 * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't
1262 * detected as separators for rules or macro definitions. 1228 * detected as separators for rules or macro definitions.
@@ -2032,159 +1998,22 @@ input(FILE *fd, int ilevel)
2032 goto end_loop; 1998 goto end_loop;
2033 } 1999 }
2034 2000
2035 // Check for target rule 2001 // Check for a macro definition
2036 a = p = expanded = expand_macros(str, FALSE); 2002 if (find_char(str, '=') != NULL) {
2037 if ((q = find_colon(p)) != NULL) {
2038 // All tokens before ':' must be valid targets
2039 *q = '\0';
2040 while ((a = gettok(&p)) != NULL && is_valid_target(a))
2041 ;
2042 }
2043 free(expanded);
2044
2045 if (a == NULL) {
2046 // Looks like a target rule
2047 p = expanded = expand_macros(str, FALSE);
2048
2049 // Look for colon separator
2050 q = find_colon(p);
2051 if (q == NULL)
2052 error("expected separator");
2053
2054 *q++ = '\0'; // Separate targets and prerequisites
2055
2056 // Double colon
2057 dbl = !posix && *q == ':';
2058 if (dbl)
2059 q++;
2060
2061 // Look for semicolon separator
2062 cp = NULL;
2063 s = strchr(q, ';');
2064 if (s) {
2065 *s = '\0';
2066 // Retrieve command from copy of line
2067 if ((p = find_char(copy, ':')) && (p = strchr(p, ';')))
2068 newcmd(&cp, process_command(p + 1));
2069 }
2070 semicolon_cmd = cp != NULL;
2071
2072 // Create list of prerequisites
2073 dp = NULL;
2074 while (((p = gettok(&q)) != NULL)) {
2075 char *newp = NULL;
2076
2077 if (!posix) {
2078 // Allow prerequisites of form library(member1 member2).
2079 // Leading and trailing spaces in the brackets are skipped.
2080 if (!lib) {
2081 s = strchr(p, '(');
2082 if (s && !ends_with_bracket(s) && strchr(q, ')')) {
2083 // Looks like an unterminated archive member
2084 // with a terminator later on the line.
2085 lib = p;
2086 if (s[1] != '\0') {
2087 p = newp = auto_concat(lib, ")");
2088 s[1] = '\0';
2089 } else {
2090 continue;
2091 }
2092 }
2093 } else if (ends_with_bracket(p)) {
2094 if (*p != ')')
2095 p = newp = auto_concat(lib, p);
2096 lib = NULL;
2097 if (newp == NULL)
2098 continue;
2099 } else {
2100 p = newp = auto_string(xasprintf("%s%s)", lib, p));
2101 }
2102 }
2103
2104 // If not in POSIX mode expand wildcards in the name.
2105 nfile = 1;
2106 files = &p;
2107 if (!posix && wildcard(p, &gd)) {
2108 nfile = gd.gl_pathc;
2109 files = gd.gl_pathv;
2110 }
2111 for (i = 0; i < nfile; ++i) {
2112 if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0)
2113 continue;
2114 np = newname(files[i]);
2115 newdep(&dp, np);
2116 }
2117 if (files != &p)
2118 globfree(&gd);
2119 free(newp);
2120 }
2121 lib = NULL;
2122
2123 // Create list of commands
2124 startno = dispno;
2125 while ((str2 = readline(fd)) && *str2 == '\t') {
2126 newcmd(&cp, process_command(str2));
2127 free(str2);
2128 }
2129 dispno = startno;
2130
2131 // Create target names and attach rule to them
2132 q = expanded;
2133 count = 0;
2134 seen_inference = FALSE;
2135 while ((p = gettok(&q)) != NULL) {
2136 // If not in POSIX mode expand wildcards in the name.
2137 nfile = 1;
2138 files = &p;
2139 if (!posix && wildcard(p, &gd)) {
2140 nfile = gd.gl_pathc;
2141 files = gd.gl_pathv;
2142 }
2143 for (i = 0; i < nfile; ++i) {
2144 int ttype = target_type(files[i]);
2145
2146 np = newname(files[i]);
2147 if (ttype != T_NORMAL) {
2148 if (ttype == T_INFERENCE && posix) {
2149 if (semicolon_cmd)
2150 error_in_inference_rule("'; command'");
2151 seen_inference = TRUE;
2152 }
2153 np->n_flag |= N_SPECIAL;
2154 } else if (!firstname) {
2155 firstname = np;
2156 }
2157 addrule(np, dp, cp, dbl);
2158 count++;
2159 }
2160 if (files != &p)
2161 globfree(&gd);
2162 }
2163 if (seen_inference && count != 1)
2164 error_in_inference_rule("multiple targets");
2165
2166 // Prerequisites and commands will be unused if there were
2167 // no targets. Avoid leaking memory.
2168 if (count == 0) {
2169 freedeps(dp);
2170 freecmds(cp);
2171 }
2172 goto end_loop;
2173 }
2174
2175 // If we get here it must be a macro definition
2176 q = find_char(str, '=');
2177 if (q != NULL) {
2178 int level = (useenv || fd == NULL) ? 4 : 3; 2003 int level = (useenv || fd == NULL) ? 4 : 3;
2004 // Use a copy of the line: we might need the original
2005 // if this turns out to be a target rule.
2006 char *copy2 = xstrdup(str);
2179 char *newq = NULL; 2007 char *newq = NULL;
2180 char eq = '\0'; 2008 char eq = '\0';
2009 q = find_char(copy2, '='); // q can't be NULL
2181 2010
2182 if (q - 1 > str) { 2011 if (q - 1 > copy2) {
2183 switch (q[-1]) { 2012 switch (q[-1]) {
2184 case ':': 2013 case ':':
2185 // '::=' and ':::=' are from POSIX 202X. 2014 // '::=' and ':::=' are from POSIX 202X.
2186 if (!POSIX_2017 && q - 2 > str && q[-2] == ':') { 2015 if (!POSIX_2017 && q - 2 > copy2 && q[-2] == ':') {
2187 if (q - 3 > str && q[-3] == ':') { 2016 if (q - 3 > copy2 && q[-3] == ':') {
2188 eq = 'B'; // BSD-style ':=' 2017 eq = 'B'; // BSD-style ':='
2189 q[-3] = '\0'; 2018 q[-3] = '\0';
2190 } else { 2019 } else {
@@ -2216,8 +2045,19 @@ input(FILE *fd, int ilevel)
2216 *p = '\0'; 2045 *p = '\0';
2217 2046
2218 // Expand left-hand side of assignment 2047 // Expand left-hand side of assignment
2219 p = expanded = expand_macros(str, FALSE); 2048 p = expanded = expand_macros(copy2, FALSE);
2220 if ((a = gettok(&p)) == NULL || gettok(&p)) 2049 if ((a = gettok(&p)) == NULL)
2050 error("invalid macro assignment");
2051
2052 // If the expanded LHS contains ':' and ';' it can't be a
2053 // macro assignment but it might be a target rule.
2054 if ((s = strchr(a, ':')) != NULL && strchr(s, ';') != NULL) {
2055 free(expanded);
2056 free(copy2);
2057 goto try_target;
2058 }
2059
2060 if (gettok(&p))
2221 error("invalid macro assignment"); 2061 error("invalid macro assignment");
2222 2062
2223 if (eq == ':') { 2063 if (eq == ':') {
@@ -2257,8 +2097,138 @@ input(FILE *fd, int ilevel)
2257 } 2097 }
2258 setmacro(a, q, level); 2098 setmacro(a, q, level);
2259 free(newq); 2099 free(newq);
2260 } else { 2100 free(copy2);
2261 error("missing separator"); 2101 goto end_loop;
2102 }
2103
2104 // If we get here it must be a target rule
2105 try_target:
2106 p = expanded = expand_macros(str, FALSE);
2107
2108 // Look for colon separator
2109 q = find_char(p, ':');
2110 if (q == NULL)
2111 error("expected separator");
2112
2113 *q++ = '\0'; // Separate targets and prerequisites
2114
2115 // Double colon
2116 dbl = !posix && *q == ':';
2117 if (dbl)
2118 q++;
2119
2120 // Look for semicolon separator
2121 cp = NULL;
2122 s = strchr(q, ';');
2123 if (s) {
2124 // Retrieve command from expanded copy of line
2125 char *copy3 = expand_macros(copy, FALSE);
2126 if ((p = find_char(copy3, ':')) && (p = strchr(p, ';')))
2127 newcmd(&cp, process_command(p + 1));
2128 free(copy3);
2129 *s = '\0';
2130 }
2131 semicolon_cmd = cp != NULL;
2132
2133 // Create list of prerequisites
2134 dp = NULL;
2135 while (((p = gettok(&q)) != NULL)) {
2136 char *newp = NULL;
2137
2138 if (!posix) {
2139 // Allow prerequisites of form library(member1 member2).
2140 // Leading and trailing spaces in the brackets are skipped.
2141 if (!lib) {
2142 s = strchr(p, '(');
2143 if (s && !ends_with_bracket(s) && strchr(q, ')')) {
2144 // Looks like an unterminated archive member
2145 // with a terminator later on the line.
2146 lib = p;
2147 if (s[1] != '\0') {
2148 p = newp = auto_concat(lib, ")");
2149 s[1] = '\0';
2150 } else {
2151 continue;
2152 }
2153 }
2154 } else if (ends_with_bracket(p)) {
2155 if (*p != ')')
2156 p = newp = auto_concat(lib, p);
2157 lib = NULL;
2158 if (newp == NULL)
2159 continue;
2160 } else {
2161 p = newp = auto_string(xasprintf("%s%s)", lib, p));
2162 }
2163 }
2164
2165 // If not in POSIX mode expand wildcards in the name.
2166 nfile = 1;
2167 files = &p;
2168 if (!posix && wildcard(p, &gd)) {
2169 nfile = gd.gl_pathc;
2170 files = gd.gl_pathv;
2171 }
2172 for (i = 0; i < nfile; ++i) {
2173 if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0)
2174 continue;
2175 np = newname(files[i]);
2176 newdep(&dp, np);
2177 }
2178 if (files != &p)
2179 globfree(&gd);
2180 free(newp);
2181 }
2182 lib = NULL;
2183
2184 // Create list of commands
2185 startno = dispno;
2186 while ((str2 = readline(fd)) && *str2 == '\t') {
2187 newcmd(&cp, process_command(str2));
2188 free(str2);
2189 }
2190 dispno = startno;
2191
2192 // Create target names and attach rule to them
2193 q = expanded;
2194 count = 0;
2195 seen_inference = FALSE;
2196 while ((p = gettok(&q)) != NULL) {
2197 // If not in POSIX mode expand wildcards in the name.
2198 nfile = 1;
2199 files = &p;
2200 if (!posix && wildcard(p, &gd)) {
2201 nfile = gd.gl_pathc;
2202 files = gd.gl_pathv;
2203 }
2204 for (i = 0; i < nfile; ++i) {
2205 int ttype = target_type(files[i]);
2206
2207 np = newname(files[i]);
2208 if (ttype != T_NORMAL) {
2209 if (ttype == T_INFERENCE && posix) {
2210 if (semicolon_cmd)
2211 error_in_inference_rule("'; command'");
2212 seen_inference = TRUE;
2213 }
2214 np->n_flag |= N_SPECIAL;
2215 } else if (!firstname) {
2216 firstname = np;
2217 }
2218 addrule(np, dp, cp, dbl);
2219 count++;
2220 }
2221 if (files != &p)
2222 globfree(&gd);
2223 }
2224 if (seen_inference && count != 1)
2225 error_in_inference_rule("multiple targets");
2226
2227 // Prerequisites and commands will be unused if there were
2228 // no targets. Avoid leaking memory.
2229 if (count == 0) {
2230 freedeps(dp);
2231 freecmds(cp);
2262 } 2232 }
2263 2233
2264 end_loop: 2234 end_loop:
diff --git a/testsuite/make.tests b/testsuite/make.tests
index 0397ab4de..6438c90c9 100755
--- a/testsuite/make.tests
+++ b/testsuite/make.tests
@@ -103,6 +103,29 @@ a = a
103target:;@echo a = $(a) 103target:;@echo a = $(a)
104' 104'
105 105
106# Ensure an inline command on a target rule can be detected even if
107# the semicolon is obfuscated.
108testing "make equal sign in obfuscated inline command" \
109 "make -f -" "a = a\n" "" '
110a = a
111semi = ;
112target:$(semi)@echo a = $(a)
113'
114
115# The fix for the above test broke a complex chain of macro assignments
116# generated by autotools.
117testing "make complex chain of macro assignments" \
118 "make -f -" "flag 1\n" "" '
119FLAG_ = $(FLAG_$(VALUE))
120FLAG_0 = flag 0
121FLAG_1 = flag 1
122MYFLAG = $(FLAG_$(VALUE))
123VALUE = 1
124
125target:
126 @echo $(MYFLAG)
127'
128
106# When a build command fails and the '-k' option has been provided 129# When a build command fails and the '-k' option has been provided
107# (continue execution on error) no further commands should be executed 130# (continue execution on error) no further commands should be executed
108# for the current target. 131# for the current target.