make: only switch to POSIX mode if '.POSIX:' is the first line

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html
says that in order to make a makefile POSIX-conforming, its first
non-comment line must be the special dependency line '.POSIX:' without
any source dependencies.

Previously, make switched to POSIX mode even if such a line occurred
anywhere else, which was allowed by POSIX but was deep in the
"unspecified behavior" area.  For NetBSD make, there is no big
difference since it doesn't ship any <posix.mk> file, this change mainly
affects the bmake distribution.

Previously, makefiles that contain '.POSIX:' somewhere in the middle
could fail due to <posix.mk> resetting .SUFFIXES, among other things.

Suggested by Simon J. Gerraty, who also reviewed an earlier version of
this change.
This commit is contained in:
rillig 2022-04-18 15:06:27 +00:00
parent 3b7543fb74
commit ddac43c43f
8 changed files with 157 additions and 36 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1192 2022/04/08 23:35:52 riastradh Exp $
# $NetBSD: mi,v 1.1193 2022/04/18 15:06:27 rillig Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -5565,6 +5565,8 @@
./usr/tests/usr.bin/make/unit-tests/deptgt-path.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-phony.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-phony.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-posix.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-posix.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-precious.exp tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-precious.mk tests-usr.bin-tests compattestfile,atf
./usr/tests/usr.bin/make/unit-tests/deptgt-shell.exp tests-usr.bin-tests compattestfile,atf

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.579 2022/03/22 23:37:09 rillig Exp $ */
/* $NetBSD: main.c,v 1.580 2022/04/18 15:06:27 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -111,7 +111,7 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: main.c,v 1.579 2022/03/22 23:37:09 rillig Exp $");
MAKE_RCSID("$NetBSD: main.c,v 1.580 2022/04/18 15:06:27 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
@ -1480,6 +1480,7 @@ main_ReadFiles(void)
if (!opts.noBuiltins)
ReadBuiltinRules();
posix_state = PS_MAYBE_NEXT_LINE;
if (!Lst_IsEmpty(&opts.makefiles))
ReadAllMakefiles(&opts.makefiles);
else

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.307 2022/03/26 15:39:58 sjg Exp $
.\" $NetBSD: make.1,v 1.308 2022/04/18 15:06:27 rillig 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 March 24, 2022
.Dd April 18, 2022
.Dt MAKE 1
.Os
.Sh NAME
@ -2290,17 +2290,15 @@ Apply the
.Ic .PHONY
attribute to any specified sources.
.It Ic .POSIX
This should be the first non-comment line in a Makefile.
It results in the variable
If this is the first non-comment line in the main makefile,
the variable
.Va %POSIX
being defined with the value
.Ql 1003.2 .
The first time
.Ic .POSIX
is encountered, the makefile
.Ql posix.mk
will be included if possible,
to provide POSIX compatible default rules.
is set to the value
.Ql 1003.2
and the makefile
.Ql <posix.mk>
is included if it exists,
to provide POSIX-compatible default rules.
If
.Nm
is run with the

View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.299 2022/03/26 14:02:40 rillig Exp $ */
/* $NetBSD: make.h,v 1.300 2022/04/18 15:06:27 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -506,6 +506,17 @@ typedef struct GNode {
unsigned lineno;
} GNode;
/*
* Keep track of whether to include <posix.mk> when parsing the line
* '.POSIX:'.
*/
extern enum PosixState {
PS_NOT_YET,
PS_MAYBE_NEXT_LINE,
PS_NOW_OR_NEVER,
PS_TOO_LATE
} posix_state;
/* Error levels for diagnostics during parsing. */
typedef enum ParseErrorLevel {
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: parse.c,v 1.668 2022/03/25 21:16:04 sjg Exp $ */
/* $NetBSD: parse.c,v 1.669 2022/04/18 15:06:27 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -106,7 +106,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: parse.c,v 1.668 2022/03/25 21:16:04 sjg Exp $");
MAKE_RCSID("$NetBSD: parse.c,v 1.669 2022/04/18 15:06:27 rillig Exp $");
/*
* A file being read.
@ -294,6 +294,7 @@ static const struct {
{ ".WAIT", SP_WAIT, OP_NONE },
};
enum PosixState posix_state = PS_NOT_YET;
static IncludedFile *
GetInclude(size_t i)
@ -1252,23 +1253,9 @@ HandleDependencySourcesEmpty(ParseSpecial special, SearchPathList *paths)
break;
#ifdef POSIX
case SP_POSIX:
Global_Set("%POSIX", "1003.2");
{
static bool first_posix = true;
/*
* Since .POSIX: should be the first
* operative line in a makefile,
* if '-r' flag is used, no default rules have
* been read yet, in which case 'posix.mk' can
* be a substiute for 'sys.mk'.
* If '-r' is not used, then 'posix.mk' acts
* as an extension of 'sys.mk'.
*/
if (first_posix) {
first_posix = false;
IncludeFile("posix.mk", true, false, true);
}
if (posix_state == PS_NOW_OR_NEVER) {
Global_Set("%POSIX", "1003.2");
IncludeFile("posix.mk", true, false, true);
}
break;
#endif
@ -2590,6 +2577,10 @@ ReadHighLevelLine(void)
for (;;) {
line = ReadLowLevelLine(LK_NONEMPTY);
if (posix_state == PS_MAYBE_NEXT_LINE)
posix_state = PS_NOW_OR_NEVER;
else
posix_state = PS_TOO_LATE;
if (line == NULL)
return NULL;

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.311 2022/03/26 12:44:57 rillig Exp $
# $NetBSD: Makefile,v 1.312 2022/04/18 15:06:28 rillig Exp $
#
# Unit tests for make(1)
#
@ -143,6 +143,7 @@ TESTS+= deptgt-order
TESTS+= deptgt-path
TESTS+= deptgt-path-suffix
TESTS+= deptgt-phony
TESTS+= deptgt-posix
TESTS+= deptgt-precious
TESTS+= deptgt-shell
TESTS+= deptgt-silent

View File

@ -0,0 +1 @@
exit status 0

View File

@ -0,0 +1,116 @@
# $NetBSD: deptgt-posix.mk,v 1.1 2022/04/18 15:06:28 rillig Exp $
#
# Tests for the special target '.POSIX', which enables POSIX mode.
#
# As of 2022-04-18, this only means that the variable '%POSIX' is defined and
# that the variables and rules specified by POSIX replace the default ones.
# This is done by loading <posix.mk>, if available. That file is not included
# in NetBSD, but only in the bmake distribution. As of 2022-04-18, POSIX
# support is not complete.
#
# Implementation node: this test needs to be isolated from the usual test
# to prevent unit-tests/posix.mk from interfering with the posix.mk from the
# system directory that this test uses.
#
# See also:
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html
TMPDIR?= /tmp/make.test.deptgt-posix
SYSDIR= ${TMPDIR}/sysdir
MAIN_MK= ${TMPDIR}/main.mk
INCLUDED_MK= ${TMPDIR}/included.mk
all: .PHONY
.SILENT:
set-up-sysdir: .USEBEFORE
mkdir -p ${SYSDIR}
printf '%s\n' > ${SYSDIR}/sys.mk \
'CC=sys-cc' \
'SEEN_SYS_MK=yes'
printf '%s\n' > ${SYSDIR}/posix.mk \
'CC=posix-cc'
check-is-posix: .USE
printf '%s\n' >> ${MAIN_MK} \
'.if $${CC} != "posix-cc"' \
'. error' \
'.endif' \
'.if $${%POSIX} != "1003.2"' \
'. error' \
'.endif' \
'all: .PHONY'
check-not-posix: .USE
printf '%s\n' >> ${MAIN_MK} \
'.if $${CC} != "sys-cc"' \
'. error' \
'.endif' \
'.if defined(%POSIX)' \
'. error' \
'.endif' \
'all: .PHONY'
check-not-seen-sys-mk: .USE
printf '%s\n' >> ${MAIN_MK} \
'.if defined(SEEN_SYS_MK)' \
'. error' \
'.endif'
run: .USE
(cd "${TMPDIR}" && MAKEFLAGS=${MAKEFLAGS.${.TARGET}:Q} ${MAKE} \
-m "${SYSDIR}" -f ${MAIN_MK:T})
rm -rf ${TMPDIR}
# If the main makefile has a '.for' loop as its first non-comment line, a
# strict reading of POSIX 2018 makes the makefile non-conforming.
all: after-for
after-for: .PHONY set-up-sysdir check-not-posix run
printf '%s\n' > ${MAIN_MK} \
'# comment' \
'' \
'.for i in once' \
'.POSIX:' \
'.endfor'
# If the main makefile has an '.if' conditional as its first non-comment line,
# a strict reading of POSIX 2018 makes the makefile non-conforming.
all: after-if
after-if: .PHONY set-up-sysdir check-not-posix run
printf '%s\n' > ${MAIN_MK} \
'# comment' \
'' \
'.if 1' \
'.POSIX:' \
'.endif'
# If the main makefile first includes another makefile and that included
# makefile tries to switch to POSIX mode, that's too late.
all: in-included-file
in-included-file: .PHONY set-up-sysdir check-not-posix run
printf 'include included.mk\n' > ${MAIN_MK}
printf '.POSIX:\n' > ${INCLUDED_MK}
# If the main makefile switches to POSIX mode in its very first line, before
# and comment lines or empty lines, that works.
all: in-first-line
in-first-line: .PHONY set-up-sysdir check-is-posix run
printf '%s\n' > ${MAIN_MK} \
'.POSIX:'
# The only allowed lines before switching to POSIX mode are comment lines.
# POSIX defines that empty and blank lines are called comment lines as well.
all: after-comment-lines
after-comment-lines: .PHONY set-up-sysdir check-is-posix run
printf '%s\n' > ${MAIN_MK} \
'# comment' \
'' \
'.POSIX:'
# Running make with the option '-r' skips the builtin rules from <sys.mk>.
# In that mode, '.POSIX:' just loads <posix.mk>, which works as well.
MAKEFLAGS.no-builtins= -r
all: no-builtins
no-builtins: .PHONY set-up-sysdir check-is-posix check-not-seen-sys-mk run
printf '%s\n' > ${MAIN_MK} \
'.POSIX:'