Module Name: src
Committed By: christos
Date: Sat Aug 23 14:50:24 UTC 2014
Modified Files:
src/tests/usr.bin/make: t_make.sh
src/usr.bin/make: make.1 parse.c var.c
Added Files:
src/tests/usr.bin/make: d_posix.mk d_posix.out
Log Message:
PR/49085: Jarmo Jaakkola: fix several parsing errors
Don't exit from var.c:Var_Parse() before possible modifiers are handled
on D and F modified versions of local variables. Properly expand $(?D)
and $(?F) too.
Make line continuations in rule's commands POSIX compliant.
Fix the syntax error caused by lib(member) as the last target before
a dependency operator.
Document the line continuation change in the manual page. Also talk
more about the POSIX style local variables and their modifiers.
Add tests covering the fixed problems into d_posix.mk. The test is
a known failure at the moment because of PR 49086 and PR 49092.
[XXX: unconverted tests]
To generate a diff of this commit:
cvs rdiff -u -r0 -r1.3 src/tests/usr.bin/make/d_posix.mk \
src/tests/usr.bin/make/d_posix.out
cvs rdiff -u -r1.2 -r1.3 src/tests/usr.bin/make/t_make.sh
cvs rdiff -u -r1.230 -r1.231 src/usr.bin/make/make.1
cvs rdiff -u -r1.198 -r1.199 src/usr.bin/make/parse.c
cvs rdiff -u -r1.186 -r1.187 src/usr.bin/make/var.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/tests/usr.bin/make/t_make.sh
diff -u src/tests/usr.bin/make/t_make.sh:1.2 src/tests/usr.bin/make/t_make.sh:1.3
--- src/tests/usr.bin/make/t_make.sh:1.2 Fri Aug 22 12:45:32 2014
+++ src/tests/usr.bin/make/t_make.sh Sat Aug 23 10:50:24 2014
@@ -1,4 +1,4 @@
-# $NetBSD: t_make.sh,v 1.2 2014/08/22 16:45:32 apb Exp $
+# $NetBSD: t_make.sh,v 1.3 2014/08/23 14:50:24 christos Exp $
#
# Copyright (c) 2008, 2010, 2014 The NetBSD Foundation, Inc.
# All rights reserved.
@@ -32,12 +32,28 @@ run_and_check()
local makename="${1}"; shift
local srcdir="$(atf_get_srcdir)"
- local testdir="$(atf_get_srcdir)/unit-tests"
+ local in="${srcdir}/d_${name}.mk"
+ local out="${srcdir}/d_${name}.out"
- atf_check -s exit:0 -o ignore -e ignore \
- make -f "${testdir}/Makefile" "${makename}.out"
- atf_check -o file:"${testdir}/${makename}.exp" \
- cat "${makename}.out"
+ if [ "x${name}" = "xposix" ]; then
+ # Include $(INPUTFILE) for d_posix.mk, so it can re-run make
+ # on the same makefile. Make sets $(MAKEFILE), but it is
+ # not in POSIX, so it can't be used as such. It can't be
+ # set explicitly because make always sets it itself and
+ # the test shouldn't use anything not provided for by in
+ # the POSIX standard.
+ args="INPUTFILE='${in}'"
+ atf_expect_fail 'PR/49086 [$(<)], PR/49092 [output order]'
+ atf_check -o file:"${out}" -x \
+ "make -kf'${in}' ${args} 2>&1 | sed -e 's,${srcdir}/d_,,'"
+ else
+ local testdir="$(atf_get_srcdir)/unit-tests"
+
+ atf_check -s exit:0 -o ignore -e ignore \
+ make -f "${testdir}/Makefile" "${makename}.out"
+ atf_check -o file:"${testdir}/${makename}.exp" \
+ cat "${makename}.out"
+ fi
}
# Defines a test case for make(1), parsing a given file and comparing the
Index: src/usr.bin/make/make.1
diff -u src/usr.bin/make/make.1:1.230 src/usr.bin/make/make.1:1.231
--- src/usr.bin/make/make.1:1.230 Sat Feb 15 13:55:30 2014
+++ src/usr.bin/make/make.1 Sat Aug 23 10:50:24 2014
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.230 2014/02/15 18:55:30 sjg Exp $
+.\" $NetBSD: make.1,v 1.231 2014/08/23 14:50:24 christos Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -382,8 +382,11 @@ conditional directives, for loops, and c
In general, lines may be continued from one line to the next by ending
them with a backslash
.Pq Ql \e .
-The trailing newline character and initial whitespace on the following
-line are compressed into a single space.
+For any line that is not a shell command line (i.e. it does not begin
+with a tab), the backslash, the following newline character, and initial
+whitespace on the following line are compressed into a single space.
+On command lines the backslash and the newline are left intact and
+if the following line begins with tab(s), the first one is removed.
.Sh FILE DEPENDENCY SPECIFICATIONS
Dependency lines consist of one or more targets, an operator, and zero
or more sources.
@@ -500,20 +503,52 @@ operation does not change their behavior
For example, any command which needs to use
.Dq cd
or
-.Dq chdir ,
-without side-effect should be put in parenthesis:
+.Dq chdir
+without side-effects should be put in parenthesis so they are executed
+in a subshell:
.Bd -literal -offset indent
avoid-chdir-side-effects:
@echo Building $@ in `pwd`
- @(cd ${.CURDIR} && ${.MAKE} $@)
+ @(cd ${.CURDIR} && ${MAKE} $@)
@echo Back in `pwd`
ensure-one-shell-regardless-of-mode:
@echo Building $@ in `pwd`; \\
- (cd ${.CURDIR} && ${.MAKE} $@); \\
+ (cd ${.CURDIR} && ${MAKE} $@); \\
echo Back in `pwd`
.Ed
+.Pp
+The backslash and the following newline are retained in the input to
+the shell, but if the next line starts with tab(s), the first one of
+those is removed.
+This allows you to align the commands in the rule without introducing
+unwanted whitespace into the command line itself.
+What happens to the backslash-newline pair is up to the shell.
+The standard shell,
+.Xr sh 1 ,
+removes them both elsewhere than in single quotes, effectively catenating
+the two lines.
+The following examples demonstrate escaped newlines in command lines.
+.Bl -column -offset indent " echo \*qfoo" "| cd dir \\"
+.It "echo-foobar:" Ta "| syntax-error:"
+.It " echo \*qfoo\\" Ta "| for i in a b\\"
+.It " bar\*q" Ta "| do\\"
+.It Ta "| echo $i\\"
+.It Ta "| done"
+.El
+.Pp
+The first one is an unnecessarily contrived way of doing
+.Bd -compact -offset indent
+.Ic "echo \*qfoobar\*q"
+.Ed
+The second one demonstrates why the semicolon is required in many places
+where in a similar looking regular shell script it wouldn't be.
+After the shell has removed the backslash newline pairs, the result
+would be the syntactically incorrect command
+.Bd -compact -offset indent
+.Ic "for i in a bdo echo $idone"
+.Ed
.Sh VARIABLE ASSIGNMENTS
Variables in make are much like variables in the shell, and, by tradition,
consist of all upper-case letters.
@@ -623,14 +658,18 @@ Variables defined in the makefile or in
.It Command line variables
Variables defined as part of the command line.
.It Local variables
-Variables that are defined specific to a certain target.
-The seven local variables are as follows:
+There are seven variables that are defined specific to a certain target.
+Five of these are defined in POSIX but only in their short form.
+The variables are as follows:
.Bl -tag -width ".ARCHIVE"
.It Va .ALLSRC
The list of all sources for this target; also known as
.Ql Va \&\*[Gt] .
+This variable is a non-POSIX extension.
.It Va .ARCHIVE
-The name of the archive file.
+The name of the archive file; also known as
+.Ql Va \&! .
+This variable is a non-POSIX extension.
.It Va .IMPSRC
In suffix-transformation rules, the name/path of the source from which the
target is to be transformed (the
@@ -639,7 +678,8 @@ source); also known as
.Ql Va \&\*[Lt] .
It is not defined in explicit rules.
.It Va .MEMBER
-The name of the archive member.
+The name of the archive member; also known as
+.Ql Va % .
.It Va .OODATE
The list of sources for this target that were deemed out-of-date; also
known as
@@ -648,31 +688,47 @@ known as
The file prefix of the target, containing only the file portion, no suffix
or preceding directory components; also known as
.Ql Va * .
+.Pp
+POSIX only requires this variable to be availabe for suffix transformation
+rules, but this implementation makes it available for all targets.
+If the target does not have a known suffix (see
+.Ic .SUFFIXES ) ,
+it is equivalent to
+.Ql Va .TARGET .
.It Va .TARGET
The name of the target; also known as
.Ql Va @ .
.El
.Pp
-The shorter forms
-.Ql Va @ ,
+To increase readability, the shorter forms
+.Ql ( Va @ ,
+.Ql Va \&! ,
+.Ql Va % ,
.Ql Va \&? ,
-.Ql Va \&\*[Lt] ,
-.Ql Va \&\*[Gt] ,
+.Ql Va \*[Lt] ,
+.Ql Va \*[Gt] ,
and
-.Ql Va *
-are permitted for backward
-compatibility with historical makefiles and are not recommended.
-The six variables
-.Ql Va "@F" ,
-.Ql Va "@D" ,
-.Ql Va "\*[Lt]F" ,
-.Ql Va "\*[Lt]D" ,
-.Ql Va "*F" ,
+.Ql Va * )
+should only be used when compatibility with POSIX or other implementations
+is desired.
+For compatibility with POSIX, all of the short forms may also be written
+with an appended uppercase
+.Ql D
+or
+.Ql F ,
+e.g.\&
+.Ql @D
+or
+.Ql *F .
+These modified versions replace each word in the expansion with their
+.Em directory part ( Ql D )
+or
+.Em filename part ( Ql F ) ,
+and are exactly equivalent to the
+.Cm \&:H
and
-.Ql Va "*D"
-are permitted for compatibility with
-.At V
-makefiles and are not recommended.
+.Cm \&:T
+variable modifiers, respectively.
.Pp
Four of the local variables may be used in sources on dependency lines
because they expand to the proper value for each target on the line.
@@ -1052,12 +1108,26 @@ If any of the modifiers in the modifier_
.Pq Ql $ ,
these must be doubled to avoid early expansion.
.Pp
+Variable modifiers are not POSIX compatible except for
+.Ql Cm :old_string=new_string ,
+.Ql Cm \&:H
+and
+.Ql Cm \&:T .
+Even these have notable caveats, see their individual descriptions.
+.Pp
The supported modifiers are:
.Bl -tag -width EEE
.It Cm \&:E
Replaces each word in the variable with its suffix.
.It Cm \&:H
-Replaces each word in the variable with everything but the last component.
+Replaces each word in the variable with everything but the last component
+.Ql ( "head only" ) .
+In POSIX compatible makefiles this modification is only available for local
+variables and with different syntax.
+It is achieved by writing the variable with an appended
+.Ql D ,
+e.g.\&
+.Ql Va @D .
.It Cm \&:M Ns Ar pattern
Select only those words that match
.Ar pattern .
@@ -1260,7 +1330,14 @@ and
are subjected to variable expansion before being parsed as
regular expressions.
.It Cm \&:T
-Replaces each word in the variable with its last component.
+Replaces each word in the variable with its last component
+.Ql ( "tail only" ) .
+In POSIX compatible makefiles this modification is only available for local
+variables and with different syntax.
+It is achieved by writing the variable with an appended
+.Ql F ,
+e.g.\&
+.Ql Va @F .
.It Cm \&:u
Remove adjacent duplicate words (like
.Xr uniq 1 ) .
@@ -1283,7 +1360,7 @@ to determine is any words match "42" you
.It Ar :old_string=new_string
This is the
.At V
-style variable substitution.
+style variable substitution, later standardized in POSIX.
It must be the last modifier specified.
If
.Ar old_string
@@ -1300,6 +1377,9 @@ is the substring of
.Ar old_string
to be replaced in
.Ar new_string .
+The special meaning of
+.Ar %
+is not POSIX compatible.
.Pp
Variable expansion occurs in the normal fashion inside both
.Ar old_string
@@ -1749,7 +1829,20 @@ of three.
Comments begin with a hash
.Pq Ql \&#
character, anywhere but in a shell
-command line, and continue to the end of an unescaped new line.
+command line, and continue to the end of an
+.Em unescaped
+new line.
+This can be used to a great effect to comment out continued sections of
+a makefile but it can also lead to very subtle, and possibly difficult
+to spot, errors for that same reason.
+Please observe some actually encountered problems:
+.Bl -column -offset indent "| target: source" "| target: source" \
+ "| target: source"
+.It "#OLD_NAME =\\" Ta "| VAR =\\" Ta "| #VAR2 =\\"
+.It "NEW_NAME =\\" Ta "| foo\\" Ta "| # foo\\"
+.It "foo\\" Ta "| # bar\\" Ta "| # bar\\"
+.It "bar" Ta "| baz" Ta "| target: source"
+.El
.Sh SPECIAL SOURCES (ATTRIBUTES)
.Bl -tag -width .IGNOREx
.It Ic .EXEC
Index: src/usr.bin/make/parse.c
diff -u src/usr.bin/make/parse.c:1.198 src/usr.bin/make/parse.c:1.199
--- src/usr.bin/make/parse.c:1.198 Wed Jul 16 15:31:11 2014
+++ src/usr.bin/make/parse.c Sat Aug 23 10:50:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.198 2014/07/16 19:31:11 justin Exp $ */
+/* $NetBSD: parse.c,v 1.199 2014/08/23 14:50:24 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: parse.c,v 1.198 2014/07/16 19:31:11 justin Exp $";
+static char rcsid[] = "$NetBSD: parse.c,v 1.199 2014/08/23 14:50:24 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: parse.c,v 1.198 2014/07/16 19:31:11 justin Exp $");
+__RCSID("$NetBSD: parse.c,v 1.199 2014/08/23 14:50:24 christos Exp $");
#endif
#endif /* not lint */
#endif
@@ -1193,8 +1193,14 @@ ParseDoDependency(char *line)
curTargs = Lst_Init(FALSE);
+ /*
+ * Get targets. After each iteration 'line' is reset to point
+ * past the word that was just read so that it points at the start of
+ * the next one (i.e. first non-space) or any of the preceding blanks.
+ */
+ cp = line;
do {
- for (cp = line; *cp && (ParseIsEscaped(lstart, cp) ||
+ for ( ; *cp && (ParseIsEscaped(lstart, cp) ||
!(isspace((unsigned char)*cp) ||
*cp == '!' || *cp == ':' || *cp == LPAREN));
cp++) {
@@ -1205,6 +1211,8 @@ ParseDoDependency(char *line)
* so we can safely advance beyond it...There should be
* no errors in this, as they would have been discovered
* in the initial Var_Subst and we wouldn't be here.
+ * [XXX] Shouldn't this be an error? I thought dynamic
+ * source stuff is only allowed on the source side.
*/
int length;
void *freeIt;
@@ -1218,22 +1226,18 @@ ParseDoDependency(char *line)
if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) {
/*
- * Archives must be handled specially to make sure the OP_ARCHV
- * flag is set in their 'type' field, for one thing, and because
- * things like "archive(file1.o file2.o file3.o)" are permissible.
- * Arch_ParseArchive will set 'line' to be the first non-blank
- * after the archive-spec. It creates/finds nodes for the members
- * and places them on the given list, returning SUCCESS if all
- * went well and FAILURE if there was an error in the
- * specification. On error, line should remain untouched.
+ * Archive member spec. On SUCCESS relevant archive member
+ * nodes are added to targets and cp has been advanced to
+ * the first non-blank after the spec. On FAILURE cp is not
+ * changed, targets might not be so lucky.
*/
- if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) {
+ cp = line;
+ if (Arch_ParseArchive(&cp, targets, VAR_CMD) != SUCCESS) {
Parse_Error(PARSE_FATAL,
- "Error in archive specification: \"%s\"", line);
+ "Error in archive specification: \"%s\"", cp);
goto out;
- } else {
+ } else
continue;
- }
}
savec = *cp;
@@ -1436,9 +1440,9 @@ ParseDoDependency(char *line)
cp++;
}
}
- line = cp;
- } while (*line && (ParseIsEscaped(lstart, line) ||
- ((*line != '!') && (*line != ':'))));
+ /* Inline assignment in case of continue statements. */
+ } while (*(line = cp) != '\0' && (ParseIsEscaped(lstart, cp) ||
+ ((*cp != '!') && (*cp != ':'))));
/*
* Don't need the list of target names anymore...
@@ -2772,10 +2776,22 @@ ParseGetLine(int flags, int *length)
continue;
}
- /* Escaped '\n' replace following whitespace with a single ' ' */
- while (ptr[0] == ' ' || ptr[0] == '\t')
- ptr++;
- ch = ' ';
+ /*
+ * Escaped '\n'. On regular lines it is replaced with a space
+ * along with all the leading whitespace on the next line.
+ * On command-lines, backslash and newline must preserved for
+ * the shell but if there is a leading tab on the next line
+ * it is removed. Weird, but this is all from POSIX.
+ */
+ if (line[0] != '\t') {
+ while (ptr[0] == ' ' || ptr[0] == '\t')
+ ptr++;
+ ch = ' ';
+ } else {
+ *tp++ = '\\';
+ if (ptr[0] == '\t')
+ ptr++;
+ }
}
/* Delete any trailing spaces - eg from empty continuations */
Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.186 src/usr.bin/make/var.c:1.187
--- src/usr.bin/make/var.c:1.186 Fri Jun 20 02:13:45 2014
+++ src/usr.bin/make/var.c Sat Aug 23 10:50:24 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.186 2014/06/20 06:13:45 sjg Exp $ */
+/* $NetBSD: var.c,v 1.187 2014/08/23 14:50:24 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: var.c,v 1.186 2014/06/20 06:13:45 sjg Exp $";
+static char rcsid[] = "$NetBSD: var.c,v 1.187 2014/08/23 14:50:24 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: var.c,v 1.186 2014/06/20 06:13:45 sjg Exp $");
+__RCSID("$NetBSD: var.c,v 1.187 2014/08/23 14:50:24 christos Exp $");
#endif
#endif /* not lint */
#endif
@@ -3603,6 +3603,7 @@ Var_Parse(const char *str, GNode *ctxt,
char name[2];
*freePtr = NULL;
+ nstr = NULL;
dynamic = FALSE;
start = str;
parsestate.oneBigWord = FALSE;
@@ -3734,7 +3735,7 @@ Var_Parse(const char *str, GNode *ctxt,
*/
if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) &&
(vlen == 2) && (str[1] == 'F' || str[1] == 'D') &&
- strchr("@%*!<>", str[0]) != NULL) {
+ strchr("@%?*!<>", str[0]) != NULL) {
/*
* Well, it's local -- go look for it.
*/
@@ -3746,7 +3747,7 @@ Var_Parse(const char *str, GNode *ctxt,
/*
* No need for nested expansion or anything, as we're
* the only one who sets these things and we sure don't
- * but nested invocations in them...
+ * put nested invocations in them...
*/
nstr = Buf_GetAll(&v->val, NULL);
@@ -3759,13 +3760,10 @@ Var_Parse(const char *str, GNode *ctxt,
}
/*
* Resulting string is dynamically allocated, so
- * tell caller to free it.
+ * tell caller to free it. Can't return yet, there
+ * might be modifiers.
*/
*freePtr = nstr;
- *lengthPtr = tstr-start+1;
- Buf_Destroy(&buf, TRUE);
- VarFreeEnv(v, TRUE);
- return nstr;
}
}
@@ -3837,28 +3835,34 @@ Var_Parse(const char *str, GNode *ctxt,
Buf_Destroy(&buf, TRUE);
}
- if (v->flags & VAR_IN_USE) {
- Fatal("Variable %s is recursive.", v->name);
- /*NOTREACHED*/
- } else {
- v->flags |= VAR_IN_USE;
- }
- /*
- * Before doing any modification, we have to make sure the value
- * has been fully expanded. If it looks like recursion might be
- * necessary (there's a dollar sign somewhere in the variable's value)
- * we just call Var_Subst to do any other substitutions that are
- * necessary. Note that the value returned by Var_Subst will have
- * been dynamically-allocated, so it will need freeing when we
- * return.
- */
- nstr = Buf_GetAll(&v->val, NULL);
- if (strchr(nstr, '$') != NULL) {
- nstr = Var_Subst(NULL, nstr, ctxt, errnum);
- *freePtr = nstr;
- }
+ if (nstr == NULL) {
+ /*
+ * D and F modified versions of the short forms of local variables
+ * are already expanded. Expand others.
+ */
+ if (v->flags & VAR_IN_USE) {
+ Fatal("Variable %s is recursive.", v->name);
+ /*NOTREACHED*/
+ } else {
+ v->flags |= VAR_IN_USE;
+ }
+ /*
+ * Before doing any modification, we have to make sure the value
+ * has been fully expanded. If it looks like recursion might be
+ * necessary (there's a dollar sign somewhere in the variable's value)
+ * we just call Var_Subst to do any other substitutions that are
+ * necessary. Note that the value returned by Var_Subst will have
+ * been dynamically-allocated, so it will need freeing when we
+ * return.
+ */
+ nstr = Buf_GetAll(&v->val, NULL);
+ if (strchr(nstr, '$') != NULL) {
+ nstr = Var_Subst(NULL, nstr, ctxt, errnum);
+ *freePtr = nstr;
+ }
- v->flags &= ~VAR_IN_USE;
+ v->flags &= ~VAR_IN_USE;
+ }
if ((nstr != NULL) && haveModifier) {
int used;
Added files:
Index: src/tests/usr.bin/make/d_posix.mk
diff -u /dev/null src/tests/usr.bin/make/d_posix.mk:1.3
--- /dev/null Sat Aug 23 10:50:24 2014
+++ src/tests/usr.bin/make/d_posix.mk Sat Aug 23 10:50:24 2014
@@ -0,0 +1,142 @@
+# $NetBSD: d_posix.mk,v 1.3 2014/08/23 14:50:24 christos Exp $
+
+# Keep the default suffixes from interfering, just in case.
+.SUFFIXES:
+
+all: line-continuations suffix-substitution localvars
+
+#
+# Line continuations
+#
+
+# Escaped newlines and leading whitespace from the next line are replaced
+# with single space, except in commands, where the escape and the newline
+# are retained, but a single leading tab (if any) from the next line is
+# removed. (PR 49085)
+# Expect:
+# $(VAR) = "foo bar baz"
+# a
+# b
+# c
+VAR = foo\
+\
+ bar\
+ baz
+
+line-continuations:
+ @echo '$$(VAR) = "$(VAR)"'
+ @echo 'aXbXc' | sed -e 's/X/\
+ /g'
+
+
+#
+# Suffix substitution
+#
+
+# The only variable modifier accepted by POSIX.
+# $(VAR:s1=s2): replace s1, if found, with s2 at end of each word in
+# $(VAR). s1 and s2 may contain macro expansions.
+# Expect: foo baR baz, bar baz, foo bar baz, fooadd baradd bazadd
+suffix-substitution:
+ @echo '$(VAR:r=R), $(VAR:foo=), $(VAR:not_there=wrong), $(VAR:=add)'
+
+
+#
+# Local variables: regular forms, D/F forms and suffix substitution.
+#
+
+# In the past substitutions did not work with the D/F forms and those
+# forms were not available for $?. (PR 49085)
+
+# dir/obj_1.o is inferred, obj2.o has an explicit rule.
+localvars: dir/obj_1.o obj2.o
+
+# $@ = target or archive name $< = implied source
+# $* = target without suffix $? = sources newer than target
+# $% = archive member name
+LOCALS = \
+ "Local variables\n\
+ \$$(@)=\"$(@)\" \$$(<)=\"$(<)\"\n\
+ \$$(*)=\"$(*)\" \$$(?)=\"$(?)\"\n\
+ \$$(%%)=\"$(%)\"\n\n"
+
+# $XD = directory part of X $XF = file part of X
+# X is one of the local variables.
+LOCAL_ALTERNATIVES = \
+ "Directory and filename parts of local variables\n\
+ \$$(@D)=\"$(@D)\" \$$(@F)=\"$(@F)\"\n\
+ \$$(<D)=\"$(<D)\" \$$(<F)=\"$(<F)\"\n\
+ \$$(*D)=\"$(*D)\" \$$(*F)=\"$(*F)\"\n\
+ \$$(?D)=\"$(?D)\" \$$(?F)=\"$(?F)\"\n\
+ \$$(%%D)=\"$(%D)\" \$$(%%F)=\"$(%F)\"\n\n"
+
+# Do all kinds of meaningless substitutions on local variables to see
+# if they work. Add, remove and replace things.
+VAR2 = .o
+VAR3 = foo
+LOCAL_SUBSTITUTIONS = \
+ "Local variable substitutions\n\
+ \$$(@:.o=)=\"$(@:.o=)\" \$$(<:.c=.C)=\"$(<:.c=.C)\"\n\
+ \$$(*:=.h)=\"$(*:=.h)\" \$$(?:.h=.H)=\"$(?:.h=.H)\"\n\
+ \$$(%%:=)=\"$(%:=)\"\n\n"
+
+LOCAL_ALTERNATIVE_SUBSTITUTIONS = \
+ "Target with suffix transformations\n\
+ \$$(@D:=append)=\"$(@D:=append)\"\n\
+ \$$(@F:.o=.O)=\"$(@F:.o=.O)\"\n\
+ \n\
+ Implied source with suffix transformations\n\
+ \$$(<D:r=rr)=\"$(<D:r=rr)\"\n\
+ \$$(<F:.c=.C)=\"$(<F:.c=.C)\"\n\
+ \n\
+ Suffixless target with suffix transformations\n\
+ \$$(*D:.=dot)=\"$(*D:.=dot)\"\n\
+ \$$(*F:.a=)=\"$(*F:.a=)\"\n\
+ \n\
+ Out-of-date dependencies with suffix transformations\n\
+ \$$(?D:ir=)=\"$(?D:ir=)\"\n\
+ \$$(?F:.h=.H)=\"$(?F:.h=.H)\"\n\
+ \n\
+ Member with suffix transformations\n\
+ \$$(%%D:.=)=\"$(%D:.=)\"\n\
+ \$$(%%F:\$$(VAR2)=\$$(VAR))=\"$(%F:$(VAR2)=$(VAR))\"\n\n"
+
+.SUFFIXES: .c .o
+
+.c.o:
+ @printf $(LOCALS)
+ @printf $(LOCAL_ALTERNATIVES)
+ @printf $(LOCAL_SUBSTITUTIONS)
+ @printf $(LOCAL_ALTERNATIVE_SUBSTITUTIONS)
+ cc -c -o '$(@)' '$(<)'
+
+# Some of these rules are padded with useless extra dependencies just so
+# that $(?) has more than one file.
+
+dir/obj_1.o: dir/obj_1.h
+
+# According to POSIX, $* is only required for inference rules and $<'s
+# value is unspecified outside of inference rules. Strictly speaking
+# we shouldn't be expanding them here but who cares. At least we get
+# to check that the program does nothing stupid (like crash) with them.
+# The C file is named differently from the object file because there
+# was a bug which forced dependencies based on inference rules on all
+# applicable targets (PR 49086).
+obj2.o: obj_2.c obj_2.h dir/obj_1.h
+ @printf $(LOCALS)
+ @printf $(LOCAL_ALTERNATIVES)
+ @printf $(LOCAL_SUBSTITUTIONS)
+ @printf $(LOCAL_ALTERNATIVE_SUBSTITUTIONS)
+ cc -c -o '$(@)' 'obj_2.c'
+
+# Hey, this is make, we can make our own test data setup! obj2.c is not
+# used, so it should not get created. It's here as a bait for
+# a regression into the forced dependencies discussed earlier.
+dir/obj_1.c obj2.c obj_2.c:
+ mkdir -p '$(@D)'
+ printf '#include "$(@F:.c=.h)"\nconst char* $(@F:.c=) = "$(@)";' \
+ >'$(@)'
+
+dir/obj_1.h obj_2.h:
+ mkdir -p '$(@D)'
+ touch '$(@)'
Index: src/tests/usr.bin/make/d_posix.out
diff -u /dev/null src/tests/usr.bin/make/d_posix.out:1.3
--- /dev/null Sat Aug 23 10:50:24 2014
+++ src/tests/usr.bin/make/d_posix.out Sat Aug 23 10:50:24 2014
@@ -0,0 +1,91 @@
+$(VAR) = "foo bar baz"
+a
+b
+c
+foo baR baz, bar baz, foo bar baz, fooadd baradd bazadd
+mkdir -p 'dir'
+touch 'dir/obj_1.h'
+mkdir -p 'dir'
+printf '#include "obj_1.h"\nconst char* obj_1 = "dir/obj_1.c";' \
+ >'dir/obj_1.c'
+Local variables
+ $(@)="dir/obj_1.o" $(<)="dir/obj_1.c"
+ $(*)="dir/obj_1" $(?)="dir/obj_1.h dir/obj_1.c"
+ $(%)=""
+
+Directory and filename parts of local variables
+ $(@D)="dir" $(@F)="obj_1.o"
+ $(<D)="dir" $(<F)="obj_1.c"
+ $(*D)="dir" $(*F)="obj_1"
+ $(?D)="dir dir" $(?F)="obj_1.h obj_1.c"
+ $(%D)="" $(%F)=""
+
+Local variable substitutions
+ $(@:.o=)="dir/obj_1" $(<:.c=.C)="dir/obj_1.C"
+ $(*:=.h)="dir/obj_1.h" $(?:.h=.H)="dir/obj_1.H dir/obj_1.c"
+ $(%:=)=""
+
+Target with suffix transformations
+ $(@D:=append)="dirappend"
+ $(@F:.o=.O)="obj_1.O"
+
+ Implied source with suffix transformations
+ $(<D:r=rr)="dirr"
+ $(<F:.c=.C)="obj_1.C"
+
+ Suffixless target with suffix transformations
+ $(*D:.=dot)="dir"
+ $(*F:.a=)="obj_1"
+
+ Out-of-date dependencies with suffix transformations
+ $(?D:ir=)="d d"
+ $(?F:.h=.H)="obj_1.H obj_1.c"
+
+ Member with suffix transformations
+ $(%D:.=)=""
+ $(%F:$(VAR2)=$(VAR))=""
+
+cc -c -o 'dir/obj_1.o' 'dir/obj_1.c'
+mkdir -p '.'
+printf '#include "obj_2.h"\nconst char* obj_2 = "obj_2.c";' \
+ >'obj_2.c'
+mkdir -p '.'
+touch 'obj_2.h'
+Local variables
+ $(@)="obj2.o" $(<)=""
+ $(*)="obj2" $(?)="obj_2.c obj_2.h dir/obj_1.h"
+ $(%)=""
+
+Directory and filename parts of local variables
+ $(@D)="." $(@F)="obj2.o"
+ $(<D)="" $(<F)=""
+ $(*D)="." $(*F)="obj2"
+ $(?D)=". . dir" $(?F)="obj_2.c obj_2.h obj_1.h"
+ $(%D)="" $(%F)=""
+
+Local variable substitutions
+ $(@:.o=)="obj2" $(<:.c=.C)=""
+ $(*:=.h)="obj2.h" $(?:.h=.H)="obj_2.c obj_2.H dir/obj_1.H"
+ $(%:=)=""
+
+Target with suffix transformations
+ $(@D:=append)=".append"
+ $(@F:.o=.O)="obj2.O"
+
+ Implied source with suffix transformations
+ $(<D:r=rr)=""
+ $(<F:.c=.C)=""
+
+ Suffixless target with suffix transformations
+ $(*D:.=dot)="dot"
+ $(*F:.a=)="obj2"
+
+ Out-of-date dependencies with suffix transformations
+ $(?D:ir=)=". . d"
+ $(?F:.h=.H)="obj_2.c obj_2.H obj_1.H"
+
+ Member with suffix transformations
+ $(%D:.=)=""
+ $(%F:$(VAR2)=$(VAR))=""
+
+cc -c -o 'obj2.o' 'obj_2.c'