marc@sun-valley.mit.edu (Marc Ullman) (07/06/89)
B U G R E P O R T Program: GNU Make Version: 3.48 Computer: Sun 3 Operating System: SunOS 4.0.1 Reported By: Marc Ullman (marc@sun-valley.stanford.edu) Date: July 5, 1989 gnumake has a bug with nested variable references when text strings contain parentheses "(",")" and/or braces "{", "}" as the following example makefile shows: SOURCES = src1.c src2.c src3.c LIB_NAME = testlib.a LIB_OBJS = ${SOURCES:%.c=$(LIB_NAME)(%.o)} all : @echo "$(LIB_OBJS)" running gnumake on this file produces the following result: testlib.a(src1.o testlib.a(src2.o testlib.a(src3.o) ^ ^ | | Missing parentheses The bug results from an incorrect assumption in the function variable_expand() in the file variable.c. A fragment of this function is shown below: /* Is there a variable reference inside the parens or braces? If so, expand it before expanding the entire reference. */ p1 = index (beg, closeparen); if (p1 != 0) p1 = lindex (beg, p1, '$'); if (p1 != 0) { /* BEG now points past the opening paren or brace. Count parens or braces until it is matched. */ int count = 0; for (p = beg; *p != '\0'; ++p) { if (*p == openparen) ++count; else if (*p == closeparen && --count < 0) break; } /* If count is >= 0, there were unmatched opening parens or braces, so we go to the simple case of a variable name such as `$($(a)'. */ if (count < 0) { char *name = expand_argument (beg, p); p1 = concat ("$(", name, ")"); free (name); name = expand_argument (p1, p1 + strlen (p1)); o = variable_buffer_output (o, name, strlen (name)); free (name); break; } } The line p1 = concat ("$(", name, ")"); blindly restores parentheses around the previously evaluated expression rather than restoring the appropriate delimiters depending on the current state of the variables "openparen" and "closeparen". A possible fix is shown below: if (count < 0) { char var_start_str[3] = {'$', openparen, '\0' }; /* NEW */ char var_end_str[2] = {closeparen, '\0'}; /* NEW */ char *name = expand_argument (beg, p); p1 = concat (var_start_str, name, var_end_str); /* REVISED */ free (name); name = expand_argument (p1, p1 + strlen (p1)); o = variable_buffer_output (o, name, strlen (name)); free (name); break; } With this revision, the sample makefile shown above yields the desirerd result: testlib.a(src1.o) testlib.a(src2.o) testlib.a(src3.o)