Import bmake-20200517

Changes since 20181221 are mostly portability related
hence the large gap in versions imported.

There are however some bug fixes, and a rework of filemon handling.
In NetBSD make/filemon/filemon_ktrace.c allows use of fktrace
and elimination of filemon(4) which has not had the TLC it needs.

FreeBSD filemon(4) is in much better shape, so bmake/filemon/filemon_dev.c
allows use of that, with a bit less overhead than the ktrace model.

Summary of changes from ChangeLog

	o str.c: empty string does not match % pattern
	  plus unit-test changes
	o var.c: import handling of old sysV style modifier using '%'
	o str.c: refactor brk_string
	o meta.c: meta_oodate, CHECK_VALID_META is too aggressive for CMD
	  a blank command is perfectly valid.
	o meta.c: meta_oodate, check for corrupted meta file
	  earlier and more often.
	* meta.c: meta_compat_parent check for USE_FILEMON
	  patch from Soeren Tempel
	o meta.c: fix compat mode, need to call meta_job_output()
	o job.c: extra fds for meta mode not needed if using filemon_dev
	o meta.c: avoid passing NULL to filemon_*() when meta_needed()
	  returns FALSE.
	o filemon/filemon_{dev,ktrace}.c: allow selection of
	  filemon implementation.  filemon_dev.c uses the kernel module
	  while filemon_ktrace.c leverages the fktrace api available in
	  NetBSD.  filemon_ktrace.c can hopefully form the basis for
	  adding support for other tracing mechanisms such as strace on
	  Linux.
	o meta.c: when target is out-of-date per normal make rules
	  record value of .OODATE in meta file.
	o parse.c: don't pass NULL to realpath(3)
	  some versions cannot handle it.
	o parse.c: ParseDoDependency: free paths rather than assert

plus more unit-tests
This commit is contained in:
Simon J. Gerraty 2020-05-20 19:34:48 +00:00
parent 14ade6f031
commit b897d72a5a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/NetBSD/bmake/dist/; revision=361288
svn path=/vendor/NetBSD/bmake/20200517/; revision=361289; tag=vendor/NetBSD/bmake/20200517
74 changed files with 3282 additions and 864 deletions

135
ChangeLog
View File

@ -1,3 +1,138 @@
2020-05-17 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200517
Merge with NetBSD make, pick up
o modified dollar tests to avoid shell dependencies
o new tests for .INCLUDEFROM
2020-05-16 Simon J Gerraty <sjg@beast.crufty.net>
* unit-tests/dollar.mk: tweak '1 dollar literal' test
to not depend so much on shell behavior
2020-05-10 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200510
Merge with NetBSD make, pick up
o unit test for dollar handling
2020-05-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200506
Merge with NetBSD make, pick up
o str.c: empty string does not match % pattern
plus unit-test changes
2020-05-04 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200504
May the 4th be with you
Merge with NetBSD make, pick up
o var.c: import handling of old sysV style modifier using '%'
o str.c: refactor brk_string
o unit-tests: add test case for lazy conditions
2020-04-18 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200418
* configure.in: use_makefile=no for cygwin et al.
case insensitive filesystems just don't work if both
makefile and Makefile exist.
NOTE: bmake does not support cygwin and likely never will,
but if brave souls want to try it - help them out.
2020-04-02 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200402
Merge with NetBSD make, pick up
o meta.c: meta_oodate, CHECK_VALID_META is too aggressive for CMD
a blank command is perfectly valid.
2020-03-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200330
Merge with NetBSD make, pick up
o make.h: extern debug_file
2020-03-18 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200318
Merge with NetBSD make, pick up
o meta.c: meta_oodate, check for corrupted meta file
earlier and more often.
2020-02-20 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200220
2020-02-19 Simon J Gerraty <sjg@beast.crufty.net>
* boot-strap: unset MAKEFLAGS
2020-02-12 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200212
* meta.c: meta_compat_parent check for USE_FILEMON
patch from Soeren Tempel
2020-02-05 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20200205
Merge with NetBSD make, pick up
o meta.c: fix compat mode, need to call meta_job_output()
o job.c: extra fds for meta mode not needed if using filemon_dev
2020-01-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20200122
Merge with NetBSD make, pick up
o meta.c: avoid passing NULL to filemon_*() when meta_needed()
returns FALSE.
2020-01-21 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20200121
Merge with NetBSD make, pick up
o filemon/filemon_{dev,ktrace}.c: allow selection of
filemon implementation. filemon_dev.c uses the kernel module
while filemon_ktrace.c leverages the fktrace api available in
NetBSD. filemon_ktrace.c can hopefully form the basis for
adding support for other tracing mechanisms such as strace on
Linux.
o meta.c: when target is out-of-date per normal make rules
record value of .OODATE in meta file.
2019-09-26 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20190926
Merge with NetBSD make, pick up
o parse.c: don't pass NULL to realpath(3)
some versions cannot handle it.
2019-04-09 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20190409
Merge with NetBSD make, pick up
o parse.c: ParseDoDependency: free paths rather than assert
2018-12-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20181222
* configure.in: add --without-makefile to avoid generating
makefile and make-bootstrap.sh
* include Makefile.inc if it exists
* Use Makefile and Makefile.config.in in unit-tests
so we can use just: make obj && make && make test
when bmake is already available.
We add --without-makefile to CONFIGURE_ARGS in this case.
* tweak bsd.after-import.mk (captures Makefile.config etc
after import to FreeBSD for example) to cope with all the above.
2018-12-21 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION: 20181221

16
FILES
View File

@ -22,6 +22,9 @@ configure.in
dir.c
dir.h
dirname.c
filemon/filemon.h
filemon/filemon_dev.c
filemon/filemon_ktrace.c
find_lib.sh
for.c
getopt.c
@ -94,13 +97,18 @@ suff.c
targ.c
trace.c
trace.h
unit-tests/Makefile.in
unit-tests/Makefile
unit-tests/Makefile.config.in
unit-tests/comment.exp
unit-tests/comment.mk
unit-tests/cond1.exp
unit-tests/cond1.mk
unit-tests/cond2.exp
unit-tests/cond2.mk
unit-tests/cond-late.mk
unit-tests/cond-late.exp
unit-tests/dollar.exp
unit-tests/dollar.mk
unit-tests/doterror.exp
unit-tests/doterror.mk
unit-tests/dotwait.exp
@ -123,6 +131,10 @@ unit-tests/hash.exp
unit-tests/hash.mk
unit-tests/impsrc.exp
unit-tests/impsrc.mk
unit-tests/include-main.exp
unit-tests/include-main.mk
unit-tests/include-sub.mk
unit-tests/include-subsub.mk
unit-tests/misc.exp
unit-tests/misc.mk
unit-tests/moderrs.exp
@ -163,6 +175,8 @@ unit-tests/varcmd.exp
unit-tests/varcmd.mk
unit-tests/varmisc.exp
unit-tests/varmisc.mk
unit-tests/varmod-edge.exp
unit-tests/varmod-edge.mk
unit-tests/varquote.exp
unit-tests/varquote.mk
unit-tests/varshell.exp

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.99 2017/08/13 20:12:53 sjg Exp $
# $Id: Makefile,v 1.104 2020/02/06 01:33:54 sjg Exp $
PROG= bmake
@ -56,6 +56,7 @@ SRCS+= \
lstSucc.c
.-include "VERSION"
.-include "Makefile.inc"
# this file gets generated by configure
.-include "Makefile.config"
@ -77,11 +78,23 @@ CFLAGS+= -I. -I${srcdir} ${XDEFS} -DMAKE_NATIVE
CFLAGS+= ${COPTS.${.ALLSRC:M*.c:T:u}}
COPTS.main.c+= "-DMAKE_VERSION=\"${_MAKE_VERSION}\""
# meta mode can be useful even without filemon
# meta mode can be useful even without filemon
# should be set by now
USE_FILEMON ?= no
.if ${USE_FILEMON:tl} != "no"
.PATH: ${.CURDIR}/filemon
SRCS+= filemon_${USE_FILEMON}.c
COPTS.meta.c+= -DUSE_FILEMON -DUSE_FILEMON_${USE_FILEMON:tu}
COPTS.job.c+= ${COPTS.meta.c}
.if ${USE_FILEMON} == "dev"
FILEMON_H ?= /usr/include/dev/filemon/filemon.h
.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h"
COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
COPTS.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H}
.endif
.endif # USE_FILEMON == dev
.endif # USE_FILEMON
.PATH: ${srcdir}
.PATH: ${srcdir}/lst.lib
@ -198,6 +211,8 @@ main.o: ${SRCS} ${.CURDIR}/VERSION
.if ${MK_AUTOCONF_MK} == "yes"
CONFIGURE_DEPS += ${.CURDIR}/VERSION
# we do not need or want the generated makefile
CONFIGURE_ARGS += --without-makefile
.include <autoconf.mk>
.endif
SHARE_MK?=${SHAREDIR}/mk

View File

@ -14,7 +14,8 @@ CFLAGS+= ${CPPFLAGS} @DEFS@
LDFLAGS+= @LDFLAGS@
LIBOBJS+= @LIBOBJS@
LDADD+= @LIBS@
USE_META= @use_meta@
USE_META?= @use_meta@
USE_FILEMON?= @use_filemon@
FILEMON_H?= @filemon_h@
BMAKE_PATH_MAX?= @bmake_path_max@
# used if MAXPATHLEN not defined

View File

@ -1,2 +1,2 @@
# keep this compatible with sh and make
_MAKE_VERSION=20181221
_MAKE_VERSION=20200517

8
arch.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: arch.c,v 1.70 2017/04/16 20:49:09 riastradh Exp $ */
/* $NetBSD: arch.c,v 1.71 2019/10/05 23:35:57 mrg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: arch.c,v 1.70 2017/04/16 20:49:09 riastradh Exp $";
static char rcsid[] = "$NetBSD: arch.c,v 1.71 2019/10/05 23:35:57 mrg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94";
#else
__RCSID("$NetBSD: arch.c,v 1.70 2017/04/16 20:49:09 riastradh Exp $");
__RCSID("$NetBSD: arch.c,v 1.71 2019/10/05 23:35:57 mrg Exp $");
#endif
#endif /* not lint */
#endif
@ -682,7 +682,7 @@ ArchStatMember(char *archive, char *member, Boolean hash)
arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0';
size = (int)strtol(arh.AR_SIZE, NULL, 10);
(void)strncpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME));
for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) {
continue;
}

View File

@ -89,6 +89,10 @@
# disable use of filemon(9) which is currently only
# available for NetBSD and FreeBSD.
#
# --with-filemon=ktrace
# on NetBSD or others with fktrace(2), use ktrace
# version of filemon.
#
# --with-filemon="path/to/filemon.h"
# enables use of filemon(9) by meta mode.
#
@ -111,7 +115,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: boot-strap,v 1.49 2016/05/29 00:09:14 sjg Exp $
# $Id: boot-strap,v 1.51 2020/02/19 16:46:23 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
@ -161,6 +165,8 @@ cmd_args="$@"
# clear some things from the environment that we care about
unset MAKEOBJDIR MAKEOBJDIRPREFIX
# or that might be incompatible
unset MAKE MAKEFLAGS
# --install[-host-target] will set this
INSTALL_PREFIX=

View File

@ -1,4 +1,4 @@
# $Id: bsd.after-import.mk,v 1.13 2017/08/13 00:56:10 sjg Exp $
# $Id: bsd.after-import.mk,v 1.15 2018/12/30 17:14:24 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@ -63,7 +63,7 @@ MAKEFILE_SED = sed -e '/^MACHINE/d' \
-e 's,${SRCTOP},$${SRCTOP},g'
# These are the simple files we want to capture
configured_files= config.h Makefile.config unit-tests/Makefile
configured_files= config.h Makefile.config unit-tests/Makefile.config
after-import: bootstrap ${MAKEFILE}
.for f in ${configured_files:M*.[ch]}
@ -87,7 +87,6 @@ _makefile: bootstrap ${MAKEFILE}
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
echo '#'; echo '# $$${HOST_OS}$$'; \
echo; echo '.sinclude "Makefile.inc"'; \
echo; echo 'SRCTOP?= $${.CURDIR:${.CURDIR:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; \
echo; echo '# look here first for config.h'; \
echo 'CFLAGS+= -I$${.CURDIR}'; echo; \

View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.107 2017/07/20 19:29:54 sjg Exp $ */
/* $NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: compat.c,v 1.107 2017/07/20 19:29:54 sjg Exp $";
static char rcsid[] = "$NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
#else
__RCSID("$NetBSD: compat.c,v 1.107 2017/07/20 19:29:54 sjg Exp $");
__RCSID("$NetBSD: compat.c,v 1.110 2020/01/19 19:42:32 riastradh Exp $");
#endif
#endif /* not lint */
#endif
@ -407,7 +407,7 @@ CompatRunCommand(void *cmdp, void *gnp)
#ifdef USE_META
if (useMeta) {
meta_compat_parent();
meta_compat_parent(cpid);
}
#endif

View File

@ -143,7 +143,7 @@
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
/* Define to 1 if `struct stat' is a member of `st_rdev'. */
/* Define to 1 if `st_rdev' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_RDEV
/* Define to 1 if your `struct stat' has `st_rdev'. Deprecated, use

1105
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
dnl
dnl RCSid:
dnl $Id: configure.in,v 1.59 2017/11/26 22:39:20 sjg Exp $
dnl $Id: configure.in,v 1.63 2020/04/19 05:17:57 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ(2.50)
AC_INIT([bmake], [20171126], [sjg@NetBSD.org])
AC_INIT([bmake], [20200418], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@ -16,6 +16,7 @@ esac
dnl get _MAKE_VERSION
. $srcdir/VERSION
OS=`uname -s`
dnl
AC_ARG_WITH(defshell,
@ -32,6 +33,17 @@ no) ;;
;;
esac])
dnl
case "$OS" in
CYGWIN*|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
AC_ARG_WITH(makefile,
[ --without-makefile dissable use of generated makefile],
[case "${withval}" in
yes|no) use_makefile=${withval};;
*) AC_MSG_ERROR(bad value ${withval} given for makefile) ;;
esac])
dnl
use_meta=yes
AC_ARG_WITH(meta,
[ --without-meta dissable use of meta-mode],
@ -41,30 +53,41 @@ yes|no) use_meta=${withval};;
esac])
dnl
AC_ARG_WITH(filemon,
[ --with-filemon=path/filemon.h indicate path to filemon.h for meta-mode],
[ --with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev],
[ case "/${withval}" in
/no|*/filemon.h) filemon_h="${withval}";;
/no) use_filemon=no;;
/*trace) filemon_h=no use_filemon="${withval}";;
*/filemon.h) filemon_h="${withval}";;
*/filemon*) filemon_h="${withval}/filemon.h";;
*) AC_MSG_ERROR(bad value ${withval} given for filemon) ;;
esac],
[
OS=`uname -s`
for d in "/usr/include/dev/filemon" "$prefix/include/dev/filemon" "$srcdir/filemon" "$srcdir/../filemon" "$srcdir/../../sys/dev/filemon"
do
for x in "/$OS" ""
do
filemon_h="$d$x/filemon.h"
test -s "$filemon_h" && break
done
test -s "$filemon_h" && break
done
test -s "${filemon_h:-/dev/null}" || filemon_h=no
case "$OS" in
NetBSD) filemon_h=no use_filemon=ktrace;;
*)
for d in "/usr/include/dev/filemon" "$prefix/include/dev/filemon" "$srcdir/../../sys/dev/filemon"
do
for x in "/$OS" ""
do
filemon_h="$d$x/filemon.h"
test -s "$filemon_h" && break
done
test -s "$filemon_h" && { use_filemon=dev; break; }
done
;;
esac
use_filemon=${use_filemon:-no}
case "$use_filemon" in
dev) ;;
*) filemon_h=no;;
esac
])
dnl echo "Note: use_meta=$use_meta filemon_h=$filemon_h" >&6
dnl echo "Note: use_meta=$use_meta use_filemon=$use_filemon filemon_h=$filemon_h" >&6
case "$use_meta" in
yes)
case "$filemon_h" in
*.h) echo "Using: filemon=$filemon_h" >&6;;
case "$use_filemon" in
no) ;;
*) echo "Using: filemon_${use_filemon}.c" >&6;;
esac
;;
esac
@ -388,10 +411,14 @@ AC_SUBST(INSTALL)
AC_SUBST(GCC)
AC_SUBST(diff_u)
AC_SUBST(use_meta)
AC_SUBST(use_filemon)
AC_SUBST(filemon_h)
AC_SUBST(_MAKE_VERSION)
AC_OUTPUT(makefile Makefile.config make-bootstrap.sh unit-tests/Makefile)
bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh"
if test $use_makefile = yes; then
bm_outfiles="makefile $bm_outfiles"
fi
AC_OUTPUT($bm_outfiles)
cat <<EOF
You can now run

53
filemon/filemon.h Normal file
View File

@ -0,0 +1,53 @@
/* $NetBSD: filemon.h,v 1.2 2020/01/22 22:10:36 sjg Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Taylor R. Campbell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FILEMON_H_
#define FILEMON_H_
#include <sys/types.h>
struct filemon;
const char *
filemon_path(void);
struct filemon *
filemon_open(void);
int filemon_close(struct filemon *);
int filemon_setfd(struct filemon *, int);
void filemon_setpid_parent(struct filemon *, pid_t);
int filemon_setpid_child(const struct filemon *, pid_t);
int filemon_readfd(const struct filemon *);
int filemon_process(struct filemon *);
#endif /* FILEMON_H_ */

151
filemon/filemon_dev.c Normal file
View File

@ -0,0 +1,151 @@
/* $NetBSD: filemon_dev.c,v 1.1 2020/01/19 19:49:37 riastradh Exp $ */
/*-
* Copyright (c) 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Taylor R. Campbell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "filemon.h"
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#ifdef HAVE_FILEMON_H
# include <filemon.h>
#endif
#ifndef _PATH_FILEMON
#define _PATH_FILEMON "/dev/filemon"
#endif
struct filemon {
int fd;
};
const char *
filemon_path(void)
{
return _PATH_FILEMON;
}
struct filemon *
filemon_open(void)
{
struct filemon *F;
unsigned i;
int error;
/* Allocate and zero a struct filemon object. */
F = calloc(1, sizeof(*F));
if (F == NULL)
return NULL;
/* Try opening /dev/filemon, up to six times (cargo cult!). */
for (i = 0; (F->fd = open(_PATH_FILEMON, O_RDWR)) == -1; i++) {
if (i == 5) {
error = errno;
goto fail0;
}
}
/* Success! */
return F;
fail0: free(F);
errno = error;
return NULL;
}
int
filemon_setfd(struct filemon *F, int fd)
{
/* Point the kernel at this file descriptor. */
if (ioctl(F->fd, FILEMON_SET_FD, &fd) == -1)
return -1;
/* No need for it in userland any more; close it. */
(void)close(fd);
/* Success! */
return 0;
}
void
filemon_setpid_parent(struct filemon *F, pid_t pid)
{
/* Nothing to do! */
}
int
filemon_setpid_child(const struct filemon *F, pid_t pid)
{
/* Just pass it on to the kernel. */
return ioctl(F->fd, FILEMON_SET_PID, &pid);
}
int
filemon_close(struct filemon *F)
{
int error = 0;
/* Close the filemon device fd. */
if (close(F->fd) == -1 && error == 0)
error = errno;
/* Free the filemon descriptor. */
free(F);
/* Set errno and return -1 if anything went wrong. */
if (error) {
errno = error;
return -1;
}
/* Success! */
return 0;
}
int
filemon_readfd(const struct filemon *F)
{
return -1;
}
int
filemon_process(struct filemon *F)
{
return 0;
}

878
filemon/filemon_ktrace.c Normal file
View File

@ -0,0 +1,878 @@
/* $NetBSD: filemon_ktrace.c,v 1.2 2020/01/19 20:22:57 riastradh Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Taylor R. Campbell.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define _KERNTYPES /* register_t */
#include "filemon.h"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/rbtree.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <sys/ktrace.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef AT_CWD
#define AT_CWD -1
#endif
struct filemon;
struct filemon_key;
struct filemon_state;
typedef struct filemon_state *filemon_syscall_t(struct filemon *,
const struct filemon_key *, const struct ktr_syscall *);
static filemon_syscall_t filemon_sys_chdir;
static filemon_syscall_t filemon_sys_execve;
static filemon_syscall_t filemon_sys_exit;
static filemon_syscall_t filemon_sys_fork;
static filemon_syscall_t filemon_sys_link;
static filemon_syscall_t filemon_sys_open;
static filemon_syscall_t filemon_sys_openat;
static filemon_syscall_t filemon_sys_symlink;
static filemon_syscall_t filemon_sys_unlink;
static filemon_syscall_t filemon_sys_rename;
static filemon_syscall_t *const filemon_syscalls[] = {
[SYS_chdir] = &filemon_sys_chdir,
[SYS_execve] = &filemon_sys_execve,
[SYS_exit] = &filemon_sys_exit,
[SYS_fork] = &filemon_sys_fork,
[SYS_link] = &filemon_sys_link,
[SYS_open] = &filemon_sys_open,
[SYS_openat] = &filemon_sys_openat,
[SYS_symlink] = &filemon_sys_symlink,
[SYS_unlink] = &filemon_sys_unlink,
[SYS_rename] = &filemon_sys_rename,
};
struct filemon {
int ktrfd; /* kernel writes ktrace events here */
FILE *in; /* we read ktrace events from here */
FILE *out; /* we write filemon events to here */
rb_tree_t active;
pid_t child;
/* I/O state machine. */
enum {
FILEMON_START = 0,
FILEMON_HEADER,
FILEMON_PAYLOAD,
FILEMON_ERROR,
} state;
unsigned char *p;
size_t resid;
/* I/O buffer. */
struct ktr_header hdr;
union {
struct ktr_syscall syscall;
struct ktr_sysret sysret;
char namei[PATH_MAX];
unsigned char buf[4096];
} payload;
};
struct filemon_state {
struct filemon_key {
pid_t pid;
lwpid_t lid;
} key;
struct rb_node node;
int syscode;
void (*show)(struct filemon *, const struct filemon_state *,
const struct ktr_sysret *);
unsigned i;
unsigned npath;
char *path[/*npath*/];
};
static int
compare_filemon_states(void *cookie, const void *na, const void *nb)
{
const struct filemon_state *Sa = na;
const struct filemon_state *Sb = nb;
if (Sa->key.pid < Sb->key.pid)
return -1;
if (Sa->key.pid > Sb->key.pid)
return +1;
if (Sa->key.lid < Sb->key.lid)
return -1;
if (Sa->key.lid > Sb->key.lid)
return +1;
return 0;
}
static int
compare_filemon_key(void *cookie, const void *n, const void *k)
{
const struct filemon_state *S = n;
const struct filemon_key *key = k;
if (S->key.pid < key->pid)
return -1;
if (S->key.pid > key->pid)
return +1;
if (S->key.lid < key->lid)
return -1;
if (S->key.lid > key->lid)
return +1;
return 0;
}
static const rb_tree_ops_t filemon_rb_ops = {
.rbto_compare_nodes = &compare_filemon_states,
.rbto_compare_key = &compare_filemon_key,
.rbto_node_offset = offsetof(struct filemon_state, node),
.rbto_context = NULL,
};
/*
* filemon_path()
*
* Return a pointer to a constant string denoting the `path' of
* the filemon.
*/
const char *
filemon_path(void)
{
return "ktrace";
}
/*
* filemon_open()
*
* Allocate a filemon descriptor. Returns NULL and sets errno on
* failure.
*/
struct filemon *
filemon_open(void)
{
struct filemon *F;
int ktrpipe[2];
int error;
/* Allocate and zero a struct filemon object. */
F = calloc(1, sizeof(*F));
if (F == NULL)
return NULL;
/* Create a pipe for ktrace events. */
if (pipe2(ktrpipe, O_CLOEXEC|O_NONBLOCK) == -1) {
error = errno;
goto fail0;
}
/* Create a file stream for reading the ktrace events. */
if ((F->in = fdopen(ktrpipe[0], "r")) == NULL) {
error = errno;
goto fail1;
}
ktrpipe[0] = -1; /* claimed by fdopen */
/*
* Set the fd for writing ktrace events and initialize the
* rbtree. The rest can be safely initialized to zero.
*/
F->ktrfd = ktrpipe[1];
rb_tree_init(&F->active, &filemon_rb_ops);
/* Success! */
return F;
fail2: __unused
(void)fclose(F->in);
fail1: (void)close(ktrpipe[0]);
(void)close(ktrpipe[1]);
fail0: free(F);
errno = error;
return NULL;
}
/*
* filemon_closefd(F)
*
* Internal subroutine to try to flush and close the output file.
* If F is not open for output, do nothing. Never leaves F open
* for output even on failure. Returns 0 on success; sets errno
* and return -1 on failure.
*/
static int
filemon_closefd(struct filemon *F)
{
int error = 0;
/* If we're not open, nothing to do. */
if (F->out == NULL)
return 0;
/*
* Flush it, close it, and null it unconditionally, but be
* careful to return the earliest error in errno.
*/
if (fflush(F->out) == EOF && error == 0)
error = errno;
if (fclose(F->out) == EOF && error == 0)
error = errno;
F->out = NULL;
/* Set errno and return -1 if anything went wrong. */
if (error) {
errno = error;
return -1;
}
/* Success! */
return 0;
}
/*
* filemon_setfd(F, fd)
*
* Cause filemon activity on F to be sent to fd. Claims ownership
* of fd; caller should not use fd afterward, and any duplicates
* of fd may see their file positions changed.
*/
int
filemon_setfd(struct filemon *F, int fd)
{
/*
* Close an existing output file if done. Fail now if there's
* an error closing.
*/
if ((filemon_closefd(F)) == -1)
return -1;
assert(F->out == NULL);
/* Open a file stream and claim ownership of the fd. */
if ((F->out = fdopen(fd, "a")) == NULL)
return -1;
/*
* Print the opening output. Any failure will be deferred
* until closing. For hysterical raisins, we show the parent
* pid, not the child pid.
*/
fprintf(F->out, "# filemon version 4\n");
fprintf(F->out, "# Target pid %jd\n", (intmax_t)getpid());
fprintf(F->out, "V 4\n");
/* Success! */
return 0;
}
/*
* filemon_setpid_parent(F, pid)
*
* Set the traced pid, from the parent. Never fails.
*/
void
filemon_setpid_parent(struct filemon *F, pid_t pid)
{
F->child = pid;
}
/*
* filemon_setpid_child(F, pid)
*
* Set the traced pid, from the child. Returns 0 on success; sets
* errno and returns -1 on failure.
*/
int
filemon_setpid_child(const struct filemon *F, pid_t pid)
{
int ops, trpoints;
ops = KTROP_SET|KTRFLAG_DESCEND;
trpoints = KTRFACv2;
trpoints |= KTRFAC_SYSCALL|KTRFAC_NAMEI|KTRFAC_SYSRET;
trpoints |= KTRFAC_INHERIT;
if (fktrace(F->ktrfd, ops, trpoints, pid) == -1)
return -1;
return 0;
}
/*
* filemon_close(F)
*
* Close F for output if necessary, and free a filemon descriptor.
* Returns 0 on success; sets errno and returns -1 on failure, but
* frees the filemon descriptor either way;
*/
int
filemon_close(struct filemon *F)
{
struct filemon_state *S;
int error = 0;
/* Close for output. */
if (filemon_closefd(F) == -1 && error == 0)
error = errno;
/* Close the ktrace pipe. */
if (fclose(F->in) == EOF && error == 0)
error = errno;
if (close(F->ktrfd) == -1 && error == 0)
error = errno;
/* Free any active records. */
while ((S = RB_TREE_MIN(&F->active)) != NULL) {
rb_tree_remove_node(&F->active, S);
free(S);
}
/* Free the filemon descriptor. */
free(F);
/* Set errno and return -1 if anything went wrong. */
if (error) {
errno = error;
return -1;
}
/* Success! */
return 0;
}
/*
* filemon_readfd(F)
*
* Returns a file descriptor which will select/poll ready for read
* when there are filemon events to be processed by
* filemon_process, or -1 if anything has gone wrong.
*/
int
filemon_readfd(const struct filemon *F)
{
if (F->state == FILEMON_ERROR)
return -1;
return fileno(F->in);
}
/*
* filemon_dispatch(F)
*
* Internal subroutine to dispatch a filemon ktrace event.
* Silently ignore events that we don't recognize.
*/
static void
filemon_dispatch(struct filemon *F)
{
const struct filemon_key key = {
.pid = F->hdr.ktr_pid,
.lid = F->hdr.ktr_lid,
};
struct filemon_state *S;
switch (F->hdr.ktr_type) {
case KTR_SYSCALL: {
struct ktr_syscall *call = &F->payload.syscall;
struct filemon_state *S1;
/* Validate the syscall code. */
if (call->ktr_code < 0 ||
(size_t)call->ktr_code >= __arraycount(filemon_syscalls) ||
filemon_syscalls[call->ktr_code] == NULL)
break;
/*
* Invoke the syscall-specific logic to create a new
* active state.
*/
S = (*filemon_syscalls[call->ktr_code])(F, &key, call);
if (S == NULL)
break;
/*
* Insert the active state, or ignore it if there
* already is one.
*
* Collisions shouldn't happen because the states are
* keyed by <pid,lid>, in which syscalls should happen
* sequentially in CALL/RET pairs, but let's be
* defensive.
*/
S1 = rb_tree_insert_node(&F->active, S);
if (S1 != S) {
/* XXX Which one to drop? */
free(S);
break;
}
break;
}
case KTR_NAMEI:
/* Find an active syscall state, or drop it. */
S = rb_tree_find_node(&F->active, &key);
if (S == NULL)
break;
/* Find the position of the next path, or drop it. */
if (S->i >= S->npath)
break;
/* Record the path. */
S->path[S->i++] = strndup(F->payload.namei,
sizeof F->payload.namei);
break;
case KTR_SYSRET: {
struct ktr_sysret *ret = &F->payload.sysret;
unsigned i;
/* Find and remove an active syscall state, or drop it. */
S = rb_tree_find_node(&F->active, &key);
if (S == NULL)
break;
rb_tree_remove_node(&F->active, S);
/*
* If the active syscall state matches this return,
* invoke the syscall-specific logic to show a filemon
* event.
*/
/* XXX What to do if syscall code doesn't match? */
if (S->i == S->npath && S->syscode == ret->ktr_code)
(*S->show)(F, S, ret);
/* Free the state now that it is no longer active. */
for (i = 0; i < S->i; i++)
free(S->path[i]);
free(S);
break;
}
default:
/* Ignore all other ktrace events. */
break;
}
}
/*
* filemon_process(F)
*
* Process all pending events after filemon_readfd(F) has
* selected/polled ready for read.
*
* Returns -1 on failure, 0 on end of events, and anything else if
* there may be more events.
*
* XXX What about fairness to other activities in the event loop?
* If we stop while there's events buffered in F->in, then select
* or poll may not return ready even though there's work queued up
* in the buffer of F->in, but if we don't stop then ktrace events
* may overwhelm all other activity in the event loop.
*/
int
filemon_process(struct filemon *F)
{
size_t nread;
top: /* If the child has exited, nothing to do. */
/* XXX What if one thread calls exit while another is running? */
if (F->child == 0)
return 0;
/* If we're waiting for input, read some. */
if (F->resid) {
nread = fread(F->p, 1, F->resid, F->in);
if (nread == 0) {
if (feof(F->in))
return 0;
assert(ferror(F->in));
/*
* If interrupted or would block, there may be
* more events. Otherwise fail.
*/
if (errno == EAGAIN || errno == EINTR)
return 1;
F->state = FILEMON_ERROR;
F->p = NULL;
F->resid = 0;
return -1;
}
assert(nread <= F->resid);
F->p += nread;
F->resid -= nread;
if (F->resid) /* may be more events */
return 1;
}
/* Process a state transition now that we've read a buffer. */
switch (F->state) {
case FILEMON_START: /* just started filemon; read header next */
F->state = FILEMON_HEADER;
F->p = (void *)&F->hdr;
F->resid = sizeof F->hdr;
goto top;
case FILEMON_HEADER: /* read header */
/* Sanity-check ktrace header; then read payload. */
if (F->hdr.ktr_len < 0 ||
(size_t)F->hdr.ktr_len > sizeof F->payload) {
F->state = FILEMON_ERROR;
F->p = NULL;
F->resid = 0;
errno = EIO;
return -1;
}
F->state = FILEMON_PAYLOAD;
F->p = (void *)&F->payload;
F->resid = (size_t)F->hdr.ktr_len;
goto top;
case FILEMON_PAYLOAD: /* read header and payload */
/* Dispatch ktrace event; then read next header. */
filemon_dispatch(F);
F->state = FILEMON_HEADER;
F->p = (void *)&F->hdr;
F->resid = sizeof F->hdr;
goto top;
default: /* paranoia */
F->state = FILEMON_ERROR;
/*FALLTHROUGH*/
case FILEMON_ERROR: /* persistent error indicator */
F->p = NULL;
F->resid = 0;
errno = EIO;
return -1;
}
}
static struct filemon_state *
syscall_enter(struct filemon *F,
const struct filemon_key *key, const struct ktr_syscall *call,
unsigned npath,
void (*show)(struct filemon *, const struct filemon_state *,
const struct ktr_sysret *))
{
struct filemon_state *S;
unsigned i;
S = calloc(1, offsetof(struct filemon_state, path[npath]));
if (S == NULL)
return NULL;
S->key = *key;
S->show = show;
S->syscode = call->ktr_code;
S->i = 0;
S->npath = npath;
for (i = 0; i < npath; i++)
S->path[i] = NULL; /* paranoia */
return S;
}
static void
show_paths(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret, const char *prefix)
{
unsigned i;
/* Caller must ensure all paths have been specified. */
assert(S->i == S->npath);
/*
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if
* we're not producing output.
*/
if (ret->ktr_error && ret->ktr_error != -2)
return;
if (F->out == NULL)
return;
/*
* Print the prefix, pid, and paths -- with the paths quoted if
* there's more than one.
*/
fprintf(F->out, "%s %jd", prefix, (intmax_t)S->key.pid);
for (i = 0; i < S->npath; i++) {
const char *q = S->npath > 1 ? "'" : "";
fprintf(F->out, " %s%s%s", q, S->path[i], q);
}
fprintf(F->out, "\n");
}
static void
show_retval(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret, const char *prefix)
{
/*
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if
* we're not producing output.
*/
if (ret->ktr_error && ret->ktr_error != -2)
return;
if (F->out == NULL)
return;
fprintf(F->out, "%s %jd %jd\n", prefix, (intmax_t)S->key.pid,
(intmax_t)ret->ktr_retval);
}
static void
show_chdir(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "C");
}
static void
show_execve(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
return show_paths(F, S, ret, "E");
}
static void
show_fork(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_retval(F, S, ret, "F");
}
static void
show_link(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "L"); /* XXX same as symlink */
}
static void
show_open_read(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "R");
}
static void
show_open_write(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "W");
}
static void
show_open_readwrite(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "R");
show_paths(F, S, ret, "W");
}
static void
show_openat_read(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
if (S->path[0][0] != '/')
show_paths(F, S, ret, "A");
show_paths(F, S, ret, "R");
}
static void
show_openat_write(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
if (S->path[0][0] != '/')
show_paths(F, S, ret, "A");
show_paths(F, S, ret, "W");
}
static void
show_openat_readwrite(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
if (S->path[0][0] != '/')
show_paths(F, S, ret, "A");
show_paths(F, S, ret, "R");
show_paths(F, S, ret, "W");
}
static void
show_symlink(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "L"); /* XXX same as link */
}
static void
show_unlink(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "D");
}
static void
show_rename(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret)
{
show_paths(F, S, ret, "M");
}
static struct filemon_state *
filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 1, &show_chdir);
}
static struct filemon_state *
filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 1, &show_execve);
}
static struct filemon_state *
filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
const register_t *args = (const void *)&call[1];
int status = args[0];
if (F->out) {
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
if (key->pid == F->child) {
fprintf(F->out, "# Bye bye\n");
F->child = 0;
}
}
return NULL;
}
static struct filemon_state *
filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 0, &show_fork);
}
static struct filemon_state *
filemon_sys_link(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 2, &show_link);
}
static struct filemon_state *
filemon_sys_open(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
const register_t *args = (const void *)&call[1];
int flags;
if (call->ktr_argsize < 2)
return NULL;
flags = args[1];
if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, &show_open_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1, &show_open_write);
else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1, &show_open_read);
else
return NULL; /* XXX Do we care if no read or write? */
}
static struct filemon_state *
filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
const register_t *args = (const void *)&call[1];
int flags, fd;
if (call->ktr_argsize < 3)
return NULL;
fd = args[0];
flags = args[2];
if (fd == AT_CWD) {
if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1,
&show_open_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1,
&show_open_write);
else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1, &show_open_read);
else
return NULL;
} else {
if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1,
&show_openat_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1,
&show_openat_write);
else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1,
&show_openat_read);
else
return NULL;
}
}
static struct filemon_state *
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 2, &show_symlink);
}
static struct filemon_state *
filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 1, &show_unlink);
}
static struct filemon_state *
filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
return syscall_enter(F, key, call, 2, &show_rename);
}

72
job.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $ */
/* $NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -70,14 +70,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $";
static char rcsid[] = "$NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94";
#else
__RCSID("$NetBSD: job.c,v 1.195 2018/05/13 22:13:28 sjg Exp $");
__RCSID("$NetBSD: job.c,v 1.197 2020/02/06 01:13:19 sjg Exp $");
#endif
#endif /* not lint */
#endif
@ -342,6 +342,8 @@ static Job childExitJob; /* child exit pseudo-job */
#define CHILD_EXIT "."
#define DO_JOB_RESUME "R"
static const int npseudojobs = 2; /* number of pseudo-jobs */
#define TARG_FMT "%s %s ---\n" /* Default format */
#define MESSAGE(fp, gn) \
if (maxJobs != 1 && targPrefix && *targPrefix) \
@ -373,6 +375,16 @@ static void JobSigReset(void);
#endif
const char *malloc_options= MALLOC_OPTIONS;
static unsigned
nfds_per_job(void)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta)
return 2;
#endif
return 1;
}
static void
job_table_dump(const char *where)
{
@ -1458,6 +1470,12 @@ JobExec(Job *job, char **argv)
Trace_Log(JOBSTART, job);
#ifdef USE_META
if (useMeta) {
meta_job_parent(job, cpid);
}
#endif
/*
* Set the current position in the buffer to the beginning
* and mark another stream to watch in the outputs mask
@ -2140,12 +2158,24 @@ Job_CatchOutput(void)
if (nready == 0)
return;
for (i = 2; i < nfds; i++) {
for (i = npseudojobs*nfds_per_job(); i < nfds; i++) {
if (!fds[i].revents)
continue;
job = jobfds[i];
if (job->job_state == JOB_ST_RUNNING)
JobDoOutput(job, FALSE);
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
/*
* With meta mode, we may have activity on the job's filemon
* descriptor too, which at the moment is any pollfd other than
* job->inPollfd.
*/
if (useMeta && job->inPollfd != &fds[i]) {
if (meta_job_event(job) <= 0) {
fds[i].events = 0; /* never mind */
}
}
#endif
if (--nready == 0)
return;
}
@ -2290,9 +2320,11 @@ Job_Init(void)
JobCreatePipe(&childExitJob, 3);
/* We can only need to wait for tokens, children and output from each job */
fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs));
jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs));
/* Preallocate enough for the maximum number of jobs. */
fds = bmake_malloc(sizeof(*fds) *
(npseudojobs + maxJobs) * nfds_per_job());
jobfds = bmake_malloc(sizeof(*jobfds) *
(npseudojobs + maxJobs) * nfds_per_job());
/* These are permanent entries and take slots 0 and 1 */
watchfd(&tokenWaitJob);
@ -2811,6 +2843,14 @@ watchfd(Job *job)
jobfds[nfds] = job;
job->inPollfd = &fds[nfds];
nfds++;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
fds[nfds].fd = meta_job_fd(job);
fds[nfds].events = fds[nfds].fd == -1 ? 0 : POLLIN;
jobfds[nfds] = job;
nfds++;
}
#endif
}
static void
@ -2821,6 +2861,18 @@ clearfd(Job *job)
Punt("Unwatching unwatched job");
i = job->inPollfd - fds;
nfds--;
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
/*
* Sanity check: there should be two fds per job, so the job's
* pollfd number should be even.
*/
assert(nfds_per_job() == 2);
if (i % 2)
Punt("odd-numbered fd with meta");
nfds--;
}
#endif
/*
* Move last job in table into hole made by dead job.
*/
@ -2828,6 +2880,12 @@ clearfd(Job *job)
fds[i] = fds[nfds];
jobfds[i] = jobfds[nfds];
jobfds[i]->inPollfd = &fds[i];
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
if (useMeta) {
fds[i + 1] = fds[nfds + 1];
jobfds[i + 1] = jobfds[nfds + 1];
}
#endif
}
job->inPollfd = NULL;
}

View File

@ -23,6 +23,10 @@ MDEFS="-DMAKE_VERSION=\"$MAKE_VERSION\" \
LDFLAGS="@LDFLAGS@"
LIBS="@LIBS@"
toUpper() {
${TR:-tr} abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ
}
do_compile2() {
obj="$1"; shift
src="$1"; shift
@ -32,7 +36,10 @@ do_compile2() {
do_compile() {
obj="$1"; shift
src=`basename "$obj" .o`.c
case "$1" in
*.c) src=$1; shift;;
*) src=`basename "$obj" .o`.c;;
esac
for d in "$srcdir" "$srcdir/lst.lib"
do
@ -52,7 +59,7 @@ do_link() {
}
BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o getopt hash.o \
job.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
suff.o targ.o trace.o var.o util.o"
LST_OBJECTS="lstAppend.o lstDupl.o lstInit.o lstOpen.o \
@ -73,12 +80,22 @@ done
case "@use_meta@" in
yes)
case "@filemon_h@" in
*/filemon.h) FDEFS="-DHAVE_FILEMON_H -I`dirname @filemon_h@`";;
case "@use_filemon@" in
no) MDEFS=;;
*)
MDEFS="-DUSE_FILEMON -DUSE_FILEMON_`echo @use_filemon@ | toUpper`"
case "@use_filemon@,@filemon_h@" in
dev,*/filemon.h) FDEFS="-DHAVE_FILEMON_H -I`dirname @filemon_h@`";;
*) FDEFS=;;
esac
do_compile filemon_@use_filemon@.o filemon/filemon_@use_filemon@.c ${FDEFS}
BASE_OBJECTS="filemon_@use_filemon@.o $BASE_OBJECTS"
;;
esac
do_compile meta.o ${FDEFS}
BASE_OBJECTS="meta.o ${BASE_OBJECTS}"
;;
do_compile meta.o ${MDEFS}
BASE_OBJECTS="meta.o ${BASE_OBJECTS}"
;;
esac
do_compile job.o ${MDEFS}
do_link bmake main.o ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS}
do_link bmake main.o job.o ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS}

43
make.1
View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.273 2018/05/27 01:14:51 christos Exp $
.\" $NetBSD: make.1,v 1.280 2020/04/27 20:03:08 christos 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 May 26, 2018
.Dd April 27, 2020
.Dt MAKE 1
.Os
.Sh NAME
@ -272,7 +272,7 @@ that do not depend on the target whose creation caused the error.
.It Fl m Ar directory
Specify a directory in which to search for sys.mk and makefiles included
via the
.Ao Ar file Ac Ns -style
.Li \&< Ns Ar file Ns Li \&> Ns -style
include statement.
The
.Fl m
@ -280,7 +280,7 @@ option can be used multiple times to form a search path.
This path will override the default system include path: /usr/share/mk.
Furthermore the system include path will be appended to the search path used
for
.Qo Ar file Qc Ns -style
.Li \*q Ns Ar file Ns Li \*q Ns -style
include statements (see the
.Fl I
option).
@ -1234,7 +1234,7 @@ Quotes every shell meta-character in the variable, and also doubles
characters so that it can be passed
safely through recursive invocations of
.Nm .
This is equivalent to:
This is equivalent to:
.Sq \&:S/\e\&$/&&/g:Q .
.It Cm \&:R
Replaces each word in the variable with everything but its suffix.
@ -1429,6 +1429,29 @@ is the substring of
.Ar old_string
to be replaced in
.Ar new_string .
If only
.Ar old_string
contains the pattern matching character
.Ar % ,
and
.Ar old_string
matches, then the result is the
.Ar new_string .
If only the
.Ar new_string
contains the pattern matching character
.Ar % ,
then it is not treated specially and it is printed as a literal
.Ar %
on match.
If there is more than one pattern matching character
.Ar ( % )
in either the
.Ar new_string
or
.Ar old_string ,
only the first instance is treated specially (as the pattern character);
all subsequent instances are treated as regular characters
.Pp
Variable expansion occurs in the normal fashion inside both
.Ar old_string
@ -1623,9 +1646,9 @@ dot
.Pq Ql \&.
character.
Files are included with either
.Cm \&.include Aq Ar file
.Cm \&.include \&< Ns Ar file Ns Cm \&>
or
.Cm \&.include Pf \*q Ar file Ns \*q .
.Cm \&.include \&\*q Ns Ar file Ns Cm \&\*q .
Variables between the angle brackets or double quotes are expanded
to form the file name.
If angle brackets are used, the included makefile is expected to be in
@ -2404,9 +2427,9 @@ file).
.Sh BUGS
The
.Nm
syntax is difficult to parse without actually acting of the data.
For instance finding the end of a variable use should involve scanning each
the modifiers using the correct terminator for each field.
syntax is difficult to parse without actually acting on the data.
For instance, finding the end of a variable's use should involve scanning
each of the modifiers, using the correct terminator for each field.
In many places
.Nm
just counts {} and () in order to find the end of a variable expansion.

2
make.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.105 2020/03/30 02:41:06 sjg Exp $ */
/* $NetBSD: make.h,v 1.107 2020/04/03 03:35:16 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993

192
meta.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.70 2018/02/13 19:37:30 sjg Exp $ */
/* $NetBSD: meta.c,v 1.81 2020/04/03 03:32:28 sjg Exp $ */
/*
* Implement 'meta' mode.
@ -36,7 +36,6 @@
# include "config.h"
#endif
#include <sys/stat.h>
#include <sys/ioctl.h>
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#elif !defined(HAVE_DIRNAME)
@ -50,11 +49,8 @@ char * dirname(char *);
#include "make.h"
#include "job.h"
#ifdef HAVE_FILEMON_H
# include <filemon.h>
#endif
#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD)
# define USE_FILEMON
#ifdef USE_FILEMON
#include "filemon/filemon.h"
#endif
static BuildMon Mybm; /* for compat */
@ -121,30 +117,24 @@ extern char **environ;
* the benefits are more limited.
*/
#ifdef USE_FILEMON
# ifndef _PATH_FILEMON
# define _PATH_FILEMON "/dev/filemon"
# endif
/*
* Open the filemon device.
*/
static void
filemon_open(BuildMon *pbm)
meta_open_filemon(BuildMon *pbm)
{
int retry;
pbm->mon_fd = pbm->filemon_fd = -1;
if (!useFilemon)
int dupfd;
pbm->mon_fd = -1;
pbm->filemon = NULL;
if (!useFilemon || !pbm->mfp)
return;
for (retry = 5; retry >= 0; retry--) {
if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0)
break;
}
if (pbm->filemon_fd < 0) {
pbm->filemon = filemon_open();
if (pbm->filemon == NULL) {
useFilemon = FALSE;
warn("Could not open %s", _PATH_FILEMON);
warn("Could not open filemon %s", filemon_path());
return;
}
@ -155,12 +145,15 @@ filemon_open(BuildMon *pbm)
* We only care about the descriptor.
*/
pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) {
if ((dupfd = dup(pbm->mon_fd)) == -1) {
err(1, "Could not dup filemon output!");
}
(void)fcntl(dupfd, F_SETFD, FD_CLOEXEC);
if (filemon_setfd(pbm->filemon, dupfd) == -1) {
err(1, "Could not set filemon file descriptor!");
}
/* we don't need these once we exec */
(void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
(void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC);
}
/*
@ -473,7 +466,7 @@ meta_create(BuildMon *pbm, GNode *gn)
const char *tname;
char *fname;
const char *cp;
char *p[4]; /* >= possible uses */
char *p[5]; /* >= possible uses */
int i;
mf.fp = NULL;
@ -528,7 +521,10 @@ meta_create(BuildMon *pbm, GNode *gn)
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
fprintf(mf.fp, "TARGET %s\n", tname);
cp = Var_Value(".OODATE", gn, &p[i++]);
if (cp && *cp) {
fprintf(mf.fp, "OODATE %s\n", cp);
}
if (metaEnv) {
for (ptr = environ; *ptr != NULL; ptr++)
fprintf(mf.fp, "ENV %s\n", *ptr);
@ -574,7 +570,7 @@ meta_init(void)
{
#ifdef USE_FILEMON
/* this allows makefiles to test if we have filemon support */
Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0);
Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL, 0);
#endif
}
@ -684,9 +680,10 @@ meta_job_start(Job *job, GNode *gn)
#endif
#ifdef USE_FILEMON
if (pbm->mfp != NULL && useFilemon) {
filemon_open(pbm);
meta_open_filemon(pbm);
} else {
pbm->mon_fd = pbm->filemon_fd = -1;
pbm->mon_fd = -1;
pbm->filemon = NULL;
}
#endif
}
@ -708,11 +705,11 @@ meta_job_child(Job *job)
}
if (pbm->mfp != NULL) {
close(fileno(pbm->mfp));
if (useFilemon) {
if (useFilemon && pbm->filemon) {
pid_t pid;
pid = getpid();
if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) {
if (filemon_setpid_child(pbm->filemon, pid) == -1) {
err(1, "Could not set filemon pid!");
}
}
@ -720,6 +717,59 @@ meta_job_child(Job *job)
#endif
}
void
meta_job_parent(Job *job, pid_t pid)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
if (job != NULL) {
pbm = &job->bm;
} else {
pbm = &Mybm;
}
if (useFilemon && pbm->filemon) {
filemon_setpid_parent(pbm->filemon, pid);
}
#endif
}
int
meta_job_fd(Job *job)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
if (job != NULL) {
pbm = &job->bm;
} else {
pbm = &Mybm;
}
if (useFilemon && pbm->filemon) {
return filemon_readfd(pbm->filemon);
}
#endif
return -1;
}
int
meta_job_event(Job *job)
{
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
BuildMon *pbm;
if (job != NULL) {
pbm = &job->bm;
} else {
pbm = &Mybm;
}
if (useFilemon && pbm->filemon) {
return filemon_process(pbm->filemon);
}
#endif
return 0;
}
void
meta_job_error(Job *job, GNode *gn, int flags, int status)
{
@ -798,13 +848,16 @@ meta_cmd_finish(void *pbmp)
pbm = &Mybm;
#ifdef USE_FILEMON
if (pbm->filemon_fd >= 0) {
if (close(pbm->filemon_fd) < 0)
if (pbm->filemon) {
while (filemon_process(pbm->filemon) > 0)
continue;
if (filemon_close(pbm->filemon) == -1)
error = errno;
x = filemon_read(pbm->mfp, pbm->mon_fd);
if (error == 0 && x != 0)
error = x;
pbm->filemon_fd = pbm->mon_fd = -1;
pbm->mon_fd = -1;
pbm->filemon = NULL;
} else
#endif
fprintf(pbm->mfp, "\n"); /* ensure end with newline */
@ -1509,7 +1562,8 @@ meta_oodate(GNode *gn, Boolean oodate)
if (buf[x - 1] == '\n')
buf[x - 1] = '\0';
}
if (!hasOODATE &&
if (p &&
!hasOODATE &&
!(gn->type & OP_NOMETA_CMP) &&
strcmp(p, cmd) != 0) {
if (DEBUG(META))
@ -1530,6 +1584,7 @@ meta_oodate(GNode *gn, Boolean oodate)
fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno);
oodate = TRUE;
}
CHECK_VALID_META(p);
if (strcmp(p, cwd) != 0) {
if (DEBUG(META))
fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir);
@ -1603,9 +1658,10 @@ meta_compat_start(void)
BuildMon *pbm = &Mybm;
if (pbm->mfp != NULL && useFilemon) {
filemon_open(pbm);
meta_open_filemon(pbm);
} else {
pbm->mon_fd = pbm->filemon_fd = -1;
pbm->mon_fd = -1;
pbm->filemon = NULL;
}
#endif
if (pipe(childPipe) < 0)
@ -1627,19 +1683,61 @@ meta_compat_child(void)
}
void
meta_compat_parent(void)
meta_compat_parent(pid_t child)
{
FILE *fp;
char buf[BUFSIZ];
int outfd, metafd, maxfd, nfds;
char buf[BUFSIZ+1];
fd_set readfds;
meta_job_parent(NULL, child);
close(childPipe[1]); /* child side */
fp = fdopen(childPipe[0], "r");
while (fgets(buf, sizeof(buf), fp)) {
meta_job_output(NULL, buf, "");
printf("%s", buf);
fflush(stdout);
outfd = childPipe[0];
#ifdef USE_FILEMON
metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1;
#else
metafd = -1;
#endif
maxfd = -1;
if (outfd > maxfd)
maxfd = outfd;
if (metafd > maxfd)
maxfd = metafd;
while (outfd != -1 || metafd != -1) {
FD_ZERO(&readfds);
if (outfd != -1) {
FD_SET(outfd, &readfds);
}
if (metafd != -1) {
FD_SET(metafd, &readfds);
}
nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL);
if (nfds == -1) {
if (errno == EINTR)
continue;
err(1, "select");
}
if (outfd != -1 && FD_ISSET(outfd, &readfds)) do {
/* XXX this is not line-buffered */
ssize_t nread = read(outfd, buf, sizeof(buf) - 1);
if (nread == -1)
err(1, "read");
if (nread == 0) {
close(outfd);
outfd = -1;
break;
}
fwrite(buf, 1, (size_t)nread, stdout);
fflush(stdout);
buf[nread] = '\0';
meta_job_output(NULL, buf, "");
} while (0);
if (metafd != -1 && FD_ISSET(metafd, &readfds)) {
if (meta_job_event(NULL) <= 0)
metafd = -1;
}
}
fclose(fp);
}
#endif /* USE_META */

9
meta.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.h,v 1.5 2016/05/12 20:28:34 sjg Exp $ */
/* $NetBSD: meta.h,v 1.6 2020/01/19 19:42:32 riastradh Exp $ */
/*
* Things needed for 'meta' mode.
@ -33,7 +33,7 @@
typedef struct BuildMon {
char meta_fname[MAXPATHLEN];
int filemon_fd;
struct filemon *filemon;
int mon_fd;
FILE *mfp;
} BuildMon;
@ -46,6 +46,9 @@ void meta_finish(void);
void meta_mode_init(const char *);
void meta_job_start(struct Job *, GNode *);
void meta_job_child(struct Job *);
void meta_job_parent(struct Job *, pid_t);
int meta_job_fd(struct Job *);
int meta_job_event(struct Job *);
void meta_job_error(struct Job *, GNode *, int, int);
void meta_job_output(struct Job *, char *, const char *);
int meta_cmd_finish(void *);
@ -53,4 +56,4 @@ int meta_job_finish(struct Job *);
Boolean meta_oodate(GNode *, Boolean);
void meta_compat_start(void);
void meta_compat_child(void);
void meta_compat_parent(void);
void meta_compat_parent(pid_t);

View File

@ -1,3 +1,86 @@
2020-05-15 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20200515
* dirdeps.mk: set _debug_* earlier and allow passing -d*
flags to submake when building DIRDEPS_CACHE
2020-05-09 Simon J Gerraty <sjg@beast.crufty.net>
* whats.mk: more easily extensible
2020-05-02 Simon J Gerraty <sjg@beast.crufty.net>
* whats.mk: greatly simplify by adding what.c to SRCS
2020-05-01 Simon J Gerraty <sjg@beast.crufty.net>
* whats.mk: for libs take care how we add to *OBJS
* lib.mk: : works better with whats.mk
2020-04-25 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20200420
* meta.stage.mk: it is not a STAGE_CONFLICT if some-target.dirdep
contains the same ${RELDIR} and a prefix match for our ${TARGET_SPEC}
2020-04-16 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20200416
* sys/*.mk: set MAKE_SHELL rather than SHELL so as not to
interfere with user env.
* sys.mk: default MAKE_SHELL to sh and SHELL to MAKE_SHELL
* autodep.mk: use MAKE_SHELL.
2019-11-21 Simon J Gerraty <sjg@beast.crufty.net>
* gendirdeps.mk: clear .SUFFIXES to avoid a lot of
wasted effort, and unexport _meta_files when no longer needed as
it consumes space we need for command line.
2019-11-11 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps.mk _DIRDEP_USE: use DIRDEP_DIR and add
DIRDEP_USE_PRELUDE at start - facilitates job distribution
2019-10-04 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps-targets.mk: Use TARGET_SPEC_LAST_LIST
defaults to ${${TARGET_SPEC_VARS:[-1]}_LIST} to match valid
TARGET_SPEC qualified depend files.
2019-10-02 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps-targets.mk: encapsulate logic for finding top-level
targets to set initial DIRDEPS for DIRDEPS_BUILD
2019-09-27 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20190911
* compiler.mk: set COMPILER_TYPE
2019-07-17 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20190704
* sys/Darwin.mk: support for Objective-C and clang
2019-05-30 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps.mk: avoid insanely long command line when generating cache
2019-05-23 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20190505
* whats.mk: handle corner case SHLIB defined but not LIB
2018-09-19 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20180919

View File

@ -5,6 +5,7 @@ auto.obj.mk
autoconf.mk
autodep.mk
auto.dep.mk
compiler.mk
cython.mk
dep.mk
doc.mk
@ -60,6 +61,7 @@ whats.mk
yacc.mk
dirdeps.mk
dirdeps-options.mk
dirdeps-targets.mk
gendirdeps.mk
install-new.mk
meta2deps.py

View File

@ -1,6 +1,6 @@
#
# RCSid:
# $Id: autodep.mk,v 1.36 2016/04/05 15:58:37 sjg Exp $
# $Id: autodep.mk,v 1.37 2020/04/17 21:08:17 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, Simon J. Gerraty
#
@ -70,6 +70,7 @@ CFLAGS_MD?=-MD
CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o
CFLAGS+= ${CFLAGS_MD} ${CFLAGS_MF}
RM?= rm
MAKE_SHELL?= sh
# watch out for people who don't use CPPFLAGS
CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS}
@ -90,26 +91,26 @@ CXX_SUFFIXES?= .cc .cpp .cxx .C
.y.d:
@echo updating dependencies for $<
@${YACC} ${YFLAGS} $<
@${SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} y.tab.c | sed '/:/s/^/$@ /' > $@" || { ${RM} -f y.tab.c $@; false; }
@${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} y.tab.c | sed '/:/s/^/$@ /' > $@" || { ${RM} -f y.tab.c $@; false; }
@${RM} -f y.tab.c
.l.d:
@echo updating dependencies for $<
${LEX} ${LFLAGS} $<
@${SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} lex.yy.c | sed '/:/s/^/$@ /' > $@" || { ${RM} -f lex.yy.c $@; false; }
@${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} lex.yy.c | sed '/:/s/^/$@ /' > $@" || { ${RM} -f lex.yy.c $@; false; }
@${RM} -f lex.yy.c
.c.d:
@echo updating dependencies for $<
@${SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
@${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
.s.d .S.d:
@echo updating dependencies for $<
@${SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} ${AINC} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
@${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} ${AINC} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
${CXX_SUFFIXES:%=%.d}:
@echo updating dependencies for $<
@${SHELL} -ec "${CXX_MD} -M ${CXXFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
@${MAKE_SHELL} -ec "${CXX_MD} -M ${CXXFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; }
.else
.y.d:
${YACC} ${YFLAGS} $<

43
mk/compiler.mk Normal file
View File

@ -0,0 +1,43 @@
# $Id: compiler.mk,v 1.6 2019/09/28 17:12:00 sjg Exp $
#
# @(#) Copyright (c) 2019, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
# Permission to copy, redistribute or otherwise
# use this file is hereby granted provided that
# the above copyright notice and this notice are
# left intact.
#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
.if ${MACHINE} == "common"
COMPILER_TYPE = none
COMPILER_VERSION = 0
.endif
.if empty(COMPILER_TYPE) || empty(COMPILER_VERSION)
# gcc does not always say gcc
_v != ${CC} --version 2> /dev/null | \
egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'
.if empty(COMPILER_TYPE)
.if ${_v:Mclang} != ""
COMPILER_TYPE = clang
.elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != ""
COMPILER_TYPE = gcc
.endif
.endif
.if empty(COMPILER_VERSION)
COMPILER_VERSION != echo "${_v:M[1-9].[0-9]*}:[1]" | \
awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3;}'
.endif
.undef _v
.endif
# just in case we don't recognize compiler
COMPILER_TYPE ?= unknown
COMPILER_VERSION ?= 0
.endif

133
mk/dirdeps-targets.mk Normal file
View File

@ -0,0 +1,133 @@
# RCSid:
# $Id: dirdeps-targets.mk,v 1.9 2019/10/06 20:07:50 sjg Exp $
#
# @(#) Copyright (c) 2019 Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
# Permission to copy, redistribute or otherwise
# use this file is hereby granted provided that
# the above copyright notice and this notice are
# left intact.
#
# Please send copies of changes and bug-fixes to:
# sjg@crufty.net
#
##
# This makefile is used to set initial DIRDEPS for top-level build
# targets.
#
# The basic idea is that we have a list of directories in
# DIRDEPS_TARGETS_DIRS which are relative to SRCTOP.
# When asked to make 'foo' we look for any directory named 'foo'
# under DIRDEPS_TARGETS_DIRS.
# We then search those dirs for any Makefile.depend*
# Finally we select any that match conditions like REQUESTED_MACHINE
# or TARGET_SPEC and initialize DIRDEPS accordingly.
#
.if ${.MAKE.LEVEL} == 0
# pickup customizations
.-include <local.dirdeps-targets.mk>
# for DIRDEPS_BUILD this is how we prime the pump
DIRDEPS_TARGETS_DIRS ?= targets targets/pseudo
# these prefixes can modify how we behave
# they need to be stripped when looking for target dirs
DIRDEPS_TARGETS_PREFIX_LIST ?= pkg- build-
# matching target dirs if any
tdirs := ${.TARGETS:Nall:${DIRDEPS_TARGETS_PREFIX_LIST:@p@S,^$p,,@:ts:}:@t@${DIRDEPS_TARGETS_DIRS:@d@$d/$t@}@:@d@${exists(${SRCTOP}/$d):?$d:}@}
.if !empty(DEBUG_DIRDEPS_TARGETS)
.info tdirs=${tdirs}
.endif
.if !empty(tdirs)
# some things we know we want to ignore
DIRDEPS_TARGETS_SKIP_LIST += \
*~ \
*.bak \
*.inc \
*.old \
*.options \
*.orig \
*.rej \
# the list of MACHINEs we consider
DIRDEPS_TARGETS_MACHINE_LIST += \
${ALL_MACHINE_LIST:U} \
${PSEUDO_MACHINE_LIST:Ucommon host host32} \
${TARGET_MACHINE_LIST}
DIRDEPS_TARGETS_MACHINE_LIST := ${DIRDEPS_TARGETS_MACHINE_LIST:O:u}
# raw Makefile.depend* list
tdeps != 'cd' ${SRCTOP} && 'ls' -1 ${tdirs:O:u:@d@$d/${.MAKE.DEPENDFILE_PREFIX}*@} 2> /dev/null; echo
.if ${DEBUG_DIRDEPS_TARGETS:U:Mdep*} != ""
.info tdeps=${tdeps}
.endif
# remove things we know we don't want
tdeps := ${tdeps:${DIRDEPS_TARGETS_SKIP_LIST:${M_ListToSkip}}}
.if ${DEBUG_DIRDEPS_TARGETS:U:Mdep*} != ""
.info tdeps=${tdeps}
.endif
# plain entries (no qualifiers) these apply to any TARGET_SPEC
ptdeps := ${tdeps:M*${.MAKE.DEPENDFILE_PREFIX}:S,/${.MAKE.DEPENDFILE_PREFIX},,}
# MACHINE qualified entries
mqtdeps := ${DIRDEPS_TARGETS_MACHINE_LIST:@m@${tdeps:M*.$m}@:S,/${.MAKE.DEPENDFILE_PREFIX},,}
tqtdeps =
.if ${TARGET_SPEC_VARS:[#]} > 1
# TARGET_SPEC qualified entries
.if !empty(TARGET_SPEC_LIST)
# we have a list of valid TARGET_SPECS; use it
tqtdeps := ${TARGET_SPEC_LIST:U:O:u:@t@${tdeps:M*.$t}@:S,/${.MAKE.DEPENDFILE_PREFIX},,}
.else
# do we have a list of valid tuple members for at least
# the last tupple element? if so match on that
TARGET_SPEC_LAST_LIST ?= ${${TARGET_SPEC_VARS:[-1]}_LIST}
.if !empty(TARGET_SPEC_LAST_LIST)
tqtdeps := ${TARGET_SPEC_LAST_LIST:U:O:u:@t@${tdeps:M*,$t}@:S,/${.MAKE.DEPENDFILE_PREFIX},,}
.else
# this is sub-optimal match MACHINE,
tqtdeps := ${DIRDEPS_TARGETS_MACHINE_LIST:@m@${tdeps:M*.$m,*}@:S,/${.MAKE.DEPENDFILE_PREFIX},,}
.endif
.endif
.endif
# now work out what we want in DIRDEPS
.if empty(REQUESTED_MACHINE)
# we want them all just as found
DIRDEPS = ${ptdeps} ${mqtdeps} ${tqtdeps}
.else
# we only want those that match REQUESTED_MACHINE/REQUESTED_TARGET_SPEC
# or REQUESTED_TARGET_SPEC (TARGET_SPEC)
DIRDEPS = \
${ptdeps:@d@$d.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC:U${REQUESTED_MACHINE}}}@} \
${mqtdeps:M*.${REQUESTED_MACHINE}} \
${tqtdeps:M*.${REQUESTED_TARGET_SPEC:U${TARGET_SPEC}}}
.endif
# clean up
DIRDEPS := ${DIRDEPS:O:u}
.if !empty(DEBUG_DIRDEPS_TARGETS)
.for x in tdeps ptdeps mqtdeps tqtdeps DIRDEPS
.info $x=${$x}
.endfor
.endif
.endif
# if we got DIRDEPS get to work
.if !empty(DIRDEPS)
.include <dirdeps.mk>
DIRDEPS_TARGETS_SKIP += all clean* destroy*
.for t in ${.TARGETS:${DIRDEPS_TARGETS_SKIP:${M_ListToSkip}}}
$t: dirdeps
.endfor
.endif
.endif

View File

@ -1,6 +1,7 @@
# $Id: dirdeps.mk,v 1.96 2018/06/20 22:26:39 sjg Exp $
# $Id: dirdeps.mk,v 1.104 2020/05/16 23:21:48 sjg Exp $
# Copyright (c) 2010-2013, Juniper Networks, Inc.
# Copyright (c) 2010-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -339,6 +340,17 @@ BUILD_DIRDEPS ?= yes
DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${.TARGETS:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.}
.endif
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
_debug_reldir = 1
.else
_debug_reldir = 0
.endif
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend:L:M$x}@} != ""
_debug_search = 1
.else
_debug_search = 0
.endif
# pickup customizations
# as below you can use !target(_DIRDEP_USE) to protect things
# which should only be done once.
@ -378,7 +390,8 @@ DIRDEPS_FILTER += M${_DEP_RELDIR}
.endif
# this is what we run below
DIRDEP_MAKE?= ${.MAKE}
DIRDEP_MAKE ?= ${.MAKE}
DIRDEP_DIR ?= ${.TARGET:R}
# we suppress SUBDIR when visiting the leaves
# we assume sys.mk will set MACHINE_ARCH
@ -388,10 +401,11 @@ _DIRDEP_USE: .USE .MAKE
@for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \
test -s ${.TARGET:R}/$$m || continue; \
echo "${TRACER}Checking ${.TARGET:R} for ${.TARGET:E} ..."; \
${DIRDEP_USE_PRELUDE} \
MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \
TARGET_SPEC=${.TARGET:E} \
MACHINE=${.TARGET:E} \
${DIRDEP_MAKE} -C ${.TARGET:R} || exit 1; \
${DIRDEP_MAKE} -C ${DIRDEP_DIR} || exit 1; \
break; \
done
@ -475,7 +489,7 @@ BUILD_DIRDEPS_TARGETS ?= ${.TARGETS}
${DIRDEPS_CACHE}: .META .NOMETA_CMP
+@{ echo '# Autogenerated - do NOT edit!'; echo; \
echo 'BUILD_DIRDEPS=no'; echo; \
echo '.include <dirdeps.mk>'; \
echo '.include <dirdeps.mk>'; echo; \
} > ${.TARGET}.new
+@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \
DIRDEPS="${DIRDEPS}" \
@ -484,6 +498,7 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \
.MAKE.DEPENDFILE=.none \
${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \
${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \
3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g' >> ${.TARGET}.new && \
mv ${.TARGET}.new ${.TARGET}
@ -504,16 +519,6 @@ _count_dirdeps: .NOMETA
.endif
.if ${BUILD_DIRDEPS} == "yes"
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.${DEP_MACHINE}:L:M$x}@} != ""
_debug_reldir = 1
.else
_debug_reldir = 0
.endif
.if ${DEBUG_DIRDEPS:@x@${DEP_RELDIR:M$x}${${DEP_RELDIR}.depend:L:M$x}@} != ""
_debug_search = 1
.else
_debug_search = 0
.endif
# the rest is done repeatedly for every Makefile.depend we read.
# if we are anything but the original dir we care only about the
@ -639,9 +644,15 @@ _build_all_dirs := ${_build_all_dirs:O:u}
.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS}} == ""
.if !empty(_build_all_dirs)
.if ${BUILD_DIRDEPS_CACHE} == "yes"
x!= { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; \
echo 'dirdeps: ${_build_all_dirs:${M_oneperline}}'; echo; } >&3; echo
x!= { ${_build_all_dirs:@x@${target($x):?:echo '$x: _DIRDEP_USE';}@} echo; } >&3; echo
# guard against _build_all_dirs being too big for a single command line
# first get list of dirs that need _DIRDEP_USE
# then export that and _build_all_dirs
_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@}
.export _new_dirdeps _build_all_dirs
x!= echo; { echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; \
echo "dirdeps: \\"; \
for x in $$_build_all_dirs; do echo " $$x \\"; done; echo; \
for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; echo; } >&3
.if !empty(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
@ -671,7 +682,10 @@ DEP_EXPORT_VARS=
.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
x!= { echo; echo '${_this_dir}.$m: ${_build_dirs:M*.$q:${M_oneperline}}'; echo; } >&3; echo
_cache_deps := ${_build_dirs:M*.$q}
.export _cache_deps
x!= echo; { echo "${_this_dir}.$m: \\"; \
for x in $$_cache_deps; do echo " $$x \\"; done; echo; } >&3
.else
${_this_dir}.$m: ${_build_dirs:M*.$q}
.endif
@ -681,7 +695,10 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q}
.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
x!= { echo; echo '${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m:${M_oneperline}}'; echo; } >&3; echo
_cache_deps := ${_build_dirs:M*.$m:N${_this_dir}.$m}
.export _cache_deps
x!= echo; { echo "${_this_dir}.$m: \\"; \
for x in $$_cache_deps; do echo " $$x \\"; done; echo; } >&3
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
.endif

View File

@ -1,8 +1,10 @@
# $Id: doc.mk,v 1.6 2017/05/01 21:24:10 sjg Exp $
# $Id: doc.mk,v 1.7 2019/06/09 16:22:08 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
.include <init.mk>
BIB?= bib
EQN?= eqn
GREMLIN?= grn
@ -16,9 +18,8 @@ TBL?= tbl
.PATH: ${.CURDIR}
.if !target(all)
.MAIN: all
all: paper.ps
.if !defined(_SKIP_BUILD)
realbuild: paper.ps
.endif
.if !target(paper.ps)
@ -56,8 +57,6 @@ install:
spell: ${SRCS}
spell ${SRCS} | sort | comm -23 - spell.ok > paper.spell
.include <own.mk>
.if !empty(DOCOWN)
DOC_INSTALL_OWN?= -o ${DOCOWN} -g ${DOCGRP}
.endif

View File

@ -1,4 +1,4 @@
# $Id: dpadd.mk,v 1.26 2018/02/12 21:54:26 sjg Exp $
# $Id: dpadd.mk,v 1.27 2019/05/17 13:58:53 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
#
@ -13,6 +13,69 @@
# sjg@crufty.net
#
##
# DESCRIPTION:
# This makefile manages a number of variables that simplify
# dealing with libs in a build.
#
# Primary inputs are DPLIBS, DPADD and SRC_LIBS:
#
# DPLIBS
# List of LIB* that we will actually link with
# should be in correct link order.
# DPLIBS is a short-cut to ensure that DPADD and LDADD are
# kept in sync.
#
# DPADD List of LIB* that should already be built.
#
# SRC_LIBS
# List of LIB* that we want headers from, we do *not*
# require that such libs have been built.
#
# The above all get added to DPMAGIC_LIBS which is what we
# process.
#
# We expect LIB* to be set to absolute path of a library -
# suitable for putting in DPADD.
# eg.
#
# LIBC ?= ${OBJTOP}/lib/libc/libc.a
#
# From such a path we can derrive a number of other variables
# for which we can supply sensible default values.
# We name all these variables for the basename of the library
# (libc in our example above -- ${__lib:T:R} in below):
#
# LDADD_${__lib:T:R}:
# What should be added to LDADD (eg -lc)
#
# OBJ_${__lib:T:R}:
# This is trivial - just the dirname of the built library.
#
# SRC_${__lib:T:R}:
# Where the src for ${__lib} is, if LIB* is set as above
# we can simply substitute ${SRCTOP} for ${OBJTOP} in
# the dirname.
#
# INCLUDES_${__lib:T:R}:
# What should be added to CFLAGS
#
# If the directory ${SRC_${__lib:T:R}}/h exists we will
# only add -I${SRC_${__lib:T:R}}/h on the basis that
# this is where the public api is kept.
#
# Otherwise default will be -I${OBJ_${__lib:T:R}}
# -I${SRC_${__lib:T:R}}
#
# Note much of the above is skipped for staged libs
# eg.
# LIBC ?= ${STAGE_OBJTOP}/usr/lib/libc.a
#
# Since we can safely assume that -I${STAGE_OBJTOP}/usr/include
# and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should
# have no need of anything else.
#
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@ -50,7 +113,7 @@ CXXFLAGS_LAST += ${CXXFLAGS_DEBUG_XTRA}
DPLIBS+= ${DPLIBS_LAST}
DPADD+= ${DPLIBS:N-*}
.for __lib in ${DPLIBS}
.if "${_lib:M-*}" != ""
.if "${__lib:M-*}" != ""
LDADD += ${__lib}
.else
LDADD += ${LDADD_${__lib:T:R}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}}

View File

@ -1,6 +1,7 @@
# $Id: gendirdeps.mk,v 1.39 2018/06/08 01:25:31 sjg Exp $
# $Id: gendirdeps.mk,v 1.42 2020/05/16 23:21:48 sjg Exp $
# Copyright (c) 2010-2013, Juniper Networks, Inc.
# Copyright (c) 2011-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -79,7 +80,6 @@ _DIRDEPS := ${DIRDEPS:U:O:u}
.endif
META_FILES := ${META_FILES:T:O:u}
.export META_FILES
# pickup customizations
.-include <local.gendirdeps.mk>
@ -183,6 +183,11 @@ x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prun
.elif ${_meta_files:[#]} > 500
.export _meta_files
x != echo; for m in $$_meta_files; do echo $$m; done > meta.list
# _meta_files is consuming a lot of env space
# that can impact command line length,
# and we do not need it any more
.undef _meta_files
.unexport _meta_files
.else
_meta_files_arg:= ${_meta_files}
.endif
@ -373,3 +378,6 @@ all ${_DEPENDFILE}:
.endif
${_DEPENDFILE}: .PRECIOUS
# don't waste time looking for ways to make .meta files
.SUFFIXES:

View File

@ -1,4 +1,4 @@
# $Id: init.mk,v 1.15 2017/05/07 20:27:54 sjg Exp $
# $Id: init.mk,v 1.16 2019/09/28 16:54:02 sjg Exp $
#
# @(#) Copyright (c) 2002, Simon J. Gerraty
#
@ -25,6 +25,7 @@ _this_mk_dir := ${.PARSEDIR}
.-include <local.init.mk>
.-include <${.CURDIR:H}/Makefile.inc>
.include <own.mk>
.include <compiler.mk>
.MAIN: all
@ -35,9 +36,9 @@ CXX_SUFFIXES?= .cc .cpp .cxx .C
.include <warnings.mk>
.endif
COPTS += ${COPTS.${.IMPSRC:T}}
CPPFLAGS += ${CPPFLAGS.${.IMPSRC:T}}
CPUFLAGS += ${CPUFLAGS.${.IMPSRC:T}}
.for x in COPTS CPPFLAGS CPUFLAGS LDFLAGS
$x += ${$x.${COMPILER_TYPE}:U} ${$x.${.IMPSRC:T}:U}
.endfor
CC_PG?= -pg
CXX_PG?= ${CC_PG}

View File

@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: install-mk,v 1.160 2018/09/20 00:07:19 sjg Exp $
# $Id: install-mk,v 1.170 2020/05/15 21:40:24 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@ -70,7 +70,7 @@
# sjg@crufty.net
#
MK_VERSION=20180919
MK_VERSION=20200515
OWNER=
GROUP=
MODE=444

View File

@ -1,4 +1,4 @@
# $Id: lib.mk,v 1.68 2018/01/26 20:08:16 sjg Exp $
# $Id: lib.mk,v 1.70 2020/05/02 02:10:20 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__:
@ -170,13 +170,15 @@ LD_solib= lib${LIB}_pic.a
.elif ${TARGET_OSNAME} == "Linux"
SHLIB_LD = ${CC}
# this is ambiguous of course
LD_shared=-shared -Wl,"-h lib${LIB}.so.${SHLIB_MAJOR}"
LD_shared=-shared -Wl,"-soname lib${LIB}.so.${SHLIB_MAJOR}"
LD_solib= -Wl,--whole-archive lib${LIB}_pic.a -Wl,--no-whole-archive
.if ${COMPILER_TYPE} == "gcc"
# Linux uses GNU ld, which is a multi-pass linker
# so we don't need to use lorder or tsort
LD_objs = ${OBJS}
LD_pobjs = ${POBJS}
LD_sobjs = ${SOBJS}
.endif
.elif ${TARGET_OSNAME} == "Darwin"
SHLIB_LD = ${CC}
SHLIB_INSTALL_VERSION ?= ${SHLIB_MAJOR}
@ -406,18 +408,18 @@ SHLIB_AGE?=0
# can't really do profiled libs with libtool - its too fascist about
# naming the output...
lib${LIB}.a:: ${OBJS}
lib${LIB}.a: ${OBJS}
@rm -f ${.TARGET}
${LIBTOOL} --mode=link ${CC} ${LT_STATIC} -o ${.TARGET:.a=.la} ${OBJS:.o=.lo} -rpath ${SHLIBDIR}:/usr/lib -version-info ${SHLIB_MAJOR}:${SHLIB_MINOR}:${SHLIB_AGE}
@ln .libs/${.TARGET} .
lib${LIB}.${LD_so}:: lib${LIB}.a
lib${LIB}.${LD_so}: lib${LIB}.a
@[ -s ${.TARGET}.${SHLIB_AGE} ] || { ln -s .libs/lib${LIB}.${LD_so}* . 2>/dev/null; : }
@[ -s ${.TARGET} ] || ln -s ${.TARGET}.${SHLIB_AGE} ${.TARGET}
.else # MK_LIBTOOL=yes
lib${LIB}.a:: ${OBJS}
lib${LIB}.a: ${OBJS}
@${META_NOECHO} building standard ${LIB} library
@rm -f ${.TARGET}
@${AR} ${AR_cq} ${.TARGET} ${LD_objs}
@ -425,7 +427,7 @@ lib${LIB}.a:: ${OBJS}
POBJS+= ${OBJS:.o=.po}
.NOPATH: ${POBJS}
lib${LIB}_p.a:: ${POBJS}
lib${LIB}_p.a: ${POBJS}
@${META_NOECHO} building profiled ${LIB} library
@rm -f ${.TARGET}
@${AR} ${AR_cq} ${.TARGET} ${LD_pobjs}
@ -433,7 +435,7 @@ lib${LIB}_p.a:: ${POBJS}
SOBJS+= ${OBJS:.o=${PICO}}
.NOPATH: ${SOBJS}
lib${LIB}_pic.a:: ${SOBJS}
lib${LIB}_pic.a: ${SOBJS}
@${META_NOECHO} building shared object ${LIB} library
@rm -f ${.TARGET}
@${AR} ${AR_cq} ${.TARGET} ${LD_sobjs}

View File

@ -1,4 +1,4 @@
# $Id: meta.stage.mk,v 1.56 2018/07/08 17:12:54 sjg Exp $
# $Id: meta.stage.mk,v 1.59 2020/04/25 18:18:27 sjg Exp $
#
# @(#) Copyright (c) 2011-2017, Simon J. Gerraty
#
@ -18,9 +18,11 @@
.if !target(__${.PARSEFILE}__)
# the guard target is defined later
.-include <local.meta.stage.mk>
.if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} != ""
# this is generally safer anyway
_dirdep ?= ${RELDIR}.${MACHINE}
_dirdep ?= ${RELDIR}.${TARGET_SPEC:U${MACHINE}}
.else
_dirdep ?= ${RELDIR}
.endif
@ -67,7 +69,7 @@ LN_CP_SCRIPT = LnCp() { \
# a warning is handy when bootstapping different options.
STAGE_CONFLICT?= ERROR
.if ${STAGE_CONFLICT:tl} == "error"
STAGE_CONFLICT_ACTION= exit 1;
STAGE_CONFLICT_ACTION= exit 1
.else
STAGE_CONFLICT_ACTION=
.endif
@ -78,8 +80,10 @@ STAGE_DIRDEP_SCRIPT = ${LN_CP_SCRIPT}; StageDirdep() { \
t=$$1; \
if [ -s $$t.dirdep ]; then \
cmp -s .dirdep $$t.dirdep && return; \
echo "${STAGE_CONFLICT}: $$t installed by `cat $$t.dirdep` not ${_dirdep}" >&2; \
${STAGE_CONFLICT_ACTION} \
x=`cat $$t.dirdep`; \
case "${RELDIR}:${_dirdep}" in $${x%.*}:$${x}*) ;; \
*) echo "${STAGE_CONFLICT}: $$t installed by $$x not ${_dirdep}" >&2; \
${STAGE_CONFLICT_ACTION} ;; esac; \
fi; \
LnCp .dirdep $$t.dirdep || exit 1; }
@ -205,7 +209,7 @@ stage_files.$s: .dirdep
STAGE_FILES ?= ${.ALLSRC:N.dirdep:Nstage_*}
stage_files: .dirdep
.endif
@${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s}
@${STAGE_FILE_SCRIPT}; StageFiles ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_FILES.$s:O}
@touch $@
.endif
.endif
@ -271,7 +275,7 @@ STAGE_AS.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
.stage_as.$s:
stage_as: stage_as.$s
stage_as.$s: .dirdep
@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS.$s:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
@touch $@
.endif
.endif
@ -286,8 +290,8 @@ STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
.stage_as_and_symlink.$s:
stage_as_and_symlink: stage_as_and_symlink.$s
stage_as_and_symlink.$s: .dirdep
@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} $f@}
@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
@${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} $f@}
@touch $@
.endif
.endif

View File

@ -1,7 +1,7 @@
# $Id: meta.sys.mk,v 1.32 2017/06/11 03:24:04 sjg Exp $
# $Id: meta.sys.mk,v 1.36 2020/05/16 23:21:48 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
# @(#) Copyright (c) 2010-2020, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@ -22,7 +22,7 @@
.-include <local.meta.sys.mk>
# absoulte path to what we are reading.
# absolute path to what we are reading.
_PARSEDIR = ${.PARSEDIR:tA}
.if !defined(SYS_MK_DIR)
@ -61,13 +61,15 @@ META_MODE += silent=yes
MACHINE = host
.endif
.if ${.MAKE.LEVEL} == 0
.if !defined(MACHINE0)
# it can be handy to know which MACHINE kicked off the build
# for example, if using Makefild.depend for multiple machines,
# allowing only MACHINE0 to update can keep things simple.
MACHINE0 := ${MACHINE}
.export MACHINE0
.endif
.if !defined(META2DEPS)
.if defined(PYTHON) && exists(${PYTHON})
# we prefer the python version of this - it is much faster
META2DEPS ?= ${.PARSEDIR}/meta2deps.py
@ -126,9 +128,11 @@ META_NOECHO= :
# ignore mtime of shell
# and mtime of makefiles does not matter in meta mode
.MAKE.META.IGNORE_PATHS += \
${MAKEFILE} \
${SHELL} \
${SYS_MK_DIR}
${MAKEFILE} \
${MAKE_SHELL} \
${SHELL} \
${SYS_MK_DIR} \
.if ${UPDATE_DEPENDFILE:Uyes:tl} != "no"
.if ${.MAKEFLAGS:Uno:M-k} != ""

View File

@ -37,9 +37,10 @@
"""
RCSid:
$Id: meta2deps.py,v 1.27 2017/05/24 00:04:04 sjg Exp $
$Id: meta2deps.py,v 1.28 2020/05/16 23:21:48 sjg Exp $
Copyright (c) 2011-2013, Juniper Networks, Inc.
Copyright (c) 2011-2019, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,4 +1,4 @@
# $Id: options.mk,v 1.10 2014/02/11 18:34:48 sjg Exp $
# $Id: options.mk,v 1.11 2020/05/02 21:23:52 sjg Exp $
#
# @(#) Copyright (c) 2012, Simon J. Gerraty
#
@ -77,3 +77,4 @@ ${OPTION_PREFIX}${o:H} ?= no
${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}}
.endif
.endfor
.undef OPTIONS_DEFAULT_VALUES OPTIONS_DEFAULT_NO OPTIONS_DEFAULT_YES

View File

@ -1,4 +1,4 @@
# $Id: sys.mk,v 1.46 2017/11/15 22:59:23 sjg Exp $
# $Id: sys.mk,v 1.47 2020/04/17 21:08:17 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
#
@ -116,6 +116,10 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
unix ?= We run ${_HOST_OSNAME}.
# We need a Bourne/POSIX shell
MAKE_SHELL ?= sh
SHELL ?= ${MAKE_SHELL}
# A race condition in mkdir, means that it can bail if another
# process made a dir that mkdir expected to.
# We repeat the mkdir -p a number of times to try and work around this.

View File

@ -1,4 +1,4 @@
# $Id: sys.vars.mk,v 1.3 2018/02/06 00:51:53 sjg Exp $
# $Id: sys.vars.mk,v 1.4 2019/05/27 20:22:52 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
#
@ -78,3 +78,8 @@ M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0
# absoulte path to what we are reading.
_PARSEDIR = ${.PARSEDIR:${M_tA}}
# many projects use MAJOR MINOR PATCH versioning
# ${OPENSSL:${M_M.M.P_VERSION}} is equivalent to
# ${OPENSSL_MAJOR_VERSION}.${OPENSSL_MINOR_VERSION}.${OPENSSL_PATCH_VERSION}
M_M.M.P_VERSION = L:@v@$${MAJOR MINOR PATCH:L:@t@$${$$v_$$t_VERSION:U0}@}@:ts.

View File

@ -72,8 +72,6 @@ PFLAGS=
COMPILE.p= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL= sh
YACC= yacc
YFLAGS= -d
YACC.y= ${YACC} ${YFLAGS}

View File

@ -4,7 +4,7 @@
OS= Darwin
unix?= We run ${OS}.
.SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h
.SUFFIXES: .out .a .ln .o .s .S .c .m ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h
.SUFFIXES: .sh .m4 .dylib
.LIBS: .a .dylib
@ -24,10 +24,15 @@ LINK.s?= ${CC} ${AFLAGS} ${LDFLAGS}
COMPILE.S?= ${CC} ${AFLAGS} ${CPPFLAGS} -c
LINK.S?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
.if exists(/usr/bin/gcc)
CC?= gcc -pipe
PIPE?= -pipe
.if exists(/usr/bin/clang)
CC?= cc ${PIPE}
CXX?= c++
.elif exists(/usr/bin/gcc)
CC?= gcc ${PIPE}
.else
CC?= cc -pipe
CC?= cc ${PIPE}
.endif
DBG?= -O2
CFLAGS?= ${DBG}
@ -52,7 +57,7 @@ CPP?= cpp
NOLINT= 1
CPPFLAGS?=
MK_DEP?= mkdeps.sh -N
MK_DEP?= mkdep
FC?= f77
FFLAGS?= -O
@ -91,8 +96,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
TSORT?= tsort -q
@ -121,6 +124,16 @@ ${CXX_SUFFIXES:%=%.a}:
${AR} ${ARFLAGS} $@ $*.o
rm -f $*.o
# Objective-C
.m:
${LINK.m} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}
.m.o:
${COMPILE.m} ${.IMPSRC}
.m.a:
${COMPILE.m} ${.IMPSRC}
${AR} ${ARFLAGS} $@ $*.o
rm -f $*.o
# Fortran/Ratfor
.f:
${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS}

View File

@ -1,4 +1,4 @@
# $Id: Generic.mk,v 1.13 2017/05/05 18:02:16 sjg Exp $
# $Id: Generic.mk,v 1.14 2020/04/17 21:08:17 sjg Exp $
#
# some reasonable defaults
@ -87,8 +87,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
YACC?= yacc

View File

@ -1,4 +1,4 @@
# $Id: HP-UX.mk,v 1.11 2017/05/05 18:02:16 sjg Exp $
# $Id: HP-UX.mk,v 1.12 2020/04/17 21:08:17 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@ -108,7 +108,7 @@ LINK.p= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
# HP's sh sucks
ENV=
SHELL= /bin/ksh
MAKE_SHELL= /bin/ksh
.if exists(/usr/local/bin/bison)
YACC= bison -y

View File

@ -77,8 +77,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
TSORT?= tsort -q

View File

@ -1,4 +1,4 @@
# $Id: Linux.mk,v 1.9 2017/05/05 18:02:16 sjg Exp $
# $Id: Linux.mk,v 1.10 2020/04/17 21:08:17 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@ -75,8 +75,6 @@ PFLAGS=
COMPILE.p= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL= sh
YACC= yacc
YFLAGS= -d
YACC.y= ${YACC} ${YFLAGS}

View File

@ -112,8 +112,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
TSORT?= tsort -q

View File

@ -1,4 +1,4 @@
# $Id: OSF1.mk,v 1.8 2017/05/05 18:02:16 sjg Exp $
# $Id: OSF1.mk,v 1.9 2020/04/17 21:08:17 sjg Exp $
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@ -82,8 +82,6 @@ PFLAGS=
COMPILE.p= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL= sh
.if exists(/usr/local/bin/bison) || exists(/opt/gnu/bin/bison)
YACC= bison -y
.else

View File

@ -87,8 +87,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
TSORT?= tsort -q

View File

@ -1,4 +1,4 @@
# $Id: SunOS.mk,v 1.8 2017/05/05 18:02:17 sjg Exp $
# $Id: SunOS.mk,v 1.9 2020/04/17 21:08:17 sjg Exp $
.if ${.PARSEFILE} == "sys.mk"
.include <host-target.mk>
@ -103,8 +103,6 @@ PFLAGS=
COMPILE.p= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL= sh
.if exists(/usr/local/bin/bison) || exists(/opt/gnu/bin/bison)
YACC= bison -y
.else

View File

@ -1,4 +1,4 @@
# $Id: UnixWare.mk,v 1.3 2017/05/05 18:02:17 sjg Exp $
# $Id: UnixWare.mk,v 1.4 2020/04/17 21:08:17 sjg Exp $
# based on "Id: SunOS.5.sys.mk,v 1.6 2003/09/30 16:42:23 sjg Exp "
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
@ -121,8 +121,6 @@ PFLAGS?=
COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c
LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS}
SHELL?= sh
SIZE?= size
TSORT?= tsort

View File

@ -1,6 +1,6 @@
# $Id: whats.mk,v 1.3 2017/10/19 06:09:14 sjg Exp $
# $Id: whats.mk,v 1.9 2020/05/09 19:48:53 sjg Exp $
#
# @(#) Copyright (c) 2014, Simon J. Gerraty
# @(#) Copyright (c) 2014-2020, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@ -13,25 +13,18 @@
# sjg@crufty.net
#
.if ${MK_WHATSTRING:Uno} != "no"
what_build_exts?= o
.if ${MK_WHATSTRING:Uno} == "yes"
# it can be useful to embed a what(1) string in binaries
# so that the build location can be seen from a core file.
.if defined(PROG) && ${.MAKE.MAKEFILES:M*prog.mk} != ""
what_thing?= ${PROGNAME:U${PROG}}
what_build_thing?= ${PROG}
.elif defined(LIB) && ${.MAKE.MAKEFILES:M*lib.mk} != ""
# probably only makes sense for shared libs
# and the plumbing needed varies depending on *lib.mk
what_thing?= lib${LIB}
.if !empty(SOBJS)
_soe:= ${SOBJS:E:[1]}
what_build_exts= ${_soe}
SOBJS+= ${what_uuid}.${_soe}
.endif
.elif defined(KMOD) && ${.MAKE.MAKEFILES:M*kmod.mk} != ""
what_thing?= ${KMOD}
what_build_thing?= ${KMOD}.ko
.if defined(KMOD)
what_thing ?= ${KMOD}
.elif defined(LIB)
what_thing ?= lib${LIB}
.elif defined(PROG)
what_thing ?= ${PROG}
SRCS ?= ${PROG}.c
.elif defined(SHLIB)
what_thing ?= lib${SHLIB}
.endif
.if !empty(what_thing)
@ -39,26 +32,38 @@ what_build_thing?= ${KMOD}.ko
what_uuid = what_${what_thing}_${.CURDIR:T:hash}
what_var = what_${.CURDIR:T:hash}
.if !empty(what_build_thing)
${what_build_thing}: ${what_build_exts:@e@${what_uuid}.$e@}
.endif
OBJS+= ${what_uuid}.o
CLEANFILES+= ${what_uuid}.c
SRCS += ${what_uuid}.c
CLEANFILES += ${what_uuid}.c
# we do not need to capture this
SUPPRESS_DEPEND+= *${what_uuid}.c
SUPPRESS_DEPEND += *${what_uuid}.c
SB?= ${SRCTOP:H}
SB_LOCATION?= ${HOST}:${SB}
what_location:= ${.OBJDIR:S,${SB},${SB_LOCATION},}
SB ?= ${SRCTOP:H}
SB_LOCATION ?= ${HOST}:${SB}
# make customization easy
WHAT_LOCATION ?= ${.OBJDIR:S,${SB},${SB_LOCATION},}
WHAT_1 ?= ${what_thing:tu} built ${%Y%m%d:L:localtime} by ${USER}
WHAT_2 ?= ${what_location}
WHAT_LINE_IDS ?= 1 2
WHAT_NOCMP_LINE_IDS ?= 1
# you can add other WHAT_* just be sure to set WHAT_LINE_IDS
# and WHAT_NOCMP_LINE_IDS accordingly
# this works with clang and gcc
_what_t= const char __attribute__ ((section(".data")))
_what1:= @(\#)${what_thing:tu} built ${%Y%m%d:L:localtime} by ${USER}
_what2:= @(\#)${what_location}
what_t = const char __attribute__ ((section(".data")))
what_location := ${WHAT_LOCATION}
# this script is done in multiple lines so we can
# use the token ${.OODATE:MNO_META_CMP}
# to prevent the variable parts making this constantly out-of-date
${what_uuid}.c:
echo '${_what_t} ${what_var}1[] = "${_what1}";' > $@ ${.OODATE:MNO_META_CMP}
echo '${_what_t} ${what_var}2[] = "${_what2}";' >> $@
echo 'extern const char ${WHAT_LINE_IDS:@i@${what_var}_$i[]@:ts,};' > $@
.for i in ${WHAT_LINE_IDS}
.if ${WHAT_NOCMP_LINE_IDS:M$i} != ""
echo '${what_t} ${what_var}_$i[] = "@(#)${WHAT_$i}";' >> $@ ${.OODATE:MNO_META_CMP}
.else
echo '${what_t} ${what_var}_$i[] = "@(#)${WHAT_$i}";' >> $@
.endif
.endfor
.endif
.endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: nonints.h,v 1.74 2016/09/05 00:40:29 sevan Exp $ */
/* $NetBSD: nonints.h,v 1.75 2020/04/25 18:20:57 christos Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@ -140,8 +140,8 @@ char *str_concat(const char *, const char *, int);
char **brk_string(const char *, int *, Boolean, char **);
char *Str_FindSubstring(const char *, const char *);
int Str_Match(const char *, const char *);
char *Str_SYSVMatch(const char *, const char *, int *len);
void Str_SYSVSubst(Buffer *, char *, char *, int);
char *Str_SYSVMatch(const char *, const char *, size_t *, Boolean *);
void Str_SYSVSubst(Buffer *, char *, char *, size_t, Boolean);
#ifndef HAVE_STRLCPY
/* strlcpy.c */

14
parse.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: parse.c,v 1.231 2018/12/22 00:36:32 sjg Exp $ */
/* $NetBSD: parse.c,v 1.233 2019/09/26 21:09:55 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: parse.c,v 1.231 2018/12/22 00:36:32 sjg Exp $";
static char rcsid[] = "$NetBSD: parse.c,v 1.233 2019/09/26 21:09:55 sjg 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.231 2018/12/22 00:36:32 sjg Exp $");
__RCSID("$NetBSD: parse.c,v 1.233 2019/09/26 21:09:55 sjg Exp $");
#endif
#endif /* not lint */
#endif
@ -685,6 +685,7 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
const char *fmt, va_list ap)
{
static Boolean fatal_warning_error_printed = FALSE;
char dirbuf[MAXPATHLEN+1];
(void)fprintf(f, "%s: ", progname);
@ -703,9 +704,7 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
if (dir == NULL)
dir = ".";
if (*dir != '/') {
dir = cp2 = realpath(dir, NULL);
free(cp);
cp = cp2; /* cp2 set to NULL by Var_Value */
dir = realpath(dir, dirbuf);
}
fname = Var_Value(".PARSEFILE", VAR_GLOBAL, &cp2);
if (fname == NULL) {
@ -1768,7 +1767,8 @@ ParseDoDependency(char *line)
}
out:
assert(paths == NULL);
if (paths)
Lst_Destroy(paths, NULL);
if (curTargs)
Lst_Destroy(curTargs, NULL);
}

142
str.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: str.c,v 1.38 2017/04/21 22:15:44 sjg Exp $ */
/* $NetBSD: str.c,v 1.42 2020/05/06 02:30:10 christos Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: str.c,v 1.38 2017/04/21 22:15:44 sjg Exp $";
static char rcsid[] = "$NetBSD: str.c,v 1.42 2020/05/06 02:30:10 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
#else
__RCSID("$NetBSD: str.c,v 1.38 2017/04/21 22:15:44 sjg Exp $");
__RCSID("$NetBSD: str.c,v 1.42 2020/05/06 02:30:10 christos Exp $");
#endif
#endif /* not lint */
#endif
@ -133,42 +133,43 @@ str_concat(const char *s1, const char *s2, int flags)
*
* returns --
* Pointer to the array of pointers to the words.
* Memory containing the actual words in *buffer.
* Memory containing the actual words in *store_words_buf.
* Both of these must be free'd by the caller.
* Number of words in *store_argc.
* Number of words in *store_words_len.
*/
char **
brk_string(const char *str, int *store_argc, Boolean expand, char **buffer)
brk_string(const char *str, int *store_words_len, Boolean expand,
char **store_words_buf)
{
int argc, ch;
char inquote, *start, *t;
const char *p;
int len;
int argmax = 50, curlen = 0;
char **argv;
char inquote;
const char *str_p;
size_t str_len;
char **words;
int words_len;
int words_cap = 50;
char *words_buf, *word_start, *word_end;
/* skip leading space chars. */
for (; *str == ' ' || *str == '\t'; ++str)
continue;
/* allocate room for a copy of the string */
if ((len = strlen(str) + 1) > curlen)
*buffer = bmake_malloc(curlen = len);
/* words_buf holds the words, separated by '\0'. */
str_len = strlen(str);
words_buf = bmake_malloc(strlen(str) + 1);
/*
* initial argmax based on len
*/
argmax = MAX((len / 5), 50);
argv = bmake_malloc((argmax + 1) * sizeof(char *));
words_cap = MAX((str_len / 5), 50);
words = bmake_malloc((words_cap + 1) * sizeof(char *));
/*
* copy the string; at the same time, parse backslashes,
* quotes and build the argument list.
* quotes and build the word list.
*/
argc = 0;
words_len = 0;
inquote = '\0';
for (p = str, start = t = *buffer;; ++p) {
switch(ch = *p) {
word_start = word_end = words_buf;
for (str_p = str;; ++str_p) {
char ch = *str_p;
switch(ch) {
case '"':
case '\'':
if (inquote) {
@ -180,21 +181,21 @@ brk_string(const char *str, int *store_argc, Boolean expand, char **buffer)
else {
inquote = (char) ch;
/* Don't miss "" or '' */
if (start == NULL && p[1] == inquote) {
if (word_start == NULL && str_p[1] == inquote) {
if (!expand) {
start = t;
*t++ = ch;
word_start = word_end;
*word_end++ = ch;
} else
start = t + 1;
p++;
word_start = word_end + 1;
str_p++;
inquote = '\0';
break;
}
}
if (!expand) {
if (!start)
start = t;
*t++ = ch;
if (word_start == NULL)
word_start = word_end;
*word_end++ = ch;
}
continue;
case ' ':
@ -202,30 +203,30 @@ brk_string(const char *str, int *store_argc, Boolean expand, char **buffer)
case '\n':
if (inquote)
break;
if (!start)
if (word_start == NULL)
continue;
/* FALLTHROUGH */
case '\0':
/*
* end of a token -- make sure there's enough argv
* end of a token -- make sure there's enough words
* space and save off a pointer.
*/
if (!start)
if (word_start == NULL)
goto done;
*t++ = '\0';
if (argc == argmax) {
argmax *= 2; /* ramp up fast */
argv = (char **)bmake_realloc(argv,
(argmax + 1) * sizeof(char *));
*word_end++ = '\0';
if (words_len == words_cap) {
words_cap *= 2; /* ramp up fast */
words = (char **)bmake_realloc(words,
(words_cap + 1) * sizeof(char *));
}
argv[argc++] = start;
start = NULL;
words[words_len++] = word_start;
word_start = NULL;
if (ch == '\n' || ch == '\0') {
if (expand && inquote) {
free(argv);
free(*buffer);
*buffer = NULL;
free(words);
free(words_buf);
*store_words_buf = NULL;
return NULL;
}
goto done;
@ -233,21 +234,22 @@ brk_string(const char *str, int *store_argc, Boolean expand, char **buffer)
continue;
case '\\':
if (!expand) {
if (!start)
start = t;
*t++ = '\\';
if (*(p+1) == '\0') /* catch '\' at end of line */
if (word_start == NULL)
word_start = word_end;
*word_end++ = '\\';
/* catch '\' at end of line */
if (str_p[1] == '\0')
continue;
ch = *++p;
ch = *++str_p;
break;
}
switch (ch = *++p) {
switch (ch = *++str_p) {
case '\0':
case '\n':
/* hmmm; fix it up as best we can */
ch = '\\';
--p;
--str_p;
break;
case 'b':
ch = '\b';
@ -267,13 +269,14 @@ brk_string(const char *str, int *store_argc, Boolean expand, char **buffer)
}
break;
}
if (!start)
start = t;
*t++ = (char) ch;
if (word_start == NULL)
word_start = word_end;
*word_end++ = ch;
}
done: argv[argc] = NULL;
*store_argc = argc;
return(argv);
done: words[words_len] = NULL;
*store_words_len = words_len;
*store_words_buf = words_buf;
return words;
}
/*
@ -407,6 +410,8 @@ Str_Match(const char *string, const char *pattern)
return 0;
while ((*pattern != ']') && (*pattern != 0))
++pattern;
if (*pattern == 0)
--pattern;
goto thisCharOK;
}
/*
@ -450,12 +455,14 @@ thisCharOK: ++pattern;
*-----------------------------------------------------------------------
*/
char *
Str_SYSVMatch(const char *word, const char *pattern, int *len)
Str_SYSVMatch(const char *word, const char *pattern, size_t *len,
Boolean *hasPercent)
{
const char *p = pattern;
const char *w = word;
const char *m;
*hasPercent = FALSE;
if (*p == '\0') {
/* Null pattern is the whole string */
*len = strlen(w);
@ -463,6 +470,11 @@ Str_SYSVMatch(const char *word, const char *pattern, int *len)
}
if ((m = strchr(p, '%')) != NULL) {
*hasPercent = TRUE;
if (*w == '\0') {
/* empty word does not match pattern */
return NULL;
}
/* check that the prefix matches */
for (; p != m && *w && *w == *p; w++, p++)
continue;
@ -507,19 +519,21 @@ Str_SYSVMatch(const char *word, const char *pattern, int *len)
*-----------------------------------------------------------------------
*/
void
Str_SYSVSubst(Buffer *buf, char *pat, char *src, int len)
Str_SYSVSubst(Buffer *buf, char *pat, char *src, size_t len,
Boolean lhsHasPercent)
{
char *m;
if ((m = strchr(pat, '%')) != NULL) {
if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) {
/* Copy the prefix */
Buf_AddBytes(buf, m - pat, pat);
/* skip the % */
pat = m + 1;
}
/* Copy the pattern */
Buf_AddBytes(buf, len, src);
if (m != NULL || !lhsHasPercent) {
/* Copy the pattern */
Buf_AddBytes(buf, len, src);
}
/* append the rest */
Buf_AddBytes(buf, strlen(pat), pat);

View File

@ -1,6 +1,6 @@
# $Id: Makefile.in,v 1.49 2018/09/21 21:39:05 sjg Exp $
# $Id: Makefile,v 1.54 2020/05/17 17:26:14 sjg Exp $
#
# $NetBSD: Makefile,v 1.53 2018/05/24 00:25:44 christos Exp $
# $NetBSD: Makefile,v 1.58 2020/05/17 12:36:26 rillig Exp $
#
# Unit tests for make(1)
# The main targets are:
@ -15,28 +15,31 @@
# and it should be added to the TESTNAMES list.
#
srcdir= @srcdir@
.MAIN: all
UNIT_TESTS:= ${srcdir}
.-include "Makefile.config"
UNIT_TESTS:= ${.PARSEDIR}
.PATH: ${UNIT_TESTS}
# Each test is in a sub-makefile.
# Keep the list sorted.
TESTNAMES= \
comment \
cond-late \
cond1 \
cond2 \
dollar \
doterror \
dotwait \
error \
export \
export-all \
export-env \
doterror \
dotwait \
forloop \
forsubst \
hash \
include-main \
misc \
moderrs \
modmatch \
@ -54,6 +57,7 @@ TESTNAMES= \
unexport-env \
varcmd \
varmisc \
varmod-edge \
varquote \
varshell
@ -88,7 +92,6 @@ TEST_MAKE?= ${.MAKE}
TOOL_SED?= sed
TOOL_TR?= tr
TOOL_DIFF?= diff
DIFF_FLAGS?= @diff_u@
.if defined(.PARSEDIR)
# ensure consistent results from sort(1)

View File

@ -0,0 +1,4 @@
# $Id: Makefile.config.in,v 1.1 2018/12/30 17:14:24 sjg Exp $
srcdir= @srcdir@
DIFF_FLAGS?= @diff_u@

3
unit-tests/cond-late.exp Normal file
View File

@ -0,0 +1,3 @@
yes
no
exit status 0

23
unit-tests/cond-late.mk Normal file
View File

@ -0,0 +1,23 @@
# $NetBSD: cond-late.mk,v 1.1 2020/04/29 23:15:21 rillig Exp $
#
# Using the :? modifier, variable expressions can contain conditional
# expressions that are evaluated late. Any variables appearing in these
# conditions are expanded before parsing the condition. This is
# different from many other places.
#
# Because of this, variables that are used in these lazy conditions
# should not contain double-quotes, or the parser will probably fail.
#
# They should also not contain operators like == or <, since these are
# actually interpreted as these operators. This is demonstrated below.
#
# If the order of evaluation were to change to first parse the condition
# and then expand the variables, the output would change from the
# current "yes no" to "yes yes", since both variables are non-empty.
COND.true= "yes" == "yes"
COND.false= "yes" != "yes"
all:
@echo ${ ${COND.true} :?yes:no}
@echo ${ ${COND.false} :?yes:no}

51
unit-tests/dollar.exp Normal file
View File

@ -0,0 +1,51 @@
Printing dollar from literals and variables
To survive the parser, a dollar character must be doubled.
1 dollar literal => <single-quote-var-value>
1 dollar literal eol => <>
2 dollar literal => <$>
4 dollar literal => <$$>
Some hungry part of make eats all the dollars after a :U modifier.
1 dollar default => <>
2 dollar default => <>
4 dollar default => <>
This works as expected.
1 dollar variable => <>
2 dollar variable => <$>
4 dollar variable => <$$>
Some hungry part of make eats all the dollars after a :U modifier.
1 dollar var-default => <>
2 dollar var-default => <$>
4 dollar var-default => <$$>
Dollar in :S pattern
S,$,word, => <$XYword>
S,$X,word, => <$XY>
S,$$X,word, => <$XY>
S,$$$X,word, => <$XY>
S,$X,replaced, => <replaced>
S,$$X,replaced, => <replaced>
S,$$$X,replaced, => <replaced>
Dollar in :C character class
The A is replaced because the $$ is reduced to a single $,
which is then resolved to the variable X with the value VAR_X.
The effective character class becomes [VAR_XY].
C,[$$XY],<&>,g => <$<A><X><Y>>
Dollar in :C pattern
For some reason, multiple dollars are folded into one.
C,$,dollar,g => <>
C,$$,dollar,g => <>
Dollar in :S replacement
For some reason, multiple dollars are folded into one.
S,word,a$Xo, => <aVAR_Xo>
S,word,a$$Xo, => <aVAR_Xo>
S,word,a$$$Xo, => <aVAR_Xo>
exit status 0

81
unit-tests/dollar.mk Normal file
View File

@ -0,0 +1,81 @@
# $NetBSD: dollar.mk,v 1.3 2020/05/17 09:37:48 rillig Exp $
#
# Test the various places where a dollar character can appear and
# see what happens. There are lots of surprises here.
#
LIST= plain 'single' "double" 'mix'"ed" back\ slashed
WORD= word
DOLLAR1= $
DOLLAR2= $$
DOLLAR4= $$$$
X= VAR_X
DOLLAR_XY= $$XY
DOLLAR_AXY= $$AXY
H= @header() { printf '\n%s\n\n' "$$*"; }; header
T= @testcase() { printf '%23s => <%s>\n' "$$@"; }; testcase
C= @comment() { printf '%s\n' "$$*"; }; comment
# These variable values are not accessed.
# The trailing dollar in the '1 dollar literal eol' test case accesses
# the empty variable instead, which is always guaranteed to be empty.
${:U }= space-var-value
${:U${.newline}}= newline-var-value
# But this one is accessed.
${:U'}= single-quote-var-value'
all:
$H 'Printing dollar from literals and variables'
$C 'To survive the parser, a dollar character must be doubled.'
$T '1 dollar literal' '$'
$T '1 dollar literal eol' ''$
$T '2 dollar literal' '$$'
$T '4 dollar literal' '$$$$'
$C 'Some hungry part of make eats all the dollars after a :U modifier.'
$T '1 dollar default' ''${:U$:Q}
$T '2 dollar default' ''${:U$$:Q}
$T '4 dollar default' ''${:U$$$$:Q}
$C 'This works as expected.'
$T '1 dollar variable' ''${DOLLAR1:Q}
$T '2 dollar variable' ''${DOLLAR2:Q}
$T '4 dollar variable' ''${DOLLAR4:Q}
$C 'Some hungry part of make eats all the dollars after a :U modifier.'
$T '1 dollar var-default' ''${:U${DOLLAR1}:Q}
$T '2 dollar var-default' ''${:U${DOLLAR2}:Q}
$T '4 dollar var-default' ''${:U${DOLLAR4}:Q}
$H 'Dollar in :S pattern'
$T 'S,$$,word,' ''${DOLLAR_XY:S,$,word,:Q}
$T 'S,$$X,word,' ''${DOLLAR_XY:S,$X,word,:Q}
$T 'S,$$$$X,word,' ''${DOLLAR_XY:S,$$X,word,:Q}
$T 'S,$$$$$$X,word,' ''${DOLLAR_XY:S,$$$X,word,:Q}
$T 'S,$$X,replaced,' ''${X:S,$X,replaced,:Q}
$T 'S,$$$$X,replaced,' ''${X:S,$$X,replaced,:Q}
$T 'S,$$$$$$X,replaced,' ''${X:S,$$$X,replaced,:Q}
$H 'Dollar in :C character class'
$C 'The A is replaced because the $$$$ is reduced to a single $$,'
$C 'which is then resolved to the variable X with the value VAR_X.'
$C 'The effective character class becomes [VAR_XY].'
$T 'C,[$$$$XY],<&>,g' ''${DOLLAR_AXY:C,[$$XY],<&>,g:Q}
$H 'Dollar in :C pattern'
$C 'For some reason, multiple dollars are folded into one.'
$T 'C,$$,dollar,g' ''${DOLLAR:C,$,dollar,g:Q}
$T 'C,$$$$,dollar,g' ''${DOLLAR:C,$$,dollar,g:Q}
$H 'Dollar in :S replacement'
$C 'For some reason, multiple dollars are folded into one.'
$T 'S,word,a$$Xo,' ''${WORD:S,word,a$Xo,:Q}
$T 'S,word,a$$$$Xo,' ''${WORD:S,word,a$$Xo,:Q}
$T 'S,word,a$$$$$$Xo,' ''${WORD:S,word,a$$$Xo,:Q}

View File

@ -1,4 +1,4 @@
# $Id: escape.mk,v 1.1.1.2 2014/11/06 01:40:37 sjg Exp $
# $Id: escape.mk,v 1.1.1.3 2020/01/22 01:07:14 sjg Exp $
#
# Test backslash escaping.
@ -35,8 +35,8 @@
# Also, our practice is that an even number of backslashes before a
# newline in a variable assignment simply stores the backslashes as part
# of the value, and treats the newline as though it was not escaped.
# Similarly, ann even number of backslashes before a newline in a
# command simply uses the backslashes as part of the command test, but
# Similarly, an even number of backslashes before a newline in a
# command simply uses the backslashes as part of the command, but
# does not escape the newline. This is compatible with GNU make.
all: .PHONY

View File

@ -7,12 +7,13 @@ x=-I"This or that"
x=-Ithat
x="-DTHIS=\"this and that\""
cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\""
newline-item=(a)
a=one b="two and three"
a=four b="five"
a=ONE b="TWO AND THREE"
a=FOUR b="FIVE"
We expect an error next:
make: "forloop.mk" line 38: Wrong number of words (9) in .for substitution list with 2 vars
make: "forloop.mk" line 46: Wrong number of words (9) in .for substitution list with 2 vars
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
OK

View File

@ -1,4 +1,4 @@
# $Id: forloop.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
# $Id: forloop.mk,v 1.1.1.2 2020/05/05 00:54:40 sjg Exp $
all: for-loop
@ -33,7 +33,15 @@ X!= echo 'cfl=${cfl}' >&2; echo
.for a b in ${EMPTY}
X!= echo 'a=$a b=$b' >&2; echo
.endfor
.endif
# Since at least 1993, iteration stops at the first newline.
# Back then, the .newline variable didn't exist, therefore it was unlikely
# that a newline ever occured.
.for var in a${.newline}b${.newline}c
X!= echo 'newline-item=('${var:Q}')' 1>&2; echo
.endfor
.endif # for-fail
.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST}
X!= echo 'a=$a b=$b' >&2; echo

View File

@ -0,0 +1,6 @@
main-before-ok
sub-before-ok
subsub-ok
sub-after-fail(include-sub.mk)
main-after-fail(include-sub.mk)
exit status 0

View File

@ -0,0 +1,30 @@
# $NetBSD: include-main.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $
#
# Demonstrates that the .INCLUDEDFROMFILE magic variable does not behave
# as described in the manual page.
#
# The manual page says that it is the "filename of the file this Makefile
# was included from", while in reality it is the "filename in which the
# latest .include happened".
#
.if !defined(.INCLUDEDFROMFILE)
LOG+= main-before-ok
.else
. for f in ${.INCLUDEDFROMFILE}
LOG+= main-before-fail\(${f:Q}\)
. endfor
.endif
.include "include-sub.mk"
.if !defined(.INCLUDEDFROMFILE)
LOG+= main-after-ok
.else
. for f in ${.INCLUDEDFROMFILE}
LOG+= main-after-fail\(${f:Q}\)
. endfor
.endif
all:
@printf '%s\n' ${LOG}

17
unit-tests/include-sub.mk Normal file
View File

@ -0,0 +1,17 @@
# $NetBSD: include-sub.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $
.if ${.INCLUDEDFROMFILE} == "include-main.mk"
LOG+= sub-before-ok
.else
LOG+= sub-before-fail
.endif
.include "include-subsub.mk"
.if ${.INCLUDEDFROMFILE} == "include-main.mk"
LOG+= sub-after-ok
.else
. for f in ${.INCLUDEDFROMFILE}
LOG+= sub-after-fail\(${f:Q}\)
. endfor
.endif

View File

@ -0,0 +1,7 @@
# $NetBSD: include-subsub.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $
.if ${.INCLUDEDFROMFILE:T} == "include-sub.mk"
LOG+= subsub-ok
.else
LOG+= subsub-fail
.endif

View File

@ -1,4 +1,4 @@
# $NetBSD: modorder.mk,v 1.1 2014/08/21 13:44:51 apb Exp $
# $NetBSD: modorder.mk,v 1.2 2020/01/07 22:42:14 rillig Exp $
LIST= one two three four five six seven eight nine ten
LISTX= ${LIST:Ox}
@ -12,8 +12,9 @@ all:
@echo "LIST:O = ${LIST:O}"
# Note that 1 in every 10! trials two independently generated
# randomized orderings will be the same. The test framework doesn't
# support checking probabilistic output, so we accept that the test
# will incorrectly fail with probability 2.8E-7.
# support checking probabilistic output, so we accept that each of the
# 3 :Ox tests will incorrectly fail with probability 2.756E-7, which
# lets the whole test fail once in 1.209.600 runs, on average.
@echo "LIST:Ox = `test '${LIST:Ox}' != '${LIST:Ox}' ${TEST_RESULT}`"
@echo "LIST:O:Ox = `test '${LIST:O:Ox}' != '${LIST:O:Ox}' ${TEST_RESULT}`"
@echo "LISTX = `test '${LISTX}' != '${LISTX}' ${TEST_RESULT}`"

View File

@ -4,4 +4,12 @@ fun
fun
fun
In the Sun
acme
aam.d
sam.c
a%.c
asam.c.c
asam.c
a.c.c
exit status 0

View File

@ -1,4 +1,4 @@
# $Id: sysv.mk,v 1.2 2014/08/30 22:25:14 sjg Exp $
# $Id: sysv.mk,v 1.4 2020/05/07 01:17:51 sjg Exp $
FOO ?=
FOOBAR = ${FOO:=bar}
@ -11,7 +11,7 @@ FUN = ${B}${S}fun
SUN = the Sun
# we expect nothing when FOO is empty
all: foo fun
all: foo fun sam bla
foo:
@echo FOOBAR = ${FOOBAR}
@ -24,3 +24,20 @@ fun:
@echo ${FUN:${B}${S}fun=fun}
@echo ${FUN:${B}${S}%=%}
@echo ${In:L:%=% ${SUN}}
SAM=sam.c
sam:
@echo ${SAM:s%.c=acme}
@echo ${SAM:s%.c=a%.d}
@echo ${SAM:s.c=a%.d}
@echo ${SAM:sam.c=a%.c}
@echo ${SAM:%=a%.c}
@echo ${SAM:%.c=a%.c}
@echo ${SAM:sam%=a%.c}
BLA=
bla:
@echo $(BLA:%=foo/%x)

View File

@ -0,0 +1,17 @@
make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
make: Unclosed substitution for INP.eq-esc (= missing)
ok M-paren
ok M-mixed
ok M-unescape
ok M-nest-mix
ok M-nest-brk
ok M-pat-err
ok M-bsbs
ok M-bs1-par
ok M-bs2-par
ok M-128
ok eq-ext
ok eq-q
ok eq-bs
ok eq-esc
exit status 0

162
unit-tests/varmod-edge.mk Normal file
View File

@ -0,0 +1,162 @@
# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $
#
# Tests for edge cases in variable modifiers.
#
# These tests demonstrate the current implementation in small examples.
# They may contain surprising behavior.
#
# Each test consists of:
# - INP, the input to the test
# - MOD, the expression for testing the modifier
# - EXP, the expected output
TESTS+= M-paren
INP.M-paren= (parentheses) {braces} (opening closing) ()
MOD.M-paren= ${INP.M-paren:M(*)}
EXP.M-paren= (parentheses) ()
# The first closing brace matches the opening parenthesis.
# The second closing brace actually ends the variable expression.
#
# XXX: This is unexpected but rarely occurs in practice.
TESTS+= M-mixed
INP.M-mixed= (paren-brace} (
MOD.M-mixed= ${INP.M-mixed:M(*}}
EXP.M-mixed= (paren-brace}
# After the :M modifier has parsed the pattern, only the closing brace
# and the colon are unescaped. The other characters are left as-is.
# To actually see this effect, the backslashes in the :M modifier need
# to be doubled since single backslashes would simply be unescaped by
# Str_Match.
#
# XXX: This is unexpected. The opening brace should also be unescaped.
TESTS+= M-unescape
INP.M-unescape= ({}): \(\{\}\)\: \(\{}\):
MOD.M-unescape= ${INP.M-unescape:M\\(\\{\\}\\)\\:}
EXP.M-unescape= \(\{}\):
# When the :M and :N modifiers are parsed, the pattern finishes as soon
# as open_parens + open_braces == closing_parens + closing_braces. This
# means that ( and } form a matching pair.
#
# Nested variable expressions are not parsed as such. Instead, only the
# parentheses and braces are counted. This leads to a parse error since
# the nested expression is not "${:U*)}" but only "${:U*)", which is
# missing the closing brace. The expression is evaluated anyway.
# The final brace in the output comes from the end of M.nest-mix.
#
# XXX: This is unexpected but rarely occurs in practice.
TESTS+= M-nest-mix
INP.M-nest-mix= (parentheses)
MOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}}
EXP.M-nest-mix= (parentheses)}
# make: Unclosed variable specification (expecting '}') for "" (value "*)") modifier U
# In contrast to parentheses and braces, the brackets are not counted
# when the :M modifier is parsed since Makefile variables only take the
# ${VAR} or $(VAR) forms, but not $[VAR].
#
# The final ] in the pattern is needed to close the character class.
TESTS+= M-nest-brk
INP.M-nest-brk= [ [[ [[[
MOD.M-nest-brk= ${INP.M-nest-brk:M${:U[[[[[]}}
EXP.M-nest-brk= [
# The pattern in the nested variable has an unclosed character class.
# No error is reported though, and the pattern is closed implicitly.
#
# XXX: It is unexpected that no error is reported.
# See str.c, function Str_Match.
#
# Before 2019-12-02, this test case triggered an out-of-bounds read
# in Str_Match.
TESTS+= M-pat-err
INP.M-pat-err= [ [[ [[[
MOD.M-pat-err= ${INP.M-pat-err:M${:U[[}}
EXP.M-pat-err= [
# The first backslash does not escape the second backslash.
# Therefore, the second backslash escapes the parenthesis.
# This means that the pattern ends there.
# The final } in the output comes from the end of MOD.M-bsbs.
#
# If the first backslash were to escape the second backslash, the first
# closing brace would match the opening parenthesis (see M-mixed), and
# the second closing brace would be needed to close the variable.
# After that, the remaining backslash would escape the parenthesis in
# the pattern, therefore (} would match.
TESTS+= M-bsbs
INP.M-bsbs= (} \( \(}
MOD.M-bsbs= ${INP.M-bsbs:M\\(}}
EXP.M-bsbs= \(}
#EXP.M-bsbs= (} # If the first backslash were to escape ...
# The backslash in \( does not escape the parenthesis, therefore it
# counts for the nesting level and matches with the first closing brace.
# The second closing brace closes the variable, and the third is copied
# literally.
#
# The second :M in the pattern is nested between ( and }, therefore it
# does not start a new modifier.
TESTS+= M-bs1-par
INP.M-bs1-par= ( (:M (:M} \( \(:M \(:M}
MOD.M-bs1-par= ${INP.M-bs1-par:M\(:M*}}}
EXP.M-bs1-par= (:M}}
# The double backslash is passed verbatim to the pattern matcher.
# The Str_Match pattern is \\(:M*}, and there the backslash is unescaped.
# Again, the ( takes place in the nesting level, and there is no way to
# prevent this, no matter how many backslashes are used.
TESTS+= M-bs2-par
INP.M-bs2-par= ( (:M (:M} \( \(:M \(:M}
MOD.M-bs2-par= ${INP.M-bs2-par:M\\(:M*}}}
EXP.M-bs2-par= \(:M}}
# Str_Match uses a recursive algorithm for matching the * patterns.
# Make sure that it survives patterns with 128 asterisks.
# That should be enough for all practical purposes.
# To produce a stack overflow, just add more :Qs below.
TESTS+= M-128
INP.M-128= ${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,x,g}
PAT.M-128= ${:U\\:Q:Q:Q:Q:Q:Q:Q:S,\\,*,g}
MOD.M-128= ${INP.M-128:M${PAT.M-128}}
EXP.M-128= ${INP.M-128}
# This is the normal SysV substitution. Nothing surprising here.
TESTS+= eq-ext
INP.eq-ext= file.c file.cc
MOD.eq-ext= ${INP.eq-ext:%.c=%.o}
EXP.eq-ext= file.o file.cc
# The SysV := modifier is greedy and consumes all the modifier text
# up until the closing brace or parenthesis. The :Q may look like a
# modifier, but it really isn't, that's why it appears in the output.
TESTS+= eq-q
INP.eq-q= file.c file.cc
MOD.eq-q= ${INP.eq-q:%.c=%.o:Q}
EXP.eq-q= file.o:Q file.cc
# The = in the := modifier can be escaped.
TESTS+= eq-bs
INP.eq-bs= file.c file.c=%.o
MOD.eq-bs= ${INP.eq-bs:%.c\=%.o=%.ext}
EXP.eq-bs= file.c file.ext
# Having only an escaped = results in a parse error.
# The call to "pattern.lhs = VarGetPattern" fails.
TESTS+= eq-esc
INP.eq-esc= file.c file...
MOD.eq-esc= ${INP.eq-esc:a\=b}
EXP.eq-esc= # empty
# make: Unclosed substitution for INP.eq-esc (= missing)
all:
.for test in ${TESTS}
. if ${MOD.${test}} == ${EXP.${test}}
@printf 'ok %s\n' ${test:Q}''
. else
@printf 'error in %s: expected %s, got %s\n' \
${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}''
. endif
.endfor

34
util.c
View File

@ -1,9 +1,9 @@
/* $NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $ */
/* $NetBSD: util.c,v 1.55 2020/01/07 21:24:16 rillig Exp $ */
/*
* Missing stuff from OS's
*
* $Id: util.c,v 1.33 2014/01/02 02:29:49 sjg Exp $
* $Id: util.c,v 1.34 2020/01/22 01:19:25 sjg Exp $
*/
#if defined(__MINT__) || defined(__linux__)
#include <signal.h>
@ -12,10 +12,10 @@
#include "make.h"
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $";
static char rcsid[] = "$NetBSD: util.c,v 1.55 2020/01/07 21:24:16 rillig Exp $";
#else
#ifndef lint
__RCSID("$NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $");
__RCSID("$NetBSD: util.c,v 1.55 2020/01/07 21:24:16 rillig Exp $");
#endif
#endif
@ -229,32 +229,6 @@ killpg(int pid, int sig)
return kill(-pid, sig);
}
#if !defined(__hpux__) && !defined(__hpux)
void
srandom(long seed)
{
srand48(seed);
}
long
random(void)
{
return lrand48();
}
#endif
#if !defined(__hpux__) && !defined(__hpux)
int
utimes(char *file, struct timeval tvp[2])
{
struct utimbuf t;
t.actime = tvp[0].tv_sec;
t.modtime = tvp[1].tv_sec;
return(utime(file, &t));
}
#endif
#if !defined(BSD) && !defined(d_fileno)
# define d_fileno d_ino
#endif

15
var.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.221 2018/12/21 05:50:19 sjg Exp $ */
/* $NetBSD: var.c,v 1.223 2020/04/25 18:20:57 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -69,14 +69,14 @@
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: var.c,v 1.221 2018/12/21 05:50:19 sjg Exp $";
static char rcsid[] = "$NetBSD: var.c,v 1.223 2020/04/25 18:20:57 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.221 2018/12/21 05:50:19 sjg Exp $");
__RCSID("$NetBSD: var.c,v 1.223 2020/04/25 18:20:57 christos Exp $");
#endif
#endif /* not lint */
#endif
@ -1401,8 +1401,9 @@ VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate,
char *word, Boolean addSpace, Buffer *buf,
void *patp)
{
int len;
size_t len;
char *ptr;
Boolean hasPercent;
VarPattern *pat = (VarPattern *)patp;
char *varexp;
@ -1411,9 +1412,9 @@ VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate,
addSpace = TRUE;
if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL) {
if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) {
varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES);
Str_SYSVSubst(buf, varexp, ptr, len);
Str_SYSVSubst(buf, varexp, ptr, len, hasPercent);
free(varexp);
} else {
Buf_AddBytes(buf, strlen(word), word);
@ -2399,8 +2400,10 @@ VarHash(char *str)
break;
case 3:
k |= (ustr[2] << 16);
/* FALLTHROUGH */
case 2:
k |= (ustr[1] << 8);
/* FALLTHROUGH */
case 1:
k |= ustr[0];
len = 0;