diff options
| author | Ron Yorston <rmy@pobox.com> | 2023-10-23 16:17:12 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2023-10-23 16:17:12 +0100 |
| commit | d6b76411684d9cc7a676e98bd9f39daecfe8af36 (patch) | |
| tree | b3a916bdbad2b229b8714f79274d54c75dd8c929 /miscutils | |
| parent | 87a3ddc0620fb3ff022663bf2241b92b483e3cf0 (diff) | |
| download | busybox-w32-d6b76411684d9cc7a676e98bd9f39daecfe8af36.tar.gz busybox-w32-d6b76411684d9cc7a676e98bd9f39daecfe8af36.tar.bz2 busybox-w32-d6b76411684d9cc7a676e98bd9f39daecfe8af36.zip | |
make: fix detection of target rules
The presence of an equal sign in an inline command on a target
rule caused the line to be detected as a macro assignment.
For example:
target:; @echo a = $(a)
Rearrange input parsing so target rules are detected before macro
assignments. This is made more complex by having to allow for the
':=', '::=' and ':::=' assignment operators. (And for targets
containing colons on Windows.)
Costs 240-248 bytes.
Diffstat (limited to 'miscutils')
| -rw-r--r-- | miscutils/make.c | 335 |
1 files changed, 183 insertions, 152 deletions
diff --git a/miscutils/make.c b/miscutils/make.c index b6103bb58..695f77fa6 100644 --- a/miscutils/make.c +++ b/miscutils/make.c | |||
| @@ -438,8 +438,13 @@ check_name(const char *name) | |||
| 438 | { | 438 | { |
| 439 | const char *s; | 439 | const char *s; |
| 440 | 440 | ||
| 441 | if (!posix) | 441 | if (!posix) { |
| 442 | for (s = name; *s; ++s) { | ||
| 443 | if (*s == '=') | ||
| 444 | return FALSE; | ||
| 445 | } | ||
| 442 | return TRUE; | 446 | return TRUE; |
| 447 | } | ||
| 443 | 448 | ||
| 444 | for (s = name; *s; ++s) { | 449 | for (s = name; *s; ++s) { |
| 445 | #if ENABLE_PLATFORM_MINGW32 | 450 | #if ENABLE_PLATFORM_MINGW32 |
| @@ -1157,44 +1162,55 @@ modify_words(const char *val, int modifier, size_t lenf, size_t lenr, | |||
| 1157 | } | 1162 | } |
| 1158 | 1163 | ||
| 1159 | /* | 1164 | /* |
| 1160 | * Return a pointer to the next instance of a given character. Macro | 1165 | * Try to detect a target rule by searching for a colon that isn't part |
| 1161 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't | 1166 | * of a macro assignment. Macros must have been expanded already. Return |
| 1162 | * detected as separators for rules or macro definitions. | 1167 | * a pointer to the colon or NULL. |
| 1163 | */ | 1168 | */ |
| 1164 | static char * | 1169 | static char * |
| 1165 | find_char(const char *str, int c) | 1170 | find_colon(char *p) |
| 1166 | { | 1171 | { |
| 1167 | const char *s; | 1172 | char *q; |
| 1168 | 1173 | ||
| 1169 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { | 1174 | #if ENABLE_PLATFORM_MINGW32 |
| 1170 | if (*s == c) | 1175 | for (q = p; (q = strchr(q, ':')); ++q) { |
| 1171 | return (char *)s; | 1176 | if (posix && !(pragma & P_WINDOWS)) |
| 1177 | break; | ||
| 1178 | if (q[1] != '/') | ||
| 1179 | break; | ||
| 1180 | } | ||
| 1181 | if (q != NULL) { | ||
| 1182 | #else | ||
| 1183 | if ((q = strchr(p, ':')) != NULL) { | ||
| 1184 | #endif | ||
| 1185 | // Skip ':=', '::=' and ':::=' macro assignments | ||
| 1186 | if ( | ||
| 1187 | // '::=' and ':::=' are from POSIX 202X. | ||
| 1188 | !(!POSIX_2017 && q[1] == ':' && q[2] == ':' && q[3] == '=') && | ||
| 1189 | !(!POSIX_2017 && q[1] == ':' && q[2] == '=') && | ||
| 1190 | // ':=' is a non-POSIX extension | ||
| 1191 | !(!posix && q[1] == '=') | ||
| 1192 | ) | ||
| 1193 | return q; | ||
| 1172 | } | 1194 | } |
| 1173 | return NULL; | 1195 | return NULL; |
| 1174 | } | 1196 | } |
| 1175 | 1197 | ||
| 1176 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1177 | /* | 1198 | /* |
| 1178 | * Ignore colons in targets of the form c:/path when looking for a | 1199 | * Return a pointer to the next instance of a given character. Macro |
| 1179 | * target rule. | 1200 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't |
| 1201 | * detected as separators for rules or macro definitions. | ||
| 1180 | */ | 1202 | */ |
| 1181 | static char * | 1203 | static char * |
| 1182 | find_colon(const char *str) | 1204 | find_char(const char *str, int c) |
| 1183 | { | 1205 | { |
| 1184 | const char *s = str; | 1206 | const char *s; |
| 1185 | 1207 | ||
| 1186 | while ((s = find_char(s, ':'))) { | 1208 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { |
| 1187 | if (posix && !(pragma & P_WINDOWS)) | 1209 | if (*s == c) |
| 1188 | break; | 1210 | return (char *)s; |
| 1189 | if (s[1] != '/') | ||
| 1190 | break; | ||
| 1191 | ++s; | ||
| 1192 | } | 1211 | } |
| 1193 | return (char *)s; | 1212 | return NULL; |
| 1194 | } | 1213 | } |
| 1195 | #else | ||
| 1196 | # define find_colon(s) find_char(s, ':') | ||
| 1197 | #endif | ||
| 1198 | 1214 | ||
| 1199 | /* | 1215 | /* |
| 1200 | * Recursively expand any macros in str to an allocated string. | 1216 | * Recursively expand any macros in str to an allocated string. |
| @@ -1881,7 +1897,147 @@ input(FILE *fd, int ilevel) | |||
| 1881 | goto end_loop; | 1897 | goto end_loop; |
| 1882 | } | 1898 | } |
| 1883 | 1899 | ||
| 1884 | // Check for a macro definition | 1900 | // Check for target rule |
| 1901 | a = p = expanded = expand_macros(str, FALSE); | ||
| 1902 | if ((q = find_colon(p)) != NULL) { | ||
| 1903 | // All tokens before ':' must be valid targets | ||
| 1904 | *q = '\0'; | ||
| 1905 | while ((a = gettok(&p)) != NULL && is_valid_target(a)) | ||
| 1906 | ; | ||
| 1907 | } | ||
| 1908 | free(expanded); | ||
| 1909 | |||
| 1910 | if (a == NULL) { | ||
| 1911 | // Looks like a target rule | ||
| 1912 | p = expanded = expand_macros(str, FALSE); | ||
| 1913 | |||
| 1914 | // Look for colon separator | ||
| 1915 | q = find_colon(p); | ||
| 1916 | if (q == NULL) | ||
| 1917 | error("expected separator"); | ||
| 1918 | |||
| 1919 | *q++ = '\0'; // Separate targets and prerequisites | ||
| 1920 | |||
| 1921 | // Double colon | ||
| 1922 | dbl = !posix && *q == ':'; | ||
| 1923 | if (dbl) | ||
| 1924 | q++; | ||
| 1925 | |||
| 1926 | // Look for semicolon separator | ||
| 1927 | cp = NULL; | ||
| 1928 | s = strchr(q, ';'); | ||
| 1929 | if (s) { | ||
| 1930 | *s = '\0'; | ||
| 1931 | // Retrieve command from copy of line | ||
| 1932 | if ((p = find_char(copy, ':')) && (p = strchr(p, ';'))) | ||
| 1933 | newcmd(&cp, process_command(p + 1)); | ||
| 1934 | } | ||
| 1935 | semicolon_cmd = cp != NULL; | ||
| 1936 | |||
| 1937 | // Create list of prerequisites | ||
| 1938 | dp = NULL; | ||
| 1939 | while (((p = gettok(&q)) != NULL)) { | ||
| 1940 | char *newp = NULL; | ||
| 1941 | |||
| 1942 | if (!posix) { | ||
| 1943 | // Allow prerequisites of form library(member1 member2). | ||
| 1944 | // Leading and trailing spaces in the brackets are skipped. | ||
| 1945 | if (!lib) { | ||
| 1946 | s = strchr(p, '('); | ||
| 1947 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { | ||
| 1948 | // Looks like an unterminated archive member | ||
| 1949 | // with a terminator later on the line. | ||
| 1950 | lib = p; | ||
| 1951 | if (s[1] != '\0') { | ||
| 1952 | p = newp = auto_concat(lib, ")"); | ||
| 1953 | s[1] = '\0'; | ||
| 1954 | } else { | ||
| 1955 | continue; | ||
| 1956 | } | ||
| 1957 | } | ||
| 1958 | } else if (ends_with_bracket(p)) { | ||
| 1959 | if (*p != ')') | ||
| 1960 | p = newp = auto_concat(lib, p); | ||
| 1961 | lib = NULL; | ||
| 1962 | if (newp == NULL) | ||
| 1963 | continue; | ||
| 1964 | } else { | ||
| 1965 | p = newp = auto_string(xasprintf("%s%s)", lib, p)); | ||
| 1966 | } | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | // If not in POSIX mode expand wildcards in the name. | ||
| 1970 | nfile = 1; | ||
| 1971 | files = &p; | ||
| 1972 | if (!posix && wildcard(p, &gd)) { | ||
| 1973 | nfile = gd.gl_pathc; | ||
| 1974 | files = gd.gl_pathv; | ||
| 1975 | } | ||
| 1976 | for (i = 0; i < nfile; ++i) { | ||
| 1977 | if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) | ||
| 1978 | continue; | ||
| 1979 | np = newname(files[i]); | ||
| 1980 | newdep(&dp, np); | ||
| 1981 | } | ||
| 1982 | if (files != &p) | ||
| 1983 | globfree(&gd); | ||
| 1984 | free(newp); | ||
| 1985 | } | ||
| 1986 | lib = NULL; | ||
| 1987 | |||
| 1988 | // Create list of commands | ||
| 1989 | startno = dispno; | ||
| 1990 | while ((str2 = readline(fd)) && *str2 == '\t') { | ||
| 1991 | newcmd(&cp, process_command(str2)); | ||
| 1992 | free(str2); | ||
| 1993 | } | ||
| 1994 | dispno = startno; | ||
| 1995 | |||
| 1996 | // Create target names and attach rule to them | ||
| 1997 | q = expanded; | ||
| 1998 | count = 0; | ||
| 1999 | seen_inference = FALSE; | ||
| 2000 | while ((p = gettok(&q)) != NULL) { | ||
| 2001 | // If not in POSIX mode expand wildcards in the name. | ||
| 2002 | nfile = 1; | ||
| 2003 | files = &p; | ||
| 2004 | if (!posix && wildcard(p, &gd)) { | ||
| 2005 | nfile = gd.gl_pathc; | ||
| 2006 | files = gd.gl_pathv; | ||
| 2007 | } | ||
| 2008 | for (i = 0; i < nfile; ++i) { | ||
| 2009 | int ttype = target_type(files[i]); | ||
| 2010 | |||
| 2011 | np = newname(files[i]); | ||
| 2012 | if (ttype != T_NORMAL) { | ||
| 2013 | if (ttype == T_INFERENCE && posix) { | ||
| 2014 | if (semicolon_cmd) | ||
| 2015 | error_in_inference_rule("'; command'"); | ||
| 2016 | seen_inference = TRUE; | ||
| 2017 | } | ||
| 2018 | np->n_flag |= N_SPECIAL; | ||
| 2019 | } else if (!firstname) { | ||
| 2020 | firstname = np; | ||
| 2021 | } | ||
| 2022 | addrule(np, dp, cp, dbl); | ||
| 2023 | count++; | ||
| 2024 | } | ||
| 2025 | if (files != &p) | ||
| 2026 | globfree(&gd); | ||
| 2027 | } | ||
| 2028 | if (seen_inference && count != 1) | ||
| 2029 | error_in_inference_rule("multiple targets"); | ||
| 2030 | |||
| 2031 | // Prerequisites and commands will be unused if there were | ||
| 2032 | // no targets. Avoid leaking memory. | ||
| 2033 | if (count == 0) { | ||
| 2034 | freedeps(dp); | ||
| 2035 | freecmds(cp); | ||
| 2036 | } | ||
| 2037 | goto end_loop; | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | // If we get here it must be a macro definition | ||
| 1885 | q = find_char(str, '='); | 2041 | q = find_char(str, '='); |
| 1886 | if (q != NULL) { | 2042 | if (q != NULL) { |
| 1887 | int level = (useenv || fd == NULL) ? 4 : 3; | 2043 | int level = (useenv || fd == NULL) ? 4 : 3; |
| @@ -1966,135 +2122,10 @@ input(FILE *fd, int ilevel) | |||
| 1966 | } | 2122 | } |
| 1967 | setmacro(a, q, level); | 2123 | setmacro(a, q, level); |
| 1968 | free(newq); | 2124 | free(newq); |
| 1969 | goto end_loop; | 2125 | } else { |
| 1970 | } | 2126 | error("expected macro definition"); |
| 1971 | |||
| 1972 | // If we get here it must be a target rule | ||
| 1973 | p = expanded = expand_macros(str, FALSE); | ||
| 1974 | |||
| 1975 | // Look for colon separator | ||
| 1976 | q = find_colon(p); | ||
| 1977 | if (q == NULL) | ||
| 1978 | error("expected separator"); | ||
| 1979 | |||
| 1980 | *q++ = '\0'; // Separate targets and prerequisites | ||
| 1981 | |||
| 1982 | // Double colon | ||
| 1983 | dbl = !posix && *q == ':'; | ||
| 1984 | if (dbl) | ||
| 1985 | q++; | ||
| 1986 | |||
| 1987 | // Look for semicolon separator | ||
| 1988 | cp = NULL; | ||
| 1989 | s = strchr(q, ';'); | ||
| 1990 | if (s) { | ||
| 1991 | *s = '\0'; | ||
| 1992 | // Retrieve command from copy of line | ||
| 1993 | if ((p = find_colon(copy)) && (p = strchr(p, ';'))) | ||
| 1994 | newcmd(&cp, process_command(p + 1)); | ||
| 1995 | } | ||
| 1996 | semicolon_cmd = cp != NULL; | ||
| 1997 | |||
| 1998 | // Create list of prerequisites | ||
| 1999 | dp = NULL; | ||
| 2000 | while (((p = gettok(&q)) != NULL)) { | ||
| 2001 | char *newp = NULL; | ||
| 2002 | |||
| 2003 | if (!posix) { | ||
| 2004 | // Allow prerequisites of form library(member1 member2). | ||
| 2005 | // Leading and trailing spaces in the brackets are skipped. | ||
| 2006 | if (!lib) { | ||
| 2007 | s = strchr(p, '('); | ||
| 2008 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { | ||
| 2009 | // Looks like an unterminated archive member | ||
| 2010 | // with a terminator later on the line. | ||
| 2011 | lib = p; | ||
| 2012 | if (s[1] != '\0') { | ||
| 2013 | p = newp = auto_concat(lib, ")"); | ||
| 2014 | s[1] = '\0'; | ||
| 2015 | } else { | ||
| 2016 | continue; | ||
| 2017 | } | ||
| 2018 | } | ||
| 2019 | } else if (ends_with_bracket(p)) { | ||
| 2020 | if (*p != ')') | ||
| 2021 | p = newp = auto_concat(lib, p); | ||
| 2022 | lib = NULL; | ||
| 2023 | if (newp == NULL) | ||
| 2024 | continue; | ||
| 2025 | } else { | ||
| 2026 | p = newp = auto_string(xasprintf("%s%s)", lib, p)); | ||
| 2027 | } | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | // If not in POSIX mode expand wildcards in the name. | ||
| 2031 | nfile = 1; | ||
| 2032 | files = &p; | ||
| 2033 | if (!posix && wildcard(p, &gd)) { | ||
| 2034 | nfile = gd.gl_pathc; | ||
| 2035 | files = gd.gl_pathv; | ||
| 2036 | } | ||
| 2037 | for (i = 0; i < nfile; ++i) { | ||
| 2038 | if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) | ||
| 2039 | continue; | ||
| 2040 | np = newname(files[i]); | ||
| 2041 | newdep(&dp, np); | ||
| 2042 | } | ||
| 2043 | if (files != &p) | ||
| 2044 | globfree(&gd); | ||
| 2045 | free(newp); | ||
| 2046 | } | 2127 | } |
| 2047 | lib = NULL; | ||
| 2048 | 2128 | ||
| 2049 | // Create list of commands | ||
| 2050 | startno = dispno; | ||
| 2051 | while ((str2 = readline(fd)) && *str2 == '\t') { | ||
| 2052 | newcmd(&cp, process_command(str2)); | ||
| 2053 | free(str2); | ||
| 2054 | } | ||
| 2055 | dispno = startno; | ||
| 2056 | |||
| 2057 | // Create target names and attach rule to them | ||
| 2058 | q = expanded; | ||
| 2059 | count = 0; | ||
| 2060 | seen_inference = FALSE; | ||
| 2061 | while ((p = gettok(&q)) != NULL) { | ||
| 2062 | // If not in POSIX mode expand wildcards in the name. | ||
| 2063 | nfile = 1; | ||
| 2064 | files = &p; | ||
| 2065 | if (!posix && wildcard(p, &gd)) { | ||
| 2066 | nfile = gd.gl_pathc; | ||
| 2067 | files = gd.gl_pathv; | ||
| 2068 | } | ||
| 2069 | for (i = 0; i < nfile; ++i) { | ||
| 2070 | int ttype = target_type(files[i]); | ||
| 2071 | |||
| 2072 | np = newname(files[i]); | ||
| 2073 | if (ttype != T_NORMAL) { | ||
| 2074 | if (ttype == T_INFERENCE && posix) { | ||
| 2075 | if (semicolon_cmd) | ||
| 2076 | error_in_inference_rule("'; command'"); | ||
| 2077 | seen_inference = TRUE; | ||
| 2078 | } | ||
| 2079 | np->n_flag |= N_SPECIAL; | ||
| 2080 | } else if (!firstname) { | ||
| 2081 | firstname = np; | ||
| 2082 | } | ||
| 2083 | addrule(np, dp, cp, dbl); | ||
| 2084 | count++; | ||
| 2085 | } | ||
| 2086 | if (files != &p) | ||
| 2087 | globfree(&gd); | ||
| 2088 | } | ||
| 2089 | if (seen_inference && count != 1) | ||
| 2090 | error_in_inference_rule("multiple targets"); | ||
| 2091 | |||
| 2092 | // Prerequisites and commands will be unused if there were | ||
| 2093 | // no targets. Avoid leaking memory. | ||
| 2094 | if (count == 0) { | ||
| 2095 | freedeps(dp); | ||
| 2096 | freecmds(cp); | ||
| 2097 | } | ||
| 2098 | end_loop: | 2129 | end_loop: |
| 2099 | free(str1); | 2130 | free(str1); |
| 2100 | dispno = lineno; | 2131 | dispno = lineno; |
