Index: head/contrib/bmake/sprite.h =================================================================== --- head/contrib/bmake/sprite.h (revision 365365) +++ head/contrib/bmake/sprite.h (nonexistent) @@ -1,116 +0,0 @@ -/* $NetBSD: sprite.h,v 1.14 2017/05/31 22:02:06 maya Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. - * - * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 - */ - -/* - * Copyright (c) 1989 by Berkeley Softworks - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. - * - * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 - */ - -/* - * sprite.h -- - * - * Common constants and type declarations for Sprite. - */ - -#ifndef MAKE_SPRITE_H -#define MAKE_SPRITE_H - - -/* - * A boolean type is defined as an integer, not an enum. This allows a - * boolean argument to be an expression that isn't strictly 0 or 1 valued. - */ - -typedef int Boolean; -#ifndef TRUE -#define TRUE 1 -#endif /* TRUE */ -#ifndef FALSE -#define FALSE 0 -#endif /* FALSE */ - -/* - * Functions that must return a status can return a ReturnStatus to - * indicate success or type of failure. - */ - -typedef int ReturnStatus; - -/* - * The following statuses overlap with the first 2 generic statuses - * defined in status.h: - * - * SUCCESS There was no error. - * FAILURE There was a general error. - */ - -#define SUCCESS 0x00000000 -#define FAILURE 0x00000001 - -#endif /* MAKE_SPRITE_H */ Property changes on: head/contrib/bmake/sprite.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/ChangeLog =================================================================== --- head/contrib/bmake/ChangeLog (revision 365365) +++ head/contrib/bmake/ChangeLog (revision 365366) @@ -1,2495 +1,2735 @@ +2020-09-02 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200902 + Merge with NetBSD make, pick up + o use make_stat to ensure no confusion over valid fields + returned by cached_stat + o var.c: make VarQuote const-correct + o add unit tests for .for + +2020-09-01 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200901 + Merge with NetBSD make, pick up + o rename Hash_Table fields + o make data types in Dir_HasWildcards more precise + +2020-08-31 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200831 + Merge with NetBSD make, pick up + o suff.c: fix unbalanced Lst_Open/Lst_Close in SuffFindCmds + o lst.c: Lst_Open renable assert that list isn't open + o unit test for .TARGET dependent flags + o var.c: fix aliasing bug in VarUniq + o more unit tests for :u + +2020-08-30 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200830 + Merge with NetBSD make, pick up + o allow for strict type checking for Boolean + o Var_Parse never returns NULL + o Var_Subst never returns NULL + o Lst_Find now takes boolean match function + o rename Lst_Memeber to Lst_FindDatum + o rename LstNode functions to match their type + o rename GNode.iParents to implicitParents + o fix assertion failure for .SUFFIXES in archives + o compat.c: clean up documentation for CompatInterrupt and Compat_Run + remove unreachable code from CompatRunCommand + o main.c: simplify getBoolean + o stc.c: replace brk_string with simpler Str_Words + o suff.c: add debug macros + +2020-08-28 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200828 + Merge with NetBSD make, pick up + o lst.c: inline LstIsValid and LstNodeIsValid + o remove trailing S from Lst function names after migration complete + o more comment cleanup/clarification + o suff.c: clean up suffix handling + o more unit tests + +2020-08-26 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200826 + Merge with NetBSD make, pick up + o enum.c: distinguish between bitsets containing flags and + ordinary enums + o var.c: fix error message for ::!= modifier with shell error + o fix bugs in -DCLEANUP mode + +2020-08-24 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200824 + Merge with NetBSD make, pick up + o in debug mode, print GNode details in symbols + +2020-08-23 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200823 + Merge with NetBSD make, pick up + o lst.c: more asserts, + make args to Lst_Find match others. + o var.c: pass flags to VarAdd + o arch.c: use Buffer + o str.c: brk_string return size_t for nwords + o more unit tests + +2020-08-22 Simon J Gerraty + + * VERSION (_MAKE_VERSION): + Merge with NetBSD make, pick up + o var.c: support for read-only variables eg .SHELL + being the shell used to run scripts. + o lst.c: more simplification + o more documentation and style cleanup + o more unit tests + o ensure unit-test/Makefile is run by TEST_MAKE + o reduce duplication of header inclusion + +2020-08-21 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200821 + Merge with NetBSD make, pick up + o lst.c: revert invalid assertion - but document it + o dir.c: split Dir_Init into two functions + +2020-08-20 Simon J Gerraty + + * lst.c: needs inttypes.h on Linux + + * VERSION (_MAKE_VERSION): 20200820 + Merge with NetBSD make, pick up + o make.1: clarify some passages + o var.c: more cleanup, clarify comments + o make_malloc.c: remove unreachable code + o cond.c: make CondGetString easier to debug + o simplify list usage + o unit-tests: more + +2020-08-16 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200816 + Merge with NetBSD make, pick up + o refactor unit-tests to be more fine grained + not all tests moved yet + +2020-08-14 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200814 + Merge with NetBSD make, pick up + o more str_concat variants + o more enums for flags + o var.c: cleanup for higher warnings level + +2020-08-10 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200810 + Merge with NetBSD make, pick up + o more unit tests + o general comment and style cleanup + +2020-08-08 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200808 + Merge with NetBSD make, pick up + o enum.[ch]: streamline, enums for use in flags and debug output + o cond.c: cleanup + o var.c: reduce duplicate code for modifiers + debug logging for Var_Parse + more detailed debug output + o more unit tests + +2020-08-06 Simon J Gerraty + + * unit-tests/Makefile: -r for recursive and include Makefile.inc + so I can run tests in meta mode + supress extra noise if in meta mode + + * VERSION (_MAKE_VERSION): 20200806 + Merge with NetBSD make, pick up + o parse.c: remove VARE_WANTRES for LINT + we just want to check parsing (for now). + +2020-08-05 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200805 + Merge with NetBSD make, pick up + o make.1: Rework the description of dependence operators + +2020-08-03 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200803 + Merge with NetBSD make, pick up + o revert some C99 usage, for max portability + o unit-tests/lint + +2020-08-02 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200802 + Merge with NetBSD make, pick up + o more unit tests + +2020-08-01 Simon J Gerraty + + * Remove NetBSD specific plumbing from unit-tests/Makefile + + * VERSION (_MAKE_VERSION): 20200801 + Merge with NetBSD make, pick up + o make Var_Value return const + o size_t for buf sizes + o optimize some buffer operations - avoid strlen + +2020-07-31 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200731 + Merge with NetBSD make, pick up + o var.c: fix undefinded behavior for incomplete :t modifier + fixes unit-test/moderrs on Ubuntu + o parse.c: When parsing variable assignments other than := + if DEBUG(LINT) test substition of value, so we get a file and + line number in the resulting error. + o dir.c: fix parsing of nested braces in dependency lines + add unit-tests + +2020-07-30 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200730 + Merge with NetBSD make, pick up + o var.c: minor cleanup + o unit-tests: more tests to improve code coverage + +2020-07-28 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200728 + Merge with NetBSD make, pick up + o var.c: more optimizations + +2020-07-26 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200726 + Merge with NetBSD make, pick up + o collapse lsd.lib into lst.c - reduce code size and allow inlining + o lots of function comment updates + o var.c: more optimizations + o make return of Var_Parse const + +2020-07-20 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200720 + Merge with NetBSD make, pick up + o DEBUG_HASH report stats at end and tone down the noise + o var.c: each flag type gets its own prefix. + move SysV string matching to var.c + make ampersand in ${VAR:from=to&} an ordinary character + cleanup and simplify implementation of modifiers + o make.1: move documentation for assignment modifiers + +2020-07-18 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20200718 + Merge with NetBSD make, pick up + o DEBUG_HASH to see how well the hash tables are working + +2020-07-11 Simon J Gerraty + + * bsd.after-import.mk: make sure we update unit-tests/Makefile + 2020-07-10 Simon J Gerraty * configure.in: use AC_INCLUDES_DEFAULT rather than AC_HEADER_STDC * VERSION (_MAKE_VERSION): 20200710 Merge with NetBSD make, pick up o filemon/filemon_dev.c: use O_CLOEXEC rather than extra syscall o meta.c: target flagged .META is out-of-date if meta file missing 2020-07-09 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200709 Merge with NetBSD make, pick up o cond.c: fix for compare_expression when doEval=0 o unit-tests/Makefile: rework o filemon/filemon_dev.c: ensure filemon fd is closed on exec. 2020-07-04 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200704 Merge with NetBSD make, pick up (most of this by rillig@) o lots of style and white-space cleanup o lots more unit tests for variable modifiers o simplified description of some functions o str.c: refactor Str_Match o var.c: debugging output for :@ constify VarModify parameter fix :hash modifier on 16-bit platforms remove unnecessary forward declarations refactor ApplyModifier_SysV to have less indentation simplify code for :E and :R clean up code for :H and :T refactor ApplyModifiers * var.c: we need stdint.h on some platforms to get uint32_t * unit-test/Makefile: we need to supress the specific error for RE substitution error in modmisc, since it varies accross different OS. 2020-07-02 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200702 Merge with NetBSD make, pick up o var.c: more improvements to avoiding unnecessary evaluation use enums for flags o remove flags arg to Var_Set which outside of var.c is always 0 2020-07-01 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200701 Merge with NetBSD make, pick up o var.c: with change to cond.c; ensure that nested variables within a variable name are expanded. o unit-tests/varmisc.mk: test for nested varname 2020-06-29 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200629 Merge with NetBSD make, pick up o cond.c: do not eval unnecessary terms of conditionals. 2020-06-25 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200625 Merge with NetBSD make, pick up o meta.c: report error if lseek in filemon_read fails 2020-06-22 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200622 Merge with NetBSD make, pick up o dieQuietly: ignore OP_SUBMAKE as too aggressive 2020-06-19 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200619 Merge with NetBSD make, pick up o str.c: performance improvement for Str_Match for multiple '*' o dieQuietly: supress the failure output from make when failing node is a sub-make or a sibling failed. This cuts down greatly on unhelpful noise at the end of build log. Disabled by -dj or .MAKE.DIE_QUIETLY=no 2020-06-10 Simon J Gerraty * FILES: add LICENSE to appease some packagers. This is an attempt to fairly represent the license on almost 200 files, which are almost all BSD-3-Clause The few exceptions being more liberal. * VERSION (_MAKE_VERSION): 20200610 Merge with NetBSD make, pick up o unit test for :Or 2020-06-06 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200606 Merge with NetBSD make, pick up o make.1: cleanup * Makefile: fix depends for main.o which broke MAKE_VERSION 2020-06-05 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200605 Merge with NetBSD make, pick up o dir.c: cached_stats - don't confuse stat and lstat results. o var.c: add :Or for reverse sort. 2020-05-24 Simon J Gerraty * configure.in: add AC_PROG_CC_C99 for mipspro compiler also if --with-filemon= specifies path to filemon.h set use_filemon=dev * dirname.c: remove include of namespace.h 2020-05-17 Simon J Gerraty * 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 * unit-tests/dollar.mk: tweak '1 dollar literal' test to not depend so much on shell behavior 2020-05-10 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200510 Merge with NetBSD make, pick up o unit test for dollar handling 2020-05-06 Simon J Gerraty * 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 * 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 * 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 * 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 * VERSION (_MAKE_VERSION): 20200330 Merge with NetBSD make, pick up o make.h: extern debug_file 2020-03-18 Simon J Gerraty * 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 * VERSION (_MAKE_VERSION): 20200220 2020-02-19 Simon J Gerraty * boot-strap: unset MAKEFLAGS 2020-02-12 Simon J Gerraty * VERSION (_MAKE_VERSION): 20200212 * meta.c: meta_compat_parent check for USE_FILEMON patch from Soeren Tempel 2020-02-05 Simon J Gerraty * 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 * 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 * 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 * 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 * VERSION: 20190409 Merge with NetBSD make, pick up o parse.c: ParseDoDependency: free paths rather than assert 2018-12-22 Simon J Gerraty * 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 * VERSION: 20181221 Merge with NetBSD make, pick up o parse.c: ParseVErrorInternal use .PARSEDIR and apply if relative, and then use .PARSEFILE for consistent result. 2018-12-20 Simon J Gerraty * VERSION: 20181220 Merge with NetBSD make, pick up o parse.c: ParseVErrorInternal use .CURDIR if .PARSEDIR is relative o var.c: avoid SEGFAULT in .unexport-env when MAKELEVEL is not set 2018-12-16 Simon J Gerraty * VERSION: 20181216 Merge with NetBSD make, pick up o fix for unit-tests/varquote.mk on Debian 2018-09-21 Simon J. Gerraty * VERSION: 20180919 Merge with NetBSD make, pick up o var.c: add :q o dir.c: cleanup caching of stats 2018-09-21 Simon J Gerraty * Makefile.config.in: use += where it makes sense. 2018-05-12 Simon J. Gerraty * VERSION: 20180512 Merge with NetBSD make, pick up o job.c: skip polling job token pipe 2018-04-05 Simon J. Gerraty * VERSION: 20180405 Merge with NetBSD make, pick up o parse.c: be more cautious about detecting depenency line rather than sysV style include. 2018-02-22 Simon J. Gerraty * VERSION: 20180222 Merge with NetBSD make, pick up o parse.c: avoid calling sysconf for every call to loadfile 2018-02-18 Simon J. Gerraty * VERSION: 20180218 Merge with NetBSD make, pick up o var.c: Var_Set handle NULL value anytime. 2018-02-12 Simon J. Gerraty * VERSION: 20180212 Merge with NetBSD make, pick up o parse.c: do not treat .info as warning with -W 2017-12-07 Simon J. Gerraty * VERSION: 20171207 Merge with NetBSD make, pick up o var.c: Var_Append use Var_Set if var not previously set so that VAR_CMD is handled correctly. Add a suitable unit-test. 2017-11-26 Simon J. Gerraty * VERSION (_MAKE_VERSION): 20171126 * aclocal.m4: use AC_LINK_IFELSE for AC_C___ATTRIBUTE__ since AC_TRY_COMPILE puts input inside main() which upsets modern compilers. 2017-11-18 Simon J. Gerraty * VERSION: 20171118 Merge with NetBSD make, pick up o var.c: do not append to variable set on command line add unit-test to catch this. 2017-10-28 Simon J. Gerraty * VERSION: 20171028 Merge with NetBSD make, pick up o main.c: ignore empty MAKEOBJDIR * Makefile.config.in: make @prefix@ @machine*@ and @default_sys_path@ defaults. 2017-10-05 Simon J. Gerraty * VERSION: 20171005 * unit-tests/dotwait.mk: redirect stderr through pipe for more consistent result on some platforms. 2017-08-13 Simon J. Gerraty * machine.sh: entry for AIX 2017-08-12 Simon J. Gerraty * VERSION (_MAKE_VERSION): Move the setting of _MAKE_VERSION to a file that can be included by configure as well as make. This allows configure to set set _MAKE_VERSION in make-bootstrap.sh 2017-08-10 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170810 Merge with NetBSD make, pick up o meta.c: if target is in subdir we only need subdir name in meta_name. 2017-07-20 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170720 Merge with NetBSD make, pick up o compat.c: pass SIGINT etc onto child and wait for it to exit before we self-terminate. 2017-07-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170711 forgot to update after merge on 20170708 ;-) o main.c: refactor to reduce size of main function. add -v option to always fully expand values. o meta.c: ensure command output in meta file has ending newline even when filemon not being used. When matching ${.MAKE.META.IGNORE_PATTERNS} do not use pathname via ':L' since any ':' in pathname breaks that. Instead set a '${.p.}' to pathname in the target context and use that. 2017-05-10 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170510 Merge with NetBSD make, pick up o main.c: Main_SetObjdir: ensure buf2 is in scope 2017-05-08 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170505 see mk/ChangeLog 2017-05-05 Simon J. Gerraty * parse.c: not everyone has stdint.h 2017-05-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170501 see mk/ChangeLog 2017-04-21 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170421 Merge with NetBSD make, pick up o str.c: Str_Match: fix closure tests for [^] and add unit-test. 2017-04-20 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170420 Merge with NetBSD make, pick up o main.c: only use -C arg "as is" if it contains no relative component. 2017-04-18 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170418 Merge with NetBSD make, pick up o main.c: fix Main_SetObjdir() for relative paths (eg obj). 2017-04-17 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170417 Merge with NetBSD make, pick up o fixes a number of coverity complaints - check return value of fseek, fcntl - plug memory leak in Dir_FindFile, Var_LoopExpand, JobPrintCommand, ParseTraditionalInclude - use bmake_malloc() where NULL is not tollerated - use MAKE_ATTR_UNUSED rather that kludges like return(unused ? 0 : 0) - use purge_cached_realpaths() rather than abuse cached_realpath() 2017-04-13 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170413 Merge with NetBSD make, pick up o main.c: when setting .OBJDIR ignore '$' in paths. * job.c: use MALLOC_OPTIONS to set malloc_options. 2017-04-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170411 Merge with NetBSD make, pick up o str.c: Str_Match: allow [^a-z] to behave as expected. 2017-03-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170326 Merge with NetBSD make, pick up o main.c: purge relative paths from realpath cache when .OBJDIR is changed. 2017-03-11 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170311 Merge with NetBSD make, pick up o main.c: only use -C arg "as is" if it starts with '/'. 2017-03-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170301 Merge with NetBSD make, pick up o main.c: use -C arg "as is" rather than getcwd() if they identify the same directory. o parse.c: ensure loadfile buffer is \n terminated in non-mmap case 2017-02-01 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170201 Merge with NetBSD make, pick up o var.c: allow :_=var and avoid use of special context. 2017-01-30 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170130 Merge with NetBSD make, pick up o var.c: add :range and :_ o main.c: partially initialize Dir_* before MainParseArgs() can be called. If -V, skip Main_ExportMAKEFLAGS() 2017-01-14 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20170114 Merge with NetBSD make, pick up o var.c: allow specifying the utc value used by :{gm,local}time 2016-12-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161212 Merge with NetBSD make, pick up o main.c: look for obj.${MACHINE}-${MACHINE_ARCH} too. 2016-12-09 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161209 Merge with NetBSD make, pick up o main.c: cleanup setting of .OBJDIR o parse.c: avoid coredump from (var)=val 2016-11-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20161126 Merge with NetBSD make, pick up o make.c: Make_OODate: report src node name if path not set 2016-09-26 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160926 Merge with NetBSD make, pick up o support for .DELETE_ON_ERROR: (remove targets that fail) 2016-09-26 Simon J. Gerraty * Makefile MAN: tweak .Dt to match ${PROG} 2016-08-18 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160818 its a neater number; pick up whitespace fixes to man page. 2016-08-17 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160817 Merge with NetBSD make, pick up o meta.c: move handling of .MAKE.META.IGNORE_* to meta_ignore() so we can call it before adding entries to missingFiles. Thus we do not track files we have been told to ignore. 2016-08-15 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160815 Merge with NetBSD make, pick up o meta_oodate: apply .MAKE.META.IGNORE_FILTER (if defined) to pathnames, and skip if the expansion is empty. Useful for dirdeps.mk when checking DIRDEPS_CACHE. 2016-08-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160812 Merge with NetBSD make, pick up o meta.c: remove all missingFiles entries that match a deleted dir. o main.c: set .ERROR_CMD if possible. 2016-06-06 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160606 Merge with NetBSD make, pick up o dir.c: extend mtimes cache to others via cached_stat() 2016-06-04 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160604 Merge with NetBSD make, pick up o meta.c: missing filemon data is only relevant if we read a meta file. Also do not return oodate for a missing metafile if gn->path points to .CURDIR 2016-06-02 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160602 Merge with NetBSD make, pick up o cached_realpath(): avoid hitting filesystem more than necessary. o meta.c: refactor need_meta decision, add knobs for missing meta file and filemon data wrt out-of-datedness. 2016-05-28 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160528 * boot-strap, make-bootstrap.sh.in: Makefile now uses _MAKE_VERSION 2016-05-12 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160512 Merge with NetBSD make, pick up o meta.c: ignore paths that match .MAKE.META.IGNORE_PATTERNS this is useful for gcov builds. o propagate errors from filemon(4). 2016-05-09 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160509 Merge with NetBSD make, pick up o remove use of non-standard types u_int etc. o meta.c: apply realpath() before matching against metaIgnorePaths 2016-04-04 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160404 Merge with NetBSD make, pick up o allow makefile to set .MAKE.JOBS * Makefile (PROG_NAME): use ${_MAKE_VERSION} 2016-03-15 Simon J. Gerraty * Makefile (_MAKE_VERSION): 20160315 Merge with NetBSD make, pick up o fix handling of archive members 2016-03-13 Simon J. Gerraty * Makefile (_MAKE_VERSION): rename variable to avoid interference with checks for ${MAKE_VERSION} 2016-03-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160310 Merge with NetBSD make, pick up o meta.c: treat missing Read file same as Write, incase we Delete it. 2016-03-07 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160307 Merge with NetBSD make, pick up o var.c: fix :ts\nnn to be octal by default. o meta.c: meta_finish() to cleanup memory. 2016-02-26 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160226 Merge with NetBSD make, pick up o meta.c: allow meta file for makeDepend if makefiles want it. 2016-02-19 Simon J. Gerraty * var.c: default .MAKE.SAVE_DOLLARS to FALSE for backwards compatability. * Makefile (MAKE_VERSION): 20160220 Merge with NetBSD make, pick up o var.c: add knob to control handling of '$$' in := 2016-02-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160218 Merge with NetBSD make, pick up o var.c: add .export-literal allows us to fix sys.clean-env.mk post the changes to Var_Subst. Var_Subst now takes flags, and does not consume '$$' in := 2016-02-17 Simon J. Gerraty * Makefile (MAKE_VERSION): 20160217 Merge with NetBSD make, pick up o var.c: preserve '$$' in := o parse.c: add .dinclude for handling included makefile like .depend 2015-12-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151220 Merge with NetBSD make, pick up o suff.c: re-initialize suffNull when clearing suffixes. 2015-12-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151201 Merge with NetBSD make, pick up o cond.c: CondCvtArg: avoid access beyond end of empty buffer. o meta.c: meta_oodate: use lstat(2) for checking link target in case it is a symlink. o var.c: avoid calling brk_string and Var_Export1 with empty strings. 2015-11-26 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151126 Merge with NetBSD make, pick up o parse.c: ParseTrackInput don't access beyond end of old value. 2015-10-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151022 * Add support for BSD/OS which lacks inttypes.h and really needs sys/param.h for sys/sysctl.h also 'type' is not a shell builtin. * var.c: eliminate uint32_t and need for inttypes.h * main.c: PrintOnError flush stdout before run .ERROR * parse.c: cope with _SC_PAGESIZE not being defined. 2015-10-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151020 Merge with NetBSD make, pick up o var.c: fix uninitialized var 2015-10-12 Simon J. Gerraty * var.c: the conditional expressions used with ':?' can be expensive, if already discarding do not evaluate or expand anything. 2015-10-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151010 Merge with NetBSD make, pick up o Add Boolean wantit flag to Var_Subst and Var_Parse when FALSE we know we are discarding the result and can skip operations like Cmd_Exec. 2015-10-09 Simon J. Gerraty * Makefile (MAKE_VERSION): 20151009 Merge with NetBSD make, pick up o var.c: don't check for NULL before free() o meta.c: meta_oodate, do not hard code ignore of makeDependfile 2015-09-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150910 Merge with NetBSD make, pick up o main.c: with -w print Enter/Leaving messages for objdir too if necessary. o centralize shell metachar handling * FILES: add metachar.[ch] 2015-06-06 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150606 Merge with NetBSD make, pick up o make.1: document .OBJDIR target 2015-05-05 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150505 Merge with NetBSD make, pick up o cond.c: be strict about lhs of comparison when evaluating .if but less so when called from variable expansion. o unit-tests/cond2.mk: test various error conditions 2015-05-04 Simon J. Gerraty * machine.sh (MACHINE): Add Bitrig patch from joerg@netbsd.org 2015-04-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150418 Merge with NetBSD make, pick up o job.c: use memmove() rather than memcpy() * unit-tests/varshell.mk: SunOS cannot handle the TERMINATED_BY_SIGNAL case, so skip it. 2015-04-11 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150411 bump version - only mk/ changes. 2015-04-10 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150410 Merge with NetBSD make, pick up o document different handling of '-' in jobs mode vs compat o fix jobs mode so that '-' only applies to whole job when shell lacks hasErrCtl o meta.c: use separate vars to track lcwd and latestdir (read) per process 2015-04-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20150401 Merge with NetBSD make, pick up o meta.c: close meta file in child * Makefile: use BINDIR.bmake if set. Same for MANDIR and SHAREDIR Handy for testing release candidates in various environments. 2015-03-26 Simon J. Gerraty * move initialization of savederr to block where it is used to avoid spurious warning from gcc5 2014-11-11 Simon J. Gerraty * Makefile (MAKE_VERSION): 20141111 just a cooler number 2014-11-05 Simon J. Gerraty * Makefile (MAKE_VERSION): 20141105 Merge with NetBSD make, pick up o revert major overhaul of suffix handling and POSIX compliance - too much breakage and impossible to make backwards compatible. o we still have the new unit test structure which is ok. o meta.c ensure "-- filemon" is at start of line. 2014-09-17 Simon J. Gerraty * configure.in: test that result of getconf PATH_MAX is numeric and discard if not. Apparently needed for Hurd. 2014-08-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140830 Merge with NetBSD make, pick up o major overhaul of suffix handling o improved POSIX compliance o overhauled unit-tests 2014-06-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140620 Merge with NetBSD make, pick up o var.c return varNoError rather than var_Error for ::= modifiers. 2014-05-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140522 Merge with NetBSD make, pick up o var.c detect some parse errors. 2014-04-05 Simon J. Gerraty * Fix spelling errors - patch from Pedro Giffuni 2014-02-14 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140214 Merge with NetBSD make, pick up o .INCLUDEFROM* o use Var_Value to get MAKEOBJDIR[PREFIX] o reduced realloc'ign in brk_string. * configure.in: add a check for compiler supporting __func__ 2014-01-03 Simon J. Gerraty * boot-strap: ignore mksrc=none 2014-01-02 Simon J. Gerraty * Makefile (DEFAULT_SYS_PATH?): use just ${prefix}/share/mk 2014-01-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20140101 * configure.in: set bmake_path_max to min(_SC_PATH_MAX,1024) * Makefile.config: defined BMAKE_PATH_MAX to bmake_path_max * make.h: use BMAKE_PATH_MAX if MAXPATHLEN not defined (needed for Hurd) * configure.in: Add AC_PREREQ and check for sysctl; patch from Andrew Shadura andrewsh at debian.org 2013-10-16 Simon J. Gerraty * Makefile (MAKE_VERSION): 20131010 * lose the const from arg to systcl to avoid problems on older BSDs. 2013-10-01 Simon J. Gerraty * Makefile (MAKE_VERSION): 20131001 Merge with NetBSD make, pick up o main.c: for NATIVE build sysctl to get MACHINE_ARCH from hw.machine_arch if necessary. o meta.c: meta_oodate - need to look at src of Link and target of Move as well. * main.c: check that CTL_HW and HW_MACHINE_ARCH exist. provide __arraycount() if needed. 2013-09-04 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130904 Merge with NetBSD make, pick up o Add VAR_INTERNAL context, so that internal setting of MAKEFILE does not override value set by makefiles. 2013-09-02 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130902 Merge with NetBSD make, pick up o CompatRunCommand: only apply shellErrFlag when errCheck is true 2013-08-28 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130828 Merge with NetBSD make, pick up o Fix VAR :sh = syntax from Will Andrews at freebsd.org o Call Job_SetPrefix() from Job_Init() so makefiles have opportunity to set .MAKE.JOB.PREFIX 2013-07-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130730 Merge with NetBSD make, pick up o Allow suppression of --- job -- tokens by setting .MAKE.JOB.PREFIX empty. 2013-07-16 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130716 Merge with NetBSD make, pick up o number of gmake compatibility tweaks -w for gmake style entering/leaving messages if .MAKE.LEVEL > 0 indicate it in progname "make[1]" etc. handle MAKEFLAGS containing only letters. o when overriding a GLOBAL variable on the command line, delete it from GLOBAL context so -V doesn't show the wrong value. 2013-07-06 Simon J. Gerraty * configure.in: We don't need MAKE_LEVEL_SAFE anymore. * Makefile (MAKE_VERSION): 20130706 Merge with NetBSD make, pick up o Shell_Init(): export shellErrFlag if commandShell hasErrCtl is true so that CompatRunCommand() can use it, to ensure consistent behavior with jobs mode. o use MAKE_LEVEL_ENV to define the variable to propagate .MAKE.LEVEL - currently set to MAKELEVEL (same as gmake). o meta.c: use .MAKE.META.IGNORE_PATHS to allow customization of paths to ignore. 2013-06-04 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130604 Merge with NetBSD make, pick up o job.c: JobCreatePipe: do fcntl() after any tweaking of fd's to avoid leaking descriptors. 2013-05-28 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130528 Merge with NetBSD make, pick up o var.c: cleanup some left-overs in VarHash() 2013-05-20 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130520 generate manifest from component FILES rather than have to update FILES when mk/FILES changes. 2013-05-18 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130518 Merge with NetBSD make, pick up o suff.c: don't skip all processsing for .PHONY targets else wildcard srcs do not get expanded. o var.c: expand name of variable to delete if necessary. 2013-03-30 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130330 Merge with NetBSD make, pick up o meta.c: refine the handling of .OODATE in commands. Rather than suppress command comparison for the entire script as though .NOMETA_CMP had been used, only suppress it for the one command line. This allows something like ${.OODATE:M.NOMETA_CMP} to be used to suppress comparison of a command without otherwise affecting it. o make.1: document that 2013-03-22 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130321 yes, not quite right but its a cooler number. Merge with NetBSD make, pick up o parse.c: fix ParseGmakeExport to be portable and add a unit-test. * meta.c: call meta_init() before makefiles are read and if built with filemon support set .MAKE.PATH_FILEMON to _PATH_FILEMON this let's makefiles test for support. Call meta_mode_init() to process .MAKE.MODE. 2013-03-13 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130305 Merge with NetBSD make, pick up o run .STALE: target when a dependency from .depend is missing. o job.c: add Job_RunTarget() for the above and .BEGIN 2013-03-03 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130303 Merge with NetBSD make, pick up o main.c: set .MAKE.OS to utsname.sysname o job.c: more checks for read and poll errors o var.c: lose VarChangeCase() saves 4% time 2013-03-02 Simon J. Gerraty * boot-strap: remove MAKEOBJDIRPREFIX from environment since we want to use MAKEOBJDIR 2013-01-27 Simon J. Gerraty * Merge with NetBSD make, pick up o make.1: more info on how shell commands are handled. o job.c,main.c: detect write errors to job pipes. 2013-01-25 Simon J. Gerraty * Makefile (MAKE_VERSION): 20130123 Merge with NetBSD make, pick up o meta.c: if script uses .OODATE and meta_oodate() decides rebuild is needed, .OODATE will be empty - set it to .ALLSRC. o var.c: in debug output indicate which variabale modifiers apply to. o remove Check_Cwd logic the makefiles have been fixed. 2012-12-12 Simon J. Gerraty * makefile.in: add a simple makefile for folk who insist on ./configure; make; make install it just runs boot-strap * include mk/* to accommodate the above * boot-strap: re-work to accommodate the above mksrc defaults to $Mydir/mk allow op={configure,build,install,clean,all} add options to facilitate install * Makefile.config.in: just the bits set by configure * Makefile: bump version to 20121212 abandon Makefile.in (NetBSD Makefile) leverage mk/* instead * configure.in: ensure srcdir is absolute 2012-11-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121111 fix generation of bmake.cat1 2012-11-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121109 Merge with NetBSD make, pick up o make.c: MakeBuildChild: return 0 so search continues if a .ORDER dependency is detected. o unit-tests/order: test the above 2012-11-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121102 Merge with NetBSD make, pick up o cond.c: allow cond_state[] to grow. In meta mode with a very large tree, we can hit the limit while processing dirdeps. 2012-10-25 Simon J. Gerraty * Makefile.in: we need to use ${srcdir} not ${.CURDIR} 2012-10-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121010 o protect syntax that only bmake parses correctly. o remove auto setting of FORCE_MACHINE, use configure's --with-force-machine=whatever if that is desired. 2012-10-08 Simon J. Gerraty * Makefile.in: do not lose history from make.1 when generating bmake.1 2012-10-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20121007 Merge with NetBSD make, pick up o compat.c: ignore empty commands - same as jobs mode. o make.1: document meta chars that cause use of shell 2012-09-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120911 * bsd.after-import.mk: include Makefile.inc early and allow it to override PROG 2012-08-31 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120831 Merge with NetBSD make, pick up o cast sizeof() to int for comparison o minor make.1 tweak 2012-08-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120830 Merge with NetBSD make, pick up o .MAKE.EXPAND_VARIABLES knob can control default behavior of -V o debug flag -dV causes -V to show raw value regardless. 2012-07-05 Simon J. Gerraty * bsd.after-import.mk (after-import): ensure unit-tests/Makefile gets SRCTOP set. 2012-07-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120704 Merge with NetBSD make, pick up o Job_ParseShell should call Shell_Init if it has been previously called. * Makefile.in: set USE_META based on configure result. also .PARSEDIR is safer indicator of bmake. 2012-06-26 Simon J. Gerraty * Makefile.in: bump version to 20120626 ensure CPPFLAGS is in CFLAGS * meta.c: avoid nested externs * bsd.after-import.mk: avoid ${.CURDIR}/Makefile as target 2012-06-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120620 Merge with NetBSD make, pick up o make_malloc.c: avoid including make_malloc.h again * Makefile.in: avoid bmake only syntax or protect with .if defined(.MAKE.LEVEL) * bsd.after-import.mk: replace .-include with .sinclude ensure? SRCTOP gets a value * configure.in: look for filemon.h in /usr/include/dev/filemon first. 2012-06-19 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120612 Merge with NetBSD make, pick up o use MAKE_ATTR_* rather than those defined by cdefs.h or compiler for greater portability. o unit-tests/forloop: check that .for works as expected wrt number of times and with "quoted strings". 2012-06-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120606 Merge with NetBSD make, pick up o compat.c: use kill(2) rather than raise(3). * configure.in: look for sys/dev/filemon * bsd.after-import.mk: add a .-include "Makefile.inc" to Makefile and pass BOOTSTRAP_XTRAS to boot-strap. 2012-06-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120604 Merge with NetBSD make, pick up o util.c and var.c share same var for tracking if environ has been reallocated. o util.c provide getenv with setenv. * Add MAKE_LEVEL_SAFE as an alternate means of passing MAKE_LEVEL when the shell actively strips .MAKE.* from the environment. We still refer to the variable always as .MAKE.LEVEL * util.c fix bug in findenv() was finding prefix of name. * compat.c: re-raising SIGINT etc after running .INTERRUPT results in more reliable termination of all activity on many platforms. 2012-06-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120602 Merge with NetBSD make, pick up o for.c: handle quoted items in .for list 2012-05-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120530 Merge with NetBSD make, pick up o compat.c: ignore empty command. 2012-05-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120524 * FILES: add bsd.after-import.mk: A simple means of integrating bmake into a BSD build system. 2012-05-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120520 Merge with NetBSD make, pick up o increased limit for nested conditionals. 2012-05-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120518 Merge with NetBSD make, pick up o use _exit(2) in signal hanlder o Don't use the [dir] cache when building nodes that might have changed since the last exec. o Avoid nested extern declaration warnings. 2012-04-27 Simon J. Gerraty * meta.c (fgetLine): avoid %z - not portable. * parse.c: Since we moved include of sys/mman.h and def's of MAP_COPY etc. we got dups from a merge. 2012-04-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120420 Merge with NetBSD make, pick up o restore duplicate supression in .MAKE.MAKEFILES runtime saving can be significant. o Var_Subst() uses Buf_DestroyCompact() to reduce memory consumption up to 20%. 2012-04-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120420 Merge with NetBSD make, pick up o remove duplicate supression in .MAKE.MAKEFILES o improved dir cache behavior o gmake'ish export command 2012-03-25 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20120325 Merge with NetBSD make, pick up o fix parsing of :[#] in conditionals. 2012-02-10 Simon J. Gerraty * Makefile.in: replace use of .Nx in bmake.1 with NetBSD since some systems cannot cope with .Nx 2011-11-14 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111111 Merge with NetBSD make, pick up o debug output for .PARSEDIR and .PARSEFILE 2011-10-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111010 2011-10-09 Simon J. Gerraty * boot-strap: check for an expected file in the dirs we look for. * make-bootstrap.sh: pass on LDSTATIC 2011-10-01 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20111001 Merge with NetBSD make, pick up o ensure .PREFIX is set for .PHONY and .TARGET set for .PHONY run via .END o __dead used consistently 2011-09-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): 20110909 is a better number ;-) 2011-09-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110905 Merge with NetBSD make, pick up o meta_oodate: ignore makeDependfile 2011-08-28 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110828 Merge with NetBSD make, pick up o silent=yes in .MAKE.MODE causes meta mode to mark targets as SILENT if a .meta file is created 2011-08-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110818 Merge with NetBSD make, pick up o in meta mode, if target flagged .META a missing .meta file means target is out-of-date o fixes for gcc 4.5 warnings o simplify job printing code 2011-08-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110808 Merge with NetBSD make, pick up o do not touch OP_SPECIAL targets when doing make -t 2011-06-22 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110622 Merge with NetBSD make, pick up o meta_oodate detect corrupted .meta file and declare oodate. * configure.in: add check for setsid 2011-06-07 Simon J. Gerraty * Merge with NetBSD make, pick up o unit-tests/modts now works on MirBSD 2011-06-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110606 Merge with NetBSD make, pick up o ApplyModifiers: when we parse a variable which is not the entire modifier string, or not followed by ':', do not consider it as containing modifiers. o loadfile: ensure newline at end of mapped file. 2011-05-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110505 Merge with NetBSD make, pick up o .MAKE.META.BAILIWICK - list of prefixes which define the scope of make's control. In meta mode, any generated file within said bailiwick, which is found to be missing, causes current target to be out-of-date. 2011-04-11 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110411 Merge with NetBSD make, pick up o when long modifiers fail to match, check sysV style. - add a test case 2011-04-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110410 Merge with NetBSD make, pick up o :hash - cheap 32bit hash of value o :localtime, :gmtime - use value as format string for strftime. 2011-03-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110330 mostly because its a cooler version. Merge with NetBSD make, pick up o NetBSD tags for meta.[ch] o job.c call meta_job_finish() after meta_job_error(). o meta_job_error() should call meta_job_finish() to ensure .meta file is closed, and safe to copy - if .ERROR target wants. meta_job_finish() is safe to call repeatedly. 2011-03-29 Simon J. Gerraty * unit-tests/modts: use printf if it is a builtin, to save us from MirBSD * Makefile.in (MAKE_VERSION): bump version to 20110329 Merge with NetBSD make, pick up o fix for use after free() in CondDoExists(). o meta_oodate() report extra commands and return earlier. 2011-03-27 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110327 Merge with NetBSD make, pick up o meta.c, if .MAKE.MODE contains curdirOk=yes allow creating .meta files in .CURDIR * boot-strap (TOOL_DIFF): aparently at least on linux distro formats the output of 'type' differently - so eat any "()" 2011-03-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110306 Merge with NetBSD make, pick up o meta.c, only do getcwd() once 2011-03-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110305 Merge with NetBSD make, pick up o correct sysV substitution handling of empty lhs and variable o correct exists() check for dir with trailing / o correct handling of modifiers for non-existant variables during evaluation of conditionals. o ensure MAP_FILE is defined. o meta.c use curdir[] now exported by main.c 2011-02-25 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110225 Merge with NetBSD make, pick up o fix for incorrect .PARSEDIR when .OBJDIR is re-computed after makefiles have been read. o fix example of :? modifier in man page. 2011-02-13 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110214 Merge with NetBSD make, pick up o meta.c handle realpath() failing when generating meta file name. * sigcompat.c: convert to ansi so we can use higher warning levels. 2011-02-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110207 Merge with NetBSD make, pick up o fix for bug in meta mode. 2011-01-03 Simon J. Gerraty * parse.c: SunOS 5.8 at least does not have MAP_FILE 2011-01-01 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20110101 Merge with NetBSD make, pick up o use mmap(2) if available, for reading makefiles 2010-12-15 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101215 Merge with NetBSD make, pick up o ensure meta_job_error() does not report a previous .meta file as being culprit. 2010-12-10 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101210 Merge with NetBSD make, pick up o meta_oodate: track cwd per process, and only consider target out-of-date if missing file is outside make's CWD. Ignore files in /tmp/ etc. o to ensure unit-tests results match, need to control LC_ALL as well as LANG. o fix for parsing bug in var.c 2010-11-26 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101126 Merge with NetBSD make, pick up o if stale dependency is an IMPSRC, search via .PATH o meta_oodate: if a referenced file is missing, target is out-of-date. o meta_oodate: if a target uses .OODATE in its commands, it (.OODATE) needs to be recomputed. o keep a pointer to youngest child node, rather than just its mtime. 2010-11-02 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20101101 2010-10-16 Simon J. Gerraty * machine.sh: like os.sh, allow for uname -p producing useless drivel 2010-09-13 Simon J. Gerraty * boot-strap: document configure knobs for meta and filemon. * Makefile.in (MAKE_VERSION): bump version to 20100911 Merge with NetBSD make, pick up o meta.c - meta mode * make-bootstrap.sh.in: handle meta.c * configure.in: add knobs for use_meta and filemon_h also, look for dirname, str[e]sep and strlcpy * util.c: add simple err[x] and warn[x] 2010-08-08 Simon J. Gerraty * boot-strap (TOOL_DIFF): set this to ensure tests use the same version of diff that configure tested * Makefile.in (MAKE_VERSION): bump version to 20100808 Merge with NetBSD make, pick up o in jobs mode, when we discover we cannot make something, call PrintOnError before exit. 2010-08-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100806 Merge with NetBSD make, pick up o formatting fixes for ignored errors o ensure jobs are cleaned up regardless of where wait() was called. 2010-06-28 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100618 * os.sh (MACHINE_ARCH): watch out for drivel from uname -p 2010-06-16 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100616 Merge with NetBSD make, pick up o man page update o call PrintOnError from JobFinish when we detect an error we are not ignoring. 2010-06-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100606 Merge with NetBSD make, pick up o man page update 2010-06-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100605 Merge with NetBSD make, pick up o use bmake_signal() which is a wrapper around sigaction() in place of signal() o add .export-env to allow exporting variables to environment without tracking (so no re-export when the internal value is changed). 2010-05-24 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100524 Merge with NetBSD make, pick up o fix for .info et al being greedy. 2010-05-23 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100520 Merge with NetBSD make, pick up o back to using realpath on argv[0] but only if contains '/' and does not start with '/'. 2010-05-10 Simon J. Gerraty * boot-strap: use absolute path for bmake when running tests. * Makefile.in (MAKE_VERSION): bump version to 20100510 Merge with NetBSD make, pick up o revert use of realpath on argv[0] too many corner cases. o print MAKE_PRINT_VAR_ON_ERROR before running .ERROR target. 2010-05-05 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100505 Merge with NetBSD make, pick up o fix for missed SIGCHLD when compiled with SunPRO actually for bmake, defining FORCE_POSIX_SIGNALS would have done the job. 2010-04-30 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100430 Merge with NetBSD make, pick up o fflush stdout before writing to stdout 2010-04-23 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100423 Merge with NetBSD make, pick up o updated unit tests for Haiku (this time for sure). * boot-strap: based on patch from joerg honor --with-default-sys-path better. * boot-strap: remove mention of --with-prefix-sys-path 2010-04-22 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100422 * Merge with NetBSD make, pick up o fix for vfork() on Darwin. o fix for bogus $TMPDIR. o set .MAKE.MODE=compat for -B o set .MAKE.JOBS=max_jobs for -j max_jobs o allow unit-tests to run without any *.mk o unit-tests/modmisc be more conservative in dirs presumed to exist. * boot-strap: ignore /usr/share/mk except on NetBSD. * unit-tests/Makefile.in: set LANG=C when running unit-tests to ensure sort(1) behaves as expected. 2010-04-21 Simon J. Gerraty * boot-strap: add FindHereOrAbove so we can use -m .../mk 2010-04-20 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100420 * Merge with NetBSD make, pick up o fix for variable realpath() behavior. we have to stat(2) the result to be sure. o fix for .export (all) when nested vars use :sh 2010-04-14 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100414 * Merge with NetBSD make, pick up o use realpath to resolve argv[0] (for .MAKE) if needed. o add realpath from libc. o add :tA to resolve variable via realpath(3) if possible. 2010-04-08 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100408 * Merge with NetBSD make, pick up o unit tests for .ERROR, .error o fix for .ERROR to ensure it cannot be default target. 2010-04-06 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100406 * Merge with NetBSD make, pick up o fix for compat mode "Error code" going to debug_file. o fix for .ALLSRC being populated twice. o support for .info, .warning and .error directives o .MAKE.MODE to control make's operational mode o .MAKE.MAKEFILE_PREFERENCE to control the preferred makefile name(s). o .MAKE.DEPENDFILE to control the name of the depend file o .ERROR target - run on failure. 2010-03-18 Simon J. Gerraty * make-bootstrap.sh.in: extract MAKE_VERSION from Makefile * os.sh,arch.c: patch for Haiku from joerg at netbsd 2010-03-17 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100222 * Merge with NetBSD make, pick up o better error msg for .for with mutiple inter vars * boot-strap: o use make-bootstrap.sh from joerg at netbsd to avoid the need for a native make when bootstrapping. o add "" everywhere ;-) o if /usr/share/tmac/andoc.tmac exists install nroff bmake.1 otherwise the pre-formated version. 2010-01-04 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20100102 * Merge with NetBSD make, pick up: o fix for -m .../ 2009-11-18 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20091118 * Merge with NetBSD make, pick up: o .unexport o report lines that start with '.' and should have ':' (catch typo's of .el*if). 2009-10-30 Simon J. Gerraty * configure.in: Ensure that srcdir and mksrc are absolute paths. 2009-10-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): fix version to 20091007 2009-10-07 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 200910007 * Merge with NetBSD make, pick up: o fix for parsing of :S;...;...; applied to .for loop iterator appearing in a dependency line. 2009-09-09 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090909 * Merge with NetBSD make, pick up: o fix for -C, .CURDIR and .OBJDIR * boot-strap: o allow share_dir to be set independent of prefix. o select default share_dir better when prefix ends in $HOST_TARGET o if FORCE_BSD_MK etc were set, include them in the suggested install-mk command. 2009-09-08 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090908 * Merge with NetBSD make, pick up: o .MAKE.LEVEL for recursion tracking o fix for :M scanning \: 2009-09-03 Simon J. Gerraty * configure.in: Don't -D__EXTENSIONS__ if AC_USE_SYSTEM_EXTENSIONS says "no". 2009-08-26 Simon J. Gerraty * Makefile.in (MAKE_VERSION): bump version to 20090826 Simplify MAKE_VERSION to just the bare date. * Merge with NetBSD make, pick up: o -C directory support. o support for SIGINFO o use $TMPDIR for temp files. o child of vfork should be careful about modifying parent's state. 2009-03-26 Simon J. Gerraty * Appy some patches for MiNT from David Brownlee 2009-02-26 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20090222 * Merge with NetBSD make, pick up: o Possible null pointer de-ref in Var_Set. 2009-02-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20090204 * Merge with NetBSD make, pick up: o bmake_malloc et al moved to their own .c o Count both () and {} when looking for the end of a :M pattern o Change 'Buffer' so that it is the actual struct, not a pointer to it. o strlist.c - functions for processing extendable arrays of pointers to strings. o ClientData replaced with void *, so const void * can be used. o New debug flag C for DEBUG_CWD 2008-11-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081111 Apply patch from Joerg Sonnenberge to configure.in: o remove some redundant checks o check for emlloc etc only in libutil and require the whole family. util.c: o remove [v]asprintf which is no longer used. 2008-11-04 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081101 * Merge with NetBSD make, pick up: o util.c: avoid use of putenv() - christos 2008-10-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20081030 pick up man page tweaks. 2008-10-29 Simon J. Gerraty * Makefile.in: move processing of LIBOBJS to after is definition! thus we'll have getenv.c in SRCS only if needed. * make.1: add examples of how to use :? * Makefile.in (BMAKE_VERSION): bump version to 20081029 * Merge with NetBSD make, pick up: o fix for .END processing with -j o segfault from Parse_Error when no makefile is open o handle numeric expressions in any variable expansion o debug output now defaults to stderr, -dF to change it - apb o make now uses bmake_malloc etc so that it can build natively on A/UX - wasn't an issue for bmake, but we want to keep in sync. 2008-09-27 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080808 * Merge with NetBSD make, pick up: o fix for PR/38840: Pierre Pronchery: make crashes while parsing long lines in Makefiles o optimizations for VarQuote by joerg o fix for PR/38756: dominik: make dumps core on invalid makefile 2008-05-15 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080515 * Merge with NetBSD make, pick up: o fix skip setting vars in VAR_GLOBAL context, to handle cases where VAR_CMD is used for other than command line vars. 2008-05-14 Simon J. Gerraty * boot-strap (make_version): we may need to look in $prefix/share/mk for sys.mk * Makefile.in (BMAKE_VERSION): bump version to 20080514 * Merge with NetBSD make, pick up: o skip setting vars in VAR_GLOBAL context, when already set in VAR_CMD which takes precedence. 2008-03-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080330 * Merge with NetBSD make, pick up: o fix for ?= when LHS contains variable reference. 2008-02-15 Simon J. Gerraty * merge some patches from NetBSD pkgsrc. * makefile.boot.in (BOOTSTRAP_SYS_PATH): Allow better control of the MAKSYSPATH used during bootstrap. * Makefile.in (BMAKE_VERSION): bump version to 20080215 * Merge with NetBSD make, pick up: o warn if non-space chars follow 'empty' in a conditional. 2008-01-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20080118 * Merge with NetBSD make, pick up: o consider dependencies read from .depend as optional - dsl o remember when buffer for reading makefile grows - dsl o add -dl (aka LOUD) - David O'Brien 2007-10-22 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071022 * Merge with NetBSD make, pick up: o Allow .PATH to be used for .include "" * boot-strap: source default settings from .bmake-boot-strap.rc 2007-10-16 Simon J. Gerraty * Makefile.in: fix maninstall on various systems provided that our man.mk is used. For non-BSD systems we install the preformatted page into $MANDIR/cat1 2007-10-15 Simon J. Gerraty * boot-strap: make bmake.1 too, so maninstall works. 2007-10-14 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071014 * Merge with NetBSD make, pick up: o revamped handling of defshell - configure no longer needs to know the content of the shells array - apb o stop Var_Subst modifying its input - apb o avoid calling ParseTrackInput too often - dsl 2007-10-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20071011 * Merge with NetBSD make, pick up: o fix Shell_Init for case that _BASENAME_DEFSHELL is absolute path. * sigcompat.c: some tweaks for HP-UX 11.x based on patch from Tobias Nygren * configure.in: update handling of --with-defshell to match new make behavior. --with-defshell=/usr/xpg4/bin/sh will now do what one might hope - provided the chosen shell behaves enough like sh. 2007-10-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20071008 * Merge with NetBSD make, pick up: o .MAKE.JOB.PREFIX - control the token output before jobs - sjg o .export/.MAKE.EXPORTED - export of variables - sjg o .MAKE.MAKEFILES - track all makefiles read - sjg o performance improvements - dsl o revamp parallel job scheduling - dsl 2006-07-28 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060728 * Merge with NetBSD make, pick up: o extra debug info during variable and cond processing - sjg o shell definition now covers newline - rillig o minor mem leak in PrintOnError - sjg 2006-05-11 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060511 * Merge with NetBSD make, pick up: o more memory leaks - coverity o possible overflow in ArchFindMember - coverity o extract variable modifier code out of Var_Parse() so it can be called recursively - sjg o unit-tests/moderrs - sjg 2006-04-12 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060412 * Merge with NetBSD make, pick up: o fixes for some memory leaks - coverity o only read first sys.mk etc when searching sysIncPath - sjg * main.c (ReadMakefile): remove hack for __INTERIX that prevented setting ${MAKEFILE} - OBATA Akio 2006-03-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060318 * Merge with NetBSD make, pick up: o cleanup of job.c to remove remote handling, distcc is more useful and this code was likely bit-rotting - dsl o fix for :P modifier - sjg * boot-strap: set default prefix to something reasonable (for me anyway). 2006-03-01 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060301 * Merge with NetBSD make, pick up: o make .WAIT apply recursively, document and test case - apb o allow variable modifiers in a variable appear anywhere in modifier list, document and test case - sjg 2006-02-22 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20060222 * Merge with NetBSD make, pick up: o improved job token handling - dsl o SIG_DFL the correct signal before exec - dsl o more debug info during parsing - dsl o allow variable modifiers to be specified via variable - sjg * boot-strap: explain why we died if no mksrc 2005-11-05 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051105 * configure.in: always set default_sys_path default is ${prefix}/share/mk - remove prefix_sys_path, anyone wanting more than above needs to set it manually. 2005-11-04 Simon J. Gerraty * boot-strap: make this a bit easier for pkgsrc folk. bootstrap still fails on IRIX64 since MACHINE_ARCH gets set to 'mips' while pkgsrc wants 'mipseb' or 'mipsel' 2005-11-02 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051102 * job.c (JobFinish): fix likely ancient merge lossage fix from Todd Vierling. * boot-strap (srcdir): allow setting mksrc=none 2005-10-31 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051031 * ranlib.h: skip on OSF too. (NetBSD PR 31864) 2005-10-10 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051002 fix a silly typo 2005-10-09 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20051001 support for UnixWare and some other systems, based on patches from pkgsrc/bootstrap 2005-09-03 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050901 * Merge with NetBSD make, pick up: o possible parse error causing us to wander off. 2005-06-06 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050606 * Merge with NetBSD make, pick up: o :0x modifier for randomizing a list o fixes for a number of -Wuninitialized issues. 2005-05-30 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050530 * Merge with NetBSD make, pick up: o Handle dependencies for .BEGIN, .END and .INTERRUPT * README: was seriously out of date. 2005-03-22 Simon J. Gerraty * Important to use .MAKE rather than MAKE. 2005-03-15 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20050315 * Merge with NetBSD make, pick up: o don't mistake .elsefoo for .else o use suffix-specific search path correctly o bunch of style nits 2004-05-11 Simon J. Gerraty * boot-strap: o ensure that args to --src and --with-mksrc are resolved before giving them to configure. o add -o "objdir" so that builder can control it, default is $OS as determined by os.sh o add -q to suppress all the install instructions. 2004-05-08 Simon J. Gerraty * Remove __IDSTRING() * Makefile.in (BMAKE_VERSION): bump to 20040508 * Merge with NetBSD make, pick up: o posix fixes - remove '-e' from compat mode - add support for '+' command-line prefix. o fix for handling '--' on command-line. o fix include in lst.lib/lstInt.h to simplify '-I's o we also picked up replacement of MAKE_BOOTSTRAP with !MAKE_NATIVE which is a noop, but possibly confusing. 2004-04-14 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040414 * Merge with NetBSD make, pick up: o allow quoted strings on lhs of conditionals o issue warning when extra .else is seen o print line numer when errors encountered during parsing from string. 2004-02-20 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040220 * Merge with NetBSD make, pick up: o fix for old :M parsing bug. o re-jigged unit-tests 2004-02-15 Simon J. Gerraty * Makefile.in (accept test): use ${.MAKE:S,^./,${.CURDIR}/,} so that './bmake -f Makefile test' works. 2004-02-14 Simon J. Gerraty * Makefile.in: (BMAKE_VERSION): bump to 20040214 * Merge with NetBSD make, pick up: o search upwards for *.mk o fix for double free of var substitution buffers o use of getopt replaced with custom code, since the usage (re-scanning) isn't posix compatible. 2004-02-12 Simon J. Gerraty * arch.c: don't include ranlib.h on ELF systems (thanks to Chuck Cranor ). 2004-01-18 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump to 20040118 * boot-strap (while): export vars we assign to on cmdline * unit-test/Makefile.in: ternary is .PHONY 2004-01-08 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20040108 * Merge with NetBSD make, pick up: o fix for ternary modifier 2004-01-06 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20040105 * Merge with NetBSD make, pick up: o fix for cond.c to handle compound expressions better o variable expansion within sysV style replacements 2003-12-22 Simon J. Gerraty * Make portable snprintf safer - output to /dev/null first to check space needed. * Makefile.in (BMAKE_VERSION): bump version to 20031222 * Merge with NetBSD make, pick up: o -dg3 to show input graph when things go wrong. o explicitly look for makefiles in objdir if not found in curdir so that errors in .depend etc will be reported accurarely. o avoid use of -e in shell scripts in jobs mode, use '|| exit $?' instead as it more accurately reflects the expected behavior and is more consistently implemented. o avoid use of asprintf. 2003-09-28 Simon J. Gerraty * util.c: Add asprintf and vasprintf. * Makefile.in (BMAKE_VERSION): bump version to 20030928 * Merge with NetBSD make, pick up: :[] modifier - allows picking words from a variable. :tW modifier - allows treating value as one big word. W flag for :C and :S - allows treating value as one big word. 2003-09-12 Simon J. Gerraty * Merge with NetBSD make pick up -de flag to enable printing failed command. don't skip 1st two dir entries (normally . and ..) since coda does not have them. 2003-09-09 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20030909 * Merge with NetBSD make, pick up: - changes for -V '${VAR}' to print fully expanded value cf. -V VAR - CompatRunCommand now prints the command that failed. - several files got updated 3 clause Berkeley license. 2003-08-02 Simon J. Gerraty * boot-strap: Allow setting configure args on command line. 2003-07-31 Simon J. Gerraty * configure.in: add --with-defshell to allow sh or ksh to be selected as default shell. * Makefile.in: bump version to 20030731 * Merge with NetBSD make Pick up .SHELL spec for ksh and associate man page changes. Also compat mode now uses the same shell specs. 2003-07-29 Simon J. Gerraty * var.c (Var_Parse): ensure delim is initialized. * unit-tests/Makefile.in: use single quotes to avoid problems from some shells. * makefile.boot.in: Run the unit-tests as part of the bootstrap procedure. 2003-07-28 Simon J. Gerraty * unit-tests/Makefile.in: always force complaints from ${TEST_MAKE} to be from 'make'. * configure.in: add check for 'diff -u' also fix some old autoconf'isms * Makefile.in (BMAKE_VERSION): bump version to 20030728. if using GCC add -Wno-cast-qual to CFLAGS for var.o * Merge with NetBSD make Pick up fix for :ts parsing error in some cases. Pick unit-tests. 2003-07-23 Simon J. Gerraty * Makefile.in (BMAKE_VERSION): bump version to 20030723. * var.c (Var_Parse): fix bug in :ts modifier, after const correctness fixes, must pass nstr to VarModify. 2003-07-14 Simon J. Gerraty * Makefile.in: BMAKE_VERSION switch to a date based version. We'll generally use the date of last import from NetBSD. * Merge with NetBSD make Pick up fixes for const-correctness, now passes WARNS=3 on NetBSD. Pick up :ts modifier, allows controlling the separator used between words in variable expansion. 2003-07-11 Simon J. Gerraty * FILES: include boot-strap and os.sh * Makefile.in: only set WARNS if we are NetBSD, the effect on FreeBSD is known to be bad. * makefile.boot.in (bootstrap): make this the default target. * Makefile.in: bump version to 3.1.19 * machine.sh: avoid A-Z with tr as it is bound to lose. 2003-07-10 Simon J. Gerraty * Merge with NetBSD make Pick up fix for PR/19781 - unhelpful error msg on unclosed ${var:foo Plus some doc fixes. 2003-04-27 Simon J. Gerraty * Merge with NetBSD make Pick up fix for PR/1523 - don't count a library as built, if there is no way to build it * Bump version to 3.1.18 2003-03-23 Simon J. Gerraty * Merge with NetBSD make Pick up fix for ParseDoSpecialSrc - we only use it if .WAIT appears in src list. 2003-03-21 Simon J. Gerraty * Merge with NetBSD make (mmm 10th anniversary!) pick up fix for .WAIT in srcs that refer to $@ or $* (PR#20828) pick up -X which tells us to not export VAR=val via setenv if we are already doing so via MAKEFLAGS. This saves valuable env space on systems like Darwin. set MAKE_VERSION to 3.1.17 * parse.c: pix up fix for suffix rules 2003-03-06 Simon J. Gerraty * Merge with NetBSD make. pick up fix for propagating -B via MAKEFLAGS. set MAKE_VERSION to 3.1.16 * Apply some patches from pkgsrc-bootstrap/bmake Originally by Grant Beattie I may have missed some - since they are based on bmake-3.1.12 2002-12-03 Simon J. Gerraty * makefile.boot.in (bmake): update install targets for those that use them, also clear MAKEFLAGS when invoking bmake.boot to avoid havoc from gmake -w. Thanks to Harlan Stenn . * bmake.cat1: update the pre-formatted man page! 2002-11-30 Simon J. Gerraty * Merge with NetBSD make. pick up fix for premature free of pointer used in call to Dir_InitCur(). set MAKE_VERSION to 3.1.15 2002-11-26 Simon J. Gerraty * configure.in: determine suitable value for MKSRC. override using --with-mksrc=PATH. * machine.sh: use `uname -p` for MACHINE_ARCH on modern SunOS systems. configs(8) will use 'sun4' as an alias for 'sparc'. 2002-11-25 Simon J. Gerraty * Merge with NetBSD make. pick up ${.PATH} pick up fix for finding ../cat.c via .PATH when .CURDIR=.. set MAKE_VERSION to 3.1.14 add configure checks for killpg and sys/socket.h 2002-09-16 Simon J. Gerraty * tag bmake-3-1-13 * makefile.boot.in (bmake): use install-mk Also setup ./mk before trying to invoke bmake.boot incase we needed install-mk to create a sys.mk for us. * configure.in: If we need to add -I${srcdir}/missing, make it an absolute path so that it works for lst.lib too. * make.h: always include sys/cdefs.h since we provide one if the host does not. * Makefile.in (install-mk): use MKSRC/install-mk which will do the right thing. use uname -p for ARCH if possible. since install-mk will setup links bsd.prog.mk -> prog.mk if needed, just .include bsd.prog.mk * Merge with NetBSD make (NetBSD-1.6) Code is ansi-C only now. Bug in handling of dotLast is fixed. Can now assign .OBJDIR and make will reset its notions of life. New modifiers :tu :tl for toUpper and toLower. Tue Oct 16 12:18:42 2001 Simon J. Gerraty * Merge with NetBSD make pick up fix for .END failure in compat mode. pick up fix for extra va_end() in ParseVErrorInternal. Thu Oct 11 13:20:06 2001 Simon J. Gerraty * configure.in: for systems that have sys/cdefs.h check if it is compatible. If not, include the one under missing, but tell it to include the native one too - necessary on Linux. * missing/sys/cdefs.h: if NEED_HOST_CDEFS_H is defined, use include_next (for gcc) to get the native sys/cdefs.h Tue Aug 21 02:29:34 2001 Simon J. Gerraty * job.c (JobFinish): Fix an earlier merge bug that resulted in leaking descriptors when using -jN. * job.c (JobPrintCommand): See if "curdir" exists before attempting to chdir(). Doing the chdir directly in make (when in compat mode) fails silently, so let the -jN version do the same. This can happen when building kernels in an object tree and playing clever games to reset .CURDIR. * Merged with NetBSD make pick up .USEBEFORE Tue Jun 26 23:45:11 2001 Simon J. Gerraty * makefile.boot.in: Give bmake.boot a MAKESYSPATH that might work. Tue Jun 12 16:48:57 2001 Simon J. Gerraty * var.c (Var_Set): Add 4th (flags) arg so VarLoopExpand can tell us not to export the iterator variable when using VAR_CMD context. Sun Jun 10 21:55:21 2001 Simon J. Gerraty * job.c (Job_CatchChildren): don't call Job_CatchOutput() here, its the wrong "fix". Sat Jun 9 00:11:24 2001 Simon J. Gerraty * Redesigned export of VAR_CMD's via MAKEFLAGS. We now simply append the variable names to .MAKEOVERRIDES, and handle duplicate suppression and quoting in ExportMAKEFLAGS using: ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@} Apart from fixing quoting bugs in previous version, this allows us to export vars to the environment by simply doing: .MAKEOVERRIDES+= PATH Merged again with NetBSD make, but the above is the only change. * configure.in: added --disable-pwd-override disable $PWD overriding getcwd() --disable-check-make-chdir disable make trying to guess when it should automatically cd ${.CURDIR} * Merge with NetBSD make, changes include: parse.c (ParseDoDependency): Spot that the syntax error is caused by an unresolved cvs/rcs conflict and say so. var.c: most of Var* functions now take a ctxt as 1st arg. now does variable substituion on rhs of sysv style modifiers. * var.c (Var_Set): exporting of command line variables (VAR_CMD) is now done here. We append the name='value' to .MAKEOVERRIDES rather than directly into MAKEFLAGS as this allows a Makefile to use .MAKEOVERRIDES= to disable this behaviour. GNU make uses a very similar mechanism. Note that in adding name='value' to .MAKEOVERRIDES we do the moral equivalent of: .MAKEOVERRIDES:= ${.MAKEOVERRIDES:Nname=*} name='val' Fri Jun 1 14:08:02 2001 Simon J. Gerraty * make-conf.h (USE_IOVEC): make it conditional on HAVE_SYS_UIO_H * Merged with NetBSD make make -dx can now be used to run commands via sh -x better error messages on exec failures. Thu May 31 01:44:54 2001 Simon J. Gerraty * Makefile.in (main.o): depends on ${SRCS} ${MAKEFILE} so that MAKE_VERSION gets updated. Also don't use ?= for MAKE_VERSION, MACHINE etc otherwise they propagate from the previous bmake. * configure.in (machine): allow --with-machine=generic to make configure use machine.sh to set MACHINE. * job.c (JobInterrupt): convert to using WAIT_T and friends. * Makefile.in: mention in bmake.1 that we use autoconf. * make.1: mention MAKE_PRINT_VAR_ON_ERROR. Wed May 30 23:17:18 2001 Simon J. Gerraty * main.c (ReadMakefile): don't set MAKEFILE if reading ".depend" as that rather defeats the usefulness of ${MAKEFILE}. * main.c (MainParseArgs): append command line variable assignments to MAKEFLAGS so that they get propagated to child make's. Apparently this is required POSIX behaviour? Its useful anyway. Tue May 29 02:20:07 2001 Simon J. Gerraty * compat.c (CompatRunCommand): don't use perror() since stdio may cause problems in child of vfork(). * compat.c, main.c: Call PrintOnError() when we are going to bail. This routine prints out the .curdir where we stopped and will also display any vars listed in ${MAKE_PRINT_VAR_ON_ERROR}. * main.c: add ${.newline} to hold a "\n" - sometimes handy in :@ expansion. * var.c: VarLoopExpand: ignore addSpace if a \n is present. * Added RCSid's for the files we've touched. Thu May 24 15:41:37 2001 Simon J. Gerraty * configure.in: Thanks to some clues from mdb@juniper.net, added autoconf magic to control setting of MACHINE, MACHINE_ARCH as well as what ends up in _PATH_DEFSYSPATH. We now have: --with-machine=MACHINE explicitly set MACHINE --with-force-machine=MACHINE set FORCE_MACHINE --with-machine_arch=MACHINE_ARCH explicitly set MACHINE_ARCH --with-default-sys-path=PATH:DIR:LIST use an explicit _PATH_DEFSYSPATH --with-prefix-sys-path=PATH:DIR:LIST prefix _PATH_PREFIX_SYSPATH --with-path-objdirprefix=PATH override _PATH_OBJDIRPREFIX If _PATH_OBJDIRPREFIX is set to "no" we won't define it. * makefile: added a pathetically simple makefile to drive bootstrapping. Running configure by hand is more useful. * Makefile.in: added MAKE_VERSION, and reworked things to be less dependent on NetBSD bsd.*.mk * pathnames.h: allow NO_PATH_OBJDIRPREFIX to stop us defining _PATH_OBJDIRPREFIX for those that don't want a default. construct _PATH_DEFSYSPATH from the info we get from configure. * main.c: allow for no _PATH_OBJDIRPREFIX, set ${MAKE_VERSION} if MAKE_VERSION is defined. * compat.c: when we bail, print out the .CURDIR we were in. Sat May 12 00:34:12 2001 Simon J. Gerraty * Merged with NetBSD make * var.c: fixed a bug in the handling of the modifier :P if the node as found but the path was null, we segfault trying to duplicate it. Mon Mar 5 16:20:33 2001 Simon J. Gerraty * Merged with NetBSD make * make.c: Make_OODate's test for a library out of date was using cmtime where it should have used mtime (my bug). * compat.c: Use perror() to tell us what really went wrong when we cannot exec a command. Fri Dec 15 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Sat Jun 10 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Thu Jun 1 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Tue May 30 10:11:08 2000 Simon J. Gerraty * Merged with NetBSD make Thu Apr 27 00:07:47 2000 Simon J. Gerraty * util.c: don't provide signal() since we use sigcompat.c * Makefile.in: added a build target. * var.c (Var_Parse): added ODE modifiers :U, :D, :L, :P, :@ and :! These allow some quite clever magic. * main.c (main): added support for getenv(MAKESYSPATH). Mon Apr 2 16:25:13 2000 Simon J. Gerraty * Disable $PWD overriding getcwd() if MAKEOBJDIRPREFIX is set. This avoids objdir having a different value depending on how a directory was reached (via command line, or subdir.mk). * If FORCE_MACHINE is defined, ignore getenv("MACHINE"). Mon Apr 2 23:15:31 2000 Simon J. Gerraty * Do a chdir(${.CURDIR}) before invoking ${.MAKE} or ${.MAKE:T} if MAKEOBJDIRPREFIX is set and NOCHECKMAKECHDIR is not. I've been testing this in NetBSD's make for some weeks. * Turn Makefile into Makefile.in and make it useful. Tue Feb 29 22:08:00 2000 Simon J. Gerraty * Imported NetBSD's -current make(1) and resolve conflicts. * Applied autoconf patches from bmake v2 * Imported clean code base from NetBSD-1.0 Index: head/contrib/bmake/FILES =================================================================== --- head/contrib/bmake/FILES (revision 365365) +++ head/contrib/bmake/FILES (revision 365366) @@ -1,189 +1,644 @@ ChangeLog FILES LICENSE Makefile Makefile.config.in PSD.doc/Makefile PSD.doc/tutorial.ms README VERSION aclocal.m4 arch.c bmake.1 bmake.cat1 boot-strap bsd.after-import.mk buf.c buf.h compat.c cond.c config.h.in configure configure.in dir.c dir.h dirname.c +enum.c +enum.h filemon/filemon.h filemon/filemon_dev.c filemon/filemon_ktrace.c find_lib.sh for.c getopt.c hash.c hash.h install-sh job.c job.h +lst.c lst.h -lst.lib/Makefile -lst.lib/lstAppend.c -lst.lib/lstAtEnd.c -lst.lib/lstAtFront.c -lst.lib/lstClose.c -lst.lib/lstConcat.c -lst.lib/lstDatum.c -lst.lib/lstDeQueue.c -lst.lib/lstDestroy.c -lst.lib/lstDupl.c -lst.lib/lstEnQueue.c -lst.lib/lstFind.c -lst.lib/lstFindFrom.c -lst.lib/lstFirst.c -lst.lib/lstForEach.c -lst.lib/lstForEachFrom.c -lst.lib/lstInit.c -lst.lib/lstInsert.c -lst.lib/lstInt.h -lst.lib/lstIsAtEnd.c -lst.lib/lstIsEmpty.c -lst.lib/lstLast.c -lst.lib/lstMember.c -lst.lib/lstNext.c -lst.lib/lstOpen.c -lst.lib/lstPrev.c -lst.lib/lstRemove.c -lst.lib/lstReplace.c -lst.lib/lstSucc.c machine.sh main.c make-bootstrap.sh.in make-conf.h make.1 make.c make.h make_malloc.c make_malloc.h makefile.in meta.c meta.h metachar.c metachar.h missing/sys/cdefs.h mkdeps.sh nonints.h os.sh parse.c pathnames.h ranlib.h realpath.c setenv.c sigcompat.c -sprite.h str.c stresep.c strlcpy.c strlist.c strlist.h suff.c targ.c trace.c trace.h unit-tests/Makefile unit-tests/Makefile.config.in +unit-tests/archive.exp +unit-tests/archive.mk +unit-tests/archive-suffix.exp +unit-tests/archive-suffix.mk +unit-tests/cmd-interrupt.exp +unit-tests/cmd-interrupt.mk +unit-tests/cmdline.exp +unit-tests/cmdline.mk unit-tests/comment.exp unit-tests/comment.mk +unit-tests/cond-cmp-numeric-eq.exp +unit-tests/cond-cmp-numeric-eq.mk +unit-tests/cond-cmp-numeric-ge.exp +unit-tests/cond-cmp-numeric-ge.mk +unit-tests/cond-cmp-numeric-gt.exp +unit-tests/cond-cmp-numeric-gt.mk +unit-tests/cond-cmp-numeric-le.exp +unit-tests/cond-cmp-numeric-le.mk +unit-tests/cond-cmp-numeric-lt.exp +unit-tests/cond-cmp-numeric-lt.mk +unit-tests/cond-cmp-numeric-ne.exp +unit-tests/cond-cmp-numeric-ne.mk +unit-tests/cond-cmp-numeric.exp +unit-tests/cond-cmp-numeric.mk +unit-tests/cond-cmp-string.exp +unit-tests/cond-cmp-string.mk +unit-tests/cond-func.exp +unit-tests/cond-func.mk +unit-tests/cond-func-commands.exp +unit-tests/cond-func-commands.mk +unit-tests/cond-func-defined.exp +unit-tests/cond-func-defined.mk +unit-tests/cond-func-empty.exp +unit-tests/cond-func-empty.mk +unit-tests/cond-func-exists.exp +unit-tests/cond-func-exists.mk +unit-tests/cond-func-make.exp +unit-tests/cond-func-make.mk +unit-tests/cond-func-target.exp +unit-tests/cond-func-target.mk +unit-tests/cond-late.exp +unit-tests/cond-late.mk +unit-tests/cond-op-and.exp +unit-tests/cond-op-and.mk +unit-tests/cond-op-not.exp +unit-tests/cond-op-not.mk +unit-tests/cond-op-or.exp +unit-tests/cond-op-or.mk +unit-tests/cond-op-parentheses.exp +unit-tests/cond-op-parentheses.mk +unit-tests/cond-op.exp +unit-tests/cond-op.mk +unit-tests/cond-short.exp +unit-tests/cond-short.mk +unit-tests/cond-token-number.exp +unit-tests/cond-token-number.mk +unit-tests/cond-token-plain.exp +unit-tests/cond-token-plain.mk +unit-tests/cond-token-string.exp +unit-tests/cond-token-string.mk +unit-tests/cond-token-var.exp +unit-tests/cond-token-var.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/cond-short.mk -unit-tests/cond-short.exp +unit-tests/counter.exp +unit-tests/counter.mk +unit-tests/dep-colon.exp +unit-tests/dep-colon.mk +unit-tests/dep-double-colon.exp +unit-tests/dep-double-colon.mk +unit-tests/dep-exclam.exp +unit-tests/dep-exclam.mk +unit-tests/dep-none.exp +unit-tests/dep-none.mk +unit-tests/dep-var.exp +unit-tests/dep-var.mk +unit-tests/dep-wildcards.exp +unit-tests/dep-wildcards.mk +unit-tests/dep.exp +unit-tests/dep.mk +unit-tests/depsrc-exec.exp +unit-tests/depsrc-exec.mk +unit-tests/depsrc-ignore.exp +unit-tests/depsrc-ignore.mk +unit-tests/depsrc-made.exp +unit-tests/depsrc-made.mk +unit-tests/depsrc-make.exp +unit-tests/depsrc-make.mk +unit-tests/depsrc-meta.exp +unit-tests/depsrc-meta.mk +unit-tests/depsrc-nometa.exp +unit-tests/depsrc-nometa.mk +unit-tests/depsrc-nometa_cmp.exp +unit-tests/depsrc-nometa_cmp.mk +unit-tests/depsrc-nopath.exp +unit-tests/depsrc-nopath.mk +unit-tests/depsrc-notmain.exp +unit-tests/depsrc-notmain.mk +unit-tests/depsrc-optional.exp +unit-tests/depsrc-optional.mk +unit-tests/depsrc-phony.exp +unit-tests/depsrc-phony.mk +unit-tests/depsrc-precious.exp +unit-tests/depsrc-precious.mk +unit-tests/depsrc-recursive.exp +unit-tests/depsrc-recursive.mk +unit-tests/depsrc-silent.exp +unit-tests/depsrc-silent.mk +unit-tests/depsrc-use.exp +unit-tests/depsrc-use.mk +unit-tests/depsrc-usebefore.exp +unit-tests/depsrc-usebefore.mk +unit-tests/depsrc-usebefore-double-colon.exp +unit-tests/depsrc-usebefore-double-colon.mk +unit-tests/depsrc-wait.exp +unit-tests/depsrc-wait.mk +unit-tests/depsrc.exp +unit-tests/depsrc.mk +unit-tests/deptgt-begin.exp +unit-tests/deptgt-begin.mk +unit-tests/deptgt-default.exp +unit-tests/deptgt-default.mk +unit-tests/deptgt-delete_on_error.exp +unit-tests/deptgt-delete_on_error.mk +unit-tests/deptgt-end.exp +unit-tests/deptgt-end.mk +unit-tests/deptgt-error.exp +unit-tests/deptgt-error.mk +unit-tests/deptgt-ignore.exp +unit-tests/deptgt-ignore.mk +unit-tests/deptgt-interrupt.exp +unit-tests/deptgt-interrupt.mk +unit-tests/deptgt-main.exp +unit-tests/deptgt-main.mk +unit-tests/deptgt-makeflags.exp +unit-tests/deptgt-makeflags.mk +unit-tests/deptgt-no_parallel.exp +unit-tests/deptgt-no_parallel.mk +unit-tests/deptgt-nopath.exp +unit-tests/deptgt-nopath.mk +unit-tests/deptgt-notparallel.exp +unit-tests/deptgt-notparallel.mk +unit-tests/deptgt-objdir.exp +unit-tests/deptgt-objdir.mk +unit-tests/deptgt-order.exp +unit-tests/deptgt-order.mk +unit-tests/deptgt-path-suffix.exp +unit-tests/deptgt-path-suffix.mk +unit-tests/deptgt-path.exp +unit-tests/deptgt-path.mk +unit-tests/deptgt-phony.exp +unit-tests/deptgt-phony.mk +unit-tests/deptgt-precious.exp +unit-tests/deptgt-precious.mk +unit-tests/deptgt-shell.exp +unit-tests/deptgt-shell.mk +unit-tests/deptgt-silent.exp +unit-tests/deptgt-silent.mk +unit-tests/deptgt-stale.exp +unit-tests/deptgt-stale.mk +unit-tests/deptgt-suffixes.exp +unit-tests/deptgt-suffixes.mk +unit-tests/deptgt.exp +unit-tests/deptgt.mk +unit-tests/dir.exp +unit-tests/dir.mk +unit-tests/dir-expand-path.exp +unit-tests/dir-expand-path.mk +unit-tests/directive-elif.exp +unit-tests/directive-elif.mk +unit-tests/directive-elifdef.exp +unit-tests/directive-elifdef.mk +unit-tests/directive-elifmake.exp +unit-tests/directive-elifmake.mk +unit-tests/directive-elifndef.exp +unit-tests/directive-elifndef.mk +unit-tests/directive-elifnmake.exp +unit-tests/directive-elifnmake.mk +unit-tests/directive-else.exp +unit-tests/directive-else.mk +unit-tests/directive-endif.exp +unit-tests/directive-endif.mk +unit-tests/directive-error.exp +unit-tests/directive-error.mk +unit-tests/directive-export-env.exp +unit-tests/directive-export-env.mk +unit-tests/directive-export-literal.exp +unit-tests/directive-export-literal.mk +unit-tests/directive-export.exp +unit-tests/directive-export.mk +unit-tests/directive-for.exp +unit-tests/directive-for.mk +unit-tests/directive-for-generating-endif.exp +unit-tests/directive-for-generating-endif.mk +unit-tests/directive-if.exp +unit-tests/directive-if.mk +unit-tests/directive-ifdef.exp +unit-tests/directive-ifdef.mk +unit-tests/directive-ifmake.exp +unit-tests/directive-ifmake.mk +unit-tests/directive-ifndef.exp +unit-tests/directive-ifndef.mk +unit-tests/directive-ifnmake.exp +unit-tests/directive-ifnmake.mk +unit-tests/directive-info.exp +unit-tests/directive-info.mk +unit-tests/directive-undef.exp +unit-tests/directive-undef.mk +unit-tests/directive-unexport-env.exp +unit-tests/directive-unexport-env.mk +unit-tests/directive-unexport.exp +unit-tests/directive-unexport.mk +unit-tests/directive-warning.exp +unit-tests/directive-warning.mk +unit-tests/directive.exp +unit-tests/directive.mk +unit-tests/directives.exp +unit-tests/directives.mk unit-tests/dollar.exp unit-tests/dollar.mk unit-tests/doterror.exp unit-tests/doterror.mk unit-tests/dotwait.exp unit-tests/dotwait.mk +unit-tests/envfirst.exp +unit-tests/envfirst.mk unit-tests/error.exp unit-tests/error.mk unit-tests/escape.exp unit-tests/escape.mk unit-tests/export-all.exp unit-tests/export-all.mk unit-tests/export-env.exp unit-tests/export-env.mk +unit-tests/export-variants.exp +unit-tests/export-variants.mk unit-tests/export.exp unit-tests/export.mk unit-tests/forloop.exp unit-tests/forloop.mk unit-tests/forsubst.exp unit-tests/forsubst.mk 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/lint.exp +unit-tests/lint.mk +unit-tests/make-exported.exp +unit-tests/make-exported.mk unit-tests/misc.exp unit-tests/misc.mk unit-tests/moderrs.exp unit-tests/moderrs.mk unit-tests/modmatch.exp unit-tests/modmatch.mk unit-tests/modmisc.exp unit-tests/modmisc.mk -unit-tests/modorder.exp -unit-tests/modorder.mk unit-tests/modts.exp unit-tests/modts.mk unit-tests/modword.exp unit-tests/modword.mk +unit-tests/opt-backwards.exp +unit-tests/opt-backwards.mk +unit-tests/opt-chdir.exp +unit-tests/opt-chdir.mk +unit-tests/opt-debug.exp +unit-tests/opt-debug.mk +unit-tests/opt-debug-g1.exp +unit-tests/opt-debug-g1.mk +unit-tests/opt-define.exp +unit-tests/opt-define.mk +unit-tests/opt-env.exp +unit-tests/opt-env.mk +unit-tests/opt-file.exp +unit-tests/opt-file.mk +unit-tests/opt-ignore.exp +unit-tests/opt-ignore.mk +unit-tests/opt-include-dir.exp +unit-tests/opt-include-dir.mk +unit-tests/opt-jobs-internal.exp +unit-tests/opt-jobs-internal.mk +unit-tests/opt-jobs.exp +unit-tests/opt-jobs.mk +unit-tests/opt-keep-going.exp +unit-tests/opt-keep-going.mk +unit-tests/opt-m-include-dir.exp +unit-tests/opt-m-include-dir.mk +unit-tests/opt-no-action-at-all.exp +unit-tests/opt-no-action-at-all.mk +unit-tests/opt-no-action.exp +unit-tests/opt-no-action.mk +unit-tests/opt-query.exp +unit-tests/opt-query.mk +unit-tests/opt-raw.exp +unit-tests/opt-raw.mk +unit-tests/opt-silent.exp +unit-tests/opt-silent.mk +unit-tests/opt-touch.exp +unit-tests/opt-touch.mk +unit-tests/opt-tracefile.exp +unit-tests/opt-tracefile.mk +unit-tests/opt-var-expanded.exp +unit-tests/opt-var-expanded.mk +unit-tests/opt-var-literal.exp +unit-tests/opt-var-literal.mk +unit-tests/opt-warnings-as-errors.exp +unit-tests/opt-warnings-as-errors.mk +unit-tests/opt-where-am-i.exp +unit-tests/opt-where-am-i.mk +unit-tests/opt-x-reduce-exported.exp +unit-tests/opt-x-reduce-exported.mk +unit-tests/opt.exp +unit-tests/opt.mk unit-tests/order.exp unit-tests/order.mk unit-tests/phony-end.exp unit-tests/phony-end.mk unit-tests/posix.exp unit-tests/posix.mk unit-tests/posix1.exp unit-tests/posix1.mk unit-tests/qequals.exp unit-tests/qequals.mk +unit-tests/recursive.exp +unit-tests/recursive.mk +unit-tests/sh-dots.exp +unit-tests/sh-dots.mk +unit-tests/sh-jobs-error.exp +unit-tests/sh-jobs-error.mk +unit-tests/sh-jobs.exp +unit-tests/sh-jobs.mk +unit-tests/sh-leading-at.exp +unit-tests/sh-leading-at.mk +unit-tests/sh-leading-hyphen.exp +unit-tests/sh-leading-hyphen.mk +unit-tests/sh-leading-plus.exp +unit-tests/sh-leading-plus.mk +unit-tests/sh-meta-chars.exp +unit-tests/sh-meta-chars.mk +unit-tests/sh-multi-line.exp +unit-tests/sh-multi-line.mk +unit-tests/sh-single-line.exp +unit-tests/sh-single-line.mk +unit-tests/sh.exp +unit-tests/sh.mk unit-tests/suffixes.exp unit-tests/suffixes.mk unit-tests/sunshcmd.exp unit-tests/sunshcmd.mk unit-tests/sysv.exp unit-tests/sysv.mk unit-tests/ternary.exp unit-tests/ternary.mk unit-tests/unexport-env.exp unit-tests/unexport-env.mk unit-tests/unexport.exp unit-tests/unexport.mk +unit-tests/use-inference.exp +unit-tests/use-inference.mk +unit-tests/var-class-cmdline.exp +unit-tests/var-class-cmdline.mk +unit-tests/var-class-env.exp +unit-tests/var-class-env.mk +unit-tests/var-class-global.exp +unit-tests/var-class-global.mk +unit-tests/var-class-local-legacy.exp +unit-tests/var-class-local-legacy.mk +unit-tests/var-class-local.exp +unit-tests/var-class-local.mk +unit-tests/var-class.exp +unit-tests/var-class.mk +unit-tests/var-op-append.exp +unit-tests/var-op-append.mk +unit-tests/var-op-assign.exp +unit-tests/var-op-assign.mk +unit-tests/var-op-default.exp +unit-tests/var-op-default.mk +unit-tests/var-op-expand.exp +unit-tests/var-op-expand.mk +unit-tests/var-op-shell.exp +unit-tests/var-op-shell.mk +unit-tests/var-op.exp +unit-tests/var-op.mk unit-tests/varcmd.exp unit-tests/varcmd.mk +unit-tests/vardebug.exp +unit-tests/vardebug.mk +unit-tests/varfind.exp +unit-tests/varfind.mk unit-tests/varmisc.exp unit-tests/varmisc.mk +unit-tests/varmod-assign.exp +unit-tests/varmod-assign.mk +unit-tests/varmod-defined.exp +unit-tests/varmod-defined.mk unit-tests/varmod-edge.exp unit-tests/varmod-edge.mk +unit-tests/varmod-exclam-shell.exp +unit-tests/varmod-exclam-shell.mk +unit-tests/varmod-extension.exp +unit-tests/varmod-extension.mk +unit-tests/varmod-gmtime.exp +unit-tests/varmod-gmtime.mk +unit-tests/varmod-hash.exp +unit-tests/varmod-hash.mk +unit-tests/varmod-head.exp +unit-tests/varmod-head.mk +unit-tests/varmod-ifelse.exp +unit-tests/varmod-ifelse.mk +unit-tests/varmod-l-name-to-value.exp +unit-tests/varmod-l-name-to-value.mk +unit-tests/varmod-localtime.exp +unit-tests/varmod-localtime.mk +unit-tests/varmod-loop.exp +unit-tests/varmod-loop.mk +unit-tests/varmod-match-escape.exp +unit-tests/varmod-match-escape.mk +unit-tests/varmod-match.exp +unit-tests/varmod-match.mk +unit-tests/varmod-no-match.exp +unit-tests/varmod-no-match.mk +unit-tests/varmod-order-reverse.exp +unit-tests/varmod-order-reverse.mk +unit-tests/varmod-order-shuffle.exp +unit-tests/varmod-order-shuffle.mk +unit-tests/varmod-order.exp +unit-tests/varmod-order.mk +unit-tests/varmod-path.exp +unit-tests/varmod-path.mk +unit-tests/varmod-quote-dollar.exp +unit-tests/varmod-quote-dollar.mk +unit-tests/varmod-quote.exp +unit-tests/varmod-quote.mk +unit-tests/varmod-range.exp +unit-tests/varmod-range.mk +unit-tests/varmod-remember.exp +unit-tests/varmod-remember.mk +unit-tests/varmod-root.exp +unit-tests/varmod-root.mk +unit-tests/varmod-select-words.exp +unit-tests/varmod-select-words.mk +unit-tests/varmod-shell.exp +unit-tests/varmod-shell.mk +unit-tests/varmod-subst-regex.exp +unit-tests/varmod-subst-regex.mk +unit-tests/varmod-subst.exp +unit-tests/varmod-subst.mk +unit-tests/varmod-sysv.exp +unit-tests/varmod-sysv.mk +unit-tests/varmod-tail.exp +unit-tests/varmod-tail.mk +unit-tests/varmod-to-abs.exp +unit-tests/varmod-to-abs.mk +unit-tests/varmod-to-lower.exp +unit-tests/varmod-to-lower.mk +unit-tests/varmod-to-many-words.exp +unit-tests/varmod-to-many-words.mk +unit-tests/varmod-to-one-word.exp +unit-tests/varmod-to-one-word.mk +unit-tests/varmod-to-separator.exp +unit-tests/varmod-to-separator.mk +unit-tests/varmod-to-upper.exp +unit-tests/varmod-to-upper.mk +unit-tests/varmod-undefined.exp +unit-tests/varmod-undefined.mk +unit-tests/varmod-unique.exp +unit-tests/varmod-unique.mk +unit-tests/varmod.exp +unit-tests/varmod.mk +unit-tests/varname-dollar.exp +unit-tests/varname-dollar.mk +unit-tests/varname-dot-alltargets.exp +unit-tests/varname-dot-alltargets.mk +unit-tests/varname-dot-curdir.exp +unit-tests/varname-dot-curdir.mk +unit-tests/varname-dot-includes.exp +unit-tests/varname-dot-includes.mk +unit-tests/varname-dot-includedfromdir.exp +unit-tests/varname-dot-includedfromdir.mk +unit-tests/varname-dot-includedfromfile.exp +unit-tests/varname-dot-includedfromfile.mk +unit-tests/varname-dot-libs.exp +unit-tests/varname-dot-libs.mk +unit-tests/varname-dot-make-dependfile.exp +unit-tests/varname-dot-make-dependfile.mk +unit-tests/varname-dot-make-expand_variables.exp +unit-tests/varname-dot-make-expand_variables.mk +unit-tests/varname-dot-make-exported.exp +unit-tests/varname-dot-make-exported.mk +unit-tests/varname-dot-make-jobs-prefix.exp +unit-tests/varname-dot-make-jobs-prefix.mk +unit-tests/varname-dot-make-jobs.exp +unit-tests/varname-dot-make-jobs.mk +unit-tests/varname-dot-make-level.exp +unit-tests/varname-dot-make-level.mk +unit-tests/varname-dot-make-makefile_preference.exp +unit-tests/varname-dot-make-makefile_preference.mk +unit-tests/varname-dot-make-makefiles.exp +unit-tests/varname-dot-make-makefiles.mk +unit-tests/varname-dot-make-meta-bailiwick.exp +unit-tests/varname-dot-make-meta-bailiwick.mk +unit-tests/varname-dot-make-meta-created.exp +unit-tests/varname-dot-make-meta-created.mk +unit-tests/varname-dot-make-meta-files.exp +unit-tests/varname-dot-make-meta-files.mk +unit-tests/varname-dot-make-meta-ignore_filter.exp +unit-tests/varname-dot-make-meta-ignore_filter.mk +unit-tests/varname-dot-make-meta-ignore_paths.exp +unit-tests/varname-dot-make-meta-ignore_paths.mk +unit-tests/varname-dot-make-meta-ignore_patterns.exp +unit-tests/varname-dot-make-meta-ignore_patterns.mk +unit-tests/varname-dot-make-meta-prefix.exp +unit-tests/varname-dot-make-meta-prefix.mk +unit-tests/varname-dot-make-mode.exp +unit-tests/varname-dot-make-mode.mk +unit-tests/varname-dot-make-path_filemon.exp +unit-tests/varname-dot-make-path_filemon.mk +unit-tests/varname-dot-make-pid.exp +unit-tests/varname-dot-make-pid.mk +unit-tests/varname-dot-make-ppid.exp +unit-tests/varname-dot-make-ppid.mk +unit-tests/varname-dot-make-save_dollars.exp +unit-tests/varname-dot-make-save_dollars.mk +unit-tests/varname-dot-makeoverrides.exp +unit-tests/varname-dot-makeoverrides.mk +unit-tests/varname-dot-newline.exp +unit-tests/varname-dot-newline.mk +unit-tests/varname-dot-objdir.exp +unit-tests/varname-dot-objdir.mk +unit-tests/varname-dot-parsedir.exp +unit-tests/varname-dot-parsedir.mk +unit-tests/varname-dot-parsefile.exp +unit-tests/varname-dot-parsefile.mk +unit-tests/varname-dot-path.exp +unit-tests/varname-dot-path.mk +unit-tests/varname-dot-shell.exp +unit-tests/varname-dot-shell.mk +unit-tests/varname-dot-targets.exp +unit-tests/varname-dot-targets.mk +unit-tests/varname-empty.exp +unit-tests/varname-empty.mk +unit-tests/varname-make.exp +unit-tests/varname-make.mk +unit-tests/varname-make_print_var_on_error.exp +unit-tests/varname-make_print_var_on_error.mk +unit-tests/varname-makeflags.exp +unit-tests/varname-makeflags.mk +unit-tests/varname-pwd.exp +unit-tests/varname-pwd.mk +unit-tests/varname-vpath.exp +unit-tests/varname-vpath.mk +unit-tests/varname.exp +unit-tests/varname.mk +unit-tests/varparse-dynamic.exp +unit-tests/varparse-dynamic.mk unit-tests/varquote.exp unit-tests/varquote.mk unit-tests/varshell.exp unit-tests/varshell.mk util.c var.c wait.h Index: head/contrib/bmake/LICENSE =================================================================== --- head/contrib/bmake/LICENSE (revision 365365) +++ head/contrib/bmake/LICENSE (revision 365366) @@ -0,0 +1,60 @@ +The individual files in this distribution are copyright their +original contributors or assignees. +Including: + + Copyright (c) 1993-2020, Simon J Gerraty + Copyright (c) 2020, Roland Illig + Copyright (c) 2009-2016, Juniper Networks, Inc. + Copyright (c) 2009, John Birrell. + Copyright (c) 1997-2020 The NetBSD Foundation, Inc. + Copyright (c) 1998 Todd C. Miller + Copyright (c) 1989 by Berkeley Softworks + Copyright (c) 1988, 1989, 1990, 1992, 1993 + The Regents of the University of California. + Copyright (c) 1988, 1989 by Adam de Boor + +With the exception of the GNU configure script, which states: + + # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. + # + # This configure script is free software; the Free Software Foundation + # gives unlimited permission to copy, distribute and modify it. + +The license for this distribution is considered to be: + + SPDX-License-Identifier: BSD-3-Clause + +example (from https://opensource.org/licenses/BSD-3-Clause): + + Copyright + + 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. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. + Index: head/contrib/bmake/Makefile =================================================================== --- head/contrib/bmake/Makefile (revision 365365) +++ head/contrib/bmake/Makefile (revision 365366) @@ -1,241 +1,215 @@ -# $Id: Makefile,v 1.107 2020/06/07 21:18:46 sjg Exp $ +# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $ PROG= bmake SRCS= \ arch.c \ buf.c \ compat.c \ cond.c \ dir.c \ + enum.c \ for.c \ hash.c \ job.c \ + lst.c \ main.c \ make.c \ make_malloc.c \ meta.c \ metachar.c \ parse.c \ str.c \ strlist.c \ suff.c \ targ.c \ trace.c \ util.c \ var.c -# from lst.lib/ -SRCS+= \ - lstAppend.c \ - lstAtEnd.c \ - lstAtFront.c \ - lstClose.c \ - lstConcat.c \ - lstDatum.c \ - lstDeQueue.c \ - lstDestroy.c \ - lstDupl.c \ - lstEnQueue.c \ - lstFind.c \ - lstFindFrom.c \ - lstFirst.c \ - lstForEach.c \ - lstForEachFrom.c \ - lstInit.c \ - lstInsert.c \ - lstIsAtEnd.c \ - lstIsEmpty.c \ - lstLast.c \ - lstMember.c \ - lstNext.c \ - lstOpen.c \ - lstPrev.c \ - lstRemove.c \ - lstReplace.c \ - lstSucc.c - .-include "VERSION" .-include "Makefile.inc" # this file gets generated by configure .-include "Makefile.config" .if !empty(LIBOBJS) SRCS+= ${LIBOBJS:T:.o=.c} .endif # just in case prefix?= /usr srcdir?= ${.CURDIR} DEFAULT_SYS_PATH?= ${prefix}/share/mk CPPFLAGS+= -DUSE_META CFLAGS+= ${CPPFLAGS} CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" 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 # should be set by now USE_FILEMON ?= no .if ${USE_FILEMON:tl} != "no" .PATH: ${srcdir}/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.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} .endif .endif # USE_FILEMON == dev .endif # USE_FILEMON .PATH: ${srcdir} -.PATH: ${srcdir}/lst.lib .if make(obj) || make(clean) SUBDIR+= unit-tests .endif # start-delete1 for bsd.after-import.mk # we skip a lot of this when building as part of FreeBSD etc. # list of OS's which are derrived from BSD4.4 BSD44_LIST= NetBSD FreeBSD OpenBSD DragonFly MirBSD Bitrig # we are... -OS!= uname -s +OS := ${.MAKE.OS:U${uname -s:L:sh}} # are we 4.4BSD ? isBSD44:=${BSD44_LIST:M${OS}} .if ${isBSD44} == "" MANTARGET= cat INSTALL?=${srcdir}/install-sh .if (${MACHINE} == "sun386") # even I don't have one of these anymore :-) CFLAGS+= -DPORTAR .elif (${MACHINE} != "sunos") SRCS+= sigcompat.c CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART .endif .else MANTARGET?= man .endif # turn this on by default - ignored if we are root WITH_INSTALL_AS_USER= # suppress with -DWITHOUT_* OPTIONS_DEFAULT_YES+= \ AUTOCONF_MK \ INSTALL_MK \ PROG_LINK OPTIONS_DEFAULT_NO+= \ PROG_VERSION # process options now .include .if ${MK_PROG_VERSION} == "yes" PROG_NAME= ${PROG}-${_MAKE_VERSION} .if ${MK_PROG_LINK} == "yes" SYMLINKS+= ${PROG_NAME} ${BINDIR}/${PROG} .endif .endif EXTRACT_MAN=no # end-delete1 MAN= ${PROG}.1 MAN1= ${MAN} .if (${PROG} != "make") CLEANFILES+= my.history .if make(${MAN}) || !exists(${srcdir}/${MAN}) my.history: @(echo ".Nm"; \ echo "is derived from NetBSD"; \ echo ".Xr make 1 ."; \ echo "It uses autoconf to facilitate portability to other platforms."; \ echo ".Pp") > $@ .NOPATH: ${MAN} ${MAN}: make.1 my.history @echo making $@ @sed \ -e '/^.Dt/s/MAKE/${PROG:tu}/' \ -e 's/^.Nx/NetBSD/' \ -e '/^.Nm/s/make/${PROG}/' \ -e '/^.Sh HISTORY/rmy.history' \ -e '/^.Sh HISTORY/,$$s,^.Nm,make,' ${srcdir}/make.1 > $@ all beforeinstall: ${MAN} _mfromdir=. .endif .endif MANTARGET?= cat MANDEST?= ${MANDIR}/${MANTARGET}1 .if ${MANTARGET} == "cat" _mfromdir=${srcdir} .endif .include CPPFLAGS+= -DMAKE_NATIVE -DHAVE_CONFIG_H COPTS.var.c += -Wno-cast-qual COPTS.job.c += -Wno-format-nonliteral COPTS.parse.c += -Wno-format-nonliteral COPTS.var.c += -Wno-format-nonliteral # Force these SHAREDIR= ${SHAREDIR.bmake:U${prefix}/share} BINDIR= ${BINDIR.bmake:U${prefix}/bin} MANDIR= ${MANDIR.bmake:U${SHAREDIR}/man} .if !exists(.depend) ${OBJS}: config.h .endif # start-delete2 for bsd.after-import.mk # make sure that MAKE_VERSION gets updated. main.o: ${srcdir}/VERSION .if ${MK_AUTOCONF_MK} == "yes" CONFIGURE_DEPS += ${.CURDIR}/VERSION # we do not need or want the generated makefile CONFIGURE_ARGS += --without-makefile .include .endif SHARE_MK?=${SHAREDIR}/mk MKSRC=${srcdir}/mk INSTALL?=${srcdir}/install-sh .if ${MK_INSTALL_MK} == "yes" install: install-mk .endif beforeinstall: test -d ${DESTDIR}${BINDIR} || ${INSTALL} -m 775 -d ${DESTDIR}${BINDIR} test -d ${DESTDIR}${MANDEST} || ${INSTALL} -m 775 -d ${DESTDIR}${MANDEST} install-mk: .if exists(${MKSRC}/install-mk) test -d ${DESTDIR}${SHARE_MK} || ${INSTALL} -m 775 -d ${DESTDIR}${SHARE_MK} sh ${MKSRC}/install-mk -v -m 644 ${DESTDIR}${SHARE_MK} .else @echo need to unpack mk.tar.gz under ${srcdir} or set MKSRC; false .endif # end-delete2 # A simple unit-test driver to help catch regressions +TEST_MAKE ?= ${.OBJDIR}/${PROG:T} accept test: - cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} + cd ${.CURDIR}/unit-tests && \ + MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}} + Index: head/contrib/bmake/VERSION =================================================================== --- head/contrib/bmake/VERSION (revision 365365) +++ head/contrib/bmake/VERSION (revision 365366) @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20200710 +_MAKE_VERSION=20200902 Index: head/contrib/bmake/arch.c =================================================================== --- head/contrib/bmake/arch.c (revision 365365) +++ head/contrib/bmake/arch.c (revision 365366) @@ -1,1416 +1,1275 @@ -/* $NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94"; #else -__RCSID("$NetBSD: arch.c,v 1.73 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: arch.c,v 1.107 2020/08/30 11:15:05 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * arch.c -- * Functions to manipulate libraries, archives and their members. * * Once again, cacheing/hashing comes into play in the manipulation * of archives. The first time an archive is referenced, all of its members' * headers are read and hashed and the archive closed again. All hashed * archives are kept on a list which is searched each time an archive member * is referenced. * * The interface to this module is: * Arch_ParseArchive Given an archive specification, return a list * of GNode's, one for each member in the spec. - * FAILURE is returned if the specification is + * FALSE is returned if the specification is * invalid for some reason. * * Arch_Touch Alter the modification time of the archive * member described by the given node to be * the current time. * * Arch_TouchLib Update the modification time of the library * described by the given node. This is special * because it also updates the modification time * of the library's table of contents. * * Arch_MTime Find the modification time of a member of * an archive *in the archive*. The time is also * placed in the member's GNode. Returns the * modification time. * * Arch_MemTime Find the modification time of a member of * an archive. Called when the member doesn't * already exist. Looks in the archive for the * modification time. Returns the modification * time. * * Arch_FindLib Search for a library along a path. The * library name in the GNode should be in * -l format. * * Arch_LibOODate Special function to decide if a library node * is out-of-date. * * Arch_Init Initialize this module. * * Arch_End Cleanup this module. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #ifdef HAVE_AR_H #include #else struct ar_hdr { char ar_name[16]; /* name */ char ar_date[12]; /* modification time */ char ar_uid[6]; /* user id */ char ar_gid[6]; /* group id */ char ar_mode[8]; /* octal file permissions */ char ar_size[10]; /* size in bytes */ #ifndef ARFMAG #define ARFMAG "`\n" #endif char ar_fmag[2]; /* consistency check */ }; #endif #if defined(HAVE_RANLIB_H) && !(defined(__ELF__) || defined(NO_RANLIB)) #include #endif #include #include #ifdef HAVE_UTIME_H #include #endif #include "make.h" #include "hash.h" #include "dir.h" #ifdef TARGET_MACHINE #undef MAKE_MACHINE #define MAKE_MACHINE TARGET_MACHINE #endif #ifdef TARGET_MACHINE_ARCH #undef MAKE_MACHINE_ARCH #define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH #endif static Lst archives; /* Lst of archives we've already examined */ typedef struct Arch { char *name; /* Name of archive */ Hash_Table members; /* All the members of the archive described * by key/value pairs */ char *fnametab; /* Extended name table strings */ size_t fnamesize; /* Size of the string table */ } Arch; -static int ArchFindArchive(const void *, const void *); -#ifdef CLEANUP -static void ArchFree(void *); -#endif -static struct ar_hdr *ArchStatMember(char *, char *, Boolean); -static FILE *ArchFindMember(char *, char *, struct ar_hdr *, const char *); +static struct ar_hdr *ArchStatMember(const char *, const char *, Boolean); +static FILE *ArchFindMember(const char *, const char *, + struct ar_hdr *, const char *); #if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) #define SVR4ARCHIVES static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); #endif #if defined(_AIX) # define AR_NAME _ar_name.ar_name # define AR_FMAG _ar_name.ar_fmag # define SARMAG SAIAMAG # define ARMAG AIAMAG # define ARFMAG AIAFMAG #endif #ifndef AR_NAME # define AR_NAME ar_name #endif #ifndef AR_DATE # define AR_DATE ar_date #endif #ifndef AR_SIZE # define AR_SIZE ar_size #endif #ifndef AR_FMAG # define AR_FMAG ar_fmag #endif #ifndef ARMAG # define ARMAG "!\n" #endif #ifndef SARMAG # define SARMAG 8 #endif #define AR_MAX_NAME_LEN (sizeof(arh.AR_NAME)-1) #ifdef CLEANUP -/*- - *----------------------------------------------------------------------- - * ArchFree -- - * Free memory used by an archive - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ static void ArchFree(void *ap) { Arch *a = (Arch *)ap; Hash_Search search; Hash_Entry *entry; /* Free memory from hash entries */ for (entry = Hash_EnumFirst(&a->members, &search); entry != NULL; entry = Hash_EnumNext(&search)) free(Hash_GetValue(entry)); free(a->name); free(a->fnametab); Hash_DeleteTable(&a->members); free(a); } #endif /*- *----------------------------------------------------------------------- * Arch_ParseArchive -- * Parse the archive specification in the given line and find/create * the nodes for the specified archive members, placing their nodes * on the given list. * * Input: * linePtr Pointer to start of specification * nodeLst Lst on which to place the nodes * ctxt Context in which to expand variables * * Results: - * SUCCESS if it was a valid specification. The linePtr is updated + * TRUE if it was a valid specification. The linePtr is updated * to point to the first non-space after the archive spec. The * nodes for the members are placed on the given list. - * - * Side Effects: - * Some nodes may be created. The given list is extended. - * *----------------------------------------------------------------------- */ -ReturnStatus +Boolean Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) { char *cp; /* Pointer into line */ GNode *gn; /* New node */ char *libName; /* Library-part of specification */ char *memName; /* Member-part of specification */ - char *nameBuf; /* temporary place for node name */ char saveChar; /* Ending delimiter of member-name */ Boolean subLibName; /* TRUE if libName should have/had * variable substitution performed on it */ libName = *linePtr; subLibName = FALSE; for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; - void *freeIt; - char *result; + void *result_freeIt; + const char *result; + Boolean isError; - result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, - &length, &freeIt); - free(freeIt); + result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES, + &length, &result_freeIt); + isError = result == var_Error; + free(result_freeIt); + if (isError) + return FALSE; - if (result == var_Error) { - return FAILURE; - } else { - subLibName = TRUE; - } - - cp += length-1; + subLibName = TRUE; + cp += length - 1; } } *cp++ = '\0'; if (subLibName) { - libName = Var_Subst(NULL, libName, ctxt, VARF_UNDEFERR|VARF_WANTRES); + libName = Var_Subst(libName, ctxt, VARE_UNDEFERR|VARE_WANTRES); } for (;;) { /* * First skip to the start of the member's name, mark that * place and skip to the end of it (either white-space or * a close paren). */ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) { cp++; } memName = cp; while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; void *freeIt; - char *result; + const char *result; + Boolean isError; - result = Var_Parse(cp, ctxt, VARF_UNDEFERR|VARF_WANTRES, + result = Var_Parse(cp, ctxt, VARE_UNDEFERR|VARE_WANTRES, &length, &freeIt); + isError = result == var_Error; free(freeIt); - if (result == var_Error) { - return FAILURE; - } else { - doSubst = TRUE; - } + if (isError) + return FALSE; + doSubst = TRUE; cp += length; } else { cp++; } } /* * If the specification ends without a closing parenthesis, * chances are there's something wrong (like a missing backslash), * so it's better to return failure than allow such things to happen */ if (*cp == '\0') { printf("No closing parenthesis in archive specification\n"); - return FAILURE; + return FALSE; } /* * If we didn't move anywhere, we must be done */ if (cp == memName) { break; } saveChar = *cp; *cp = '\0'; /* * XXX: This should be taken care of intelligently by * SuffExpandChildren, both for the archive and the member portions. */ /* * If member contains variables, try and substitute for them. * This will slow down archive specs with dynamic sources, of course, * since we'll be (non-)substituting them three times, but them's * the breaks -- we need to do this since SuffExpandChildren calls * us, otherwise we could assume the thing would be taken care of * later. */ if (doSubst) { char *buf; char *sacrifice; char *oldMemName = memName; - size_t sz; - memName = Var_Subst(NULL, memName, ctxt, - VARF_UNDEFERR|VARF_WANTRES); + memName = Var_Subst(memName, ctxt, VARE_UNDEFERR | VARE_WANTRES); /* * Now form an archive spec and recurse to deal with nested * variables and multi-word variable values.... The results * are just placed at the end of the nodeLst we're returning. */ - sz = strlen(memName)+strlen(libName)+3; - buf = sacrifice = bmake_malloc(sz); + buf = sacrifice = str_concat4(libName, "(", memName, ")"); - snprintf(buf, sz, "%s(%s)", libName, memName); - if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { /* * Must contain dynamic sources, so we can't deal with it now. * Just create an ARCHV node for the thing and let * SuffExpandChildren handle it... */ gn = Targ_FindNode(buf, TARG_CREATE); if (gn == NULL) { free(buf); - return FAILURE; + return FALSE; } else { gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } - } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { + } else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) { /* - * Error in nested call -- free buffer and return FAILURE + * Error in nested call -- free buffer and return FALSE * ourselves. */ free(buf); - return FAILURE; + return FALSE; } /* * Free buffer and continue with our work. */ free(buf); } else if (Dir_HasWildcards(memName)) { - Lst members = Lst_Init(FALSE); - char *member; - size_t sz = MAXPATHLEN, nsz; - nameBuf = bmake_malloc(sz); + Lst members = Lst_Init(); + Buffer nameBuf; + Buf_Init(&nameBuf, 0); Dir_Expand(memName, dirSearchPath, members); while (!Lst_IsEmpty(members)) { - member = (char *)Lst_DeQueue(members); - nsz = strlen(libName) + strlen(member) + 3; - if (sz > nsz) - nameBuf = bmake_realloc(nameBuf, sz = nsz * 2); + char *member = Lst_Dequeue(members); - snprintf(nameBuf, sz, "%s(%s)", libName, member); + Buf_Empty(&nameBuf); + Buf_AddStr(&nameBuf, libName); + Buf_AddStr(&nameBuf, "("); + Buf_AddStr(&nameBuf, member); + Buf_AddStr(&nameBuf, ")"); free(member); - gn = Targ_FindNode(nameBuf, TARG_CREATE); + + gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE); if (gn == NULL) { - free(nameBuf); - return FAILURE; + Buf_Destroy(&nameBuf, TRUE); + return FALSE; } else { /* * We've found the node, but have to make sure the rest of * the world knows it's an archive member, without having * to constantly check for parentheses, so we type the * thing with the OP_ARCHV bit before we place it on the * end of the provided list. */ gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } } - Lst_Destroy(members, NULL); - free(nameBuf); + Lst_Free(members); + Buf_Destroy(&nameBuf, TRUE); } else { - size_t sz = strlen(libName) + strlen(memName) + 3; - nameBuf = bmake_malloc(sz); - snprintf(nameBuf, sz, "%s(%s)", libName, memName); - gn = Targ_FindNode(nameBuf, TARG_CREATE); - free(nameBuf); + Buffer nameBuf; + + Buf_Init(&nameBuf, 0); + Buf_AddStr(&nameBuf, libName); + Buf_AddStr(&nameBuf, "("); + Buf_AddStr(&nameBuf, memName); + Buf_AddStr(&nameBuf, ")"); + + gn = Targ_FindNode(Buf_GetAll(&nameBuf, NULL), TARG_CREATE); + Buf_Destroy(&nameBuf, TRUE); if (gn == NULL) { - return FAILURE; + return FALSE; } else { /* * We've found the node, but have to make sure the rest of the * world knows it's an archive member, without having to * constantly check for parentheses, so we type the thing with * the OP_ARCHV bit before we place it on the end of the * provided list. */ gn->type |= OP_ARCHV; - (void)Lst_AtEnd(nodeLst, gn); + Lst_Append(nodeLst, gn); } } if (doSubst) { free(memName); } *cp = saveChar; } /* * If substituted libName, free it now, since we need it no longer. */ if (subLibName) { free(libName); } /* * We promised the pointer would be set up at the next non-space, so * we must advance cp there before setting *linePtr... (note that on * entrance to the loop, cp is guaranteed to point at a ')') */ do { cp++; } while (*cp != '\0' && isspace ((unsigned char)*cp)); *linePtr = cp; - return SUCCESS; + return TRUE; } -/*- - *----------------------------------------------------------------------- - * ArchFindArchive -- - * See if the given archive is the one we are looking for. Called - * From ArchStatMember and ArchFindMember via Lst_Find. - * - * Input: - * ar Current list element - * archName Name we want - * - * Results: - * 0 if it is, non-zero if it isn't. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int -ArchFindArchive(const void *ar, const void *archName) +/* See if the given archive is the one we are looking for. + * Called via Lst_Find. */ +static Boolean +ArchFindArchive(const void *ar, const void *desiredName) { - return strcmp(archName, ((const Arch *)ar)->name); + return strcmp(((const Arch *)ar)->name, desiredName) == 0; } /*- *----------------------------------------------------------------------- * ArchStatMember -- * Locate a member of an archive, given the path of the archive and * the path of the desired member. * * Input: * archive Path to the archive * member Name of member. If it is a path, only the last * component is used. * hash TRUE if archive should be hashed if not already so. * * Results: * A pointer to the current struct ar_hdr structure for the member. Note * That no position is returned, so this is not useful for touching * archive members. This is mostly because we have no assurances that * The archive will remain constant after we read all the headers, so * there's not much point in remembering the position... - * - * Side Effects: - * *----------------------------------------------------------------------- */ static struct ar_hdr * -ArchStatMember(char *archive, char *member, Boolean hash) +ArchStatMember(const char *archive, const char *member, Boolean hash) { FILE * arch; /* Stream to archive */ - int size; /* Size of archive member */ - char *cp; /* Useful character pointer */ + size_t size; /* Size of archive member */ char magic[SARMAG]; LstNode ln; /* Lst member containing archive descriptor */ Arch *ar; /* Archive descriptor */ Hash_Entry *he; /* Entry containing member's description */ struct ar_hdr arh; /* archive-member header for reading archive */ char memName[MAXPATHLEN+1]; - /* Current member name while hashing. */ + /* Current member name while hashing. */ /* * Because of space constraints and similar things, files are archived * using their final path components, not the entire thing, so we need * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr(member, '/'); - if (cp != NULL) { - member = cp + 1; + const char *base = strrchr(member, '/'); + if (base != NULL) { + member = base + 1; } - ln = Lst_Find(archives, archive, ArchFindArchive); + ln = Lst_Find(archives, ArchFindArchive, archive); if (ln != NULL) { - ar = (Arch *)Lst_Datum(ln); + ar = LstNode_Datum(ln); he = Hash_FindEntry(&ar->members, member); if (he != NULL) { return (struct ar_hdr *)Hash_GetValue(he); } else { /* Try truncated name */ char copy[AR_MAX_NAME_LEN+1]; size_t len = strlen(member); if (len > AR_MAX_NAME_LEN) { len = AR_MAX_NAME_LEN; - strncpy(copy, member, AR_MAX_NAME_LEN); - copy[AR_MAX_NAME_LEN] = '\0'; + snprintf(copy, sizeof copy, "%s", member); } if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) return (struct ar_hdr *)Hash_GetValue(he); return NULL; } } if (!hash) { /* * Caller doesn't want the thing hashed, just use ArchFindMember * to read the header for the member out and close down the stream * again. Since the archive is not to be hashed, we assume there's * no need to allocate extra room for the header we're returning, * so just declare it static. */ static struct ar_hdr sarh; arch = ArchFindMember(archive, member, &sarh, "r"); if (arch == NULL) { return NULL; } else { fclose(arch); return &sarh; } } /* * We don't have this archive on the list yet, so we want to find out * everything that's in it and cache it so we can get at it quickly. */ arch = fopen(archive, "r"); if (arch == NULL) { return NULL; } /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if ((fread(magic, SARMAG, 1, arch) != 1) || - (strncmp(magic, ARMAG, SARMAG) != 0)) { + (strncmp(magic, ARMAG, SARMAG) != 0)) { fclose(arch); return NULL; } ar = bmake_malloc(sizeof(Arch)); ar->name = bmake_strdup(archive); ar->fnametab = NULL; ar->fnamesize = 0; Hash_InitTable(&ar->members, -1); memName[AR_MAX_NAME_LEN] = '\0'; while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) { if (strncmp( arh.AR_FMAG, ARFMAG, sizeof(arh.AR_FMAG)) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ goto badarch; } else { + char *nameend; + /* * We need to advance the stream's pointer to the start of the * next header. Files are padded with newlines to an even-byte * boundary, so we need to extract the size of the file from the * 'size' field of the header and round it up during the seek. */ arh.AR_SIZE[sizeof(arh.AR_SIZE)-1] = '\0'; - size = (int)strtol(arh.AR_SIZE, NULL, 10); + size = (size_t)strtol(arh.ar_size, NULL, 10); memcpy(memName, arh.AR_NAME, sizeof(arh.AR_NAME)); - for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { - continue; + nameend = memName + AR_MAX_NAME_LEN; + while (*nameend == ' ') { + nameend--; } - cp[1] = '\0'; + nameend[1] = '\0'; #ifdef SVR4ARCHIVES /* * svr4 names are slash terminated. Also svr4 extended AR format. */ if (memName[0] == '/') { /* * svr4 magic mode; handle it */ switch (ArchSVR4Entry(ar, memName, size, arch)) { case -1: /* Invalid data */ goto badarch; case 0: /* List of files entry */ continue; default: /* Got the entry */ break; } } else { - if (cp[0] == '/') - cp[0] = '\0'; + if (nameend[0] == '/') + nameend[0] = '\0'; } #endif #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) { - unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); + int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); - if (elen > MAXPATHLEN) + if ((unsigned int)elen > MAXPATHLEN) goto badarch; - if (fread(memName, elen, 1, arch) != 1) + if (fread(memName, (size_t)elen, 1, arch) != 1) goto badarch; memName[elen] = '\0'; if (fseek(arch, -elen, SEEK_CUR) != 0) goto badarch; if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName); } } #endif he = Hash_CreateEntry(&ar->members, memName, NULL); Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr))); memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr)); } - if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) + if (fseek(arch, ((long)size + 1) & ~1, SEEK_CUR) != 0) goto badarch; } fclose(arch); - (void)Lst_AtEnd(archives, ar); + Lst_Append(archives, ar); /* * Now that the archive has been read and cached, we can look into * the hash table to find the desired member's header. */ he = Hash_FindEntry(&ar->members, member); if (he != NULL) { return (struct ar_hdr *)Hash_GetValue(he); } else { return NULL; } badarch: fclose(arch); Hash_DeleteTable(&ar->members); free(ar->fnametab); free(ar); return NULL; } #ifdef SVR4ARCHIVES /*- *----------------------------------------------------------------------- * ArchSVR4Entry -- * Parse an SVR4 style entry that begins with a slash. * If it is "//", then load the table of filenames * If it is "/", then try to substitute the long file name * from offset of a table previously read. + * If a table is read, the file pointer is moved to the next archive + * member. * * Results: * -1: Bad data in archive * 0: A table was loaded from the file * 1: Name was successfully substituted from table * 2: Name was not successfully substituted from table - * - * Side Effects: - * If a table is read, the file pointer is moved to the next archive - * member - * *----------------------------------------------------------------------- */ static int ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) { #define ARLONGNAMES1 "//" #define ARLONGNAMES2 "/ARFILENAMES" size_t entry; char *ptr, *eptr; if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { if (ar->fnametab != NULL) { if (DEBUG(ARCH)) { fprintf(debug_file, "Attempted to redefine an SVR4 name table\n"); } return -1; } /* * This is a table of archive names, so we build one for * ourselves */ ar->fnametab = bmake_malloc(size); ar->fnamesize = size; if (fread(ar->fnametab, size, 1, arch) != 1) { if (DEBUG(ARCH)) { fprintf(debug_file, "Reading an SVR4 name table failed\n"); } return -1; } eptr = ar->fnametab + size; for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) switch (*ptr) { case '/': entry++; *ptr = '\0'; break; case '\n': break; default: break; } if (DEBUG(ARCH)) { fprintf(debug_file, "Found svr4 archive name table with %lu entries\n", - (unsigned long)entry); + (unsigned long)entry); } return 0; } if (name[1] == ' ' || name[1] == '\0') return 2; entry = (size_t)strtol(&name[1], &eptr, 0); if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) { if (DEBUG(ARCH)) { fprintf(debug_file, "Could not parse SVR4 name %s\n", name); } return 2; } if (entry >= ar->fnamesize) { if (DEBUG(ARCH)) { fprintf(debug_file, "SVR4 entry offset %s is greater than %lu\n", name, (unsigned long)ar->fnamesize); } return 2; } if (DEBUG(ARCH)) { fprintf(debug_file, "Replaced %s with %s\n", name, &ar->fnametab[entry]); } - (void)strncpy(name, &ar->fnametab[entry], MAXPATHLEN); - name[MAXPATHLEN] = '\0'; + snprintf(name, MAXPATHLEN + 1, "%s", &ar->fnametab[entry]); return 1; } #endif /*- *----------------------------------------------------------------------- * ArchFindMember -- * Locate a member of an archive, given the path of the archive and * the path of the desired member. If the archive is to be modified, * the mode should be "r+", if not, it should be "r". + * The passed struct ar_hdr structure is filled in. * * Input: * archive Path to the archive * member Name of member. If it is a path, only the last * component is used. * arhPtr Pointer to header structure to be filled in * mode The mode for opening the stream * * Results: * An FILE *, opened for reading and writing, positioned at the * start of the member's struct ar_hdr, or NULL if the member was * nonexistent. The current struct ar_hdr for member. - * - * Side Effects: - * The passed struct ar_hdr structure is filled in. - * *----------------------------------------------------------------------- */ static FILE * -ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, +ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr, const char *mode) { FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ - char *cp; /* Useful character pointer */ char magic[SARMAG]; size_t len, tlen; + const char * base; arch = fopen(archive, mode); if (arch == NULL) { return NULL; } /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if ((fread(magic, SARMAG, 1, arch) != 1) || - (strncmp(magic, ARMAG, SARMAG) != 0)) { + (strncmp(magic, ARMAG, SARMAG) != 0)) { fclose(arch); return NULL; } /* * Because of space constraints and similar things, files are archived * using their final path components, not the entire thing, so we need * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ - cp = strrchr(member, '/'); - if (cp != NULL) { - member = cp + 1; + base = strrchr(member, '/'); + if (base != NULL) { + member = base + 1; } len = tlen = strlen(member); if (len > sizeof(arhPtr->AR_NAME)) { tlen = sizeof(arhPtr->AR_NAME); } while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) { if (strncmp(arhPtr->AR_FMAG, ARFMAG, sizeof(arhPtr->AR_FMAG) ) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ fclose(arch); return NULL; } else if (strncmp(member, arhPtr->AR_NAME, tlen) == 0) { /* * If the member's name doesn't take up the entire 'name' field, * we have to be careful of matching prefixes. Names are space- * padded to the right, so if the character in 'name' at the end * of the matched string is anything but a space, this isn't the * member we sought. */ if (tlen != sizeof(arhPtr->AR_NAME) && arhPtr->AR_NAME[tlen] != ' '){ goto skip; } else { /* * To make life easier, we reposition the file at the start * of the header we just read before we return the stream. * In a more general situation, it might be better to leave * the file at the actual member, rather than its header, but * not here... */ - if (fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR) != 0) { + if (fseek(arch, -(long)sizeof(struct ar_hdr), SEEK_CUR) != 0) { fclose(arch); return NULL; } return arch; } } else #ifdef AR_EFMT1 /* * BSD 4.4 extended AR format: #1/, with name as the * first bytes of the file */ if (strncmp(arhPtr->AR_NAME, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && isdigit((unsigned char)arhPtr->AR_NAME[sizeof(AR_EFMT1) - 1])) { - unsigned int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); + int elen = atoi(&arhPtr->AR_NAME[sizeof(AR_EFMT1)-1]); char ename[MAXPATHLEN + 1]; - if (elen > MAXPATHLEN) { + if ((unsigned int)elen > MAXPATHLEN) { fclose(arch); return NULL; } - if (fread(ename, elen, 1, arch) != 1) { + if (fread(ename, (size_t)elen, 1, arch) != 1) { fclose(arch); return NULL; } ename[elen] = '\0'; if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "ArchFind: Extended format entry for %s\n", ename); } if (strncmp(ename, member, len) == 0) { /* Found as extended name */ - if (fseek(arch, -sizeof(struct ar_hdr) - elen, + if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen, SEEK_CUR) != 0) { fclose(arch); return NULL; } return arch; } if (fseek(arch, -elen, SEEK_CUR) != 0) { fclose(arch); return NULL; } goto skip; } else #endif { skip: /* * This isn't the member we're after, so we need to advance the * stream's pointer to the start of the next header. Files are * padded with newlines to an even-byte boundary, so we need to * extract the size of the file from the 'size' field of the * header and round it up during the seek. */ arhPtr->AR_SIZE[sizeof(arhPtr->AR_SIZE)-1] = '\0'; size = (int)strtol(arhPtr->AR_SIZE, NULL, 10); if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) { fclose(arch); return NULL; } } } /* * We've looked everywhere, but the member is not to be found. Close the * archive and return NULL -- an error. */ fclose(arch); return NULL; } /*- *----------------------------------------------------------------------- * Arch_Touch -- * Touch a member of an archive. + * The modification time of the entire archive is also changed. + * For a library, this could necessitate the re-ranlib'ing of the + * whole thing. * * Input: * gn Node of member to touch * * Results: * The 'time' field of the member's header is updated. - * - * Side Effects: - * The modification time of the entire archive is also changed. - * For a library, this could necessitate the re-ranlib'ing of the - * whole thing. - * *----------------------------------------------------------------------- */ void Arch_Touch(GNode *gn) { FILE * arch; /* Stream open to archive, positioned properly */ struct ar_hdr arh; /* Current header describing member */ char *p1, *p2; arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1), Var_Value(MEMBER, gn, &p2), &arh, "r+"); - free(p1); - free(p2); + bmake_free(p1); + bmake_free(p2); snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); if (arch != NULL) { (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); fclose(arch); } } -/*- - *----------------------------------------------------------------------- - * Arch_TouchLib -- - * Given a node which represents a library, touch the thing, making - * sure that the table of contents also is touched. +/* Given a node which represents a library, touch the thing, making sure that + * the table of contents also is touched. * + * Both the modification time of the library and of the RANLIBMAG member are + * set to 'now'. + * * Input: * gn The node of the library to touch - * - * Results: - * None. - * - * Side Effects: - * Both the modification time of the library and of the RANLIBMAG - * member are set to 'now'. - * - *----------------------------------------------------------------------- */ void -#if !defined(RANLIBMAG) -Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED) -#else Arch_TouchLib(GNode *gn) -#endif { #ifdef RANLIBMAG FILE * arch; /* Stream open to archive */ struct ar_hdr arh; /* Header describing table of contents */ struct utimbuf times; /* Times for utime() call */ - arch = ArchFindMember(gn->path, UNCONST(RANLIBMAG), &arh, "r+"); + arch = ArchFindMember(gn->path, RANLIBMAG, &arh, "r+"); snprintf(arh.AR_DATE, sizeof(arh.AR_DATE), "%-12ld", (long) now); if (arch != NULL) { (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); fclose(arch); times.actime = times.modtime = now; utime(gn->path, ×); } +#else + (void)gn; #endif } -/*- - *----------------------------------------------------------------------- - * Arch_MTime -- - * Return the modification time of a member of an archive. +/* Return the modification time of a member of an archive. The mtime field + * of the given node is filled in with the value returned by the function. * * Input: * gn Node describing archive member - * - * Results: - * The modification time(seconds). - * - * Side Effects: - * The mtime field of the given node is filled in with the value - * returned by the function. - * - *----------------------------------------------------------------------- */ time_t Arch_MTime(GNode *gn) { struct ar_hdr *arhPtr; /* Header of desired member */ time_t modTime; /* Modification time as an integer */ char *p1, *p2; arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1), Var_Value(MEMBER, gn, &p2), TRUE); - free(p1); - free(p2); + bmake_free(p1); + bmake_free(p2); if (arhPtr != NULL) { modTime = (time_t)strtol(arhPtr->AR_DATE, NULL, 10); } else { modTime = 0; } gn->mtime = modTime; return modTime; } -/*- - *----------------------------------------------------------------------- - * Arch_MemMTime -- - * Given a non-existent archive member's node, get its modification - * time from its archived form, if it exists. - * - * Results: - * The modification time. - * - * Side Effects: - * The mtime field is filled in. - * - *----------------------------------------------------------------------- - */ +/* Given a non-existent archive member's node, get its modification time from + * its archived form, if it exists. gn->mtime is filled in as well. */ time_t Arch_MemMTime(GNode *gn) { LstNode ln; GNode *pgn; - char *nameStart, - *nameEnd; - if (Lst_Open(gn->parents) != SUCCESS) { - gn->mtime = 0; - return 0; - } + Lst_Open(gn->parents); while ((ln = Lst_Next(gn->parents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); + pgn = LstNode_Datum(ln); if (pgn->type & OP_ARCHV) { /* * If the parent is an archive specification and is being made * and its member's name matches the name of the node we were * given, record the modification time of the parent in the * child. We keep searching its parents in case some other * parent requires this child to exist... */ - nameStart = strchr(pgn->name, '(') + 1; - nameEnd = strchr(nameStart, ')'); + const char *nameStart = strchr(pgn->name, '(') + 1; + const char *nameEnd = strchr(nameStart, ')'); + size_t nameLen = (size_t)(nameEnd - nameStart); if ((pgn->flags & REMAKE) && - strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { - gn->mtime = Arch_MTime(pgn); + strncmp(nameStart, gn->name, nameLen) == 0) { + gn->mtime = Arch_MTime(pgn); } } else if (pgn->flags & REMAKE) { /* * Something which isn't a library depends on the existence of * this target, so it needs to exist. */ gn->mtime = 0; break; } } Lst_Close(gn->parents); return gn->mtime; } -/*- - *----------------------------------------------------------------------- - * Arch_FindLib -- - * Search for a library along the given search path. +/* Search for a library along the given search path. * + * The node's 'path' field is set to the found path (including the + * actual file name, not -l...). If the system can handle the -L + * flag when linking (or we cannot find the library), we assume that + * the user has placed the .LIBS variable in the final linking + * command (or the linker will know where to find it) and set the + * TARGET variable for this node to be the node's name. Otherwise, + * we set the TARGET variable to be the full path of the library, + * as returned by Dir_FindFile. + * * Input: * gn Node of library to find * path Search path - * - * Results: - * None. - * - * Side Effects: - * The node's 'path' field is set to the found path (including the - * actual file name, not -l...). If the system can handle the -L - * flag when linking (or we cannot find the library), we assume that - * the user has placed the .LIBRARIES variable in the final linking - * command (or the linker will know where to find it) and set the - * TARGET variable for this node to be the node's name. Otherwise, - * we set the TARGET variable to be the full path of the library, - * as returned by Dir_FindFile. - * - *----------------------------------------------------------------------- */ void Arch_FindLib(GNode *gn, Lst path) { char *libName; /* file name for archive */ size_t sz = strlen(gn->name) + 6 - 2; libName = bmake_malloc(sz); snprintf(libName, sz, "lib%s.a", &gn->name[2]); gn->path = Dir_FindFile(libName, path); free(libName); #ifdef LIBRARIES Var_Set(TARGET, gn->name, gn); #else Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); #endif /* LIBRARIES */ } -/*- - *----------------------------------------------------------------------- - * Arch_LibOODate -- - * Decide if a node with the OP_LIB attribute is out-of-date. Called - * from Make_OODate to make its life easier. +/* Decide if a node with the OP_LIB attribute is out-of-date. Called from + * Make_OODate to make its life easier. + * The library will be hashed if it hasn't been already. * - * There are several ways for a library to be out-of-date that are - * not available to ordinary files. In addition, there are ways - * that are open to regular files that are not available to - * libraries. A library that is only used as a source is never - * considered out-of-date by itself. This does not preclude the - * library's modification time from making its parent be out-of-date. - * A library will be considered out-of-date for any of these reasons, - * given that it is a target on a dependency line somewhere: - * Its modification time is less than that of one of its - * sources (gn->mtime < gn->cmgn->mtime). - * Its modification time is greater than the time at which the - * make began (i.e. it's been modified in the course - * of the make, probably by archiving). - * The modification time of one of its sources is greater than - * the one of its RANLIBMAG member (i.e. its table of contents - * is out-of-date). We don't compare of the archive time - * vs. TOC time because they can be too close. In my - * opinion we should not bother with the TOC at all since - * this is used by 'ar' rules that affect the data contents - * of the archive, not by ranlib rules, which affect the - * TOC. + * There are several ways for a library to be out-of-date that are + * not available to ordinary files. In addition, there are ways + * that are open to regular files that are not available to + * libraries. A library that is only used as a source is never + * considered out-of-date by itself. This does not preclude the + * library's modification time from making its parent be out-of-date. + * A library will be considered out-of-date for any of these reasons, + * given that it is a target on a dependency line somewhere: * + * Its modification time is less than that of one of its sources + * (gn->mtime < gn->cmgn->mtime). + * + * Its modification time is greater than the time at which the make + * began (i.e. it's been modified in the course of the make, probably + * by archiving). + * + * The modification time of one of its sources is greater than the one + * of its RANLIBMAG member (i.e. its table of contents is out-of-date). + * We don't compare of the archive time vs. TOC time because they can be + * too close. In my opinion we should not bother with the TOC at all + * since this is used by 'ar' rules that affect the data contents of the + * archive, not by ranlib rules, which affect the TOC. + * * Input: * gn The library's graph node * * Results: * TRUE if the library is out-of-date. FALSE otherwise. - * - * Side Effects: - * The library will be hashed if it hasn't been already. - * - *----------------------------------------------------------------------- */ Boolean Arch_LibOODate(GNode *gn) { Boolean oodate; if (gn->type & OP_PHONY) { oodate = TRUE; } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { oodate = FALSE; } else if ((!Lst_IsEmpty(gn->children) && gn->cmgn == NULL) || (gn->mtime > now) || (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) { oodate = TRUE; } else { #ifdef RANLIBMAG struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ int modTimeTOC; /* The table-of-contents's mod time */ - arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE); + arhPtr = ArchStatMember(gn->path, RANLIBMAG, FALSE); if (arhPtr != NULL) { modTimeTOC = (int)strtol(arhPtr->AR_DATE, NULL, 10); if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); } oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC); } else { /* * A library w/o a table of contents is out-of-date */ if (DEBUG(ARCH) || DEBUG(MAKE)) { fprintf(debug_file, "No t.o.c...."); } oodate = TRUE; } #else oodate = FALSE; #endif } return oodate; } -/*- - *----------------------------------------------------------------------- - * Arch_Init -- - * Initialize things for this module. - * - * Results: - * None. - * - * Side Effects: - * The 'archives' list is initialized. - * - *----------------------------------------------------------------------- - */ +/* Initialize things for this module. */ void Arch_Init(void) { - archives = Lst_Init(FALSE); + archives = Lst_Init(); } - - -/*- - *----------------------------------------------------------------------- - * Arch_End -- - * Cleanup things for this module. - * - * Results: - * None. - * - * Side Effects: - * The 'archives' list is freed - * - *----------------------------------------------------------------------- - */ +/* Clean up things for this module. */ void Arch_End(void) { #ifdef CLEANUP Lst_Destroy(archives, ArchFree); #endif } -/*- - *----------------------------------------------------------------------- - * Arch_IsLib -- - * Check if the node is a library - * - * Results: - * True or False. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -int +Boolean Arch_IsLib(GNode *gn) { static const char armag[] = "!\n"; - char buf[sizeof(armag)-1]; + char buf[sizeof armag - 1]; int fd; if ((fd = open(gn->path, O_RDONLY)) == -1) return FALSE; - if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + if (read(fd, buf, sizeof buf) != sizeof buf) { (void)close(fd); return FALSE; } (void)close(fd); - return memcmp(buf, armag, sizeof(buf)) == 0; + return memcmp(buf, armag, sizeof buf) == 0; } Index: head/contrib/bmake/bmake.1 =================================================================== --- head/contrib/bmake/bmake.1 (revision 365365) +++ head/contrib/bmake/bmake.1 (revision 365366) @@ -1,2445 +1,2455 @@ -.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $ +.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" 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. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd June 5, 2020 +.Dd August 28, 2020 .Dt BMAKE 1 .Os .Sh NAME .Nm bmake .Nd maintain program dependencies .Sh SYNOPSIS .Nm .Op Fl BeikNnqrstWwX .Op Fl C Ar directory .Op Fl D Ar variable .Op Fl d Ar flags .Op Fl f Ar makefile .Op Fl I Ar directory .Op Fl J Ar private .Op Fl j Ar max_jobs .Op Fl m Ar directory .Op Fl T Ar file .Op Fl V Ar variable .Op Fl v Ar variable .Op Ar variable=value .Op Ar target ... .Sh DESCRIPTION .Nm is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. If no .Fl f Ar makefile makefile option is given, .Nm will try to open .Ql Pa makefile then .Ql Pa Makefile in order to find the specifications. If the file .Ql Pa .depend exists, it is read (see .Xr mkdep 1 ) . .Pp This manual page is intended as a reference document only. For a more thorough description of .Nm and makefiles, please refer to .%T "PMake \- A Tutorial" . .Pp .Nm will prepend the contents of the .Va MAKEFLAGS environment variable to the command line arguments before parsing them. .Pp The options are as follows: .Bl -tag -width Ds .It Fl B Try to be backwards compatible by executing a single shell per command and by executing the commands to make the sources of a dependency line in sequence. .It Fl C Ar directory Change to .Ar directory before reading the makefiles or doing anything else. If multiple .Fl C options are specified, each is interpreted relative to the previous one: .Fl C Pa / Fl C Pa etc is equivalent to .Fl C Pa /etc . .It Fl D Ar variable Define .Ar variable to be 1, in the global context. .It Fl d Ar [-]flags Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by .Ql \- they are added to the .Va MAKEFLAGS environment variable and will be processed by any child make processes. By default, debugging information is printed to standard error, but this can be changed using the .Ar F debugging flag. The debugging output is always unbuffered; in addition, if debugging is enabled but debugging output is not directed to standard output, then the standard output is line buffered. .Ar Flags is one or more of the following: .Bl -tag -width Ds .It Ar A Print all possible debugging information; equivalent to specifying all of the debugging flags. .It Ar a Print debugging information about archive searching and caching. .It Ar C Print debugging information about current working directory. .It Ar c Print debugging information about conditional evaluation. .It Ar d Print debugging information about directory searching and caching. .It Ar e Print debugging information about failed commands and targets. .It Ar F Ns Oo Sy \&+ Oc Ns Ar filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the argument. If the character immediately after the .Ql F flag is .Ql \&+ , then the file will be opened in append mode; otherwise the file will be overwritten. If the file name is .Ql stdout or .Ql stderr then debugging output will be written to the standard output or standard error output file descriptors respectively (and the .Ql \&+ option has no effect). Otherwise, the output will be written to the named file. If the file name ends .Ql .%d then the .Ql %d is replaced by the pid. .It Ar f Print debugging information about loop evaluation. .It Ar "g1" Print the input graph before making anything. .It Ar "g2" Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. +.It Ar h +Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. +.It Ar L +Turn on lint checks. +This will throw errors for variable assignments that do not parse +correctly, at the time of assignment so the file and line number +are available. .It Ar l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ or other "quiet" flags. Also known as "loud" behavior. .It Ar M Print debugging information about "meta" mode decisions about targets. .It Ar m Print debugging information about making targets, including modification dates. .It Ar n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in the directory referred to by the .Ev TMPDIR environment variable, or in .Pa /tmp if .Ev TMPDIR is unset or set to the empty string. The temporary scripts are created by .Xr mkstemp 3 , and have names of the form .Pa makeXXXXXX . .Em NOTE : This can create many files in .Ev TMPDIR or .Pa /tmp , so use with care. .It Ar p Print debugging information about makefile parsing. .It Ar s Print debugging information about suffix-transformation rules. .It Ar t Print debugging information about target list maintenance. .It Ar V Force the .Fl V option to print raw values of variables, overriding the default behavior set via .Va .MAKE.EXPAND_VARIABLES . .It Ar v Print debugging information about variable assignment. .It Ar x Run shell commands with .Fl x so the actual commands are printed as they are executed. .El .It Fl e Specify that environment variables override macro assignments within makefiles. .It Fl f Ar makefile Specify a makefile to read instead of the default .Ql Pa makefile . If .Ar makefile is .Ql Fl , standard input is read. Multiple makefiles may be specified, and are read in the order specified. .It Fl I Ar directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the .Fl m option) is automatically included as part of this list. .It Fl i Ignore non-zero exit of shell commands in the makefile. Equivalent to specifying .Ql Fl before each command line in the makefile. .It Fl J Ar private This option should .Em not be specified by the user. .Pp When the .Ar j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. .It Fl j Ar max_jobs Specify the maximum number of jobs that .Nm may have running at any one time. The value is saved in .Va .MAKE.JOBS . Turns compatibility mode off, unless the .Ar B flag is also specified. When compatibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the traditional one shell invocation per line. This can break traditional scripts which change directories on each command invocation and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. .It Fl k Continue processing after errors are encountered, but only on those targets 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 .Li \&< Ns Ar file Ns Li \&> Ns -style include statement. The .Fl m 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 .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). .Pp If a file or directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string .Qq \&.../ then .Nm will search for the specified file or directory named in the remaining part of the argument string. The search starts with the current directory of the Makefile and then works upward towards the root of the file system. If the search is successful, then the resulting directory replaces the .Qq \&.../ specification in the .Fl m argument. If used, this feature allows .Nm to easily search in the current source tree for customized sys.mk files (e.g., by using .Qq \&.../mk/sys.mk as an argument). .It Fl n Display the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE special -source (see below). +source (see below) or the command is prefixed with +.Ql Ic + . .It Fl N Display the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles without descending into subdirectories. .It Fl q Do not execute any commands, but exit 0 if the specified targets are up-to-date and 1, otherwise. .It Fl r Do not use the built-in rules specified in the system makefile. .It Fl s Do not echo any commands as they are executed. Equivalent to specifying .Ql Ic @ before each command line in the makefile. .It Fl T Ar tracefile When used with the .Fl j flag, append a trace record to .Ar tracefile for each job started and completed. .It Fl t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up-to-date. .It Fl V Ar variable Print the value of .Ar variable . Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or undefined variable. The value printed is extracted from the global context after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded variable references) are shown. If .Ar variable contains a .Ql \&$ then the value will be recursively expanded to its complete resultant text before printing. The expanded value will also be printed if .Va .MAKE.EXPAND_VARIABLES is set to true and the .Fl dV option has not been used to override it. Note that loop-local and target-local variables, as well as values taken temporarily by global variables during makefile processing, are not accessible via this option. The .Fl dv debug mode can be used to see these at the cost of generating substantial extraneous output. .It Fl v Ar variable Like .Fl V but the variable is always expanded to its complete value. .It Fl W Treat any warnings during makefile parsing as errors. .It Fl w Print entering and leaving directory messages, pre and post processing. .It Fl X Don't export variables passed on the command line to the environment individually. Variables passed on the command line are still exported via the .Va MAKEFLAGS environment variable. This option may be useful on systems which have a small limit on the size of command arguments. .It Ar variable=value Set the value of the variable .Ar variable to .Ar value . Normally, all values passed on the command line are also exported to sub-makes in the environment. The .Fl X flag disables this behavior. Variable assignments should follow options for POSIX compatibility but no ordering is enforced. .El .Pp There are seven different types of lines in a makefile: file dependency specifications, shell commands, variable assignments, include statements, conditional directives, for loops, and comments. .Pp In general, lines may be continued from one line to the next by ending them with a backslash .Pq Ql \e . The trailing newline character and initial whitespace on the following line are compressed into a single space. .Sh FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets .Dq depend on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: +and are customarily created from them. +A target is considered out-of-date if it does not exist, or if its +modification time is less than that of any of its sources. +An out-of-date target will be re-created, but not until all sources +have been examined and themselves re-created as needed. +Three operators may be used: .Bl -tag -width flag .It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if +Many dependency lines may name this target but only one may have +attached shell commands. +All sources named in all dependency lines are considered together, +and if needed the attached shell commands are run to create or +re-create the target. +If .Nm -is interrupted. +is interrupted, the target is removed. .It Ic \&! -Targets are always re-created, but not until all sources have been -examined and re-created as necessary. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. +The same, but the target is always re-created whether or not it is out +of date. .It Ic \&:: -If no sources are specified, the target is always re-created. -Otherwise, a target is considered out-of-date if any of its sources has -been modified more recently than the target. -Sources for a target do not accumulate over dependency lines when this -operator is used. -The target will not be removed if +Any dependency line may have attached shell commands, but each one +is handled independently: its sources are considered and the attached +shell commands are run if the target is out of date with respect to +(only) those sources. +Thus, different groups of the attached shell commands may be run +depending on the circumstances. +Furthermore, unlike +.Ic \&:, +for dependency lines with no sources, the attached shell +commands are always run. +Also unlike +.Ic \&:, +the target will not be removed if .Nm is interrupted. .El +All dependency lines mentioning a particular target must use the same +operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , .Ql * , .Ql [] , and .Ql {} . The values .Ql \&? , .Ql * , and .Ql [] may only be used as part of the final component of the target or source, and must be used to describe existing files. The value .Ql {} need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. .Sh SHELL COMMANDS Each target may have associated with it one or more lines of shell commands, normally used to create the target. Each of the lines in this script .Em must be preceded by a tab. (For historical reasons, spaces are not accepted.) While targets can appear in many dependency lines if desired, by default only one of these rules may be followed by a creation script. If the .Ql Ic \&:: operator is used, however, all rules may include scripts and the scripts are executed in the order found. .Pp Each line is treated as a separate shell command, unless the end of line is escaped with a backslash .Pq Ql \e in which case that line and the next are combined. .\" The escaped newline is retained and passed to the shell, which .\" normally ignores it. .\" However, the tab at the beginning of the following line is removed. If the first characters of the command are any combination of .Ql Ic @ , .Ql Ic + , or .Ql Ic \- , the command is treated specially. A .Ql Ic @ causes the command not to be echoed before it is executed. A .Ql Ic + causes the command to be executed even when .Fl n is given. This is similar to the effect of the .MAKE special source, except that the effect can be limited to a single line of a script. A .Ql Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. .Pp When .Nm is run in jobs mode with .Fl j Ar max_jobs , the entire script for the target is fed to a single instance of the shell. In compatibility (non-jobs) mode, each command is run in a separate process. If the command contains any shell meta characters .Pq Ql #=|^(){};&<>*?[]:$`\e\en it will be passed to the shell; otherwise .Nm will attempt direct execution. If a line starts with .Ql Ic \- and the shell has ErrCtl enabled then failure of the command line will be ignored as in compatibility mode. Otherwise .Ql Ic \- affects the entire job; the script will stop at the first command line that fails, but the target will not be deemed to have failed. .Pp Makefiles should be written so that the mode of .Nm operation does not change their behavior. For example, any command which needs to use .Dq cd or .Dq chdir without potentially changing the directory for subsequent commands should be put in parentheses so it executes in a subshell. To force the use of one shell, escape the line breaks so as to make the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \e (cd ${.CURDIR} && ${MAKE} $@); \e echo Back in `pwd` .Ed .Pp Since .Nm will .Xr chdir 2 to .Ql Va .OBJDIR before executing any targets, each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradition, consist of all upper-case letters. .Ss Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overridden. .It Ic \&+= Append the value to the current value of the variable. .It Ic \&?= Assign the value to the variable if it is not already defined. .It Ic \&:= Assign with expansion, i.e. expand the value before assigning it to the variable. Normally, expansion is not done until the variable is referenced. .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. .It Ic \&!= Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. .El .Pp Any white-space before the assigned .Ar value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. .Pp Variables are expanded by surrounding the variable name with either curly braces .Pq Ql {} or parentheses .Pq Ql () and preceding it with a dollar sign .Pq Ql \&$ . If the variable name contains only a single letter, the surrounding braces or parentheses are not required. This shorter form is not recommended. .Pp If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parenthesis, or whitespace are really best avoided! +braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ the string is expanded again. .Pp Variable substitution occurs at three distinct times, depending on where the variable is being used. .Bl -enum .It Variables in dependency lines are expanded as the line is read. .It Variables in shell commands are expanded when the shell command is executed. .It .Dq .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the following example code: .Bd -literal -offset indent .Dv .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .Dv .endfor all: @echo ${a} @echo ${b} .Ed will print: .Bd -literal -offset indent 1 2 3 3 3 3 .Ed Because while ${a} contains .Dq 1 2 3 after the loop is executed, ${b} contains .Dq ${j} ${j} ${j} which expands to .Dq 3 3 3 since after the loop completes ${j} contains .Dq 3 . .El .Ss Variable classes The four different classes of variables (in order of increasing precedence) are: .Bl -tag -width Ds .It Environment variables Variables defined as part of .Nm Ns 's environment. .It Global variables Variables defined in the makefile or in included makefiles. .It Command line variables Variables defined as part of the command line. .It Local variables Variables that are defined specific to a certain target. .El .Pp Local variables are all built in and their values vary magically from target to target. It is not currently possible to define new local variables. The seven local variables are as follows: .Bl -tag -width ".ARCHIVE" -offset indent .It Va .ALLSRC The list of all sources for this target; also known as .Ql Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Ql Va \&! . .It Va .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the .Dq implied source); also known as .Ql Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Ql Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Ql Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Ql Va * . The suffix must be one of the known suffixes declared with .Ic .SUFFIXES or it will not be recognized. .It Va .TARGET The name of the target; also known as .Ql Va @ . For compatibility with other makes this is an alias for .Ic .ARCHIVE in archive member rules. .El .Pp The shorter forms .Ql ( Va > , .Ql Va \&! , .Ql Va < , .Ql Va % , .Ql Va \&? , .Ql Va * , and .Ql Va @ ) are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. .Pp Variants of these variables with the punctuation followed immediately by .Ql D or .Ql F , e.g. .Ql Va $(@D) , are legacy forms equivalent to using the .Ql :H and .Ql :T modifiers. These forms are accepted for compatibility with .At V makefiles and POSIX but are not recommended. .Pp Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. These variables are .Ql Va .TARGET , .Ql Va .PREFIX , .Ql Va .ARCHIVE , and .Ql Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: .Bl -tag -width .MAKEOVERRIDES .It Va \&$ A single dollar sign .Ql \&$ , i.e. .Ql \&$$ expands to a single dollar sign. .It Va .ALLTARGETS The list of all targets encountered in the Makefile. If evaluated during Makefile parsing, lists only those targets encountered thus far. .It Va .CURDIR A path to the directory where .Nm was executed. Refer to the description of .Ql Ev PWD for more details. .It Va .INCLUDEDFROMDIR The directory of the file this Makefile was included from. .It Va .INCLUDEDFROMFILE The filename of the file this Makefile was included from. .It Ev MAKE The name that .Nm was executed with .Pq Va argv[0] . For compatibility .Nm also sets .Va .MAKE with the same value. The preferred variable to use is the environment variable .Ev MAKE because it is more compatible with other versions of .Nm and cannot be confused with the special target with the same name. .It Va .MAKE.DEPENDFILE Names the makefile (default .Ql Pa .depend ) from which generated dependencies are read. .It Va .MAKE.EXPAND_VARIABLES A boolean that controls the default behavior of the .Fl V option. If true, variable values printed with .Fl V are fully expanded; if false, the raw variable contents (which may include additional unexpanded variable references) are shown. .It Va .MAKE.EXPORTED The list of variables exported by .Nm . .It Va .MAKE.JOBS The argument to the .Fl j option. .It Va .MAKE.JOB.PREFIX If .Nm is run with .Ar j then output for each target is prefixed with a token .Ql --- target --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. .br For example: .Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like .Ql ---make[1234] target --- making it easier to track the degree of parallelism being achieved. .It Ev MAKEFLAGS The environment variable .Ql Ev MAKEFLAGS may contain anything that may be specified on .Nm Ns 's command line. Anything specified on .Nm Ns 's command line is appended to the .Ql Ev MAKEFLAGS variable which is then entered into the environment for all programs which .Nm executes. .It Va .MAKE.LEVEL The recursion depth of .Nm . The initial instance of .Nm will be 0, and an incremented value is put into the environment to be seen by the next generation. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 to protect things which should only be evaluated in the initial instance of .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Ql Pa makefile , .Ql Pa Makefile ) that .Nm will look for. .It Va .MAKE.MAKEFILES The list of makefiles read by .Nm , which is useful for tracking dependencies. Each makefile is recorded only once, regardless of the number of times read. .It Va .MAKE.MODE Processed after reading all makefiles. Can affect the mode that .Nm runs in. It can contain a number of keywords: .Bl -hang -width missing-filemon=bf. .It Pa compat Like .Fl B , puts .Nm into "compat" mode. .It Pa meta Puts .Nm into "meta" mode, where meta files are created for each target to capture the command run, the output generated and if .Xr filemon 4 is available, the system calls which are of interest to .Nm . The captured output can be very useful when diagnosing errors. .It Pa curdirOk= Ar bf Normally .Nm will not create .meta files in .Ql Va .CURDIR . This can be overridden by setting .Va bf to a value which represents True. .It Pa missing-meta= Ar bf If .Va bf is True, then a missing .meta file makes the target out-of-date. .It Pa missing-filemon= Ar bf If .Va bf is True, then missing filemon data makes the target out-of-date. .It Pa nofilemon Do not use .Xr filemon 4 . .It Pa env For debugging, it can be useful to include the environment in the .meta file. .It Pa verbose If in "meta" mode, print a clue about the target being built. This is useful if the build is otherwise running silently. The message printed the value of: .Va .MAKE.META.PREFIX . .It Pa ignore-cmd Some makefiles have commands which are simply not stable. This keyword causes them to be ignored for determining whether a target is out of date in "meta" mode. See also .Ic .NOMETA_CMP . .It Pa silent= Ar bf If .Va bf is True, when a .meta file is created, mark the target .Ic .SILENT . .El .It Va .MAKE.META.BAILIWICK In "meta" mode, provides a list of prefixes which match the directories controlled by .Nm . If a file that was generated outside of .Va .OBJDIR but within said bailiwick is missing, the current target is considered out-of-date. .It Va .MAKE.META.CREATED In "meta" mode, this variable contains a list of all the meta files updated. If not empty, it can be used to trigger processing of .Va .MAKE.META.FILES . .It Va .MAKE.META.FILES In "meta" mode, this variable contains a list of all the meta files used (updated or not). This list can be used to process the meta files to extract dependency information. .It Va .MAKE.META.IGNORE_PATHS Provides a list of path prefixes that should be ignored; because the contents are expected to change over time. The default list includes: .Ql Pa /dev /etc /proc /tmp /var/run /var/tmp .It Va .MAKE.META.IGNORE_PATTERNS Provides a list of patterns to match against pathnames. Ignore any that match. .It Va .MAKE.META.IGNORE_FILTER Provides a list of variable modifiers to apply to each pathname. Ignore if the expansion is an empty string. .It Va .MAKE.META.PREFIX Defines the message printed for each meta file updated in "meta verbose" mode. The default value is: .Dl Building ${.TARGET:H:tA}/${.TARGET:T} .It Va .MAKEOVERRIDES This variable is used to record the names of variables assigned to on the command line, so that they may be exported as part of .Ql Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Ql Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Ql Va .MAKEOVERRIDES . .Ql Ev MAKEFLAGS is re-exported whenever .Ql Va .MAKEOVERRIDES is modified. .It Va .MAKE.PATH_FILEMON If .Nm was built with .Xr filemon 4 support, this is set to the path of the device node. This allows makefiles to test for this support. .It Va .MAKE.PID The process-id of .Nm . .It Va .MAKE.PPID The parent process-id of .Nm . .It Va .MAKE.SAVE_DOLLARS value should be a boolean that controls whether .Ql $$ are preserved when doing .Ql := assignments. The default is false, for backwards compatibility. Set to true for compatability with other makes. If set to false, .Ql $$ becomes .Ql $ per normal evaluation rules. .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Ql Va .ERROR_TARGET to the name of the target that failed, .Ql Va .ERROR_CMD to the commands of the failed target, and in "meta" mode, it also sets .Ql Va .ERROR_CWD to the .Xr getcwd 3 , and .Ql Va .ERROR_META_FILE to the path of the meta file (if any) describing the failed target. It then prints its name and the value of .Ql Va .CURDIR as well as the value of any variables named in .Ql Va MAKE_PRINT_VAR_ON_ERROR . .It Va .newline This variable is simply assigned a newline character as its value. This allows expansions using the .Cm \&:@ modifier to put a newline between iterations of the loop rather than a space. For example, the printing of .Ql Va MAKE_PRINT_VAR_ON_ERROR could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. .It Va .OBJDIR A path to the directory where the targets are built. Its value is determined by trying to .Xr chdir 2 to the following directories in order and using the first match: .Bl -enum .It .Ev ${MAKEOBJDIRPREFIX}${.CURDIR} .Pp (Only if .Ql Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It .Ev ${MAKEOBJDIR} .Pp (Only if .Ql Ev MAKEOBJDIR is set in the environment or on the command line.) .It .Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} .It .Ev ${.CURDIR} Ns Pa /obj .It .Pa /usr/obj/ Ns Ev ${.CURDIR} .It .Ev ${.CURDIR} .El .Pp Variable expansion is performed on the value before it's used, so expressions such as .Dl ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Ql Ev MAKEOBJDIR . .Pp .Ql Va .OBJDIR may be modified in the makefile via the special target .Ql Ic .OBJDIR . In all cases, .Nm will .Xr chdir 2 to the specified directory if it exists, and set .Ql Va .OBJDIR and .Ql Ev PWD to that directory before executing any targets. . .It Va .PARSEDIR A path to the directory of the current .Ql Pa Makefile being parsed. .It Va .PARSEFILE The basename of the current .Ql Pa Makefile being parsed. This variable and .Ql Va .PARSEDIR are both set only while the .Ql Pa Makefiles are being parsed. If you want to retain their current values, assign them to a variable using assignment with expansion: .Pq Ql Cm \&:= . .It Va .PATH A variable that represents the list of directories that .Nm will search for files. The search list should be updated using the target .Ql Va .PATH rather than the variable. .It Ev PWD Alternate path to the current directory. .Nm normally sets .Ql Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Ql Ev PWD is set and gives a path to the current directory, then .Nm sets .Ql Va .CURDIR to the value of .Ql Ev PWD instead. This behavior is disabled if .Ql Ev MAKEOBJDIRPREFIX is set or .Ql Ev MAKEOBJDIR contains a variable transform. .Ql Ev PWD is set to the value of .Ql Va .OBJDIR for all programs which .Nm executes. +.It Ev .SHELL +The pathname of the shell used to run target scripts. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH Colon-separated .Pq Dq \&: lists of directories that .Nm will search for files. The variable is supported for compatibility with old make programs only, use .Ql Va .PATH instead. .El .Ss Variable modifiers Variable expansion may be modified to select or modify each word of the variable (where a .Dq word is white-space delimited sequence of characters). The general format of a variable expansion is as follows: .Pp .Dl ${variable[:modifier[:...]]} .Pp Each modifier begins with a colon, which may be escaped with a backslash .Pq Ql \e . .Pp A set of modifiers can be specified via a variable, as follows: .Pp .Dl modifier_variable=modifier[:...] .Dl ${variable:${modifier_variable}[:...]} .Pp In this case the first modifier in the modifier_variable does not start with a colon, since that must appear in the referencing variable. If any of the modifiers in the modifier_variable contain a dollar sign .Pq Ql $ , these must be doubled to avoid early expansion. .Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern -Select only those words that match +Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql Oo Oc ) may be used. The wildcard characters may be escaped with a backslash .Pq Ql \e . As a consequence of the way values are split into words, matched, and then joined, a construct like .Dl ${VAR:M*} will normalize the inter-word spacing, removing all leading and trailing space, and converting multiple consecutive spaces to single spaces. . .It Cm \&:N Ns Ar pattern This is identical to .Ql Cm \&:M , but selects all words which do not match .Ar pattern . .It Cm \&:O -Order every word in variable alphabetically. +Orders every word in variable alphabetically. .It Cm \&:Or -Order every word in variable in reverse alphabetical order. +Orders every word in variable in reverse alphabetical order. .It Cm \&:Ox -Randomize words in variable. +Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion .Pq Ql Cm \&:= to prevent such behavior. For example, .Bd -literal -offset indent LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" .Ed may produce output similar to: .Bd -literal -offset indent quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre .Ed .It Cm \&:Q Quotes every shell meta-character in the variable, so that it can be passed safely to the shell. .It Cm \&:q Quotes every shell meta-character in the variable, and also doubles .Sq $ characters so that it can be passed safely through recursive invocations of .Nm . This is equivalent to: .Sq \&:S/\e\&$/&&/g:Q . .It Cm \&:R Replaces each word in the variable with everything but its suffix. .It Cm \&:range[=count] The value is an integer sequence representing the words of the original value, or the supplied .Va count . .It Cm \&:gmtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr gmtime 3 . If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:hash -Compute a 32-bit hash of the value and encode it as hex digits. +Computes a 32-bit hash of the value and encode it as hex digits. .It Cm \&:localtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr localtime 3 . If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempt to convert variable to an absolute path using +Attempts to convert variable to an absolute path using .Xr realpath 3 , if that fails, the value is unchanged. .It Cm \&:tl Converts variable to lower-case letters. .It Cm \&:ts Ns Ar c Words in the variable are normally separated by a space on expansion. This modifier sets the separator to the character .Ar c . If .Ar c is omitted, then no separator is used. -The common escapes (including octal numeric codes), work as expected. +The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW Causes the value to be treated as a single word (possibly containing embedded white space). See also .Ql Cm \&:[*] . .It Cm \&:tw Causes the value to be treated as a sequence of words delimited by white space. See also .Ql Cm \&:[@] . .Sm off .It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW .Sm on -Modify the first occurrence of +Modifies the first occurrence of .Ar old_string -in the variable's value, replacing it with +in each word of the variable's value, replacing it with .Ar new_string . If a .Ql g -is appended to the last slash of the pattern, all occurrences +is appended to the last delimiter of the pattern, all occurrences in each word are replaced. If a .Ql 1 -is appended to the last slash of the pattern, only the first word +is appended to the last delimiter of the pattern, only the first occurrence is affected. If a .Ql W -is appended to the last slash of the pattern, +is appended to the last delimiter of the pattern, then the value is treated as a single word (possibly containing embedded white space). If .Ar old_string begins with a caret .Pq Ql ^ , .Ar old_string is anchored at the beginning of each word. If .Ar old_string ends with a dollar sign .Pq Ql \&$ , it is anchored at the end of each word. Inside .Ar new_string , an ampersand .Pq Ql & is replaced by .Ar old_string (without any .Ql ^ or .Ql \&$ ) . Any character may be used as a delimiter for the parts of the modifier string. The anchoring, ampersand and delimiter characters may be escaped with a backslash .Pq Ql \e . .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string and .Ar new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign .Pq Ql \&$ , not a preceding dollar sign as is usual. .Sm off .It Cm \&:C No \&/ Ar pattern No \&/ Ar replacement No \&/ Op Cm 1gW .Sm on The .Cm \&:C modifier is just like the .Cm \&:S modifier except that the old and new strings, instead of being simple strings, are an extended regular expression (see .Xr regex 3 ) string .Ar pattern and an .Xr ed 1 Ns \-style string .Ar replacement . Normally, the first occurrence of the pattern .Ar pattern in each word of the value is substituted with .Ar replacement . The .Ql 1 modifier causes the substitution to apply to at most one word; the .Ql g modifier causes the substitution to apply to as many instances of the search pattern .Ar pattern as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word (possibly containing embedded white space). -Note that -.Ql 1 -and -.Ql g -are orthogonal; the former specifies whether multiple words are -potentially affected, the latter whether multiple substitutions can -potentially occur within each affected word. .Pp As for the .Cm \&:S modifier, the .Ar pattern and .Ar replacement are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last component. +Replaces each word in the variable with its last path component. .It Cm \&:u -Remove adjacent duplicate words (like +Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&? Ar true_string Cm \&: Ar false_string .Sm on If the variable name (not its value), when parsed as a .if conditional expression, evaluates to true, return as its value the .Ar true_string , otherwise return the .Ar false_string . Since the variable name is used as the expression, \&:\&? must be the first modifier after the variable name itself - which will, of course, usually contain variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} which actually tests defined(NUMBERS), -to determine is any words match "42" you need to use something like: +to determine if any words match "42" you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the .At V style variable substitution. It must be the last modifier specified. If .Ar old_string or .Ar new_string do not contain the pattern matching character .Ar % then it is assumed that they are anchored at the end of each word, so only suffixes or entire words may be replaced. Otherwise .Ar % 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 +all subsequent instances are treated as regular characters. .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string and .Ar new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign .Pq Ql \&$ , not a preceding dollar sign as is usual. .Sm off .It Cm \&:@ Ar temp Cm @ Ar string Cm @ .Sm on This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for -loops expansion occurs at the time of -reference. -Assign +loops, expansion occurs at the time of reference. +Assigns .Ar temp -to each word in the variable and evaluate +to each word in the variable and evaluates .Ar string . The ODE convention is that .Ar temp should start and end with a period. For example. .Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} .Pp However a single character variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] -Save the current variable value in +Saves the current variable value in .Ql $_ or the named .Va var for later reference. Example usage: .Bd -literal -offset indent M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \&\\ \\* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .Dv .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} .Ed Here .Ql $_ is used to save the result of the .Ql :S modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U Ns Ar newval -If the variable is undefined +If the variable is undefined, .Ar newval is the value. If the variable is defined, the existing value is returned. This is another ODE make feature. It is handy for setting per-target CFLAGS for instance: .Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D Ns Ar newval -If the variable is defined +If the variable is defined, .Ar newval is the value. .It Cm \&:L The name of the variable is the value. .It Cm \&:P The path of the node which has the same name as the variable is the value. If no such node exists or its path is null, then the name of the variable is used. In order for this modifier to work, the name (node) must at least have appeared on the rhs of a dependency. .Sm off .It Cm \&:\&! Ar cmd Cm \&! .Sm on The output of running .Ar cmd is the value. .It Cm \&:sh If the variable is non-empty it is run as a command and the output becomes the new value. .It Cm \&::= Ns Ar str The variable is assigned the value .Ar str after substitution. This modifier and its variations are useful in obscure situations such as wanting to set a variable when shell commands are being parsed. These assignment modifiers always expand to nothing, so if appearing in a rule line by themselves should be preceded with something to keep .Nm happy. .Pp The .Ql Cm \&:: helps avoid false matches with the .At V style .Cm \&:= modifier and since substitution always occurs the .Cm \&::= form is vaguely appropriate. .It Cm \&::?= Ns Ar str As for .Cm \&::= but only if the variable does not already have a value. .It Cm \&::+= Ns Ar str Append .Ar str to the variable. .It Cm \&::!= Ns Ar cmd Assign the output of .Ar cmd to the variable. .It Cm \&:\&[ Ns Ar range Ns Cm \&] Selects one or more words from the value, or performs other operations related to the way in which the value is divided into words. .Pp Ordinarily, a value is treated as a sequence of words delimited by white space. Some modifiers suppress this behavior, causing a value to be treated as a single word (possibly containing embedded white space). An empty value, or a value that consists entirely of white-space, is treated as a single word. For the purposes of the .Ql Cm \&:[] modifier, the words are indexed both forwards using positive integers (where index 1 represents the first word), and backwards using negative integers (where index \-1 represents the last word). .Pp The .Ar range is subjected to variable expansion, and the expanded result is then interpreted as follows: .Bl -tag -width index .\" :[n] .It Ar index Selects a single word from the value. .\" :[start..end] .It Ar start Ns Cm \&.. Ns Ar end Selects all words from .Ar start to .Ar end , inclusive. For example, .Ql Cm \&:[2..-1] selects all words from the second word to the last word. If .Ar start is greater than .Ar end , then the words are output in reverse order. For example, .Ql Cm \&:[-1..1] selects all the words from last to first. If the list is already ordered, then this effectively reverses the list, but it is more efficient to use .Ql Cm \&:Or instead of .Ql Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of \&"$*\&" in Bourne shell. .\" :[0] .It 0 Means the same as .Ql Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words delimited by white space. Analogous to the effect of \&"$@\&" in Bourne shell. .\" :[#] .It Cm \&# Returns the number of words in the value. .El \" :[range] .El .Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent +Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in .Nm . All such structures are identified by a line beginning with a single dot .Pq Ql \&. character. Files are included with either .Cm \&.include \&< Ns Ar file Ns Cm \&> or .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 the system makefile directory. If double quotes are used, the including makefile's directory and any directories specified using the .Fl I option are searched before the system makefile directory. For compatibility with other versions of .Nm .Ql include file ... is also accepted. .Pp If the include statement is written as .Cm .-include or as .Cm .sinclude then errors locating and/or opening include files are ignored. .Pp If the include statement is written as .Cm .dinclude not only are errors locating and/or opening include files ignored, but stale dependencies within the included file will be ignored just like .Va .MAKE.DEPENDFILE . .Pp Conditional expressions are also preceded by a single dot as the first character of a line. The possible conditionals are as follows: .Bl -tag -width Ds .It Ic .error Ar message The message is printed along with the name of the makefile and line number, then .Nm -will exit. +will exit immediately. .It Ic .export Ar variable ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables (those that start with .Ql \&. ) . This is not affected by the .Fl X flag, so should be used with caution. For compatibility with other .Nm programs .Ql export variable=value is also accepted. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. .It Ic .export-env Ar variable ... The same as .Ql .export , except that the variable is not appended to .Va .MAKE.EXPORTED . This allows exporting a value to the environment which is different from that used by .Nm internally. .It Ic .export-literal Ar variable ... The same as .Ql .export-env , except that variables in the value are not expanded. .It Ic .info Ar message The message is printed along with the name of the makefile and line number. .It Ic .undef Ar variable Un-define the specified global variable. Only global variables may be un-defined. .It Ic .unexport Ar variable ... The opposite of .Ql .export . The specified global .Va variable will be removed from .Va .MAKE.EXPORTED . If no variable list is provided, all globals are unexported, and .Va .MAKE.EXPORTED deleted. .It Ic .unexport-env Unexport all globals previously exported and clear the environment inherited from the parent. This operation will cause a memory leak of the original environment, so should be used sparingly. Testing for .Va .MAKE.LEVEL being 0, would make sense. Also note that any variables which originated in the parent environment should be explicitly preserved if desired. For example: .Bd -literal -offset indent .Li .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .Li .unexport-env .Li .export PATH .Li .endif .Pp .Ed Would result in an environment containing only .Ql Ev PATH , which is the minimal useful environment. Actually .Ql Ev .MAKE.LEVEL will also be pushed into the new environment. .It Ic .warning Ar message The message prefixed by .Ql Pa warning: is printed along with the name of the makefile and line number. .It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... Test the value of an expression. .It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... Test the value of a variable. .It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... Test the value of a variable. .It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... Test the target being built. .It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... Test the target being built. .It Ic .else Reverse the sense of the last conditional. .It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... A combination of .Ql Ic .else followed by .Ql Ic .if . .It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifdef . .It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifndef . .It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifmake . .It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifnmake . .It Ic .endif End the body of the conditional. .El .Pp The .Ar operator may be any one of the following: .Bl -tag -width "Cm XX" .It Cm \&|\&| Logical OR. .It Cm \&&& Logical .Tn AND ; of higher precedence than .Dq \&|\&| . .El .Pp As in C, .Nm will only evaluate a conditional as far as is necessary to determine its value. Parentheses may be used to change the order of evaluation. The boolean operator .Ql Ic \&! may be used to logically negate an entire conditional. It is of higher precedence than .Ql Ic \&&& . .Pp The value of .Ar expression may be any of the following: .Bl -tag -width defined .It Ic defined Takes a variable name as an argument and evaluates to true if the variable has been defined. .It Ic make Takes a target name as an argument and evaluates to true if the target was specified as part of .Nm Ns 's command line or was declared the default target (either implicitly or explicitly, see .Va .MAIN ) before the line containing the conditional. .It Ic empty Takes a variable, with possible modifiers, and evaluates to true if the expansion of the variable would result in an empty string. .It Ic exists Takes a file name as an argument and evaluates to true if the file exists. The file is searched for on the system search path (see .Va .PATH ) . .It Ic target Takes a target name as an argument and evaluates to true if the target has been defined. .It Ic commands Takes a target name as an argument and evaluates to true if the target has been defined and has commands associated with it. .El .Pp .Ar Expression may also be an arithmetic or string comparison. Variable expansion is performed on both sides of the comparison, after which the integral values are compared. A value is interpreted as hexadecimal if it is preceded by 0x, otherwise it is decimal; octal numbers are not supported. The standard C relational operators are all supported. If after variable expansion, either the left or right hand side of a .Ql Ic == or .Ql Ic "!=" operator is not an integral value, then string comparison is performed between the expanded variables. If no relational operator is given, it is assumed that the expanded -variable is being compared against 0 or an empty string in the case +variable is being compared against 0, or an empty string in the case of a string comparison. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters a (white-space separated) word it doesn't recognize, either the .Dq make or .Dq defined expression is applied to it, depending on the form of the conditional. If the form is .Ql Ic .ifdef , .Ql Ic .ifndef , or .Ql Ic .if the .Dq defined expression is applied. Similarly, if the form is .Ql Ic .ifmake or .Ql Ic .ifnmake , the .Dq make expression is applied. .Pp If the conditional evaluates to true the parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a .Ql Ic .else or .Ql Ic .endif is found. .Pp For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds .It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-rules +.It Aq make-lines .It Ic \&.endfor .El .Pp After the for .Ic expression is evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each .Ic variable , in order, and these .Ic variables are substituted into the -.Ic make-rules +.Ic make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. .Sh COMMENTS Comments begin with a hash .Pq Ql \&# character, anywhere but in a shell command line, and continue to the end of an unescaped new line. .Sh SPECIAL SOURCES (ATTRIBUTES) .Bl -tag -width .IGNOREx .It Ic .EXEC Target is never out of date, but always execute commands anyway. .It Ic .IGNORE Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . .\" .It Ic .INVISIBLE .\" XXX .\" .It Ic .JOIN .\" XXX .It Ic .MADE Mark all sources of this target as being up-to-date. .It Ic .MAKE Execute the commands associated with this target even if the .Fl n or .Fl t options were specified. Normally used to mark recursive .Nm Ns s . .It Ic .META Create a meta file for the target, even if it is flagged as .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL . Usage in conjunction with .Ic .MAKE is the most likely case. In "meta" mode, the target is out-of-date if the meta file is missing. .It Ic .NOMETA Do not create a meta file for the target. Meta files are also not created for .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL targets. .It Ic .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which always changes. If the number of commands change, though, the target will still be out of date. The same effect applies to any command line that uses the variable .Va .OODATE , which can be used for that purpose even when not otherwise needed or desired: .Bd -literal -offset indent skip-compare-for-some: @echo this will be compared @echo this will not ${.OODATE:M.NOMETA_CMP} @echo this will also be compared .Ed The .Cm \&:M pattern suppresses any expansion of the unwanted variable. .It Ic .NOPATH Do not search for the target in the directories specified by .Ic .PATH . .It Ic .NOTMAIN Normally .Nm selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .It Ic .OPTIONAL If a target is marked with this attribute and .Nm can't figure out how to create it, it will ignore this fact and assume the file isn't needed or already exists. .It Ic .PHONY The target does not correspond to an actual file; it is always considered to be out of date, and will not be created with the .Fl t option. Suffix-transformation rules are not applied to .Ic .PHONY targets. .It Ic .PRECIOUS When .Nm is interrupted, it normally removes any partially made targets. This source prevents the target from being removed. .It Ic .RECURSIVE Synonym for .Ic .MAKE . .It Ic .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign .Pq Ql @ . .It Ic .USE Turn the target into .Nm Ns 's version of a macro. When the target is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .Ic .USE ) of the source. If the target already has commands, the .Ic .USE target's commands are appended to them. .It Ic .USEBEFORE Exactly like .Ic .USE , but prepend the .Ic .USEBEFORE target commands to the target. .It Ic .WAIT If .Ic .WAIT appears in a dependency line, the sources that precede it are made before the sources that succeed it in the line. Since the dependents of files are not made until the file itself could be made, this also stops the dependents being built unless they are needed for another branch of the dependency tree. So given: .Bd -literal x: a .WAIT b echo x a: echo a b: b1 echo b b1: echo b1 .Ed the output is always .Ql a , .Ql b1 , .Ql b , .Ql x . .br The ordering imposed by .Ic .WAIT is only relevant for parallel makes. .El .Sh SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .Bl -tag -width .BEGINx .It Ic .BEGIN Any command lines attached to this target are executed before anything else is done. .It Ic .DEFAULT This is sort of a .Ic .USE rule for any target (that was used only as a source) that .Nm can't figure out any other way to create. Only the shell script is used. The .Ic .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's commands is set to the target's own name. .It Ic .DELETE_ON_ERROR If this target is present in the makefile, it globally causes make to delete targets whose commands fail. (By default, only targets whose commands are interrupted during execution are deleted. This is the historical behavior.) This setting can be used to help prevent half-finished or malformed targets from being left around and corrupting future rebuilds. .It Ic .END Any command lines attached to this target are executed after everything else is done. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The .Ic .ERROR_TARGET variable is set to the target that failed. See also .Ic MAKE_PRINT_VAR_ON_ERROR . .It Ic .IGNORE Mark each of the sources with the .Ic .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the .Fl i option. .It Ic .INTERRUPT If .Nm is interrupted, the commands for this target will be executed. .It Ic .MAIN If no target is specified when .Nm is invoked, this target will be built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm when the makefile is used. The flags are as if typed to the shell, though the .Fl f option will have no effect. .\" XXX: NOT YET!!!! .\" .It Ic .NOTPARALLEL .\" The named targets are executed in non parallel mode. .\" If no targets are .\" specified, then all targets are executed in non parallel mode. .It Ic .NOPATH Apply the .Ic .NOPATH attribute to any specified sources. .It Ic .NOTPARALLEL Disable parallel mode. .It Ic .NO_PARALLEL Synonym for .Ic .NOTPARALLEL , for compatibility with other pmake variants. .It Ic .OBJDIR The source is a new value for .Ql Va .OBJDIR . If it exists, .Nm will .Xr chdir 2 to it and update the value of .Ql Va .OBJDIR . .It Ic .ORDER The named targets are made in sequence. This ordering does not add targets to the list of targets to be made. Since the dependents of a target do not get built until the target itself could be built, unless .Ql a is built by another part of the dependency graph, the following is a dependency loop: .Bd -literal \&.ORDER: b a b: a .Ed .Pp The ordering imposed by .Ic .ORDER is only relevant for parallel makes. .\" XXX: NOT YET!!!! .\" .It Ic .PARALLEL .\" The named targets are executed in parallel mode. .\" If no targets are .\" specified, then all targets are executed in parallel mode. .It Ic .PATH The sources are directories which are to be searched for files not found in the current directory. If no sources are specified, any previously specified directories are deleted. If the source is the special .Ic .DOTLAST target, then the current working directory is searched last. .It Ic .PATH. Ns Va suffix Like .Ic .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .Ic .SUFFIXES . .It Ic .PHONY Apply the .Ic .PHONY attribute to any specified sources. .It Ic .PRECIOUS Apply the .Ic .PRECIOUS attribute to any specified sources. If no sources are specified, the .Ic .PRECIOUS attribute is applied to every target in the file. .It Ic .SHELL Sets the shell that .Nm will use to execute commands. The sources are a set of .Ar field=value pairs. .Bl -tag -width hasErrCtls .It Ar name This is the minimal specification, used to select one of the built-in shell specs; .Ar sh , .Ar ksh , and .Ar csh . .It Ar path Specifies the path to the shell. .It Ar hasErrCtl Indicates whether the shell supports exit on error. .It Ar check The command to turn on error checking. .It Ar ignore The command to disable error checking. .It Ar echo The command to turn on echoing of commands executed. .It Ar quiet The command to turn off echoing of commands executed. .It Ar filter The output to filter after issuing the .Ar quiet command. It is typically identical to .Ar quiet . .It Ar errFlag The flag to pass the shell to enable error checking. .It Ar echoFlag The flag to pass the shell to enable command echoing. .It Ar newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. .El Example: .Bd -literal \&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e check="set \-e" ignore="set +e" \e echo="set \-v" quiet="set +v" filter="set +v" \e echoFlag=v errFlag=e newline="'\en'" .Ed .It Ic .SILENT Apply the .Ic .SILENT attribute to any specified sources. If no sources are specified, the .Ic .SILENT attribute is applied to every command in the file. .It Ic .STALE This target gets run when a dependency file contains stale entries, having .Va .ALLSRC set to the name of that dependency file. .It Ic .SUFFIXES Each source specifies a suffix to .Nm . If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. .Pp Example: .Bd -literal \&.SUFFIXES: .o \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed .El .Sh ENVIRONMENT .Nm uses the following environment variables, if they exist: .Ev MACHINE , .Ev MACHINE_ARCH , .Ev MAKE , .Ev MAKEFLAGS , .Ev MAKEOBJDIR , .Ev MAKEOBJDIRPREFIX , .Ev MAKESYSPATH , .Ev PWD , and .Ev TMPDIR . .Pp .Ev MAKEOBJDIRPREFIX and .Ev MAKEOBJDIR may only be set in the environment or on the command line to .Nm and not as makefile variables; see the description of .Ql Va .OBJDIR for more details. .Sh FILES .Bl -tag -width /usr/share/mk -compact .It .depend list of dependencies .It Makefile list of dependencies .It makefile list of dependencies .It sys.mk system makefile .It /usr/share/mk system makefile directory .El .Sh COMPATIBILITY The basic make syntax is compatible between different versions of make; however the special variables, variable modifiers and conditionals are not. .Ss Older versions An incomplete list of changes in older versions of .Nm : .Pp The way that .for loop variables are substituted changed after NetBSD 5.0 so that they still appear to be variable expansions. In particular this stops them being treated as syntax, and removes some obscure problems using them in .if statements. .Pp The way that parallel makes are scheduled changed in NetBSD 4.0 so that .ORDER and .WAIT apply recursively to the dependent nodes. The algorithms used may change again in the future. .Ss Other make dialects Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not support most of the features of .Nm as described in this manual. Most notably: .Bl -bullet -offset indent .It The .Ic .WAIT and .Ic .ORDER declarations and most functionality pertaining to parallelization. (GNU make supports parallelization but lacks these features needed to control it effectively.) .It Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) .It All built-in variables that begin with a dot. .It Most of the special sources and targets that begin with a dot, with the notable exception of .Ic .PHONY , .Ic .PRECIOUS , and .Ic .SUFFIXES . .It Variable modifiers, except for the .Dl :old=new string substitution, which does not portably support globbing with .Ql % and historically only works on declared suffixes. .It The .Ic $> variable even in its short form; most makes support this functionality but its name varies. .El .Pp Some features are somewhat more portable, such as assignment with .Ic += , .Ic ?= , and .Ic != . The .Ic .PATH functionality is based on an older feature .Ic VPATH found in GNU make and many versions of SVR4 make; however, historically its behavior is too ill-defined (and too buggy) to rely upon. .Pp The .Ic $@ and .Ic $< variables are more or less universally portable, as is the .Ic $(MAKE) variable. Basic use of suffix rules (for files only in the current directory, not trying to chain transformations together, etc.) is also reasonably portable. .Sh SEE ALSO .Xr mkdep 1 .Sh HISTORY .Nm is derived from NetBSD .Xr make 1 . It uses autoconf to facilitate portability to other platforms. .Pp A make command appeared in .At v7 . This make implementation is based on Adam De Boor's pmake program which was written for Sprite at Berkeley. It was designed to be a parallel distributed make running jobs on different machines using a daemon called .Dq customs . .Pp Historically the target/dependency .Dq FRC has been used to FoRCe rebuilding (since the target/dependency does not exist... unless someone creates an .Dq FRC file). .Sh BUGS The make 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 make just counts {} and () in order to find the end of a variable expansion. .Pp There is no way of escaping a space character in a filename. Index: head/contrib/bmake/bmake.cat1 =================================================================== --- head/contrib/bmake/bmake.cat1 (revision 365365) +++ head/contrib/bmake/bmake.cat1 (revision 365366) @@ -1,1559 +1,1571 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) NAME bmake -- maintain program dependencies SYNOPSIS bmake [-BeikNnqrstWwX] [-C directory] [-D variable] [-d flags] [-f makefile] [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file] [-V variable] [-v variable] [variable=value] [target ...] DESCRIPTION bmake is a program designed to simplify the maintenance of other pro- grams. Its input is a list of specifications as to the files upon which programs and other files depend. If no -f makefile makefile option is given, bmake will try to open `makefile' then `Makefile' in order to find the specifications. If the file `.depend' exists, it is read (see mkdep(1)). This manual page is intended as a reference document only. For a more thorough description of bmake and makefiles, please refer to PMake - A Tutorial. bmake will prepend the contents of the MAKEFLAGS environment variable to the command line arguments before parsing them. The options are as follows: -B Try to be backwards compatible by executing a single shell per command and by executing the commands to make the sources of a dependency line in sequence. -C directory Change to directory before reading the makefiles or doing any- thing else. If multiple -C options are specified, each is inter- preted relative to the previous one: -C / -C etc is equivalent to -C /etc. -D variable Define variable to be 1, in the global context. -d [-]flags Turn on debugging, and specify which portions of bmake are to print debugging information. Unless the flags are preceded by `-' they are added to the MAKEFLAGS environment variable and will be processed by any child make processes. By default, debugging information is printed to standard error, but this can be changed using the F debugging flag. The debugging output is always unbuffered; in addition, if debugging is enabled but debugging output is not directed to standard output, then the standard out- put is line buffered. Flags is one or more of the following: A Print all possible debugging information; equivalent to specifying all of the debugging flags. a Print debugging information about archive searching and caching. C Print debugging information about current working direc- tory. c Print debugging information about conditional evaluation. d Print debugging information about directory searching and caching. e Print debugging information about failed commands and targets. F[+]filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the argument. If the character immediately after the `F' flag is `+', then the file will be opened in append mode; otherwise the file will be overwritten. If the file name is `stdout' or `stderr' then debugging output will be written to the standard output or standard error output file descriptors respectively (and the `+' option has no effect). Otherwise, the output will be written to the named file. If the file name ends `.%d' then the `%d' is replaced by the pid. f Print debugging information about loop evaluation. g1 Print the input graph before making anything. g2 Print the input graph after making everything, or before exiting on error. g3 Print the input graph before exiting on error. + h Print debugging information about hash table operations. + j Print debugging information about running multiple shells. + L Turn on lint checks. This will throw errors for variable + assignments that do not parse correctly, at the time of + assignment so the file and line number are available. + l Print commands in Makefiles regardless of whether or not they are prefixed by `@' or other "quiet" flags. Also known as "loud" behavior. M Print debugging information about "meta" mode decisions about targets. m Print debugging information about making targets, includ- ing modification dates. n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in the directory referred to by the TMPDIR environment vari- able, or in /tmp if TMPDIR is unset or set to the empty string. The temporary scripts are created by mkstemp(3), and have names of the form makeXXXXXX. NOTE: This can create many files in TMPDIR or /tmp, so use with care. p Print debugging information about makefile parsing. s Print debugging information about suffix-transformation rules. t Print debugging information about target list mainte- nance. V Force the -V option to print raw values of variables, overriding the default behavior set via .MAKE.EXPAND_VARIABLES. v Print debugging information about variable assignment. x Run shell commands with -x so the actual commands are printed as they are executed. -e Specify that environment variables override macro assignments within makefiles. -f makefile Specify a makefile to read instead of the default `makefile'. If makefile is `-', standard input is read. Multiple makefiles may be specified, and are read in the order specified. -I directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the -m option) is automatically included as part of this list. -i Ignore non-zero exit of shell commands in the makefile. Equiva- lent to specifying `-' before each command line in the makefile. -J private This option should not be specified by the user. When the j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. -j max_jobs Specify the maximum number of jobs that bmake may have running at any one time. The value is saved in .MAKE.JOBS. Turns compati- bility mode off, unless the B flag is also specified. When com- patibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the tradi- tional one shell invocation per line. This can break traditional scripts which change directories on each command invocation and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. -k Continue processing after errors are encountered, but only on those targets that do not depend on the target whose creation caused the error. -m directory Specify a directory in which to search for sys.mk and makefiles included via the <file>-style include statement. The -m option can be used multiple times to form a search path. This path will override the default system include path: /usr/share/mk. Fur- thermore the system include path will be appended to the search path used for "file"-style include statements (see the -I option). If a file or directory name in the -m argument (or the MAKESYSPATH environment variable) starts with the string ".../" then bmake will search for the specified file or directory named in the remaining part of the argument string. The search starts with the current directory of the Makefile and then works upward towards the root of the file system. If the search is success- ful, then the resulting directory replaces the ".../" specifica- tion in the -m argument. If used, this feature allows bmake to easily search in the current source tree for customized sys.mk files (e.g., by using ".../mk/sys.mk" as an argument). -n Display the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE spe- - cial source (see below). + cial source (see below) or the command is prefixed with `+'. -N Display the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles without descending into subdirectories. -q Do not execute any commands, but exit 0 if the specified targets are up-to-date and 1, otherwise. -r Do not use the built-in rules specified in the system makefile. -s Do not echo any commands as they are executed. Equivalent to specifying `@' before each command line in the makefile. -T tracefile When used with the -j flag, append a trace record to tracefile for each job started and completed. -t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up- to-date. -V variable Print the value of variable. Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or unde- fined variable. The value printed is extracted from the global context after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded vari- able references) are shown. If variable contains a `$' then the value will be recursively expanded to its complete resultant text before printing. The expanded value will also be printed if .MAKE.EXPAND_VARIABLES is set to true and the -dV option has not been used to override it. Note that loop-local and target-local variables, as well as values taken temporarily by global vari- ables during makefile processing, are not accessible via this option. The -dv debug mode can be used to see these at the cost of generating substantial extraneous output. -v variable Like -V but the variable is always expanded to its complete value. -W Treat any warnings during makefile parsing as errors. -w Print entering and leaving directory messages, pre and post pro- cessing. -X Don't export variables passed on the command line to the environ- ment individually. Variables passed on the command line are still exported via the MAKEFLAGS environment variable. This option may be useful on systems which have a small limit on the size of command arguments. variable=value Set the value of the variable variable to value. Normally, all values passed on the command line are also exported to sub-makes in the environment. The -X flag disables this behavior. Vari- able assignments should follow options for POSIX compatibility but no ordering is enforced. There are seven different types of lines in a makefile: file dependency specifications, shell commands, variable assignments, include statements, conditional directives, for loops, and comments. In general, lines may be continued from one line to the next by ending them with a backslash (`\'). The trailing newline character and initial whitespace on the following line are compressed into a single space. FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets ``depend'' - on the sources and are usually created from them. The exact relationship - between the target and the source is determined by the operator that sep- - arates them. The three operators are as follows: + on the sources and are customarily created from them. A target is con- + sidered out-of-date if it does not exist, or if its modification time is + less than that of any of its sources. An out-of-date target will be re- + created, but not until all sources have been examined and themselves re- + created as needed. Three operators may be used: - : A target is considered out-of-date if its modification time is less - than those of any of its sources. Sources for a target accumulate - over dependency lines when this operator is used. The target is - removed if bmake is interrupted. + : Many dependency lines may name this target but only one may have + attached shell commands. All sources named in all dependency lines + are considered together, and if needed the attached shell commands + are run to create or re-create the target. If bmake is inter- + rupted, the target is removed. - ! Targets are always re-created, but not until all sources have been - examined and re-created as necessary. Sources for a target accumu- - late over dependency lines when this operator is used. The target - is removed if bmake is interrupted. + ! The same, but the target is always re-created whether or not it is + out of date. - :: If no sources are specified, the target is always re-created. Oth- - erwise, a target is considered out-of-date if any of its sources - has been modified more recently than the target. Sources for a - target do not accumulate over dependency lines when this operator - is used. The target will not be removed if bmake is interrupted. + :: Any dependency line may have attached shell commands, but each one + is handled independently: its sources are considered and the + attached shell commands are run if the target is out of date with + respect to (only) those sources. Thus, different groups of the + attached shell commands may be run depending on the circumstances. + Furthermore, unlike :, for dependency lines with no sources, the + attached shell commands are always run. Also unlike :, the target + will not be removed if bmake is interrupted. + All dependency lines mentioning a particular target must use the same + operator. Targets and sources may contain the shell wildcard values `?', `*', `[]', and `{}'. The values `?', `*', and `[]' may only be used as part of the final component of the target or source, and must be used to describe existing files. The value `{}' need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. SHELL COMMANDS Each target may have associated with it one or more lines of shell com- mands, normally used to create the target. Each of the lines in this script must be preceded by a tab. (For historical reasons, spaces are not accepted.) While targets can appear in many dependency lines if desired, by default only one of these rules may be followed by a creation script. If the `::' operator is used, however, all rules may include scripts and the scripts are executed in the order found. Each line is treated as a separate shell command, unless the end of line is escaped with a backslash (`\') in which case that line and the next are combined. If the first characters of the command are any combination of `@', `+', or `-', the command is treated specially. A `@' causes the command not to be echoed before it is executed. A `+' causes the command to be executed even when -n is given. This is similar to the effect of the .MAKE special source, except that the effect can be limited to a sin- gle line of a script. A `-' in compatibility mode causes any non-zero exit status of the command line to be ignored. When bmake is run in jobs mode with -j max_jobs, the entire script for the target is fed to a single instance of the shell. In compatibility (non-jobs) mode, each command is run in a separate process. If the com- mand contains any shell meta characters (`#=|^(){};&<>*?[]:$`\\n') it will be passed to the shell; otherwise bmake will attempt direct execu- tion. If a line starts with `-' and the shell has ErrCtl enabled then failure of the command line will be ignored as in compatibility mode. Otherwise `-' affects the entire job; the script will stop at the first command line that fails, but the target will not be deemed to have failed. Makefiles should be written so that the mode of bmake operation does not change their behavior. For example, any command which needs to use ``cd'' or ``chdir'' without potentially changing the directory for subse- quent commands should be put in parentheses so it executes in a subshell. To force the use of one shell, escape the line breaks so as to make the whole script one command. For example: avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \ (cd ${.CURDIR} && ${MAKE} $@); \ echo Back in `pwd` Since bmake will chdir(2) to `.OBJDIR' before executing any targets, each child process starts with that as its current working directory. VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradi- tion, consist of all upper-case letters. Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: = Assign the value to the variable. Any previous value is overrid- den. += Append the value to the current value of the variable. ?= Assign the value to the variable if it is not already defined. := Assign with expansion, i.e. expand the value before assigning it to the variable. Normally, expansion is not done until the vari- able is referenced. NOTE: References to undefined variables are not expanded. This can cause problems when variable modifiers are used. != Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. Any white-space before the assigned value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. Variables are expanded by surrounding the variable name with either curly braces (`{}') or parentheses (`()') and preceding it with a dollar sign (`$'). If the variable name contains only a single letter, the surround- ing braces or parentheses are not required. This shorter form is not recommended. If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names con- - taining dollar, braces, parenthesis, or whitespace are really best + taining dollar, braces, parentheses, or whitespace are really best avoided! If the result of expanding a variable contains a dollar sign (`$') the string is expanded again. Variable substitution occurs at three distinct times, depending on where the variable is being used. 1. Variables in dependency lines are expanded as the line is read. 2. Variables in shell commands are expanded when the shell command is executed. 3. ``.for'' loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the fol- lowing example code: .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .endfor all: @echo ${a} @echo ${b} will print: 1 2 3 3 3 3 Because while ${a} contains ``1 2 3'' after the loop is executed, ${b} contains ``${j} ${j} ${j}'' which expands to ``3 3 3'' since after the loop completes ${j} contains ``3''. Variable classes The four different classes of variables (in order of increasing prece- dence) are: Environment variables Variables defined as part of bmake's environment. Global variables Variables defined in the makefile or in included makefiles. Command line variables Variables defined as part of the command line. Local variables Variables that are defined specific to a certain target. Local variables are all built in and their values vary magically from target to target. It is not currently possible to define new local vari- ables. The seven local variables are as follows: .ALLSRC The list of all sources for this target; also known as `>'. .ARCHIVE The name of the archive file; also known as `!'. .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the ``implied'' source); also known as `<'. It is not defined in explicit rules. .MEMBER The name of the archive member; also known as `%'. .OODATE The list of sources for this target that were deemed out- of-date; also known as `?'. .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as `*'. The suffix must be one of the known suffixes declared with .SUFFIXES or it will not be recog- nized. .TARGET The name of the target; also known as `@'. For compati- bility with other makes this is an alias for .ARCHIVE in archive member rules. The shorter forms (`>', `!', `<', `%', `?', `*', and `@') are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. Variants of these variables with the punctuation followed immediately by `D' or `F', e.g. `$(@D)', are legacy forms equivalent to using the `:H' and `:T' modifiers. These forms are accepted for compatibility with AT&T System V UNIX makefiles and POSIX but are not recommended. Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. These variables are `.TARGET', `.PREFIX', `.ARCHIVE', and `.MEMBER'. Additional built-in variables In addition, bmake sets or knows about the following variables: $ A single dollar sign `$', i.e. `$$' expands to a single dollar sign. .ALLTARGETS The list of all targets encountered in the Makefile. If evaluated during Makefile parsing, lists only those tar- gets encountered thus far. .CURDIR A path to the directory where bmake was executed. Refer to the description of `PWD' for more details. .INCLUDEDFROMDIR The directory of the file this Makefile was included from. .INCLUDEDFROMFILE The filename of the file this Makefile was included from. MAKE The name that bmake was executed with (argv[0]). For compatibility bmake also sets .MAKE with the same value. The preferred variable to use is the environment variable MAKE because it is more compatible with other versions of bmake and cannot be confused with the special target with the same name. .MAKE.DEPENDFILE Names the makefile (default `.depend') from which gener- ated dependencies are read. .MAKE.EXPAND_VARIABLES A boolean that controls the default behavior of the -V option. If true, variable values printed with -V are fully expanded; if false, the raw variable contents (which may include additional unexpanded variable refer- ences) are shown. .MAKE.EXPORTED The list of variables exported by bmake. .MAKE.JOBS The argument to the -j option. .MAKE.JOB.PREFIX If bmake is run with j then output for each target is prefixed with a token `--- target ---' the first part of which can be controlled via .MAKE.JOB.PREFIX. If .MAKE.JOB.PREFIX is empty, no token is printed. For example: .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like `---make[1234] target ---' mak- ing it easier to track the degree of parallelism being achieved. MAKEFLAGS The environment variable `MAKEFLAGS' may contain anything that may be specified on bmake's command line. Anything specified on bmake's command line is appended to the `MAKEFLAGS' variable which is then entered into the envi- ronment for all programs which bmake executes. .MAKE.LEVEL The recursion depth of bmake. The initial instance of bmake will be 0, and an incremented value is put into the environment to be seen by the next generation. This allows tests like: .if ${.MAKE.LEVEL} == 0 to protect things which should only be evaluated in the initial instance of bmake. .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default `makefile', `Makefile') that bmake will look for. .MAKE.MAKEFILES The list of makefiles read by bmake, which is useful for tracking dependencies. Each makefile is recorded only once, regardless of the number of times read. .MAKE.MODE Processed after reading all makefiles. Can affect the mode that bmake runs in. It can contain a number of key- words: compat Like -B, puts bmake into "compat" mode. meta Puts bmake into "meta" mode, where meta files are created for each tar- get to capture the command run, the output generated and if filemon(4) is available, the system calls which are of interest to bmake. The cap- tured output can be very useful when diagnosing errors. curdirOk= bf Normally bmake will not create .meta files in `.CURDIR'. This can be overridden by setting bf to a value which represents True. missing-meta= bf If bf is True, then a missing .meta file makes the target out-of-date. missing-filemon= bf If bf is True, then missing filemon data makes the target out-of-date. nofilemon Do not use filemon(4). env For debugging, it can be useful to include the environment in the .meta file. verbose If in "meta" mode, print a clue about the target being built. This is useful if the build is otherwise running silently. The message printed the value of: .MAKE.META.PREFIX. ignore-cmd Some makefiles have commands which are simply not stable. This keyword causes them to be ignored for deter- mining whether a target is out of date in "meta" mode. See also .NOMETA_CMP. silent= bf If bf is True, when a .meta file is created, mark the target .SILENT. .MAKE.META.BAILIWICK In "meta" mode, provides a list of prefixes which match the directories controlled by bmake. If a file that was generated outside of .OBJDIR but within said bailiwick is missing, the current target is considered out-of-date. .MAKE.META.CREATED In "meta" mode, this variable contains a list of all the meta files updated. If not empty, it can be used to trigger processing of .MAKE.META.FILES. .MAKE.META.FILES In "meta" mode, this variable contains a list of all the meta files used (updated or not). This list can be used to process the meta files to extract dependency informa- tion. .MAKE.META.IGNORE_PATHS Provides a list of path prefixes that should be ignored; because the contents are expected to change over time. The default list includes: `/dev /etc /proc /tmp /var/run /var/tmp' .MAKE.META.IGNORE_PATTERNS Provides a list of patterns to match against pathnames. Ignore any that match. .MAKE.META.IGNORE_FILTER Provides a list of variable modifiers to apply to each pathname. Ignore if the expansion is an empty string. .MAKE.META.PREFIX Defines the message printed for each meta file updated in "meta verbose" mode. The default value is: Building ${.TARGET:H:tA}/${.TARGET:T} .MAKEOVERRIDES This variable is used to record the names of variables assigned to on the command line, so that they may be exported as part of `MAKEFLAGS'. This behavior can be disabled by assigning an empty value to `.MAKEOVERRIDES' within a makefile. Extra variables can be exported from a makefile by appending their names to `.MAKEOVERRIDES'. `MAKEFLAGS' is re-exported whenever `.MAKEOVERRIDES' is modified. .MAKE.PATH_FILEMON If bmake was built with filemon(4) support, this is set to the path of the device node. This allows makefiles to test for this support. .MAKE.PID The process-id of bmake. .MAKE.PPID The parent process-id of bmake. .MAKE.SAVE_DOLLARS value should be a boolean that controls whether `$$' are preserved when doing `:=' assignments. The default is false, for backwards compatibility. Set to true for com- patability with other makes. If set to false, `$$' becomes `$' per normal evaluation rules. MAKE_PRINT_VAR_ON_ERROR When bmake stops due to an error, it sets `.ERROR_TARGET' to the name of the target that failed, `.ERROR_CMD' to the commands of the failed target, and in "meta" mode, it also sets `.ERROR_CWD' to the getcwd(3), and `.ERROR_META_FILE' to the path of the meta file (if any) describing the failed target. It then prints its name and the value of `.CURDIR' as well as the value of any variables named in `MAKE_PRINT_VAR_ON_ERROR'. .newline This variable is simply assigned a newline character as its value. This allows expansions using the :@ modifier to put a newline between iterations of the loop rather than a space. For example, the printing of `MAKE_PRINT_VAR_ON_ERROR' could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. .OBJDIR A path to the directory where the targets are built. Its value is determined by trying to chdir(2) to the follow- ing directories in order and using the first match: 1. ${MAKEOBJDIRPREFIX}${.CURDIR} (Only if `MAKEOBJDIRPREFIX' is set in the environ- ment or on the command line.) 2. ${MAKEOBJDIR} (Only if `MAKEOBJDIR' is set in the environment or on the command line.) 3. ${.CURDIR}/obj.${MACHINE} 4. ${.CURDIR}/obj 5. /usr/obj/${.CURDIR} 6. ${.CURDIR} Variable expansion is performed on the value before it's used, so expressions such as ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with `MAKEOBJDIR'. `.OBJDIR' may be modified in the makefile via the special target `.OBJDIR'. In all cases, bmake will chdir(2) to the specified directory if it exists, and set `.OBJDIR' and `PWD' to that directory before executing any targets. .PARSEDIR A path to the directory of the current `Makefile' being parsed. .PARSEFILE The basename of the current `Makefile' being parsed. This variable and `.PARSEDIR' are both set only while the `Makefiles' are being parsed. If you want to retain their current values, assign them to a variable using assignment with expansion: (`:='). .PATH A variable that represents the list of directories that bmake will search for files. The search list should be updated using the target `.PATH' rather than the vari- able. PWD Alternate path to the current directory. bmake normally sets `.CURDIR' to the canonical path given by getcwd(3). However, if the environment variable `PWD' is set and gives a path to the current directory, then bmake sets `.CURDIR' to the value of `PWD' instead. This behavior is disabled if `MAKEOBJDIRPREFIX' is set or `MAKEOBJDIR' contains a variable transform. `PWD' is set to the value of `.OBJDIR' for all programs which bmake executes. + .SHELL The pathname of the shell used to run target scripts. It + is read-only. + .TARGETS The list of targets explicitly specified on the command line, if any. VPATH Colon-separated (``:'') lists of directories that bmake will search for files. The variable is supported for compatibility with old make programs only, use `.PATH' instead. Variable modifiers Variable expansion may be modified to select or modify each word of the variable (where a ``word'' is white-space delimited sequence of charac- ters). The general format of a variable expansion is as follows: ${variable[:modifier[:...]]} Each modifier begins with a colon, which may be escaped with a backslash (`\'). A set of modifiers can be specified via a variable, as follows: modifier_variable=modifier[:...] ${variable:${modifier_variable}[:...]} In this case the first modifier in the modifier_variable does not start with a colon, since that must appear in the referencing variable. If any of the modifiers in the modifier_variable contain a dollar sign (`$'), these must be doubled to avoid early expansion. The supported modifiers are: :E Replaces each word in the variable with its suffix. :H Replaces each word in the variable with everything but the last com- ponent. :Mpattern - Select only those words that match pattern. The standard shell + Selects only those words that match pattern. The standard shell wildcard characters (`*', `?', and `[]') may be used. The wildcard characters may be escaped with a backslash (`\'). As a consequence of the way values are split into words, matched, and then joined, a construct like ${VAR:M*} will normalize the inter-word spacing, removing all leading and trailing space, and converting multiple consecutive spaces to single spaces. :Npattern This is identical to `:M', but selects all words which do not match pattern. - :O Order every word in variable alphabetically. + :O Orders every word in variable alphabetically. - :Or Order every word in variable in reverse alphabetical order. + :Or Orders every word in variable in reverse alphabetical order. - :Ox Randomize words in variable. The results will be different each + :Ox Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion (`:=') to prevent such behavior. For example, LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" may produce output similar to: quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre :Q Quotes every shell meta-character in the variable, so that it can be passed safely to the shell. :q Quotes every shell meta-character in the variable, and also doubles `$' characters so that it can be passed safely through recursive invocations of bmake. This is equivalent to: `:S/\$/&&/g:Q'. :R Replaces each word in the variable with everything but its suffix. :range[=count] The value is an integer sequence representing the words of the orig- inal value, or the supplied count. :gmtime[=utc] The value is a format string for strftime(3), using gmtime(3). If a utc value is not provided or is 0, the current time is used. :hash - Compute a 32-bit hash of the value and encode it as hex digits. + Computes a 32-bit hash of the value and encode it as hex digits. :localtime[=utc] The value is a format string for strftime(3), using localtime(3). If a utc value is not provided or is 0, the current time is used. - :tA Attempt to convert variable to an absolute path using realpath(3), + :tA Attempts to convert variable to an absolute path using realpath(3), if that fails, the value is unchanged. :tl Converts variable to lower-case letters. :tsc Words in the variable are normally separated by a space on expan- sion. This modifier sets the separator to the character c. If c is omitted, then no separator is used. The common escapes (including - octal numeric codes), work as expected. + octal numeric codes) work as expected. :tu Converts variable to upper-case letters. :tW Causes the value to be treated as a single word (possibly containing embedded white space). See also `:[*]'. :tw Causes the value to be treated as a sequence of words delimited by white space. See also `:[@]'. :S/old_string/new_string/[1gW] - Modify the first occurrence of old_string in the variable's value, - replacing it with new_string. If a `g' is appended to the last - slash of the pattern, all occurrences in each word are replaced. If - a `1' is appended to the last slash of the pattern, only the first - word is affected. If a `W' is appended to the last slash of the - pattern, then the value is treated as a single word (possibly con- - taining embedded white space). If old_string begins with a caret - (`^'), old_string is anchored at the beginning of each word. If - old_string ends with a dollar sign (`$'), it is anchored at the end - of each word. Inside new_string, an ampersand (`&') is replaced by - old_string (without any `^' or `$'). Any character may be used as a - delimiter for the parts of the modifier string. The anchoring, - ampersand and delimiter characters may be escaped with a backslash - (`\'). + Modifies the first occurrence of old_string in each word of the + variable's value, replacing it with new_string. If a `g' is + appended to the last delimiter of the pattern, all occurrences in + each word are replaced. If a `1' is appended to the last delimiter + of the pattern, only the first occurrence is affected. If a `W' is + appended to the last delimiter of the pattern, then the value is + treated as a single word (possibly containing embedded white space). + If old_string begins with a caret (`^'), old_string is anchored at + the beginning of each word. If old_string ends with a dollar sign + (`$'), it is anchored at the end of each word. Inside new_string, + an ampersand (`&') is replaced by old_string (without any `^' or + `$'). Any character may be used as a delimiter for the parts of the + modifier string. The anchoring, ampersand and delimiter characters + may be escaped with a backslash (`\'). Variable expansion occurs in the normal fashion inside both old_string and new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign (`$'), not a pre- ceding dollar sign as is usual. :C/pattern/replacement/[1gW] The :C modifier is just like the :S modifier except that the old and new strings, instead of being simple strings, are an extended regu- lar expression (see regex(3)) string pattern and an ed(1)-style string replacement. Normally, the first occurrence of the pattern pattern in each word of the value is substituted with replacement. The `1' modifier causes the substitution to apply to at most one word; the `g' modifier causes the substitution to apply to as many instances of the search pattern pattern as occur in the word or words it is found in; the `W' modifier causes the value to be treated as a single word (possibly containing embedded white space). - Note that `1' and `g' are orthogonal; the former specifies whether - multiple words are potentially affected, the latter whether multiple - substitutions can potentially occur within each affected word. As for the :S modifier, the pattern and replacement are subjected to variable expansion before being parsed as regular expressions. - :T Replaces each word in the variable with its last component. + :T Replaces each word in the variable with its last path component. - :u Remove adjacent duplicate words (like uniq(1)). + :u Removes adjacent duplicate words (like uniq(1)). :?true_string:false_string If the variable name (not its value), when parsed as a .if condi- tional expression, evaluates to true, return as its value the true_string, otherwise return the false_string. Since the variable name is used as the expression, :? must be the first modifier after the variable name itself - which will, of course, usually contain variable expansions. A common error is trying to use expressions like ${NUMBERS:M42:?match:no} - which actually tests defined(NUMBERS), to determine is any words + which actually tests defined(NUMBERS), to determine if any words match "42" you need to use something like: ${"${NUMBERS:M42}" != "":?match:no}. :old_string=new_string This is the AT&T System V UNIX style variable substitution. It must be the last modifier specified. If old_string or new_string do not contain the pattern matching character % then it is assumed that they are anchored at the end of each word, so only suffixes or entire words may be replaced. Otherwise % is the substring of old_string to be replaced in new_string. If only old_string con- tains the pattern matching character %, and old_string matches, then the result is the new_string. If only the new_string contains the pattern matching character %, then it is not treated specially and it is printed as a literal % on match. If there is more than one pattern matching character (%) in either the new_string or old_string, only the first instance is treated specially (as the pattern character); all subsequent instances are treated as regular - characters + characters. Variable expansion occurs in the normal fashion inside both old_string and new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign (`$'), not a pre- ceding dollar sign as is usual. :@temp@string@ This is the loop expansion mechanism from the OSF Development Envi- - ronment (ODE) make. Unlike .for loops expansion occurs at the time - of reference. Assign temp to each word in the variable and evaluate - string. The ODE convention is that temp should start and end with a - period. For example. + ronment (ODE) make. Unlike .for loops, expansion occurs at the time + of reference. Assigns temp to each word in the variable and evalu- + ates string. The ODE convention is that temp should start and end + with a period. For example. ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} However a single character variable is often more readable: ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} :_[=var] - Save the current variable value in `$_' or the named var for later + Saves the current variable value in `$_' or the named var for later reference. Example usage: M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \ \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} Here `$_' is used to save the result of the `:S' modifier which is later referenced using the index values from `:range'. :Unewval - If the variable is undefined newval is the value. If the variable + If the variable is undefined, newval is the value. If the variable is defined, the existing value is returned. This is another ODE make feature. It is handy for setting per-target CFLAGS for instance: ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} If a value is only required if the variable is undefined, use: ${VAR:D:Unewval} :Dnewval - If the variable is defined newval is the value. + If the variable is defined, newval is the value. :L The name of the variable is the value. :P The path of the node which has the same name as the variable is the value. If no such node exists or its path is null, then the name of the variable is used. In order for this modifier to work, the name (node) must at least have appeared on the rhs of a dependency. :!cmd! The output of running cmd is the value. :sh If the variable is non-empty it is run as a command and the output becomes the new value. ::=str The variable is assigned the value str after substitution. This modifier and its variations are useful in obscure situations such as wanting to set a variable when shell commands are being parsed. These assignment modifiers always expand to nothing, so if appearing in a rule line by themselves should be preceded with something to keep bmake happy. The `::' helps avoid false matches with the AT&T System V UNIX style := modifier and since substitution always occurs the ::= form is vaguely appropriate. ::?=str As for ::= but only if the variable does not already have a value. ::+=str Append str to the variable. ::!=cmd Assign the output of cmd to the variable. :[range] Selects one or more words from the value, or performs other opera- tions related to the way in which the value is divided into words. Ordinarily, a value is treated as a sequence of words delimited by white space. Some modifiers suppress this behavior, causing a value to be treated as a single word (possibly containing embedded white space). An empty value, or a value that consists entirely of white- space, is treated as a single word. For the purposes of the `:[]' modifier, the words are indexed both forwards using positive inte- gers (where index 1 represents the first word), and backwards using negative integers (where index -1 represents the last word). The range is subjected to variable expansion, and the expanded result is then interpreted as follows: index Selects a single word from the value. start..end Selects all words from start to end, inclusive. For example, `:[2..-1]' selects all words from the second word to the last word. If start is greater than end, then the words are out- put in reverse order. For example, `:[-1..1]' selects all the words from last to first. If the list is already ordered, then this effectively reverses the list, but it is more efficient to use `:Or' instead of `:O:[-1..1]'. * Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of "$*" in Bourne shell. 0 Means the same as `:[*]'. @ Causes subsequent modifiers to treat the value as a sequence of words delimited by white space. Analogous to the effect of "$@" in Bourne shell. # Returns the number of words in the value. INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS - Makefile inclusion, conditional structures and for loops reminiscent of + Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in bmake. All such structures are identified by a line beginning with a single dot (`.') character. Files are included with either .include <file> or .include "file". Vari- ables 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 the system makefile directory. If double quotes are used, the including makefile's directory and any directories specified using the -I option are searched before the system makefile directory. For compatibility with other versions of bmake `include file ...' is also accepted. If the include statement is written as .-include or as .sinclude then errors locating and/or opening include files are ignored. If the include statement is written as .dinclude not only are errors locating and/or opening include files ignored, but stale dependencies within the included file will be ignored just like .MAKE.DEPENDFILE. Conditional expressions are also preceded by a single dot as the first character of a line. The possible conditionals are as follows: .error message The message is printed along with the name of the makefile and - line number, then bmake will exit. + line number, then bmake will exit immediately. .export variable ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables (those that start with `.'). This is not affected by the -X flag, so should be used with caution. For compatibility with other bmake programs `export variable=value' is also accepted. Appending a variable name to .MAKE.EXPORTED is equivalent to exporting a variable. .export-env variable ... The same as `.export', except that the variable is not appended to .MAKE.EXPORTED. This allows exporting a value to the environ- ment which is different from that used by bmake internally. .export-literal variable ... The same as `.export-env', except that variables in the value are not expanded. .info message The message is printed along with the name of the makefile and line number. .undef variable Un-define the specified global variable. Only global variables may be un-defined. .unexport variable ... The opposite of `.export'. The specified global variable will be removed from .MAKE.EXPORTED. If no variable list is provided, all globals are unexported, and .MAKE.EXPORTED deleted. .unexport-env Unexport all globals previously exported and clear the environ- ment inherited from the parent. This operation will cause a mem- ory leak of the original environment, so should be used spar- ingly. Testing for .MAKE.LEVEL being 0, would make sense. Also note that any variables which originated in the parent environ- ment should be explicitly preserved if desired. For example: .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .unexport-env .export PATH .endif Would result in an environment containing only `PATH', which is the minimal useful environment. Actually `.MAKE.LEVEL' will also be pushed into the new environment. .warning message The message prefixed by `warning:' is printed along with the name of the makefile and line number. .if [!]expression [operator expression ...] Test the value of an expression. .ifdef [!]variable [operator variable ...] Test the value of a variable. .ifndef [!]variable [operator variable ...] Test the value of a variable. .ifmake [!]target [operator target ...] Test the target being built. .ifnmake [!] target [operator target ...] Test the target being built. .else Reverse the sense of the last conditional. .elif [!] expression [operator expression ...] A combination of `.else' followed by `.if'. .elifdef [!]variable [operator variable ...] A combination of `.else' followed by `.ifdef'. .elifndef [!]variable [operator variable ...] A combination of `.else' followed by `.ifndef'. .elifmake [!]target [operator target ...] A combination of `.else' followed by `.ifmake'. .elifnmake [!]target [operator target ...] A combination of `.else' followed by `.ifnmake'. .endif End the body of the conditional. The operator may be any one of the following: || Logical OR. && Logical AND; of higher precedence than ``||''. As in C, bmake will only evaluate a conditional as far as is necessary to determine its value. Parentheses may be used to change the order of evaluation. The boolean operator `!' may be used to logically negate an entire conditional. It is of higher precedence than `&&'. The value of expression may be any of the following: defined Takes a variable name as an argument and evaluates to true if the variable has been defined. make Takes a target name as an argument and evaluates to true if the target was specified as part of bmake's command line or was declared the default target (either implicitly or explicitly, see .MAIN) before the line containing the conditional. empty Takes a variable, with possible modifiers, and evaluates to true if the expansion of the variable would result in an empty string. exists Takes a file name as an argument and evaluates to true if the file exists. The file is searched for on the system search path (see .PATH). target Takes a target name as an argument and evaluates to true if the target has been defined. commands Takes a target name as an argument and evaluates to true if the target has been defined and has commands associated with it. Expression may also be an arithmetic or string comparison. Variable expansion is performed on both sides of the comparison, after which the integral values are compared. A value is interpreted as hexadecimal if it is preceded by 0x, otherwise it is decimal; octal numbers are not sup- ported. The standard C relational operators are all supported. If after variable expansion, either the left or right hand side of a `==' or `!=' operator is not an integral value, then string comparison is performed between the expanded variables. If no relational operator is given, it - is assumed that the expanded variable is being compared against 0 or an + is assumed that the expanded variable is being compared against 0, or an empty string in the case of a string comparison. When bmake is evaluating one of these conditional expressions, and it encounters a (white-space separated) word it doesn't recognize, either the ``make'' or ``defined'' expression is applied to it, depending on the form of the conditional. If the form is `.ifdef', `.ifndef', or `.if' the ``defined'' expression is applied. Similarly, if the form is `.ifmake' or `.ifnmake', the ``make'' expression is applied. If the conditional evaluates to true the parsing of the makefile contin- ues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a `.else' or `.endif' is found. For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: .for variable [variable ...] in expression - + .endfor After the for expression is evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each variable, - in order, and these variables are substituted into the make-rules inside + in order, and these variables are substituted into the make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. COMMENTS Comments begin with a hash (`#') character, anywhere but in a shell com- mand line, and continue to the end of an unescaped new line. SPECIAL SOURCES (ATTRIBUTES) .EXEC Target is never out of date, but always execute commands any- way. .IGNORE Ignore any errors from the commands associated with this tar- get, exactly as if they all were preceded by a dash (`-'). .MADE Mark all sources of this target as being up-to-date. .MAKE Execute the commands associated with this target even if the -n or -t options were specified. Normally used to mark recursive bmakes. .META Create a meta file for the target, even if it is flagged as .PHONY, .MAKE, or .SPECIAL. Usage in conjunction with .MAKE is the most likely case. In "meta" mode, the target is out-of- date if the meta file is missing. .NOMETA Do not create a meta file for the target. Meta files are also not created for .PHONY, .MAKE, or .SPECIAL targets. .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which always changes. If the number of commands change, though, the target will still be out of date. The same effect applies to any command line that uses the variable .OODATE, which can be used for that purpose even when not otherwise needed or desired: skip-compare-for-some: @echo this will be compared @echo this will not ${.OODATE:M.NOMETA_CMP} @echo this will also be compared The :M pattern suppresses any expansion of the unwanted vari- able. .NOPATH Do not search for the target in the directories specified by .PATH. .NOTMAIN Normally bmake selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .OPTIONAL If a target is marked with this attribute and bmake can't fig- ure out how to create it, it will ignore this fact and assume the file isn't needed or already exists. .PHONY The target does not correspond to an actual file; it is always considered to be out of date, and will not be created with the -t option. Suffix-transformation rules are not applied to .PHONY targets. .PRECIOUS When bmake is interrupted, it normally removes any partially made targets. This source prevents the target from being removed. .RECURSIVE Synonym for .MAKE. .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign (`@'). .USE Turn the target into bmake's version of a macro. When the tar- get is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .USE) of the source. If the target already has commands, the .USE target's commands are appended to them. .USEBEFORE Exactly like .USE, but prepend the .USEBEFORE target commands to the target. .WAIT If .WAIT appears in a dependency line, the sources that precede it are made before the sources that succeed it in the line. Since the dependents of files are not made until the file itself could be made, this also stops the dependents being built unless they are needed for another branch of the depen- dency tree. So given: x: a .WAIT b echo x a: echo a b: b1 echo b b1: echo b1 the output is always `a', `b1', `b', `x'. The ordering imposed by .WAIT is only relevant for parallel makes. SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .BEGIN Any command lines attached to this target are executed before anything else is done. .DEFAULT This is sort of a .USE rule for any target (that was used only as a source) that bmake can't figure out any other way to cre- ate. Only the shell script is used. The .IMPSRC variable of a target that inherits .DEFAULT's commands is set to the target's own name. .DELETE_ON_ERROR If this target is present in the makefile, it globally causes make to delete targets whose commands fail. (By default, only targets whose commands are interrupted during execution are deleted. This is the historical behavior.) This setting can be used to help prevent half-finished or malformed targets from being left around and corrupting future rebuilds. .END Any command lines attached to this target are executed after everything else is done. .ERROR Any command lines attached to this target are executed when another target fails. The .ERROR_TARGET variable is set to the target that failed. See also MAKE_PRINT_VAR_ON_ERROR. .IGNORE Mark each of the sources with the .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the -i option. .INTERRUPT If bmake is interrupted, the commands for this target will be executed. .MAIN If no target is specified when bmake is invoked, this target will be built. .MAKEFLAGS This target provides a way to specify flags for bmake when the makefile is used. The flags are as if typed to the shell, though the -f option will have no effect. .NOPATH Apply the .NOPATH attribute to any specified sources. .NOTPARALLEL Disable parallel mode. .NO_PARALLEL Synonym for .NOTPARALLEL, for compatibility with other pmake variants. .OBJDIR The source is a new value for `.OBJDIR'. If it exists, bmake will chdir(2) to it and update the value of `.OBJDIR'. .ORDER The named targets are made in sequence. This ordering does not add targets to the list of targets to be made. Since the depen- dents of a target do not get built until the target itself could be built, unless `a' is built by another part of the dependency graph, the following is a dependency loop: .ORDER: b a b: a The ordering imposed by .ORDER is only relevant for parallel makes. .PATH The sources are directories which are to be searched for files not found in the current directory. If no sources are speci- fied, any previously specified directories are deleted. If the source is the special .DOTLAST target, then the current working directory is searched last. .PATH.suffix Like .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .SUFFIXES. .PHONY Apply the .PHONY attribute to any specified sources. .PRECIOUS Apply the .PRECIOUS attribute to any specified sources. If no sources are specified, the .PRECIOUS attribute is applied to every target in the file. .SHELL Sets the shell that bmake will use to execute commands. The sources are a set of field=value pairs. name This is the minimal specification, used to select one of the built-in shell specs; sh, ksh, and csh. path Specifies the path to the shell. hasErrCtl Indicates whether the shell supports exit on error. check The command to turn on error checking. ignore The command to disable error checking. echo The command to turn on echoing of commands executed. quiet The command to turn off echoing of commands exe- cuted. filter The output to filter after issuing the quiet com- mand. It is typically identical to quiet. errFlag The flag to pass the shell to enable error checking. echoFlag The flag to pass the shell to enable command echo- ing. newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. Example: .SHELL: name=ksh path=/bin/ksh hasErrCtl=true \ check="set -e" ignore="set +e" \ echo="set -v" quiet="set +v" filter="set +v" \ echoFlag=v errFlag=e newline="'\n'" .SILENT Apply the .SILENT attribute to any specified sources. If no sources are specified, the .SILENT attribute is applied to every command in the file. .STALE This target gets run when a dependency file contains stale entries, having .ALLSRC set to the name of that dependency file. .SUFFIXES Each source specifies a suffix to bmake. If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. Example: .SUFFIXES: .o .c.o: cc -o ${.TARGET} -c ${.IMPSRC} ENVIRONMENT bmake uses the following environment variables, if they exist: MACHINE, MACHINE_ARCH, MAKE, MAKEFLAGS, MAKEOBJDIR, MAKEOBJDIRPREFIX, MAKESYSPATH, PWD, and TMPDIR. MAKEOBJDIRPREFIX and MAKEOBJDIR may only be set in the environment or on the command line to bmake and not as makefile variables; see the descrip- tion of `.OBJDIR' for more details. FILES .depend list of dependencies Makefile list of dependencies makefile list of dependencies sys.mk system makefile /usr/share/mk system makefile directory COMPATIBILITY The basic make syntax is compatible between different versions of make; however the special variables, variable modifiers and conditionals are not. Older versions An incomplete list of changes in older versions of bmake: The way that .for loop variables are substituted changed after NetBSD 5.0 so that they still appear to be variable expansions. In particular this stops them being treated as syntax, and removes some obscure problems using them in .if statements. The way that parallel makes are scheduled changed in NetBSD 4.0 so that .ORDER and .WAIT apply recursively to the dependent nodes. The algo- rithms used may change again in the future. Other make dialects Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup- port most of the features of bmake as described in this manual. Most notably: +o The .WAIT and .ORDER declarations and most functionality per- taining to parallelization. (GNU make supports parallelization but lacks these features needed to control it effectively.) +o Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) +o All built-in variables that begin with a dot. +o Most of the special sources and targets that begin with a dot, with the notable exception of .PHONY, .PRECIOUS, and .SUFFIXES. +o Variable modifiers, except for the :old=new string substitution, which does not portably support globbing with `%' and historically only works on declared suffixes. +o The $> variable even in its short form; most makes support this functionality but its name varies. Some features are somewhat more portable, such as assignment with +=, ?=, and !=. The .PATH functionality is based on an older feature VPATH found in GNU make and many versions of SVR4 make; however, historically its behavior is too ill-defined (and too buggy) to rely upon. The $@ and $< variables are more or less universally portable, as is the $(MAKE) variable. Basic use of suffix rules (for files only in the cur- rent directory, not trying to chain transformations together, etc.) is also reasonably portable. SEE ALSO mkdep(1) HISTORY bmake is derived from NetBSD make(1). It uses autoconf to facilitate portability to other platforms. A make command appeared in Version 7 AT&T UNIX. This make implementation is based on Adam De Boor's pmake program which was written for Sprite at Berkeley. It was designed to be a parallel distributed make running jobs on different machines using a daemon called ``customs''. Historically the target/dependency ``FRC'' has been used to FoRCe rebuilding (since the target/dependency does not exist... unless someone creates an ``FRC'' file). BUGS The make 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 make just counts {} and () in order to find the end of a variable expansion. There is no way of escaping a space character in a filename. -FreeBSD 11.3 June 5, 2020 FreeBSD 11.3 +FreeBSD 11.3 August 28, 2020 FreeBSD 11.3 Index: head/contrib/bmake/bsd.after-import.mk =================================================================== --- head/contrib/bmake/bsd.after-import.mk (revision 365365) +++ head/contrib/bmake/bsd.after-import.mk (revision 365366) @@ -1,111 +1,126 @@ -# $Id: bsd.after-import.mk,v 1.15 2018/12/30 17:14:24 sjg Exp $ +# $Id: bsd.after-import.mk,v 1.16 2020/07/12 03:39:01 sjg Exp $ # This makefile is for use when integrating bmake into a BSD build # system. Use this makefile after importing bmake. # It will bootstrap the new version, # capture the generated files we need, and add an after-import # target to allow the process to be easily repeated. # The goal is to allow the benefits of autoconf without # the overhead of running configure. -all: _makefile +all: _makefile _utmakefile all: after-import # we rely on bmake .if !defined(.PARSEDIR) .error this makefile requires bmake .endif _this := ${MAKEFILE:tA} BMAKE_SRC := ${.PARSEDIR} # it helps to know where the top of the tree is. .if !defined(SRCTOP) srctop := ${.MAKE.MAKEFILES:M*src/share/mk/sys.mk:H:H:H} .if empty(srctop) # likely locations? .for d in contrib/bmake external/bsd/bmake/dist .if ${BMAKE_SRC:M*/$d} != "" srctop := ${BMAKE_SRC:tA:S,/$d,,} .endif .endfor .endif .if !empty(srctop) SRCTOP := ${srctop} .endif .endif # This lets us match what boot-strap does -.if !defined(HOST_OS) +.if defined(.MAKE.OS) +HOST_OS:= ${.MAKE.OS} +.elif !defined(HOST_OS) HOST_OS!= uname .endif BOOTSTRAP_ARGS = \ --prefix /usr \ --share /usr/share .if !empty(DEFAULT_SYS_PATH) BOOTSTRAP_ARGS += --with-default-sys-path='${DEFAULT_SYS_PATH}' .endif # run boot-strap with minimal influence bootstrap: ${BMAKE_SRC}/boot-strap ${MAKEFILE} HOME=/ ${BMAKE_SRC}/boot-strap -o ${HOST_OS} ${BOOTSTRAP_ARGS} ${BOOTSTRAP_XTRAS} touch ${.TARGET} # Makefiles need a little more tweaking than say config.h MAKEFILE_SED = sed -e '/^MACHINE/d' \ -e '/include.*VERSION/d' \ -e '/^PROG/ { s,=,?=,;s,bmake,$${.CURDIR:T},; }' \ -e 's,^.-include,.sinclude,' \ -e '/^\..*include * ${.CURDIR}/$f .endfor .for f in ${configured_files:M*Makefile*} @echo Capturing $f @mkdir -p ${${.CURDIR}/$f:L:H} @(echo '# This is a generated file, do NOT edit!'; \ echo '# See ${_this:S,${SRCTOP}/,,}'; \ echo '#'; echo '# $$${HOST_OS}$$'; echo; \ echo 'SRCTOP?= $${.CURDIR:${${.CURDIR}/$f:L:H:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; echo; \ ${MAKEFILE_SED} ${HOST_OS}/$f ) > ${.CURDIR}/$f .endfor # this needs the most work _makefile: bootstrap ${MAKEFILE} @echo Generating ${.CURDIR}/Makefile @(echo '# This is a generated file, do NOT edit!'; \ echo '# See ${_this:S,${SRCTOP}/,,}'; \ echo '#'; echo '# $$${HOST_OS}$$'; \ 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; \ echo '# for after-import'; \ echo 'CLEANDIRS+= ${HOST_OS}'; \ echo 'CLEANFILES+= bootstrap'; echo; \ ${MAKEFILE_SED} \ ${1 2:L:@n@-e '/start-delete$n/,/end-delete$n/d'@} \ ${BMAKE_SRC}/Makefile; \ echo; echo '# override some simple things'; \ echo 'BINDIR= /usr/bin'; \ echo 'MANDIR= ${MANDIR:U/usr/share/man}'; \ echo; echo '# make sure we get this'; \ echo 'CFLAGS+= $${COPTS.$${.IMPSRC:T}}'; \ echo; echo 'after-import: ${_this:S,${SRCTOP},\${SRCTOP},}'; \ echo ' cd $${.CURDIR} && $${.MAKE} -f ${_this:S,${SRCTOP},\${SRCTOP},}'; \ echo ) > ${.TARGET} @cmp -s ${.TARGET} ${.CURDIR}/Makefile || \ mv ${.TARGET} ${.CURDIR}/Makefile + +_utmakefile: bootstrap ${MAKEFILE} + @echo Generating ${.CURDIR}/unit-tests/Makefile + @mkdir -p ${.CURDIR}/unit-tests + @(echo '# This is a generated file, do NOT edit!'; \ + echo '# See ${_this:S,${SRCTOP}/,,}'; \ + echo '#'; echo '# $$${HOST_OS}$$'; \ + ${MAKEFILE_SED} \ + -e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \ + ${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET} + @cmp -s ${.TARGET} ${.CURDIR}/unit-tests/Makefile || \ + mv ${.TARGET} ${.CURDIR}/unit-tests/Makefile + .include Index: head/contrib/bmake/buf.c =================================================================== --- head/contrib/bmake/buf.c (revision 365365) +++ head/contrib/bmake/buf.c (revision 365366) @@ -1,291 +1,223 @@ -/* $NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: buf.c,v 1.26 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $"); #endif #endif /* not lint */ #endif -/*- - * buf.c -- - * Functions for automatically-expanded buffers. - */ +/* Functions for automatically-expanded null-terminated buffers. */ -#include "make.h" -#include "buf.h" +#include +#include "make.h" -#ifndef max -#define max(a,b) ((a) > (b) ? (a) : (b)) -#endif - -#define BUF_DEF_SIZE 256 /* Default buffer size */ - -/*- - *----------------------------------------------------------------------- - * Buf_Expand_1 -- - * Extend buffer for single byte add. - * - *----------------------------------------------------------------------- - */ +/* Extend the buffer for adding a single byte. */ void Buf_Expand_1(Buffer *bp) { - bp->size += max(bp->size, 16); + bp->size += MAX(bp->size, 16); bp->buffer = bmake_realloc(bp->buffer, bp->size); } -/*- - *----------------------------------------------------------------------- - * Buf_AddBytes -- - * Add a number of bytes to the buffer. - * - * Results: - * None. - * - * Side Effects: - * Guess what? - * - *----------------------------------------------------------------------- - */ +/* Add the given bytes to the buffer. */ void -Buf_AddBytes(Buffer *bp, int numBytes, const Byte *bytesPtr) +Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes) { - int count = bp->count; - Byte *ptr; + size_t count = bp->count; + char *ptr; if (__predict_false(count + numBytes >= bp->size)) { - bp->size += max(bp->size, numBytes + 16); + bp->size += MAX(bp->size, numBytes + 16); bp->buffer = bmake_realloc(bp->buffer, bp->size); } ptr = bp->buffer + count; bp->count = count + numBytes; - ptr[numBytes] = 0; memcpy(ptr, bytesPtr, numBytes); + ptr[numBytes] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_GetAll -- - * Get all the available data at once. - * - * Results: - * A pointer to the data and the number of bytes available. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -Byte * -Buf_GetAll(Buffer *bp, int *numBytesPtr) +/* Add the bytes between start and end to the buffer. */ +void +Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end) { + Buf_AddBytes(bp, start, (size_t)(end - start)); +} +/* Add the given string to the buffer. */ +void +Buf_AddStr(Buffer *bp, const char *str) +{ + Buf_AddBytes(bp, str, strlen(str)); +} + +/* Add the given number to the buffer. */ +void +Buf_AddInt(Buffer *bp, int n) +{ + enum { + bits = sizeof(int) * CHAR_BIT, + max_octal_digits = (bits + 2) / 3, + max_decimal_digits = /* at most */ max_octal_digits, + max_sign_chars = 1, + buf_size = max_sign_chars + max_decimal_digits + 1 + }; + char buf[buf_size]; + + size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n); + Buf_AddBytes(bp, buf, len); +} + +/* Get the data (usually a string) from the buffer. + * The returned data is valid until the next modifying operation + * on the buffer. + * + * Returns the pointer to the data and optionally the length of the + * data in the buffer. */ +char * +Buf_GetAll(Buffer *bp, size_t *numBytesPtr) +{ if (numBytesPtr != NULL) *numBytesPtr = bp->count; - return bp->buffer; } -/*- - *----------------------------------------------------------------------- - * Buf_Empty -- - * Throw away bytes in a buffer. - * - * Results: - * None. - * - * Side Effects: - * The bytes are discarded. - * - *----------------------------------------------------------------------- - */ +/* Mark the buffer as empty, so it can be filled with data again. */ void Buf_Empty(Buffer *bp) { - bp->count = 0; - *bp->buffer = 0; + bp->buffer[0] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_Init -- - * Initialize a buffer. If no initial size is given, a reasonable - * default is used. - * - * Input: - * size Initial size for the buffer - * - * Results: - * A buffer to be given to other functions in this library. - * - * Side Effects: - * The buffer is created, the space allocated and pointers - * initialized. - * - *----------------------------------------------------------------------- - */ +/* Initialize a buffer. + * If the given initial size is 0, a reasonable default is used. */ void -Buf_Init(Buffer *bp, int size) +Buf_Init(Buffer *bp, size_t size) { if (size <= 0) { - size = BUF_DEF_SIZE; + size = 256; } bp->size = size; bp->count = 0; bp->buffer = bmake_malloc(size); - *bp->buffer = 0; + bp->buffer[0] = '\0'; } -/*- - *----------------------------------------------------------------------- - * Buf_Destroy -- - * Nuke a buffer and all its resources. - * - * Input: - * buf Buffer to destroy - * freeData TRUE if the data should be destroyed - * - * Results: - * Data buffer, NULL if freed - * - * Side Effects: - * The buffer is freed. - * - *----------------------------------------------------------------------- - */ -Byte * +/* Reset the buffer. + * If freeData is TRUE, the data from the buffer is freed as well. + * Otherwise it is kept and returned. */ +char * Buf_Destroy(Buffer *buf, Boolean freeData) { - Byte *data; - - data = buf->buffer; + char *data = buf->buffer; if (freeData) { free(data); data = NULL; } buf->size = 0; buf->count = 0; buf->buffer = NULL; return data; } - -/*- - *----------------------------------------------------------------------- - * Buf_DestroyCompact -- - * Nuke a buffer and return its data. - * - * Input: - * buf Buffer to destroy - * - * Results: - * Data buffer - * - * Side Effects: - * If the buffer size is much greater than its content, - * a new buffer will be allocated and the old one freed. - * - *----------------------------------------------------------------------- - */ #ifndef BUF_COMPACT_LIMIT -# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ +# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ #endif -Byte * +/* Reset the buffer and return its data. + * + * If the buffer size is much greater than its content, + * a new buffer will be allocated and the old one freed. */ +char * Buf_DestroyCompact(Buffer *buf) { #if BUF_COMPACT_LIMIT > 0 - Byte *data; - if (buf->size - buf->count >= BUF_COMPACT_LIMIT) { /* We trust realloc to be smart */ - data = bmake_realloc(buf->buffer, buf->count + 1); - if (data) { - data[buf->count] = 0; - Buf_Destroy(buf, FALSE); - return data; - } + char *data = bmake_realloc(buf->buffer, buf->count + 1); + data[buf->count] = '\0'; + Buf_Destroy(buf, FALSE); + return data; } #endif return Buf_Destroy(buf, FALSE); } Index: head/contrib/bmake/buf.h =================================================================== --- head/contrib/bmake/buf.h (revision 365365) +++ head/contrib/bmake/buf.h (revision 365366) @@ -1,119 +1,125 @@ -/* $NetBSD: buf.h,v 1.19 2017/05/31 22:02:06 maya Exp $ */ +/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 */ -/*- - * buf.h -- - * Header for users of the buf library. - */ +/* Automatically growing null-terminated buffers of characters. */ #ifndef MAKE_BUF_H #define MAKE_BUF_H -typedef char Byte; +#include +/* An automatically growing null-terminated buffer of characters. */ typedef struct Buffer { - int size; /* Current size of the buffer */ - int count; /* Number of bytes in buffer */ - Byte *buffer; /* The buffer itself (zero terminated) */ + size_t size; /* Allocated size of the buffer, including the null */ + size_t count; /* Number of bytes in buffer, excluding the null */ + char *buffer; /* The buffer itself (always null-terminated) */ } Buffer; -/* If we aren't on netbsd, __predict_false() might not be defined. */ +/* If we aren't on NetBSD, __predict_false() might not be defined. */ #ifndef __predict_false #define __predict_false(x) (x) #endif +void Buf_Expand_1(Buffer *); + /* Buf_AddByte adds a single byte to a buffer. */ -#define Buf_AddByte(bp, byte) do { \ - int _count = ++(bp)->count; \ - char *_ptr; \ - if (__predict_false(_count >= (bp)->size)) \ - Buf_Expand_1(bp); \ - _ptr = (bp)->buffer + _count; \ - _ptr[-1] = (byte); \ - _ptr[0] = 0; \ - } while (0) +static inline void MAKE_ATTR_UNUSED +Buf_AddByte(Buffer *bp, char byte) +{ + size_t count = ++bp->count; + char *ptr; + if (__predict_false(count >= bp->size)) + Buf_Expand_1(bp); + ptr = bp->buffer + count; + ptr[-1] = byte; + ptr[0] = 0; +} -#define BUF_ERROR 256 +static inline size_t MAKE_ATTR_UNUSED +Buf_Size(const Buffer *bp) +{ + return bp->count; +} -#define Buf_Size(bp) ((bp)->count) - -void Buf_Expand_1(Buffer *); -void Buf_AddBytes(Buffer *, int, const Byte *); -Byte *Buf_GetAll(Buffer *, int *); +void Buf_AddBytes(Buffer *, const char *, size_t); +void Buf_AddBytesBetween(Buffer *, const char *, const char *); +void Buf_AddStr(Buffer *, const char *); +void Buf_AddInt(Buffer *, int); +char *Buf_GetAll(Buffer *, size_t *); void Buf_Empty(Buffer *); -void Buf_Init(Buffer *, int); -Byte *Buf_Destroy(Buffer *, Boolean); -Byte *Buf_DestroyCompact(Buffer *); +void Buf_Init(Buffer *, size_t); +char *Buf_Destroy(Buffer *, Boolean); +char *Buf_DestroyCompact(Buffer *); #endif /* MAKE_BUF_H */ Index: head/contrib/bmake/compat.c =================================================================== --- head/contrib/bmake/compat.c (revision 365365) +++ head/contrib/bmake/compat.c (revision 365366) @@ -1,781 +1,751 @@ -/* $NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: compat.c,v 1.113 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * compat.c -- * The routines in this file implement the full-compatibility * mode of PMake. Most of the special functionality of PMake * is available in this mode. Things not supported: * - different shells. * - friendly variable substitution. * * Interface: * Compat_Run Initialize things for this module and recreate * thems as need creatin' */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "wait.h" #include #include #include #include #include "make.h" #include "hash.h" #include "dir.h" #include "job.h" #include "metachar.h" #include "pathnames.h" static GNode *curTarg = NULL; static GNode *ENDNode; static void CompatInterrupt(int); static pid_t compatChild; static int compatSigno; /* * CompatDeleteTarget -- delete a failed, interrupted, or otherwise * duffed target if not inhibited by .PRECIOUS. */ static void CompatDeleteTarget(GNode *gn) { if ((gn != NULL) && !Targ_Precious (gn)) { - char *p1; - char *file = Var_Value(TARGET, gn, &p1); + char *p1; + const char *file = Var_Value(TARGET, gn, &p1); if (!noExecute && eunlink(file) != -1) { Error("*** %s removed", file); } - free(p1); + bmake_free(p1); } } -/*- - *----------------------------------------------------------------------- - * CompatInterrupt -- - * Interrupt the creation of the current target and remove it if - * it ain't precious. +/* Interrupt the creation of the current target and remove it if it ain't + * precious. Then exit. * - * Results: - * None. + * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED. * - * Side Effects: - * The target is removed and the process exits. If .INTERRUPT exists, - * its commands are run first WITH INTERRUPTS IGNORED.. - * * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've * left the logic alone for now. - dholland 20160826 - * - *----------------------------------------------------------------------- */ static void CompatInterrupt(int signo) { GNode *gn; CompatDeleteTarget(curTarg); if ((curTarg != NULL) && !Targ_Precious (curTarg)) { /* * Run .INTERRUPT only if hit with interrupt signal */ if (signo == SIGINT) { gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); if (gn != NULL) { Compat_Make(gn, gn); } } } if (signo == SIGQUIT) _exit(signo); /* * If there is a child running, pass the signal on * we will exist after it has exited. */ compatSigno = signo; if (compatChild > 0) { KILLPG(compatChild, signo); } else { bmake_signal(signo, SIG_DFL); kill(myPid, signo); } } - + /*- *----------------------------------------------------------------------- * CompatRunCommand -- * Execute the next command for a target. If the command returns an * error, the node's made field is set to ERROR and creation stops. * * Input: * cmdp Command to execute * gnp Node from which the command came * * Results: * 0 if the command succeeded, 1 if an error occurred. * * Side Effects: * The node's 'made' field may be set to ERROR. * *----------------------------------------------------------------------- */ int CompatRunCommand(void *cmdp, void *gnp) { char *cmdStart; /* Start of expanded command */ char *cp, *bp; Boolean silent, /* Don't print command */ - doIt; /* Execute even if -n */ + doIt; /* Execute even if -n */ volatile Boolean errCheck; /* Check errors */ WAIT_T reason; /* Reason for child's death */ int status; /* Description of child's death */ pid_t cpid; /* Child actually found */ pid_t retstat; /* Result of wait */ LstNode cmdNode; /* Node where current command is located */ const char ** volatile av; /* Argument vector for thing to exec */ char ** volatile mav;/* Copy of the argument vector for freeing */ - int argc; /* Number of arguments in av or 0 if not - * dynamically allocated */ - Boolean local; /* TRUE if command should be executed - * locally */ Boolean useShell; /* TRUE if command should be executed * using a shell */ char * volatile cmd = (char *)cmdp; GNode *gn = (GNode *)gnp; - silent = gn->type & OP_SILENT; + silent = (gn->type & OP_SILENT) != 0; errCheck = !(gn->type & OP_IGNORE); doIt = FALSE; - cmdNode = Lst_Member(gn->commands, cmd); - cmdStart = Var_Subst(NULL, cmd, gn, VARF_WANTRES); + cmdNode = Lst_FindDatum(gn->commands, cmd); + cmdStart = Var_Subst(cmd, gn, VARE_WANTRES); /* * brk_string will return an argv with a NULL in av[0], thus causing * execvp to choke and die horribly. Besides, how can we execute a null * command? In any case, we warn the user that the command expanded to * nothing (is this the right thing to do?). */ if (*cmdStart == '\0') { free(cmdStart); return 0; } cmd = cmdStart; - Lst_Replace(cmdNode, cmdStart); + LstNode_Set(cmdNode, cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { - (void)Lst_AtEnd(ENDNode->commands, cmdStart); + assert(ENDNode != NULL); + Lst_Append(ENDNode->commands, cmdStart); return 0; } if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return 0; } while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { switch (*cmd) { case '@': - silent = DEBUG(LOUD) ? FALSE : TRUE; + silent = !DEBUG(LOUD); break; case '-': errCheck = FALSE; break; case '+': doIt = TRUE; if (!shellName) /* we came here from jobs */ Shell_Init(); break; } cmd++; } while (isspace((unsigned char)*cmd)) cmd++; /* * If we did not end up with a command, just skip it. */ if (!*cmd) return 0; #if !defined(MAKE_NATIVE) /* * In a non-native build, the host environment might be weird enough * that it's necessary to go through a shell to get the correct * behaviour. Or perhaps the shell has been replaced with something * that does extra logging, and that should not be bypassed. */ useShell = TRUE; #else /* * Search for meta characters in the command. If there are no meta * characters, there's no need to execute a shell to execute the * command. * * Additionally variable assignments and empty commands * go to the shell. Therefore treat '=' and ':' like shell * meta characters as documented in make(1). */ useShell = needshell(cmd, FALSE); #endif /* * Print the command before echoing if we're not supposed to be quiet for * this one. We also print the command if -n given. */ if (!silent || NoExecute(gn)) { printf("%s\n", cmd); fflush(stdout); } /* * If we're not supposed to execute any commands, this is as far as * we go... */ if (!doIt && NoExecute(gn)) { return 0; } if (DEBUG(JOB)) fprintf(debug_file, "Execute: '%s'\n", cmd); -again: if (useShell) { /* * We need to pass the command off to the shell, typically * because the command contains a "meta" character. */ static const char *shargv[5]; int shargc; shargc = 0; shargv[shargc++] = shellPath; /* * The following work for any of the builtin shell specs. */ if (errCheck && shellErrFlag) { shargv[shargc++] = shellErrFlag; } if (DEBUG(SHELL)) shargv[shargc++] = "-xc"; else shargv[shargc++] = "-c"; shargv[shargc++] = cmd; - shargv[shargc++] = NULL; + shargv[shargc] = NULL; av = shargv; - argc = 0; bp = NULL; mav = NULL; } else { /* * No meta-characters, so no need to exec a shell. Break the command * into words to form an argument vector we can execute. */ - mav = brk_string(cmd, &argc, TRUE, &bp); - if (mav == NULL) { - useShell = 1; - goto again; - } + Words words = Str_Words(cmd, FALSE); + mav = words.words; + bp = words.freeIt; av = (void *)mav; } - local = TRUE; - #ifdef USE_META if (useMeta) { meta_compat_start(); } #endif /* * Fork and execute the single command. If the fork fails, we abort. */ compatChild = cpid = vFork(); if (cpid < 0) { Fatal("Could not fork"); } if (cpid == 0) { Var_ExportVars(); #ifdef USE_META if (useMeta) { meta_compat_child(); } #endif - if (local) - (void)execvp(av[0], (char *const *)UNCONST(av)); - else - (void)execv(av[0], (char *const *)UNCONST(av)); + (void)execvp(av[0], (char *const *)UNCONST(av)); execError("exec", av[0]); _exit(1); } free(mav); free(bp); - Lst_Replace(cmdNode, NULL); + /* XXX: Memory management looks suspicious here. */ + /* XXX: Setting a list item to NULL is unexpected. */ + LstNode_SetNull(cmdNode); #ifdef USE_META if (useMeta) { meta_compat_parent(cpid); } #endif /* * The child is off and running. Now all we can do is wait... */ while (1) { while ((retstat = wait(&reason)) != cpid) { if (retstat > 0) JobReapChild(retstat, reason, FALSE); /* not ours? */ if (retstat == -1 && errno != EINTR) { break; } } if (retstat > -1) { if (WIFSTOPPED(reason)) { status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { status = WEXITSTATUS(reason); /* exited */ #if defined(USE_META) && defined(USE_FILEMON_ONCE) if (useMeta) { meta_cmd_finish(NULL); } #endif if (status != 0) { if (DEBUG(ERROR)) { - fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", + fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", gn->name); - for (cp = cmd; *cp; ) { - if (isspace((unsigned char)*cp)) { + for (cp = cmd; *cp; ) { + if (isspace((unsigned char)*cp)) { fprintf(debug_file, " "); - while (isspace((unsigned char)*cp)) + while (isspace((unsigned char)*cp)) cp++; } else { fprintf(debug_file, "%c", *cp); - cp++; + cp++; } - } + } fprintf(debug_file, "\n"); } printf("*** Error code %d", status); } } else { status = WTERMSIG(reason); /* signaled */ printf("*** Signal %d", status); } if (!WIFEXITED(reason) || (status != 0)) { if (errCheck) { #ifdef USE_META if (useMeta) { meta_job_error(NULL, gn, 0, status); } #endif gn->made = ERROR; if (keepgoing) { /* * Abort the current target, but let others * continue. */ printf(" (continuing)\n"); } else { printf("\n"); } if (deleteOnError) { CompatDeleteTarget(gn); } } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ printf(" (ignored)\n"); status = 0; } } break; } else { Fatal("error in wait: %d: %s", retstat, strerror(errno)); /*NOTREACHED*/ } } free(cmdStart); compatChild = 0; if (compatSigno) { bmake_signal(compatSigno, SIG_DFL); kill(myPid, compatSigno); } return status; } - + /*- *----------------------------------------------------------------------- * Compat_Make -- * Make a target. * * Input: * gnp The node to make * pgnp Parent to abort if necessary * * Results: * 0 * * Side Effects: * If an error is detected and not being ignored, the process exits. * *----------------------------------------------------------------------- */ int Compat_Make(void *gnp, void *pgnp) { GNode *gn = (GNode *)gnp; GNode *pgn = (GNode *)pgnp; if (!shellName) /* we came here from jobs */ Shell_Init(); if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) { /* * First mark ourselves to be made, then apply whatever transformations * the suffix module thinks are necessary. Once that's done, we can * descend and make all our children. If any of them has an error * but the -k flag was given, our 'make' field will be set FALSE again. * This is our signal to not attempt to do anything but abort our * parent as well. */ gn->flags |= REMAKE; gn->made = BEINGMADE; if ((gn->type & OP_MADE) == 0) Suff_FindDeps(gn); Lst_ForEach(gn->children, Compat_Make, gn); if ((gn->flags & REMAKE) == 0) { gn->made = ABORTED; - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; goto cohorts; } - if (Lst_Member(gn->iParents, pgn) != NULL) { + if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) { char *p1; Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn); - free(p1); + bmake_free(p1); } /* * All the children were made ok. Now cmgn->mtime contains the * modification time of the newest child, we need to find out if we * exist and when we were modified last. The criteria for datedness * are defined by the Make_OODate function. */ if (DEBUG(MAKE)) { fprintf(debug_file, "Examining %s...", gn->name); } if (! Make_OODate(gn)) { gn->made = UPTODATE; if (DEBUG(MAKE)) { fprintf(debug_file, "up-to-date.\n"); } goto cohorts; } else if (DEBUG(MAKE)) { fprintf(debug_file, "out-of-date.\n"); } /* * If the user is just seeing if something is out-of-date, exit now * to tell him/her "yes". */ if (queryFlag) { exit(1); } /* * We need to be re-made. We also have to make sure we've got a $? * variable. To be nice, we also define the $> variable using * Make_DoAllVar(). */ Make_DoAllVar(gn); /* * Alter our type to tell if errors should be ignored or things * should not be printed so CompatRunCommand knows what to do. */ if (Targ_Ignore(gn)) { gn->type |= OP_IGNORE; } if (Targ_Silent(gn)) { gn->type |= OP_SILENT; } if (Job_CheckCommands(gn, Fatal)) { /* * Our commands are ok, but we still have to worry about the -t * flag... */ if (!touchFlag || (gn->type & OP_MAKE)) { curTarg = gn; #ifdef USE_META if (useMeta && !NoExecute(gn)) { meta_job_start(NULL, gn); } #endif Lst_ForEach(gn->commands, CompatRunCommand, gn); curTarg = NULL; } else { - Job_Touch(gn, gn->type & OP_SILENT); + Job_Touch(gn, (gn->type & OP_SILENT) != 0); } } else { gn->made = ERROR; } #ifdef USE_META if (useMeta && !NoExecute(gn)) { if (meta_job_finish(NULL) != 0) gn->made = ERROR; } #endif if (gn->made != ERROR) { /* * If the node was made successfully, mark it so, update * its modification time and timestamp all its parents. Note * that for .ZEROTIME targets, the timestamping isn't done. * This is to keep its state from affecting that of its parent. */ gn->made = MADE; pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; if (!(gn->type & OP_EXEC)) { pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } } else if (keepgoing) { - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; } else { PrintOnError(gn, "\nStop."); exit(1); } } else if (gn->made == ERROR) { /* * Already had an error when making this beastie. Tell the parent * to abort. */ - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; } else { - if (Lst_Member(gn->iParents, pgn) != NULL) { + if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) { char *p1; - Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn); - free(p1); + const char *target = Var_Value(TARGET, gn, &p1); + Var_Set(IMPSRC, target != NULL ? target : "", pgn); + bmake_free(p1); } switch(gn->made) { case BEINGMADE: Error("Graph cycles through %s", gn->name); gn->made = ERROR; - pgn->flags &= ~REMAKE; + pgn->flags &= ~(unsigned)REMAKE; break; case MADE: if ((gn->type & OP_EXEC) == 0) { pgn->flags |= CHILDMADE; Make_TimeStamp(pgn, gn); } break; case UPTODATE: if ((gn->type & OP_EXEC) == 0) { Make_TimeStamp(pgn, gn); } break; default: break; } } cohorts: Lst_ForEach(gn->cohorts, Compat_Make, pgnp); return 0; } - -/*- - *----------------------------------------------------------------------- - * Compat_Run -- - * Initialize this mode and start making. + +/* Initialize this module and start making. * * Input: - * targs List of target nodes to re-create - * - * Results: - * None. - * - * Side Effects: - * Guess what? - * - *----------------------------------------------------------------------- + * targs The target nodes to re-create */ void Compat_Run(Lst targs) { GNode *gn = NULL;/* Current root target */ int errors; /* Number of targets not remade due to errors */ if (!shellName) Shell_Init(); if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) { bmake_signal(SIGINT, CompatInterrupt); } if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) { bmake_signal(SIGTERM, CompatInterrupt); } if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) { bmake_signal(SIGHUP, CompatInterrupt); } if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) { bmake_signal(SIGQUIT, CompatInterrupt); } ENDNode = Targ_FindNode(".END", TARG_CREATE); ENDNode->type = OP_SPECIAL; /* * If the user has defined a .BEGIN target, execute the commands attached * to it. */ if (!queryFlag) { gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); if (gn != NULL) { Compat_Make(gn, gn); - if (gn->made == ERROR) { - PrintOnError(gn, "\nStop."); - exit(1); - } + if (gn->made == ERROR) { + PrintOnError(gn, "\nStop."); + exit(1); + } } } /* * Expand .USE nodes right now, because they can modify the structure * of the tree. */ Make_ExpandUse(targs); /* * For each entry in the list of targets to create, call Compat_Make on * it to create the thing. Compat_Make will leave the 'made' field of gn * in one of several states: * UPTODATE gn was already up-to-date * MADE gn was recreated successfully * ERROR An error occurred while gn was being created * ABORTED gn was not remade because one of its inferiors * could not be made due to errors. */ errors = 0; - while (!Lst_IsEmpty (targs)) { - gn = (GNode *)Lst_DeQueue(targs); + while (!Lst_IsEmpty(targs)) { + gn = Lst_Dequeue(targs); Compat_Make(gn, gn); if (gn->made == UPTODATE) { printf("`%s' is up to date.\n", gn->name); } else if (gn->made == ABORTED) { printf("`%s' not remade because of errors.\n", gn->name); errors += 1; } } /* * If the user has defined a .END target, run its commands. */ if (errors == 0) { Compat_Make(ENDNode, ENDNode); if (gn->made == ERROR) { PrintOnError(gn, "\nStop."); exit(1); } } } Index: head/contrib/bmake/cond.c =================================================================== --- head/contrib/bmake/cond.c (revision 365365) +++ head/contrib/bmake/cond.c (revision 365366) @@ -1,1439 +1,1282 @@ -/* $NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $ */ +/* $NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"; +static char rcsid[] = "$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; #else -__RCSID("$NetBSD: cond.c,v 1.79 2020/07/09 22:34:08 sjg Exp $"); +__RCSID("$NetBSD: cond.c,v 1.106 2020/08/29 13:38:48 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * cond.c -- * Functions to handle conditionals in a makefile. * * Interface: * Cond_Eval Evaluate the conditional in the passed line. * */ -#include -#include -#include /* For strtoul() error checking */ +#include -#include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" +#include "make.h" +#include "dir.h" /* * The parsing of conditional expressions is based on this grammar: * E -> F || E * E -> F * F -> T && F * F -> T * T -> defined(variable) * T -> make(target) * T -> exists(file) * T -> empty(varspec) * T -> target(name) * T -> commands(name) * T -> symbol * T -> $(varspec) op value * T -> $(varspec) == "string" * T -> $(varspec) != "string" * T -> "string" * T -> ( E ) * T -> ! T * op -> == | != | > | < | >= | <= * - * 'symbol' is some other symbol to which the default function (condDefProc) - * is applied. + * 'symbol' is some other symbol to which the default function is applied. * * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||', * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate * the other terminal symbols, using either the default function or the * function given in the terminal, and return the result as either TOK_TRUE * or TOK_FALSE. * * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. * * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on * error. */ typedef enum { TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR } Token; -/*- - * Structures to handle elegantly the different forms of #if's. The - * last two fields are stored in condInvert and condDefProc, respectively. - */ -static void CondPushBack(Token); -static int CondGetArg(Boolean, char **, char **, const char *); -static Boolean CondDoDefined(int, const char *); -static int CondStrMatch(const void *, const void *); -static Boolean CondDoMake(int, const char *); -static Boolean CondDoExists(int, const char *); -static Boolean CondDoTarget(int, const char *); -static Boolean CondDoCommands(int, const char *); -static Boolean CondCvtArg(char *, double *); -static Token CondToken(Boolean); -static Token CondT(Boolean); -static Token CondF(Boolean); static Token CondE(Boolean); -static int do_Cond_EvalExpression(Boolean *); +static CondEvalResult do_Cond_EvalExpression(Boolean *); -static const struct If { - const char *form; /* Form of if */ - int formlen; /* Length of form */ - Boolean doNot; /* TRUE if default function should be negated */ - Boolean (*defProc)(int, const char *); /* Default function to apply */ -} ifs[] = { - { "def", 3, FALSE, CondDoDefined }, - { "ndef", 4, TRUE, CondDoDefined }, - { "make", 4, FALSE, CondDoMake }, - { "nmake", 5, TRUE, CondDoMake }, - { "", 0, FALSE, CondDoDefined }, - { NULL, 0, FALSE, NULL } -}; - -static const struct If *if_info; /* Info for current statement */ -static char *condExpr; /* The expression to parse */ -static Token condPushBack=TOK_NONE; /* Single push-back token used in +static const struct If *if_info; /* Info for current statement */ +static const char *condExpr; /* The expression to parse */ +static Token condPushBack = TOK_NONE; /* Single push-back token used in * parsing */ -static unsigned int cond_depth = 0; /* current .if nesting level */ -static unsigned int cond_min_depth = 0; /* depth at makefile open */ +static unsigned int cond_depth = 0; /* current .if nesting level */ +static unsigned int cond_min_depth = 0; /* depth at makefile open */ /* * Indicate when we should be strict about lhs of comparisons. * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc) * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers * since lhs is already expanded and we cannot tell if * it was a variable reference or not. */ static Boolean lhsStrict; static int istoken(const char *str, const char *tok, size_t len) { - return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); + return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); } -/*- - *----------------------------------------------------------------------- - * CondPushBack -- - * Push back the most recent token read. We only need one level of - * this, so the thing is just stored in 'condPushback'. - * - * Input: - * t Token to push back into the "stream" - * - * Results: - * None. - * - * Side Effects: - * condPushback is overwritten. - * - *----------------------------------------------------------------------- - */ +/* Push back the most recent token read. We only need one level of + * this, so the thing is just stored in 'condPushback'. */ static void CondPushBack(Token t) { condPushBack = t; } - + /*- - *----------------------------------------------------------------------- - * CondGetArg -- - * Find the argument of a built-in function. + * Parse the argument of a built-in function. * * Results: - * The length of the argument and the address of the argument. - * - * Side Effects: - * The pointer is set to point to the closing parenthesis of the - * function call. - * - *----------------------------------------------------------------------- + * The length of the argument. + * *argPtr receives the argument as string. + * *linePtr is updated to point behind the ')' of the function call. */ static int -CondGetArg(Boolean doEval, char **linePtr, char **argPtr, const char *func) +CondGetArg(Boolean doEval, const char **linePtr, char **argPtr, + const char *func) { - char *cp; - int argLen; - Buffer buf; - int paren_depth; - char ch; + const char *cp; + Buffer buf; + int paren_depth; + char ch; + size_t argLen; cp = *linePtr; if (func != NULL) - /* Skip opening '(' - verfied by caller */ + /* Skip opening '(' - verified by caller */ cp++; if (*cp == '\0') { /* * No arguments whatsoever. Because 'make' and 'defined' aren't really * "reserved words", we don't print a message. I think this is better * than hitting the user with a warning message every time s/he uses * the word 'make' or 'defined' at the beginning of a symbol... */ *argPtr = NULL; return 0; } while (*cp == ' ' || *cp == '\t') { cp++; } /* * Create a buffer for the argument and start it out at 16 characters * long. Why 16? Why not? */ Buf_Init(&buf, 16); paren_depth = 0; for (;;) { ch = *cp; if (ch == 0 || ch == ' ' || ch == '\t') break; if ((ch == '&' || ch == '|') && paren_depth == 0) break; if (*cp == '$') { /* * Parse the variable spec and install it as part of the argument * if it's valid. We tell Var_Parse to complain on an undefined * variable, so we don't do it too. Nor do we return an error, * though perhaps we should... */ - char *cp2; - int len; - void *freeIt; - - cp2 = Var_Parse(cp, VAR_CMD, VARF_UNDEFERR| - (doEval ? VARF_WANTRES : 0), - &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); + int len; + void *freeIt; + VarEvalFlags eflags = VARE_UNDEFERR | (doEval ? VARE_WANTRES : 0); + const char *cp2 = Var_Parse(cp, VAR_CMD, eflags, &len, &freeIt); + Buf_AddStr(&buf, cp2); free(freeIt); cp += len; continue; } if (ch == '(') paren_depth++; - else - if (ch == ')' && --paren_depth < 0) - break; + else if (ch == ')' && --paren_depth < 0) + break; Buf_AddByte(&buf, *cp); cp++; } *argPtr = Buf_GetAll(&buf, &argLen); Buf_Destroy(&buf, FALSE); while (*cp == ' ' || *cp == '\t') { cp++; } if (func != NULL && *cp++ != ')') { Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", - func); + func); return 0; } *linePtr = cp; return argLen; } - -/*- - *----------------------------------------------------------------------- - * CondDoDefined -- - * Handle the 'defined' function for conditionals. - * - * Results: - * TRUE if the given variable is defined. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* Test whether the given variable is defined. */ static Boolean CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) { - char *p1; - Boolean result; - - if (Var_Value(arg, VAR_CMD, &p1) != NULL) { - result = TRUE; - } else { - result = FALSE; - } - - free(p1); + char *freeIt; + Boolean result = Var_Value(arg, VAR_CMD, &freeIt) != NULL; + bmake_free(freeIt); return result; } - -/*- - *----------------------------------------------------------------------- - * CondStrMatch -- - * Front-end for Str_Match so it returns 0 on match and non-zero - * on mismatch. Callback function for CondDoMake via Lst_Find - * - * Results: - * 0 if string matches pattern - * - * Side Effects: - * None - * - *----------------------------------------------------------------------- - */ -static int -CondStrMatch(const void *string, const void *pattern) + +/* Wrapper around Str_Match, to be used by Lst_Find. */ +static Boolean +CondFindStrMatch(const void *string, const void *pattern) { - return !Str_Match(string, pattern); + return Str_Match(string, pattern); } - -/*- - *----------------------------------------------------------------------- - * CondDoMake -- - * Handle the 'make' function for conditionals. - * - * Results: - * TRUE if the given target is being made. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given target is being made. */ static Boolean CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) { - return Lst_Find(create, arg, CondStrMatch) != NULL; + return Lst_Find(create, CondFindStrMatch, arg) != NULL; } - -/*- - *----------------------------------------------------------------------- - * CondDoExists -- - * See if the given file exists. - * - * Results: - * TRUE if the file exists and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given file exists. */ static Boolean CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) { Boolean result; - char *path; + char *path; path = Dir_FindFile(arg, dirSearchPath); if (DEBUG(COND)) { fprintf(debug_file, "exists(%s) result is \"%s\"\n", - arg, path ? path : ""); + arg, path ? path : ""); } if (path != NULL) { result = TRUE; free(path); } else { result = FALSE; } return result; } - -/*- - *----------------------------------------------------------------------- - * CondDoTarget -- - * See if the given node exists and is an actual target. - * - * Results: - * TRUE if the node exists as a target and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ + +/* See if the given node exists and is an actual target. */ static Boolean CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) { - GNode *gn; + GNode *gn; gn = Targ_FindNode(arg, TARG_NOCREATE); return gn != NULL && !OP_NOP(gn->type); } -/*- - *----------------------------------------------------------------------- - * CondDoCommands -- - * See if the given node exists and is an actual target with commands - * associated with it. - * - * Results: - * TRUE if the node exists as a target and has commands associated with - * it and FALSE if it does not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* See if the given node exists and is an actual target with commands + * associated with it. */ static Boolean CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) { - GNode *gn; + GNode *gn; gn = Targ_FindNode(arg, TARG_NOCREATE); return gn != NULL && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); } - + /*- - *----------------------------------------------------------------------- - * CondCvtArg -- - * Convert the given number into a double. - * We try a base 10 or 16 integer conversion first, if that fails - * then we try a floating point conversion instead. + * Convert the given number into a double. + * We try a base 10 or 16 integer conversion first, if that fails + * then we try a floating point conversion instead. * * Results: * Sets 'value' to double value of string. - * Returns 'true' if the convertion suceeded - * - *----------------------------------------------------------------------- + * Returns TRUE if the conversion succeeded. */ static Boolean -CondCvtArg(char *str, double *value) +CondCvtArg(const char *str, double *value) { char *eptr, ech; unsigned long l_val; double d_val; errno = 0; if (!*str) { *value = (double)0; return TRUE; } l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); ech = *eptr; if (ech == 0 && errno != ERANGE) { d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; } else { if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') return FALSE; d_val = strtod(str, &eptr); if (*eptr) return FALSE; } *value = d_val; return TRUE; } /*- - *----------------------------------------------------------------------- - * CondGetString -- - * Get a string from a variable reference or an optionally quoted - * string. This is called for the lhs and rhs of string compares. + * Get a string from a variable reference or an optionally quoted + * string. This is called for the lhs and rhs of string compares. * * Results: - * Sets freeIt if needed, - * Sets quoted if string was quoted, - * Returns NULL on error, - * else returns string - absent any quotes. + * Returns the string, absent any quotes, or NULL on error. + * Sets quoted if the string was quoted. + * Sets freeIt if needed. * * Side Effects: - * Moves condExpr to end of this token. - * - * - *----------------------------------------------------------------------- + * Moves condExpr past the end of this token. */ /* coverity:[+alloc : arg-*2] */ -static char * +static const char * CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) { Buffer buf; - char *cp; - char *str; - int len; - int qt; - char *start; + const char *cp; + const char *str; + int len; + Boolean qt; + const char *start; + VarEvalFlags eflags; Buf_Init(&buf, 0); str = NULL; *freeIt = NULL; *quoted = qt = *condExpr == '"' ? 1 : 0; if (qt) condExpr++; for (start = condExpr; *condExpr && str == NULL; condExpr++) { switch (*condExpr) { case '\\': if (condExpr[1] != '\0') { condExpr++; Buf_AddByte(&buf, *condExpr); } break; case '"': if (qt) { condExpr++; /* we don't want the quotes */ goto got_str; } else Buf_AddByte(&buf, *condExpr); /* likely? */ break; case ')': case '!': case '=': case '>': case '<': case ' ': case '\t': if (!qt) goto got_str; else Buf_AddByte(&buf, *condExpr); break; case '$': /* if we are in quotes, then an undefined variable is ok */ - str = Var_Parse(condExpr, VAR_CMD, - ((!qt && doEval) ? VARF_UNDEFERR : 0) | - (doEval ? VARF_WANTRES : 0), &len, freeIt); + eflags = ((!qt && doEval) ? VARE_UNDEFERR : 0) | + (doEval ? VARE_WANTRES : 0); + str = Var_Parse(condExpr, VAR_CMD, eflags, &len, freeIt); if (str == var_Error) { if (*freeIt) { free(*freeIt); *freeIt = NULL; } /* * Even if !doEval, we still report syntax errors, which * is what getting var_Error back with !doEval means. */ str = NULL; goto cleanup; } condExpr += len; /* * If the '$' was first char (no quotes), and we are * followed by space, the operator or end of expression, * we are done. */ if ((condExpr == start + len) && (*condExpr == '\0' || - isspace((unsigned char) *condExpr) || + isspace((unsigned char)*condExpr) || strchr("!=><)", *condExpr))) { goto cleanup; } /* * Nope, we better copy str to buf */ for (cp = str; *cp; cp++) { Buf_AddByte(&buf, *cp); } if (*freeIt) { free(*freeIt); *freeIt = NULL; } - str = NULL; /* not finished yet */ - condExpr--; /* don't skip over next char */ + str = NULL; /* not finished yet */ + condExpr--; /* don't skip over next char */ break; default: if (strictLHS && !qt && *start != '$' && - !isdigit((unsigned char) *start)) { + !isdigit((unsigned char)*start)) { /* lhs must be quoted, a variable reference or number */ if (*freeIt) { free(*freeIt); *freeIt = NULL; } str = NULL; goto cleanup; } Buf_AddByte(&buf, *condExpr); break; } } - got_str: - str = Buf_GetAll(&buf, NULL); - *freeIt = str; - cleanup: +got_str: + *freeIt = Buf_GetAll(&buf, NULL); + str = *freeIt; +cleanup: Buf_Destroy(&buf, FALSE); return str; } - + +/* The different forms of #if's. */ +static const struct If { + const char *form; /* Form of if */ + size_t formlen; /* Length of form */ + Boolean doNot; /* TRUE if default function should be negated */ + Boolean (*defProc)(int, const char *); /* Default function to apply */ +} ifs[] = { + { "def", 3, FALSE, CondDoDefined }, + { "ndef", 4, TRUE, CondDoDefined }, + { "make", 4, FALSE, CondDoMake }, + { "nmake", 5, TRUE, CondDoMake }, + { "", 0, FALSE, CondDoDefined }, + { NULL, 0, FALSE, NULL } +}; + /*- - *----------------------------------------------------------------------- - * CondToken -- - * Return the next token from the input. + * Return the next token from the input. * - * Results: - * A Token for the next lexical token in the stream. - * * Side Effects: * condPushback will be set back to TOK_NONE if it is used. - * - *----------------------------------------------------------------------- */ static Token compare_expression(Boolean doEval) { - Token t; - char *lhs; - char *rhs; - char *op; - void *lhsFree; - void *rhsFree; + Token t; + const char *lhs; + const char *rhs; + const char *op; + void *lhsFree; + void *rhsFree; Boolean lhsQuoted; Boolean rhsQuoted; - double left, right; + double left, right; t = TOK_ERROR; rhs = NULL; - lhsFree = rhsFree = FALSE; + lhsFree = rhsFree = NULL; lhsQuoted = rhsQuoted = FALSE; /* * Parse the variable spec and skip over it, saving its * value in lhs. */ lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); if (!lhs) goto done; /* * Skip whitespace to get to the operator */ - while (isspace((unsigned char) *condExpr)) + while (isspace((unsigned char)*condExpr)) condExpr++; /* * Make sure the operator is a valid one. If it isn't a * known relational operator, pretend we got a * != 0 comparison. */ op = condExpr; switch (*condExpr) { - case '!': - case '=': - case '<': - case '>': - if (condExpr[1] == '=') { - condExpr += 2; - } else { - condExpr += 1; - } - break; - default: - if (!doEval) { - t = TOK_FALSE; - goto done; - } - /* For .ifxxx "..." check for non-empty string. */ - if (lhsQuoted) { - t = lhs[0] != 0; - goto done; - } - /* For .ifxxx compare against zero */ - if (CondCvtArg(lhs, &left)) { - t = left != 0.0; - goto done; - } - /* For .if ${...} check for non-empty string (defProc is ifdef). */ - if (if_info->form[0] == 0) { - t = lhs[0] != 0; - goto done; - } - /* Otherwise action default test ... */ - t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; + case '!': + case '=': + case '<': + case '>': + if (condExpr[1] == '=') { + condExpr += 2; + } else { + condExpr += 1; + } + break; + default: + if (!doEval) { + t = TOK_FALSE; goto done; + } + /* For .ifxxx "..." check for non-empty string. */ + if (lhsQuoted) { + t = lhs[0] != 0; + goto done; + } + /* For .ifxxx compare against zero */ + if (CondCvtArg(lhs, &left)) { + t = left != 0.0; + goto done; + } + /* For .if ${...} check for non-empty string (defProc is ifdef). */ + if (if_info->form[0] == 0) { + t = lhs[0] != 0; + goto done; + } + /* Otherwise action default test ... */ + t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; + goto done; } while (isspace((unsigned char)*condExpr)) condExpr++; if (*condExpr == '\0') { Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); goto done; } rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); if (!rhs) goto done; if (!doEval) { t = TOK_FALSE; goto done; } if (rhsQuoted || lhsQuoted) { -do_string_compare: + do_string_compare: if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { Parse_Error(PARSE_WARNING, - "String comparison operator should be either == or !="); + "String comparison operator should be either == or !="); goto done; } if (DEBUG(COND)) { fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", - lhs, rhs, op); + lhs, rhs, op); } /* * Null-terminate rhs and perform the comparison. * t is set to the result. */ if (*op == '=') { t = strcmp(lhs, rhs) == 0; } else { t = strcmp(lhs, rhs) != 0; } } else { /* * rhs is either a float or an integer. Convert both the * lhs and the rhs to a double and compare the two. */ if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) goto do_string_compare; if (DEBUG(COND)) { fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, - right, op); + right, op); } - switch(op[0]) { + switch (op[0]) { case '!': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto done; } t = (left != right); break; case '=': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto done; } t = (left == right); break; case '<': if (op[1] == '=') { t = (left <= right); } else { t = (left < right); } break; case '>': if (op[1] == '=') { t = (left >= right); } else { t = (left > right); } break; } } done: free(lhsFree); free(rhsFree); return t; } static int -get_mpt_arg(Boolean doEval, char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) +get_mpt_arg(Boolean doEval, const char **linePtr, char **argPtr, + const char *func MAKE_ATTR_UNUSED) { /* * Use Var_Parse to parse the spec in parens and return * TOK_TRUE if the resulting string is empty. */ - int length; - void *freeIt; - char *val; - char *cp = *linePtr; + int length; + void *val_freeIt; + const char *val; + const char *cp = *linePtr; /* We do all the work here and return the result as the length */ *argPtr = NULL; - val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARF_WANTRES : 0, &length, &freeIt); + val = Var_Parse(cp - 1, VAR_CMD, doEval ? VARE_WANTRES : 0, &length, + &val_freeIt); /* * Advance *linePtr to beyond the closing ). Note that * we subtract one because 'length' is calculated from 'cp - 1'. */ *linePtr = cp - 1 + length; if (val == var_Error) { - free(freeIt); + free(val_freeIt); return -1; } /* A variable is empty when it just contains spaces... 4/15/92, christos */ - while (isspace(*(unsigned char *)val)) + while (isspace((unsigned char)val[0])) val++; /* * For consistency with the other functions we can't generate the * true/false here. */ length = *val ? 2 : 1; - free(freeIt); + free(val_freeIt); return length; } static Boolean CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) { return arglen == 1; } static Token compare_function(Boolean doEval) { static const struct fn_def { - const char *fn_name; - int fn_name_len; - int (*fn_getarg)(Boolean, char **, char **, const char *); - Boolean (*fn_proc)(int, const char *); + const char *fn_name; + size_t fn_name_len; + int (*fn_getarg)(Boolean, const char **, char **, const char *); + Boolean (*fn_proc)(int, const char *); } fn_defs[] = { - { "defined", 7, CondGetArg, CondDoDefined }, - { "make", 4, CondGetArg, CondDoMake }, - { "exists", 6, CondGetArg, CondDoExists }, - { "empty", 5, get_mpt_arg, CondDoEmpty }, - { "target", 6, CondGetArg, CondDoTarget }, - { "commands", 8, CondGetArg, CondDoCommands }, - { NULL, 0, NULL, NULL }, + { "defined", 7, CondGetArg, CondDoDefined }, + { "make", 4, CondGetArg, CondDoMake }, + { "exists", 6, CondGetArg, CondDoExists }, + { "empty", 5, get_mpt_arg, CondDoEmpty }, + { "target", 6, CondGetArg, CondDoTarget }, + { "commands", 8, CondGetArg, CondDoCommands }, + { NULL, 0, NULL, NULL }, }; const struct fn_def *fn_def; - Token t; - char *arg = NULL; - int arglen; - char *cp = condExpr; - char *cp1; + Token t; + char *arg = NULL; + int arglen; + const char *cp = condExpr; + const char *cp1; for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) continue; cp += fn_def->fn_name_len; /* There can only be whitespace before the '(' */ - while (isspace(*(unsigned char *)cp)) + while (isspace((unsigned char)*cp)) cp++; if (*cp != '(') break; arglen = fn_def->fn_getarg(doEval, &cp, &arg, fn_def->fn_name); if (arglen <= 0) { condExpr = cp; return arglen < 0 ? TOK_ERROR : TOK_FALSE; } /* Evaluate the argument using the required function. */ t = !doEval || fn_def->fn_proc(arglen, arg); free(arg); condExpr = cp; return t; } /* Push anything numeric through the compare expression */ cp = condExpr; if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) return compare_expression(doEval); /* * Most likely we have a naked token to apply the default function to. * However ".if a == b" gets here when the "a" is unquoted and doesn't * start with a '$'. This surprises people. * If what follows the function argument is a '=' or '!' then the syntax * would be invalid if we did "defined(a)" - so instead treat as an * expression. */ arglen = CondGetArg(doEval, &cp, &arg, NULL); - for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) + for (cp1 = cp; isspace((unsigned char)*cp1); cp1++) continue; if (*cp1 == '=' || *cp1 == '!') return compare_expression(doEval); condExpr = cp; /* * Evaluate the argument using the default function. * This path always treats .if as .ifdef. To get here the character * after .if must have been taken literally, so the argument cannot * be empty - even if it contained a variable expansion. */ t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; free(arg); return t; } static Token CondToken(Boolean doEval) { Token t; t = condPushBack; if (t != TOK_NONE) { condPushBack = TOK_NONE; return t; } while (*condExpr == ' ' || *condExpr == '\t') { condExpr++; } switch (*condExpr) { case '(': condExpr++; return TOK_LPAREN; case ')': condExpr++; return TOK_RPAREN; case '|': if (condExpr[1] == '|') { condExpr++; } condExpr++; return TOK_OR; case '&': if (condExpr[1] == '&') { condExpr++; } condExpr++; return TOK_AND; case '!': condExpr++; return TOK_NOT; case '#': case '\n': case '\0': return TOK_EOF; case '"': case '$': return compare_expression(doEval); default: return compare_function(doEval); } } /*- *----------------------------------------------------------------------- * CondT -- * Parse a single term in the expression. This consists of a terminal * symbol or TOK_NOT and a terminal symbol (not including the binary * operators): * T -> defined(variable) | make(target) | exists(file) | symbol * T -> ! T | ( E ) * * Results: * TOK_TRUE, TOK_FALSE or TOK_ERROR. * * Side Effects: * Tokens are consumed. * *----------------------------------------------------------------------- */ static Token CondT(Boolean doEval) { - Token t; + Token t; t = CondToken(doEval); if (t == TOK_EOF) { /* * If we reached the end of the expression, the expression * is malformed... */ t = TOK_ERROR; } else if (t == TOK_LPAREN) { /* * T -> ( E ) */ t = CondE(doEval); if (t != TOK_ERROR) { if (CondToken(doEval) != TOK_RPAREN) { t = TOK_ERROR; } } } else if (t == TOK_NOT) { t = CondT(doEval); if (t == TOK_TRUE) { t = TOK_FALSE; } else if (t == TOK_FALSE) { t = TOK_TRUE; } } return t; } - + /*- *----------------------------------------------------------------------- * CondF -- * Parse a conjunctive factor (nice name, wot?) * F -> T && F | T * * Results: * TOK_TRUE, TOK_FALSE or TOK_ERROR * * Side Effects: * Tokens are consumed. * *----------------------------------------------------------------------- */ static Token CondF(Boolean doEval) { - Token l, o; + Token l, o; l = CondT(doEval); if (l != TOK_ERROR) { o = CondToken(doEval); if (o == TOK_AND) { /* * F -> T && F * * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to * parse the r.h.s. anyway (to throw it away). * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no. */ if (l == TOK_TRUE) { l = CondF(doEval); } else { (void)CondF(FALSE); } } else { /* * F -> T */ CondPushBack(o); } } return l; } - + /*- *----------------------------------------------------------------------- * CondE -- * Main expression production. * E -> F || E | F * * Results: * TOK_TRUE, TOK_FALSE or TOK_ERROR. * * Side Effects: * Tokens are, of course, consumed. * *----------------------------------------------------------------------- */ static Token CondE(Boolean doEval) { - Token l, o; + Token l, o; l = CondF(doEval); if (l != TOK_ERROR) { o = CondToken(doEval); if (o == TOK_OR) { /* * E -> F || E * * A similar thing occurs for ||, except that here we make sure * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. * Once again, if l is TOK_FALSE, the result is the r.h.s. and once * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. */ if (l == TOK_FALSE) { l = CondE(doEval); } else { (void)CondE(FALSE); } } else { /* * E -> F */ CondPushBack(o); } } return l; } +static CondEvalResult +do_Cond_EvalExpression(Boolean *value) +{ + + switch (CondE(TRUE)) { + case TOK_TRUE: + if (CondToken(TRUE) == TOK_EOF) { + *value = TRUE; + return COND_PARSE; + } + break; + case TOK_FALSE: + if (CondToken(TRUE) == TOK_EOF) { + *value = FALSE; + return COND_PARSE; + } + break; + default: + case TOK_ERROR: + break; + } + + return COND_INVALID; +} + /*- *----------------------------------------------------------------------- * Cond_EvalExpression -- * Evaluate an expression in the passed line. The expression * consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. * * Results: * COND_PARSE if the condition was valid grammatically * COND_INVALID if not a valid conditional. * * (*value) is set to the boolean value of the condition * * Side Effects: - * None. - * + * Any effects from evaluating the variables. *----------------------------------------------------------------------- */ -int -Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS) +CondEvalResult +Cond_EvalExpression(const struct If *info, char *line, Boolean *value, + int eprint, Boolean strictLHS) { static const struct If *dflt_info; const struct If *sv_if_info = if_info; - char *sv_condExpr = condExpr; + const char *sv_condExpr = condExpr; Token sv_condPushBack = condPushBack; int rval; lhsStrict = strictLHS; while (*line == ' ' || *line == '\t') line++; if (info == NULL && (info = dflt_info) == NULL) { /* Scan for the entry for .if - it can't be first */ - for (info = ifs; ; info++) + for (info = ifs;; info++) if (info->form[0] == 0) break; dflt_info = info; } assert(info != NULL); if_info = info; condExpr = line; condPushBack = TOK_NONE; rval = do_Cond_EvalExpression(value); if (rval == COND_INVALID && eprint) Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); if_info = sv_if_info; condExpr = sv_condExpr; condPushBack = sv_condPushBack; return rval; } -static int -do_Cond_EvalExpression(Boolean *value) -{ - switch (CondE(TRUE)) { - case TOK_TRUE: - if (CondToken(TRUE) == TOK_EOF) { - *value = TRUE; - return COND_PARSE; - } - break; - case TOK_FALSE: - if (CondToken(TRUE) == TOK_EOF) { - *value = FALSE; - return COND_PARSE; - } - break; - default: - case TOK_ERROR: - break; - } - - return COND_INVALID; -} - - /*- *----------------------------------------------------------------------- * Cond_Eval -- * Evaluate the conditional in the passed line. The line * looks like this: * . * where is any of if, ifmake, ifnmake, ifdef, * ifndef, elif, elifmake, elifnmake, elifdef, elifndef * and consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. * * Input: * line Line to parse * * Results: * COND_PARSE if should parse lines after the conditional * COND_SKIP if should skip lines after the conditional * COND_INVALID if not a valid conditional. * - * Side Effects: - * None. - * * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order - * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) + * to detect spurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF), * otherwise .else could be treated as '.elif 1'. - * *----------------------------------------------------------------------- */ -int +CondEvalResult Cond_Eval(char *line) { -#define MAXIF 128 /* maximum depth of .if'ing */ -#define MAXIF_BUMP 32 /* how much to grow by */ + enum { MAXIF = 128 }; /* maximum depth of .if'ing */ + enum { MAXIF_BUMP = 32 }; /* how much to grow by */ enum if_states { IF_ACTIVE, /* .if or .elif part active */ ELSE_ACTIVE, /* .else part active */ SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ - SKIP_TO_ELSE, /* has been true, but not seen '.else' */ + SKIP_TO_ELSE, /* has been true, but not seen '.else' */ SKIP_TO_ENDIF /* nothing else to execute */ }; static enum if_states *cond_state = NULL; static unsigned int max_if_depth = MAXIF; const struct If *ifp; - Boolean isElif; - Boolean value; - int level; /* Level at which to report errors. */ - enum if_states state; + Boolean isElif; + Boolean value; + int level; /* Level at which to report errors. */ + enum if_states state; level = PARSE_FATAL; if (!cond_state) { cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); cond_state[0] = IF_ACTIVE; } /* skip leading character (the '.') and any whitespace */ for (line++; *line == ' ' || *line == '\t'; line++) continue; /* Find what type of if we're dealing with. */ if (line[0] == 'e') { if (line[1] != 'l') { if (!istoken(line + 1, "ndif", 4)) return COND_INVALID; /* End of conditional section */ if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less endif"); return COND_PARSE; } /* Return state for previous conditional */ cond_depth--; - return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + return cond_state[cond_depth] <= ELSE_ACTIVE + ? COND_PARSE : COND_SKIP; } /* Quite likely this is 'else' or 'elif' */ line += 2; if (istoken(line, "se", 2)) { /* It is else... */ if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less else"); return COND_PARSE; } state = cond_state[cond_depth]; switch (state) { case SEARCH_FOR_ELIF: state = ELSE_ACTIVE; break; case ELSE_ACTIVE: case SKIP_TO_ENDIF: Parse_Error(PARSE_WARNING, "extra else"); /* FALLTHROUGH */ default: case IF_ACTIVE: case SKIP_TO_ELSE: state = SKIP_TO_ENDIF; break; } cond_state[cond_depth] = state; return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; } /* Assume for now it is an elif */ isElif = TRUE; } else isElif = FALSE; if (line[0] != 'i' || line[1] != 'f') /* Not an ifxxx or elifxxx line */ return COND_INVALID; /* * Figure out what sort of conditional it is -- what its default * function is, etc. -- by looking in the table of valid "ifs" */ line += 2; - for (ifp = ifs; ; ifp++) { + for (ifp = ifs;; ifp++) { if (ifp->form == NULL) return COND_INVALID; if (istoken(ifp->form, line, ifp->formlen)) { line += ifp->formlen; break; } } /* Now we know what sort of 'if' it is... */ if (isElif) { if (cond_depth == cond_min_depth) { Parse_Error(level, "if-less elif"); return COND_PARSE; } state = cond_state[cond_depth]; if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { Parse_Error(PARSE_WARNING, "extra elif"); cond_state[cond_depth] = SKIP_TO_ENDIF; return COND_SKIP; } if (state != SEARCH_FOR_ELIF) { /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } } else { /* Normal .if */ if (cond_depth + 1 >= max_if_depth) { /* * This is rare, but not impossible. * In meta mode, dirdeps.mk (only runs at level 0) * can need more than the default. */ max_if_depth += MAXIF_BUMP; - cond_state = bmake_realloc(cond_state, max_if_depth * - sizeof(*cond_state)); + cond_state = bmake_realloc(cond_state, + max_if_depth * sizeof(*cond_state)); } state = cond_state[cond_depth]; cond_depth++; if (state > ELSE_ACTIVE) { /* If we aren't parsing the data, treat as always false */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } } /* And evaluate the conditional expresssion */ if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) { /* Syntax error in conditional, error message already output. */ /* Skip everything to matching .endif */ cond_state[cond_depth] = SKIP_TO_ELSE; return COND_SKIP; } if (!value) { cond_state[cond_depth] = SEARCH_FOR_ELIF; return COND_SKIP; } cond_state[cond_depth] = IF_ACTIVE; return COND_PARSE; } - - -/*- - *----------------------------------------------------------------------- - * Cond_End -- - * Make sure everything's clean at the end of a makefile. - * - * Results: - * None. - * - * Side Effects: - * Parse_Error will be called if open conditionals are around. - * - *----------------------------------------------------------------------- - */ void Cond_restore_depth(unsigned int saved_depth) { int open_conds = cond_depth - cond_min_depth; if (open_conds != 0 || saved_depth > cond_depth) { Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, open_conds == 1 ? "" : "s"); cond_depth = cond_min_depth; } cond_min_depth = saved_depth; } unsigned int Cond_save_depth(void) { int depth = cond_min_depth; cond_min_depth = cond_depth; return depth; } Index: head/contrib/bmake/dir.c =================================================================== --- head/contrib/bmake/dir.c (revision 365365) +++ head/contrib/bmake/dir.c (revision 365366) @@ -1,1858 +1,1770 @@ -/* $NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; #else -__RCSID("$NetBSD: dir.c,v 1.76 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: dir.c,v 1.135 2020/09/02 04:32:13 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * dir.c -- * Directory searching using wildcards and/or normal names... * Used both for source wildcarding in the Makefile and for finding * implicit sources. * * The interface for this module is: * Dir_Init Initialize the module. * * Dir_InitCur Set the cur Path. * * Dir_InitDot Set the dot Path. * * Dir_End Cleanup the module. * * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. * * Dir_HasWildcards Returns TRUE if the name given it needs to * be wildcard-expanded. * * Dir_Expand Given a pattern and a path, return a Lst of names * which match the pattern on the search path. * * Dir_FindFile Searches for a file on a given search path. * If it exists, the entire path is returned. * Otherwise NULL is returned. * * Dir_FindHereOrAbove Search for a path in the current directory and * then all the directories above it in turn until * the path is found or we reach the root ("/"). * * Dir_MTime Return the modification time of a node. The file * is searched for along the default search path. * The path and mtime fields of the node are filled * in. * * Dir_AddDir Add a directory to a search path. * * Dir_MakeFlags Given a search path and a command flag, create * a string with each of the directories in the path * preceded by the command flag and all of them * separated by a space. * * Dir_Destroy Destroy an element of a search path. Frees up all * things that can be freed for the element as long * as the element is no longer referenced by any other * search path. * Dir_ClearPath Resets a search path to the empty list. * * For debugging: * Dir_PrintDirectories Print stats about the directory cache. */ #include #include #include #include #include #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" + +#define DIR_DEBUG0(fmt) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt) + +#define DIR_DEBUG1(fmt, arg1) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1) + +#define DIR_DEBUG2(fmt, arg1, arg2) \ + if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) + + /* * A search path consists of a Lst of Path structures. A Path structure * has in it the name of the directory and a hash table of all the files * in the directory. This is used to cut down on the number of system * calls necessary to find implicit dependents and their like. Since * these searches are made before any actions are taken, we need not * worry about the directory changing due to creation commands. If this * hampers the style of some makefiles, they must be changed. * * A list of all previously-read directories is kept in the * openDirectories Lst. This list is checked first before a directory * is opened. * * The need for the caching of whole directories is brought about by * the multi-level transformation code in suff.c, which tends to search * for far more files than regular make does. In the initial * implementation, the amount of time spent performing "stat" calls was * truly astronomical. The problem with hashing at the start is, * of course, that pmake doesn't then detect changes to these directories * during the course of the make. Three possibilities suggest themselves: * * 1) just use stat to test for a file's existence. As mentioned * above, this is very inefficient due to the number of checks * engendered by the multi-level transformation code. * 2) use readdir() and company to search the directories, keeping * them open between checks. I have tried this and while it * didn't slow down the process too much, it could severely * affect the amount of parallelism available as each directory * open would take another file descriptor out of play for * handling I/O for another job. Given that it is only recently * that UNIX OS's have taken to allowing more than 20 or 32 * file descriptors for a process, this doesn't seem acceptable * to me. * 3) record the mtime of the directory in the Path structure and * verify the directory hasn't changed since the contents were * hashed. This will catch the creation or deletion of files, * but not the updating of files. However, since it is the * creation and deletion that is the problem, this could be * a good thing to do. Unfortunately, if the directory (say ".") * were fairly large and changed fairly frequently, the constant * rehashing could seriously degrade performance. It might be * good in such cases to keep track of the number of rehashes * and if the number goes over a (small) limit, resort to using * stat in its place. * * An additional thing to consider is that pmake is used primarily * to create C programs and until recently pcc-based compilers refused * to allow you to specify where the resulting object file should be * placed. This forced all objects to be created in the current * directory. This isn't meant as a full excuse, just an explanation of * some of the reasons for the caching used here. * * One more note: the location of a target's file is only performed * on the downward traversal of the graph and then only for terminal * nodes in the graph. This could be construed as wrong in some cases, * but prevents inadvertent modification of files when the "installed" * directory for a file is provided in the search path. * * Another data structure maintained by this module is an mtime * cache used when the searching of cached directories fails to find * a file. In the past, Dir_FindFile would simply perform an access() * call in such a case to determine if the file could be found using * just the name given. When this hit, however, all that was gained * was the knowledge that the file existed. Given that an access() is * essentially a stat() without the copyout() call, and that the same * filesystem overhead would have to be incurred in Dir_MTime, it made * sense to replace the access() with a stat() and record the mtime * in a cache for when Dir_MTime was actually called. */ -Lst dirSearchPath; /* main search path */ +Lst dirSearchPath; /* main search path */ -static Lst openDirectories; /* the list of all open directories */ +static Lst openDirectories; /* the list of all open directories */ /* * Variables for gathering statistics on the efficiency of the hashing * mechanism. */ -static int hits, /* Found in directory cache */ - misses, /* Sad, but not evil misses */ - nearmisses, /* Found under search path */ - bigmisses; /* Sought by itself */ +static int hits; /* Found in directory cache */ +static int misses; /* Sad, but not evil misses */ +static int nearmisses; /* Found under search path */ +static int bigmisses; /* Sought by itself */ -static Path *dot; /* contents of current directory */ -static Path *cur; /* contents of current directory, if not dot */ -static Path *dotLast; /* a fake path entry indicating we need to - * look for . last */ -static Hash_Table mtimes; /* Results of doing a last-resort stat in - * Dir_FindFile -- if we have to go to the - * system to find the file, we might as well - * have its mtime on record. XXX: If this is done - * way early, there's a chance other rules will - * have already updated the file, in which case - * we'll update it again. Generally, there won't - * be two rules to update a single file, so this - * should be ok, but... */ +static Path *dot; /* contents of current directory */ +static Path *cur; /* contents of current directory, if not dot */ +static Path *dotLast; /* a fake path entry indicating we need to + * look for . last */ -static Hash_Table lmtimes; /* same as mtimes but for lstat */ +/* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to + * the system to find the file, we might as well have its mtime on record. + * + * XXX: If this is done way early, there's a chance other rules will have + * already updated the file, in which case we'll update it again. Generally, + * there won't be two rules to update a single file, so this should be ok, + * but... */ +static Hash_Table mtimes; -static int DirFindName(const void *, const void *); -static int DirMatchFiles(const char *, Path *, Lst); +static Hash_Table lmtimes; /* same as mtimes but for lstat */ + static void DirExpandCurly(const char *, const char *, Lst, Lst); static void DirExpandInt(const char *, Lst, Lst); static int DirPrintWord(void *, void *); static int DirPrintDir(void *, void *); static char *DirLookup(Path *, const char *, const char *, Boolean); static char *DirLookupSubdir(Path *, const char *); static char *DirFindDot(Boolean, const char *, const char *); static char *DirLookupAbs(Path *, const char *, const char *); /* - * We use stat(2) a lot, cache the results + * We use stat(2) a lot, cache the results. * mtime and mode are all we care about. */ struct cache_st { - time_t lmtime; /* lstat */ - time_t mtime; /* stat */ - mode_t mode; + time_t lmtime; /* lstat */ + time_t mtime; /* stat */ + mode_t mode; }; /* minimize changes below */ -#define CST_LSTAT 1 -#define CST_UPDATE 2 +typedef enum { + CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */ + CST_UPDATE = 0x02 /* ignore existing cached entry */ +} CachedStatsFlags; +/* Returns 0 and the result of stat(2) or lstat(2) in *mst, or -1 on error. */ static int -cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, int flags) +cached_stats(Hash_Table *htp, const char *pathname, struct make_stat *mst, + CachedStatsFlags flags) { Hash_Entry *entry; + struct stat sys_st; struct cache_st *cst; int rc; if (!pathname || !pathname[0]) return -1; entry = Hash_FindEntry(htp, pathname); - if (entry && (flags & CST_UPDATE) == 0) { - cst = entry->clientPtr; + if (entry && !(flags & CST_UPDATE)) { + cst = Hash_GetValue(entry); - memset(st, 0, sizeof(*st)); - st->st_mode = cst->mode; - st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; - if (st->st_mtime) { - if (DEBUG(DIR)) { - fprintf(debug_file, "Using cached time %s for %s\n", - Targ_FmtTime(st->st_mtime), pathname); - } + mst->mst_mode = cst->mode; + mst->mst_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; + if (mst->mst_mtime) { + DIR_DEBUG2("Using cached time %s for %s\n", + Targ_FmtTime(mst->mst_mtime), pathname); return 0; } } - rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); + rc = (flags & CST_LSTAT) + ? lstat(pathname, &sys_st) + : stat(pathname, &sys_st); if (rc == -1) return -1; - if (st->st_mtime == 0) - st->st_mtime = 1; /* avoid confusion with missing file */ + if (sys_st.st_mtime == 0) + sys_st.st_mtime = 1; /* avoid confusion with missing file */ - if (!entry) + mst->mst_mode = sys_st.st_mode; + mst->mst_mtime = sys_st.st_mtime; + + if (entry == NULL) entry = Hash_CreateEntry(htp, pathname, NULL); - if (!entry->clientPtr) { - entry->clientPtr = bmake_malloc(sizeof(*cst)); - memset(entry->clientPtr, 0, sizeof(*cst)); + if (Hash_GetValue(entry) == NULL) { + Hash_SetValue(entry, bmake_malloc(sizeof(*cst))); + memset(Hash_GetValue(entry), 0, sizeof(*cst)); } - cst = entry->clientPtr; - if ((flags & CST_LSTAT)) { - cst->lmtime = st->st_mtime; + cst = Hash_GetValue(entry); + if (flags & CST_LSTAT) { + cst->lmtime = sys_st.st_mtime; } else { - cst->mtime = st->st_mtime; + cst->mtime = sys_st.st_mtime; } - cst->mode = st->st_mode; - if (DEBUG(DIR)) { - fprintf(debug_file, " Caching %s for %s\n", - Targ_FmtTime(st->st_mtime), pathname); - } + cst->mode = sys_st.st_mode; + DIR_DEBUG2(" Caching %s for %s\n", + Targ_FmtTime(sys_st.st_mtime), pathname); return 0; } int -cached_stat(const char *pathname, void *st) +cached_stat(const char *pathname, struct make_stat *st) { return cached_stats(&mtimes, pathname, st, 0); } int -cached_lstat(const char *pathname, void *st) +cached_lstat(const char *pathname, struct make_stat *st) { return cached_stats(&lmtimes, pathname, st, CST_LSTAT); } -/*- - *----------------------------------------------------------------------- - * Dir_Init -- - * initialize things for this module - * - * Results: - * none - * - * Side Effects: - * some directories may be opened. - *----------------------------------------------------------------------- - */ +/* Initialize things for this module. */ void -Dir_Init(const char *cdname) +Dir_Init(void) { - if (!cdname) { - dirSearchPath = Lst_Init(FALSE); - openDirectories = Lst_Init(FALSE); - Hash_InitTable(&mtimes, 0); - Hash_InitTable(&lmtimes, 0); - return; - } + dirSearchPath = Lst_Init(); + openDirectories = Lst_Init(); + Hash_InitTable(&mtimes, 0); + Hash_InitTable(&lmtimes, 0); +} + +void +Dir_InitDir(const char *cdname) +{ Dir_InitCur(cdname); dotLast = bmake_malloc(sizeof(Path)); dotLast->refCount = 1; dotLast->hits = 0; dotLast->name = bmake_strdup(".DOTLAST"); Hash_InitTable(&dotLast->files, -1); } /* - * Called by Dir_Init() and whenever .CURDIR is assigned to. + * Called by Dir_InitDir and whenever .CURDIR is assigned to. */ void Dir_InitCur(const char *cdname) { Path *p; if (cdname != NULL) { /* * Our build directory is not the same as our source directory. * Keep this one around too. */ if ((p = Dir_AddDir(NULL, cdname))) { p->refCount += 1; if (cur && cur != p) { /* * We've been here before, cleanup. */ cur->refCount -= 1; Dir_Destroy(cur); } cur = p; } } } -/*- - *----------------------------------------------------------------------- - * Dir_InitDot -- - * (re)initialize "dot" (current/object directory) path hash - * - * Results: - * none - * - * Side Effects: - * some directories may be opened. - *----------------------------------------------------------------------- - */ +/* (Re)initialize "dot" (current/object directory) path hash. + * Some directories may be opened. */ void Dir_InitDot(void) { if (dot != NULL) { LstNode ln; /* Remove old entry from openDirectories, but do not destroy. */ - ln = Lst_Member(openDirectories, dot); - (void)Lst_Remove(openDirectories, ln); + ln = Lst_FindDatum(openDirectories, dot); + Lst_Remove(openDirectories, ln); } dot = Dir_AddDir(NULL, "."); if (dot == NULL) { Error("Cannot open `.' (%s)", strerror(errno)); exit(1); } /* * We always need to have dot around, so we increment its reference count * to make sure it's not destroyed. */ dot->refCount += 1; - Dir_SetPATH(); /* initialize */ + Dir_SetPATH(); /* initialize */ } -/*- - *----------------------------------------------------------------------- - * Dir_End -- - * cleanup things for this module - * - * Results: - * none - * - * Side Effects: - * none - *----------------------------------------------------------------------- - */ +/* Clean up things for this module. */ void Dir_End(void) { #ifdef CLEANUP if (cur) { cur->refCount -= 1; Dir_Destroy(cur); } dot->refCount -= 1; dotLast->refCount -= 1; Dir_Destroy(dotLast); Dir_Destroy(dot); Dir_ClearPath(dirSearchPath); - Lst_Destroy(dirSearchPath, NULL); + Lst_Free(dirSearchPath); Dir_ClearPath(openDirectories); - Lst_Destroy(openDirectories, NULL); + Lst_Free(openDirectories); Hash_DeleteTable(&mtimes); #endif } /* * We want ${.PATH} to indicate the order in which we will actually * search, so we rebuild it after any .PATH: target. * This is the simplest way to deal with the effect of .DOTLAST. */ void Dir_SetPATH(void) { - LstNode ln; /* a list element */ + LstNode ln; /* a list element */ Path *p; - Boolean hasLastDot = FALSE; /* true we should search dot last */ + Boolean hasLastDot = FALSE; /* true if we should search dot last */ Var_Delete(".PATH", VAR_GLOBAL); - if (Lst_Open(dirSearchPath) == SUCCESS) { - if ((ln = Lst_First(dirSearchPath)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) { - hasLastDot = TRUE; - Var_Append(".PATH", dotLast->name, VAR_GLOBAL); - } + Lst_Open(dirSearchPath); + if ((ln = Lst_First(dirSearchPath)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + Var_Append(".PATH", dotLast->name, VAR_GLOBAL); } + } - if (!hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } + if (!hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } - while ((ln = Lst_Next(dirSearchPath)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) - continue; - if (p == dot && hasLastDot) - continue; - Var_Append(".PATH", p->name, VAR_GLOBAL); - } + while ((ln = Lst_Next(dirSearchPath)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) + continue; + if (p == dot && hasLastDot) + continue; + Var_Append(".PATH", p->name, VAR_GLOBAL); + } - if (hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } - Lst_Close(dirSearchPath); + if (hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); } + Lst_Close(dirSearchPath); } -/*- - *----------------------------------------------------------------------- - * DirFindName -- - * See if the Path structure describes the same directory as the - * given one by comparing their names. Called from Dir_AddDir via - * Lst_Find when searching the list of open directories. - * - * Input: - * p Current name - * dname Desired name - * - * Results: - * 0 if it is the same. Non-zero otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -DirFindName(const void *p, const void *dname) +/* See if the Path structure describes the same directory as the + * given one by comparing their names. Called from Dir_AddDir via + * Lst_Find when searching the list of open directories. */ +static Boolean +DirFindName(const void *p, const void *desiredName) { - return strcmp(((const Path *)p)->name, dname); + return strcmp(((const Path *)p)->name, desiredName) == 0; } -/*- - *----------------------------------------------------------------------- - * Dir_HasWildcards -- - * see if the given name has any wildcard characters in it - * be careful not to expand unmatching brackets or braces. - * XXX: This code is not 100% correct. ([^]] fails etc.) - * I really don't think that make(1) should be expanding - * patterns, because then you have to set a mechanism for - * escaping the expansion! +/* See if the given name has any wildcard characters in it. Be careful not to + * expand unmatching brackets or braces. * + * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think + * that make(1) should be expanding patterns, because then you have to set a + * mechanism for escaping the expansion! + * * Input: * name name to check * * Results: * returns TRUE if the word should be expanded, FALSE otherwise - * - * Side Effects: - * none - *----------------------------------------------------------------------- */ Boolean -Dir_HasWildcards(char *name) +Dir_HasWildcards(const char *name) { - char *cp; - int wild = 0, brace = 0, bracket = 0; + const char *cp; + Boolean wild = FALSE; + int braces = 0, brackets = 0; for (cp = name; *cp; cp++) { - switch(*cp) { + switch (*cp) { case '{': - brace++; - wild = 1; - break; + braces++; + wild = TRUE; + break; case '}': - brace--; - break; + braces--; + break; case '[': - bracket++; - wild = 1; - break; + brackets++; + wild = TRUE; + break; case ']': - bracket--; - break; + brackets--; + break; case '?': case '*': - wild = 1; - break; + wild = TRUE; + break; default: - break; + break; } } - return wild && bracket == 0 && brace == 0; + return wild && brackets == 0 && braces == 0; } /*- *----------------------------------------------------------------------- * DirMatchFiles -- * Given a pattern and a Path structure, see if any files * match the pattern and add their names to the 'expansions' list if * any do. This is incomplete -- it doesn't take care of patterns like * src / *src / *.c properly (just *.c on any of the directories), but it * will do for now. * * Input: * pattern Pattern to look for * p Directory to search * expansion Place to store the results * - * Results: - * Always returns 0 - * * Side Effects: * File names are added to the expansions lst. The directory will be * fully hashed when this is done. *----------------------------------------------------------------------- */ -static int +static void DirMatchFiles(const char *pattern, Path *p, Lst expansions) { - Hash_Search search; /* Index into the directory's table */ - Hash_Entry *entry; /* Current entry in the table */ - Boolean isDot; /* TRUE if the directory being searched is . */ + Hash_Search search; /* Index into the directory's table */ + Hash_Entry *entry; /* Current entry in the table */ + Boolean isDot; /* TRUE if the directory being searched is . */ isDot = (*p->name == '.' && p->name[1] == '\0'); for (entry = Hash_EnumFirst(&p->files, &search); entry != NULL; entry = Hash_EnumNext(&search)) { /* * See if the file matches the given pattern. Note we follow the UNIX * convention that dot files will only be found if the pattern * begins with a dot (note also that as a side effect of the hashing * scheme, .* won't match . or .. since they aren't hashed). */ if (Str_Match(entry->name, pattern) && ((entry->name[0] != '.') || (pattern[0] == '.'))) { - (void)Lst_AtEnd(expansions, - (isDot ? bmake_strdup(entry->name) : - str_concat(p->name, entry->name, - STR_ADDSLASH))); + Lst_Append(expansions, + (isDot ? bmake_strdup(entry->name) : + str_concat3(p->name, "/", entry->name))); } } - return 0; } +/* Find the next closing brace in the string, taking nested braces into + * account. */ +static const char * +closing_brace(const char *p) +{ + int nest = 0; + while (*p != '\0') { + if (*p == '}' && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; +} + +/* Find the next closing brace or comma in the string, taking nested braces + * into account. */ +static const char * +separator_comma(const char *p) +{ + int nest = 0; + while (*p != '\0') { + if ((*p == '}' || *p == ',') && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; +} + +static Boolean +contains_wildcard(const char *p) +{ + for (; *p != '\0'; p++) { + switch (*p) { + case '*': + case '?': + case '{': + case '[': + return TRUE; + } + } + return FALSE; +} + +static char * +concat3(const char *a, size_t a_len, const char *b, size_t b_len, + const char *c, size_t c_len) +{ + size_t s_len = a_len + b_len + c_len; + char *s = bmake_malloc(s_len + 1); + memcpy(s, a, a_len); + memcpy(s + a_len, b, b_len); + memcpy(s + a_len + b_len, c, c_len); + s[s_len] = '\0'; + return s; +} + /*- *----------------------------------------------------------------------- * DirExpandCurly -- * Expand curly braces like the C shell. Does this recursively. * Note the special case: if after the piece of the curly brace is * done there are no wildcard characters in the result, the result is * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. * * Input: * word Entire word to expand * brace First curly brace in it * path Search path to use * expansions Place to store the expansions * * Results: * None. * * Side Effects: * The given list is filled with the expansions... * *----------------------------------------------------------------------- */ static void DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) { - const char *end; /* Character after the closing brace */ - const char *cp; /* Current position in brace clause */ - const char *start; /* Start of current piece of brace clause */ - int bracelevel; /* Number of braces we've seen. If we see a - * right brace when this is 0, we've hit the - * end of the clause. */ - char *file; /* Current expansion */ - int otherLen; /* The length of the other pieces of the - * expansion (chars before and after the - * clause in 'word') */ - char *cp2; /* Pointer for checking for wildcards in - * expansion before calling Dir_Expand */ + const char *prefix, *middle, *piece, *middle_end, *suffix; + size_t prefix_len, suffix_len; - start = brace+1; + /* Split the word into prefix '{' middle '}' suffix. */ - /* - * Find the end of the brace clause first, being wary of nested brace - * clauses. - */ - for (end = start, bracelevel = 0; *end != '\0'; end++) { - if (*end == '{') { - bracelevel++; - } else if ((*end == '}') && (bracelevel-- == 0)) { - break; - } - } - if (*end == '\0') { - Error("Unterminated {} clause \"%s\"", start); + middle = brace + 1; + middle_end = closing_brace(middle); + if (*middle_end == '\0') { + Error("Unterminated {} clause \"%s\"", middle); return; - } else { - end++; } - otherLen = brace - word + strlen(end); - for (cp = start; cp < end; cp++) { - /* - * Find the end of this piece of the clause. - */ - bracelevel = 0; - while (*cp != ',') { - if (*cp == '{') { - bracelevel++; - } else if ((*cp == '}') && (bracelevel-- <= 0)) { - break; - } - cp++; - } - /* - * Allocate room for the combination and install the three pieces. - */ - file = bmake_malloc(otherLen + cp - start + 1); - if (brace != word) { - strncpy(file, word, brace-word); - } - if (cp != start) { - strncpy(&file[brace-word], start, cp-start); - } - strcpy(&file[(brace-word)+(cp-start)], end); + prefix = word; + prefix_len = (size_t)(brace - prefix); + suffix = middle_end + 1; + suffix_len = strlen(suffix); - /* - * See if the result has any wildcards in it. If we find one, call - * Dir_Expand right away, telling it to place the result on our list - * of expansions. - */ - for (cp2 = file; *cp2 != '\0'; cp2++) { - switch(*cp2) { - case '*': - case '?': - case '{': - case '[': - Dir_Expand(file, path, expansions); - goto next; - } - } - if (*cp2 == '\0') { - /* - * Hit the end w/o finding any wildcards, so stick the expansion - * on the end of the list. - */ - (void)Lst_AtEnd(expansions, file); - } else { - next: + /* Split the middle into pieces, separated by commas. */ + + piece = middle; + while (piece < middle_end + 1) { + const char *piece_end = separator_comma(piece); + size_t piece_len = (size_t)(piece_end - piece); + + char *file = concat3(prefix, prefix_len, piece, piece_len, + suffix, suffix_len); + + if (contains_wildcard(file)) { + Dir_Expand(file, path, expansions); free(file); + } else { + Lst_Append(expansions, file); } - start = cp+1; + + piece = piece_end + 1; /* skip over the comma or closing brace */ } } /*- *----------------------------------------------------------------------- * DirExpandInt -- * Internal expand routine. Passes through the directories in the * path one by one, calling DirMatchFiles for each. NOTE: This still * doesn't handle patterns in directories... * * Input: * word Word to expand * path Path on which to look * expansions Place to store the result * * Results: * None. * * Side Effects: * Things are added to the expansions list. * *----------------------------------------------------------------------- */ static void DirExpandInt(const char *word, Lst path, Lst expansions) { - LstNode ln; /* Current node */ - Path *p; /* Directory in the node */ + LstNode ln; /* Current node */ - if (Lst_Open(path) == SUCCESS) { - while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - DirMatchFiles(word, p, expansions); - } - Lst_Close(path); + Lst_Open(path); + while ((ln = Lst_Next(path)) != NULL) { + Path *p = LstNode_Datum(ln); + DirMatchFiles(word, p, expansions); } + Lst_Close(path); } -/*- - *----------------------------------------------------------------------- - * DirPrintWord -- - * Print a word in the list of expansions. Callback for Dir_Expand - * when DEBUG(DIR), via Lst_ForEach. - * - * Results: - * === 0 - * - * Side Effects: - * The passed word is printed, followed by a space. - * - *----------------------------------------------------------------------- - */ +/* Print a word in the list of expansions. + * Callback for Dir_Expand when DEBUG(DIR), via Lst_ForEach. */ static int DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) { fprintf(debug_file, "%s ", (char *)word); return 0; } /*- *----------------------------------------------------------------------- * Dir_Expand -- * Expand the given word into a list of words by globbing it looking * in the directories on the given search path. * * Input: * word the word to expand * path the list of directories in which to find the * resulting files * expansions the list on which to place the results * * Results: * A list of words consisting of the files which exist along the search * path matching the given pattern. * * Side Effects: * Directories may be opened. Who knows? + * Undefined behavior if the word is really in read-only memory. *----------------------------------------------------------------------- */ void Dir_Expand(const char *word, Lst path, Lst expansions) { - const char *cp; + const char *cp; - if (DEBUG(DIR)) { - fprintf(debug_file, "Expanding \"%s\"... ", word); - } + assert(path != NULL); + assert(expansions != NULL); + DIR_DEBUG1("Expanding \"%s\"... ", word); + cp = strchr(word, '{'); if (cp) { DirExpandCurly(word, cp, path, expansions); } else { cp = strchr(word, '/'); if (cp) { /* * The thing has a directory component -- find the first wildcard * in the string. */ for (cp = word; *cp; cp++) { if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { break; } } if (*cp == '{') { /* * This one will be fun. */ DirExpandCurly(word, cp, path, expansions); return; } else if (*cp != '\0') { /* * Back up to the start of the component */ - char *dirpath; - while (cp > word && *cp != '/') { cp--; } if (cp != word) { char sc; + char *dirpath; /* * If the glob isn't in the first component, try and find * all the components up to the one with a wildcard. */ sc = cp[1]; ((char *)UNCONST(cp))[1] = '\0'; dirpath = Dir_FindFile(word, path); ((char *)UNCONST(cp))[1] = sc; /* * dirpath is null if can't find the leading component * XXX: Dir_FindFile won't find internal components. * i.e. if the path contains ../Etc/Object and we're * looking for Etc, it won't be found. Ah well. * Probably not important. */ if (dirpath != NULL) { char *dp = &dirpath[strlen(dirpath) - 1]; if (*dp == '/') *dp = '\0'; - path = Lst_Init(FALSE); + path = Lst_Init(); (void)Dir_AddDir(path, dirpath); - DirExpandInt(cp+1, path, expansions); - Lst_Destroy(path, NULL); + DirExpandInt(cp + 1, path, expansions); + Lst_Free(path); } } else { /* * Start the search from the local directory */ DirExpandInt(word, path, expansions); } } else { /* * Return the file -- this should never happen. */ DirExpandInt(word, path, expansions); } } else { /* * First the files in dot */ DirMatchFiles(word, dot, expansions); /* * Then the files in every other directory on the path. */ DirExpandInt(word, path, expansions); } } if (DEBUG(DIR)) { Lst_ForEach(expansions, DirPrintWord, NULL); fprintf(debug_file, "\n"); } } /*- *----------------------------------------------------------------------- * DirLookup -- * Find if the file with the given name exists in the given path. * * Results: * The path to the file or NULL. This path is guaranteed to be in a * different part of memory than name and so may be safely free'd. * * Side Effects: * None. *----------------------------------------------------------------------- */ static char * DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, - Boolean hasSlash MAKE_ATTR_UNUSED) + Boolean hasSlash MAKE_ATTR_UNUSED) { - char *file; /* the current filename to check */ + char *file; /* the current filename to check */ - if (DEBUG(DIR)) { - fprintf(debug_file, " %s ...\n", p->name); - } + DIR_DEBUG1(" %s ...\n", p->name); if (Hash_FindEntry(&p->files, cp) == NULL) return NULL; - file = str_concat(p->name, cp, STR_ADDSLASH); - if (DEBUG(DIR)) { - fprintf(debug_file, " returning %s\n", file); - } + file = str_concat3(p->name, "/", cp); + DIR_DEBUG1(" returning %s\n", file); p->hits += 1; hits += 1; return file; } /*- *----------------------------------------------------------------------- * DirLookupSubdir -- * Find if the file with the given name exists in the given path. * * Results: * The path to the file or NULL. This path is guaranteed to be in a * different part of memory than name and so may be safely free'd. * * Side Effects: * If the file is found, it is added in the modification times hash * table. *----------------------------------------------------------------------- */ static char * DirLookupSubdir(Path *p, const char *name) { - struct stat stb; /* Buffer for stat, if necessary */ - char *file; /* the current filename to check */ + struct make_stat mst; + char *file; /* the current filename to check */ if (p != dot) { - file = str_concat(p->name, name, STR_ADDSLASH); + file = str_concat3(p->name, "/", name); } else { /* * Checking in dot -- DON'T put a leading ./ on the thing. */ file = bmake_strdup(name); } - if (DEBUG(DIR)) { - fprintf(debug_file, "checking %s ...\n", file); - } + DIR_DEBUG1("checking %s ...\n", file); - if (cached_stat(file, &stb) == 0) { + if (cached_stat(file, &mst) == 0) { nearmisses += 1; return file; } free(file); return NULL; } /*- *----------------------------------------------------------------------- * DirLookupAbs -- * Find if the file with the given name exists in the given path. * * Results: * The path to the file, the empty string or NULL. If the file is * the empty string, the search should be terminated. * This path is guaranteed to be in a different part of memory * than name and so may be safely free'd. * * Side Effects: * None. *----------------------------------------------------------------------- */ static char * DirLookupAbs(Path *p, const char *name, const char *cp) { - char *p1; /* pointer into p->name */ - const char *p2; /* pointer into name */ + char *p1; /* pointer into p->name */ + const char *p2; /* pointer into name */ - if (DEBUG(DIR)) { - fprintf(debug_file, " %s ...\n", p->name); - } + DIR_DEBUG1(" %s ...\n", p->name); - /* - * If the file has a leading path component and that component - * exactly matches the entire name of the current search - * directory, we can attempt another cache lookup. And if we don't - * have a hit, we can safely assume the file does not exist at all. - */ - for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { - continue; - } - if (*p1 != '\0' || p2 != cp - 1) { - return NULL; - } + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * directory, we can attempt another cache lookup. And if we don't + * have a hit, we can safely assume the file does not exist at all. + */ + for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { + continue; + } + if (*p1 != '\0' || p2 != cp - 1) { + return NULL; + } - if (Hash_FindEntry(&p->files, cp) == NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " must be here but isn't -- returning\n"); - } - /* Return empty string: terminates search */ - return bmake_strdup(""); - } + if (Hash_FindEntry(&p->files, cp) == NULL) { + DIR_DEBUG0(" must be here but isn't -- returning\n"); + /* Return empty string: terminates search */ + return bmake_strdup(""); + } - p->hits += 1; - hits += 1; - if (DEBUG(DIR)) { - fprintf(debug_file, " returning %s\n", name); - } - return bmake_strdup(name); + p->hits += 1; + hits += 1; + DIR_DEBUG1(" returning %s\n", name); + return bmake_strdup(name); } /*- *----------------------------------------------------------------------- * DirFindDot -- * Find the file given on "." or curdir * * Results: * The path to the file or NULL. This path is guaranteed to be in a * different part of memory than name and so may be safely free'd. * * Side Effects: * Hit counts change *----------------------------------------------------------------------- */ static char * DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) { - if (Hash_FindEntry(&dot->files, cp) != NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " in '.'\n"); - } - hits += 1; - dot->hits += 1; - return bmake_strdup(name); - } - if (cur && - Hash_FindEntry(&cur->files, cp) != NULL) { - if (DEBUG(DIR)) { - fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); - } - hits += 1; - cur->hits += 1; - return str_concat(cur->name, cp, STR_ADDSLASH); - } + if (Hash_FindEntry(&dot->files, cp) != NULL) { + DIR_DEBUG0(" in '.'\n"); + hits += 1; + dot->hits += 1; + return bmake_strdup(name); + } + if (cur && Hash_FindEntry(&cur->files, cp) != NULL) { + DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name); + hits += 1; + cur->hits += 1; + return str_concat3(cur->name, "/", cp); + } - return NULL; + return NULL; } /*- *----------------------------------------------------------------------- * Dir_FindFile -- * Find the file with the given name along the given search path. * * Input: * name the file to find * path the Lst of directories to search * * Results: * The path to the file or NULL. This path is guaranteed to be in a * different part of memory than name and so may be safely free'd. * * Side Effects: * If the file is found in a directory which is not on the path * already (either 'name' is absolute or it is a relative path * [ dir1/.../dirn/file ] which exists below one of the directories * already on the search path), its directory is added to the end * of the path on the assumption that there will be more files in * that directory later on. Sometimes this is true. Sometimes not. *----------------------------------------------------------------------- */ char * Dir_FindFile(const char *name, Lst path) { - LstNode ln; /* a list element */ - char *file; /* the current filename to check */ - Path *p; /* current path member */ - const char *cp; /* Terminal name of file */ - Boolean hasLastDot = FALSE; /* true we should search dot last */ - Boolean hasSlash; /* true if 'name' contains a / */ - struct stat stb; /* Buffer for stat, if necessary */ - const char *trailing_dot = "."; + LstNode ln; /* a list element */ + char *file; /* the current filename to check */ + Path *p; /* current path member */ + const char *cp; /* Terminal name of file */ + Boolean hasLastDot = FALSE; /* true we should search dot last */ + Boolean hasSlash; /* true if 'name' contains a / */ + struct make_stat mst; /* Buffer for stat, if necessary */ + const char *trailing_dot = "."; /* * Find the final component of the name and note whether it has a * slash in it (the name, I mean) */ cp = strrchr(name, '/'); if (cp) { hasSlash = TRUE; cp += 1; } else { hasSlash = FALSE; cp = name; } - if (DEBUG(DIR)) { - fprintf(debug_file, "Searching for %s ...", name); - } + DIR_DEBUG1("Searching for %s ...", name); - if (Lst_Open(path) == FAILURE) { - if (DEBUG(DIR)) { - fprintf(debug_file, "couldn't open path, file not found\n"); - } + if (path == NULL) { + DIR_DEBUG0("couldn't open path, file not found\n"); misses += 1; return NULL; } + Lst_Open(path); if ((ln = Lst_First(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) { hasLastDot = TRUE; - if (DEBUG(DIR)) - fprintf(debug_file, "[dot last]..."); + DIR_DEBUG0("[dot last]..."); } } - if (DEBUG(DIR)) { - fprintf(debug_file, "\n"); - } + DIR_DEBUG0("\n"); /* * If there's no leading directory components or if the leading * directory component is exactly `./', consult the cached contents * of each of the directories on the search path. */ if (!hasSlash || (cp - name == 2 && *name == '.')) { - /* - * We look through all the directories on the path seeking one which - * contains the final component of the given name. If such a beast - * is found, we concatenate the directory name and the final - * component and return the resulting string. If we don't find any - * such thing, we go on to phase two... - * - * No matter what, we always look for the file in the current - * directory before anywhere else (unless we found the magic - * DOTLAST path, in which case we search it last) and we *do not* - * add the ./ to it if it exists. - * This is so there are no conflicts between what the user - * specifies (fish.c) and what pmake finds (./fish.c). - */ - if (!hasLastDot && - (file = DirFindDot(hasSlash, name, cp)) != NULL) { - Lst_Close(path); - return file; - } + /* + * We look through all the directories on the path seeking one which + * contains the final component of the given name. If such a beast + * is found, we concatenate the directory name and the final + * component and return the resulting string. If we don't find any + * such thing, we go on to phase two... + * + * No matter what, we always look for the file in the current + * directory before anywhere else (unless we found the magic + * DOTLAST path, in which case we search it last) and we *do not* + * add the ./ to it if it exists. + * This is so there are no conflicts between what the user + * specifies (fish.c) and what pmake finds (./fish.c). + */ + if (!hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } - while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - if (p == dotLast) - continue; - if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { - Lst_Close(path); - return file; - } + while ((ln = Lst_Next(path)) != NULL) { + p = LstNode_Datum(ln); + if (p == dotLast) + continue; + if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { + Lst_Close(path); + return file; } + } - if (hasLastDot && - (file = DirFindDot(hasSlash, name, cp)) != NULL) { - Lst_Close(path); - return file; - } + if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } } Lst_Close(path); /* * We didn't find the file on any directory in the search path. * If the name doesn't contain a slash, that means it doesn't exist. * If it *does* contain a slash, however, there is still hope: it * could be in a subdirectory of one of the members of the search * path. (eg. /usr/include and sys/types.h. The above search would * fail to turn up types.h in /usr/include, but it *is* in * /usr/include/sys/types.h). * [ This no longer applies: If we find such a beast, we assume there * will be more (what else can we assume?) and add all but the last * component of the resulting name onto the search path (at the * end).] * This phase is only performed if the file is *not* absolute. */ if (!hasSlash) { - if (DEBUG(DIR)) { - fprintf(debug_file, " failed.\n"); - } + DIR_DEBUG0(" failed.\n"); misses += 1; return NULL; } if (*cp == '\0') { /* we were given a trailing "/" */ cp = trailing_dot; } if (name[0] != '/') { - Boolean checkedDot = FALSE; + Boolean checkedDot = FALSE; - if (DEBUG(DIR)) { - fprintf(debug_file, " Trying subdirectories...\n"); - } + DIR_DEBUG0(" Trying subdirectories...\n"); if (!hasLastDot) { - if (dot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; + if (dot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; } - (void)Lst_Open(path); + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) continue; if (p == dot) { - if (checkedDot) - continue; + if (checkedDot) + continue; checkedDot = TRUE; } if ((file = DirLookupSubdir(p, name)) != NULL) { Lst_Close(path); return file; } } Lst_Close(path); if (hasLastDot) { - if (dot && !checkedDot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; + if (dot && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; } if (checkedDot) { /* * Already checked by the given name, since . was in the path, * so no point in proceeding... */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Checked . already, returning NULL\n"); - } + DIR_DEBUG0(" Checked . already, returning NULL\n"); return NULL; } } else { /* name[0] == '/' */ /* * For absolute names, compare directory path prefix against the * the directory path of each member on the search path for an exact * match. If we have an exact match on any member of the search path, * use the cached contents of that member to lookup the final file * component. If that lookup fails we can safely assume that the * file does not exist at all. This is signified by DirLookupAbs() * returning an empty string. */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Trying exact path matches...\n"); - } + DIR_DEBUG0(" Trying exact path matches...\n"); - if (!hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) - != NULL)) { + if (!hasLastDot && cur && + ((file = DirLookupAbs(cur, name, cp)) != NULL)) { if (file[0] == '\0') { free(file); return NULL; } return file; } - (void)Lst_Open(path); + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); if (p == dotLast) continue; if ((file = DirLookupAbs(p, name, cp)) != NULL) { Lst_Close(path); if (file[0] == '\0') { free(file); return NULL; } return file; } } Lst_Close(path); - if (hasLastDot && cur && ((file = DirLookupAbs(cur, name, cp)) - != NULL)) { + if (hasLastDot && cur && + ((file = DirLookupAbs(cur, name, cp)) != NULL)) { if (file[0] == '\0') { free(file); return NULL; } return file; } } /* * Didn't find it that way, either. Sigh. Phase 3. Add its directory * onto the search path in any case, just in case, then look for the * thing in the hash table. If we find it, grand. We return a new * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. * Note that if the directory holding the file doesn't exist, this will * do an extra search of the final directory on the path. Unless something * weird happens, this search won't succeed and life will be groovy. * * Sigh. We cannot add the directory onto the search path because * of this amusing case: * $(INSTALLDIR)/$(FILE): $(FILE) * * $(FILE) exists in $(INSTALLDIR) but not in the current one. * When searching for $(FILE), we will find it in $(INSTALLDIR) * b/c we added it here. This is not good... */ #ifdef notdef if (cp == traling_dot) { cp = strrchr(name, '/'); cp += 1; } cp[-1] = '\0'; (void)Dir_AddDir(path, name); cp[-1] = '/'; bigmisses += 1; ln = Lst_Last(path); if (ln == NULL) { return NULL; } else { - p = (Path *)Lst_Datum(ln); + p = LstNode_Datum(ln); } if (Hash_FindEntry(&p->files, cp) != NULL) { return bmake_strdup(name); } else { return NULL; } #else /* !notdef */ - if (DEBUG(DIR)) { - fprintf(debug_file, " Looking for \"%s\" ...\n", name); - } + DIR_DEBUG1(" Looking for \"%s\" ...\n", name); bigmisses += 1; - if (cached_stat(name, &stb) == 0) { + if (cached_stat(name, &mst) == 0) { return bmake_strdup(name); } - if (DEBUG(DIR)) { - fprintf(debug_file, " failed. Returning NULL\n"); - } + DIR_DEBUG0(" failed. Returning NULL\n"); return NULL; #endif /* notdef */ } /*- *----------------------------------------------------------------------- * Dir_FindHereOrAbove -- * search for a path starting at a given directory and then working * our way up towards the root. * * Input: * here starting directory * search_path the path we are looking for * result the result of a successful search is placed here - * rlen the length of the result buffer + * result_len the length of the result buffer * (typically MAXPATHLEN + 1) * * Results: * 0 on failure, 1 on success [in which case the found path is put * in the result buffer]. * * Side Effects: *----------------------------------------------------------------------- */ -int -Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { +Boolean +Dir_FindHereOrAbove(const char *here, const char *search_path, + char *result, int result_len) +{ + struct make_stat mst; + char dirbase[MAXPATHLEN + 1], *dirbase_end; + char try[MAXPATHLEN + 1], *try_end; - struct stat st; - char dirbase[MAXPATHLEN + 1], *db_end; - char try[MAXPATHLEN + 1], *try_end; + /* copy out our starting point */ + snprintf(dirbase, sizeof(dirbase), "%s", here); + dirbase_end = dirbase + strlen(dirbase); - /* copy out our starting point */ - snprintf(dirbase, sizeof(dirbase), "%s", here); - db_end = dirbase + strlen(dirbase); + /* loop until we determine a result */ + while (TRUE) { - /* loop until we determine a result */ - while (1) { + /* try and stat(2) it ... */ + snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); + if (cached_stat(try, &mst) != -1) { + /* + * success! if we found a file, chop off + * the filename so we return a directory. + */ + if ((mst.mst_mode & S_IFMT) != S_IFDIR) { + try_end = try + strlen(try); + while (try_end > try && *try_end != '/') + try_end--; + if (try_end > try) + *try_end = '\0'; /* chop! */ + } - /* try and stat(2) it ... */ - snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); - if (cached_stat(try, &st) != -1) { - /* - * success! if we found a file, chop off - * the filename so we return a directory. - */ - if ((st.st_mode & S_IFMT) != S_IFDIR) { - try_end = try + strlen(try); - while (try_end > try && *try_end != '/') - try_end--; - if (try_end > try) - *try_end = 0; /* chop! */ - } + snprintf(result, result_len, "%s", try); + return TRUE; + } - /* - * done! - */ - snprintf(result, rlen, "%s", try); - return 1; - } + /* + * nope, we didn't find it. if we used up dirbase we've + * reached the root and failed. + */ + if (dirbase_end == dirbase) + break; /* failed! */ - /* - * nope, we didn't find it. if we used up dirbase we've - * reached the root and failed. - */ - if (db_end == dirbase) - break; /* failed! */ - - /* - * truncate dirbase from the end to move up a dir - */ - while (db_end > dirbase && *db_end != '/') - db_end--; - *db_end = 0; /* chop! */ - - } /* while (1) */ - /* - * we failed... + * truncate dirbase from the end to move up a dir */ - return 0; + while (dirbase_end > dirbase && *dirbase_end != '/') + dirbase_end--; + *dirbase_end = '\0'; /* chop! */ + + } /* while (TRUE) */ + + return FALSE; } /*- *----------------------------------------------------------------------- * Dir_MTime -- * Find the modification time of the file described by gn along the * search path dirSearchPath. * * Input: * gn the file whose modification time is desired * * Results: * The modification time or 0 if it doesn't exist * * Side Effects: * The modification time is placed in the node's mtime slot. * If the node didn't have a path entry before, and Dir_FindFile * found one for it, the full name is placed in the path slot. *----------------------------------------------------------------------- */ int Dir_MTime(GNode *gn, Boolean recheck) { - char *fullName; /* the full pathname of name */ - struct stat stb; /* buffer for finding the mod time */ + char *fullName; /* the full pathname of name */ + struct make_stat mst; /* buffer for finding the mod time */ if (gn->type & OP_ARCHV) { return Arch_MTime(gn); } else if (gn->type & OP_PHONY) { gn->mtime = 0; return 0; } else if (gn->path == NULL) { if (gn->type & OP_NOPATH) fullName = NULL; else { fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); if (fullName == NULL && gn->flags & FROM_DEPEND && - !Lst_IsEmpty(gn->iParents)) { + !Lst_IsEmpty(gn->implicitParents)) { char *cp; cp = strrchr(gn->name, '/'); if (cp) { /* * This is an implied source, and it may have moved, * see if we can find it via the current .PATH */ cp++; fullName = Dir_FindFile(cp, Suff_FindPath(gn)); if (fullName) { /* * Put the found file in gn->path * so that we give that to the compiler. */ gn->path = bmake_strdup(fullName); if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, - "%s: %s, %d: ignoring stale %s for %s, " - "found %s\n", progname, gn->fname, gn->lineno, - makeDependfile, gn->name, fullName); + "%s: %s, %d: ignoring stale %s for %s, " + "found %s\n", progname, gn->fname, + gn->lineno, + makeDependfile, gn->name, fullName); } } } - if (DEBUG(DIR)) - fprintf(debug_file, "Found '%s' as '%s'\n", - gn->name, fullName ? fullName : "(not found)" ); + DIR_DEBUG2("Found '%s' as '%s'\n", + gn->name, fullName ? fullName : "(not found)"); } } else { fullName = gn->path; } if (fullName == NULL) { fullName = bmake_strdup(gn->name); } - if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { + if (cached_stats(&mtimes, fullName, &mst, recheck ? CST_UPDATE : 0) < 0) { if (gn->type & OP_MEMBER) { if (fullName != gn->path) free(fullName); return Arch_MemMTime(gn); } else { - stb.st_mtime = 0; + mst.mst_mtime = 0; } } if (fullName && gn->path == NULL) { gn->path = fullName; } - gn->mtime = stb.st_mtime; + gn->mtime = mst.mst_mtime; return gn->mtime; } /*- *----------------------------------------------------------------------- * Dir_AddDir -- * Add the given name to the end of the given path. The order of * the arguments is backwards so ParseDoDependency can do a * Lst_ForEach of its list of paths... * * Input: * path the path to which the directory should be * added + * XXX: Why would this ever be NULL, and what does + * that mean? * name the name of the directory to add * * Results: * none * * Side Effects: * A structure is added to the list and the directory is * read and hashed. *----------------------------------------------------------------------- */ Path * Dir_AddDir(Lst path, const char *name) { - LstNode ln = NULL; /* node in case Path structure is found */ - Path *p = NULL; /* pointer to new Path structure */ - DIR *d; /* for reading directory */ - struct dirent *dp; /* entry in directory */ + LstNode ln = NULL; /* node in case Path structure is found */ + Path *p = NULL; /* pointer to new Path structure */ + DIR *d; /* for reading directory */ + struct dirent *dp; /* entry in directory */ - if (strcmp(name, ".DOTLAST") == 0) { - ln = Lst_Find(path, name, DirFindName); + if (path != NULL && strcmp(name, ".DOTLAST") == 0) { + ln = Lst_Find(path, DirFindName, name); if (ln != NULL) - return (Path *)Lst_Datum(ln); - else { - dotLast->refCount += 1; - (void)Lst_AtFront(path, dotLast); - } + return LstNode_Datum(ln); + + dotLast->refCount++; + Lst_Prepend(path, dotLast); } - if (path) - ln = Lst_Find(openDirectories, name, DirFindName); + if (path != NULL) + ln = Lst_Find(openDirectories, DirFindName, name); if (ln != NULL) { - p = (Path *)Lst_Datum(ln); - if (path && Lst_Member(path, p) == NULL) { + p = LstNode_Datum(ln); + if (Lst_FindDatum(path, p) == NULL) { p->refCount += 1; - (void)Lst_AtEnd(path, p); + Lst_Append(path, p); } - } else { - if (DEBUG(DIR)) { - fprintf(debug_file, "Caching %s ...", name); - } + return p; + } - if ((d = opendir(name)) != NULL) { - p = bmake_malloc(sizeof(Path)); - p->name = bmake_strdup(name); - p->hits = 0; - p->refCount = 1; - Hash_InitTable(&p->files, -1); + DIR_DEBUG1("Caching %s ...", name); - while ((dp = readdir(d)) != NULL) { + if ((d = opendir(name)) != NULL) { + p = bmake_malloc(sizeof(Path)); + p->name = bmake_strdup(name); + p->hits = 0; + p->refCount = 1; + Hash_InitTable(&p->files, -1); + + while ((dp = readdir(d)) != NULL) { #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ - /* - * The sun directory library doesn't check for a 0 inode - * (0-inode slots just take up space), so we have to do - * it ourselves. - */ - if (dp->d_fileno == 0) { - continue; - } -#endif /* sun && d_ino */ - (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); + /* + * The sun directory library doesn't check for a 0 inode + * (0-inode slots just take up space), so we have to do + * it ourselves. + */ + if (dp->d_fileno == 0) { + continue; } - (void)closedir(d); - (void)Lst_AtEnd(openDirectories, p); - if (path != NULL) - (void)Lst_AtEnd(path, p); +#endif /* sun && d_ino */ + (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); } - if (DEBUG(DIR)) { - fprintf(debug_file, "done\n"); - } + (void)closedir(d); + Lst_Append(openDirectories, p); + if (path != NULL) + Lst_Append(path, p); } + DIR_DEBUG0("done\n"); return p; } /*- *----------------------------------------------------------------------- * Dir_CopyDir -- - * Callback function for duplicating a search path via Lst_Duplicate. + * Callback function for duplicating a search path via Lst_Copy. * Ups the reference count for the directory. * * Results: * Returns the Path it was given. - * - * Side Effects: - * The refCount of the path is incremented. - * *----------------------------------------------------------------------- */ void * Dir_CopyDir(void *p) { ((Path *)p)->refCount += 1; return p; } /*- *----------------------------------------------------------------------- * Dir_MakeFlags -- * Make a string by taking all the directories in the given search * path and preceding them by the given flag. Used by the suffix * module to create variables for compilers based on suffix search * paths. * * Input: * flag flag which should precede each directory * path list of directories * * Results: * The string mentioned above. Note that there is no space between * the given flag and each directory. The empty string is returned if * Things don't go well. * * Side Effects: * None *----------------------------------------------------------------------- */ char * Dir_MakeFlags(const char *flag, Lst path) { - char *str; /* the string which will be returned */ - char *s1, *s2;/* the current directory preceded by 'flag' */ - LstNode ln; /* the node of the current directory */ - Path *p; /* the structure describing the current directory */ + Buffer buf; + LstNode ln; /* the node of the current directory */ - str = bmake_strdup(""); + Buf_Init(&buf, 0); - if (Lst_Open(path) == SUCCESS) { + if (path != NULL) { + Lst_Open(path); while ((ln = Lst_Next(path)) != NULL) { - p = (Path *)Lst_Datum(ln); - s2 = str_concat(flag, p->name, 0); - str = str_concat(s1 = str, s2, STR_ADDSPACE); - free(s1); - free(s2); + Path *p = LstNode_Datum(ln); + Buf_AddStr(&buf, " "); + Buf_AddStr(&buf, flag); + Buf_AddStr(&buf, p->name); } Lst_Close(path); } - return str; + return Buf_Destroy(&buf, FALSE); } /*- *----------------------------------------------------------------------- * Dir_Destroy -- * Nuke a directory descriptor, if possible. Callback procedure * for the suffixes module when destroying a search path. * * Input: * pp The directory descriptor to nuke * * Results: * None. * * Side Effects: * If no other path references this directory (refCount == 0), * the Path and all its data are freed. * *----------------------------------------------------------------------- */ void Dir_Destroy(void *pp) { - Path *p = (Path *)pp; + Path *p = (Path *)pp; p->refCount -= 1; if (p->refCount == 0) { - LstNode ln; + LstNode ln; - ln = Lst_Member(openDirectories, p); - (void)Lst_Remove(openDirectories, ln); + ln = Lst_FindDatum(openDirectories, p); + Lst_Remove(openDirectories, ln); Hash_DeleteTable(&p->files); free(p->name); free(p); } } /*- *----------------------------------------------------------------------- * Dir_ClearPath -- * Clear out all elements of the given search path. This is different * from destroying the list, notice. * * Input: * path Path to clear * * Results: * None. * * Side Effects: * The path is set to the empty list. * *----------------------------------------------------------------------- */ void Dir_ClearPath(Lst path) { - Path *p; while (!Lst_IsEmpty(path)) { - p = (Path *)Lst_DeQueue(path); + Path *p = Lst_Dequeue(path); Dir_Destroy(p); } } /*- *----------------------------------------------------------------------- * Dir_Concat -- * Concatenate two paths, adding the second to the end of the first. * Makes sure to avoid duplicates. * * Input: * path1 Dest * path2 Source * * Results: * None * * Side Effects: * Reference counts for added dirs are upped. * *----------------------------------------------------------------------- */ void Dir_Concat(Lst path1, Lst path2) { LstNode ln; - Path *p; + Path *p; - for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { - p = (Path *)Lst_Datum(ln); - if (Lst_Member(path1, p) == NULL) { + for (ln = Lst_First(path2); ln != NULL; ln = LstNode_Next(ln)) { + p = LstNode_Datum(ln); + if (Lst_FindDatum(path1, p) == NULL) { p->refCount += 1; - (void)Lst_AtEnd(path1, p); + Lst_Append(path1, p); } } } +static int +percentage(int num, int den) +{ + return den != 0 ? num * 100 / den : 0; +} + /********** DEBUG INFO **********/ void Dir_PrintDirectories(void) { - LstNode ln; - Path *p; + LstNode ln; fprintf(debug_file, "#*** Directory Cache:\n"); - fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", - hits, misses, nearmisses, bigmisses, - (hits+bigmisses+nearmisses ? - hits * 100 / (hits + bigmisses + nearmisses) : 0)); + fprintf(debug_file, + "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", + hits, misses, nearmisses, bigmisses, + percentage(hits, hits + bigmisses + nearmisses)); fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); - if (Lst_Open(openDirectories) == SUCCESS) { - while ((ln = Lst_Next(openDirectories)) != NULL) { - p = (Path *)Lst_Datum(ln); - fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); - } - Lst_Close(openDirectories); + + Lst_Open(openDirectories); + while ((ln = Lst_Next(openDirectories)) != NULL) { + Path *p = LstNode_Datum(ln); + fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, + p->hits); } + Lst_Close(openDirectories); } static int DirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED) { fprintf(debug_file, "%s ", ((Path *)p)->name); return 0; } void Dir_PrintPath(Lst path) { Lst_ForEach(path, DirPrintDir, NULL); } Index: head/contrib/bmake/dir.h =================================================================== --- head/contrib/bmake/dir.h (revision 365365) +++ head/contrib/bmake/dir.h (revision 365366) @@ -1,108 +1,116 @@ -/* $NetBSD: dir.h,v 1.18 2017/05/31 22:02:06 maya Exp $ */ +/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 */ -/* dir.h -- - */ - #ifndef MAKE_DIR_H #define MAKE_DIR_H -typedef struct Path { +/* A cache of a directory, remembering all the files that exist in that + * directory. */ +typedef struct { char *name; /* Name of directory */ int refCount; /* Number of paths with this directory */ int hits; /* the number of times a file in this * directory has been found */ - Hash_Table files; /* Hash table of files in directory */ + Hash_Table files; /* Hash set of files in directory */ } Path; -void Dir_Init(const char *); +struct make_stat { + time_t mst_mtime; + mode_t mst_mode; +}; + +void Dir_Init(void); +void Dir_InitDir(const char *); void Dir_InitCur(const char *); void Dir_InitDot(void); void Dir_End(void); void Dir_SetPATH(void); -Boolean Dir_HasWildcards(char *); +Boolean Dir_HasWildcards(const char *); void Dir_Expand(const char *, Lst, Lst); char *Dir_FindFile(const char *, Lst); -int Dir_FindHereOrAbove(char *, char *, char *, int); +Boolean Dir_FindHereOrAbove(const char *, const char *, char *, int); int Dir_MTime(GNode *, Boolean); Path *Dir_AddDir(Lst, const char *); char *Dir_MakeFlags(const char *, Lst); void Dir_ClearPath(Lst); void Dir_Concat(Lst, Lst); void Dir_PrintDirectories(void); void Dir_PrintPath(Lst); void Dir_Destroy(void *); -void * Dir_CopyDir(void *); +void *Dir_CopyDir(void *); + +int cached_lstat(const char *, struct make_stat *); +int cached_stat(const char *, struct make_stat *); #endif /* MAKE_DIR_H */ Index: head/contrib/bmake/enum.c =================================================================== --- head/contrib/bmake/enum.c (nonexistent) +++ head/contrib/bmake/enum.c (revision 365366) @@ -0,0 +1,100 @@ +/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */ + +/* + Copyright (c) 2020 Roland Illig + All rights reserved. + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 MAKE_NATIVE +static char rcsid[] = "$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $"; +#else +#include +#ifndef lint +__RCSID("$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $"); +#endif +#endif + +#include +#include +#include + +#include "enum.h" + +/* Convert a bitset into a string representation, showing the names of the + * individual bits. + * + * Optionally, shortcuts for groups of bits can be added. To have an effect, + * they need to be listed before their individual bits. */ +const char * +Enum_FlagsToString(char *buf, size_t buf_size, + int value, const EnumToStringSpec *spec) +{ + const char *buf_start = buf; + const char *sep = ""; + size_t sep_len = 0; + + for (; spec->es_value != 0; spec++) { + size_t name_len; + + if ((value & spec->es_value) != spec->es_value) + continue; + value &= ~spec->es_value; + + assert(buf_size >= sep_len + 1); + memcpy(buf, sep, sep_len); + buf += sep_len; + buf_size -= sep_len; + + name_len = strlen(spec->es_name); + assert(buf_size >= name_len + 1); + memcpy(buf, spec->es_name, name_len); + buf += name_len; + buf_size -= name_len; + + sep = ENUM__SEP; + sep_len = sizeof ENUM__SEP - 1; + } + + /* If this assertion fails, the listed enum values are incomplete. */ + assert(value == 0); + + if (buf == buf_start) + return "none"; + + assert(buf_size >= 1); + buf[0] = '\0'; + return buf_start; +} + +/* Convert a fixed-value enum into a string representation. */ +const char * +Enum_ValueToString(int value, const EnumToStringSpec *spec) +{ + for (; spec->es_name[0] != '\0'; spec++) { + if (value == spec->es_value) + return spec->es_name; + } + abort(/* unknown enum value */); +} Property changes on: head/contrib/bmake/enum.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/enum.h =================================================================== --- head/contrib/bmake/enum.h (nonexistent) +++ head/contrib/bmake/enum.h (revision 365366) @@ -0,0 +1,193 @@ +/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */ + +/* + Copyright (c) 2020 Roland Illig + All rights reserved. + + 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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 MAKE_ENUM_H +#define MAKE_ENUM_H + +/* Generate string representations for bitmasks and simple enums. */ + +#include + +typedef struct { + int es_value; + const char *es_name; +} EnumToStringSpec; + +const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *); +const char *Enum_ValueToString(int, const EnumToStringSpec *); + +/* For Enum_FlagsToString, the separator between flags. */ +#define ENUM__SEP "|" + +/* Generate the string that joins all possible flags, to see how large the + * buffer must be. */ +#define ENUM__JOIN_STR_1(v1) \ + #v1 +#define ENUM__JOIN_STR_2(v1, v2) \ + ENUM__JOIN_STR_1(v1) ENUM__SEP \ + ENUM__JOIN_STR_1(v2) +#define ENUM__JOIN_STR_4(v1, v2, v3, v4) \ + ENUM__JOIN_STR_2(v1, v2) ENUM__SEP \ + ENUM__JOIN_STR_2(v3, v4) +#define ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__JOIN_STR_4(v1, v2, v3, v4) ENUM__SEP \ + ENUM__JOIN_STR_4(v5, v6, v7, v8) +#define ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16) \ + ENUM__JOIN_STR_8(v01, v02, v03, v04, v05, v06, v07, v08) ENUM__SEP \ + ENUM__JOIN_STR_8(v09, v10, v11, v12, v13, v14, v15, v16) + +#define ENUM__JOIN_2(part1, part2) \ + part1 ENUM__SEP part2 +#define ENUM__JOIN_3(part1, part2, part3) \ + part1 ENUM__SEP part2 ENUM__SEP part3 +#define ENUM__JOIN_4(part1, part2, part3, part4) \ + part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 +#define ENUM__JOIN_5(part1, part2, part3, part4, part5) \ + part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 ENUM__SEP part5 + +/* List the pairs of enum value and corresponding name. */ +#define ENUM__SPEC_1(v1) \ + { v1, #v1 } +#define ENUM__SPEC_2(v1, v2) \ + ENUM__SPEC_1(v1), \ + ENUM__SPEC_1(v2) +#define ENUM__SPEC_4(v1, v2, v3, v4) \ + ENUM__SPEC_2(v1, v2), \ + ENUM__SPEC_2(v3, v4) +#define ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8) +#define ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16) \ + ENUM__SPEC_8(v01, v02, v03, v04, v05, v06, v07, v08), \ + ENUM__SPEC_8(v09, v10, v11, v12, v13, v14, v15, v16) + +#define ENUM__SPECS_2(part1, part2) \ + { part1, part2, { 0, "" } } +#define ENUM__SPECS_3(part1, part2, part3) \ + { part1, part2, part3, { 0, "" } } +#define ENUM__SPECS_4(part1, part2, part3, part4) \ + { part1, part2, part3, part4, { 0, "" } } +#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \ + { part1, part2, part3, part4, part5, { 0, "" } } + +/* Declare the necessary data structures for calling Enum_ValueToString. */ +#define ENUM__VALUE_RTTI(typnam, specs) \ + static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs + +/* Declare the necessary data structures for calling Enum_FlagsToString. */ +#define ENUM__FLAGS_RTTI(typnam, specs, joined) \ + static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ + enum { typnam ## _ ## ToStringSize = sizeof joined } + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 3 flags. */ +#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_2(v1, v2), \ + ENUM__SPEC_1(v3)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_2(v1, v2), \ + ENUM__JOIN_STR_1(v3))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 8 flags. */ +#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_4(v1, v2, v3, v4), \ + ENUM__JOIN_STR_4(v5, v6, v7, v8))) + +/* Declare the necessary data structures for calling Enum_ValueToString + * for an enum with 8 constants. */ +#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ + ENUM__VALUE_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_4(v1, v2, v3, v4), \ + ENUM__SPEC_4(v5, v6, v7, v8))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 10 flags. */ +#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \ + ENUM__SPEC_2(v9, v10)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ + ENUM__JOIN_STR_2(v9, v10))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 31 flags. */ +#define ENUM_FLAGS_RTTI_31(typnam, \ + v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16, \ + v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_5( \ + ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \ + ENUM__SPEC_4(v25, v26, v27, v28), \ + ENUM__SPEC_2(v29, v30), \ + ENUM__SPEC_1(v31)), \ + ENUM__JOIN_5( \ + ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__JOIN_STR_8(v17, v18, v19, v20, v21, v22, v23, v24), \ + ENUM__JOIN_STR_4(v25, v26, v27, v28), \ + ENUM__JOIN_STR_2(v29, v30), \ + ENUM__JOIN_STR_1(v31))) + +/* Declare the necessary data structures for calling Enum_FlagsToString + * for an enum with 32 flags. */ +#define ENUM_FLAGS_RTTI_32(typnam, \ + v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16, \ + v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31, v32) \ + ENUM__FLAGS_RTTI(typnam, \ + ENUM__SPECS_2( \ + ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31, v32)), \ + ENUM__JOIN_2( \ + ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \ + v09, v10, v11, v12, v13, v14, v15, v16), \ + ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \ + v25, v26, v27, v28, v29, v30, v31, v32))) + +#endif Property changes on: head/contrib/bmake/enum.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/for.c =================================================================== --- head/contrib/bmake/for.c (revision 365365) +++ head/contrib/bmake/for.c (revision 365366) @@ -1,496 +1,467 @@ -/* $NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. * All rights reserved. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: for.c,v 1.54 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * for.c -- * Functions to handle loops in a makefile. * * Interface: * For_Eval Evaluate the loop in the passed line. * For_Run Run accumulated loop * */ -#include -#include - #include "make.h" -#include "hash.h" -#include "dir.h" -#include "buf.h" #include "strlist.h" #define FOR_SUB_ESCAPE_CHAR 1 #define FOR_SUB_ESCAPE_BRACE 2 #define FOR_SUB_ESCAPE_PAREN 4 /* * For statements are of the form: * * .for in * ... * .endfor * * The trick is to look for the matching end inside for for loop * To do that, we count the current nesting level of the for loops. * and the .endfor statements, accumulating all the statements between * the initial .for loop and the matching .endfor; * then we evaluate the for loop for each variable in the varlist. * * Note that any nested fors are just passed through; they get handled * recursively in For_Eval when we're expanding the enclosing for in * For_Run. */ -static int forLevel = 0; /* Nesting level */ +static int forLevel = 0; /* Nesting level */ /* * State of a for loop. */ -typedef struct _For { - Buffer buf; /* Body of loop */ - strlist_t vars; /* Iteration variables */ - strlist_t items; /* Substitution items */ - char *parse_buf; - int short_var; - int sub_next; +typedef struct { + Buffer buf; /* Body of loop */ + strlist_t vars; /* Iteration variables */ + strlist_t items; /* Substitution items */ + char *parse_buf; + int short_var; + int sub_next; } For; -static For *accumFor; /* Loop being accumulated */ +static For *accumFor; /* Loop being accumulated */ - -static char * -make_str(const char *ptr, int len) -{ - char *new_ptr; - - new_ptr = bmake_malloc(len + 1); - memcpy(new_ptr, ptr, len); - new_ptr[len] = 0; - return new_ptr; -} - static void For_Free(For *arg) { Buf_Destroy(&arg->buf, TRUE); strlist_clean(&arg->vars); strlist_clean(&arg->items); free(arg->parse_buf); free(arg); } /*- *----------------------------------------------------------------------- * For_Eval -- * Evaluate the for loop in the passed line. The line * looks like this: * .for in * * Input: * line Line to parse * * Results: * 0: Not a .for statement, parse the line * 1: We found a for loop * -1: A .for statement with a bad syntax error, discard. * * Side Effects: * None. * *----------------------------------------------------------------------- */ int For_Eval(char *line) { For *new_for; char *ptr = line, *sub; - int len; + size_t len; int escapes; unsigned char ch; - char **words, *word_buf; - int n, nwords; + Words words; /* Skip the '.' and any following whitespace */ - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) continue; /* * If we are not in a for loop quickly determine if the statement is * a for. */ if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || - !isspace((unsigned char) ptr[3])) { - if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) { + !isspace((unsigned char)ptr[3])) { + if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) { Parse_Error(PARSE_FATAL, "for-less endfor"); return -1; } return 0; } ptr += 3; /* * we found a for loop, and now we are going to parse it. */ new_for = bmake_malloc(sizeof *new_for); memset(new_for, 0, sizeof *new_for); /* Grab the variables. Terminate on "in". */ for (;; ptr += len) { - while (*ptr && isspace((unsigned char) *ptr)) + while (*ptr && isspace((unsigned char)*ptr)) ptr++; if (*ptr == '\0') { Parse_Error(PARSE_FATAL, "missing `in' in for"); For_Free(new_for); return -1; } for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) continue; if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { ptr += 2; break; } if (len == 1) new_for->short_var = 1; - strlist_add_str(&new_for->vars, make_str(ptr, len), len); + strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len); } if (strlist_num(&new_for->vars) == 0) { Parse_Error(PARSE_FATAL, "no iteration variables in for"); For_Free(new_for); return -1; } - while (*ptr && isspace((unsigned char) *ptr)) + while (*ptr && isspace((unsigned char)*ptr)) ptr++; /* * Make a list with the remaining words * The values are substituted as ${:U...} so we must \ escape * characters that break that syntax. * Variables are fully expanded - so it is safe for escape $. * We can't do the escapes here - because we don't know whether * we are substuting into ${...} or $(...). */ - sub = Var_Subst(NULL, ptr, VAR_GLOBAL, VARF_WANTRES); + sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES); /* * Split into words allowing for quoted strings. */ - words = brk_string(sub, &nwords, FALSE, &word_buf); + words = Str_Words(sub, FALSE); free(sub); - if (words != NULL) { - for (n = 0; n < nwords; n++) { - ptr = words[n]; + { + size_t n; + + for (n = 0; n < words.len; n++) { + ptr = words.words[n]; if (!*ptr) continue; escapes = 0; while ((ch = *ptr++)) { - switch(ch) { + switch (ch) { case ':': case '$': case '\\': escapes |= FOR_SUB_ESCAPE_CHAR; break; case ')': escapes |= FOR_SUB_ESCAPE_PAREN; break; case /*{*/ '}': escapes |= FOR_SUB_ESCAPE_BRACE; break; } } /* * We have to dup words[n] to maintain the semantics of * strlist. */ - strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes); + strlist_add_str(&new_for->items, bmake_strdup(words.words[n]), + escapes); } - free(words); - free(word_buf); + Words_Free(words); if ((len = strlist_num(&new_for->items)) > 0 && len % (n = strlist_num(&new_for->vars))) { Parse_Error(PARSE_FATAL, - "Wrong number of words (%d) in .for substitution list" - " with %d vars", len, n); + "Wrong number of words (%zu) in .for substitution list" + " with %zu vars", len, n); /* * Return 'success' so that the body of the .for loop is * accumulated. * Remove all items so that the loop doesn't iterate. */ strlist_clean(&new_for->items); } } Buf_Init(&new_for->buf, 0); accumFor = new_for; forLevel = 1; return 1; } /* * Add another line to a .for loop. * Returns 0 when the matching .endfor is reached. */ int For_Accum(char *line) { char *ptr = line; if (*ptr == '.') { - for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++) continue; if (strncmp(ptr, "endfor", 6) == 0 && - (isspace((unsigned char) ptr[6]) || !ptr[6])) { + (isspace((unsigned char)ptr[6]) || !ptr[6])) { if (DEBUG(FOR)) (void)fprintf(debug_file, "For: end for %d\n", forLevel); if (--forLevel <= 0) return 0; } else if (strncmp(ptr, "for", 3) == 0 && - isspace((unsigned char) ptr[3])) { + isspace((unsigned char)ptr[3])) { forLevel++; if (DEBUG(FOR)) (void)fprintf(debug_file, "For: new loop %d\n", forLevel); } } - Buf_AddBytes(&accumFor->buf, strlen(line), line); + Buf_AddStr(&accumFor->buf, line); Buf_AddByte(&accumFor->buf, '\n'); return 1; } - -/*- - *----------------------------------------------------------------------- - * For_Run -- - * Run the for loop, imitating the actions of an include file - * - * Results: - * None. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int +static size_t for_var_len(const char *var) { char ch, var_start, var_end; int depth; - int len; + size_t len; var_start = *var; if (var_start == 0) /* just escape the $ */ return 0; if (var_start == '(') var_end = ')'; else if (var_start == '{') var_end = '}'; else /* Single char variable */ return 1; depth = 1; for (len = 1; (ch = var[len++]) != 0;) { if (ch == var_start) depth++; else if (ch == var_end && --depth == 0) return len; } /* Variable end not found, escape the $ */ return 0; } static void for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) { - const char *item = strlist_str(items, item_no); - int len; char ch; + const char *item = strlist_str(items, item_no); + /* If there were no escapes, or the only escape is the other variable * terminator, then just substitute the full string */ if (!(strlist_info(items, item_no) & - (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { - Buf_AddBytes(cmds, strlen(item), item); + (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { + Buf_AddStr(cmds, item); return; } /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */ while ((ch = *item++) != 0) { if (ch == '$') { - len = for_var_len(item); + size_t len = for_var_len(item); if (len != 0) { - Buf_AddBytes(cmds, len + 1, item - 1); + Buf_AddBytes(cmds, item - 1, len + 1); item += len; continue; } Buf_AddByte(cmds, '\\'); } else if (ch == ':' || ch == '\\' || ch == ech) Buf_AddByte(cmds, '\\'); Buf_AddByte(cmds, ch); } } static char * For_Iterate(void *v_arg, size_t *ret_len) { For *arg = v_arg; - int i, len; + int i; char *var; char *cp; char *cmd_cp; char *body_end; char ch; Buffer cmds; + size_t cmd_len; if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) { /* No more iterations */ For_Free(arg); return NULL; } free(arg->parse_buf); arg->parse_buf = NULL; /* * Scan the for loop body and replace references to the loop variables * with variable references that expand to the required text. * Using variable expansions ensures that the .for loop can't generate * syntax, and that the later parsing will still see a variable. * We assume that the null variable will never be defined. * * The detection of substitions of the loop control variable is naive. * Many of the modifiers use \ to escape $ (not $) so it is possible * to contrive a makefile where an unwanted substitution happens. */ - cmd_cp = Buf_GetAll(&arg->buf, &len); - body_end = cmd_cp + len; - Buf_Init(&cmds, len + 256); + cmd_cp = Buf_GetAll(&arg->buf, &cmd_len); + body_end = cmd_cp + cmd_len; + Buf_Init(&cmds, cmd_len + 256); for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) { char ech; ch = *++cp; if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) { cp++; /* Check variable name against the .for loop variables */ STRLIST_FOREACH(var, &arg->vars, i) { - len = strlist_info(&arg->vars, i); - if (memcmp(cp, var, len) != 0) + size_t vlen = strlist_info(&arg->vars, i); + if (memcmp(cp, var, vlen) != 0) continue; - if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') + if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\') continue; /* Found a variable match. Replace with :U */ - Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); - Buf_AddBytes(&cmds, 2, ":U"); - cp += len; + Buf_AddBytesBetween(&cmds, cmd_cp, cp); + Buf_AddStr(&cmds, ":U"); + cp += vlen; cmd_cp = cp; for_substitute(&cmds, &arg->items, arg->sub_next + i, ech); break; } continue; } if (ch == 0) break; /* Probably a single character name, ignore $$ and stupid ones. {*/ if (!arg->short_var || strchr("}):$", ch) != NULL) { cp++; continue; } STRLIST_FOREACH(var, &arg->vars, i) { if (var[0] != ch || var[1] != 0) continue; /* Found a variable match. Replace with ${:U} */ - Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); - Buf_AddBytes(&cmds, 3, "{:U"); + Buf_AddBytesBetween(&cmds, cmd_cp, cp); + Buf_AddStr(&cmds, "{:U"); cmd_cp = ++cp; for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}'); - Buf_AddBytes(&cmds, 1, "}"); + Buf_AddByte(&cmds, '}'); break; } } - Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp); + Buf_AddBytesBetween(&cmds, cmd_cp, body_end); cp = Buf_Destroy(&cmds, FALSE); if (DEBUG(FOR)) (void)fprintf(debug_file, "For: loop body:\n%s", cp); arg->sub_next += strlist_num(&arg->vars); arg->parse_buf = cp; *ret_len = strlen(cp); return cp; } +/* Run the for loop, imitating the actions of an include file. */ void For_Run(int lineno) { For *arg; arg = accumFor; accumFor = NULL; if (strlist_num(&arg->items) == 0) { - /* Nothing to expand - possibly due to an earlier syntax error. */ - For_Free(arg); - return; + /* Nothing to expand - possibly due to an earlier syntax error. */ + For_Free(arg); + return; } Parse_SetInput(NULL, lineno, -1, For_Iterate, arg); } Index: head/contrib/bmake/hash.c =================================================================== --- head/contrib/bmake/hash.c (revision 365365) +++ head/contrib/bmake/hash.c (revision 365366) @@ -1,477 +1,404 @@ -/* $NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $ */ +/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $"; +static char rcsid[] = "$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: hash.c,v 1.22 2020/07/03 17:03:09 rillig Exp $"); +__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $"); #endif #endif /* not lint */ #endif /* hash.c -- * * This module contains routines to manipulate a hash table. * See hash.h for a definition of the structure of the hash * table. Hash tables grow automatically as the amount of * information increases. */ -#include "sprite.h" #include "make.h" -#include "hash.h" /* * Forward references to local procedures that are used before they're * defined: */ static void RebuildTable(Hash_Table *); /* * The following defines the ratio of # entries to # buckets * at which we rebuild the table to make it larger. */ #define rebuildLimit 3 -/* - *--------------------------------------------------------- +/* The hash function(s) */ + +#ifndef HASH +/* The default: this one matches Gosling's emacs */ +#define HASH(h, key, p) do { \ + for (h = 0, p = key; *p;) \ + h = (h << 5) - h + *p++; \ + } while (0) + +#endif + +/* Sets up the hash table. * - * Hash_InitTable -- - * - * This routine just sets up the hash table. - * * Input: - * t Structure to to hold table. + * t Structure to to hold the table. * numBuckets How many buckets to create for starters. This * number is rounded up to a power of two. If * <= 0, a reasonable default is chosen. The * table will grow in size later as needed. - * - * Results: - * None. - * - * Side Effects: - * Memory is allocated for the initial bucket area. - * - *--------------------------------------------------------- */ - void Hash_InitTable(Hash_Table *t, int numBuckets) { int i; struct Hash_Entry **hp; /* * Round up the size to a power of two. */ if (numBuckets <= 0) i = 16; else { for (i = 2; i < numBuckets; i <<= 1) continue; } t->numEntries = 0; - t->size = i; - t->mask = i - 1; - t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + t->maxchain = 0; + t->bucketsSize = i; + t->bucketsMask = i - 1; + t->buckets = hp = bmake_malloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; } -/* - *--------------------------------------------------------- - * - * Hash_DeleteTable -- - * - * This routine removes everything from a hash table - * and frees up the memory space it occupied (except for - * the space in the Hash_Table structure). - * - * Results: - * None. - * - * Side Effects: - * Lots of memory is freed up. - * - *--------------------------------------------------------- - */ - +/* Removes everything from the hash table and frees up the memory space it + * occupied (except for the space in the Hash_Table structure). */ void Hash_DeleteTable(Hash_Table *t) { struct Hash_Entry **hp, *h, *nexth = NULL; int i; - for (hp = t->bucketPtr, i = t->size; --i >= 0;) { + for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) { for (h = *hp++; h != NULL; h = nexth) { nexth = h->next; free(h); } } - free(t->bucketPtr); + free(t->buckets); /* * Set up the hash table to cause memory faults on any future access * attempts until re-initialization. */ - t->bucketPtr = NULL; + t->buckets = NULL; } -/* - *--------------------------------------------------------- +/* Searches the hash table for an entry corresponding to the key. * - * Hash_FindEntry -- - * - * Searches a hash table for an entry corresponding to key. - * * Input: * t Hash table to search. * key A hash key. * * Results: - * The return value is a pointer to the entry for key, - * if key was present in the table. If key was not - * present, NULL is returned. - * - * Side Effects: - * None. - * - *--------------------------------------------------------- + * Returns a pointer to the entry for key, or NULL if the table contains + * no entry for the key. */ - Hash_Entry * Hash_FindEntry(Hash_Table *t, const char *key) { Hash_Entry *e; unsigned h; const char *p; + int chainlen; - if (t == NULL || t->bucketPtr == NULL) { + if (t == NULL || t->buckets == NULL) { return NULL; } - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; + HASH(h, key, p); p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) + chainlen = 0; +#ifdef DEBUG_HASH_LOOKUP + if (DEBUG(HASH)) + fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__, + t, h, key); +#endif + for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) { + chainlen++; if (e->namehash == h && strcmp(e->name, p) == 0) - return e; - return NULL; + break; + } + if (chainlen > t->maxchain) + t->maxchain = chainlen; + return e; } -/* - *--------------------------------------------------------- +/* Searches the hash table for an entry corresponding to the key. + * If no entry is found, then one is created. * - * Hash_CreateEntry -- - * - * Searches a hash table for an entry corresponding to - * key. If no entry is found, then one is created. - * * Input: * t Hash table to search. * key A hash key. - * newPtr Filled in with TRUE if new entry created, + * newPtr Filled with TRUE if new entry created, * FALSE otherwise. - * - * Results: - * The return value is a pointer to the entry. If *newPtr - * isn't NULL, then *newPtr is filled in with TRUE if a - * new entry was created, and FALSE if an entry already existed - * with the given key. - * - * Side Effects: - * Memory may be allocated, and the hash buckets may be modified. - *--------------------------------------------------------- */ - Hash_Entry * Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) { Hash_Entry *e; unsigned h; const char *p; int keylen; + int chainlen; struct Hash_Entry **hp; /* * Hash the key. As a side effect, save the length (strlen) of the * key in case we need to create the entry. */ - for (h = 0, p = key; *p;) - h = (h << 5) - h + *p++; + HASH(h, key, p); keylen = p - key; p = key; - for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { + chainlen = 0; +#ifdef DEBUG_HASH_LOOKUP + if (DEBUG(HASH)) + fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__, + t, h, key); +#endif + for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) { + chainlen++; if (e->namehash == h && strcmp(e->name, p) == 0) { if (newPtr != NULL) *newPtr = FALSE; - return e; + break; } } + if (chainlen > t->maxchain) + t->maxchain = chainlen; + if (e) + return e; /* * The desired entry isn't there. Before allocating a new entry, * expand the table if necessary (and this changes the resulting * bucket chain). */ - if (t->numEntries >= rebuildLimit * t->size) + if (t->numEntries >= rebuildLimit * t->bucketsSize) RebuildTable(t); e = bmake_malloc(sizeof(*e) + keylen); - hp = &t->bucketPtr[h & t->mask]; + hp = &t->buckets[h & t->bucketsMask]; e->next = *hp; *hp = e; Hash_SetValue(e, NULL); e->namehash = h; (void)strcpy(e->name, p); t->numEntries++; if (newPtr != NULL) *newPtr = TRUE; return e; } -/* - *--------------------------------------------------------- - * - * Hash_DeleteEntry -- - * - * Delete the given hash table entry and free memory associated with - * it. - * - * Results: - * None. - * - * Side Effects: - * Hash chain that entry lives in is modified and memory is freed. - * - *--------------------------------------------------------- - */ - +/* Delete the given hash table entry and free memory associated with it. */ void Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) { Hash_Entry **hp, *p; if (e == NULL) return; - for (hp = &t->bucketPtr[e->namehash & t->mask]; + for (hp = &t->buckets[e->namehash & t->bucketsMask]; (p = *hp) != NULL; hp = &p->next) { if (p == e) { *hp = p->next; free(p); t->numEntries--; return; } } (void)write(2, "bad call to Hash_DeleteEntry\n", 29); abort(); } -/* - *--------------------------------------------------------- +/* Sets things up for enumerating all entries in the hash table. * - * Hash_EnumFirst -- - * This procedure sets things up for a complete search - * of all entries recorded in the hash table. - * * Input: * t Table to be searched. * searchPtr Area in which to keep state about search. * * Results: * The return value is the address of the first entry in * the hash table, or NULL if the table is empty. - * - * Side Effects: - * The information in searchPtr is initialized so that successive - * calls to Hash_Next will return successive HashEntry's - * from the table. - * - *--------------------------------------------------------- */ - Hash_Entry * Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr) { - searchPtr->tablePtr = t; - searchPtr->nextIndex = 0; - searchPtr->hashEntryPtr = NULL; + searchPtr->table = t; + searchPtr->nextBucket = 0; + searchPtr->entry = NULL; return Hash_EnumNext(searchPtr); } -/* - *--------------------------------------------------------- +/* Returns the next entry in the hash table, or NULL if the end of the table + * is reached. * - * Hash_EnumNext -- - * This procedure returns successive entries in the hash table. - * * Input: * searchPtr Area used to keep state about search. - * - * Results: - * The return value is a pointer to the next HashEntry - * in the table, or NULL when the end of the table is - * reached. - * - * Side Effects: - * The information in searchPtr is modified to advance to the - * next entry. - * - *--------------------------------------------------------- */ - Hash_Entry * Hash_EnumNext(Hash_Search *searchPtr) { Hash_Entry *e; - Hash_Table *t = searchPtr->tablePtr; + Hash_Table *t = searchPtr->table; /* - * The hashEntryPtr field points to the most recently returned - * entry, or is nil if we are starting up. If not nil, we have + * The entry field points to the most recently returned + * entry, or is NULL if we are starting up. If not NULL, we have * to start at the next one in the chain. */ - e = searchPtr->hashEntryPtr; + e = searchPtr->entry; if (e != NULL) e = e->next; /* * If the chain ran out, or if we are starting up, we need to * find the next nonempty chain. */ while (e == NULL) { - if (searchPtr->nextIndex >= t->size) + if (searchPtr->nextBucket >= t->bucketsSize) return NULL; - e = t->bucketPtr[searchPtr->nextIndex++]; + e = t->buckets[searchPtr->nextBucket++]; } - searchPtr->hashEntryPtr = e; + searchPtr->entry = e; return e; } -/* - *--------------------------------------------------------- - * - * RebuildTable -- - * This local routine makes a new hash table that - * is larger than the old one. - * - * Results: - * None. - * - * Side Effects: - * The entire hash table is moved, so any bucket numbers - * from the old table are invalid. - * - *--------------------------------------------------------- - */ - +/* Makes a new hash table that is larger than the old one. The entire hash + * table is moved, so any bucket numbers from the old table become invalid. */ static void RebuildTable(Hash_Table *t) { Hash_Entry *e, *next = NULL, **hp, **xp; int i, mask; - Hash_Entry **oldhp; + Hash_Entry **oldhp; int oldsize; - oldhp = t->bucketPtr; - oldsize = i = t->size; + oldhp = t->buckets; + oldsize = i = t->bucketsSize; i <<= 1; - t->size = i; - t->mask = mask = i - 1; - t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + t->bucketsSize = i; + t->bucketsMask = mask = i - 1; + t->buckets = hp = bmake_malloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; for (hp = oldhp, i = oldsize; --i >= 0;) { for (e = *hp++; e != NULL; e = next) { next = e->next; - xp = &t->bucketPtr[e->namehash & mask]; + xp = &t->buckets[e->namehash & mask]; e->next = *xp; *xp = e; } } free(oldhp); + if (DEBUG(HASH)) + fprintf(debug_file, "%s: %p size=%d entries=%d maxchain=%d\n", + __func__, t, t->bucketsSize, t->numEntries, t->maxchain); + t->maxchain = 0; } -void Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data) +void +Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data) { Hash_Search search; Hash_Entry *e; for (e = Hash_EnumFirst(t, &search); e != NULL; e = Hash_EnumNext(&search)) action(Hash_GetValue(e), data); +} + +void +Hash_DebugStats(Hash_Table *t, const char *name) +{ + if (DEBUG(HASH)) + fprintf(debug_file, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n", + name, t->bucketsSize, t->numEntries, t->maxchain); } Index: head/contrib/bmake/hash.h =================================================================== --- head/contrib/bmake/hash.h (revision 365365) +++ head/contrib/bmake/hash.h (revision 365366) @@ -1,150 +1,131 @@ -/* $NetBSD: hash.h,v 1.13 2020/07/03 17:03:09 rillig Exp $ */ +/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 */ -/* hash.h -- - * - * This file contains definitions used by the hash module, - * which maintains hash tables. - */ +/* Hash tables with strings as keys and arbitrary pointers as values. */ -#ifndef _HASH_H -#define _HASH_H +#ifndef MAKE_HASH_H +#define MAKE_HASH_H -/* - * The following defines one entry in the hash table. - */ - +/* A single key-value entry in the hash table. */ typedef struct Hash_Entry { - struct Hash_Entry *next; /* Used to link together all the - * entries associated with the same - * bucket. */ - void *clientPtr; /* Arbitrary pointer */ - unsigned namehash; /* hash value of key */ - char name[1]; /* key string */ + struct Hash_Entry *next; /* Used to link together all the entries + * associated with the same bucket. */ + void *value; + unsigned namehash; /* hash value of key */ + char name[1]; /* key string, variable length */ } Hash_Entry; +/* The hash table containing the entries. */ typedef struct Hash_Table { - struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one - * for each bucket in the table. */ - int size; /* Actual size of array. */ + Hash_Entry **buckets; /* Pointers to Hash_Entry, one + * for each bucket in the table. */ + int bucketsSize; int numEntries; /* Number of entries in the table. */ - int mask; /* Used to select bits for hashing. */ + int bucketsMask; /* Used to select the bucket for a hash. */ + int maxchain; /* max length of chain detected */ } Hash_Table; /* * The following structure is used by the searching routines * to record where we are in the search. */ - typedef struct Hash_Search { - Hash_Table *tablePtr; /* Table being searched. */ - int nextIndex; /* Next bucket to check (after current). */ - Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */ + Hash_Table *table; /* Table being searched. */ + int nextBucket; /* Next bucket to check (after current). */ + Hash_Entry *entry; /* Next entry to check in current bucket. */ } Hash_Search; -/* - * Macros. - */ +static inline void * MAKE_ATTR_UNUSED +Hash_GetValue(Hash_Entry *h) +{ + return h->value; +} -/* - * void * Hash_GetValue(h) - * Hash_Entry *h; - */ +static inline void MAKE_ATTR_UNUSED +Hash_SetValue(Hash_Entry *h, void *datum) +{ + h->value = datum; +} -#define Hash_GetValue(h) ((h)->clientPtr) - -/* - * Hash_SetValue(h, val); - * Hash_Entry *h; - * char *val; - */ - -#define Hash_SetValue(h, val) ((h)->clientPtr = (val)) - -/* - * Hash_Size(n) returns the number of words in an object of n bytes - */ - -#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) - void Hash_InitTable(Hash_Table *, int); void Hash_DeleteTable(Hash_Table *); Hash_Entry *Hash_FindEntry(Hash_Table *, const char *); Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *); void Hash_DeleteEntry(Hash_Table *, Hash_Entry *); Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *); Hash_Entry *Hash_EnumNext(Hash_Search *); void Hash_ForEach(Hash_Table *, void (*)(void *, void *), void *); +void Hash_DebugStats(Hash_Table *, const char *); -#endif /* _HASH_H */ +#endif /* MAKE_HASH_H */ Index: head/contrib/bmake/job.c =================================================================== --- head/contrib/bmake/job.c (revision 365365) +++ head/contrib/bmake/job.c (revision 365366) @@ -1,3179 +1,3147 @@ -/* $NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: job.c,v 1.201 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: job.c,v 1.227 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * job.c -- * handle the creation etc. of our child processes. * * Interface: * Job_Make Start the creation of the given target. * * Job_CatchChildren Check for and handle the termination of any * children. This must be called reasonably * frequently to keep the whole make going at * a decent clip, since job table entries aren't * removed until their process is caught this way. * * Job_CatchOutput Print any output our children have produced. * Should also be called fairly frequently to * keep the user informed of what's going on. * If no output is waiting, it will block for * a time given by the SEL_* constants, below, * or until output is ready. * * Job_Init Called to initialize this module. in addition, * any commands attached to the .BEGIN target * are executed before this function returns. * Hence, the makefile must have been parsed * before this function is called. * * Job_End Cleanup any memory used. * * Job_ParseShell Given the line following a .SHELL target, parse * the line as a shell specification. Returns - * FAILURE if the spec was incorrect. + * FALSE if the spec was incorrect. * * Job_Finish Perform any final processing which needs doing. * This includes the execution of any commands * which have been/were attached to the .END * target. It should only be called when the * job table is empty. * * Job_AbortAll Abort all currently running jobs. It doesn't * handle output or do anything for the jobs, * just kills them. It should only be called in * an emergency, as it were. * * Job_CheckCommands Verify that the commands for a target are * ok. Provide them if necessary and possible. * * Job_Touch Update a target without really updating it. * * Job_Wait Wait for all currently-running jobs to finish. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "wait.h" -#include #include #if !defined(USE_SELECT) && defined(HAVE_POLL_H) #include #else #ifndef USE_SELECT /* no poll.h */ # define USE_SELECT #endif #if defined(HAVE_SYS_SELECT_H) # include #endif #endif #include -#include -#include #include #if defined(HAVE_SYS_SOCKET_H) # include #endif #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" #include "pathnames.h" #include "trace.h" # define STATIC static /* * FreeBSD: traditionally .MAKE is not required to * pass jobs queue to sub-makes. * Use .MAKE.ALWAYS_PASS_JOB_QUEUE=no to disable. */ #define MAKE_ALWAYS_PASS_JOB_QUEUE ".MAKE.ALWAYS_PASS_JOB_QUEUE" static int Always_pass_job_queue = TRUE; /* * FreeBSD: aborting entire parallel make isn't always * desired. When doing tinderbox for example, failure of * one architecture should not stop all. * We still want to bail on interrupt though. */ #define MAKE_JOB_ERROR_TOKEN "MAKE_JOB_ERROR_TOKEN" static int Job_error_token = TRUE; /* * error handling variables */ static int errors = 0; /* number of errors reported */ static int aborting = 0; /* why is the make aborting? */ #define ABORT_ERROR 1 /* Because of an error */ #define ABORT_INTERRUPT 2 /* Because it was interrupted */ #define ABORT_WAIT 3 /* Waiting for jobs to finish */ #define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ /* * this tracks the number of tokens currently "out" to build jobs. */ int jobTokensRunning = 0; -int not_parallel = 0; /* set if .NOT_PARALLEL */ /* * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file * is a char! So when we go above 127 we turn negative! */ #define FILENO(a) ((unsigned) fileno(a)) /* * post-make command processing. The node postCommands is really just the * .END target but we keep it around to avoid having to search for it * all the time. */ static GNode *postCommands = NULL; /* node containing commands to execute when * everything else is done */ static int numCommands; /* The number of commands actually printed * for a target. Should this number be * 0, no shell will be executed. */ /* * Return values from JobStart. */ #define JOB_RUNNING 0 /* Job is running */ #define JOB_ERROR 1 /* Error in starting the job */ #define JOB_FINISHED 2 /* The job is already finished */ /* * Descriptions for various shells. * * The build environment may set DEFSHELL_INDEX to one of * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to * select one of the prefedined shells as the default shell. * * Alternatively, the build environment may set DEFSHELL_CUSTOM to the * name or the full path of a sh-compatible shell, which will be used as * the default shell. * * ".SHELL" lines in Makefiles can choose the default shell from the # set defined here, or add additional shells. */ #ifdef DEFSHELL_CUSTOM #define DEFSHELL_INDEX_CUSTOM 0 #define DEFSHELL_INDEX_SH 1 #define DEFSHELL_INDEX_KSH 2 #define DEFSHELL_INDEX_CSH 3 #else /* !DEFSHELL_CUSTOM */ #define DEFSHELL_INDEX_SH 0 #define DEFSHELL_INDEX_KSH 1 #define DEFSHELL_INDEX_CSH 2 #endif /* !DEFSHELL_CUSTOM */ #ifndef DEFSHELL_INDEX #define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */ #endif /* !DEFSHELL_INDEX */ static Shell shells[] = { #ifdef DEFSHELL_CUSTOM /* * An sh-compatible shell with a non-standard name. * * Keep this in sync with the "sh" description below, but avoid * non-portable features that might not be supplied by all * sh-compatible shells. */ { DEFSHELL_CUSTOM, FALSE, "", "", "", 0, FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', "", "", }, #endif /* DEFSHELL_CUSTOM */ /* * SH description. Echo control is also possible and, under * sun UNIX anyway, one can even control error checking. */ { "sh", FALSE, "", "", "", 0, FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', #if defined(MAKE_NATIVE) && defined(__NetBSD__) "q", #else "", #endif "", }, /* * KSH description. */ { "ksh", TRUE, "set +v", "set -v", "set +v", 6, FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', "v", "", }, /* * CSH description. The csh can do echo control by playing * with the setting of the 'echo' shell variable. Sadly, * however, it is unable to do error control nicely. */ { "csh", TRUE, "unset verbose", "set verbose", "unset verbose", 10, FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#', "v", "e", }, /* * UNKNOWN. */ { NULL, FALSE, NULL, NULL, NULL, 0, FALSE, NULL, NULL, NULL, NULL, 0, NULL, NULL, } }; static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to * which we pass all * commands in the Makefile. * It is set by the * Job_ParseShell function */ const char *shellPath = NULL, /* full pathname of * executable image */ - *shellName = NULL; /* last component of shell */ + *shellName = NULL; /* last component of shell */ char *shellErrFlag = NULL; -static const char *shellArgv = NULL; /* Custom shell args */ +static char *shellArgv = NULL; /* Custom shell args */ STATIC Job *job_table; /* The structures that describe them */ STATIC Job *job_table_end; /* job_table + maxJobs */ static int wantToken; /* we want a token */ static int lurking_children = 0; static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */ /* * Set of descriptors of pipes connected to * the output channels of children */ static struct pollfd *fds = NULL; static Job **jobfds = NULL; static int nfds = 0; static void watchfd(Job *); static void clearfd(Job *); static int readyfd(Job *); STATIC GNode *lastNode; /* The node for which output was most recently * produced. */ static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */ static Job tokenWaitJob; /* token wait pseudo-job */ 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) \ (void)fprintf(fp, TARG_FMT, targPrefix, gn->name) static sigset_t caught_signals; /* Set of signals we handle */ static void JobChildSig(int); static void JobContinueSig(int); static Job *JobFindPid(int, int, Boolean); static int JobPrintCommand(void *, void *); static int JobSaveCommand(void *, void *); static void JobClose(Job *); static void JobExec(Job *, char **); static void JobMakeArgv(Job *, char **); static int JobStart(GNode *, int); static char *JobOutput(Job *, char *, char *, int); static void JobDoOutput(Job *, Boolean); static Shell *JobMatchShell(const char *); static void JobInterrupt(int, int) MAKE_ATTR_DEAD; static void JobRestartJobs(void); static void JobTokenAdd(void); static void JobSigLock(sigset_t *); static void JobSigUnlock(sigset_t *); static void JobSigReset(void); -#if !defined(MALLOC_OPTIONS) -# define MALLOC_OPTIONS "A" -#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) { Job *job; fprintf(debug_file, "job table @ %s\n", where); for (job = job_table; job < job_table_end; job++) { fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n", (int)(job - job_table), job->job_state, job->flags, job->pid); } } /* * Delete the target of a failed, interrupted, or otherwise * unsuccessful job unless inhibited by .PRECIOUS. */ static void JobDeleteTarget(GNode *gn) { if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) { char *file = (gn->path == NULL ? gn->name : gn->path); if (!noExecute && eunlink(file) != -1) { Error("*** %s removed", file); } } } /* * JobSigLock/JobSigUnlock * * Signal lock routines to get exclusive access. Currently used to * protect `jobs' and `stoppedJobs' list manipulations. */ static void JobSigLock(sigset_t *omaskp) { if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) { Punt("JobSigLock: sigprocmask: %s", strerror(errno)); sigemptyset(omaskp); } } static void JobSigUnlock(sigset_t *omaskp) { (void)sigprocmask(SIG_SETMASK, omaskp, NULL); } static void JobCreatePipe(Job *job, int minfd) { int i, fd, flags; if (pipe(job->jobPipe) == -1) Punt("Cannot create pipe: %s", strerror(errno)); for (i = 0; i < 2; i++) { /* Avoid using low numbered fds */ fd = fcntl(job->jobPipe[i], F_DUPFD, minfd); if (fd != -1) { close(job->jobPipe[i]); job->jobPipe[i] = fd; } } /* Set close-on-exec flag for both */ if (fcntl(job->jobPipe[0], F_SETFD, FD_CLOEXEC) == -1) Punt("Cannot set close-on-exec: %s", strerror(errno)); if (fcntl(job->jobPipe[1], F_SETFD, FD_CLOEXEC) == -1) Punt("Cannot set close-on-exec: %s", strerror(errno)); /* * We mark the input side of the pipe non-blocking; we poll(2) the * pipe when we're waiting for a job token, but we might lose the * race for the token when a new one becomes available, so the read * from the pipe should not block. */ flags = fcntl(job->jobPipe[0], F_GETFL, 0); if (flags == -1) Punt("Cannot get flags: %s", strerror(errno)); flags |= O_NONBLOCK; if (fcntl(job->jobPipe[0], F_SETFL, flags) == -1) Punt("Cannot set flags: %s", strerror(errno)); } /*- *----------------------------------------------------------------------- * JobCondPassSig -- * Pass a signal to a job * * Input: * signop Signal to send it * * Side Effects: * None, except the job may bite it. * *----------------------------------------------------------------------- */ static void JobCondPassSig(int signo) { Job *job; if (DEBUG(JOB)) { (void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo); } for (job = job_table; job < job_table_end; job++) { if (job->job_state != JOB_ST_RUNNING) continue; if (DEBUG(JOB)) { (void)fprintf(debug_file, "JobCondPassSig passing signal %d to child %d.\n", signo, job->pid); } KILLPG(job->pid, signo); } } /*- *----------------------------------------------------------------------- * JobChldSig -- * SIGCHLD handler. * * Input: * signo The signal number we've received * * Results: * None. * * Side Effects: * Sends a token on the child exit pipe to wake us up from * select()/poll(). * *----------------------------------------------------------------------- */ static void JobChildSig(int signo MAKE_ATTR_UNUSED) { while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN) continue; } /*- *----------------------------------------------------------------------- * JobContinueSig -- * Resume all stopped jobs. * * Input: * signo The signal number we've received * * Results: * None. * * Side Effects: * Jobs start running again. * *----------------------------------------------------------------------- */ static void JobContinueSig(int signo MAKE_ATTR_UNUSED) { /* * Defer sending to SIGCONT to our stopped children until we return * from the signal handler. */ while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 && errno == EAGAIN) continue; } /*- *----------------------------------------------------------------------- * JobPassSig -- * Pass a signal on to all jobs, then resend to ourselves. * * Input: * signo The signal number we've received * * Results: * None. * * Side Effects: * We die by the same signal. * *----------------------------------------------------------------------- */ MAKE_ATTR_DEAD static void JobPassSig_int(int signo) { /* Run .INTERRUPT target then exit */ JobInterrupt(TRUE, signo); } MAKE_ATTR_DEAD static void JobPassSig_term(int signo) { /* Dont run .INTERRUPT target then exit */ JobInterrupt(FALSE, signo); } static void JobPassSig_suspend(int signo) { sigset_t nmask, omask; struct sigaction act; /* Suppress job started/continued messages */ make_suspended = 1; /* Pass the signal onto every job */ JobCondPassSig(signo); /* * Send ourselves the signal now we've given the message to everyone else. * Note we block everything else possible while we're getting the signal. * This ensures that all our jobs get continued when we wake up before * we take any other signal. */ sigfillset(&nmask); sigdelset(&nmask, signo); (void)sigprocmask(SIG_SETMASK, &nmask, &omask); act.sa_handler = SIG_DFL; sigemptyset(&act.sa_mask); act.sa_flags = 0; (void)sigaction(signo, &act, NULL); if (DEBUG(JOB)) { (void)fprintf(debug_file, "JobPassSig passing signal %d to self.\n", signo); } (void)kill(getpid(), signo); /* * We've been continued. * * A whole host of signals continue to happen! * SIGCHLD for any processes that actually suspended themselves. * SIGCHLD for any processes that exited while we were alseep. * The SIGCONT that actually caused us to wakeup. * * Since we defer passing the SIGCONT on to our children until * the main processing loop, we can be sure that all the SIGCHLD * events will have happened by then - and that the waitpid() will * collect the child 'suspended' events. * For correct sequencing we just need to ensure we process the * waitpid() before passign on the SIGCONT. * * In any case nothing else is needed here. */ /* Restore handler and signal mask */ act.sa_handler = JobPassSig_suspend; (void)sigaction(signo, &act, NULL); (void)sigprocmask(SIG_SETMASK, &omask, NULL); } /*- *----------------------------------------------------------------------- * JobFindPid -- * Compare the pid of the job with the given pid and return 0 if they * are equal. This function is called from Job_CatchChildren * to find the job descriptor of the finished job. * * Input: * job job to examine * pid process id desired * * Results: * Job with matching pid * * Side Effects: * None *----------------------------------------------------------------------- */ static Job * JobFindPid(int pid, int status, Boolean isJobs) { Job *job; for (job = job_table; job < job_table_end; job++) { if ((job->job_state == status) && job->pid == pid) return job; } if (DEBUG(JOB) && isJobs) job_table_dump("no pid"); return NULL; } /*- *----------------------------------------------------------------------- * JobPrintCommand -- * Put out another command for the given job. If the command starts * with an @ or a - we process it specially. In the former case, * so long as the -s and -n flags weren't given to make, we stick * a shell-specific echoOff command in the script. In the latter, * we ignore errors for the entire job, unless the shell has error * control. * If the command is just "..." we take all future commands for this * job to be commands to be executed once the entire graph has been * made and return non-zero to signal that the end of the commands * was reached. These commands are later attached to the postCommands * node and executed by Job_End when all things are done. * This function is called from JobStart via Lst_ForEach. * * Input: * cmdp command string to print * jobp job for which to print it * * Results: * Always 0, unless the command was "..." * * Side Effects: * If the command begins with a '-' and the shell has no error control, * the JOB_IGNERR flag is set in the job descriptor. * If the command is "..." and we're not ignoring such things, * tailCmds is set to the successor node of the cmd. * numCommands is incremented if the command is actually printed. *----------------------------------------------------------------------- */ static int JobPrintCommand(void *cmdp, void *jobp) { Boolean noSpecials; /* true if we shouldn't worry about * inserting special commands into * the input stream. */ Boolean shutUp = FALSE; /* true if we put a no echo command * into the command file */ Boolean errOff = FALSE; /* true if we turned error checking * off before printing the command * and need to turn it back on */ const char *cmdTemplate; /* Template to use when printing the * command */ char *cmdStart; /* Start of expanded command */ char *escCmd = NULL; /* Command with quotes/backticks escaped */ char *cmd = (char *)cmdp; Job *job = (Job *)jobp; - int i, j; noSpecials = NoExecute(job->node); if (strcmp(cmd, "...") == 0) { job->node->type |= OP_SAVE_CMDS; if ((job->flags & JOB_IGNDOTS) == 0) { - job->tailCmds = Lst_Succ(Lst_Member(job->node->commands, - cmd)); + LstNode dotsNode = Lst_FindDatum(job->node->commands, cmd); + job->tailCmds = dotsNode != NULL ? LstNode_Next(dotsNode) : NULL; return 1; } return 0; } #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \ (void)fprintf(debug_file, fmt, arg); \ } \ (void)fprintf(job->cmdFILE, fmt, arg); \ (void)fflush(job->cmdFILE); numCommands += 1; - cmdStart = cmd = Var_Subst(NULL, cmd, job->node, VARF_WANTRES); + cmdStart = cmd = Var_Subst(cmd, job->node, VARE_WANTRES); cmdTemplate = "%s\n"; /* * Check for leading @' and -'s to control echoing and error checking. */ while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) { switch (*cmd) { case '@': shutUp = DEBUG(LOUD) ? FALSE : TRUE; break; case '-': errOff = TRUE; break; case '+': if (noSpecials) { /* * We're not actually executing anything... * but this one needs to be - use compat mode just for it. */ CompatRunCommand(cmdp, job->node); free(cmdStart); return 0; } break; } cmd++; } while (isspace((unsigned char) *cmd)) cmd++; /* * If the shell doesn't have error control the alternate echo'ing will * be done (to avoid showing additional error checking code) * and this will need the characters '$ ` \ "' escaped */ if (!commandShell->hasErrCtl) { + int i, j; + /* Worst that could happen is every char needs escaping. */ escCmd = bmake_malloc((strlen(cmd) * 2) + 1); - for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) { - if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || - cmd[i] == '"') - escCmd[j++] = '\\'; - escCmd[j] = cmd[i]; + for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) { + if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || + cmd[i] == '"') + escCmd[j++] = '\\'; + escCmd[j] = cmd[i]; } - escCmd[j] = 0; + escCmd[j] = '\0'; } if (shutUp) { if (!(job->flags & JOB_SILENT) && !noSpecials && commandShell->hasEchoCtl) { DBPRINTF("%s\n", commandShell->echoOff); } else { if (commandShell->hasErrCtl) shutUp = FALSE; } } if (errOff) { if (!noSpecials) { if (commandShell->hasErrCtl) { /* * we don't want the error-control commands showing * up either, so we turn off echoing while executing * them. We could put another field in the shell * structure to tell JobDoOutput to look for this * string too, but why make it any more complex than * it already is? */ if (!(job->flags & JOB_SILENT) && !shutUp && commandShell->hasEchoCtl) { DBPRINTF("%s\n", commandShell->echoOff); DBPRINTF("%s\n", commandShell->ignErr); DBPRINTF("%s\n", commandShell->echoOn); } else { DBPRINTF("%s\n", commandShell->ignErr); } } else if (commandShell->ignErr && (*commandShell->ignErr != '\0')) { /* * The shell has no error control, so we need to be * weird to get it to ignore any errors from the command. * If echoing is turned on, we turn it off and use the * errCheck template to echo the command. Leave echoing * off so the user doesn't see the weirdness we go through * to ignore errors. Set cmdTemplate to use the weirdness * instead of the simple "%s\n" template. */ job->flags |= JOB_IGNERR; if (!(job->flags & JOB_SILENT) && !shutUp) { if (commandShell->hasEchoCtl) { DBPRINTF("%s\n", commandShell->echoOff); } DBPRINTF(commandShell->errCheck, escCmd); shutUp = TRUE; } else { if (!shutUp) { DBPRINTF(commandShell->errCheck, escCmd); } } cmdTemplate = commandShell->ignErr; /* * The error ignoration (hee hee) is already taken care * of by the ignErr template, so pretend error checking * is still on. */ errOff = FALSE; } else { errOff = FALSE; } } else { errOff = FALSE; } } else { /* * If errors are being checked and the shell doesn't have error control * but does supply an errOut template, then setup commands to run * through it. */ if (!commandShell->hasErrCtl && commandShell->errOut && (*commandShell->errOut != '\0')) { if (!(job->flags & JOB_SILENT) && !shutUp) { if (commandShell->hasEchoCtl) { DBPRINTF("%s\n", commandShell->echoOff); } DBPRINTF(commandShell->errCheck, escCmd); shutUp = TRUE; } /* If it's a comment line or blank, treat as an ignored error */ if ((escCmd[0] == commandShell->commentChar) || (escCmd[0] == 0)) cmdTemplate = commandShell->ignErr; else cmdTemplate = commandShell->errOut; errOff = FALSE; } } if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 && (job->flags & JOB_TRACED) == 0) { DBPRINTF("set -%s\n", "x"); job->flags |= JOB_TRACED; } DBPRINTF(cmdTemplate, cmd); free(cmdStart); free(escCmd); if (errOff) { /* * If echoing is already off, there's no point in issuing the * echoOff command. Otherwise we issue it and pretend it was on * for the whole command... */ if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ DBPRINTF("%s\n", commandShell->echoOff); shutUp = TRUE; } DBPRINTF("%s\n", commandShell->errCheck); } if (shutUp && commandShell->hasEchoCtl) { DBPRINTF("%s\n", commandShell->echoOn); } return 0; } /*- *----------------------------------------------------------------------- * JobSaveCommand -- * Save a command to be executed when everything else is done. * Callback function for JobFinish... * * Results: * Always returns 0 * * Side Effects: - * The command is tacked onto the end of postCommands's commands list. + * The command is tacked onto the end of postCommands' commands list. * *----------------------------------------------------------------------- */ static int JobSaveCommand(void *cmd, void *gn) { - cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, VARF_WANTRES); - (void)Lst_AtEnd(postCommands->commands, cmd); + cmd = Var_Subst((char *)cmd, (GNode *)gn, VARE_WANTRES); + Lst_Append(postCommands->commands, cmd); return 0; } /*- *----------------------------------------------------------------------- * JobClose -- * Called to close both input and output pipes when a job is finished. * * Results: * Nada * * Side Effects: * The file descriptors associated with the job are closed. * *----------------------------------------------------------------------- */ static void JobClose(Job *job) { clearfd(job); (void)close(job->outPipe); job->outPipe = -1; JobDoOutput(job, TRUE); (void)close(job->inPipe); job->inPipe = -1; } /*- *----------------------------------------------------------------------- * JobFinish -- * Do final processing for the given job including updating * parents and starting new jobs as available/necessary. Note * that we pay no attention to the JOB_IGNERR flag here. * This is because when we're called because of a noexecute flag * or something, jstat.w_status is 0 and when called from * Job_CatchChildren, the status is zeroed if it s/b ignored. * * Input: * job job to finish * status sub-why job went away * * Results: * None * * Side Effects: * Final commands for the job are placed on postCommands. * * If we got an error and are aborting (aborting == ABORT_ERROR) and * the job list is now empty, we are done for the day. * If we recognized an error (errors !=0), we set the aborting flag * to ABORT_ERROR so no more jobs will be started. *----------------------------------------------------------------------- */ /*ARGSUSED*/ static void JobFinish (Job *job, WAIT_T status) { Boolean done, return_job_token; if (DEBUG(JOB)) { fprintf(debug_file, "Jobfinish: %d [%s], status %d\n", job->pid, job->node->name, status); } if ((WIFEXITED(status) && (((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) || WIFSIGNALED(status)) { /* * If it exited non-zero and either we're doing things our * way or we're not ignoring errors, the job is finished. * Similarly, if the shell died because of a signal * the job is also finished. In these * cases, finish out the job's output before printing the exit * status... */ JobClose(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { (void)fclose(job->cmdFILE); job->cmdFILE = NULL; } done = TRUE; } else if (WIFEXITED(status)) { /* * Deal with ignored errors in -B mode. We need to print a message * telling of the ignored error as well as setting status.w_status * to 0 so the next command gets run. To do this, we set done to be * TRUE if in -B mode and the job exited non-zero. */ done = WEXITSTATUS(status) != 0; /* * Old comment said: "Note we don't * want to close down any of the streams until we know we're at the * end." * But we do. Otherwise when are we going to print the rest of the * stuff? */ JobClose(job); } else { /* * No need to close things down or anything. */ done = FALSE; } if (done) { if (WIFEXITED(status)) { if (DEBUG(JOB)) { (void)fprintf(debug_file, "Process %d [%s] exited.\n", job->pid, job->node->name); } if (WEXITSTATUS(status) != 0) { if (job->node != lastNode) { MESSAGE(stdout, job->node); lastNode = job->node; } #ifdef USE_META if (useMeta) { meta_job_error(job, job->node, job->flags, WEXITSTATUS(status)); } #endif if (!dieQuietly(job->node, -1)) (void)printf("*** [%s] Error code %d%s\n", job->node->name, WEXITSTATUS(status), (job->flags & JOB_IGNERR) ? " (ignored)" : ""); if (job->flags & JOB_IGNERR) { WAIT_STATUS(status) = 0; } else { if (deleteOnError) { JobDeleteTarget(job->node); } PrintOnError(job->node, NULL); } } else if (DEBUG(JOB)) { if (job->node != lastNode) { MESSAGE(stdout, job->node); lastNode = job->node; } (void)printf("*** [%s] Completed successfully\n", job->node->name); } } else { if (job->node != lastNode) { MESSAGE(stdout, job->node); lastNode = job->node; } (void)printf("*** [%s] Signal %d\n", job->node->name, WTERMSIG(status)); if (deleteOnError) { JobDeleteTarget(job->node); } } (void)fflush(stdout); } #ifdef USE_META if (useMeta) { int x; if ((x = meta_job_finish(job)) != 0 && status == 0) { status = x; } } #endif return_job_token = FALSE; Trace_Log(JOBEND, job); if (!(job->flags & JOB_SPECIAL)) { if ((WAIT_STATUS(status) != 0) || (aborting == ABORT_ERROR) || (aborting == ABORT_INTERRUPT)) return_job_token = TRUE; } if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (WAIT_STATUS(status) == 0)) { /* * As long as we aren't aborting and the job didn't return a non-zero * status that we shouldn't ignore, we call Make_Update to update * the parents. In addition, any saved commands for the node are placed * on the .END target. */ if (job->tailCmds != NULL) { Lst_ForEachFrom(job->node->commands, job->tailCmds, JobSaveCommand, - job->node); + job->node); } job->node->made = MADE; if (!(job->flags & JOB_SPECIAL)) return_job_token = TRUE; Make_Update(job->node); job->job_state = JOB_ST_FREE; } else if (WAIT_STATUS(status)) { errors += 1; job->job_state = JOB_ST_FREE; } /* * Set aborting if any error. */ if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { /* * If we found any errors in this batch of children and the -k flag * wasn't given, we set the aborting flag so no more jobs get * started. */ aborting = ABORT_ERROR; } if (return_job_token) Job_TokenReturn(); if (aborting == ABORT_ERROR && jobTokensRunning == 0) { /* * If we are aborting and the job table is now empty, we finish. */ Finish(errors); } } /*- *----------------------------------------------------------------------- * Job_Touch -- * Touch the given target. Called by JobStart when the -t flag was * given * * Input: * gn the node of the file to touch * silent TRUE if should not print message * * Results: * None * * Side Effects: * The data modification of the file is changed. In addition, if the * file did not exist, it is created. *----------------------------------------------------------------------- */ void Job_Touch(GNode *gn, Boolean silent) { int streamID; /* ID of stream opened to do the touch */ struct utimbuf times; /* Times for utime() call */ if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL| OP_SPECIAL|OP_PHONY)) { /* * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets * and, as such, shouldn't really be created. */ return; } if (!silent || NoExecute(gn)) { (void)fprintf(stdout, "touch %s\n", gn->name); (void)fflush(stdout); } if (NoExecute(gn)) { return; } if (gn->type & OP_ARCHV) { Arch_Touch(gn); } else if (gn->type & OP_LIB) { Arch_TouchLib(gn); } else { char *file = gn->path ? gn->path : gn->name; times.actime = times.modtime = now; if (utime(file, ×) < 0){ streamID = open(file, O_RDWR | O_CREAT, 0666); if (streamID >= 0) { char c; /* * Read and write a byte to the file to change the * modification time, then close the file. */ if (read(streamID, &c, 1) == 1) { (void)lseek(streamID, (off_t)0, SEEK_SET); while (write(streamID, &c, 1) == -1 && errno == EAGAIN) continue; } (void)close(streamID); } else { (void)fprintf(stdout, "*** couldn't touch %s: %s", file, strerror(errno)); (void)fflush(stdout); } } } } /*- *----------------------------------------------------------------------- * Job_CheckCommands -- * Make sure the given node has all the commands it needs. * * Input: * gn The target whose commands need verifying * abortProc Function to abort with message * * Results: * TRUE if the commands list is/was ok. * * Side Effects: * The node will have commands from the .DEFAULT rule added to it * if it needs them. *----------------------------------------------------------------------- */ Boolean Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) { if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) && ((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) { /* * No commands. Look for .DEFAULT rule from which we might infer * commands */ if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) && (gn->type & OP_SPECIAL) == 0) { char *p1; /* * Make only looks for a .DEFAULT if the node was never the * target of an operator, so that's what we do too. If * a .DEFAULT was given, we substitute its commands for gn's * commands and set the IMPSRC variable to be the target's name * The DEFAULT node acts like a transformation rule, in that * gn also inherits any attributes or sources attached to * .DEFAULT itself. */ Make_HandleUse(DEFAULT, gn); Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn); - free(p1); + bmake_free(p1); } else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) { /* * The node wasn't the target of an operator we have no .DEFAULT * rule to go on and the target doesn't already exist. There's * nothing more we can do for this branch. If the -k flag wasn't * given, we stop in our tracks, otherwise we just don't update * this node's parents so they never get examined. */ static const char msg[] = ": don't know how to make"; if (gn->flags & FROM_DEPEND) { if (!Job_RunTarget(".STALE", gn->fname)) fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n", progname, gn->fname, gn->lineno, makeDependfile, gn->name); return TRUE; } if (gn->type & OP_OPTIONAL) { (void)fprintf(stdout, "%s%s %s (ignored)\n", progname, msg, gn->name); (void)fflush(stdout); } else if (keepgoing) { (void)fprintf(stdout, "%s%s %s (continuing)\n", progname, msg, gn->name); (void)fflush(stdout); - return FALSE; + return FALSE; } else { (*abortProc)("%s%s %s. Stop", progname, msg, gn->name); return FALSE; } } } return TRUE; } /*- *----------------------------------------------------------------------- * JobExec -- * Execute the shell for the given job. Called from JobStart * * Input: * job Job to execute * * Results: * None. * * Side Effects: * A shell is executed, outputs is altered and the Job structure added * to the job table. * *----------------------------------------------------------------------- */ static void JobExec(Job *job, char **argv) { int cpid; /* ID of new child */ sigset_t mask; job->flags &= ~JOB_TRACED; if (DEBUG(JOB)) { int i; (void)fprintf(debug_file, "Running %s %sly\n", job->node->name, "local"); (void)fprintf(debug_file, "\tCommand: "); for (i = 0; argv[i] != NULL; i++) { (void)fprintf(debug_file, "%s ", argv[i]); } - (void)fprintf(debug_file, "\n"); + (void)fprintf(debug_file, "\n"); } /* * Some jobs produce no output and it's disconcerting to have * no feedback of their running (since they produce no output, the * banner with their name in it never appears). This is an attempt to * provide that feedback, even if nothing follows it. */ if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) { MESSAGE(stdout, job->node); lastNode = job->node; } /* No interruptions until this job is on the `jobs' list */ JobSigLock(&mask); /* Pre-emptively mark job running, pid still zero though */ job->job_state = JOB_ST_RUNNING; cpid = vFork(); if (cpid == -1) Punt("Cannot vfork: %s", strerror(errno)); if (cpid == 0) { /* Child */ sigset_t tmask; #ifdef USE_META if (useMeta) { meta_job_child(job); } #endif /* * Reset all signal handlers; this is necessary because we also * need to unblock signals before we exec(2). */ JobSigReset(); /* Now unblock signals */ sigemptyset(&tmask); JobSigUnlock(&tmask); /* * Must duplicate the input stream down to the child's input and * reset it to the beginning (again). Since the stream was marked * close-on-exec, we must clear that bit in the new input. */ if (dup2(FILENO(job->cmdFILE), 0) == -1) { execError("dup2", "job->cmdFILE"); _exit(1); } if (fcntl(0, F_SETFD, 0) == -1) { execError("fcntl clear close-on-exec", "stdin"); _exit(1); } if (lseek(0, (off_t)0, SEEK_SET) == -1) { execError("lseek to 0", "stdin"); _exit(1); } if (Always_pass_job_queue || (job->node->type & (OP_MAKE | OP_SUBMAKE))) { /* * Pass job token pipe to submakes. */ if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1) { execError("clear close-on-exec", "tokenWaitJob.inPipe"); _exit(1); } if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1) { execError("clear close-on-exec", "tokenWaitJob.outPipe"); _exit(1); } } /* * Set up the child's output to be routed through the pipe * we've created for it. */ if (dup2(job->outPipe, 1) == -1) { execError("dup2", "job->outPipe"); _exit(1); } /* * The output channels are marked close on exec. This bit was * duplicated by the dup2(on some systems), so we have to clear * it before routing the shell's error output to the same place as * its standard output. */ if (fcntl(1, F_SETFD, 0) == -1) { execError("clear close-on-exec", "stdout"); _exit(1); } if (dup2(1, 2) == -1) { execError("dup2", "1, 2"); _exit(1); } /* * We want to switch the child into a different process family so * we can kill it and all its descendants in one fell swoop, * by killing its process family, but not commit suicide. */ #if defined(HAVE_SETPGID) (void)setpgid(0, getpid()); #else #if defined(HAVE_SETSID) /* XXX: dsl - I'm sure this should be setpgrp()... */ (void)setsid(); #else (void)setpgrp(0, getpid()); #endif #endif Var_ExportVars(); (void)execv(shellPath, argv); execError("exec", shellPath); _exit(1); } /* Parent, continuing after the child exec */ job->pid = cpid; 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 */ job->curPos = 0; watchfd(job); if (job->cmdFILE != NULL && job->cmdFILE != stdout) { (void)fclose(job->cmdFILE); job->cmdFILE = NULL; } /* * Now the job is actually running, add it to the table. */ if (DEBUG(JOB)) { fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n", job->node->name, job->pid); job_table_dump("job started"); } JobSigUnlock(&mask); } /*- *----------------------------------------------------------------------- * JobMakeArgv -- * Create the argv needed to execute the shell for a given job. * * * Results: * * Side Effects: * *----------------------------------------------------------------------- */ static void JobMakeArgv(Job *job, char **argv) { int argc; static char args[10]; /* For merged arguments */ argv[0] = UNCONST(shellName); argc = 1; if ((commandShell->exit && (*commandShell->exit != '-')) || (commandShell->echo && (*commandShell->echo != '-'))) { /* * At least one of the flags doesn't have a minus before it, so * merge them together. Have to do this because the *(&(@*#*&#$# * Bourne shell thinks its second argument is a file to source. * Grrrr. Note the ten-character limitation on the combined arguments. */ (void)snprintf(args, sizeof(args), "-%s%s", ((job->flags & JOB_IGNERR) ? "" : (commandShell->exit ? commandShell->exit : "")), ((job->flags & JOB_SILENT) ? "" : (commandShell->echo ? commandShell->echo : ""))); if (args[1]) { argv[argc] = args; argc++; } } else { if (!(job->flags & JOB_IGNERR) && commandShell->exit) { argv[argc] = UNCONST(commandShell->exit); argc++; } if (!(job->flags & JOB_SILENT) && commandShell->echo) { argv[argc] = UNCONST(commandShell->echo); argc++; } } argv[argc] = NULL; } /*- *----------------------------------------------------------------------- * JobStart -- * Start a target-creation process going for the target described * by the graph node gn. * * Input: * gn target to create * flags flags for the job to override normal ones. * e.g. JOB_SPECIAL or JOB_IGNDOTS * previous The previous Job structure for this node, if any. * * Results: * JOB_ERROR if there was an error in the commands, JOB_FINISHED * if there isn't actually anything left to do for the job and * JOB_RUNNING if the job has been started. * * Side Effects: * A new Job node is created and added to the list of running * jobs. PMake is forked and a child shell created. * * NB: I'm fairly sure that this code is never called with JOB_SPECIAL set * JOB_IGNDOTS is never set (dsl) * Also the return value is ignored by everyone. *----------------------------------------------------------------------- */ static int JobStart(GNode *gn, int flags) { Job *job; /* new job descriptor */ char *argv[10]; /* Argument vector to shell */ Boolean cmdsOK; /* true if the nodes commands were all right */ Boolean noExec; /* Set true if we decide not to run the job */ int tfd; /* File descriptor to the temp file */ for (job = job_table; job < job_table_end; job++) { if (job->job_state == JOB_ST_FREE) break; } if (job >= job_table_end) Punt("JobStart no job slots vacant"); memset(job, 0, sizeof *job); job->job_state = JOB_ST_SETUP; if (gn->type & OP_SPECIAL) flags |= JOB_SPECIAL; job->node = gn; job->tailCmds = NULL; /* * Set the initial value of the flags for this job based on the global * ones and the node's attributes... Any flags supplied by the caller * are also added to the field. */ job->flags = 0; if (Targ_Ignore(gn)) { job->flags |= JOB_IGNERR; } if (Targ_Silent(gn)) { job->flags |= JOB_SILENT; } job->flags |= flags; /* * Check the commands now so any attributes from .DEFAULT have a chance * to migrate to the node */ cmdsOK = Job_CheckCommands(gn, Error); job->inPollfd = NULL; /* * If the -n flag wasn't given, we open up OUR (not the child's) * temporary file to stuff commands in it. The thing is rd/wr so we don't * need to reopen it to feed it to the shell. If the -n flag *was* given, * we just set the file to be stdout. Cute, huh? */ if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) || (!noExecute && !touchFlag)) { /* * tfile is the name of a file into which all shell commands are * put. It is removed before the child shell is executed, unless * DEBUG(SCRIPT) is set. */ char *tfile; sigset_t mask; /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) { PrintOnError(gn, NULL); /* provide some clue */ DieHorribly(); } JobSigLock(&mask); tfd = mkTempFile(TMPPAT, &tfile); if (!DEBUG(SCRIPT)) (void)eunlink(tfile); JobSigUnlock(&mask); job->cmdFILE = fdopen(tfd, "w+"); if (job->cmdFILE == NULL) { Punt("Could not fdopen %s", tfile); } (void)fcntl(FILENO(job->cmdFILE), F_SETFD, FD_CLOEXEC); /* * Send the commands to the command file, flush all its buffers then * rewind and remove the thing. */ noExec = FALSE; #ifdef USE_META if (useMeta) { meta_job_start(job, gn); if (Targ_Silent(gn)) { /* might have changed */ job->flags |= JOB_SILENT; } } #endif /* * We can do all the commands at once. hooray for sanity */ numCommands = 0; Lst_ForEach(gn->commands, JobPrintCommand, job); /* * If we didn't print out any commands to the shell script, * there's not much point in executing the shell, is there? */ if (numCommands == 0) { noExec = TRUE; } free(tfile); } else if (NoExecute(gn)) { /* * Not executing anything -- just print all the commands to stdout * in one fell swoop. This will still set up job->tailCmds correctly. */ if (lastNode != gn) { MESSAGE(stdout, gn); lastNode = gn; } job->cmdFILE = stdout; /* * Only print the commands if they're ok, but don't die if they're * not -- just let the user know they're bad and keep going. It * doesn't do any harm in this case and may do some good. */ if (cmdsOK) { Lst_ForEach(gn->commands, JobPrintCommand, job); } /* * Don't execute the shell, thank you. */ noExec = TRUE; } else { /* * Just touch the target and note that no shell should be executed. * Set cmdFILE to stdout to make life easier. Check the commands, too, * but don't die if they're no good -- it does no harm to keep working * up the graph. */ job->cmdFILE = stdout; - Job_Touch(gn, job->flags&JOB_SILENT); + Job_Touch(gn, job->flags&JOB_SILENT); noExec = TRUE; } /* Just in case it isn't already... */ (void)fflush(job->cmdFILE); /* * If we're not supposed to execute a shell, don't. */ if (noExec) { if (!(job->flags & JOB_SPECIAL)) Job_TokenReturn(); /* * Unlink and close the command file if we opened one */ if (job->cmdFILE != stdout) { if (job->cmdFILE != NULL) { (void)fclose(job->cmdFILE); job->cmdFILE = NULL; } } /* * We only want to work our way up the graph if we aren't here because * the commands for the job were no good. */ if (cmdsOK && aborting == 0) { if (job->tailCmds != NULL) { Lst_ForEachFrom(job->node->commands, job->tailCmds, - JobSaveCommand, - job->node); + JobSaveCommand, + job->node); } job->node->made = MADE; Make_Update(job->node); } job->job_state = JOB_ST_FREE; return cmdsOK ? JOB_FINISHED : JOB_ERROR; } /* * Set up the control arguments to the shell. This is based on the flags * set earlier for this job. */ JobMakeArgv(job, argv); /* Create the pipe by which we'll get the shell's output. */ JobCreatePipe(job, 3); JobExec(job, argv); return JOB_RUNNING; } static char * JobOutput(Job *job, char *cp, char *endp, int msg) { char *ecp; if (commandShell->noPrint) { ecp = Str_FindSubstring(cp, commandShell->noPrint); while (ecp != NULL) { if (cp != ecp) { *ecp = '\0'; if (!beSilent && msg && job->node != lastNode) { MESSAGE(stdout, job->node); lastNode = job->node; } /* * The only way there wouldn't be a newline after * this line is if it were the last in the buffer. * however, since the non-printable comes after it, * there must be a newline, so we don't print one. */ (void)fprintf(stdout, "%s", cp); (void)fflush(stdout); } cp = ecp + commandShell->noPLen; if (cp != endp) { /* * Still more to print, look again after skipping * the whitespace following the non-printable * command.... */ cp++; while (*cp == ' ' || *cp == '\t' || *cp == '\n') { cp++; } ecp = Str_FindSubstring(cp, commandShell->noPrint); } else { return cp; } } } return cp; } /*- *----------------------------------------------------------------------- * JobDoOutput -- * This function is called at different times depending on * whether the user has specified that output is to be collected * via pipes or temporary files. In the former case, we are called * whenever there is something to read on the pipe. We collect more * output from the given job and store it in the job's outBuf. If * this makes up a line, we print it tagged by the job's identifier, * as necessary. * If output has been collected in a temporary file, we open the * file and read it line by line, transfering it to our own * output channel until the file is empty. At which point we * remove the temporary file. * In both cases, however, we keep our figurative eye out for the * 'noPrint' line for the shell from which the output came. If * we recognize a line, we don't print it. If the command is not * alone on the line (the character after it is not \0 or \n), we * do print whatever follows it. * * Input: * job the job whose output needs printing * finish TRUE if this is the last time we'll be called * for this job * * Results: * None * * Side Effects: * curPos may be shifted as may the contents of outBuf. *----------------------------------------------------------------------- */ STATIC void JobDoOutput(Job *job, Boolean finish) { Boolean gotNL = FALSE; /* true if got a newline */ Boolean fbuf; /* true if our buffer filled up */ int nr; /* number of bytes read */ int i; /* auxiliary index into outBuf */ int max; /* limit for i (end of current data) */ int nRead; /* (Temporary) number of bytes read */ /* * Read as many bytes as will fit in the buffer. */ end_loop: gotNL = FALSE; fbuf = FALSE; nRead = read(job->inPipe, &job->outBuf[job->curPos], JOB_BUFSIZE - job->curPos); if (nRead < 0) { if (errno == EAGAIN) return; if (DEBUG(JOB)) { perror("JobDoOutput(piperead)"); } nr = 0; } else { nr = nRead; } /* * If we hit the end-of-file (the job is dead), we must flush its * remaining output, so pretend we read a newline if there's any * output remaining in the buffer. * Also clear the 'finish' flag so we stop looping. */ if ((nr == 0) && (job->curPos != 0)) { job->outBuf[job->curPos] = '\n'; nr = 1; finish = FALSE; } else if (nr == 0) { finish = FALSE; } /* * Look for the last newline in the bytes we just got. If there is * one, break out of the loop with 'i' as its index and gotNL set * TRUE. */ max = job->curPos + nr; for (i = job->curPos + nr - 1; i >= job->curPos; i--) { if (job->outBuf[i] == '\n') { gotNL = TRUE; break; } else if (job->outBuf[i] == '\0') { /* * Why? */ job->outBuf[i] = ' '; } } if (!gotNL) { job->curPos += nr; if (job->curPos == JOB_BUFSIZE) { /* * If we've run out of buffer space, we have no choice * but to print the stuff. sigh. */ fbuf = TRUE; i = job->curPos; } } if (gotNL || fbuf) { /* * Need to send the output to the screen. Null terminate it * first, overwriting the newline character if there was one. * So long as the line isn't one we should filter (according * to the shell description), we print the line, preceded * by a target banner if this target isn't the same as the * one for which we last printed something. * The rest of the data in the buffer are then shifted down * to the start of the buffer and curPos is set accordingly. */ job->outBuf[i] = '\0'; if (i >= job->curPos) { char *cp; cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE); /* * There's still more in that thar buffer. This time, though, * we know there's no newline at the end, so we add one of * our own free will. */ if (*cp != '\0') { if (!beSilent && job->node != lastNode) { MESSAGE(stdout, job->node); lastNode = job->node; } #ifdef USE_META if (useMeta) { meta_job_output(job, cp, gotNL ? "\n" : ""); } #endif (void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); (void)fflush(stdout); } } /* * max is the last offset still in the buffer. Move any remaining * characters to the start of the buffer and update the end marker * curPos. */ if (i < max) { (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1)); job->curPos = max - (i + 1); } else { assert(i == max); job->curPos = 0; } } if (finish) { /* * If the finish flag is true, we must loop until we hit * end-of-file on the pipe. This is guaranteed to happen * eventually since the other end of the pipe is now closed * (we closed it explicitly and the child has exited). When * we do get an EOF, finish will be set FALSE and we'll fall * through and out. */ goto end_loop; } } static void JobRun(GNode *targ) { #ifdef notyet /* * Unfortunately it is too complicated to run .BEGIN, .END, * and .INTERRUPT job in the parallel job module. This has * the nice side effect that it avoids a lot of other problems. */ - Lst lst = Lst_Init(FALSE); - Lst_AtEnd(lst, targ); + Lst lst = Lst_Init(); + Lst_Append(lst, targ); (void)Make_Run(lst); Lst_Destroy(lst, NULL); JobStart(targ, JOB_SPECIAL); while (jobTokensRunning) { Job_CatchOutput(); } #else Compat_Make(targ, targ); if (targ->made == ERROR) { PrintOnError(targ, "\n\nStop."); exit(1); } #endif } /*- *----------------------------------------------------------------------- * Job_CatchChildren -- * Handle the exit of a child. Called from Make_Make. * * Input: * block TRUE if should block on the wait * * Results: * none. * * Side Effects: * The job descriptor is removed from the list of children. * * Notes: * We do waits, blocking or not, according to the wisdom of our * caller, until there are no more children to report. For each * job, call JobFinish to finish things off. * *----------------------------------------------------------------------- */ void Job_CatchChildren(void) { int pid; /* pid of dead child */ WAIT_T status; /* Exit/termination status */ /* * Don't even bother if we know there's no one around. */ if (jobTokensRunning == 0) return; while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) { if (DEBUG(JOB)) { (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid, WAIT_STATUS(status)); } JobReapChild(pid, status, TRUE); } } /* * It is possible that wait[pid]() was called from elsewhere, * this lets us reap jobs regardless. */ void JobReapChild(pid_t pid, WAIT_T status, Boolean isJobs) { Job *job; /* job descriptor for dead child */ /* * Don't even bother if we know there's no one around. */ if (jobTokensRunning == 0) return; job = JobFindPid(pid, JOB_ST_RUNNING, isJobs); if (job == NULL) { if (isJobs) { if (!lurking_children) Error("Child (%d) status %x not in table?", pid, status); } return; /* not ours */ } if (WIFSTOPPED(status)) { if (DEBUG(JOB)) { (void)fprintf(debug_file, "Process %d (%s) stopped.\n", job->pid, job->node->name); } if (!make_suspended) { switch (WSTOPSIG(status)) { case SIGTSTP: (void)printf("*** [%s] Suspended\n", job->node->name); break; case SIGSTOP: (void)printf("*** [%s] Stopped\n", job->node->name); break; default: (void)printf("*** [%s] Stopped -- signal %d\n", job->node->name, WSTOPSIG(status)); } job->job_suspended = 1; } (void)fflush(stdout); return; } job->job_state = JOB_ST_FINISHED; job->exit_status = WAIT_STATUS(status); JobFinish(job, status); } /*- *----------------------------------------------------------------------- * Job_CatchOutput -- * Catch the output from our children, if we're using * pipes do so. Otherwise just block time until we get a * signal(most likely a SIGCHLD) since there's no point in * just spinning when there's nothing to do and the reaping * of a child can wait for a while. * * Results: * None * * Side Effects: * Output is read from pipes if we're piping. * ----------------------------------------------------------------------- */ void Job_CatchOutput(void) { int nready; Job *job; int i; (void)fflush(stdout); /* The first fd in the list is the job token pipe */ do { nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC); } while (nready < 0 && errno == EINTR); if (nready < 0) Punt("poll: %s", strerror(errno)); if (nready > 0 && readyfd(&childExitJob)) { char token = 0; ssize_t count; count = read(childExitJob.inPipe, &token, 1); switch (count) { case 0: Punt("unexpected eof on token pipe"); case -1: Punt("token pipe read: %s", strerror(errno)); case 1: if (token == DO_JOB_RESUME[0]) /* Complete relay requested from our SIGCONT handler */ JobRestartJobs(); break; default: abort(); } --nready; } Job_CatchChildren(); if (nready == 0) return; 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; } } /*- *----------------------------------------------------------------------- * Job_Make -- * Start the creation of a target. Basically a front-end for * JobStart used by the Make module. * * Results: * None. * * Side Effects: * Another job is started. * *----------------------------------------------------------------------- */ void Job_Make(GNode *gn) { (void)JobStart(gn, 0); } void Shell_Init(void) { if (shellPath == NULL) { /* * We are using the default shell, which may be an absolute * path if DEFSHELL_CUSTOM is defined. */ shellName = commandShell->name; #ifdef DEFSHELL_CUSTOM if (*shellName == '/') { shellPath = shellName; shellName = strrchr(shellPath, '/'); shellName++; } else #endif - shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); + shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName); } + Var_Set_with_flags(".SHELL", shellPath, VAR_CMD, VAR_SET_READONLY); if (commandShell->exit == NULL) { commandShell->exit = ""; } if (commandShell->echo == NULL) { commandShell->echo = ""; } if (commandShell->hasErrCtl && *commandShell->exit) { if (shellErrFlag && strcmp(commandShell->exit, &shellErrFlag[1]) != 0) { free(shellErrFlag); shellErrFlag = NULL; } if (!shellErrFlag) { int n = strlen(commandShell->exit) + 2; shellErrFlag = bmake_malloc(n); if (shellErrFlag) { snprintf(shellErrFlag, n, "-%s", commandShell->exit); } } } else if (shellErrFlag) { free(shellErrFlag); shellErrFlag = NULL; } } /*- * Returns the string literal that is used in the current command shell * to produce a newline character. */ const char * Shell_GetNewline(void) { - return commandShell->newline; } void Job_SetPrefix(void) { - if (targPrefix) { free(targPrefix); } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL); } - targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", - VAR_GLOBAL, VARF_WANTRES); + targPrefix = Var_Subst("${" MAKE_JOB_PREFIX "}", + VAR_GLOBAL, VARE_WANTRES); } -/*- - *----------------------------------------------------------------------- - * Job_Init -- - * Initialize the process module - * - * Input: - * - * Results: - * none - * - * Side Effects: - * lists and counters are initialized - *----------------------------------------------------------------------- - */ +/* Initialize the process module. */ void Job_Init(void) { Job_SetPrefix(); /* Allocate space for all the job info */ job_table = bmake_malloc(maxJobs * sizeof *job_table); memset(job_table, 0, maxJobs * sizeof *job_table); job_table_end = job_table + maxJobs; wantToken = 0; aborting = 0; errors = 0; lastNode = NULL; Always_pass_job_queue = getBoolean(MAKE_ALWAYS_PASS_JOB_QUEUE, Always_pass_job_queue); Job_error_token = getBoolean(MAKE_JOB_ERROR_TOKEN, Job_error_token); /* * There is a non-zero chance that we already have children. * eg after 'make -f- < 0) continue; if (rval == 0) lurking_children = 1; break; } Shell_Init(); JobCreatePipe(&childExitJob, 3); /* 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); watchfd(&childExitJob); sigemptyset(&caught_signals); /* * Install a SIGCHLD handler. */ (void)bmake_signal(SIGCHLD, JobChildSig); sigaddset(&caught_signals, SIGCHLD); #define ADDSIG(s,h) \ if (bmake_signal(s, SIG_IGN) != SIG_IGN) { \ sigaddset(&caught_signals, s); \ (void)bmake_signal(s, h); \ } /* * Catch the four signals that POSIX specifies if they aren't ignored. * JobPassSig will take care of calling JobInterrupt if appropriate. */ ADDSIG(SIGINT, JobPassSig_int) ADDSIG(SIGHUP, JobPassSig_term) ADDSIG(SIGTERM, JobPassSig_term) ADDSIG(SIGQUIT, JobPassSig_term) /* * There are additional signals that need to be caught and passed if * either the export system wants to be told directly of signals or if * we're giving each job its own process group (since then it won't get * signals from the terminal driver as we own the terminal) */ ADDSIG(SIGTSTP, JobPassSig_suspend) ADDSIG(SIGTTOU, JobPassSig_suspend) ADDSIG(SIGTTIN, JobPassSig_suspend) ADDSIG(SIGWINCH, JobCondPassSig) ADDSIG(SIGCONT, JobContinueSig) #undef ADDSIG (void)Job_RunTarget(".BEGIN", NULL); postCommands = Targ_FindNode(".END", TARG_CREATE); } static void JobSigReset(void) { #define DELSIG(s) \ if (sigismember(&caught_signals, s)) { \ (void)bmake_signal(s, SIG_DFL); \ } DELSIG(SIGINT) DELSIG(SIGHUP) DELSIG(SIGQUIT) DELSIG(SIGTERM) DELSIG(SIGTSTP) DELSIG(SIGTTOU) DELSIG(SIGTTIN) DELSIG(SIGWINCH) DELSIG(SIGCONT) #undef DELSIG (void)bmake_signal(SIGCHLD, SIG_DFL); } -/*- - *----------------------------------------------------------------------- - * JobMatchShell -- - * Find a shell in 'shells' given its name. - * - * Results: - * A pointer to the Shell structure. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Find a shell in 'shells' given its name, or return NULL. */ static Shell * JobMatchShell(const char *name) { Shell *sh; for (sh = shells; sh->name != NULL; sh++) { if (strcmp(name, sh->name) == 0) return sh; } return NULL; } /*- *----------------------------------------------------------------------- * Job_ParseShell -- * Parse a shell specification and set up commandShell, shellPath * and shellName appropriately. * * Input: * line The shell spec * * Results: - * FAILURE if the specification was incorrect. + * FALSE if the specification was incorrect. * * Side Effects: * commandShell points to a Shell structure (either predefined or * created from the shell spec), shellPath is the full path of the * shell described by commandShell, while shellName is just the * final component of shellPath. * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * newline String literal to represent a newline char * check Command to turn on error checking if hasErrCtl * is TRUE or template of command to echo a command * for which error checking is off if hasErrCtl is * FALSE. * ignore Command to turn off error checking if hasErrCtl * is TRUE or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is FALSE. * *----------------------------------------------------------------------- */ -ReturnStatus +Boolean Job_ParseShell(char *line) { + Words wordsList; char **words; char **argv; - int argc; + size_t argc; char *path; Shell newShell; Boolean fullSpec = FALSE; Shell *sh; while (isspace((unsigned char)*line)) { line++; } - free(UNCONST(shellArgv)); + free(shellArgv); memset(&newShell, 0, sizeof(newShell)); /* * Parse the specification by keyword */ - words = brk_string(line, &argc, TRUE, &path); + wordsList = Str_Words(line, TRUE); + words = wordsList.words; + argc = wordsList.len; + path = wordsList.freeIt; if (words == NULL) { Error("Unterminated quoted string [%s]", line); - return FAILURE; + return FALSE; } shellArgv = path; for (path = NULL, argv = words; argc != 0; argc--, argv++) { if (strncmp(*argv, "path=", 5) == 0) { path = &argv[0][5]; } else if (strncmp(*argv, "name=", 5) == 0) { newShell.name = &argv[0][5]; } else { if (strncmp(*argv, "quiet=", 6) == 0) { newShell.echoOff = &argv[0][6]; } else if (strncmp(*argv, "echo=", 5) == 0) { newShell.echoOn = &argv[0][5]; } else if (strncmp(*argv, "filter=", 7) == 0) { newShell.noPrint = &argv[0][7]; newShell.noPLen = strlen(newShell.noPrint); } else if (strncmp(*argv, "echoFlag=", 9) == 0) { newShell.echo = &argv[0][9]; } else if (strncmp(*argv, "errFlag=", 8) == 0) { newShell.exit = &argv[0][8]; } else if (strncmp(*argv, "hasErrCtl=", 10) == 0) { char c = argv[0][10]; newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && (c != 'T') && (c != 't')); } else if (strncmp(*argv, "newline=", 8) == 0) { newShell.newline = &argv[0][8]; } else if (strncmp(*argv, "check=", 6) == 0) { newShell.errCheck = &argv[0][6]; } else if (strncmp(*argv, "ignore=", 7) == 0) { newShell.ignErr = &argv[0][7]; } else if (strncmp(*argv, "errout=", 7) == 0) { newShell.errOut = &argv[0][7]; } else if (strncmp(*argv, "comment=", 8) == 0) { newShell.commentChar = argv[0][8]; } else { Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", *argv); free(words); - return FAILURE; + return FALSE; } fullSpec = TRUE; } } if (path == NULL) { /* * If no path was given, the user wants one of the pre-defined shells, * yes? So we find the one s/he wants with the help of JobMatchShell * and set things up the right way. shellPath will be set up by * Shell_Init. */ if (newShell.name == NULL) { Parse_Error(PARSE_FATAL, "Neither path nor name specified"); free(words); - return FAILURE; + return FALSE; } else { if ((sh = JobMatchShell(newShell.name)) == NULL) { Parse_Error(PARSE_WARNING, "%s: No matching shell", newShell.name); free(words); - return FAILURE; + return FALSE; } commandShell = sh; shellName = newShell.name; if (shellPath) { /* Shell_Init has already been called! Do it again. */ free(UNCONST(shellPath)); shellPath = NULL; Shell_Init(); } } } else { /* * The user provided a path. If s/he gave nothing else (fullSpec is * FALSE), try and find a matching shell in the ones we know of. * Else we just take the specification at its word and copy it * to a new location. In either case, we need to record the * path the user gave for the shell. */ shellPath = path; path = strrchr(path, '/'); if (path == NULL) { path = UNCONST(shellPath); } else { path += 1; } if (newShell.name != NULL) { shellName = newShell.name; } else { shellName = path; } if (!fullSpec) { if ((sh = JobMatchShell(shellName)) == NULL) { Parse_Error(PARSE_WARNING, "%s: No matching shell", shellName); free(words); - return FAILURE; + return FALSE; } commandShell = sh; } else { commandShell = bmake_malloc(sizeof(Shell)); *commandShell = newShell; } /* this will take care of shellErrFlag */ Shell_Init(); } if (commandShell->echoOn && commandShell->echoOff) { commandShell->hasEchoCtl = TRUE; } if (!commandShell->hasErrCtl) { if (commandShell->errCheck == NULL) { commandShell->errCheck = ""; } if (commandShell->ignErr == NULL) { commandShell->ignErr = "%s\n"; } } /* * Do not free up the words themselves, since they might be in use by the * shell specification. */ free(words); - return SUCCESS; + return TRUE; } /*- *----------------------------------------------------------------------- * JobInterrupt -- * Handle the receipt of an interrupt. * * Input: * runINTERRUPT Non-zero if commands for the .INTERRUPT target * should be executed * signo signal received * * Results: * None * * Side Effects: * All children are killed. Another job will be started if the * .INTERRUPT target was given. *----------------------------------------------------------------------- */ static void JobInterrupt(int runINTERRUPT, int signo) { Job *job; /* job descriptor in that element */ GNode *interrupt; /* the node describing the .INTERRUPT target */ sigset_t mask; GNode *gn; aborting = ABORT_INTERRUPT; JobSigLock(&mask); for (job = job_table; job < job_table_end; job++) { if (job->job_state != JOB_ST_RUNNING) continue; gn = job->node; JobDeleteTarget(gn); if (job->pid) { if (DEBUG(JOB)) { (void)fprintf(debug_file, "JobInterrupt passing signal %d to child %d.\n", signo, job->pid); } KILLPG(job->pid, signo); } } JobSigUnlock(&mask); if (runINTERRUPT && !touchFlag) { interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); if (interrupt != NULL) { ignoreErrors = FALSE; JobRun(interrupt); } } Trace_Log(MAKEINTR, 0); exit(signo); } /* *----------------------------------------------------------------------- * Job_Finish -- * Do final processing such as the running of the commands * attached to the .END target. * * Results: * Number of errors reported. * * Side Effects: * None. *----------------------------------------------------------------------- */ int Job_Finish(void) { if (postCommands != NULL && (!Lst_IsEmpty(postCommands->commands) || !Lst_IsEmpty(postCommands->children))) { if (errors) { Error("Errors reported so .END ignored"); } else { JobRun(postCommands); } } return errors; } /*- *----------------------------------------------------------------------- * Job_End -- * Cleanup any memory used by the jobs module * * Results: * None. * * Side Effects: * Memory is freed *----------------------------------------------------------------------- */ void Job_End(void) { #ifdef CLEANUP free(shellArgv); #endif } /*- *----------------------------------------------------------------------- * Job_Wait -- * Waits for all running jobs to finish and returns. Sets 'aborting' * to ABORT_WAIT to prevent other jobs from starting. * * Results: * None. * * Side Effects: * Currently running jobs finish. * *----------------------------------------------------------------------- */ void Job_Wait(void) { aborting = ABORT_WAIT; while (jobTokensRunning != 0) { Job_CatchOutput(); } aborting = 0; } /*- *----------------------------------------------------------------------- * Job_AbortAll -- * Abort all currently running jobs without handling output or anything. * This function is to be called only in the event of a major * error. Most definitely NOT to be called from JobInterrupt. * * Results: * None * * Side Effects: * All children are killed, not just the firstborn *----------------------------------------------------------------------- */ void Job_AbortAll(void) { Job *job; /* the job descriptor in that element */ WAIT_T foo; aborting = ABORT_ERROR; if (jobTokensRunning) { for (job = job_table; job < job_table_end; job++) { if (job->job_state != JOB_ST_RUNNING) continue; /* * kill the child process with increasingly drastic signals to make * darn sure it's dead. */ KILLPG(job->pid, SIGINT); KILLPG(job->pid, SIGKILL); } } /* * Catch as many children as want to report in at first, then give up */ while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) continue; } - /*- *----------------------------------------------------------------------- * JobRestartJobs -- * Tries to restart stopped jobs if there are slots available. * Called in process context in response to a SIGCONT. * * Results: * None. * * Side Effects: * Resumes jobs. * *----------------------------------------------------------------------- */ static void JobRestartJobs(void) { Job *job; for (job = job_table; job < job_table_end; job++) { if (job->job_state == JOB_ST_RUNNING && (make_suspended || job->job_suspended)) { if (DEBUG(JOB)) { (void)fprintf(debug_file, "Restarting stopped job pid %d.\n", job->pid); } if (job->job_suspended) { (void)printf("*** [%s] Continued\n", job->node->name); (void)fflush(stdout); } job->job_suspended = 0; if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid); } } if (job->job_state == JOB_ST_FINISHED) /* Job exit deferred after calling waitpid() in a signal handler */ JobFinish(job, job->exit_status); } make_suspended = 0; } static void watchfd(Job *job) { if (job->inPollfd != NULL) Punt("Watching watched job"); fds[nfds].fd = job->inPipe; fds[nfds].events = POLLIN; 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 clearfd(Job *job) { int i; if (job->inPollfd == NULL) 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. */ if (nfds != i) { 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; } static int readyfd(Job *job) { if (job->inPollfd == NULL) Punt("Polling unwatched job"); return (job->inPollfd->revents & POLLIN) != 0; } /*- *----------------------------------------------------------------------- * JobTokenAdd -- * Put a token into the job pipe so that some make process can start * another job. * * Side Effects: * Allows more build jobs to be spawned somewhere. * *----------------------------------------------------------------------- */ static void JobTokenAdd(void) { char tok = JOB_TOKENS[aborting], tok1; if (!Job_error_token && aborting == ABORT_ERROR) { if (jobTokensRunning == 0) return; tok = '+'; /* no error token */ } /* If we are depositing an error token flush everything else */ while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1) continue; if (DEBUG(JOB)) fprintf(debug_file, "(%d) aborting %d, deposit token %c\n", getpid(), aborting, tok); while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; } /*- *----------------------------------------------------------------------- * Job_ServerStartTokenAdd -- * Prep the job token pipe in the root make process. * *----------------------------------------------------------------------- */ void Job_ServerStart(int max_tokens, int jp_0, int jp_1) { int i; char jobarg[64]; if (jp_0 >= 0 && jp_1 >= 0) { /* Pipe passed in from parent */ tokenWaitJob.inPipe = jp_0; tokenWaitJob.outPipe = jp_1; (void)fcntl(jp_0, F_SETFD, FD_CLOEXEC); (void)fcntl(jp_1, F_SETFD, FD_CLOEXEC); return; } JobCreatePipe(&tokenWaitJob, 15); snprintf(jobarg, sizeof(jobarg), "%d,%d", tokenWaitJob.inPipe, tokenWaitJob.outPipe); Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); /* * Preload the job pipe with one token per job, save the one * "extra" token for the primary job. * * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is * larger than the write buffer size of the pipe, we will * deadlock here. */ for (i = 1; i < max_tokens; i++) JobTokenAdd(); } /*- *----------------------------------------------------------------------- * Job_TokenReturn -- * Return a withdrawn token to the pool. * *----------------------------------------------------------------------- */ void Job_TokenReturn(void) { jobTokensRunning--; if (jobTokensRunning < 0) Punt("token botch"); if (jobTokensRunning || JOB_TOKENS[aborting] != '+') JobTokenAdd(); } /*- *----------------------------------------------------------------------- * Job_TokenWithdraw -- * Attempt to withdraw a token from the pool. * * Results: * Returns TRUE if a token was withdrawn, and FALSE if the pool * is currently empty. * * Side Effects: * If pool is empty, set wantToken so that we wake up * when a token is released. * *----------------------------------------------------------------------- */ Boolean Job_TokenWithdraw(void) { char tok, tok1; int count; wantToken = 0; if (DEBUG(JOB)) fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n", getpid(), aborting, jobTokensRunning); if (aborting || (jobTokensRunning >= maxJobs)) return FALSE; count = read(tokenWaitJob.inPipe, &tok, 1); if (count == 0) Fatal("eof on job pipe!"); if (count < 0 && jobTokensRunning != 0) { if (errno != EAGAIN) { Fatal("job pipe read: %s", strerror(errno)); } if (DEBUG(JOB)) fprintf(debug_file, "(%d) blocked for token\n", getpid()); return FALSE; } if (count == 1 && tok != '+') { /* make being abvorted - remove any other job tokens */ if (DEBUG(JOB)) fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok); while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) continue; /* And put the stopper back */ while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; if (dieQuietly(NULL, 1)) exit(2); Fatal("A failure has been detected in another branch of the parallel make"); } if (count == 1 && jobTokensRunning == 0) /* We didn't want the token really */ while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) continue; jobTokensRunning++; if (DEBUG(JOB)) fprintf(debug_file, "(%d) withdrew token\n", getpid()); return TRUE; } /*- *----------------------------------------------------------------------- * Job_RunTarget -- * Run the named target if found. If a filename is specified, then * set that to the sources. * * Results: * None * * Side Effects: * exits if the target fails. * *----------------------------------------------------------------------- */ Boolean Job_RunTarget(const char *target, const char *fname) { GNode *gn = Targ_FindNode(target, TARG_NOCREATE); if (gn == NULL) return FALSE; if (fname) Var_Set(ALLSRC, fname, gn); JobRun(gn); if (gn->made == ERROR) { PrintOnError(gn, "\n\nStop."); exit(1); } return TRUE; } #ifdef USE_SELECT int emul_poll(struct pollfd *fd, int nfd, int timeout) { fd_set rfds, wfds; int i, maxfd, nselect, npoll; struct timeval tv, *tvp; long usecs; FD_ZERO(&rfds); FD_ZERO(&wfds); maxfd = -1; for (i = 0; i < nfd; i++) { fd[i].revents = 0; if (fd[i].events & POLLIN) FD_SET(fd[i].fd, &rfds); if (fd[i].events & POLLOUT) FD_SET(fd[i].fd, &wfds); if (fd[i].fd > maxfd) maxfd = fd[i].fd; } if (maxfd >= FD_SETSIZE) { Punt("Ran out of fd_set slots; " "recompile with a larger FD_SETSIZE."); } if (timeout < 0) { tvp = NULL; } else { usecs = timeout * 1000; tv.tv_sec = usecs / 1000000; tv.tv_usec = usecs % 1000000; - tvp = &tv; + tvp = &tv; } nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); if (nselect <= 0) return nselect; npoll = 0; for (i = 0; i < nfd; i++) { if (FD_ISSET(fd[i].fd, &rfds)) fd[i].revents |= POLLIN; if (FD_ISSET(fd[i].fd, &wfds)) fd[i].revents |= POLLOUT; if (fd[i].revents) npoll++; } return npoll; } #endif /* USE_SELECT */ Index: head/contrib/bmake/job.h =================================================================== --- head/contrib/bmake/job.h (revision 365365) +++ head/contrib/bmake/job.h (revision 365366) @@ -1,274 +1,269 @@ -/* $NetBSD: job.h,v 1.43 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)job.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)job.h 8.1 (Berkeley) 6/6/93 */ /*- * job.h -- * Definitions pertaining to the running of jobs in parallel mode. */ -#ifndef _JOB_H_ -#define _JOB_H_ +#ifndef MAKE_JOB_H +#define MAKE_JOB_H #define TMPPAT "makeXXXXXX" /* relative to tmpdir */ #ifdef USE_SELECT /* * Emulate poll() in terms of select(). This is not a complete * emulation but it is sufficient for make's purposes. */ #define poll emul_poll #define pollfd emul_pollfd struct emul_pollfd { int fd; short events; short revents; }; #define POLLIN 0x0001 #define POLLOUT 0x0004 int emul_poll(struct pollfd *fd, int nfd, int timeout); #endif /* * The POLL_MSEC constant determines the maximum number of milliseconds spent * in poll before coming out to see if a child has finished. */ #define POLL_MSEC 5000 - /*- * Job Table definitions. * * Each job has several things associated with it: * 1) The process id of the child shell * 2) The graph node describing the target being made by this job * 3) A LstNode for the first command to be saved after the job * completes. This is NULL if there was no "..." in the job's * commands. * 4) An FILE* for writing out the commands. This is only * used before the job is actually started. * 5) The output is being caught via a pipe and * the descriptors of our pipe, an array in which output is line * buffered and the current position in that buffer are all * maintained for each job. * 6) A word of flags which determine how the module handles errors, * echoing, etc. for the job * * When a job is finished, the Make_Update function is called on each of the * parents of the node which was just remade. This takes care of the upward * traversal of the dependency graph. */ struct pollfd; #ifdef USE_META # include "meta.h" #endif #define JOB_BUFSIZE 1024 typedef struct Job { int pid; /* The child's process ID */ GNode *node; /* The target the child is making */ LstNode tailCmds; /* The node of the first command to be * saved when the job has been run */ FILE *cmdFILE; /* When creating the shell script, this is * where the commands go */ int exit_status; /* from wait4() in signal handler */ char job_state; /* status of the job entry */ #define JOB_ST_FREE 0 /* Job is available */ #define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */ #define JOB_ST_RUNNING 3 /* Job is running, pid valid */ #define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */ char job_suspended; short flags; /* Flags to control treatment of job */ #define JOB_IGNERR 0x001 /* Ignore non-zero exits */ #define JOB_SILENT 0x002 /* no output */ #define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally * if we can't export it and maxLocal is 0 */ #define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing * commands */ #define JOB_TRACED 0x400 /* we've sent 'set -x' */ - int jobPipe[2]; /* Pipe for readind output from job */ + int jobPipe[2]; /* Pipe for reading output from job */ struct pollfd *inPollfd; /* pollfd associated with inPipe */ char outBuf[JOB_BUFSIZE + 1]; /* Buffer for storing the output of the * job, line by line */ int curPos; /* Current position in op_outBuf */ #ifdef USE_META struct BuildMon bm; #endif } Job; #define inPipe jobPipe[0] #define outPipe jobPipe[1] - /*- * Shell Specifications: * Each shell type has associated with it the following information: * 1) The string which must match the last character of the shell name * for the shell to be considered of this type. The longest match * wins. * 2) A command to issue to turn off echoing of command lines * 3) A command to issue to turn echoing back on again * 4) What the shell prints, and its length, when given the echo-off * command. This line will not be printed when received from the shell * 5) A boolean to tell if the shell has the ability to control * error checking for individual commands. * 6) The string to turn this checking on. * 7) The string to turn it off. * 8) The command-flag to give to cause the shell to start echoing * commands right away. * 9) The command-flag to cause the shell to Lib_Exit when an error is * detected in one of the commands. * * Some special stuff goes on if a shell doesn't have error control. In such * a case, errCheck becomes a printf template for echoing the command, * should echoing be on and ignErr becomes another printf template for * executing the command while ignoring the return status. Finally errOut * is a printf template for running the command and causing the shell to * exit on error. If any of these strings are empty when hasErrCtl is FALSE, * the command will be executed anyway as is and if it causes an error, so be * it. Any templates setup to echo the command will escape any '$ ` \ "'i * characters in the command string to avoid common problems with * echo "%s\n" as a template. */ typedef struct Shell { const char *name; /* the name of the shell. For Bourne and C * shells, this is used only to find the * shell description when used as the single * source of a .SHELL target. For user-defined * shells, this is the full path of the shell. */ Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */ const char *echoOff; /* command to turn off echo */ const char *echoOn; /* command to turn it back on again */ const char *noPrint; /* command to skip when printing output from * shell. This is usually the command which * was executed to turn off echoing */ int noPLen; /* length of noPrint command */ Boolean hasErrCtl; /* set if can control error checking for * individual commands */ const char *errCheck; /* string to turn error checking on */ const char *ignErr; /* string to turn off error checking */ const char *errOut; /* string to use for testing exit code */ const char *newline; /* string literal that results in a newline * character when it appears outside of any * 'quote' or "quote" characters */ char commentChar; /* character used by shell for comment lines */ /* * command-line flags */ const char *echo; /* echo commands */ const char *exit; /* exit on error */ } Shell; extern const char *shellPath; extern const char *shellName; extern char *shellErrFlag; extern int jobTokensRunning; /* tokens currently "out" */ extern int maxJobs; /* Max jobs we can run */ void Shell_Init(void); const char *Shell_GetNewline(void); void Job_Touch(GNode *, Boolean); Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...)); -#define CATCH_BLOCK 1 void Job_CatchChildren(void); void Job_CatchOutput(void); void Job_Make(GNode *); void Job_Init(void); -Boolean Job_Full(void); Boolean Job_Empty(void); -ReturnStatus Job_ParseShell(char *); +Boolean Job_ParseShell(char *); int Job_Finish(void); void Job_End(void); void Job_Wait(void); void Job_AbortAll(void); -void JobFlagForMigration(int); void Job_TokenReturn(void); Boolean Job_TokenWithdraw(void); void Job_ServerStart(int, int, int); void Job_SetPrefix(void); Boolean Job_RunTarget(const char *, const char *); -#endif /* _JOB_H_ */ +#endif /* MAKE_JOB_H */ Index: head/contrib/bmake/lst.c =================================================================== --- head/contrib/bmake/lst.c (nonexistent) +++ head/contrib/bmake/lst.c (revision 365366) @@ -0,0 +1,641 @@ +/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_INTTYPES_H +#include +#elif defined(HAVE_STDINT_H) +#include +#endif + +#include "make.h" + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $"; +#else +#include +#ifndef lint +__RCSID("$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $"); +#endif /* not lint */ +#endif + +struct ListNode { + struct ListNode *prev; /* previous element in list */ + struct ListNode *next; /* next in list */ + uint8_t useCount; /* Count of functions using the node. + * node may not be deleted until count + * goes to 0 */ + Boolean deleted; /* List node should be removed when done */ + union { + void *datum; /* datum associated with this element */ + const GNode *gnode; /* alias, just for debugging */ + const char *str; /* alias, just for debugging */ + }; +}; + +typedef enum { + Head, Middle, Tail, Unknown +} Where; + +struct List { + LstNode first; /* first node in list */ + LstNode last; /* last node in list */ + + /* fields for sequential access */ + Boolean isOpen; /* true if list has been Lst_Open'ed */ + Where lastAccess; /* Where in the list the last access was */ + LstNode curr; /* current node, if open. NULL if + * *just* opened */ + LstNode prev; /* Previous node, if open. Used by Lst_Remove */ +}; + +/* Allocate and initialize a list node. + * + * The fields 'prev' and 'next' must be initialized by the caller. + */ +static LstNode +LstNodeNew(void *datum) +{ + LstNode node = bmake_malloc(sizeof *node); + node->useCount = 0; + node->deleted = FALSE; + node->datum = datum; + return node; +} + +static Boolean +LstIsEmpty(Lst list) +{ + return list->first == NULL; +} + +/* Create and initialize a new, empty list. */ +Lst +Lst_Init(void) +{ + Lst list = bmake_malloc(sizeof *list); + + list->first = NULL; + list->last = NULL; + list->isOpen = FALSE; + list->lastAccess = Unknown; + + return list; +} + +/* Duplicate an entire list, usually by copying the datum pointers. + * If copyProc is given, that function is used to create the new datum from the + * old datum, usually by creating a copy of it. */ +Lst +Lst_Copy(Lst list, LstCopyProc copyProc) +{ + Lst newList; + LstNode node; + + assert(list != NULL); + + newList = Lst_Init(); + + for (node = list->first; node != NULL; node = node->next) { + void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum; + Lst_Append(newList, datum); + } + + return newList; +} + +/* Free a list and all its nodes. The list data itself are not freed though. */ +void +Lst_Free(Lst list) +{ + LstNode node; + LstNode next; + + assert(list != NULL); + + for (node = list->first; node != NULL; node = next) { + next = node->next; + free(node); + } + + free(list); +} + +/* Destroy a list and free all its resources. The freeProc is called with the + * datum from each node in turn before the node is freed. */ +void +Lst_Destroy(Lst list, LstFreeProc freeProc) +{ + LstNode node; + LstNode next; + + assert(list != NULL); + assert(freeProc != NULL); + + for (node = list->first; node != NULL; node = next) { + next = node->next; + freeProc(node->datum); + free(node); + } + + free(list); +} + +/* + * Functions to modify a list + */ + +/* Insert a new node with the given piece of data before the given node in the + * given list. */ +void +Lst_InsertBefore(Lst list, LstNode node, void *datum) +{ + LstNode newNode; + + assert(list != NULL); + assert(!LstIsEmpty(list)); + assert(node != NULL); + assert(datum != NULL); + + newNode = LstNodeNew(datum); + newNode->prev = node->prev; + newNode->next = node; + + if (node->prev != NULL) { + node->prev->next = newNode; + } + node->prev = newNode; + + if (node == list->first) { + list->first = newNode; + } +} + +/* Add a piece of data at the start of the given list. */ +void +Lst_Prepend(Lst list, void *datum) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + node = LstNodeNew(datum); + node->prev = NULL; + node->next = list->first; + + if (list->first == NULL) { + list->first = node; + list->last = node; + } else { + list->first->prev = node; + list->first = node; + } +} + +/* Add a piece of data at the end of the given list. */ +void +Lst_Append(Lst list, void *datum) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + node = LstNodeNew(datum); + node->prev = list->last; + node->next = NULL; + + if (list->last == NULL) { + list->first = node; + list->last = node; + } else { + list->last->next = node; + list->last = node; + } +} + +/* Remove the given node from the given list. + * The datum stored in the node must be freed by the caller, if necessary. */ +void +Lst_Remove(Lst list, LstNode node) +{ + assert(list != NULL); + assert(node != NULL); + + /* + * unlink it from the list + */ + if (node->next != NULL) { + node->next->prev = node->prev; + } + if (node->prev != NULL) { + node->prev->next = node->next; + } + + /* + * if either the first or last of the list point to this node, + * adjust them accordingly + */ + if (list->first == node) { + list->first = node->next; + } + if (list->last == node) { + list->last = node->prev; + } + + /* + * Sequential access stuff. If the node we're removing is the current + * node in the list, reset the current node to the previous one. If the + * previous one was non-existent (prev == NULL), we set the + * end to be Unknown, since it is. + */ + if (list->isOpen && list->curr == node) { + list->curr = list->prev; + if (list->curr == NULL) { + list->lastAccess = Unknown; + } + } + + /* + * note that the datum is unmolested. The caller must free it as + * necessary and as expected. + */ + if (node->useCount == 0) { + free(node); + } else { + node->deleted = TRUE; + } +} + +/* Replace the datum in the given node with the new datum. */ +void +LstNode_Set(LstNode node, void *datum) +{ + assert(node != NULL); + assert(datum != NULL); + + node->datum = datum; +} + +/* Replace the datum in the given node to NULL. */ +void +LstNode_SetNull(LstNode node) +{ + assert(node != NULL); + + node->datum = NULL; +} + + +/* + * Node-specific functions + */ + +/* Return the first node from the given list, or NULL if the list is empty. */ +LstNode +Lst_First(Lst list) +{ + assert(list != NULL); + + return list->first; +} + +/* Return the last node from the given list, or NULL if the list is empty. */ +LstNode +Lst_Last(Lst list) +{ + assert(list != NULL); + + return list->last; +} + +/* Return the successor to the given node on its list, or NULL. */ +LstNode +LstNode_Next(LstNode node) +{ + assert(node != NULL); + + return node->next; +} + +/* Return the predecessor to the given node on its list, or NULL. */ +LstNode +LstNode_Prev(LstNode node) +{ + assert(node != NULL); + return node->prev; +} + +/* Return the datum stored in the given node. */ +void * +LstNode_Datum(LstNode node) +{ + assert(node != NULL); + return node->datum; +} + + +/* + * Functions for entire lists + */ + +/* Return TRUE if the given list is empty. */ +Boolean +Lst_IsEmpty(Lst list) +{ + assert(list != NULL); + + return LstIsEmpty(list); +} + +/* Return the first node from the list for which the match function returns + * TRUE, or NULL if none of the nodes matched. */ +LstNode +Lst_Find(Lst list, LstFindProc match, const void *matchArgs) +{ + return Lst_FindFrom(list, Lst_First(list), match, matchArgs); +} + +/* Return the first node from the list, starting at the given node, for which + * the match function returns TRUE, or NULL if none of the nodes matches. + * + * The start node may be NULL, in which case nothing is found. This allows + * for passing Lst_First or LstNode_Next as the start node. */ +LstNode +Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs) +{ + LstNode tln; + + assert(list != NULL); + assert(match != NULL); + + for (tln = node; tln != NULL; tln = tln->next) { + if (match(tln->datum, matchArgs)) + return tln; + } + + return NULL; +} + +/* Return the first node that contains the given datum, or NULL. */ +LstNode +Lst_FindDatum(Lst list, const void *datum) +{ + LstNode node; + + assert(list != NULL); + assert(datum != NULL); + + for (node = list->first; node != NULL; node = node->next) { + if (node->datum == datum) { + return node; + } + } + + return NULL; +} + +/* Apply the given function to each element of the given list. The function + * should return 0 if traversal should continue and non-zero if it should + * abort. */ +int +Lst_ForEach(Lst list, LstActionProc proc, void *procData) +{ + if (LstIsEmpty(list)) + return 0; /* XXX: Document what this value means. */ + return Lst_ForEachFrom(list, Lst_First(list), proc, procData); +} + +/* Apply the given function to each element of the given list, starting from + * the given node. The function should return 0 if traversal should continue, + * and non-zero if it should abort. */ +int +Lst_ForEachFrom(Lst list, LstNode node, + LstActionProc proc, void *procData) +{ + LstNode tln = node; + LstNode next; + Boolean done; + int result; + + assert(list != NULL); + assert(node != NULL); + assert(proc != NULL); + + do { + /* + * Take care of having the current element deleted out from under + * us. + */ + + next = tln->next; + + /* + * We're done with the traversal if + * - the next node to examine doesn't exist and + * - nothing's been added after the current node (check this + * after proc() has been called). + */ + done = next == NULL; + + tln->useCount++; + result = (*proc)(tln->datum, procData); + tln->useCount--; + + /* + * Now check whether a node has been added. + * Note: this doesn't work if this node was deleted before + * the new node was added. + */ + if (next != tln->next) { + next = tln->next; + done = 0; + } + + if (tln->deleted) { + free((char *)tln); + } + tln = next; + } while (!result && !LstIsEmpty(list) && !done); + + return result; +} + +/* Move all nodes from list2 to the end of list1. + * List2 is destroyed and freed. */ +void +Lst_MoveAll(Lst list1, Lst list2) +{ + assert(list1 != NULL); + assert(list2 != NULL); + + if (list2->first != NULL) { + list2->first->prev = list1->last; + if (list1->last != NULL) { + list1->last->next = list2->first; + } else { + list1->first = list2->first; + } + list1->last = list2->last; + } + free(list2); +} + +/* Copy the element data from src to the start of dst. */ +void +Lst_PrependAll(Lst dst, Lst src) +{ + LstNode node; + for (node = src->last; node != NULL; node = node->prev) + Lst_Prepend(dst, node->datum); +} + +/* Copy the element data from src to the end of dst. */ +void +Lst_AppendAll(Lst dst, Lst src) +{ + LstNode node; + for (node = src->first; node != NULL; node = node->next) + Lst_Append(dst, node->datum); +} + +/* + * these functions are for dealing with a list as a table, of sorts. + * An idea of the "current element" is kept and used by all the functions + * between Lst_Open() and Lst_Close(). + * + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. + */ + +/* Open a list for sequential access. A list can still be searched, etc., + * without confusing these functions. */ +void +Lst_Open(Lst list) +{ + assert(list != NULL); + assert(!list->isOpen); + + list->isOpen = TRUE; + list->lastAccess = LstIsEmpty(list) ? Head : Unknown; + list->curr = NULL; +} + +/* Return the next node for the given list, or NULL if the end has been + * reached. */ +LstNode +Lst_Next(Lst list) +{ + LstNode node; + + assert(list != NULL); + assert(list->isOpen); + + list->prev = list->curr; + + if (list->curr == NULL) { + if (list->lastAccess == Unknown) { + /* + * If we're just starting out, lastAccess will be Unknown. + * Then we want to start this thing off in the right + * direction -- at the start with lastAccess being Middle. + */ + list->curr = node = list->first; + list->lastAccess = Middle; + } else { + node = NULL; + list->lastAccess = Tail; + } + } else { + node = list->curr->next; + list->curr = node; + + if (node == list->first || node == NULL) { + /* + * If back at the front, then we've hit the end... + */ + list->lastAccess = Tail; + } else { + /* + * Reset to Middle if gone past first. + */ + list->lastAccess = Middle; + } + } + + return node; +} + +/* Close a list which was opened for sequential access. */ +void +Lst_Close(Lst list) +{ + assert(list != NULL); + assert(list->isOpen); + + list->isOpen = FALSE; + list->lastAccess = Unknown; +} + + +/* + * for using the list as a queue + */ + +/* Add the datum to the tail of the given list. */ +void +Lst_Enqueue(Lst list, void *datum) +{ + Lst_Append(list, datum); +} + +/* Remove and return the datum at the head of the given list. */ +void * +Lst_Dequeue(Lst list) +{ + void *datum; + + assert(list != NULL); + assert(!LstIsEmpty(list)); + + datum = list->first->datum; + Lst_Remove(list, list->first); + assert(datum != NULL); + return datum; +} Property changes on: head/contrib/bmake/lst.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/lst.h =================================================================== --- head/contrib/bmake/lst.h (revision 365365) +++ head/contrib/bmake/lst.h (revision 365366) @@ -1,189 +1,179 @@ -/* $NetBSD: lst.h,v 1.20 2014/09/07 20:55:34 joerg Exp $ */ +/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 */ -/*- - * lst.h -- - * Header for using the list library - */ -#ifndef _LST_H_ -#define _LST_H_ +/* Doubly-linked lists of arbitrary pointers. */ -#include -#include +#ifndef MAKE_LST_H +#define MAKE_LST_H -#include "sprite.h" +#include +#include -/* - * basic typedef. This is what the Lst_ functions handle - */ - +/* A doubly-linked list of pointers. */ typedef struct List *Lst; +/* A single node in the doubly-linked list. */ typedef struct ListNode *LstNode; -typedef void *DuplicateProc(void *); -typedef void FreeProc(void *); +/* Copy a node, usually by allocating a copy of the given object. + * For reference-counted objects, the original object may need to be + * modified, therefore the parameter is not const. */ +typedef void *LstCopyProc(void *); +/* Free the datum of a node, called before freeing the node itself. */ +typedef void LstFreeProc(void *); +/* Return TRUE if the datum matches the args, for Lst_Find. */ +typedef Boolean LstFindProc(const void *datum, const void *args); +/* An action for Lst_ForEach. */ +typedef int LstActionProc(void *datum, void *args); -#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ -#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ +/* Create or destroy a list */ -/* - * Creation/destruction functions - */ -/* Create a new list */ -Lst Lst_Init(Boolean); -/* Duplicate an existing list */ -Lst Lst_Duplicate(Lst, DuplicateProc *); -/* Destroy an old one */ -void Lst_Destroy(Lst, FreeProc *); -/* True if list is empty */ -Boolean Lst_IsEmpty(Lst); +/* Create a new list. */ +Lst Lst_Init(void); +/* Duplicate an existing list. */ +Lst Lst_Copy(Lst, LstCopyProc); +/* Free the list, leaving the node data unmodified. */ +void Lst_Free(Lst); +/* Free the list, freeing the node data using the given function. */ +void Lst_Destroy(Lst, LstFreeProc); -/* - * Functions to modify a list - */ -/* Insert an element before another */ -ReturnStatus Lst_InsertBefore(Lst, LstNode, void *); -/* Insert an element after another */ -ReturnStatus Lst_InsertAfter(Lst, LstNode, void *); -/* Place an element at the front of a lst. */ -ReturnStatus Lst_AtFront(Lst, void *); -/* Place an element at the end of a lst. */ -ReturnStatus Lst_AtEnd(Lst, void *); -/* Remove an element */ -ReturnStatus Lst_Remove(Lst, LstNode); -/* Replace a node with a new value */ -ReturnStatus Lst_Replace(LstNode, void *); -/* Concatenate two lists */ -ReturnStatus Lst_Concat(Lst, Lst, int); +/* Get information about a list */ -/* - * Node-specific functions - */ -/* Return first element in list */ -LstNode Lst_First(Lst); -/* Return last element in list */ -LstNode Lst_Last(Lst); -/* Return successor to given element */ -LstNode Lst_Succ(LstNode); -/* Return predecessor to given element */ -LstNode Lst_Prev(LstNode); -/* Get datum from LstNode */ -void *Lst_Datum(LstNode); +Boolean Lst_IsEmpty(Lst); +/* Return the first node of the list, or NULL. */ +LstNode Lst_First(Lst); +/* Return the last node of the list, or NULL. */ +LstNode Lst_Last(Lst); +/* Find the first node for which the function returns TRUE, or NULL. */ +LstNode Lst_Find(Lst, LstFindProc, const void *); +/* Find the first node for which the function returns TRUE, or NULL. + * The search starts at the given node, towards the end of the list. */ +LstNode Lst_FindFrom(Lst, LstNode, LstFindProc, const void *); +/* Find the first node that contains the given datum, or NULL. */ +LstNode Lst_FindDatum(Lst, const void *); -/* - * Functions for entire lists - */ -/* Find an element in a list */ -LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *)); -/* Find an element starting from somewhere */ -LstNode Lst_FindFrom(Lst, LstNode, const void *, - int (*cProc)(const void *, const void *)); -/* - * See if the given datum is on the list. Returns the LstNode containing - * the datum - */ -LstNode Lst_Member(Lst, void *); -/* Apply a function to all elements of a lst */ -int Lst_ForEach(Lst, int (*)(void *, void *), void *); -/* - * Apply a function to all elements of a lst starting from a certain point. - * If the list is circular, the application will wrap around to the - * beginning of the list again. - */ -int Lst_ForEachFrom(Lst, LstNode, int (*)(void *, void *), - void *); -/* - * these functions are for dealing with a list as a table, of sorts. - * An idea of the "current element" is kept and used by all the functions - * between Lst_Open() and Lst_Close(). - */ -/* Open the list */ -ReturnStatus Lst_Open(Lst); -/* Next element please */ -LstNode Lst_Next(Lst); -/* Done yet? */ -Boolean Lst_IsAtEnd(Lst); -/* Finish table access */ -void Lst_Close(Lst); +/* Modify a list */ -/* - * for using the list as a queue - */ -/* Place an element at tail of queue */ -ReturnStatus Lst_EnQueue(Lst, void *); -/* Remove an element from head of queue */ -void *Lst_DeQueue(Lst); +/* Insert a datum before the given node. */ +void Lst_InsertBefore(Lst, LstNode, void *); +/* Place a datum at the front of the list. */ +void Lst_Prepend(Lst, void *); +/* Place a datum at the end of the list. */ +void Lst_Append(Lst, void *); +/* Remove the node from the list. */ +void Lst_Remove(Lst, LstNode); +void Lst_PrependAll(Lst, Lst); +void Lst_AppendAll(Lst, Lst); +void Lst_MoveAll(Lst, Lst); -#endif /* _LST_H_ */ +/* Node-specific functions */ + +/* Return the successor of the node, or NULL. */ +LstNode LstNode_Next(LstNode); +/* Return the predecessor of the node, or NULL. */ +LstNode LstNode_Prev(LstNode); +/* Return the datum of the node. Usually not NULL. */ +void *LstNode_Datum(LstNode); +/* Replace the value of the node. */ +void LstNode_Set(LstNode, void *); +/* Set the value of the node to NULL. Having NULL in a list is unusual. */ +void LstNode_SetNull(LstNode); + +/* Iterating over a list, using a callback function */ + +/* Apply a function to each datum of the list, until the callback function + * returns non-zero. */ +int Lst_ForEach(Lst, LstActionProc, void *); +/* Apply a function to each datum of the list, starting at the node, + * until the callback function returns non-zero. */ +int Lst_ForEachFrom(Lst, LstNode, LstActionProc, void *); + +/* Iterating over a list while keeping track of the current node and possible + * concurrent modifications */ + +/* Start iterating the list. */ +void Lst_Open(Lst); +/* Return the next node, or NULL. */ +LstNode Lst_Next(Lst); +/* Finish iterating the list. */ +void Lst_Close(Lst); + +/* Using the list as a queue */ + +/* Add a datum at the tail of the queue. */ +void Lst_Enqueue(Lst, void *); +/* Remove the head node of the queue and return its datum. */ +void *Lst_Dequeue(Lst); + +#endif /* MAKE_LST_H */ Index: head/contrib/bmake/lst.lib/lstAtEnd.c =================================================================== --- head/contrib/bmake/lst.lib/lstAtEnd.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstAtEnd.c (nonexistent) @@ -1,79 +0,0 @@ -/* $NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAtEnd.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAtEnd.c -- - * Add a node at the end of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_AtEnd -- - * Add a node to the end of the given list - * - * Input: - * l List to which to add the datum - * d Datum to add - * - * Results: - * SUCCESS if life is good. - * - * Side Effects: - * A new ListNode is created and added to the list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_AtEnd(Lst l, void *d) -{ - LstNode end; - - end = Lst_Last(l); - return Lst_InsertAfter(l, end, d); -} Property changes on: head/contrib/bmake/lst.lib/lstAtEnd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstForEachFrom.c =================================================================== --- head/contrib/bmake/lst.lib/lstForEachFrom.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstForEachFrom.c (nonexistent) @@ -1,124 +0,0 @@ -/* $NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstForEachFrom.c,v 1.18 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * lstForEachFrom.c -- - * Perform a given function on all elements of a list starting from - * a given point. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_ForEachFrom -- - * Apply the given function to each element of the given list. The - * function should return 0 if traversal should continue and non- - * zero if it should abort. - * - * Results: - * None. - * - * Side Effects: - * Only those created by the passed-in function. - * - *----------------------------------------------------------------------- - */ -/*VARARGS2*/ -int -Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *), - void *d) -{ - ListNode tln = ln; - List list = l; - ListNode next; - Boolean done; - int result; - - if (!LstValid (list) || LstIsEmpty (list)) { - return 0; - } - - do { - /* - * Take care of having the current element deleted out from under - * us. - */ - - next = tln->nextPtr; - - /* - * We're done with the traversal if - * - the next node to examine is the first in the queue or - * doesn't exist and - * - nothing's been added after the current node (check this - * after proc() has been called). - */ - done = (next == NULL || next == list->firstPtr); - - (void) tln->useCount++; - result = (*proc) (tln->datum, d); - (void) tln->useCount--; - - /* - * Now check whether a node has been added. - * Note: this doesn't work if this node was deleted before - * the new node was added. - */ - if (next != tln->nextPtr) { - next = tln->nextPtr; - done = 0; - } - - if (tln->flags & LN_DELETED) { - free((char *)tln); - } - tln = next; - } while (!result && !LstIsEmpty(list) && !done); - - return result; -} Property changes on: head/contrib/bmake/lst.lib/lstForEachFrom.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstFind.c =================================================================== --- head/contrib/bmake/lst.lib/lstFind.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstFind.c (nonexistent) @@ -1,73 +0,0 @@ -/* $NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFind.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFind.c -- - * Find a node on a list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Find -- - * Find a node on the given list using the given comparison function - * and the given datum. - * - * Results: - * The found node or NULL if none matches. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *)) -{ - return Lst_FindFrom(l, Lst_First(l), d, cProc); -} Property changes on: head/contrib/bmake/lst.lib/lstFind.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstLast.c =================================================================== --- head/contrib/bmake/lst.lib/lstLast.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstLast.c (nonexistent) @@ -1,76 +0,0 @@ -/* $NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstLast.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstLast.c -- - * Return the last element of a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Last -- - * Return the last node on the list l. - * - * Results: - * The requested node or NULL if the list is empty. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Last(Lst l) -{ - if (!LstValid(l) || LstIsEmpty (l)) { - return NULL; - } else { - return l->lastPtr; - } -} Property changes on: head/contrib/bmake/lst.lib/lstLast.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstNext.c =================================================================== --- head/contrib/bmake/lst.lib/lstNext.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstNext.c (nonexistent) @@ -1,119 +0,0 @@ -/* $NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstNext.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstNext.c -- - * Return the next node for a list. - * The sequential functions access the list in a slightly different way. - * CurPtr points to their idea of the current node in the list and they - * access the list based on it. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Next -- - * Return the next node for the given list. - * - * Results: - * The next node or NULL if the list has yet to be opened. Also - * if the list is non-circular and the end has been reached, NULL - * is returned. - * - * Side Effects: - * the curPtr field is updated. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Next(Lst l) -{ - ListNode tln; - List list = l; - - if ((LstValid (l) == FALSE) || - (list->isOpen == FALSE)) { - return NULL; - } - - list->prevPtr = list->curPtr; - - if (list->curPtr == NULL) { - if (list->atEnd == Unknown) { - /* - * If we're just starting out, atEnd will be Unknown. - * Then we want to start this thing off in the right - * direction -- at the start with atEnd being Middle. - */ - list->curPtr = tln = list->firstPtr; - list->atEnd = Middle; - } else { - tln = NULL; - list->atEnd = Tail; - } - } else { - tln = list->curPtr->nextPtr; - list->curPtr = tln; - - if (tln == list->firstPtr || tln == NULL) { - /* - * If back at the front, then we've hit the end... - */ - list->atEnd = Tail; - } else { - /* - * Reset to Middle if gone past first. - */ - list->atEnd = Middle; - } - } - - return tln; -} Property changes on: head/contrib/bmake/lst.lib/lstNext.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstDestroy.c =================================================================== --- head/contrib/bmake/lst.lib/lstDestroy.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstDestroy.c (nonexistent) @@ -1,101 +0,0 @@ -/* $NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDestroy.c -- - * Nuke a list and all its resources - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Destroy -- - * Destroy a list and free all its resources. If the freeProc is - * given, it is called with the datum from each node in turn before - * the node is freed. - * - * Results: - * None. - * - * Side Effects: - * The given list is freed in its entirety. - * - *----------------------------------------------------------------------- - */ -void -Lst_Destroy(Lst list, FreeProc *freeProc) -{ - ListNode ln; - ListNode tln = NULL; - - if (list == NULL) - return; - - /* To ease scanning */ - if (list->lastPtr != NULL) - list->lastPtr->nextPtr = NULL; - else { - free(list); - return; - } - - if (freeProc) { - for (ln = list->firstPtr; ln != NULL; ln = tln) { - tln = ln->nextPtr; - freeProc(ln->datum); - free(ln); - } - } else { - for (ln = list->firstPtr; ln != NULL; ln = tln) { - tln = ln->nextPtr; - free(ln); - } - } - - free(list); -} Property changes on: head/contrib/bmake/lst.lib/lstDestroy.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstConcat.c =================================================================== --- head/contrib/bmake/lst.lib/lstConcat.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstConcat.c (nonexistent) @@ -1,184 +0,0 @@ -/* $NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstConcat.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * listConcat.c -- - * Function to concatentate two lists. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Concat -- - * Concatenate two lists. New elements are created to hold the data - * elements, if specified, but the elements themselves are not copied. - * If the elements should be duplicated to avoid confusion with another - * list, the Lst_Duplicate function should be called first. - * If LST_CONCLINK is specified, the second list is destroyed since - * its pointers have been corrupted and the list is no longer useable. - * - * Input: - * l1 The list to which l2 is to be appended - * l2 The list to append to l1 - * flags LST_CONCNEW if LstNode's should be duplicated - * LST_CONCLINK if should just be relinked - * - * Results: - * SUCCESS if all went well. FAILURE otherwise. - * - * Side Effects: - * New elements are created and appended the first list. - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Concat(Lst l1, Lst l2, int flags) -{ - ListNode ln; /* original LstNode */ - ListNode nln; /* new LstNode */ - ListNode last; /* the last element in the list. Keeps - * bookkeeping until the end */ - List list1 = l1; - List list2 = l2; - - if (!LstValid (l1) || !LstValid (l2)) { - return FAILURE; - } - - if (flags == LST_CONCLINK) { - if (list2->firstPtr != NULL) { - /* - * We set the nextPtr of the - * last element of list two to be NIL to make the loop easier and - * so we don't need an extra case should the first list turn - * out to be non-circular -- the final element will already point - * to NIL space and the first element will be untouched if it - * existed before and will also point to NIL space if it didn't. - */ - list2->lastPtr->nextPtr = NULL; - /* - * So long as the second list isn't empty, we just link the - * first element of the second list to the last element of the - * first list. If the first list isn't empty, we then link the - * last element of the list to the first element of the second list - * The last element of the second list, if it exists, then becomes - * the last element of the first list. - */ - list2->firstPtr->prevPtr = list1->lastPtr; - if (list1->lastPtr != NULL) { - list1->lastPtr->nextPtr = list2->firstPtr; - } else { - list1->firstPtr = list2->firstPtr; - } - list1->lastPtr = list2->lastPtr; - } - if (list1->isCirc && list1->firstPtr != NULL) { - /* - * If the first list is supposed to be circular and it is (now) - * non-empty, we must make sure it's circular by linking the - * first element to the last and vice versa - */ - list1->firstPtr->prevPtr = list1->lastPtr; - list1->lastPtr->nextPtr = list1->firstPtr; - } - free(l2); - } else if (list2->firstPtr != NULL) { - /* - * We set the nextPtr of the last element of list 2 to be nil to make - * the loop less difficult. The loop simply goes through the entire - * second list creating new LstNodes and filling in the nextPtr, and - * prevPtr to fit into l1 and its datum field from the - * datum field of the corresponding element in l2. The 'last' node - * follows the last of the new nodes along until the entire l2 has - * been appended. Only then does the bookkeeping catch up with the - * changes. During the first iteration of the loop, if 'last' is nil, - * the first list must have been empty so the newly-created node is - * made the first node of the list. - */ - list2->lastPtr->nextPtr = NULL; - for (last = list1->lastPtr, ln = list2->firstPtr; - ln != NULL; - ln = ln->nextPtr) - { - PAlloc (nln, ListNode); - nln->datum = ln->datum; - if (last != NULL) { - last->nextPtr = nln; - } else { - list1->firstPtr = nln; - } - nln->prevPtr = last; - nln->flags = nln->useCount = 0; - last = nln; - } - - /* - * Finish bookkeeping. The last new element becomes the last element - * of list one. - */ - list1->lastPtr = last; - - /* - * The circularity of both list one and list two must be corrected - * for -- list one because of the new nodes added to it; list two - * because of the alteration of list2->lastPtr's nextPtr to ease the - * above for loop. - */ - if (list1->isCirc) { - list1->lastPtr->nextPtr = list1->firstPtr; - list1->firstPtr->prevPtr = list1->lastPtr; - } else { - last->nextPtr = NULL; - } - - if (list2->isCirc) { - list2->lastPtr->nextPtr = list2->firstPtr; - } - } - - return SUCCESS; -} Property changes on: head/contrib/bmake/lst.lib/lstConcat.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstInsert.c =================================================================== --- head/contrib/bmake/lst.lib/lstInsert.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstInsert.c (nonexistent) @@ -1,121 +0,0 @@ -/* $NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstInsert.c,v 1.15 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstInsert.c -- - * Insert a new datum before an old one - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_InsertBefore -- - * Insert a new node with the given piece of data before the given - * node in the given list. - * - * Input: - * l list to manipulate - * ln node before which to insert d - * d datum to be inserted - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * the firstPtr field will be changed if ln is the first node in the - * list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_InsertBefore(Lst l, LstNode ln, void *d) -{ - ListNode nLNode; /* new lnode for d */ - ListNode lNode = ln; - List list = l; - - - /* - * check validity of arguments - */ - if (LstValid (l) && (LstIsEmpty (l) && ln == NULL)) - goto ok; - - if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { - return FAILURE; - } - - ok: - PAlloc (nLNode, ListNode); - - nLNode->datum = d; - nLNode->useCount = nLNode->flags = 0; - - if (ln == NULL) { - if (list->isCirc) { - nLNode->prevPtr = nLNode->nextPtr = nLNode; - } else { - nLNode->prevPtr = nLNode->nextPtr = NULL; - } - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = lNode->prevPtr; - nLNode->nextPtr = lNode; - - if (nLNode->prevPtr != NULL) { - nLNode->prevPtr->nextPtr = nLNode; - } - lNode->prevPtr = nLNode; - - if (lNode == list->firstPtr) { - list->firstPtr = nLNode; - } - } - - return SUCCESS; -} Property changes on: head/contrib/bmake/lst.lib/lstInsert.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstInt.h =================================================================== --- head/contrib/bmake/lst.lib/lstInt.h (revision 365365) +++ head/contrib/bmake/lst.lib/lstInt.h (nonexistent) @@ -1,105 +0,0 @@ -/* $NetBSD: lstInt.h,v 1.22 2014/09/07 20:55:34 joerg Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. - * - * from: @(#)lstInt.h 8.1 (Berkeley) 6/6/93 - */ - -/*- - * lstInt.h -- - * Internals for the list library - */ -#ifndef _LSTINT_H_ -#define _LSTINT_H_ - -#include "../lst.h" -#include "../make_malloc.h" - -typedef struct ListNode { - struct ListNode *prevPtr; /* previous element in list */ - struct ListNode *nextPtr; /* next in list */ - unsigned int useCount:8, /* Count of functions using the node. - * node may not be deleted until count - * goes to 0 */ - flags:8; /* Node status flags */ - void *datum; /* datum associated with this element */ -} *ListNode; -/* - * Flags required for synchronization - */ -#define LN_DELETED 0x0001 /* List node should be removed when done */ - -typedef enum { - Head, Middle, Tail, Unknown -} Where; - -typedef struct List { - ListNode firstPtr; /* first node in list */ - ListNode lastPtr; /* last node in list */ - Boolean isCirc; /* true if the list should be considered - * circular */ -/* - * fields for sequential access - */ - Where atEnd; /* Where in the list the last access was */ - Boolean isOpen; /* true if list has been Lst_Open'ed */ - ListNode curPtr; /* current node, if open. NULL if - * *just* opened */ - ListNode prevPtr; /* Previous node, if open. Used by - * Lst_Remove */ -} *List; - -/* - * PAlloc (var, ptype) -- - * Allocate a pointer-typedef structure 'ptype' into the variable 'var' - */ -#define PAlloc(var,ptype) var = (ptype) bmake_malloc(sizeof *(var)) - -/* - * LstValid (l) -- - * Return TRUE if the list l is valid - */ -#define LstValid(l) ((Lst)(l) != NULL) - -/* - * LstNodeValid (ln, l) -- - * Return TRUE if the LstNode ln is valid with respect to l - */ -#define LstNodeValid(ln, l) ((ln) != NULL) - -/* - * LstIsEmpty (l) -- - * TRUE if the list l is empty. - */ -#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL) - -#endif /* _LSTINT_H_ */ Property changes on: head/contrib/bmake/lst.lib/lstInt.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstFindFrom.c =================================================================== --- head/contrib/bmake/lst.lib/lstFindFrom.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstFindFrom.c (nonexistent) @@ -1,89 +0,0 @@ -/* $NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFindFrom.c,v 1.16 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFindFrom.c -- - * Find a node on a list from a given starting point. Used by Lst_Find. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_FindFrom -- - * Search for a node starting and ending with the given one on the - * given list using the passed datum and comparison function to - * determine when it has been found. - * - * Results: - * The found node or NULL - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_FindFrom(Lst l, LstNode ln, const void *d, - int (*cProc)(const void *, const void *)) -{ - ListNode tln; - - if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { - return NULL; - } - - tln = ln; - - do { - if ((*cProc)(tln->datum, d) == 0) - return tln; - tln = tln->nextPtr; - } while (tln != ln && tln != NULL); - - return NULL; -} Property changes on: head/contrib/bmake/lst.lib/lstFindFrom.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstSucc.c =================================================================== --- head/contrib/bmake/lst.lib/lstSucc.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstSucc.c (nonexistent) @@ -1,78 +0,0 @@ -/* $NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstSucc.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstSucc.c -- - * return the successor to a given node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Succ -- - * Return the successor to the given node on its list. - * - * Results: - * The successor of the node, if it exists (note that on a circular - * list, if the node is the only one in the list, it is its own - * successor). - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Succ(LstNode ln) -{ - if (ln == NULL) { - return NULL; - } else { - return ln->nextPtr; - } -} Property changes on: head/contrib/bmake/lst.lib/lstSucc.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstOpen.c =================================================================== --- head/contrib/bmake/lst.lib/lstOpen.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstOpen.c (nonexistent) @@ -1,86 +0,0 @@ -/* $NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstOpen.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstOpen.c -- - * Open a list for sequential access. The sequential functions access the - * list in a slightly different way. CurPtr points to their idea of the - * current node in the list and they access the list based on it. - * If the list is circular, Lst_Next and Lst_Prev will go around - * the list forever. Lst_IsAtEnd must be used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Open -- - * Open a list for sequential access. A list can still be searched, - * etc., without confusing these functions. - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * isOpen is set TRUE and curPtr is set to NULL so the - * other sequential functions no it was just opened and can choose - * the first element accessed based on this. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Open(Lst l) -{ - if (LstValid (l) == FALSE) { - return FAILURE; - } - (l)->isOpen = TRUE; - (l)->atEnd = LstIsEmpty (l) ? Head : Unknown; - (l)->curPtr = NULL; - - return SUCCESS; -} Property changes on: head/contrib/bmake/lst.lib/lstOpen.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstForEach.c =================================================================== --- head/contrib/bmake/lst.lib/lstForEach.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstForEach.c (nonexistent) @@ -1,75 +0,0 @@ -/* $NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstForEach.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstForeach.c -- - * Perform a given function on all elements of a list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_ForEach -- - * Apply the given function to each element of the given list. The - * function should return 0 if Lst_ForEach should continue and non- - * zero if it should abort. - * - * Results: - * None. - * - * Side Effects: - * Only those created by the passed-in function. - * - *----------------------------------------------------------------------- - */ -/*VARARGS2*/ -int -Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d) -{ - return Lst_ForEachFrom(l, Lst_First(l), proc, d); -} Property changes on: head/contrib/bmake/lst.lib/lstForEach.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstDeQueue.c =================================================================== --- head/contrib/bmake/lst.lib/lstDeQueue.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstDeQueue.c (nonexistent) @@ -1,86 +0,0 @@ -/* $NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDeQueue.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDeQueue.c -- - * Remove the node and return its datum from the head of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_DeQueue -- - * Remove and return the datum at the head of the given list. - * - * Results: - * The datum in the node at the head or NULL if the list - * is empty. - * - * Side Effects: - * The head node is removed from the list. - * - *----------------------------------------------------------------------- - */ -void * -Lst_DeQueue(Lst l) -{ - void *rd; - ListNode tln; - - tln = Lst_First(l); - if (tln == NULL) { - return NULL; - } - - rd = tln->datum; - if (Lst_Remove(l, tln) == FAILURE) { - return NULL; - } else { - return rd; - } -} Property changes on: head/contrib/bmake/lst.lib/lstDeQueue.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstInit.c =================================================================== --- head/contrib/bmake/lst.lib/lstInit.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstInit.c (nonexistent) @@ -1,85 +0,0 @@ -/* $NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstInit.c,v 1.13 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * init.c -- - * Initialize a new linked list. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Init -- - * Create and initialize a new list. - * - * Input: - * circ TRUE if the list should be made circular - * - * Results: - * The created list. - * - * Side Effects: - * A list is created, what else? - * - *----------------------------------------------------------------------- - */ -Lst -Lst_Init(Boolean circ) -{ - List nList; - - PAlloc (nList, List); - - nList->firstPtr = NULL; - nList->lastPtr = NULL; - nList->isOpen = FALSE; - nList->isCirc = circ; - nList->atEnd = Unknown; - - return nList; -} Property changes on: head/contrib/bmake/lst.lib/lstInit.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstDatum.c =================================================================== --- head/contrib/bmake/lst.lib/lstDatum.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstDatum.c (nonexistent) @@ -1,76 +0,0 @@ -/* $NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDatum.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstDatum.c -- - * Return the datum associated with a list node. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Datum -- - * Return the datum stored in the given node. - * - * Results: - * The datum or NULL if the node is invalid. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -void * -Lst_Datum(LstNode ln) -{ - if (ln != NULL) { - return ln->datum; - } else { - return NULL; - } -} Property changes on: head/contrib/bmake/lst.lib/lstDatum.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstDupl.c =================================================================== --- head/contrib/bmake/lst.lib/lstDupl.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstDupl.c (nonexistent) @@ -1,107 +0,0 @@ -/* $NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstDupl.c,v 1.17 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * listDupl.c -- - * Duplicate a list. This includes duplicating the individual - * elements. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Duplicate -- - * Duplicate an entire list. If a function to copy a void *is - * given, the individual client elements will be duplicated as well. - * - * Input: - * l the list to duplicate - * copyProc A function to duplicate each void * - * - * Results: - * The new Lst structure or NULL if failure. - * - * Side Effects: - * A new list is created. - *----------------------------------------------------------------------- - */ -Lst -Lst_Duplicate(Lst l, DuplicateProc *copyProc) -{ - Lst nl; - ListNode ln; - List list = l; - - if (!LstValid (l)) { - return NULL; - } - - nl = Lst_Init(list->isCirc); - if (nl == NULL) { - return NULL; - } - - ln = list->firstPtr; - while (ln != NULL) { - if (copyProc != NULL) { - if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) { - return NULL; - } - } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { - return NULL; - } - - if (list->isCirc && ln == list->lastPtr) { - ln = NULL; - } else { - ln = ln->nextPtr; - } - } - - return nl; -} Property changes on: head/contrib/bmake/lst.lib/lstDupl.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstPrev.c =================================================================== --- head/contrib/bmake/lst.lib/lstPrev.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstPrev.c (nonexistent) @@ -1,78 +0,0 @@ -/* $NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstPrev.c,v 1.4 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstPrev.c -- - * return the predecessor to a given node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Prev -- - * Return the predecessor to the given node on its list. - * - * Results: - * The predecessor of the node, if it exists (note that on a circular - * list, if the node is the only one in the list, it is its own - * predecessor). - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_Prev(LstNode ln) -{ - if (ln == NULL) { - return NULL; - } else { - return ln->prevPtr; - } -} Property changes on: head/contrib/bmake/lst.lib/lstPrev.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstAtFront.c =================================================================== --- head/contrib/bmake/lst.lib/lstAtFront.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstAtFront.c (nonexistent) @@ -1,76 +0,0 @@ -/* $NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAtFront.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAtFront.c -- - * Add a node at the front of the list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_AtFront -- - * Place a piece of data at the front of a list - * - * Results: - * SUCCESS or FAILURE - * - * Side Effects: - * A new ListNode is created and stuck at the front of the list. - * hence, firstPtr (and possible lastPtr) in the list are altered. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_AtFront(Lst l, void *d) -{ - LstNode front; - - front = Lst_First(l); - return Lst_InsertBefore(l, front, d); -} Property changes on: head/contrib/bmake/lst.lib/lstAtFront.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstIsAtEnd.c =================================================================== --- head/contrib/bmake/lst.lib/lstIsAtEnd.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstIsAtEnd.c (nonexistent) @@ -1,86 +0,0 @@ -/* $NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstIsAtEnd.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstIsAtEnd.c -- - * Tell if the current node is at the end of the list. - * The sequential functions access the list in a slightly different way. - * CurPtr points to their idea of the current node in the list and they - * access the list based on it. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_IsAtEnd -- - * Return true if have reached the end of the given list. - * - * Results: - * TRUE if at the end of the list (this includes the list not being - * open or being invalid) or FALSE if not. We return TRUE if the list - * is invalid or unopend so as to cause the caller to exit its loop - * asap, the assumption being that the loop is of the form - * while (!Lst_IsAtEnd (l)) { - * ... - * } - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -Boolean -Lst_IsAtEnd(Lst l) -{ - List list = l; - - return !LstValid (l) || !list->isOpen || - list->atEnd == Head || list->atEnd == Tail; -} Property changes on: head/contrib/bmake/lst.lib/lstIsAtEnd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstRemove.c =================================================================== --- head/contrib/bmake/lst.lib/lstRemove.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstRemove.c (nonexistent) @@ -1,134 +0,0 @@ -/* $NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstRemove.c,v 1.17 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstRemove.c -- - * Remove an element from a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Remove -- - * Remove the given node from the given list. - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * The list's firstPtr will be set to NULL if ln is the last - * node on the list. firsPtr and lastPtr will be altered if ln is - * either the first or last node, respectively, on the list. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Remove(Lst l, LstNode ln) -{ - List list = l; - ListNode lNode = ln; - - if (!LstValid (l) || !LstNodeValid (ln, l)) { - return FAILURE; - } - - /* - * unlink it from the list - */ - if (lNode->nextPtr != NULL) { - lNode->nextPtr->prevPtr = lNode->prevPtr; - } - if (lNode->prevPtr != NULL) { - lNode->prevPtr->nextPtr = lNode->nextPtr; - } - - /* - * if either the firstPtr or lastPtr of the list point to this node, - * adjust them accordingly - */ - if (list->firstPtr == lNode) { - list->firstPtr = lNode->nextPtr; - } - if (list->lastPtr == lNode) { - list->lastPtr = lNode->prevPtr; - } - - /* - * Sequential access stuff. If the node we're removing is the current - * node in the list, reset the current node to the previous one. If the - * previous one was non-existent (prevPtr == NULL), we set the - * end to be Unknown, since it is. - */ - if (list->isOpen && (list->curPtr == lNode)) { - list->curPtr = list->prevPtr; - if (list->curPtr == NULL) { - list->atEnd = Unknown; - } - } - - /* - * the only way firstPtr can still point to ln is if ln is the last - * node on the list (the list is circular, so lNode->nextptr == lNode in - * this case). The list is, therefore, empty and is marked as such - */ - if (list->firstPtr == lNode) { - list->firstPtr = NULL; - } - - /* - * note that the datum is unmolested. The caller must free it as - * necessary and as expected. - */ - if (lNode->useCount == 0) { - free(ln); - } else { - lNode->flags |= LN_DELETED; - } - - return SUCCESS; -} Property changes on: head/contrib/bmake/lst.lib/lstRemove.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstFirst.c =================================================================== --- head/contrib/bmake/lst.lib/lstFirst.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstFirst.c (nonexistent) @@ -1,76 +0,0 @@ -/* $NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstFirst.c,v 1.13 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstFirst.c -- - * Return the first node of a list - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_First -- - * Return the first node on the given list. - * - * Results: - * The first node or NULL if the list is empty. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -LstNode -Lst_First(Lst l) -{ - if (!LstValid (l) || LstIsEmpty (l)) { - return NULL; - } else { - return l->firstPtr; - } -} Property changes on: head/contrib/bmake/lst.lib/lstFirst.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstEnQueue.c =================================================================== --- head/contrib/bmake/lst.lib/lstEnQueue.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstEnQueue.c (nonexistent) @@ -1,77 +0,0 @@ -/* $NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstEnQueue.c,v 1.14 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstEnQueue.c-- - * Treat the list as a queue and place a datum at its end - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_EnQueue -- - * Add the datum to the tail of the given list. - * - * Results: - * SUCCESS or FAILURE as returned by Lst_InsertAfter. - * - * Side Effects: - * the lastPtr field is altered all the time and the firstPtr field - * will be altered if the list used to be empty. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_EnQueue(Lst l, void *d) -{ - if (LstValid (l) == FALSE) { - return FAILURE; - } - - return Lst_InsertAfter(l, Lst_Last(l), d); -} Property changes on: head/contrib/bmake/lst.lib/lstEnQueue.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstIsEmpty.c =================================================================== --- head/contrib/bmake/lst.lib/lstIsEmpty.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstIsEmpty.c (nonexistent) @@ -1,74 +0,0 @@ -/* $NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstIsEmpty.c,v 1.12 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstIsEmpty.c -- - * A single function to decide if a list is empty - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_IsEmpty -- - * Return TRUE if the given list is empty. - * - * Results: - * TRUE if the list is empty, FALSE otherwise. - * - * Side Effects: - * None. - * - * A list is considered empty if its firstPtr == NULL (or if - * the list itself is NULL). - *----------------------------------------------------------------------- - */ -Boolean -Lst_IsEmpty(Lst l) -{ - return !LstValid(l) || LstIsEmpty(l); -} Property changes on: head/contrib/bmake/lst.lib/lstIsEmpty.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstReplace.c =================================================================== --- head/contrib/bmake/lst.lib/lstReplace.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstReplace.c (nonexistent) @@ -1,77 +0,0 @@ -/* $NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstReplace.c,v 1.14 2020/07/03 08:37:57 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstReplace.c -- - * Replace the datum in a node with a new datum - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Replace -- - * Replace the datum in the given node with the new datum - * - * Results: - * SUCCESS or FAILURE. - * - * Side Effects: - * The datum field fo the node is altered. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_Replace(LstNode ln, void *d) -{ - if (ln == NULL) { - return FAILURE; - } else { - (ln)->datum = d; - return SUCCESS; - } -} Property changes on: head/contrib/bmake/lst.lib/lstReplace.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstClose.c =================================================================== --- head/contrib/bmake/lst.lib/lstClose.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstClose.c (nonexistent) @@ -1,85 +0,0 @@ -/* $NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstClose.c,v 1.12 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstClose.c -- - * Close a list for sequential access. - * The sequential functions access the list in a slightly different way. - * CurPtr points to their idea of the current node in the list and they - * access the list based on it. Because the list is circular, Lst_Next - * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be - * used to determine when to stop. - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_Close -- - * Close a list which was opened for sequential access. - * - * Input: - * l The list to close - * - * Results: - * None. - * - * Side Effects: - * The list is closed. - * - *----------------------------------------------------------------------- - */ -void -Lst_Close(Lst l) -{ - List list = l; - - if (LstValid(l) == TRUE) { - list->isOpen = FALSE; - list->atEnd = Unknown; - } -} Property changes on: head/contrib/bmake/lst.lib/lstClose.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstAppend.c =================================================================== --- head/contrib/bmake/lst.lib/lstAppend.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstAppend.c (nonexistent) @@ -1,121 +0,0 @@ -/* $NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstAppend.c,v 1.15 2020/07/03 08:37:56 rillig Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * LstAppend.c -- - * Add a new node with a new datum after an existing node - */ - -#include "lstInt.h" - -/*- - *----------------------------------------------------------------------- - * Lst_InsertAfter -- - * Create a new node and add it to the given list after the given node. - * - * Input: - * l affected list - * ln node after which to append the datum - * d said datum - * - * Results: - * SUCCESS if all went well. - * - * Side Effects: - * A new ListNode is created and linked in to the List. The lastPtr - * field of the List will be altered if ln is the last node in the - * list. lastPtr and firstPtr will alter if the list was empty and - * ln was NULL. - * - *----------------------------------------------------------------------- - */ -ReturnStatus -Lst_InsertAfter(Lst l, LstNode ln, void *d) -{ - List list; - ListNode lNode; - ListNode nLNode; - - if (LstValid (l) && (ln == NULL && LstIsEmpty (l))) { - goto ok; - } - - if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { - return FAILURE; - } - ok: - - list = l; - lNode = ln; - - PAlloc (nLNode, ListNode); - nLNode->datum = d; - nLNode->useCount = nLNode->flags = 0; - - if (lNode == NULL) { - if (list->isCirc) { - nLNode->nextPtr = nLNode->prevPtr = nLNode; - } else { - nLNode->nextPtr = nLNode->prevPtr = NULL; - } - list->firstPtr = list->lastPtr = nLNode; - } else { - nLNode->prevPtr = lNode; - nLNode->nextPtr = lNode->nextPtr; - - lNode->nextPtr = nLNode; - if (nLNode->nextPtr != NULL) { - nLNode->nextPtr->prevPtr = nLNode; - } - - if (lNode == list->lastPtr) { - list->lastPtr = nLNode; - } - } - - return SUCCESS; -} Property changes on: head/contrib/bmake/lst.lib/lstAppend.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/Makefile =================================================================== Property changes on: head/contrib/bmake/lst.lib/Makefile ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/lst.lib/lstMember.c =================================================================== --- head/contrib/bmake/lst.lib/lstMember.c (revision 365365) +++ head/contrib/bmake/lst.lib/lstMember.c (nonexistent) @@ -1,77 +0,0 @@ -/* $NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $ */ - -/* - * Copyright (c) 1988, 1989, 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Adam de Boor. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"; -#else -#include -#ifndef lint -#if 0 -static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93"; -#else -__RCSID("$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"); -#endif -#endif /* not lint */ -#endif - -/*- - * lstMember.c -- - * See if a given datum is on a given list. - */ - -#include "lstInt.h" - -LstNode -Lst_Member(Lst l, void *d) -{ - List list = l; - ListNode lNode; - - if (list == NULL) { - return NULL; - } - lNode = list->firstPtr; - if (lNode == NULL) { - return NULL; - } - - do { - if (lNode->datum == d) { - return lNode; - } - lNode = lNode->nextPtr; - } while (lNode != NULL && lNode != list->firstPtr); - - return NULL; -} Property changes on: head/contrib/bmake/lst.lib/lstMember.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/main.c =================================================================== --- head/contrib/bmake/main.c (revision 365365) +++ head/contrib/bmake/main.c (revision 365366) @@ -1,2252 +1,2220 @@ -/* $NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $"; #else #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: main.c,v 1.279 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: main.c,v 1.331 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * main.c -- * The main file for this entire program. Exit routines etc * reside here. * * Utility functions defined in this file: * Main_ParseArgLine Takes a line of arguments, breaks them and * treats them as if they were given when first * invoked. Used by the parse module to implement * the .MFLAGS target. * * Error Print a tagged error message. The global * MAKE variable must have been defined. This * takes a format string and optional arguments * for it. * * Fatal Print an error message and exit. Also takes * a format string and arguments for it. * * Punt Aborts all jobs and exits with a message. Also * takes a format string and arguments for it. * * Finish Finish things up by printing the number of * errors which occurred, as passed to it, and * exiting. */ #include #include #include #include #include #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) #include #endif #include #include "wait.h" +#include #include #include #include #include #include #include -#include #include "make.h" #include "hash.h" #include "dir.h" #include "job.h" #include "pathnames.h" #include "trace.h" #ifdef USE_IOVEC #include #endif #ifndef DEFMAXLOCAL #define DEFMAXLOCAL DEFMAXJOBS #endif /* DEFMAXLOCAL */ #ifndef __arraycount # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) #endif Lst create; /* Targets to be made */ time_t now; /* Time at start of make */ GNode *DEFAULT; /* .DEFAULT node */ Boolean allPrecious; /* .PRECIOUS given on line by itself */ Boolean deleteOnError; /* .DELETE_ON_ERROR: set */ static Boolean noBuiltins; /* -r flag */ static Lst makefiles; /* ordered list of makefiles to read */ static int printVars; /* -[vV] argument */ #define COMPAT_VARS 1 #define EXPAND_VARS 2 -static Lst variables; /* list of variables to print */ +static Lst variables; /* list of variables to print + * (for -v and -V) */ int maxJobs; /* -j argument */ static int maxJobTokens; /* -j argument */ Boolean compatMake; /* -B argument */ int debug; /* -d argument */ Boolean debugVflag; /* -dV */ Boolean noExecute; /* -n flag */ Boolean noRecursiveExecute; /* -N flag */ Boolean keepgoing; /* -k flag */ Boolean queryFlag; /* -q flag */ Boolean touchFlag; /* -t flag */ Boolean enterFlag; /* -w flag */ Boolean enterFlagObj; /* -w and objdir != srcdir */ Boolean ignoreErrors; /* -i flag */ Boolean beSilent; /* -s flag */ Boolean oldVars; /* variable substitution style */ Boolean checkEnvFirst; /* -e flag */ Boolean parseWarnFatal; /* -W flag */ -Boolean jobServer; /* -J flag */ static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ Boolean varNoExportEnv; /* -X flag */ Boolean doing_depend; /* Set while reading .depend */ static Boolean jobsRunning; /* TRUE if the jobs might be running */ static const char * tracefile; static void MainParseArgs(int, char **); -static int ReadMakefile(const void *, const void *); +static int ReadMakefile(const char *); static void usage(void) MAKE_ATTR_DEAD; static void purge_cached_realpaths(void); static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ char curdir[MAXPATHLEN + 1]; /* Startup directory */ char *progname; /* the program name */ char *makeDependfile; pid_t myPid; int makelevel; FILE *debug_file; Boolean forceJobs = FALSE; /* * On some systems MACHINE is defined as something other than * what we want. */ #ifdef FORCE_MACHINE # undef MACHINE # define MACHINE FORCE_MACHINE #endif extern Lst parseIncPath; /* * For compatibility with the POSIX version of MAKEFLAGS that includes * all the options with out -, convert flags to -f -l -a -g -s. */ static char * explode(const char *flags) { size_t len; char *nf, *st; const char *f; if (flags == NULL) return NULL; for (f = flags; *f; f++) if (!isalpha((unsigned char)*f)) break; if (*f) return bmake_strdup(flags); len = strlen(flags); st = nf = bmake_malloc(len * 3 + 1); while (*flags) { *nf++ = '-'; *nf++ = *flags++; *nf++ = ' '; } *nf = '\0'; return st; } static void parse_debug_options(const char *argvalue) { const char *modules; const char *mode; char *fname; int len; for (modules = argvalue; *modules; ++modules) { switch (*modules) { case 'A': - debug = ~0; + debug = ~(0|DEBUG_LINT); break; case 'a': debug |= DEBUG_ARCH; break; case 'C': debug |= DEBUG_CWD; break; case 'c': debug |= DEBUG_COND; break; case 'd': debug |= DEBUG_DIR; break; case 'e': debug |= DEBUG_ERROR; break; case 'f': debug |= DEBUG_FOR; break; case 'g': if (modules[1] == '1') { debug |= DEBUG_GRAPH1; ++modules; } else if (modules[1] == '2') { debug |= DEBUG_GRAPH2; ++modules; } else if (modules[1] == '3') { debug |= DEBUG_GRAPH3; ++modules; } break; + case 'h': + debug |= DEBUG_HASH; + break; case 'j': debug |= DEBUG_JOB; break; + case 'L': + debug |= DEBUG_LINT; + break; case 'l': debug |= DEBUG_LOUD; break; case 'M': debug |= DEBUG_META; break; case 'm': debug |= DEBUG_MAKE; break; case 'n': debug |= DEBUG_SCRIPT; break; case 'p': debug |= DEBUG_PARSE; break; case 's': debug |= DEBUG_SUFF; break; case 't': debug |= DEBUG_TARG; break; case 'V': debugVflag = TRUE; break; case 'v': debug |= DEBUG_VAR; break; case 'x': debug |= DEBUG_SHELL; break; case 'F': if (debug_file != stdout && debug_file != stderr) fclose(debug_file); if (*++modules == '+') { modules++; mode = "a"; } else mode = "w"; if (strcmp(modules, "stdout") == 0) { debug_file = stdout; goto debug_setbuf; } if (strcmp(modules, "stderr") == 0) { debug_file = stderr; goto debug_setbuf; } len = strlen(modules); fname = bmake_malloc(len + 20); memcpy(fname, modules, len + 1); /* Let the filename be modified by the pid */ if (strcmp(fname + len - 3, ".%d") == 0) snprintf(fname + len - 2, 20, "%d", getpid()); debug_file = fopen(fname, mode); if (!debug_file) { fprintf(stderr, "Cannot open debug file %s\n", fname); usage(); } free(fname); goto debug_setbuf; default: (void)fprintf(stderr, "%s: illegal argument to d option -- %c\n", progname, *modules); usage(); } } debug_setbuf: /* * Make the debug_file unbuffered, and make * stdout line buffered (unless debugfile == stdout). */ setvbuf(debug_file, NULL, _IONBF, 0); if (debug_file != stdout) { setvbuf(stdout, NULL, _IOLBF, 0); } } /* * does path contain any relative components */ -static int +static Boolean is_relpath(const char *path) { const char *cp; if (path[0] != '/') return TRUE; cp = path; - do { - cp = strstr(cp, "/."); - if (!cp) - break; + while ((cp = strstr(cp, "/.")) != NULL) { cp += 2; if (cp[0] == '/' || cp[0] == '\0') return TRUE; else if (cp[0] == '.') { if (cp[1] == '/' || cp[1] == '\0') return TRUE; } - } while (cp); + } return FALSE; } /*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. * * XXX: Deal with command line overriding .MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(int argc, char **argv) { char *p; - int c = '?'; + char c = '?'; int arginc; char *argvalue; const char *getopt_def; struct stat sa, sb; char *optscan; Boolean inOption, dashDash = FALSE; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ #define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w" /* Can't actually use getopt(3) because rescanning is not portable */ getopt_def = OPTFLAGS; rearg: inOption = FALSE; optscan = NULL; while(argc > 1) { char *getopt_spec; if(!inOption) optscan = argv[1]; c = *optscan++; arginc = 0; if(inOption) { if(c == '\0') { ++argv; --argc; inOption = FALSE; continue; } } else { if (c != '-' || dashDash) break; inOption = TRUE; c = *optscan++; } /* '-' found at some earlier point */ getopt_spec = strchr(getopt_def, c); if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { /* - found, and should have an arg */ inOption = FALSE; arginc = 1; argvalue = optscan; if(*argvalue == '\0') { if (argc < 3) goto noarg; argvalue = argv[2]; arginc = 2; } } else { argvalue = NULL; } switch(c) { case '\0': arginc = 1; inOption = FALSE; break; case 'B': compatMake = TRUE; Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); Var_Set(MAKE_MODE, "compat", VAR_GLOBAL); break; case 'C': if (chdir(argvalue) == -1) { (void)fprintf(stderr, "%s: chdir %s: %s\n", progname, argvalue, strerror(errno)); exit(1); } if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } if (!is_relpath(argvalue) && stat(argvalue, &sa) != -1 && stat(curdir, &sb) != -1 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) strncpy(curdir, argvalue, MAXPATHLEN); ignorePWD = TRUE; break; case 'D': if (argvalue == NULL || argvalue[0] == 0) goto noarg; Var_Set(argvalue, "1", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'I': if (argvalue == NULL) goto noarg; Parse_AddIncludeDir(argvalue); Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'J': if (argvalue == NULL) goto noarg; if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { (void)fprintf(stderr, "%s: internal error -- J option malformed (%s)\n", progname, argvalue); usage(); } if ((fcntl(jp_0, F_GETFD, 0) < 0) || (fcntl(jp_1, F_GETFD, 0) < 0)) { #if 0 (void)fprintf(stderr, "%s: ###### warning -- J descriptors were closed!\n", progname); exit(2); #endif jp_0 = -1; jp_1 = -1; compatMake = TRUE; } else { Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); - jobServer = TRUE; } break; case 'N': noExecute = TRUE; noRecursiveExecute = TRUE; Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); break; case 'T': if (argvalue == NULL) goto noarg; tracefile = bmake_strdup(argvalue); Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'V': case 'v': if (argvalue == NULL) goto noarg; printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS; - (void)Lst_AtEnd(variables, argvalue); + Lst_Append(variables, argvalue); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'W': parseWarnFatal = TRUE; break; case 'X': varNoExportEnv = TRUE; Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); break; case 'd': if (argvalue == NULL) goto noarg; /* If '-d-opts' don't pass to children */ if (argvalue[0] == '-') argvalue++; else { Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); } parse_debug_options(argvalue); break; case 'e': checkEnvFirst = TRUE; Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); break; case 'f': if (argvalue == NULL) goto noarg; - (void)Lst_AtEnd(makefiles, argvalue); + Lst_Append(makefiles, argvalue); break; case 'i': ignoreErrors = TRUE; Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); break; case 'j': if (argvalue == NULL) goto noarg; forceJobs = TRUE; maxJobs = strtol(argvalue, &p, 0); if (*p != '\0' || maxJobs < 1) { (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", progname); exit(1); } Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL); maxJobTokens = maxJobs; break; case 'k': keepgoing = TRUE; Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); break; case 'm': if (argvalue == NULL) goto noarg; /* look for magic parent directory search string */ if (strncmp(".../", argvalue, 4) == 0) { if (!Dir_FindHereOrAbove(curdir, argvalue+4, found_path, sizeof(found_path))) break; /* nothing doing */ (void)Dir_AddDir(sysIncPath, found_path); } else { (void)Dir_AddDir(sysIncPath, argvalue); } Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); break; case 'w': enterFlag = TRUE; Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); break; case '-': dashDash = TRUE; break; default: case '?': #ifndef MAKE_NATIVE fprintf(stderr, "getopt(%s) -> %d (%c)\n", OPTFLAGS, c, c); #endif usage(); } argv += arginc; argc -= arginc; } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (; argc > 1; ++argv, --argc) if (Parse_IsVar(argv[1])) { Parse_DoVar(argv[1], VAR_CMD); } else { if (!*argv[1]) Punt("illegal (null) argument."); if (*argv[1] == '-' && !dashDash) goto rearg; - (void)Lst_AtEnd(create, bmake_strdup(argv[1])); + Lst_Append(create, bmake_strdup(argv[1])); } return; noarg: (void)fprintf(stderr, "%s: option requires an argument -- %c\n", progname, c); usage(); } /*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Input: * line Line to fracture * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(const char *line) { - char **argv; /* Manufactured argument vector */ - int argc; /* Number of arguments in argv */ - char *args; /* Space used by the args */ - char *buf, *p1; - char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); - size_t len; + Words words; + char *p1; + const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); + char *buf; if (line == NULL) return; for (; *line == ' '; ++line) continue; if (!*line) return; #ifndef POSIX { /* * $MAKE may simply be naming the make(1) binary */ char *cp; if (!(cp = strrchr(line, '/'))) cp = line; if ((cp = strstr(cp, "make")) && strcmp(cp, "make") == 0) return; } #endif - buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); - (void)snprintf(buf, len, "%s %s", argv0, line); + buf = str_concat3(argv0, " ", line); free(p1); - argv = brk_string(buf, &argc, TRUE, &args); - if (argv == NULL) { + words = Str_Words(buf, TRUE); + if (words.words == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); - MainParseArgs(argc, argv); + MainParseArgs((int)words.len, words.words); - free(args); - free(argv); + Words_Free(words); } Boolean Main_SetObjdir(const char *fmt, ...) { struct stat sb; char *path; char buf[MAXPATHLEN + 1]; char buf2[MAXPATHLEN + 1]; Boolean rc = FALSE; va_list ap; va_start(ap, fmt); vsnprintf(path = buf, MAXPATHLEN, fmt, ap); va_end(ap); if (path[0] != '/') { snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path); path = buf2; } /* look for the directory and try to chdir there */ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { if (chdir(path)) { (void)fprintf(stderr, "make warning: %s: %s.\n", path, strerror(errno)); } else { - strncpy(objdir, path, MAXPATHLEN); + snprintf(objdir, sizeof objdir, "%s", path); Var_Set(".OBJDIR", objdir, VAR_GLOBAL); setenv("PWD", objdir, 1); Dir_InitDot(); purge_cached_realpaths(); rc = TRUE; if (enterFlag && strcmp(objdir, curdir) != 0) enterFlagObj = TRUE; } } return rc; } static Boolean Main_SetVarObjdir(const char *var, const char *suffix) { - char *p, *path, *xpath; + char *path_freeIt; + const char *path = Var_Value(var, VAR_CMD, &path_freeIt); + const char *xpath; + char *xpath_freeIt; - if ((path = Var_Value(var, VAR_CMD, &p)) == NULL || - *path == '\0') + if (path == NULL || path[0] == '\0') { + bmake_free(path_freeIt); return FALSE; + } /* expand variable substitutions */ + xpath = path; + xpath_freeIt = NULL; if (strchr(path, '$') != 0) - xpath = Var_Subst(NULL, path, VAR_GLOBAL, VARF_WANTRES); - else - xpath = path; + xpath = xpath_freeIt = Var_Subst(path, VAR_GLOBAL, + VARE_WANTRES); (void)Main_SetObjdir("%s%s", xpath, suffix); - if (xpath != path) - free(xpath); - free(p); + bmake_free(xpath_freeIt); + bmake_free(path_freeIt); return TRUE; } -/*- - * ReadAllMakefiles -- - * wrapper around ReadMakefile() to read all. - * - * Results: - * TRUE if ok, FALSE on error - */ -static int -ReadAllMakefiles(const void *p, const void *q) +/* Read and parse the makefile. + * Return TRUE if reading the makefile succeeded, for Lst_Find. */ +static Boolean +ReadMakefileSucceeded(const void *fname, const void *unused) { - return ReadMakefile(p, q) == 0; + return ReadMakefile(fname) == 0; } +/* Read and parse the makefile. + * Return TRUE if reading the makefile failed, for Lst_Find. */ +static Boolean +ReadMakefileFailed(const void *fname, const void *unused) +{ + return ReadMakefile(fname) != 0; +} + int str2Lst_Append(Lst lp, char *str, const char *sep) { char *cp; int n; if (!sep) sep = " \t"; for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { - (void)Lst_AtEnd(lp, cp); + Lst_Append(lp, cp); n++; } return n; } #ifdef SIGINFO /*ARGSUSED*/ static void siginfo(int signo MAKE_ATTR_UNUSED) { char dir[MAXPATHLEN]; char str[2 * MAXPATHLEN]; int len; if (getcwd(dir, sizeof(dir)) == NULL) return; len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); if (len > 0) (void)write(STDERR_FILENO, str, (size_t)len); } #endif /* * Allow makefiles some control over the mode we run in. */ void MakeMode(const char *mode) { - char *mp = NULL; + char *mode_freeIt = NULL; - if (!mode) - mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}", - VAR_GLOBAL, VARF_WANTRES); + if (mode == NULL) + mode = mode_freeIt = Var_Subst("${" MAKE_MODE ":tl}", + VAR_GLOBAL, VARE_WANTRES); - if (mode && *mode) { + if (mode[0] != '\0') { if (strstr(mode, "compat")) { compatMake = TRUE; forceJobs = FALSE; } #if USE_META if (strstr(mode, "meta")) meta_mode_init(mode); #endif } - free(mp); + free(mode_freeIt); } static void doPrintVars(void) { LstNode ln; Boolean expandVars; if (printVars == EXPAND_VARS) expandVars = TRUE; else if (debugVflag) expandVars = FALSE; else expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); - for (ln = Lst_First(variables); ln != NULL; - ln = Lst_Succ(ln)) { - char *var = (char *)Lst_Datum(ln); - char *value; + for (ln = Lst_First(variables); ln != NULL; ln = LstNode_Next(ln)) { + char *var = LstNode_Datum(ln); + const char *value; char *p1; if (strchr(var, '$')) { - value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, - VARF_WANTRES); + value = p1 = Var_Subst(var, VAR_GLOBAL, VARE_WANTRES); } else if (expandVars) { char tmp[128]; int len = snprintf(tmp, sizeof(tmp), "${%s}", var); if (len >= (int)sizeof(tmp)) Fatal("%s: variable name too big: %s", progname, var); - value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL, - VARF_WANTRES); + value = p1 = Var_Subst(tmp, VAR_GLOBAL, VARE_WANTRES); } else { value = Var_Value(var, VAR_GLOBAL, &p1); } printf("%s\n", value ? value : ""); - free(p1); + bmake_free(p1); } } static Boolean runTargets(void) { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate; /* FALSE if all targets up to date */ /* * Have now read the entire graph and need to make a list of * targets to create. If none was given on the command line, * we consult the parsing module to find the main target(s) * to create. */ if (Lst_IsEmpty(create)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); if (!compatMake) { /* * Initialize job module before traversing the graph * now that any .BEGIN and .END targets have been read. * This is done only if the -q flag wasn't given * (to prevent the .BEGIN from being executed should * it exist). */ if (!queryFlag) { Job_Init(); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs); } else { /* * Compat_Init will take care of creating all the * targets as well as initializing the module. */ Compat_Run(targs); outOfDate = FALSE; } - Lst_Destroy(targs, NULL); + Lst_Free(targs); return outOfDate; } /*- * main -- * The main function, for obvious reasons. Initializes variables * and a few modules, then parses the arguments give it in the * environment and on the command line. Reads the system makefile * followed by either Makefile, makefile or the file given by the * -f argument. Sets the .MAKEFLAGS PMake variable based on all the * flags it has received by then uses either the Make or the Compat * module to create the initial list of targets. * * Results: * If -q was given, exits -1 if anything was out-of-date. Else it exits * 0. * * Side Effects: * The program exits when done. Targets are created. etc. etc. etc. */ int main(int argc, char **argv) { Boolean outOfDate; /* FALSE if all targets up to date */ struct stat sb, sa; char *p1, *path; char mdpath[MAXPATHLEN]; #ifdef FORCE_MACHINE const char *machine = FORCE_MACHINE; #else const char *machine = getenv("MACHINE"); #endif const char *machine_arch = getenv("MACHINE_ARCH"); char *syspath = getenv("MAKESYSPATH"); Lst sysMkPath; /* Path of sys.mk */ char *cp = NULL, *start; /* avoid faults on read-only strings */ static char defsyspath[] = _PATH_DEFSYSPATH; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ struct timeval rightnow; /* to initialize random seed */ struct utsname utsname; /* default to writing debug to stderr */ debug_file = stderr; #ifdef SIGINFO (void)bmake_signal(SIGINFO, siginfo); #endif /* * Set the seed to produce a different random sequence * on each program execution. */ gettimeofday(&rightnow, NULL); srandom(rightnow.tv_sec + rightnow.tv_usec); if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) /* * get rid of resource limit on file descriptors */ { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; (void)setrlimit(RLIMIT_NOFILE, &rl); } } #endif if (uname(&utsname) == -1) { (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, strerror(errno)); exit(2); } /* * Get the name of this type of MACHINE from utsname * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * * Note that both MACHINE and MACHINE_ARCH are decided at * run-time. */ if (!machine) { #ifdef MAKE_NATIVE machine = utsname.machine; #else #ifdef MAKE_MACHINE machine = MAKE_MACHINE; #else machine = "unknown"; #endif #endif } if (!machine_arch) { #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_MACHINE_ARCH) static char machine_arch_buf[sizeof(utsname.machine)]; int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; size_t len = sizeof(machine_arch_buf); if (sysctl(mib, __arraycount(mib), machine_arch_buf, &len, NULL, 0) < 0) { (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, strerror(errno)); exit(2); } machine_arch = machine_arch_buf; #else #ifndef MACHINE_ARCH #ifdef MAKE_MACHINE_ARCH - machine_arch = MAKE_MACHINE_ARCH; + machine_arch = MAKE_MACHINE_ARCH; #else machine_arch = "unknown"; #endif #else machine_arch = MACHINE_ARCH; #endif #endif } myPid = getpid(); /* remember this for vFork() */ /* * Just in case MAKEOBJDIR wants us to do something tricky. */ Var_Init(); /* Initialize the lists of variables for * parsing arguments */ Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL); Var_Set("MACHINE", machine, VAR_GLOBAL); Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); #ifdef MAKE_VERSION Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL); #endif Var_Set(".newline", "\n", VAR_GLOBAL); /* handy for :@ loops */ /* * This is the traditional preference for makefiles. */ #ifndef MAKEFILE_PREFERENCE_LIST # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" #endif Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, VAR_GLOBAL); Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); - create = Lst_Init(FALSE); - makefiles = Lst_Init(FALSE); + create = Lst_Init(); + makefiles = Lst_Init(); printVars = 0; debugVflag = FALSE; - variables = Lst_Init(FALSE); + variables = Lst_Init(); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ keepgoing = FALSE; /* Stop on error */ allPrecious = FALSE; /* Remove targets when interrupted */ deleteOnError = FALSE; /* Historical default behavior */ queryFlag = FALSE; /* This is not just a check-run */ noBuiltins = FALSE; /* Read the built-in rules */ touchFlag = FALSE; /* Actually update targets */ debug = 0; /* No debug verbosity, please. */ jobsRunning = FALSE; maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ maxJobTokens = maxJobs; compatMake = FALSE; /* No compat mode */ ignorePWD = FALSE; /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ /* * Initialize various variables. * MAKE also gets this name, for compatibility * .MAKEFLAGS gets set to the empty string just in case. * MFLAGS also gets initialized empty, for compatibility. */ Parse_Init(); if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) { /* * Leave alone if it is an absolute path, or if it does * not contain a '/' in which case we need to find it in * the path, like execvp(3) and the shells do. */ p1 = argv[0]; } else { /* * A relative path, canonicalize it. */ p1 = cached_realpath(argv[0], mdpath); if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) { p1 = argv[0]; /* realpath failed */ } } Var_Set("MAKE", p1, VAR_GLOBAL); Var_Set(".MAKE", p1, VAR_GLOBAL); Var_Set(MAKEFLAGS, "", VAR_GLOBAL); Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL); Var_Set("MFLAGS", "", VAR_GLOBAL); Var_Set(".ALLTARGETS", "", VAR_GLOBAL); /* some makefiles need to know this */ Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMD); /* * Set some other useful macros */ { char tmp[64], *ep; makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; if (makelevel < 0) makelevel = 0; snprintf(tmp, sizeof(tmp), "%d", makelevel); Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL); snprintf(tmp, sizeof(tmp), "%u", myPid); Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); snprintf(tmp, sizeof(tmp), "%u", getppid()); Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); } if (makelevel > 0) { char pn[1024]; snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); progname = bmake_strdup(pn); } #ifdef USE_META meta_init(); #endif - Dir_Init(NULL); /* Dir_* safe to call from MainParseArgs */ + Dir_Init(); /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ #ifdef POSIX p1 = explode(getenv("MAKEFLAGS")); Main_ParseArgLine(p1); free(p1); #else Main_ParseArgLine(getenv("MAKE")); #endif /* * Find where we are (now). * We take care of PWD for the automounter below... */ if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: getcwd: %s.\n", progname, strerror(errno)); exit(2); } MainParseArgs(argc, argv); if (enterFlag) printf("%s: Entering directory `%s'\n", progname, curdir); /* * Verify that cwd is sane. */ if (stat(curdir, &sa) == -1) { (void)fprintf(stderr, "%s: %s: %s.\n", progname, curdir, strerror(errno)); exit(2); } /* * All this code is so that we know where we are when we start up * on a different machine with pmake. * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can vary depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) * or via subdir.mk in which case its likely a shell which does * not provide it. * So, to stop it breaking this case only, we ignore PWD if * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform. */ #ifndef NO_PWD_OVERRIDE if (!ignorePWD) { char *pwd, *ptmp1 = NULL, *ptmp2 = NULL; if ((pwd = getenv("PWD")) != NULL && Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &ptmp1) == NULL) { const char *makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMD, &ptmp2); if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) (void)strncpy(curdir, pwd, MAXPATHLEN); } } - free(ptmp1); - free(ptmp2); + bmake_free(ptmp1); + bmake_free(ptmp2); } #endif Var_Set(".CURDIR", curdir, VAR_GLOBAL); /* * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, * MAKEOBJDIR is set in the environment, try only that value * and fall back to .CURDIR if it does not exist. * * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none * of these paths exist, just use .CURDIR. */ - Dir_Init(curdir); + Dir_InitDir(curdir); (void)Main_SetObjdir("%s", curdir); if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) && !Main_SetVarObjdir("MAKEOBJDIR", "") && !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) && !Main_SetObjdir("%s", _PATH_OBJDIR)) (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir); /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); Trace_Init(tracefile); DEFAULT = NULL; (void)time(&now); Trace_Log(MAKESTART, NULL); /* * Set up the .TARGETS variable to contain the list of targets to be * created. If none specified, make the variable empty -- the parser * will fill the thing in with the default or .MAIN target. */ if (!Lst_IsEmpty(create)) { LstNode ln; - for (ln = Lst_First(create); ln != NULL; - ln = Lst_Succ(ln)) { - char *name = (char *)Lst_Datum(ln); - + for (ln = Lst_First(create); ln != NULL; ln = LstNode_Next(ln)) { + char *name = LstNode_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else Var_Set(".TARGETS", "", VAR_GLOBAL); /* * If no user-supplied system path was given (through the -m option) * add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ - if (syspath == NULL || *syspath == '\0') + /* XXX: mismatch: the -m option sets sysIncPath, not syspath */ + if (syspath == NULL || syspath[0] == '\0') syspath = defsyspath; else syspath = bmake_strdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; if (*cp == ':') { *cp++ = '\0'; } /* look for magic parent directory search string */ if (strncmp(".../", start, 4) != 0) { (void)Dir_AddDir(defIncPath, start); } else { if (Dir_FindHereOrAbove(curdir, start+4, found_path, sizeof(found_path))) { (void)Dir_AddDir(defIncPath, found_path); } } } if (syspath != defsyspath) free(syspath); /* * Read in the built-in rules first, followed by the specified - * makefile, if it was (makefile != NULL), or the default - * makefile and Makefile, in that order, if it wasn't. + * makefiles, or the default makefile and Makefile, in that order, + * if no makefiles were given on the command line. */ if (!noBuiltins) { LstNode ln; - sysMkPath = Lst_Init(FALSE); + sysMkPath = Lst_Init(); Dir_Expand(_PATH_DEFSYSMK, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath, sysMkPath); if (Lst_IsEmpty(sysMkPath)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); - ln = Lst_Find(sysMkPath, NULL, ReadMakefile); + ln = Lst_Find(sysMkPath, ReadMakefileSucceeded, NULL); if (ln == NULL) Fatal("%s: cannot open %s.", progname, - (char *)Lst_Datum(ln)); + (char *)LstNode_Datum(Lst_First(sysMkPath))); } if (!Lst_IsEmpty(makefiles)) { LstNode ln; - ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); + ln = Lst_Find(makefiles, ReadMakefileFailed, NULL); if (ln != NULL) Fatal("%s: cannot open %s.", progname, - (char *)Lst_Datum(ln)); + (char *)LstNode_Datum(ln)); } else { - p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", - VAR_CMD, VARF_WANTRES); - if (p1) { + p1 = Var_Subst("${" MAKEFILE_PREFERENCE "}", + VAR_CMD, VARE_WANTRES); (void)str2Lst_Append(makefiles, p1, NULL); - (void)Lst_Find(makefiles, NULL, ReadMakefile); + (void)Lst_Find(makefiles, ReadMakefileSucceeded, NULL); free(p1); - } } /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!noBuiltins || !printVars) { - makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", - VAR_CMD, VARF_WANTRES); + makeDependfile = Var_Subst("${.MAKE.DEPENDFILE:T}", + VAR_CMD, VARE_WANTRES); doing_depend = TRUE; - (void)ReadMakefile(makeDependfile, NULL); + (void)ReadMakefile(makeDependfile); doing_depend = FALSE; } if (enterFlagObj) printf("%s: Entering directory `%s'\n", progname, objdir); MakeMode(NULL); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); - free(p1); + bmake_free(p1); if (!forceJobs && !compatMake && Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) { char *value; int n; - value = Var_Subst(NULL, "${.MAKE.JOBS}", VAR_GLOBAL, VARF_WANTRES); + value = Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES); n = strtol(value, NULL, 0); if (n < 1) { (void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS -- must be positive integer!\n", progname); exit(1); } if (n != maxJobs) { Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, value, VAR_GLOBAL); } maxJobs = n; maxJobTokens = maxJobs; forceJobs = TRUE; free(value); } /* * Be compatible if user did not specify -j and did not explicitly * turned compatibility on */ if (!compatMake && !forceJobs) { compatMake = TRUE; } if (!compatMake) Job_ServerStart(maxJobTokens, jp_0, jp_1); if (DEBUG(JOB)) - fprintf(debug_file, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", - jp_0, jp_1, maxJobs, maxJobTokens, compatMake); + fprintf(debug_file, + "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", + jp_0, jp_1, maxJobs, maxJobTokens, compatMake ? 1 : 0); if (!printVars) Main_ExportMAKEFLAGS(TRUE); /* initial export */ /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * ::... */ if (Var_Exists("VPATH", VAR_CMD)) { char *vpath, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; - vpath = Var_Subst(NULL, VPATH, VAR_CMD, VARF_WANTRES); + vpath = Var_Subst(VPATH, VAR_CMD, VARE_WANTRES); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++) continue; /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ (void)Dir_AddDir(dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); free(vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* * Propagate attributes through :: dependency lists. */ Targ_Propagate(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* print the values of any variables requested by the user */ if (printVars) { doPrintVars(); outOfDate = FALSE; } else { outOfDate = runTargets(); } #ifdef CLEANUP - Lst_Destroy(variables, NULL); - Lst_Destroy(makefiles, NULL); - Lst_Destroy(create, (FreeProc *)free); + Lst_Free(variables); + Lst_Free(makefiles); + Lst_Destroy(create, free); #endif /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEEND, 0); if (enterFlagObj) printf("%s: Leaving directory `%s'\n", progname, objdir); if (enterFlag) printf("%s: Leaving directory `%s'\n", progname, curdir); #ifdef USE_META meta_finish(); #endif Suff_End(); - Targ_End(); + Targ_End(); Arch_End(); Var_End(); Parse_End(); Dir_End(); Job_End(); Trace_End(); return outOfDate ? 1 : 0; } -/*- - * ReadMakefile -- - * Open and parse the given makefile. +/* Open and parse the given makefile, with all its side effects. * * Results: * 0 if ok. -1 if couldn't open file. - * - * Side Effects: - * lots */ static int -ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) +ReadMakefile(const char *fname) { - const char *fname = p; /* makefile to read */ int fd; - size_t len = MAXPATHLEN; - char *name, *path = bmake_malloc(len); + char *name, *path = NULL; if (!strcmp(fname, "-")) { Parse_File(NULL /*stdin*/, -1); Var_Set("MAKEFILE", "", VAR_INTERNAL); } else { /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) && *fname != '/') { - size_t plen = strlen(curdir) + strlen(fname) + 2; - if (len < plen) - path = bmake_realloc(path, len = 2 * plen); - - (void)snprintf(path, len, "%s/%s", curdir, fname); + path = str_concat3(curdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } + free(path); /* If curdir failed, try objdir (ala .depend) */ - plen = strlen(objdir) + strlen(fname) + 2; - if (len < plen) - path = bmake_realloc(path, len = 2 * plen); - (void)snprintf(path, len, "%s/%s", objdir, fname); + path = str_concat3(objdir, "/", fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } } else { fd = open(fname, O_RDONLY); if (fd != -1) goto found; } /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (!name) name = Dir_FindFile(fname, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); if (!name || (fd = open(name, O_RDONLY)) == -1) { free(name); free(path); return -1; } fname = name; /* * set the MAKEFILE variable desired by System V fans -- the * placement of the setting here means it gets set to the last * makefile specified, as it is set by SysV make. */ found: if (!doing_depend) Var_Set("MAKEFILE", fname, VAR_INTERNAL); Parse_File(fname, fd); } free(path); return 0; } /*- * Cmd_Exec -- * Execute the command in cmd, and return the output of that command - * in a string. + * in a string. In the output, newlines are replaced with spaces. * * Results: - * A string containing the output of the command, or the empty string - * If errnum is not NULL, it contains the reason for the command failure + * A string containing the output of the command, or the empty string. + * *errfmt returns a format string describing the command failure, + * if any, using a single %s conversion specification. * * Side Effects: * The string must be freed by the caller. */ char * -Cmd_Exec(const char *cmd, const char **errnum) +Cmd_Exec(const char *cmd, const char **errfmt) { const char *args[4]; /* Args for invoking the shell */ int fds[2]; /* Pipe streams */ int cpid; /* Child PID */ int pid; /* PID from wait() */ - char *res; /* result */ WAIT_T status; /* command exit status */ Buffer buf; /* buffer to store the result */ + ssize_t bytes_read; + char *res; /* result */ + size_t res_len; char *cp; - int cc; /* bytes read, or -1 */ int savederr; /* saved errno */ + *errfmt = NULL; - *errnum = NULL; - if (!shellName) Shell_Init(); /* * Set up arguments for shell */ args[0] = shellName; args[1] = "-c"; args[2] = cmd; args[3] = NULL; /* * Open a pipe for fetching its output */ if (pipe(fds) == -1) { - *errnum = "Couldn't create pipe for \"%s\""; + *errfmt = "Couldn't create pipe for \"%s\""; goto bad; } /* * Fork */ switch (cpid = vFork()) { case 0: /* * Close input side of pipe */ (void)close(fds[0]); /* * Duplicate the output stream to the shell's output, then * shut the extra thing down. Note we don't fetch the error * stream...why not? Why? */ (void)dup2(fds[1], 1); (void)close(fds[1]); Var_ExportVars(); (void)execv(shellPath, UNCONST(args)); _exit(1); /*NOTREACHED*/ case -1: - *errnum = "Couldn't exec \"%s\""; + *errfmt = "Couldn't exec \"%s\""; goto bad; default: /* * No need for the writing half */ (void)close(fds[1]); savederr = 0; Buf_Init(&buf, 0); do { char result[BUFSIZ]; - cc = read(fds[0], result, sizeof(result)); - if (cc > 0) - Buf_AddBytes(&buf, cc, result); + bytes_read = read(fds[0], result, sizeof(result)); + if (bytes_read > 0) + Buf_AddBytes(&buf, result, (size_t)bytes_read); } - while (cc > 0 || (cc == -1 && errno == EINTR)); - if (cc == -1) + while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); + if (bytes_read == -1) savederr = errno; /* * Close the input side of the pipe. */ (void)close(fds[0]); /* * Wait for the process to exit. */ while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { JobReapChild(pid, status, FALSE); continue; } - cc = Buf_Size(&buf); + res_len = Buf_Size(&buf); res = Buf_Destroy(&buf, FALSE); if (savederr != 0) - *errnum = "Couldn't read shell's output for \"%s\""; + *errfmt = "Couldn't read shell's output for \"%s\""; if (WIFSIGNALED(status)) - *errnum = "\"%s\" exited on a signal"; + *errfmt = "\"%s\" exited on a signal"; else if (WEXITSTATUS(status) != 0) - *errnum = "\"%s\" returned non-zero status"; + *errfmt = "\"%s\" returned non-zero status"; - /* - * Null-terminate the result, convert newlines to spaces and - * install it in the variable. - */ - res[cc] = '\0'; - cp = &res[cc]; - - if (cc > 0 && *--cp == '\n') { - /* - * A final newline is just stripped - */ - *cp-- = '\0'; - } - while (cp >= res) { - if (*cp == '\n') { + /* Convert newlines to spaces. A final newline is just stripped */ + if (res_len > 0 && res[res_len - 1] == '\n') + res[res_len - 1] = '\0'; + for (cp = res; *cp != '\0'; cp++) + if (*cp == '\n') *cp = ' '; - } - cp--; - } break; } return res; bad: - res = bmake_malloc(1); - *res = '\0'; - return res; + return bmake_strdup(""); } /*- * Error -- * Print an error message given its format. * * Results: * None. * * Side Effects: * The message is printed. */ /* VARARGS */ void Error(const char *fmt, ...) { va_list ap; FILE *err_file; err_file = debug_file; if (err_file == stdout) err_file = stderr; (void)fflush(stdout); for (;;) { va_start(ap, fmt); fprintf(err_file, "%s: ", progname); (void)vfprintf(err_file, fmt, ap); va_end(ap); (void)fprintf(err_file, "\n"); (void)fflush(err_file); if (err_file == stderr) break; err_file = stderr; } } /*- * Fatal -- * Produce a Fatal error message. If jobs are running, waits for them * to finish. * * Results: * None * * Side Effects: * The program exits */ /* VARARGS */ void Fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (jobsRunning) Job_Wait(); (void)fflush(stdout); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintOnError(NULL, NULL); if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, 0); exit(2); /* Not 1 so -q can distinguish error */ } /* * Punt -- * Major exception once jobs are being created. Kills all jobs, prints * a message and exits. * * Results: * None * * Side Effects: * All children are killed indiscriminately and the program Lib_Exits */ /* VARARGS */ void Punt(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)fflush(stdout); (void)fprintf(stderr, "%s: ", progname); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintOnError(NULL, NULL); DieHorribly(); } /*- * DieHorribly -- * Exit without giving a message. * * Results: * None * * Side Effects: * A big one... */ void DieHorribly(void) { if (jobsRunning) Job_AbortAll(); if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, 0); exit(2); /* Not 1, so -q can distinguish error */ } /* * Finish -- * Called when aborting due to errors in child shell to signal * abnormal exit. * * Results: * None * * Side Effects: * The program exits */ void Finish(int errors) - /* number of errors encountered in Make_Make */ + /* number of errors encountered in Make_Make */ { if (dieQuietly(NULL, -1)) exit(2); Fatal("%d error%s", errors, errors == 1 ? "" : "s"); } /* * eunlink -- * Remove a file carefully, avoiding directories. */ int eunlink(const char *file) { struct stat st; if (lstat(file, &st) == -1) return -1; if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -1; } return unlink(file); } /* * execError -- * Print why exec failed, avoiding stdio. */ void execError(const char *af, const char *av) { #ifdef USE_IOVEC int i = 0; struct iovec iov[8]; #define IOADD(s) \ (void)(iov[i].iov_base = UNCONST(s), \ iov[i].iov_len = strlen(iov[i].iov_base), \ i++) #else #define IOADD(s) (void)write(2, s, strlen(s)) #endif IOADD(progname); IOADD(": "); IOADD(af); IOADD("("); IOADD(av); IOADD(") failed ("); IOADD(strerror(errno)); IOADD(")\n"); #ifdef USE_IOVEC while (writev(2, iov, 8) == -1 && errno == EAGAIN) continue; #endif } /* * usage -- * exit with usage message */ static void usage(void) { char *p; if ((p = strchr(progname, '[')) != NULL) - *p = '\0'; + *p = '\0'; (void)fprintf(stderr, -"usage: %s [-BeikNnqrstWwX] \n\ - [-C directory] [-D variable] [-d flags] [-f makefile]\n\ - [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\ - [-V variable] [-v variable] [variable=value] [target ...]\n", +"usage: %s [-BeikNnqrstWwX] \n" +" [-C directory] [-D variable] [-d flags] [-f makefile]\n" +" [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" +" [-V variable] [-v variable] [variable=value] [target ...]\n", progname); exit(2); } /* * realpath(3) can get expensive, cache results... */ static GNode *cached_realpaths = NULL; static GNode * get_cached_realpaths(void) { if (!cached_realpaths) { cached_realpaths = Targ_NewGN("Realpath"); #ifndef DEBUG_REALPATH_CACHE cached_realpaths->flags = INTERNAL; #endif } return cached_realpaths; } /* purge any relative paths */ static void purge_cached_realpaths(void) { GNode *cache = get_cached_realpaths(); Hash_Entry *he, *nhe; Hash_Search hs; he = Hash_EnumFirst(&cache->context, &hs); while (he) { nhe = Hash_EnumNext(&hs); if (he->name[0] != '/') { if (DEBUG(DIR)) fprintf(stderr, "cached_realpath: purging %s\n", he->name); Hash_DeleteEntry(&cache->context, he); } he = nhe; } } char * cached_realpath(const char *pathname, char *resolved) { GNode *cache; - char *rp, *cp; + const char *rp; + char *cp; if (!pathname || !pathname[0]) return NULL; cache = get_cached_realpaths(); if ((rp = Var_Value(pathname, cache, &cp)) != NULL) { /* a hit */ strlcpy(resolved, rp, MAXPATHLEN); } else if ((rp = realpath(pathname, resolved)) != NULL) { Var_Set(pathname, rp, cache); } /* else should we negative-cache? */ - free(cp); + bmake_free(cp); return rp ? resolved : NULL; } int PrintAddr(void *a, void *b) { printf("%lx ", (unsigned long) a); return b ? 0 : 0; } static int addErrorCMD(void *cmdp, void *gnp MAKE_ATTR_UNUSED) { if (cmdp == NULL) return 1; /* stop */ Var_Append(".ERROR_CMD", cmdp, VAR_GLOBAL); return 0; } /* * Return true if we should die without noise. * For example our failing child was a sub-make * or failure happend elsewhere. */ int dieQuietly(GNode *gn, int bf) { static int quietly = -1; if (quietly < 0) { if (DEBUG(JOB) || getBoolean(".MAKE.DIE_QUIETLY", 1) == 0) quietly = 0; else if (bf >= 0) quietly = bf; else quietly = (gn) ? ((gn->type & (OP_MAKE)) != 0) : 0; } return quietly; } void PrintOnError(GNode *gn, const char *s) { static GNode *en = NULL; - char tmp[64]; + const char *expr; char *cp; + if (DEBUG(HASH)) { + Targ_Stats(); + Var_Stats(); + } + /* we generally want to keep quiet if a sub-make died */ if (dieQuietly(gn, -1)) return; if (s) printf("%s", s); printf("\n%s: stopped in %s\n", progname, curdir); if (en) return; /* we've been here! */ if (gn) { /* * We can print this even if there is no .ERROR target. */ Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); Var_Delete(".ERROR_CMD", VAR_GLOBAL); Lst_ForEach(gn->commands, addErrorCMD, gn); } - strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", - sizeof(tmp) - 1); - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - if (cp) { - if (*cp) - printf("%s", cp); - free(cp); - } + expr = "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}"; + cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + printf("%s", cp); + free(cp); fflush(stdout); /* * Finally, see if there is a .ERROR target, and run it if so. */ en = Targ_FindNode(".ERROR", TARG_NOCREATE); if (en) { en->type |= OP_SPECIAL; Compat_Make(en, en); } } void Main_ExportMAKEFLAGS(Boolean first) { - static int once = 1; - char tmp[64]; + static Boolean once = TRUE; + const char *expr; char *s; if (once != first) return; - once = 0; + once = FALSE; - strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", - sizeof(tmp)); - s = Var_Subst(NULL, tmp, VAR_CMD, VARF_WANTRES); - if (s && *s) { + expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; + s = Var_Subst(expr, VAR_CMD, VARE_WANTRES); + if (s[0] != '\0') { #ifdef POSIX setenv("MAKEFLAGS", s, 1); #else setenv("MAKE", s, 1); #endif } } char * getTmpdir(void) { static char *tmpdir = NULL; if (!tmpdir) { struct stat st; /* * Honor $TMPDIR but only if it is valid. * Ensure it ends with /. */ - tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, - VARF_WANTRES); + tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, + VARE_WANTRES); if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { free(tmpdir); tmpdir = bmake_strdup(_PATH_TMP); } } return tmpdir; } /* * Create and open a temp file using "pattern". * If "fnamep" is provided set it to a copy of the filename created. * Otherwise unlink the file once open. */ int mkTempFile(const char *pattern, char **fnamep) { static char *tmpdir = NULL; char tfile[MAXPATHLEN]; int fd; if (!pattern) pattern = TMPPAT; if (!tmpdir) tmpdir = getTmpdir(); if (pattern[0] == '/') { snprintf(tfile, sizeof(tfile), "%s", pattern); } else { snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); } if ((fd = mkstemp(tfile)) < 0) Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); if (fnamep) { *fnamep = bmake_strdup(tfile); } else { unlink(tfile); /* we just want the descriptor */ } return fd; } /* * Convert a string representation of a boolean. * Anything that looks like "No", "False", "Off", "0" etc, * is FALSE, otherwise TRUE. */ Boolean s2Boolean(const char *s, Boolean bf) { if (s) { switch(*s) { case '\0': /* not set - the default wins */ break; case '0': case 'F': case 'f': case 'N': case 'n': bf = FALSE; break; case 'O': case 'o': switch (s[1]) { case 'F': case 'f': bf = FALSE; break; default: bf = TRUE; break; } break; default: bf = TRUE; break; } } return bf; } /* * Return a Boolean based on setting of a knob. * * If the knob is not set, the supplied default is the return value. * If set, anything that looks or smells like "No", "False", "Off", "0" etc, * is FALSE, otherwise TRUE. */ Boolean -getBoolean(const char *name, Boolean bf) +getBoolean(const char *name, Boolean fallback) { - char tmp[64]; - char *cp; - - if (snprintf(tmp, sizeof(tmp), "${%s:U:tl}", name) < (int)(sizeof(tmp))) { - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - - if (cp) { - bf = s2Boolean(cp, bf); - free(cp); - } - } - return bf; + char *expr = str_concat3("${", name, ":U:tl}"); + char *value = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + Boolean res = s2Boolean(value, fallback); + free(value); + free(expr); + return res; } Index: head/contrib/bmake/make-bootstrap.sh.in =================================================================== --- head/contrib/bmake/make-bootstrap.sh.in (revision 365365) +++ head/contrib/bmake/make-bootstrap.sh.in (revision 365366) @@ -1,101 +1,94 @@ #!/bin/sh set -e srcdir=@srcdir@ DEFAULT_SYS_PATH="@default_sys_path@" case "@use_meta@" in yes) XDEFS="-DUSE_META ${XDEFS}";; esac CC="@CC@" CFLAGS="@CFLAGS@ -I. -I${srcdir} @DEFS@ @CPPFLAGS@ -DMAKE_NATIVE ${XDEFS} -DBMAKE_PATH_MAX=@bmake_path_max@" MAKE_VERSION=@_MAKE_VERSION@ MDEFS="-DMAKE_VERSION=\"$MAKE_VERSION\" \ -D@force_machine@MACHINE=\"@machine@\" -DMACHINE_ARCH=\"@machine_arch@\" \ -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\"" LDFLAGS="@LDFLAGS@" LIBS="@LIBS@" toUpper() { ${TR:-tr} abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ } do_compile2() { obj="$1"; shift src="$1"; shift echo ${CC} -c ${CFLAGS} "$@" -o "$obj" "$src" ${CC} -c ${CFLAGS} "$@" -o "$obj" "$src" } do_compile() { obj="$1"; shift case "$1" in *.c) src=$1; shift;; *) src=`basename "$obj" .o`.c;; esac for d in "$srcdir" "$srcdir/lst.lib" do test -s "$d/$src" || continue do_compile2 "$obj" "$d/$src" "$@" || exit 1 return done echo "Unknown object file '$obj'" >&2 exit 1 } do_link() { output="$1"; shift echo ${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS} ${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS} } -BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o getopt hash.o \ -make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \ +BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \ +lst.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 \ -lstAtEnd.o lstEnQueue.o lstInsert.o lstAtFront.o lstIsAtEnd.o \ -lstClose.o lstFind.o lstIsEmpty.o lstRemove.o lstConcat.o \ -lstFindFrom.o lstLast.o lstReplace.o lstFirst.o lstDatum.o \ -lstForEach.o lstMember.o lstSucc.o lstDeQueue.o lstForEachFrom.o \ -lstDestroy.o lstNext.o lstPrev.o" LIB_OBJECTS="@LIBOBJS@" do_compile main.o ${MDEFS} for o in ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS} do do_compile "$o" done case "@use_meta@" in yes) 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 ${MDEFS} BASE_OBJECTS="meta.o ${BASE_OBJECTS}" ;; esac do_compile job.o ${MDEFS} do_link bmake main.o job.o ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS} Index: head/contrib/bmake/make-conf.h =================================================================== --- head/contrib/bmake/make-conf.h (revision 365365) +++ head/contrib/bmake/make-conf.h (revision 365366) @@ -1,162 +1,164 @@ -/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */ +/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)config.h 8.1 (Berkeley) 6/6/93 */ /* * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)config.h 8.1 (Berkeley) 6/6/93 */ /* * DEFMAXJOBS * DEFMAXLOCAL * These control the default concurrency. On no occasion will more * than DEFMAXJOBS targets be created at once (locally or remotely) * DEFMAXLOCAL is the highest number of targets which will be * created on the local machine at once. Note that if you set this * to 0, nothing will ever happen... */ #define DEFMAXJOBS 4 #define DEFMAXLOCAL 1 /* * INCLUDES * LIBRARIES * These control the handling of the .INCLUDES and .LIBS variables. * If INCLUDES is defined, the .INCLUDES variable will be filled * from the search paths of those suffixes which are marked by * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS * See suff.c for more details. */ #define INCLUDES #define LIBRARIES /* * LIBSUFF * Is the suffix used to denote libraries and is used by the Suff module * to find the search path on which to seek any -l targets. - * + */ +#define LIBSUFF ".a" + +/* * RECHECK * If defined, Make_Update will check a target for its current * modification time after it has been re-made, setting it to the * starting time of the make only if the target still doesn't exist. * Unfortunately, under NFS the modification time often doesn't * get updated in time, so a target will appear to not have been * re-made, causing later targets to appear up-to-date. On systems - * that don't have this problem, you should defined this. Under + * that don't have this problem, you should define this. Under * NFS you probably should not, unless you aren't exporting jobs. */ -#define LIBSUFF ".a" #define RECHECK /* * POSIX * Adhere to the POSIX 1003.2 draft for the make(1) program. * - Use MAKEFLAGS instead of MAKE to pick arguments from the * environment. * - Allow empty command lines if starting with tab. */ #define POSIX /* * SYSVINCLUDE * Recognize system V like include directives [include "filename"] * SYSVVARSUB * Recognize system V like ${VAR:x=y} variable substitutions */ #define SYSVINCLUDE #define SYSVVARSUB /* * GMAKEEXPORT * Recognize gmake like variable export directives [export =] */ #define GMAKEEXPORT /* * SUNSHCMD * Recognize SunOS and Solaris: * VAR :sh= CMD # Assign VAR to the command substitution of CMD * ${VAR:sh} # Return the command substitution of the value * # of ${VAR} */ #define SUNSHCMD /* * USE_IOVEC * We have writev(2) */ #ifdef HAVE_SYS_UIO_H # define USE_IOVEC #endif #if defined(MAKE_NATIVE) && !defined(__ELF__) # ifndef RANLIBMAG # define RANLIBMAG "__.SYMDEF" # endif #endif Index: head/contrib/bmake/make.1 =================================================================== --- head/contrib/bmake/make.1 (revision 365365) +++ head/contrib/bmake/make.1 (revision 365366) @@ -1,2452 +1,2462 @@ -.\" $NetBSD: make.1,v 1.282 2020/06/06 20:28:42 wiz Exp $ +.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" 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. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd June 5, 2020 +.Dd August 28, 2020 .Dt MAKE 1 .Os .Sh NAME .Nm make .Nd maintain program dependencies .Sh SYNOPSIS .Nm .Op Fl BeikNnqrstWwX .Op Fl C Ar directory .Op Fl D Ar variable .Op Fl d Ar flags .Op Fl f Ar makefile .Op Fl I Ar directory .Op Fl J Ar private .Op Fl j Ar max_jobs .Op Fl m Ar directory .Op Fl T Ar file .Op Fl V Ar variable .Op Fl v Ar variable .Op Ar variable=value .Op Ar target ... .Sh DESCRIPTION .Nm is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. If no .Fl f Ar makefile makefile option is given, .Nm will try to open .Ql Pa makefile then .Ql Pa Makefile in order to find the specifications. If the file .Ql Pa .depend exists, it is read (see .Xr mkdep 1 ) . .Pp This manual page is intended as a reference document only. For a more thorough description of .Nm and makefiles, please refer to .%T "PMake \- A Tutorial" . .Pp .Nm will prepend the contents of the .Va MAKEFLAGS environment variable to the command line arguments before parsing them. .Pp The options are as follows: .Bl -tag -width Ds .It Fl B Try to be backwards compatible by executing a single shell per command and by executing the commands to make the sources of a dependency line in sequence. .It Fl C Ar directory Change to .Ar directory before reading the makefiles or doing anything else. If multiple .Fl C options are specified, each is interpreted relative to the previous one: .Fl C Pa / Fl C Pa etc is equivalent to .Fl C Pa /etc . .It Fl D Ar variable Define .Ar variable to be 1, in the global context. .It Fl d Ar [-]flags Turn on debugging, and specify which portions of .Nm are to print debugging information. Unless the flags are preceded by .Ql \- they are added to the .Va MAKEFLAGS environment variable and will be processed by any child make processes. By default, debugging information is printed to standard error, but this can be changed using the .Ar F debugging flag. The debugging output is always unbuffered; in addition, if debugging is enabled but debugging output is not directed to standard output, then the standard output is line buffered. .Ar Flags is one or more of the following: .Bl -tag -width Ds .It Ar A Print all possible debugging information; equivalent to specifying all of the debugging flags. .It Ar a Print debugging information about archive searching and caching. .It Ar C Print debugging information about current working directory. .It Ar c Print debugging information about conditional evaluation. .It Ar d Print debugging information about directory searching and caching. .It Ar e Print debugging information about failed commands and targets. .It Ar F Ns Oo Sy \&+ Oc Ns Ar filename Specify where debugging output is written. This must be the last flag, because it consumes the remainder of the argument. If the character immediately after the .Ql F flag is .Ql \&+ , then the file will be opened in append mode; otherwise the file will be overwritten. If the file name is .Ql stdout or .Ql stderr then debugging output will be written to the standard output or standard error output file descriptors respectively (and the .Ql \&+ option has no effect). Otherwise, the output will be written to the named file. If the file name ends .Ql .%d then the .Ql %d is replaced by the pid. .It Ar f Print debugging information about loop evaluation. .It Ar "g1" Print the input graph before making anything. .It Ar "g2" Print the input graph after making everything, or before exiting on error. .It Ar "g3" Print the input graph before exiting on error. +.It Ar h +Print debugging information about hash table operations. .It Ar j Print debugging information about running multiple shells. +.It Ar L +Turn on lint checks. +This will throw errors for variable assignments that do not parse +correctly, at the time of assignment so the file and line number +are available. .It Ar l Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ or other "quiet" flags. Also known as "loud" behavior. .It Ar M Print debugging information about "meta" mode decisions about targets. .It Ar m Print debugging information about making targets, including modification dates. .It Ar n Don't delete the temporary command scripts created when running commands. These temporary scripts are created in the directory referred to by the .Ev TMPDIR environment variable, or in .Pa /tmp if .Ev TMPDIR is unset or set to the empty string. The temporary scripts are created by .Xr mkstemp 3 , and have names of the form .Pa makeXXXXXX . .Em NOTE : This can create many files in .Ev TMPDIR or .Pa /tmp , so use with care. .It Ar p Print debugging information about makefile parsing. .It Ar s Print debugging information about suffix-transformation rules. .It Ar t Print debugging information about target list maintenance. .It Ar V Force the .Fl V option to print raw values of variables, overriding the default behavior set via .Va .MAKE.EXPAND_VARIABLES . .It Ar v Print debugging information about variable assignment. .It Ar x Run shell commands with .Fl x so the actual commands are printed as they are executed. .El .It Fl e Specify that environment variables override macro assignments within makefiles. .It Fl f Ar makefile Specify a makefile to read instead of the default .Ql Pa makefile . If .Ar makefile is .Ql Fl , standard input is read. Multiple makefiles may be specified, and are read in the order specified. .It Fl I Ar directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory (or directories, see the .Fl m option) is automatically included as part of this list. .It Fl i Ignore non-zero exit of shell commands in the makefile. Equivalent to specifying .Ql Fl before each command line in the makefile. .It Fl J Ar private This option should .Em not be specified by the user. .Pp When the .Ar j option is in use in a recursive build, this option is passed by a make to child makes to allow all the make processes in the build to cooperate to avoid overloading the system. .It Fl j Ar max_jobs Specify the maximum number of jobs that .Nm may have running at any one time. The value is saved in .Va .MAKE.JOBS . Turns compatibility mode off, unless the .Ar B flag is also specified. When compatibility mode is off, all commands associated with a target are executed in a single shell invocation as opposed to the traditional one shell invocation per line. This can break traditional scripts which change directories on each command invocation and then expect to start with a fresh environment on the next line. It is more efficient to correct the scripts rather than turn backwards compatibility on. .It Fl k Continue processing after errors are encountered, but only on those targets 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 .Li \&< Ns Ar file Ns Li \&> Ns -style include statement. The .Fl m 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 .Li \*q Ns Ar file Ns Li \*q Ns -style include statements (see the .Fl I option). .Pp If a file or directory name in the .Fl m argument (or the .Ev MAKESYSPATH environment variable) starts with the string .Qq \&.../ then .Nm will search for the specified file or directory named in the remaining part of the argument string. The search starts with the current directory of the Makefile and then works upward towards the root of the file system. If the search is successful, then the resulting directory replaces the .Qq \&.../ specification in the .Fl m argument. If used, this feature allows .Nm to easily search in the current source tree for customized sys.mk files (e.g., by using .Qq \&.../mk/sys.mk as an argument). .It Fl n Display the commands that would have been executed, but do not actually execute them unless the target depends on the .MAKE special -source (see below). +source (see below) or the command is prefixed with +.Ql Ic + . .It Fl N Display the commands which would have been executed, but do not actually execute any of them; useful for debugging top-level makefiles without descending into subdirectories. .It Fl q Do not execute any commands, but exit 0 if the specified targets are up-to-date and 1, otherwise. .It Fl r Do not use the built-in rules specified in the system makefile. .It Fl s Do not echo any commands as they are executed. Equivalent to specifying .Ql Ic @ before each command line in the makefile. .It Fl T Ar tracefile When used with the .Fl j flag, append a trace record to .Ar tracefile for each job started and completed. .It Fl t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up-to-date. .It Fl V Ar variable Print the value of .Ar variable . Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or undefined variable. The value printed is extracted from the global context after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded variable references) are shown. If .Ar variable contains a .Ql \&$ then the value will be recursively expanded to its complete resultant text before printing. The expanded value will also be printed if .Va .MAKE.EXPAND_VARIABLES is set to true and the .Fl dV option has not been used to override it. Note that loop-local and target-local variables, as well as values taken temporarily by global variables during makefile processing, are not accessible via this option. The .Fl dv debug mode can be used to see these at the cost of generating substantial extraneous output. .It Fl v Ar variable Like .Fl V but the variable is always expanded to its complete value. .It Fl W Treat any warnings during makefile parsing as errors. .It Fl w Print entering and leaving directory messages, pre and post processing. .It Fl X Don't export variables passed on the command line to the environment individually. Variables passed on the command line are still exported via the .Va MAKEFLAGS environment variable. This option may be useful on systems which have a small limit on the size of command arguments. .It Ar variable=value Set the value of the variable .Ar variable to .Ar value . Normally, all values passed on the command line are also exported to sub-makes in the environment. The .Fl X flag disables this behavior. Variable assignments should follow options for POSIX compatibility but no ordering is enforced. .El .Pp There are seven different types of lines in a makefile: file dependency specifications, shell commands, variable assignments, include statements, conditional directives, for loops, and comments. .Pp In general, lines may be continued from one line to the next by ending them with a backslash .Pq Ql \e . The trailing newline character and initial whitespace on the following line are compressed into a single space. .Sh FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets .Dq depend on the sources -and are usually created from them. -The exact relationship between the target and the source is determined -by the operator that separates them. -The three operators are as follows: +and are customarily created from them. +A target is considered out-of-date if it does not exist, or if its +modification time is less than that of any of its sources. +An out-of-date target will be re-created, but not until all sources +have been examined and themselves re-created as needed. +Three operators may be used: .Bl -tag -width flag .It Ic \&: -A target is considered out-of-date if its modification time is less than -those of any of its sources. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if +Many dependency lines may name this target but only one may have +attached shell commands. +All sources named in all dependency lines are considered together, +and if needed the attached shell commands are run to create or +re-create the target. +If .Nm -is interrupted. +is interrupted, the target is removed. .It Ic \&! -Targets are always re-created, but not until all sources have been -examined and re-created as necessary. -Sources for a target accumulate over dependency lines when this operator -is used. -The target is removed if -.Nm -is interrupted. +The same, but the target is always re-created whether or not it is out +of date. .It Ic \&:: -If no sources are specified, the target is always re-created. -Otherwise, a target is considered out-of-date if any of its sources has -been modified more recently than the target. -Sources for a target do not accumulate over dependency lines when this -operator is used. -The target will not be removed if +Any dependency line may have attached shell commands, but each one +is handled independently: its sources are considered and the attached +shell commands are run if the target is out of date with respect to +(only) those sources. +Thus, different groups of the attached shell commands may be run +depending on the circumstances. +Furthermore, unlike +.Ic \&:, +for dependency lines with no sources, the attached shell +commands are always run. +Also unlike +.Ic \&:, +the target will not be removed if .Nm is interrupted. .El +All dependency lines mentioning a particular target must use the same +operator. .Pp Targets and sources may contain the shell wildcard values .Ql \&? , .Ql * , .Ql [] , and .Ql {} . The values .Ql \&? , .Ql * , and .Ql [] may only be used as part of the final component of the target or source, and must be used to describe existing files. The value .Ql {} need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. .Sh SHELL COMMANDS Each target may have associated with it one or more lines of shell commands, normally used to create the target. Each of the lines in this script .Em must be preceded by a tab. (For historical reasons, spaces are not accepted.) While targets can appear in many dependency lines if desired, by default only one of these rules may be followed by a creation script. If the .Ql Ic \&:: operator is used, however, all rules may include scripts and the scripts are executed in the order found. .Pp Each line is treated as a separate shell command, unless the end of line is escaped with a backslash .Pq Ql \e in which case that line and the next are combined. .\" The escaped newline is retained and passed to the shell, which .\" normally ignores it. .\" However, the tab at the beginning of the following line is removed. If the first characters of the command are any combination of .Ql Ic @ , .Ql Ic + , or .Ql Ic \- , the command is treated specially. A .Ql Ic @ causes the command not to be echoed before it is executed. A .Ql Ic + causes the command to be executed even when .Fl n is given. This is similar to the effect of the .MAKE special source, except that the effect can be limited to a single line of a script. A .Ql Ic \- in compatibility mode causes any non-zero exit status of the command line to be ignored. .Pp When .Nm is run in jobs mode with .Fl j Ar max_jobs , the entire script for the target is fed to a single instance of the shell. In compatibility (non-jobs) mode, each command is run in a separate process. If the command contains any shell meta characters .Pq Ql #=|^(){};&<>*?[]:$`\e\en it will be passed to the shell; otherwise .Nm will attempt direct execution. If a line starts with .Ql Ic \- and the shell has ErrCtl enabled then failure of the command line will be ignored as in compatibility mode. Otherwise .Ql Ic \- affects the entire job; the script will stop at the first command line that fails, but the target will not be deemed to have failed. .Pp Makefiles should be written so that the mode of .Nm operation does not change their behavior. For example, any command which needs to use .Dq cd or .Dq chdir without potentially changing the directory for subsequent commands should be put in parentheses so it executes in a subshell. To force the use of one shell, escape the line breaks so as to make the whole script one command. For example: .Bd -literal -offset indent avoid-chdir-side-effects: @echo Building $@ in `pwd` @(cd ${.CURDIR} && ${MAKE} $@) @echo Back in `pwd` ensure-one-shell-regardless-of-mode: @echo Building $@ in `pwd`; \e (cd ${.CURDIR} && ${MAKE} $@); \e echo Back in `pwd` .Ed .Pp Since .Nm will .Xr chdir 2 to .Ql Va .OBJDIR before executing any targets, each child process starts with that as its current working directory. .Sh VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradition, consist of all upper-case letters. .Ss Variable assignment modifiers The five operators that can be used to assign values to variables are as follows: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overridden. .It Ic \&+= Append the value to the current value of the variable. .It Ic \&?= Assign the value to the variable if it is not already defined. .It Ic \&:= Assign with expansion, i.e. expand the value before assigning it to the variable. Normally, expansion is not done until the variable is referenced. .Em NOTE : References to undefined variables are .Em not expanded. This can cause problems when variable modifiers are used. .It Ic \&!= Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. .El .Pp Any white-space before the assigned .Ar value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. .Pp Variables are expanded by surrounding the variable name with either curly braces .Pq Ql {} or parentheses .Pq Ql () and preceding it with a dollar sign .Pq Ql \&$ . If the variable name contains only a single letter, the surrounding braces or parentheses are not required. This shorter form is not recommended. .Pp If the variable name contains a dollar, then the name itself is expanded first. This allows almost arbitrary variable names, however names containing dollar, -braces, parenthesis, or whitespace are really best avoided! +braces, parentheses, or whitespace are really best avoided! .Pp If the result of expanding a variable contains a dollar sign .Pq Ql \&$ the string is expanded again. .Pp Variable substitution occurs at three distinct times, depending on where the variable is being used. .Bl -enum .It Variables in dependency lines are expanded as the line is read. .It Variables in shell commands are expanded when the shell command is executed. .It .Dq .for loop index variables are expanded on each loop iteration. Note that other variables are not expanded inside loops so the following example code: .Bd -literal -offset indent .Dv .for i in 1 2 3 a+= ${i} j= ${i} b+= ${j} .Dv .endfor all: @echo ${a} @echo ${b} .Ed will print: .Bd -literal -offset indent 1 2 3 3 3 3 .Ed Because while ${a} contains .Dq 1 2 3 after the loop is executed, ${b} contains .Dq ${j} ${j} ${j} which expands to .Dq 3 3 3 since after the loop completes ${j} contains .Dq 3 . .El .Ss Variable classes The four different classes of variables (in order of increasing precedence) are: .Bl -tag -width Ds .It Environment variables Variables defined as part of .Nm Ns 's environment. .It Global variables Variables defined in the makefile or in included makefiles. .It Command line variables Variables defined as part of the command line. .It Local variables Variables that are defined specific to a certain target. .El .Pp Local variables are all built in and their values vary magically from target to target. It is not currently possible to define new local variables. The seven local variables are as follows: .Bl -tag -width ".ARCHIVE" -offset indent .It Va .ALLSRC The list of all sources for this target; also known as .Ql Va \&> . .It Va .ARCHIVE The name of the archive file; also known as .Ql Va \&! . .It Va .IMPSRC In suffix-transformation rules, the name/path of the source from which the target is to be transformed (the .Dq implied source); also known as .Ql Va \&< . It is not defined in explicit rules. .It Va .MEMBER The name of the archive member; also known as .Ql Va % . .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Ql Va \&? . .It Va .PREFIX The file prefix of the target, containing only the file portion, no suffix or preceding directory components; also known as .Ql Va * . The suffix must be one of the known suffixes declared with .Ic .SUFFIXES or it will not be recognized. .It Va .TARGET The name of the target; also known as .Ql Va @ . For compatibility with other makes this is an alias for .Ic .ARCHIVE in archive member rules. .El .Pp The shorter forms .Ql ( Va > , .Ql Va \&! , .Ql Va < , .Ql Va % , .Ql Va \&? , .Ql Va * , and .Ql Va @ ) are permitted for backward compatibility with historical makefiles and legacy POSIX make and are not recommended. .Pp Variants of these variables with the punctuation followed immediately by .Ql D or .Ql F , e.g. .Ql Va $(@D) , are legacy forms equivalent to using the .Ql :H and .Ql :T modifiers. These forms are accepted for compatibility with .At V makefiles and POSIX but are not recommended. .Pp Four of the local variables may be used in sources on dependency lines because they expand to the proper value for each target on the line. These variables are .Ql Va .TARGET , .Ql Va .PREFIX , .Ql Va .ARCHIVE , and .Ql Va .MEMBER . .Ss Additional built-in variables In addition, .Nm sets or knows about the following variables: .Bl -tag -width .MAKEOVERRIDES .It Va \&$ A single dollar sign .Ql \&$ , i.e. .Ql \&$$ expands to a single dollar sign. .It Va .ALLTARGETS The list of all targets encountered in the Makefile. If evaluated during Makefile parsing, lists only those targets encountered thus far. .It Va .CURDIR A path to the directory where .Nm was executed. Refer to the description of .Ql Ev PWD for more details. .It Va .INCLUDEDFROMDIR The directory of the file this Makefile was included from. .It Va .INCLUDEDFROMFILE The filename of the file this Makefile was included from. .It Ev MAKE The name that .Nm was executed with .Pq Va argv[0] . For compatibility .Nm also sets .Va .MAKE with the same value. The preferred variable to use is the environment variable .Ev MAKE because it is more compatible with other versions of .Nm and cannot be confused with the special target with the same name. .It Va .MAKE.ALWAYS_PASS_JOB_QUEUE Tells .Nm whether to pass the descriptors of the job token queue even if the target is not tagged with .Ic .MAKE The default is .Ql Pa yes for backwards compatability with .Fx 9.0 and earlier. .It Va .MAKE.DEPENDFILE Names the makefile (default .Ql Pa .depend ) from which generated dependencies are read. .It Va .MAKE.EXPAND_VARIABLES A boolean that controls the default behavior of the .Fl V option. If true, variable values printed with .Fl V are fully expanded; if false, the raw variable contents (which may include additional unexpanded variable references) are shown. .It Va .MAKE.EXPORTED The list of variables exported by .Nm . .It Va .MAKE.JOBS The argument to the .Fl j option. .It Va .MAKE.JOB.PREFIX If .Nm is run with .Ar j then output for each target is prefixed with a token .Ql --- target --- the first part of which can be controlled via .Va .MAKE.JOB.PREFIX . If .Va .MAKE.JOB.PREFIX is empty, no token is printed. .br For example: .Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] would produce tokens like .Ql ---make[1234] target --- making it easier to track the degree of parallelism being achieved. .It Ev MAKEFLAGS The environment variable .Ql Ev MAKEFLAGS may contain anything that may be specified on .Nm Ns 's command line. Anything specified on .Nm Ns 's command line is appended to the .Ql Ev MAKEFLAGS variable which is then entered into the environment for all programs which .Nm executes. .It Va .MAKE.LEVEL The recursion depth of .Nm . The initial instance of .Nm will be 0, and an incremented value is put into the environment to be seen by the next generation. This allows tests like: .Li .if ${.MAKE.LEVEL} == 0 to protect things which should only be evaluated in the initial instance of .Nm . .It Va .MAKE.MAKEFILE_PREFERENCE The ordered list of makefile names (default .Ql Pa makefile , .Ql Pa Makefile ) that .Nm will look for. .It Va .MAKE.MAKEFILES The list of makefiles read by .Nm , which is useful for tracking dependencies. Each makefile is recorded only once, regardless of the number of times read. .It Va .MAKE.MODE Processed after reading all makefiles. Can affect the mode that .Nm runs in. It can contain a number of keywords: .Bl -hang -width missing-filemon=bf. .It Pa compat Like .Fl B , puts .Nm into "compat" mode. .It Pa meta Puts .Nm into "meta" mode, where meta files are created for each target to capture the command run, the output generated and if .Xr filemon 4 is available, the system calls which are of interest to .Nm . The captured output can be very useful when diagnosing errors. .It Pa curdirOk= Ar bf Normally .Nm will not create .meta files in .Ql Va .CURDIR . This can be overridden by setting .Va bf to a value which represents True. .It Pa missing-meta= Ar bf If .Va bf is True, then a missing .meta file makes the target out-of-date. .It Pa missing-filemon= Ar bf If .Va bf is True, then missing filemon data makes the target out-of-date. .It Pa nofilemon Do not use .Xr filemon 4 . .It Pa env For debugging, it can be useful to include the environment in the .meta file. .It Pa verbose If in "meta" mode, print a clue about the target being built. This is useful if the build is otherwise running silently. The message printed the value of: .Va .MAKE.META.PREFIX . .It Pa ignore-cmd Some makefiles have commands which are simply not stable. This keyword causes them to be ignored for determining whether a target is out of date in "meta" mode. See also .Ic .NOMETA_CMP . .It Pa silent= Ar bf If .Va bf is True, when a .meta file is created, mark the target .Ic .SILENT . .El .It Va .MAKE.META.BAILIWICK In "meta" mode, provides a list of prefixes which match the directories controlled by .Nm . If a file that was generated outside of .Va .OBJDIR but within said bailiwick is missing, the current target is considered out-of-date. .It Va .MAKE.META.CREATED In "meta" mode, this variable contains a list of all the meta files updated. If not empty, it can be used to trigger processing of .Va .MAKE.META.FILES . .It Va .MAKE.META.FILES In "meta" mode, this variable contains a list of all the meta files used (updated or not). This list can be used to process the meta files to extract dependency information. .It Va .MAKE.META.IGNORE_PATHS Provides a list of path prefixes that should be ignored; because the contents are expected to change over time. The default list includes: .Ql Pa /dev /etc /proc /tmp /var/run /var/tmp .It Va .MAKE.META.IGNORE_PATTERNS Provides a list of patterns to match against pathnames. Ignore any that match. .It Va .MAKE.META.IGNORE_FILTER Provides a list of variable modifiers to apply to each pathname. Ignore if the expansion is an empty string. .It Va .MAKE.META.PREFIX Defines the message printed for each meta file updated in "meta verbose" mode. The default value is: .Dl Building ${.TARGET:H:tA}/${.TARGET:T} .It Va .MAKEOVERRIDES This variable is used to record the names of variables assigned to on the command line, so that they may be exported as part of .Ql Ev MAKEFLAGS . This behavior can be disabled by assigning an empty value to .Ql Va .MAKEOVERRIDES within a makefile. Extra variables can be exported from a makefile by appending their names to .Ql Va .MAKEOVERRIDES . .Ql Ev MAKEFLAGS is re-exported whenever .Ql Va .MAKEOVERRIDES is modified. .It Va .MAKE.PATH_FILEMON If .Nm was built with .Xr filemon 4 support, this is set to the path of the device node. This allows makefiles to test for this support. .It Va .MAKE.PID The process-id of .Nm . .It Va .MAKE.PPID The parent process-id of .Nm . .It Va .MAKE.SAVE_DOLLARS value should be a boolean that controls whether .Ql $$ are preserved when doing .Ql := assignments. The default is false, for backwards compatibility. Set to true for compatability with other makes. If set to false, .Ql $$ becomes .Ql $ per normal evaluation rules. .It Va MAKE_PRINT_VAR_ON_ERROR When .Nm stops due to an error, it sets .Ql Va .ERROR_TARGET to the name of the target that failed, .Ql Va .ERROR_CMD to the commands of the failed target, and in "meta" mode, it also sets .Ql Va .ERROR_CWD to the .Xr getcwd 3 , and .Ql Va .ERROR_META_FILE to the path of the meta file (if any) describing the failed target. It then prints its name and the value of .Ql Va .CURDIR as well as the value of any variables named in .Ql Va MAKE_PRINT_VAR_ON_ERROR . .It Va .newline This variable is simply assigned a newline character as its value. This allows expansions using the .Cm \&:@ modifier to put a newline between iterations of the loop rather than a space. For example, the printing of .Ql Va MAKE_PRINT_VAR_ON_ERROR could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. .It Va .OBJDIR A path to the directory where the targets are built. Its value is determined by trying to .Xr chdir 2 to the following directories in order and using the first match: .Bl -enum .It .Ev ${MAKEOBJDIRPREFIX}${.CURDIR} .Pp (Only if .Ql Ev MAKEOBJDIRPREFIX is set in the environment or on the command line.) .It .Ev ${MAKEOBJDIR} .Pp (Only if .Ql Ev MAKEOBJDIR is set in the environment or on the command line.) .It .Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} .It .Ev ${.CURDIR} Ns Pa /obj .It .Pa /usr/obj/ Ns Ev ${.CURDIR} .It .Ev ${.CURDIR} .El .Pp Variable expansion is performed on the value before it's used, so expressions such as .Dl ${.CURDIR:S,^/usr/src,/var/obj,} may be used. This is especially useful with .Ql Ev MAKEOBJDIR . .Pp .Ql Va .OBJDIR may be modified in the makefile via the special target .Ql Ic .OBJDIR . In all cases, .Nm will .Xr chdir 2 to the specified directory if it exists, and set .Ql Va .OBJDIR and .Ql Ev PWD to that directory before executing any targets. . .It Va .PARSEDIR A path to the directory of the current .Ql Pa Makefile being parsed. .It Va .PARSEFILE The basename of the current .Ql Pa Makefile being parsed. This variable and .Ql Va .PARSEDIR are both set only while the .Ql Pa Makefiles are being parsed. If you want to retain their current values, assign them to a variable using assignment with expansion: .Pq Ql Cm \&:= . .It Va .PATH A variable that represents the list of directories that .Nm will search for files. The search list should be updated using the target .Ql Va .PATH rather than the variable. .It Ev PWD Alternate path to the current directory. .Nm normally sets .Ql Va .CURDIR to the canonical path given by .Xr getcwd 3 . However, if the environment variable .Ql Ev PWD is set and gives a path to the current directory, then .Nm sets .Ql Va .CURDIR to the value of .Ql Ev PWD instead. This behavior is disabled if .Ql Ev MAKEOBJDIRPREFIX is set or .Ql Ev MAKEOBJDIR contains a variable transform. .Ql Ev PWD is set to the value of .Ql Va .OBJDIR for all programs which .Nm executes. +.It Ev .SHELL +The pathname of the shell used to run target scripts. +It is read-only. .It Ev .TARGETS The list of targets explicitly specified on the command line, if any. .It Ev VPATH Colon-separated .Pq Dq \&: lists of directories that .Nm will search for files. The variable is supported for compatibility with old make programs only, use .Ql Va .PATH instead. .El .Ss Variable modifiers Variable expansion may be modified to select or modify each word of the variable (where a .Dq word is white-space delimited sequence of characters). The general format of a variable expansion is as follows: .Pp .Dl ${variable[:modifier[:...]]} .Pp Each modifier begins with a colon, which may be escaped with a backslash .Pq Ql \e . .Pp A set of modifiers can be specified via a variable, as follows: .Pp .Dl modifier_variable=modifier[:...] .Dl ${variable:${modifier_variable}[:...]} .Pp In this case the first modifier in the modifier_variable does not start with a colon, since that must appear in the referencing variable. If any of the modifiers in the modifier_variable contain a dollar sign .Pq Ql $ , these must be doubled to avoid early expansion. .Pp The supported modifiers are: .Bl -tag -width EEE .It Cm \&:E Replaces each word in the variable with its suffix. .It Cm \&:H Replaces each word in the variable with everything but the last component. .It Cm \&:M Ns Ar pattern -Select only those words that match +Selects only those words that match .Ar pattern . The standard shell wildcard characters .Pf ( Ql * , .Ql \&? , and .Ql Oo Oc ) may be used. The wildcard characters may be escaped with a backslash .Pq Ql \e . As a consequence of the way values are split into words, matched, and then joined, a construct like .Dl ${VAR:M*} will normalize the inter-word spacing, removing all leading and trailing space, and converting multiple consecutive spaces to single spaces. . .It Cm \&:N Ns Ar pattern This is identical to .Ql Cm \&:M , but selects all words which do not match .Ar pattern . .It Cm \&:O -Order every word in variable alphabetically. +Orders every word in variable alphabetically. .It Cm \&:Or -Order every word in variable in reverse alphabetical order. +Orders every word in variable in reverse alphabetical order. .It Cm \&:Ox -Randomize words in variable. +Shuffles the words in variable. The results will be different each time you are referring to the modified variable; use the assignment with expansion .Pq Ql Cm \&:= to prevent such behavior. For example, .Bd -literal -offset indent LIST= uno due tre quattro RANDOM_LIST= ${LIST:Ox} STATIC_RANDOM_LIST:= ${LIST:Ox} all: @echo "${RANDOM_LIST}" @echo "${RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" @echo "${STATIC_RANDOM_LIST}" .Ed may produce output similar to: .Bd -literal -offset indent quattro due tre uno tre due quattro uno due uno quattro tre due uno quattro tre .Ed .It Cm \&:Q Quotes every shell meta-character in the variable, so that it can be passed safely to the shell. .It Cm \&:q Quotes every shell meta-character in the variable, and also doubles .Sq $ characters so that it can be passed safely through recursive invocations of .Nm . This is equivalent to: .Sq \&:S/\e\&$/&&/g:Q . .It Cm \&:R Replaces each word in the variable with everything but its suffix. .It Cm \&:range[=count] The value is an integer sequence representing the words of the original value, or the supplied .Va count . .It Cm \&:gmtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr gmtime 3 . If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:hash -Compute a 32-bit hash of the value and encode it as hex digits. +Computes a 32-bit hash of the value and encode it as hex digits. .It Cm \&:localtime[=utc] The value is a format string for .Xr strftime 3 , using .Xr localtime 3 . If a .Va utc value is not provided or is 0, the current time is used. .It Cm \&:tA -Attempt to convert variable to an absolute path using +Attempts to convert variable to an absolute path using .Xr realpath 3 , if that fails, the value is unchanged. .It Cm \&:tl Converts variable to lower-case letters. .It Cm \&:ts Ns Ar c Words in the variable are normally separated by a space on expansion. This modifier sets the separator to the character .Ar c . If .Ar c is omitted, then no separator is used. -The common escapes (including octal numeric codes), work as expected. +The common escapes (including octal numeric codes) work as expected. .It Cm \&:tu Converts variable to upper-case letters. .It Cm \&:tW Causes the value to be treated as a single word (possibly containing embedded white space). See also .Ql Cm \&:[*] . .It Cm \&:tw Causes the value to be treated as a sequence of words delimited by white space. See also .Ql Cm \&:[@] . .Sm off .It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW .Sm on -Modify the first occurrence of +Modifies the first occurrence of .Ar old_string -in the variable's value, replacing it with +in each word of the variable's value, replacing it with .Ar new_string . If a .Ql g -is appended to the last slash of the pattern, all occurrences +is appended to the last delimiter of the pattern, all occurrences in each word are replaced. If a .Ql 1 -is appended to the last slash of the pattern, only the first word +is appended to the last delimiter of the pattern, only the first occurrence is affected. If a .Ql W -is appended to the last slash of the pattern, +is appended to the last delimiter of the pattern, then the value is treated as a single word (possibly containing embedded white space). If .Ar old_string begins with a caret .Pq Ql ^ , .Ar old_string is anchored at the beginning of each word. If .Ar old_string ends with a dollar sign .Pq Ql \&$ , it is anchored at the end of each word. Inside .Ar new_string , an ampersand .Pq Ql & is replaced by .Ar old_string (without any .Ql ^ or .Ql \&$ ) . Any character may be used as a delimiter for the parts of the modifier string. The anchoring, ampersand and delimiter characters may be escaped with a backslash .Pq Ql \e . .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string and .Ar new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign .Pq Ql \&$ , not a preceding dollar sign as is usual. .Sm off .It Cm \&:C No \&/ Ar pattern No \&/ Ar replacement No \&/ Op Cm 1gW .Sm on The .Cm \&:C modifier is just like the .Cm \&:S modifier except that the old and new strings, instead of being simple strings, are an extended regular expression (see .Xr regex 3 ) string .Ar pattern and an .Xr ed 1 Ns \-style string .Ar replacement . Normally, the first occurrence of the pattern .Ar pattern in each word of the value is substituted with .Ar replacement . The .Ql 1 modifier causes the substitution to apply to at most one word; the .Ql g modifier causes the substitution to apply to as many instances of the search pattern .Ar pattern as occur in the word or words it is found in; the .Ql W modifier causes the value to be treated as a single word (possibly containing embedded white space). -Note that -.Ql 1 -and -.Ql g -are orthogonal; the former specifies whether multiple words are -potentially affected, the latter whether multiple substitutions can -potentially occur within each affected word. .Pp As for the .Cm \&:S modifier, the .Ar pattern and .Ar replacement are subjected to variable expansion before being parsed as regular expressions. .It Cm \&:T -Replaces each word in the variable with its last component. +Replaces each word in the variable with its last path component. .It Cm \&:u -Remove adjacent duplicate words (like +Removes adjacent duplicate words (like .Xr uniq 1 ) . .Sm off .It Cm \&:\&? Ar true_string Cm \&: Ar false_string .Sm on If the variable name (not its value), when parsed as a .if conditional expression, evaluates to true, return as its value the .Ar true_string , otherwise return the .Ar false_string . Since the variable name is used as the expression, \&:\&? must be the first modifier after the variable name itself - which will, of course, usually contain variable expansions. A common error is trying to use expressions like .Dl ${NUMBERS:M42:?match:no} which actually tests defined(NUMBERS), -to determine is any words match "42" you need to use something like: +to determine if any words match "42" you need to use something like: .Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . .It Ar :old_string=new_string This is the .At V style variable substitution. It must be the last modifier specified. If .Ar old_string or .Ar new_string do not contain the pattern matching character .Ar % then it is assumed that they are anchored at the end of each word, so only suffixes or entire words may be replaced. Otherwise .Ar % 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 +all subsequent instances are treated as regular characters. .Pp Variable expansion occurs in the normal fashion inside both .Ar old_string and .Ar new_string with the single exception that a backslash is used to prevent the expansion of a dollar sign .Pq Ql \&$ , not a preceding dollar sign as is usual. .Sm off .It Cm \&:@ Ar temp Cm @ Ar string Cm @ .Sm on This is the loop expansion mechanism from the OSF Development Environment (ODE) make. Unlike .Cm \&.for -loops expansion occurs at the time of -reference. -Assign +loops, expansion occurs at the time of reference. +Assigns .Ar temp -to each word in the variable and evaluate +to each word in the variable and evaluates .Ar string . The ODE convention is that .Ar temp should start and end with a period. For example. .Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} .Pp However a single character variable is often more readable: .Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} .It Cm \&:_[=var] -Save the current variable value in +Saves the current variable value in .Ql $_ or the named .Va var for later reference. Example usage: .Bd -literal -offset indent M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \&\\ \\* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .Dv .if ${VERSION:${M_cmpv}} < ${3.1.12:L:${M_cmpv}} .Ed Here .Ql $_ is used to save the result of the .Ql :S modifier which is later referenced using the index values from .Ql :range . .It Cm \&:U Ns Ar newval -If the variable is undefined +If the variable is undefined, .Ar newval is the value. If the variable is defined, the existing value is returned. This is another ODE make feature. It is handy for setting per-target CFLAGS for instance: .Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} If a value is only required if the variable is undefined, use: .Dl ${VAR:D:Unewval} .It Cm \&:D Ns Ar newval -If the variable is defined +If the variable is defined, .Ar newval is the value. .It Cm \&:L The name of the variable is the value. .It Cm \&:P The path of the node which has the same name as the variable is the value. If no such node exists or its path is null, then the name of the variable is used. In order for this modifier to work, the name (node) must at least have appeared on the rhs of a dependency. .Sm off .It Cm \&:\&! Ar cmd Cm \&! .Sm on The output of running .Ar cmd is the value. .It Cm \&:sh If the variable is non-empty it is run as a command and the output becomes the new value. .It Cm \&::= Ns Ar str The variable is assigned the value .Ar str after substitution. This modifier and its variations are useful in obscure situations such as wanting to set a variable when shell commands are being parsed. These assignment modifiers always expand to nothing, so if appearing in a rule line by themselves should be preceded with something to keep .Nm happy. .Pp The .Ql Cm \&:: helps avoid false matches with the .At V style .Cm \&:= modifier and since substitution always occurs the .Cm \&::= form is vaguely appropriate. .It Cm \&::?= Ns Ar str As for .Cm \&::= but only if the variable does not already have a value. .It Cm \&::+= Ns Ar str Append .Ar str to the variable. .It Cm \&::!= Ns Ar cmd Assign the output of .Ar cmd to the variable. .It Cm \&:\&[ Ns Ar range Ns Cm \&] Selects one or more words from the value, or performs other operations related to the way in which the value is divided into words. .Pp Ordinarily, a value is treated as a sequence of words delimited by white space. Some modifiers suppress this behavior, causing a value to be treated as a single word (possibly containing embedded white space). An empty value, or a value that consists entirely of white-space, is treated as a single word. For the purposes of the .Ql Cm \&:[] modifier, the words are indexed both forwards using positive integers (where index 1 represents the first word), and backwards using negative integers (where index \-1 represents the last word). .Pp The .Ar range is subjected to variable expansion, and the expanded result is then interpreted as follows: .Bl -tag -width index .\" :[n] .It Ar index Selects a single word from the value. .\" :[start..end] .It Ar start Ns Cm \&.. Ns Ar end Selects all words from .Ar start to .Ar end , inclusive. For example, .Ql Cm \&:[2..-1] selects all words from the second word to the last word. If .Ar start is greater than .Ar end , then the words are output in reverse order. For example, .Ql Cm \&:[-1..1] selects all the words from last to first. If the list is already ordered, then this effectively reverses the list, but it is more efficient to use .Ql Cm \&:Or instead of .Ql Cm \&:O:[-1..1] . .\" :[*] .It Cm \&* Causes subsequent modifiers to treat the value as a single word (possibly containing embedded white space). Analogous to the effect of \&"$*\&" in Bourne shell. .\" :[0] .It 0 Means the same as .Ql Cm \&:[*] . .\" :[*] .It Cm \&@ Causes subsequent modifiers to treat the value as a sequence of words delimited by white space. Analogous to the effect of \&"$@\&" in Bourne shell. .\" :[#] .It Cm \&# Returns the number of words in the value. .El \" :[range] .El .Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS -Makefile inclusion, conditional structures and for loops reminiscent +Makefile inclusion, conditional structures and for loops reminiscent of the C programming language are provided in .Nm . All such structures are identified by a line beginning with a single dot .Pq Ql \&. character. Files are included with either .Cm \&.include \&< Ns Ar file Ns Cm \&> or .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 the system makefile directory. If double quotes are used, the including makefile's directory and any directories specified using the .Fl I option are searched before the system makefile directory. For compatibility with other versions of .Nm .Ql include file ... is also accepted. .Pp If the include statement is written as .Cm .-include or as .Cm .sinclude then errors locating and/or opening include files are ignored. .Pp If the include statement is written as .Cm .dinclude not only are errors locating and/or opening include files ignored, but stale dependencies within the included file will be ignored just like .Va .MAKE.DEPENDFILE . .Pp Conditional expressions are also preceded by a single dot as the first character of a line. The possible conditionals are as follows: .Bl -tag -width Ds .It Ic .error Ar message The message is printed along with the name of the makefile and line number, then .Nm -will exit. +will exit immediately. .It Ic .export Ar variable ... Export the specified global variable. If no variable list is provided, all globals are exported except for internal variables (those that start with .Ql \&. ) . This is not affected by the .Fl X flag, so should be used with caution. For compatibility with other .Nm programs .Ql export variable=value is also accepted. .Pp Appending a variable name to .Va .MAKE.EXPORTED is equivalent to exporting a variable. .It Ic .export-env Ar variable ... The same as .Ql .export , except that the variable is not appended to .Va .MAKE.EXPORTED . This allows exporting a value to the environment which is different from that used by .Nm internally. .It Ic .export-literal Ar variable ... The same as .Ql .export-env , except that variables in the value are not expanded. .It Ic .info Ar message The message is printed along with the name of the makefile and line number. .It Ic .undef Ar variable Un-define the specified global variable. Only global variables may be un-defined. .It Ic .unexport Ar variable ... The opposite of .Ql .export . The specified global .Va variable will be removed from .Va .MAKE.EXPORTED . If no variable list is provided, all globals are unexported, and .Va .MAKE.EXPORTED deleted. .It Ic .unexport-env Unexport all globals previously exported and clear the environment inherited from the parent. This operation will cause a memory leak of the original environment, so should be used sparingly. Testing for .Va .MAKE.LEVEL being 0, would make sense. Also note that any variables which originated in the parent environment should be explicitly preserved if desired. For example: .Bd -literal -offset indent .Li .if ${.MAKE.LEVEL} == 0 PATH := ${PATH} .Li .unexport-env .Li .export PATH .Li .endif .Pp .Ed Would result in an environment containing only .Ql Ev PATH , which is the minimal useful environment. Actually .Ql Ev .MAKE.LEVEL will also be pushed into the new environment. .It Ic .warning Ar message The message prefixed by .Ql Pa warning: is printed along with the name of the makefile and line number. .It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... Test the value of an expression. .It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... Test the value of a variable. .It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... Test the value of a variable. .It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... Test the target being built. .It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... Test the target being built. .It Ic .else Reverse the sense of the last conditional. .It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... A combination of .Ql Ic .else followed by .Ql Ic .if . .It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifdef . .It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... A combination of .Ql Ic .else followed by .Ql Ic .ifndef . .It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifmake . .It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... A combination of .Ql Ic .else followed by .Ql Ic .ifnmake . .It Ic .endif End the body of the conditional. .El .Pp The .Ar operator may be any one of the following: .Bl -tag -width "Cm XX" .It Cm \&|\&| Logical OR. .It Cm \&&& Logical .Tn AND ; of higher precedence than .Dq \&|\&| . .El .Pp As in C, .Nm will only evaluate a conditional as far as is necessary to determine its value. Parentheses may be used to change the order of evaluation. The boolean operator .Ql Ic \&! may be used to logically negate an entire conditional. It is of higher precedence than .Ql Ic \&&& . .Pp The value of .Ar expression may be any of the following: .Bl -tag -width defined .It Ic defined Takes a variable name as an argument and evaluates to true if the variable has been defined. .It Ic make Takes a target name as an argument and evaluates to true if the target was specified as part of .Nm Ns 's command line or was declared the default target (either implicitly or explicitly, see .Va .MAIN ) before the line containing the conditional. .It Ic empty Takes a variable, with possible modifiers, and evaluates to true if the expansion of the variable would result in an empty string. .It Ic exists Takes a file name as an argument and evaluates to true if the file exists. The file is searched for on the system search path (see .Va .PATH ) . .It Ic target Takes a target name as an argument and evaluates to true if the target has been defined. .It Ic commands Takes a target name as an argument and evaluates to true if the target has been defined and has commands associated with it. .El .Pp .Ar Expression may also be an arithmetic or string comparison. Variable expansion is performed on both sides of the comparison, after which the integral values are compared. A value is interpreted as hexadecimal if it is preceded by 0x, otherwise it is decimal; octal numbers are not supported. The standard C relational operators are all supported. If after variable expansion, either the left or right hand side of a .Ql Ic == or .Ql Ic "!=" operator is not an integral value, then string comparison is performed between the expanded variables. If no relational operator is given, it is assumed that the expanded -variable is being compared against 0 or an empty string in the case +variable is being compared against 0, or an empty string in the case of a string comparison. .Pp When .Nm is evaluating one of these conditional expressions, and it encounters a (white-space separated) word it doesn't recognize, either the .Dq make or .Dq defined expression is applied to it, depending on the form of the conditional. If the form is .Ql Ic .ifdef , .Ql Ic .ifndef , or .Ql Ic .if the .Dq defined expression is applied. Similarly, if the form is .Ql Ic .ifmake or .Ql Ic .ifnmake , the .Dq make expression is applied. .Pp If the conditional evaluates to true the parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a .Ql Ic .else or .Ql Ic .endif is found. .Pp For loops are typically used to apply a set of rules to a list of files. The syntax of a for loop is: .Pp .Bl -tag -compact -width Ds .It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression -.It Aq make-rules +.It Aq make-lines .It Ic \&.endfor .El .Pp After the for .Ic expression is evaluated, it is split into words. On each iteration of the loop, one word is taken and assigned to each .Ic variable , in order, and these .Ic variables are substituted into the -.Ic make-rules +.Ic make-lines inside the body of the for loop. The number of words must come out even; that is, if there are three iteration variables, the number of words provided must be a multiple of three. .Sh COMMENTS Comments begin with a hash .Pq Ql \&# character, anywhere but in a shell command line, and continue to the end of an unescaped new line. .Sh SPECIAL SOURCES (ATTRIBUTES) .Bl -tag -width .IGNOREx .It Ic .EXEC Target is never out of date, but always execute commands anyway. .It Ic .IGNORE Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . .\" .It Ic .INVISIBLE .\" XXX .\" .It Ic .JOIN .\" XXX .It Ic .MADE Mark all sources of this target as being up-to-date. .It Ic .MAKE Execute the commands associated with this target even if the .Fl n or .Fl t options were specified. Normally used to mark recursive .Nm Ns s . .It Ic .META Create a meta file for the target, even if it is flagged as .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL . Usage in conjunction with .Ic .MAKE is the most likely case. In "meta" mode, the target is out-of-date if the meta file is missing. .It Ic .NOMETA Do not create a meta file for the target. Meta files are also not created for .Ic .PHONY , .Ic .MAKE , or .Ic .SPECIAL targets. .It Ic .NOMETA_CMP Ignore differences in commands when deciding if target is out of date. This is useful if the command contains a value which always changes. If the number of commands change, though, the target will still be out of date. The same effect applies to any command line that uses the variable .Va .OODATE , which can be used for that purpose even when not otherwise needed or desired: .Bd -literal -offset indent skip-compare-for-some: @echo this will be compared @echo this will not ${.OODATE:M.NOMETA_CMP} @echo this will also be compared .Ed The .Cm \&:M pattern suppresses any expansion of the unwanted variable. .It Ic .NOPATH Do not search for the target in the directories specified by .Ic .PATH . .It Ic .NOTMAIN Normally .Nm selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .It Ic .OPTIONAL If a target is marked with this attribute and .Nm can't figure out how to create it, it will ignore this fact and assume the file isn't needed or already exists. .It Ic .PHONY The target does not correspond to an actual file; it is always considered to be out of date, and will not be created with the .Fl t option. Suffix-transformation rules are not applied to .Ic .PHONY targets. .It Ic .PRECIOUS When .Nm is interrupted, it normally removes any partially made targets. This source prevents the target from being removed. .It Ic .RECURSIVE Synonym for .Ic .MAKE . .It Ic .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign .Pq Ql @ . .It Ic .USE Turn the target into .Nm Ns 's version of a macro. When the target is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .Ic .USE ) of the source. If the target already has commands, the .Ic .USE target's commands are appended to them. .It Ic .USEBEFORE Exactly like .Ic .USE , but prepend the .Ic .USEBEFORE target commands to the target. .It Ic .WAIT If .Ic .WAIT appears in a dependency line, the sources that precede it are made before the sources that succeed it in the line. Since the dependents of files are not made until the file itself could be made, this also stops the dependents being built unless they are needed for another branch of the dependency tree. So given: .Bd -literal x: a .WAIT b echo x a: echo a b: b1 echo b b1: echo b1 .Ed the output is always .Ql a , .Ql b1 , .Ql b , .Ql x . .br The ordering imposed by .Ic .WAIT is only relevant for parallel makes. .El .Sh SPECIAL TARGETS Special targets may not be included with other targets, i.e. they must be the only target specified. .Bl -tag -width .BEGINx .It Ic .BEGIN Any command lines attached to this target are executed before anything else is done. .It Ic .DEFAULT This is sort of a .Ic .USE rule for any target (that was used only as a source) that .Nm can't figure out any other way to create. Only the shell script is used. The .Ic .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's commands is set to the target's own name. .It Ic .DELETE_ON_ERROR If this target is present in the makefile, it globally causes make to delete targets whose commands fail. (By default, only targets whose commands are interrupted during execution are deleted. This is the historical behavior.) This setting can be used to help prevent half-finished or malformed targets from being left around and corrupting future rebuilds. .It Ic .END Any command lines attached to this target are executed after everything else is done. .It Ic .ERROR Any command lines attached to this target are executed when another target fails. The .Ic .ERROR_TARGET variable is set to the target that failed. See also .Ic MAKE_PRINT_VAR_ON_ERROR . .It Ic .IGNORE Mark each of the sources with the .Ic .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the .Fl i option. .It Ic .INTERRUPT If .Nm is interrupted, the commands for this target will be executed. .It Ic .MAIN If no target is specified when .Nm is invoked, this target will be built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm when the makefile is used. The flags are as if typed to the shell, though the .Fl f option will have no effect. .\" XXX: NOT YET!!!! .\" .It Ic .NOTPARALLEL .\" The named targets are executed in non parallel mode. .\" If no targets are .\" specified, then all targets are executed in non parallel mode. .It Ic .NOPATH Apply the .Ic .NOPATH attribute to any specified sources. .It Ic .NOTPARALLEL Disable parallel mode. .It Ic .NO_PARALLEL Synonym for .Ic .NOTPARALLEL , for compatibility with other pmake variants. .It Ic .OBJDIR The source is a new value for .Ql Va .OBJDIR . If it exists, .Nm will .Xr chdir 2 to it and update the value of .Ql Va .OBJDIR . .It Ic .ORDER The named targets are made in sequence. This ordering does not add targets to the list of targets to be made. Since the dependents of a target do not get built until the target itself could be built, unless .Ql a is built by another part of the dependency graph, the following is a dependency loop: .Bd -literal \&.ORDER: b a b: a .Ed .Pp The ordering imposed by .Ic .ORDER is only relevant for parallel makes. .\" XXX: NOT YET!!!! .\" .It Ic .PARALLEL .\" The named targets are executed in parallel mode. .\" If no targets are .\" specified, then all targets are executed in parallel mode. .It Ic .PATH The sources are directories which are to be searched for files not found in the current directory. If no sources are specified, any previously specified directories are deleted. If the source is the special .Ic .DOTLAST target, then the current working directory is searched last. .It Ic .PATH. Ns Va suffix Like .Ic .PATH but applies only to files with a particular suffix. The suffix must have been previously declared with .Ic .SUFFIXES . .It Ic .PHONY Apply the .Ic .PHONY attribute to any specified sources. .It Ic .PRECIOUS Apply the .Ic .PRECIOUS attribute to any specified sources. If no sources are specified, the .Ic .PRECIOUS attribute is applied to every target in the file. .It Ic .SHELL Sets the shell that .Nm will use to execute commands. The sources are a set of .Ar field=value pairs. .Bl -tag -width hasErrCtls .It Ar name This is the minimal specification, used to select one of the built-in shell specs; .Ar sh , .Ar ksh , and .Ar csh . .It Ar path Specifies the path to the shell. .It Ar hasErrCtl Indicates whether the shell supports exit on error. .It Ar check The command to turn on error checking. .It Ar ignore The command to disable error checking. .It Ar echo The command to turn on echoing of commands executed. .It Ar quiet The command to turn off echoing of commands executed. .It Ar filter The output to filter after issuing the .Ar quiet command. It is typically identical to .Ar quiet . .It Ar errFlag The flag to pass the shell to enable error checking. .It Ar echoFlag The flag to pass the shell to enable command echoing. .It Ar newline The string literal to pass the shell that results in a single newline character when used outside of any quoting characters. .El Example: .Bd -literal \&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e check="set \-e" ignore="set +e" \e echo="set \-v" quiet="set +v" filter="set +v" \e echoFlag=v errFlag=e newline="'\en'" .Ed .It Ic .SILENT Apply the .Ic .SILENT attribute to any specified sources. If no sources are specified, the .Ic .SILENT attribute is applied to every command in the file. .It Ic .STALE This target gets run when a dependency file contains stale entries, having .Va .ALLSRC set to the name of that dependency file. .It Ic .SUFFIXES Each source specifies a suffix to .Nm . If no sources are specified, any previously specified suffixes are deleted. It allows the creation of suffix-transformation rules. .Pp Example: .Bd -literal \&.SUFFIXES: .o \&.c.o: cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed .El .Sh ENVIRONMENT .Nm uses the following environment variables, if they exist: .Ev MACHINE , .Ev MACHINE_ARCH , .Ev MAKE , .Ev MAKEFLAGS , .Ev MAKEOBJDIR , .Ev MAKEOBJDIRPREFIX , .Ev MAKESYSPATH , .Ev PWD , and .Ev TMPDIR . .Pp .Ev MAKEOBJDIRPREFIX and .Ev MAKEOBJDIR may only be set in the environment or on the command line to .Nm and not as makefile variables; see the description of .Ql Va .OBJDIR for more details. .Sh FILES .Bl -tag -width /usr/share/mk -compact .It .depend list of dependencies .It Makefile list of dependencies .It makefile list of dependencies .It sys.mk system makefile .It /usr/share/mk system makefile directory .El .Sh COMPATIBILITY The basic make syntax is compatible between different versions of make; however the special variables, variable modifiers and conditionals are not. .Ss Older versions An incomplete list of changes in older versions of .Nm : .Pp The way that .for loop variables are substituted changed after .Nx 5.0 so that they still appear to be variable expansions. In particular this stops them being treated as syntax, and removes some obscure problems using them in .if statements. .Pp The way that parallel makes are scheduled changed in .Nx 4.0 so that .ORDER and .WAIT apply recursively to the dependent nodes. The algorithms used may change again in the future. .Ss Other make dialects Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not support most of the features of .Nm as described in this manual. Most notably: .Bl -bullet -offset indent .It The .Ic .WAIT and .Ic .ORDER declarations and most functionality pertaining to parallelization. (GNU make supports parallelization but lacks these features needed to control it effectively.) .It Directives, including for loops and conditionals and most of the forms of include files. (GNU make has its own incompatible and less powerful syntax for conditionals.) .It All built-in variables that begin with a dot. .It Most of the special sources and targets that begin with a dot, with the notable exception of .Ic .PHONY , .Ic .PRECIOUS , and .Ic .SUFFIXES . .It Variable modifiers, except for the .Dl :old=new string substitution, which does not portably support globbing with .Ql % and historically only works on declared suffixes. .It The .Ic $> variable even in its short form; most makes support this functionality but its name varies. .El .Pp Some features are somewhat more portable, such as assignment with .Ic += , .Ic ?= , and .Ic != . The .Ic .PATH functionality is based on an older feature .Ic VPATH found in GNU make and many versions of SVR4 make; however, historically its behavior is too ill-defined (and too buggy) to rely upon. .Pp The .Ic $@ and .Ic $< variables are more or less universally portable, as is the .Ic $(MAKE) variable. Basic use of suffix rules (for files only in the current directory, not trying to chain transformations together, etc.) is also reasonably portable. .Sh SEE ALSO .Xr mkdep 1 , .Xr style.Makefile 5 .Sh HISTORY A .Nm command appeared in .At v7 . This .Nm implementation is based on Adam De Boor's pmake program which was written for Sprite at Berkeley. It was designed to be a parallel distributed make running jobs on different machines using a daemon called .Dq customs . .Pp Historically the target/dependency .Dq FRC has been used to FoRCe rebuilding (since the target/dependency does not exist... unless someone creates an .Dq FRC file). .Sh BUGS The .Nm 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. .Pp There is no way of escaping a space character in a filename. Index: head/contrib/bmake/make.c =================================================================== --- head/contrib/bmake/make.c (revision 365365) +++ head/contrib/bmake/make.c (revision 365366) @@ -1,1555 +1,1561 @@ -/* $NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: make.c,v 1.99 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: make.c,v 1.133 2020/08/30 14:11:42 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * make.c -- * The functions which perform the examination of targets and * their suitability for creation * * Interface: * Make_Run Initialize things for the module and recreate * whatever needs recreating. Returns TRUE if * work was (or would have been) done and FALSE * otherwise. * * Make_Update Update all parents of a given child. Performs * various bookkeeping chores like the updating * of the cmgn field of the parent, filling * of the IMPSRC context variable, etc. It will * place the parent on the toBeMade queue if it * should be. * * Make_TimeStamp Function to set the parent's cmgn field * based on a child's modification time. * * Make_DoAllVar Set up the various local variables for a * target, including the .ALLSRC variable, making * sure that any variable that needs to exist * at the very least has the empty value. * * Make_OODate Determine if a target is out-of-date. * * Make_HandleUse See if a child is a .USE node for a parent * and perform the .USE actions if so. * * Make_ExpandUse Expand .USE nodes */ #include "make.h" -#include "hash.h" +#include "enum.h" #include "dir.h" #include "job.h" static unsigned int checked = 1;/* Sequence # to detect recursion */ static Lst toBeMade; /* The current fringe of the graph. These * are nodes which await examination by * MakeOODate. It is added to by * Make_Update and subtracted from by * MakeStartJobs */ static int MakeAddChild(void *, void *); static int MakeFindChild(void *, void *); static int MakeUnmark(void *, void *); static int MakeAddAllSrc(void *, void *); static int MakeTimeStamp(void *, void *); static int MakeHandleUse(void *, void *); static Boolean MakeStartJobs(void); static int MakePrintStatus(void *, void *); static int MakeCheckOrder(void *, void *); static int MakeBuildChild(void *, void *); static int MakeBuildParent(void *, void *); MAKE_ATTR_DEAD static void make_abort(GNode *gn, int line) { static int two = 2; fprintf(debug_file, "make_abort from line %d\n", line); Targ_PrintNode(gn, &two); Lst_ForEach(toBeMade, Targ_PrintNode, &two); Targ_PrintGraph(3); abort(); } +ENUM_VALUE_RTTI_8(GNodeMade, + UNMADE, DEFERRED, REQUESTED, BEINGMADE, + MADE, UPTODATE, ERROR, ABORTED); + +ENUM_FLAGS_RTTI_31(GNodeType, + OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP, + /* OP_OPMASK is omitted since it combines other flags */ + OP_OPTIONAL, OP_USE, OP_EXEC, OP_IGNORE, + OP_PRECIOUS, OP_SILENT, OP_MAKE, OP_JOIN, + OP_MADE, OP_SPECIAL, OP_USEBEFORE, OP_INVISIBLE, + OP_NOTMAIN, OP_PHONY, OP_NOPATH, OP_WAIT, + OP_NOMETA, OP_META, OP_NOMETA_CMP, OP_SUBMAKE, + OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV, + OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK); + +ENUM_FLAGS_RTTI_10(GNodeFlags, + REMAKE, CHILDMADE, FORCE, DONE_WAIT, + DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE, + DONECYCLE, INTERNAL); + +void +GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, + const char *suffix) +{ + char type_buf[GNodeType_ToStringSize]; + char flags_buf[GNodeFlags_ToStringSize]; + + fprintf(f, "%smade %s, type %s, flags %s%s", + prefix, + Enum_ValueToString(gn->made, GNodeMade_ToStringSpecs), + Enum_FlagsToString(type_buf, sizeof type_buf, + gn->type, GNodeType_ToStringSpecs), + Enum_FlagsToString(flags_buf, sizeof flags_buf, + gn->flags, GNodeFlags_ToStringSpecs), + suffix); +} + /*- *----------------------------------------------------------------------- * Make_TimeStamp -- * Set the cmgn field of a parent node based on the mtime stamp in its * child. Called from MakeOODate via Lst_ForEach. * * Input: * pgn the current parent * cgn the child we've just examined * * Results: * Always returns 0. * * Side Effects: * The cmgn of the parent node will be changed if the mtime * field of the child is greater than it. *----------------------------------------------------------------------- */ int Make_TimeStamp(GNode *pgn, GNode *cgn) { if (pgn->cmgn == NULL || cgn->mtime > pgn->cmgn->mtime) { pgn->cmgn = cgn; } return 0; } /* * Input: * pgn the current parent * cgn the child we've just examined * */ static int MakeTimeStamp(void *pgn, void *cgn) { return Make_TimeStamp((GNode *)pgn, (GNode *)cgn); } - + /*- *----------------------------------------------------------------------- * Make_OODate -- * See if a given node is out of date with respect to its sources. * Used by Make_Run when deciding which nodes to place on the * toBeMade queue initially and by Make_Update to screen out USE and * EXEC nodes. In the latter case, however, any other sort of node * must be considered out-of-date since at least one of its children * will have been recreated. * * Input: * gn the node to check * * Results: * TRUE if the node is out of date. FALSE otherwise. * * Side Effects: * The mtime field of the node and the cmgn field of its parents * will/may be changed. *----------------------------------------------------------------------- */ Boolean Make_OODate(GNode *gn) { Boolean oodate; /* * Certain types of targets needn't even be sought as their datedness * doesn't depend on their modification time... */ if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) { (void)Dir_MTime(gn, 1); if (DEBUG(MAKE)) { if (gn->mtime != 0) { fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime)); } else { fprintf(debug_file, "non-existent..."); } } } /* * A target is remade in one of the following circumstances: * its modification time is smaller than that of its youngest child * and it would actually be run (has commands or type OP_NOP) * it's the object of a force operator * it has no children, was on the lhs of an operator and doesn't exist * already. * * Libraries are only considered out-of-date if the archive module says * they are. * * These weird rules are brought to you by Backward-Compatibility and * the strange people who wrote 'Make'. */ if (gn->type & (OP_USE|OP_USEBEFORE)) { /* * If the node is a USE node it is *never* out of date * no matter *what*. */ if (DEBUG(MAKE)) { fprintf(debug_file, ".USE node..."); } oodate = FALSE; } else if ((gn->type & OP_LIB) && ((gn->mtime==0) || Arch_IsLib(gn))) { if (DEBUG(MAKE)) { fprintf(debug_file, "library..."); } /* * always out of date if no children and :: target * or non-existent. */ oodate = (gn->mtime == 0 || Arch_LibOODate(gn) || (gn->cmgn == NULL && (gn->type & OP_DOUBLEDEP))); } else if (gn->type & OP_JOIN) { /* * A target with the .JOIN attribute is only considered * out-of-date if any of its children was out-of-date. */ if (DEBUG(MAKE)) { fprintf(debug_file, ".JOIN node..."); } if (DEBUG(MAKE)) { fprintf(debug_file, "source %smade...", gn->flags & CHILDMADE ? "" : "not "); } oodate = (gn->flags & CHILDMADE) ? TRUE : FALSE; } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { /* * A node which is the object of the force (!) operator or which has * the .EXEC attribute is always considered out-of-date. */ if (DEBUG(MAKE)) { if (gn->type & OP_FORCE) { fprintf(debug_file, "! operator..."); } else if (gn->type & OP_PHONY) { fprintf(debug_file, ".PHONY node..."); } else { fprintf(debug_file, ".EXEC node..."); } } oodate = TRUE; } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) || (gn->cmgn == NULL && ((gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) || gn->type & OP_DOUBLEDEP))) { /* * A node whose modification time is less than that of its * youngest child or that has no children (cmgn == NULL) and * either doesn't exist (mtime == 0) and it isn't optional * or was the object of a * :: operator is out-of-date. * Why? Because that's the way Make does it. */ if (DEBUG(MAKE)) { if (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) { fprintf(debug_file, "modified before source %s...", gn->cmgn->path ? gn->cmgn->path : gn->cmgn->name); } else if (gn->mtime == 0) { fprintf(debug_file, "non-existent and no sources..."); } else { fprintf(debug_file, ":: operator and no sources..."); } } oodate = TRUE; } else { /* * When a non-existing child with no sources * (such as a typically used FORCE source) has been made and * the target of the child (usually a directory) has the same * timestamp as the timestamp just given to the non-existing child * after it was considered made. */ if (DEBUG(MAKE)) { if (gn->flags & FORCE) fprintf(debug_file, "non existing child..."); } oodate = (gn->flags & FORCE) ? TRUE : FALSE; } #ifdef USE_META if (useMeta) { oodate = meta_oodate(gn, oodate); } #endif /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date * but aren't, because they have no commands and aren't of type OP_NOP, * have their mtime stay below their children's mtime to keep parents from * thinking they're out-of-date. */ if (!oodate) { Lst_ForEach(gn->parents, MakeTimeStamp, gn); } return oodate; } - + /*- *----------------------------------------------------------------------- * MakeAddChild -- * Function used by Make_Run to add a child to the list l. * It will only add the child if its make field is FALSE. * * Input: * gnp the node to add * lp the list to which to add it * * Results: * Always returns 0 * * Side Effects: * The given list is extended *----------------------------------------------------------------------- */ static int MakeAddChild(void *gnp, void *lp) { GNode *gn = (GNode *)gnp; Lst l = (Lst) lp; if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) { if (DEBUG(MAKE)) fprintf(debug_file, "MakeAddChild: need to examine %s%s\n", gn->name, gn->cohort_num); - (void)Lst_EnQueue(l, gn); + Lst_Enqueue(l, gn); } return 0; } - + /*- *----------------------------------------------------------------------- * MakeFindChild -- * Function used by Make_Run to find the pathname of a child * that was already made. * * Input: * gnp the node to find * * Results: * Always returns 0 * * Side Effects: * The path and mtime of the node and the cmgn of the parent are * updated; the unmade children count of the parent is decremented. *----------------------------------------------------------------------- */ static int MakeFindChild(void *gnp, void *pgnp) { GNode *gn = (GNode *)gnp; GNode *pgn = (GNode *)pgnp; (void)Dir_MTime(gn, 0); Make_TimeStamp(pgn, gn); pgn->unmade--; return 0; } - -/*- - *----------------------------------------------------------------------- - * Make_HandleUse -- - * Function called by Make_Run and SuffApplyTransform on the downward - * pass to handle .USE and transformation nodes. It implements the - * .USE and transformation functionality by copying the node's commands, - * type flags and children to the parent node. + +/* Called by Make_Run and SuffApplyTransform on the downward pass to handle + * .USE and transformation nodes, by copying the child node's commands, type + * flags and children to the parent node. * - * A .USE node is much like an explicit transformation rule, except - * its commands are always added to the target node, even if the - * target already has commands. + * A .USE node is much like an explicit transformation rule, except its + * commands are always added to the target node, even if the target already + * has commands. * * Input: * cgn The .USE node * pgn The target of the .USE node - * - * Results: - * none - * - * Side Effects: - * Children and commands may be added to the parent and the parent's - * type may be changed. - * - *----------------------------------------------------------------------- */ void Make_HandleUse(GNode *cgn, GNode *pgn) { LstNode ln; /* An element in the children list */ #ifdef DEBUG_SRC if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) { fprintf(debug_file, "Make_HandleUse: called for plain node %s\n", cgn->name); return; } #endif if ((cgn->type & (OP_USE|OP_USEBEFORE)) || Lst_IsEmpty(pgn->commands)) { if (cgn->type & OP_USEBEFORE) { - /* - * .USEBEFORE -- - * prepend the child's commands to the parent. - */ - Lst cmds = pgn->commands; - pgn->commands = Lst_Duplicate(cgn->commands, NULL); - (void)Lst_Concat(pgn->commands, cmds, LST_CONCNEW); - Lst_Destroy(cmds, NULL); + /* .USEBEFORE */ + Lst_PrependAll(pgn->commands, cgn->commands); } else { - /* - * .USE or target has no commands -- - * append the child's commands to the parent. - */ - (void)Lst_Concat(pgn->commands, cgn->commands, LST_CONCNEW); + /* .USE, or target has no commands */ + Lst_AppendAll(pgn->commands, cgn->commands); } } - if (Lst_Open(cgn->children) == SUCCESS) { - while ((ln = Lst_Next(cgn->children)) != NULL) { - GNode *tgn, *gn = (GNode *)Lst_Datum(ln); + Lst_Open(cgn->children); + while ((ln = Lst_Next(cgn->children)) != NULL) { + GNode *gn = LstNode_Datum(ln); - /* - * Expand variables in the .USE node's name - * and save the unexpanded form. - * We don't need to do this for commands. - * They get expanded properly when we execute. - */ - if (gn->uname == NULL) { - gn->uname = gn->name; - } else { - free(gn->name); - } - gn->name = Var_Subst(NULL, gn->uname, pgn, VARF_WANTRES); - if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { - /* See if we have a target for this node. */ - tgn = Targ_FindNode(gn->name, TARG_NOCREATE); - if (tgn != NULL) - gn = tgn; - } - - (void)Lst_AtEnd(pgn->children, gn); - (void)Lst_AtEnd(gn->parents, pgn); - pgn->unmade += 1; + /* + * Expand variables in the .USE node's name + * and save the unexpanded form. + * We don't need to do this for commands. + * They get expanded properly when we execute. + */ + if (gn->uname == NULL) { + gn->uname = gn->name; + } else { + free(gn->name); } - Lst_Close(cgn->children); + gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES); + if (gn->uname && strcmp(gn->name, gn->uname) != 0) { + /* See if we have a target for this node. */ + GNode *tgn = Targ_FindNode(gn->name, TARG_NOCREATE); + if (tgn != NULL) + gn = tgn; + } + + Lst_Append(pgn->children, gn); + Lst_Append(gn->parents, pgn); + pgn->unmade += 1; } + Lst_Close(cgn->children); pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM); } /*- *----------------------------------------------------------------------- * MakeHandleUse -- * Callback function for Lst_ForEach, used by Make_Run on the downward * pass to handle .USE nodes. Should be called before the children * are enqueued to be looked at by MakeAddChild. * This function calls Make_HandleUse to copy the .USE node's commands, * type flags and children to the parent node. * * Input: * cgnp the child we've just examined * pgnp the current parent * * Results: * returns 0. * * Side Effects: * After expansion, .USE child nodes are removed from the parent * *----------------------------------------------------------------------- */ static int MakeHandleUse(void *cgnp, void *pgnp) { GNode *cgn = (GNode *)cgnp; GNode *pgn = (GNode *)pgnp; LstNode ln; /* An element in the children list */ int unmarked; unmarked = ((cgn->type & OP_MARK) == 0); cgn->type |= OP_MARK; if ((cgn->type & (OP_USE|OP_USEBEFORE)) == 0) return 0; if (unmarked) Make_HandleUse(cgn, pgn); /* * This child node is now "made", so we decrement the count of * unmade children in the parent... We also remove the child * from the parent's list to accurately reflect the number of decent * children the parent has. This is used by Make_Run to decide * whether to queue the parent or examine its children... */ - if ((ln = Lst_Member(pgn->children, cgn)) != NULL) { + if ((ln = Lst_FindDatum(pgn->children, cgn)) != NULL) { Lst_Remove(pgn->children, ln); pgn->unmade--; } return 0; } /*- *----------------------------------------------------------------------- * Make_Recheck -- * Check the modification time of a gnode, and update it as described * in the comments below. * * Results: * returns 0 if the gnode does not exist, or its filesystem * time if it does. * * Side Effects: * the gnode's modification time and path name are affected. * *----------------------------------------------------------------------- */ time_t Make_Recheck(GNode *gn) { time_t mtime = Dir_MTime(gn, 1); #ifndef RECHECK /* * We can't re-stat the thing, but we can at least take care of rules * where a target depends on a source that actually creates the * target, but only if it has changed, e.g. * * parse.h : parse.o * * parse.o : parse.y * yacc -d parse.y * cc -c y.tab.c * mv y.tab.o parse.o * cmp -s y.tab.h parse.h || mv y.tab.h parse.h * * In this case, if the definitions produced by yacc haven't changed * from before, parse.h won't have been updated and gn->mtime will * reflect the current modification time for parse.h. This is * something of a kludge, I admit, but it's a useful one.. * XXX: People like to use a rule like * * FRC: * * To force things that depend on FRC to be made, so we have to * check for gn->children being empty as well... */ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { gn->mtime = now; } #else /* * This is what Make does and it's actually a good thing, as it * allows rules like * * cmp -s y.tab.h parse.h || cp y.tab.h parse.h * * to function as intended. Unfortunately, thanks to the stateless * nature of NFS (by which I mean the loose coupling of two clients * using the same file from a common server), there are times * when the modification time of a file created on a remote * machine will not be modified before the local stat() implied by * the Dir_MTime occurs, thus leading us to believe that the file * is unchanged, wreaking havoc with files that depend on this one. * * I have decided it is better to make too much than to make too * little, so this stuff is commented out unless you're sure it's ok. * -- ardeb 1/12/88 */ /* * Christos, 4/9/92: If we are saving commands pretend that * the target is made now. Otherwise archives with ... rules * don't work! */ if (NoExecute(gn) || (gn->type & OP_SAVE_CMDS) || (mtime == 0 && !(gn->type & OP_WAIT))) { if (DEBUG(MAKE)) { fprintf(debug_file, " recheck(%s): update time from %s to now\n", gn->name, Targ_FmtTime(gn->mtime)); } gn->mtime = now; } else { if (DEBUG(MAKE)) { fprintf(debug_file, " recheck(%s): current update time: %s\n", gn->name, Targ_FmtTime(gn->mtime)); } } #endif return mtime; } /*- *----------------------------------------------------------------------- * Make_Update -- * Perform update on the parents of a node. Used by JobFinish once * a node has been dealt with and by MakeStartJobs if it finds an * up-to-date node. * * Input: * cgn the child node * * Results: * Always returns 0 * * Side Effects: * The unmade field of pgn is decremented and pgn may be placed on * the toBeMade queue if this field becomes 0. * * If the child was made, the parent's flag CHILDMADE field will be * set true. * * If the child is not up-to-date and still does not exist, * set the FORCE flag on the parents. * * If the child wasn't made, the cmgn field of the parent will be * altered if the child's mtime is big enough. * * Finally, if the child is the implied source for the parent, the * parent's IMPSRC variable is set appropriately. * *----------------------------------------------------------------------- */ void Make_Update(GNode *cgn) { GNode *pgn; /* the parent node */ - char *cname; /* the child's name */ - LstNode ln; /* Element in parents and iParents lists */ + const char *cname; /* the child's name */ + LstNode ln; /* Element in parents and implicitParents lists */ time_t mtime = -1; char *p1; Lst parents; GNode *centurion; /* It is save to re-examine any nodes again */ checked++; cname = Var_Value(TARGET, cgn, &p1); - free(p1); + bmake_free(p1); if (DEBUG(MAKE)) fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); /* * If the child was actually made, see what its modification time is * now -- some rules won't actually update the file. If the file still * doesn't exist, make its mtime now. */ if (cgn->made != UPTODATE) { mtime = Make_Recheck(cgn); } /* * If this is a `::' node, we must consult its first instance * which is where all parents are linked. */ if ((centurion = cgn->centurion) != NULL) { if (!Lst_IsEmpty(cgn->parents)) Punt("%s%s: cohort has parents", cgn->name, cgn->cohort_num); centurion->unmade_cohorts -= 1; if (centurion->unmade_cohorts < 0) Error("Graph cycles through centurion %s", centurion->name); } else { centurion = cgn; } parents = centurion->parents; /* If this was a .ORDER node, schedule the RHS */ Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade)); /* Now mark all the parents as having one less unmade child */ - if (Lst_Open(parents) == SUCCESS) { - while ((ln = Lst_Next(parents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); + Lst_Open(parents); + while ((ln = Lst_Next(parents)) != NULL) { + pgn = LstNode_Datum(ln); + if (DEBUG(MAKE)) + fprintf(debug_file, "inspect parent %s%s: flags %x, " + "type %x, made %d, unmade %d ", + pgn->name, pgn->cohort_num, pgn->flags, + pgn->type, pgn->made, pgn->unmade-1); + + if (!(pgn->flags & REMAKE)) { + /* This parent isn't needed */ if (DEBUG(MAKE)) - fprintf(debug_file, "inspect parent %s%s: flags %x, " - "type %x, made %d, unmade %d ", - pgn->name, pgn->cohort_num, pgn->flags, - pgn->type, pgn->made, pgn->unmade-1); + fprintf(debug_file, "- not needed\n"); + continue; + } + if (mtime == 0 && !(cgn->type & OP_WAIT)) + pgn->flags |= FORCE; - if (!(pgn->flags & REMAKE)) { - /* This parent isn't needed */ - if (DEBUG(MAKE)) - fprintf(debug_file, "- not needed\n"); - continue; - } - if (mtime == 0 && !(cgn->type & OP_WAIT)) - pgn->flags |= FORCE; + /* + * If the parent has the .MADE attribute, its timestamp got + * updated to that of its newest child, and its unmake + * child count got set to zero in Make_ExpandUse(). + * However other things might cause us to build one of its + * children - and so we mustn't do any processing here when + * the child build finishes. + */ + if (pgn->type & OP_MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- .MADE\n"); + continue; + } - /* - * If the parent has the .MADE attribute, its timestamp got - * updated to that of its newest child, and its unmake - * child count got set to zero in Make_ExpandUse(). - * However other things might cause us to build one of its - * children - and so we mustn't do any processing here when - * the child build finishes. - */ - if (pgn->type & OP_MADE) { - if (DEBUG(MAKE)) - fprintf(debug_file, "- .MADE\n"); - continue; - } + if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { + if (cgn->made == MADE) + pgn->flags |= CHILDMADE; + (void)Make_TimeStamp(pgn, cgn); + } - if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { - if (cgn->made == MADE) - pgn->flags |= CHILDMADE; - (void)Make_TimeStamp(pgn, cgn); + /* + * A parent must wait for the completion of all instances + * of a `::' dependency. + */ + if (centurion->unmade_cohorts != 0 || centurion->made < MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, + "- centurion made %d, %d unmade cohorts\n", + centurion->made, centurion->unmade_cohorts); + continue; + } + + /* One more child of this parent is now made */ + pgn->unmade -= 1; + if (pgn->unmade < 0) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "Graph cycles through %s%s\n", + pgn->name, pgn->cohort_num); + Targ_PrintGraph(2); } + Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); + } + /* We must always rescan the parents of .WAIT and .ORDER nodes. */ + if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) + && !(centurion->flags & DONE_ORDER)) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- unmade children\n"); + continue; + } + if (pgn->made != DEFERRED) { /* - * A parent must wait for the completion of all instances - * of a `::' dependency. + * Either this parent is on a different branch of the tree, + * or it on the RHS of a .WAIT directive + * or it is already on the toBeMade list. */ - if (centurion->unmade_cohorts != 0 || centurion->made < MADE) { - if (DEBUG(MAKE)) - fprintf(debug_file, - "- centurion made %d, %d unmade cohorts\n", - centurion->made, centurion->unmade_cohorts); - continue; - } - - /* One more child of this parent is now made */ - pgn->unmade -= 1; - if (pgn->unmade < 0) { - if (DEBUG(MAKE)) { - fprintf(debug_file, "Graph cycles through %s%s\n", - pgn->name, pgn->cohort_num); - Targ_PrintGraph(2); - } - Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); - } - - /* We must always rescan the parents of .WAIT and .ORDER nodes. */ - if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) - && !(centurion->flags & DONE_ORDER)) { - if (DEBUG(MAKE)) - fprintf(debug_file, "- unmade children\n"); - continue; - } - if (pgn->made != DEFERRED) { - /* - * Either this parent is on a different branch of the tree, - * or it on the RHS of a .WAIT directive - * or it is already on the toBeMade list. - */ - if (DEBUG(MAKE)) - fprintf(debug_file, "- not deferred\n"); - continue; - } - if (pgn->order_pred - && Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { - /* A .ORDER rule stops us building this */ - continue; - } - if (DEBUG(MAKE)) { - static int two = 2; - fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n", - cgn->name, cgn->cohort_num, - pgn->name, pgn->cohort_num, pgn->made); - Targ_PrintNode(pgn, &two); - } - /* Ok, we can schedule the parent again */ - pgn->made = REQUESTED; - (void)Lst_EnQueue(toBeMade, pgn); + if (DEBUG(MAKE)) + fprintf(debug_file, "- not deferred\n"); + continue; } - Lst_Close(parents); + assert(pgn->order_pred != NULL); + if (Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { + /* A .ORDER rule stops us building this */ + continue; + } + if (DEBUG(MAKE)) { + static int two = 2; + fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n", + cgn->name, cgn->cohort_num, + pgn->name, pgn->cohort_num, pgn->made); + Targ_PrintNode(pgn, &two); + } + /* Ok, we can schedule the parent again */ + pgn->made = REQUESTED; + Lst_Enqueue(toBeMade, pgn); } + Lst_Close(parents); /* * Set the .PREFIX and .IMPSRC variables for all the implied parents * of this node. */ - if (Lst_Open(cgn->iParents) == SUCCESS) { - char *cpref = Var_Value(PREFIX, cgn, &p1); + Lst_Open(cgn->implicitParents); + { + const char *cpref = Var_Value(PREFIX, cgn, &p1); - while ((ln = Lst_Next(cgn->iParents)) != NULL) { - pgn = (GNode *)Lst_Datum(ln); + while ((ln = Lst_Next(cgn->implicitParents)) != NULL) { + pgn = LstNode_Datum(ln); if (pgn->flags & REMAKE) { Var_Set(IMPSRC, cname, pgn); if (cpref != NULL) Var_Set(PREFIX, cpref, pgn); } } - free(p1); - Lst_Close(cgn->iParents); + bmake_free(p1); + Lst_Close(cgn->implicitParents); } } - + /*- *----------------------------------------------------------------------- * MakeAddAllSrc -- * Add a child's name to the ALLSRC and OODATE variables of the given * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. * .EXEC and .USE children are very rarely going to be files, so... * If the child is a .JOIN node, its ALLSRC is propagated to the parent. * * A child is added to the OODATE variable if its modification time is * later than that of its parent, as defined by Make, except if the * parent is a .JOIN node. In that case, it is only added to the OODATE * variable if it was actually made (since .JOIN nodes don't have * modification times, the comparison is rather unfair...).. * * Results: * Always returns 0 * * Side Effects: * The ALLSRC variable for the given node is extended. *----------------------------------------------------------------------- */ static int MakeUnmark(void *cgnp, void *pgnp MAKE_ATTR_UNUSED) { GNode *cgn = (GNode *)cgnp; cgn->type &= ~OP_MARK; return 0; } /* * Input: * cgnp The child to add * pgnp The parent to whose ALLSRC variable it should * be added * */ static int MakeAddAllSrc(void *cgnp, void *pgnp) { GNode *cgn = (GNode *)cgnp; GNode *pgn = (GNode *)pgnp; if (cgn->type & OP_MARK) return 0; cgn->type |= OP_MARK; if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) { - char *child, *allsrc; + const char *child, *allsrc; char *p1 = NULL, *p2 = NULL; if (cgn->type & OP_ARCHV) child = Var_Value(MEMBER, cgn, &p1); else child = cgn->path ? cgn->path : cgn->name; if (cgn->type & OP_JOIN) { allsrc = Var_Value(ALLSRC, cgn, &p2); } else { allsrc = child; } if (allsrc != NULL) Var_Append(ALLSRC, allsrc, pgn); - free(p2); + bmake_free(p2); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) { Var_Append(OODATE, child, pgn); } } else if ((pgn->mtime < cgn->mtime) || (cgn->mtime >= now && cgn->made == MADE)) { /* * It goes in the OODATE variable if the parent is younger than the * child or if the child has been modified more recently than * the start of the make. This is to keep pmake from getting * confused if something else updates the parent after the * make starts (shouldn't happen, I know, but sometimes it * does). In such a case, if we've updated the kid, the parent * is likely to have a modification time later than that of * the kid and anything that relies on the OODATE variable will * be hosed. * * XXX: This will cause all made children to go in the OODATE * variable, even if they're not touched, if RECHECK isn't defined, * since cgn->mtime is set to now in Make_Update. According to * some people, this is good... */ Var_Append(OODATE, child, pgn); } - free(p1); + bmake_free(p1); } return 0; } - + /*- *----------------------------------------------------------------------- * Make_DoAllVar -- * Set up the ALLSRC and OODATE variables. Sad to say, it must be * done separately, rather than while traversing the graph. This is * because Make defined OODATE to contain all sources whose modification * times were later than that of the target, *not* those sources that * were out-of-date. Since in both compatibility and native modes, * the modification time of the parent isn't found until the child * has been dealt with, we have to wait until now to fill in the * variable. As for ALLSRC, the ordering is important and not * guaranteed when in native mode, so it must be set here, too. * * Results: * None * * Side Effects: * The ALLSRC and OODATE variables of the given node is filled in. * If the node is a .JOIN node, its TARGET variable will be set to * match its ALLSRC variable. *----------------------------------------------------------------------- */ void Make_DoAllVar(GNode *gn) { if (gn->flags & DONE_ALLSRC) return; Lst_ForEach(gn->children, MakeUnmark, gn); Lst_ForEach(gn->children, MakeAddAllSrc, gn); if (!Var_Exists (OODATE, gn)) { Var_Set(OODATE, "", gn); } if (!Var_Exists (ALLSRC, gn)) { Var_Set(ALLSRC, "", gn); } if (gn->type & OP_JOIN) { char *p1; Var_Set(TARGET, Var_Value(ALLSRC, gn, &p1), gn); - free(p1); + bmake_free(p1); } gn->flags |= DONE_ALLSRC; } - + /*- *----------------------------------------------------------------------- * MakeStartJobs -- * Start as many jobs as possible. * * Results: * If the query flag was given to pmake, no job will be started, * but as soon as an out-of-date target is found, this function * returns TRUE. At all other times, this function returns FALSE. * * Side Effects: * Nodes are removed from the toBeMade queue and job table slots * are filled. * *----------------------------------------------------------------------- */ static int MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED) { GNode *bn = v_bn; if (bn->made >= MADE || !(bn->flags & REMAKE)) return 0; if (DEBUG(MAKE)) fprintf(debug_file, "MakeCheckOrder: Waiting for .ORDER node %s%s\n", bn->name, bn->cohort_num); return 1; } static int MakeBuildChild(void *v_cn, void *toBeMade_next) { GNode *cn = v_cn; if (DEBUG(MAKE)) fprintf(debug_file, "MakeBuildChild: inspect %s%s, made %d, type %x\n", cn->name, cn->cohort_num, cn->made, cn->type); if (cn->made > DEFERRED) return 0; /* If this node is on the RHS of a .ORDER, check LHSs. */ - if (cn->order_pred && Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) { + assert(cn->order_pred); + if (Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) { /* Can't build this (or anything else in this child list) yet */ cn->made = DEFERRED; return 0; /* but keep looking */ } if (DEBUG(MAKE)) fprintf(debug_file, "MakeBuildChild: schedule %s%s\n", cn->name, cn->cohort_num); cn->made = REQUESTED; if (toBeMade_next == NULL) - Lst_AtEnd(toBeMade, cn); + Lst_Append(toBeMade, cn); else Lst_InsertBefore(toBeMade, toBeMade_next, cn); if (cn->unmade_cohorts != 0) Lst_ForEach(cn->cohorts, MakeBuildChild, toBeMade_next); /* * If this node is a .WAIT node with unmade chlidren * then don't add the next sibling. */ return cn->type & OP_WAIT && cn->unmade > 0; } /* When a .ORDER LHS node completes we do this on each RHS */ static int MakeBuildParent(void *v_pn, void *toBeMade_next) { GNode *pn = v_pn; if (pn->made != DEFERRED) return 0; if (MakeBuildChild(pn, toBeMade_next) == 0) { /* Mark so that when this node is built we reschedule its parents */ pn->flags |= DONE_ORDER; } return 0; } static Boolean MakeStartJobs(void) { GNode *gn; int have_token = 0; - while (!Lst_IsEmpty (toBeMade)) { + while (!Lst_IsEmpty(toBeMade)) { /* Get token now to avoid cycling job-list when we only have 1 token */ if (!have_token && !Job_TokenWithdraw()) break; have_token = 1; - gn = (GNode *)Lst_DeQueue(toBeMade); + gn = Lst_Dequeue(toBeMade); if (DEBUG(MAKE)) fprintf(debug_file, "Examining %s%s...\n", gn->name, gn->cohort_num); if (gn->made != REQUESTED) { if (DEBUG(MAKE)) fprintf(debug_file, "state %d\n", gn->made); make_abort(gn, __LINE__); } if (gn->checked == checked) { /* We've already looked at this node since a job finished... */ if (DEBUG(MAKE)) fprintf(debug_file, "already checked %s%s\n", gn->name, gn->cohort_num); gn->made = DEFERRED; continue; } gn->checked = checked; if (gn->unmade != 0) { /* * We can't build this yet, add all unmade children to toBeMade, * just before the current first element. */ gn->made = DEFERRED; Lst_ForEach(gn->children, MakeBuildChild, Lst_First(toBeMade)); /* and drop this node on the floor */ if (DEBUG(MAKE)) fprintf(debug_file, "dropped %s%s\n", gn->name, gn->cohort_num); continue; } gn->made = BEINGMADE; if (Make_OODate(gn)) { if (DEBUG(MAKE)) { fprintf(debug_file, "out-of-date\n"); } if (queryFlag) { return TRUE; } Make_DoAllVar(gn); Job_Make(gn); have_token = 0; } else { if (DEBUG(MAKE)) { fprintf(debug_file, "up-to-date\n"); } gn->made = UPTODATE; if (gn->type & OP_JOIN) { /* * Even for an up-to-date .JOIN node, we need it to have its * context variables so references to it get the correct * value for .TARGET when building up the context variables * of its parent(s)... */ Make_DoAllVar(gn); } Make_Update(gn); } } if (have_token) Job_TokenReturn(); return FALSE; } - + /*- *----------------------------------------------------------------------- * MakePrintStatus -- * Print the status of a top-level node, viz. it being up-to-date * already or not created due to an error in a lower level. * Callback function for Make_Run via Lst_ForEach. * * Input: * gnp Node to examine * cyclep True if gn->unmade being non-zero implies a * cycle in the graph, not an error in an * inferior. * * Results: * Always returns 0. * * Side Effects: * A message may be printed. * *----------------------------------------------------------------------- */ static int MakePrintStatusOrder(void *ognp, void *gnp) { GNode *ogn = ognp; GNode *gn = gnp; if (!(ogn->flags & REMAKE) || ogn->made > REQUESTED) /* not waiting for this one */ return 0; - printf(" `%s%s' has .ORDER dependency against %s%s " - "(made %d, flags %x, type %x)\n", - gn->name, gn->cohort_num, - ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); - if (DEBUG(MAKE) && debug_file != stdout) - fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s " - "(made %d, flags %x, type %x)\n", - gn->name, gn->cohort_num, - ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + printf(" `%s%s' has .ORDER dependency against %s%s ", + gn->name, gn->cohort_num, ogn->name, ogn->cohort_num); + GNode_FprintDetails(stdout, "(", ogn, ")\n"); + + if (DEBUG(MAKE) && debug_file != stdout) { + fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s ", + gn->name, gn->cohort_num, ogn->name, ogn->cohort_num); + GNode_FprintDetails(debug_file, "(", ogn, ")\n"); + } return 0; } static int MakePrintStatus(void *gnp, void *v_errors) { GNode *gn = (GNode *)gnp; int *errors = v_errors; if (gn->flags & DONECYCLE) /* We've completely processed this node before, don't do it again. */ return 0; if (gn->unmade == 0) { gn->flags |= DONECYCLE; switch (gn->made) { case UPTODATE: printf("`%s%s' is up to date.\n", gn->name, gn->cohort_num); break; case MADE: break; case UNMADE: case DEFERRED: case REQUESTED: case BEINGMADE: (*errors)++; - printf("`%s%s' was not built (made %d, flags %x, type %x)!\n", - gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); - if (DEBUG(MAKE) && debug_file != stdout) - fprintf(debug_file, - "`%s%s' was not built (made %d, flags %x, type %x)!\n", - gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + printf("`%s%s' was not built", gn->name, gn->cohort_num); + GNode_FprintDetails(stdout, " (", gn, ")!\n"); + if (DEBUG(MAKE) && debug_file != stdout) { + fprintf(debug_file, "`%s%s' was not built", + gn->name, gn->cohort_num); + GNode_FprintDetails(debug_file, " (", gn, ")!\n"); + } /* Most likely problem is actually caused by .ORDER */ Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn); break; default: /* Errors - already counted */ printf("`%s%s' not remade because of errors.\n", gn->name, gn->cohort_num); if (DEBUG(MAKE) && debug_file != stdout) fprintf(debug_file, "`%s%s' not remade because of errors.\n", gn->name, gn->cohort_num); break; } return 0; } if (DEBUG(MAKE)) fprintf(debug_file, "MakePrintStatus: %s%s has %d unmade children\n", gn->name, gn->cohort_num, gn->unmade); /* * If printing cycles and came to one that has unmade children, * print out the cycle by recursing on its children. */ if (!(gn->flags & CYCLE)) { /* Fist time we've seen this node, check all children */ gn->flags |= CYCLE; Lst_ForEach(gn->children, MakePrintStatus, errors); /* Mark that this node needn't be processed again */ gn->flags |= DONECYCLE; return 0; } /* Only output the error once per node */ gn->flags |= DONECYCLE; Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num); if ((*errors)++ > 100) /* Abandon the whole error report */ return 1; /* Reporting for our children will give the rest of the loop */ Lst_ForEach(gn->children, MakePrintStatus, errors); return 0; } - + /*- *----------------------------------------------------------------------- * Make_ExpandUse -- * Expand .USE nodes and create a new targets list * * Input: * targs the initial list of targets * * Side Effects: *----------------------------------------------------------------------- */ void Make_ExpandUse(Lst targs) { GNode *gn; /* a temporary pointer */ Lst examine; /* List of targets to examine */ - examine = Lst_Duplicate(targs, NULL); + examine = Lst_Copy(targs, NULL); /* * Make an initial downward pass over the graph, marking nodes to be made * as we go down. We call Suff_FindDeps to find where a node is and * to get some children for it if it has none and also has no commands. * If the node is a leaf, we stick it on the toBeMade queue to * be looked at in a minute, otherwise we add its children to our queue * and go on about our business. */ - while (!Lst_IsEmpty (examine)) { - gn = (GNode *)Lst_DeQueue(examine); + while (!Lst_IsEmpty(examine)) { + gn = Lst_Dequeue(examine); if (gn->flags & REMAKE) /* We've looked at this one already */ continue; gn->flags |= REMAKE; if (DEBUG(MAKE)) fprintf(debug_file, "Make_ExpandUse: examine %s%s\n", gn->name, gn->cohort_num); - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) { - /* Append all the 'cohorts' to the list of things to examine */ - Lst new; - new = Lst_Duplicate(gn->cohorts, NULL); - Lst_Concat(new, examine, LST_CONCLINK); - examine = new; - } + if (gn->type & OP_DOUBLEDEP) + Lst_PrependAll(examine, gn->cohorts); /* * Apply any .USE rules before looking for implicit dependencies * to make sure everything has commands that should... * Make sure that the TARGET is set, so that we can make * expansions. */ if (gn->type & OP_ARCHV) { char *eoa, *eon; eoa = strchr(gn->name, '('); eon = strchr(gn->name, ')'); if (eoa == NULL || eon == NULL) continue; *eoa = '\0'; *eon = '\0'; Var_Set(MEMBER, eoa + 1, gn); Var_Set(ARCHIVE, gn->name, gn); *eoa = '('; *eon = ')'; } (void)Dir_MTime(gn, 0); Var_Set(TARGET, gn->path ? gn->path : gn->name, gn); Lst_ForEach(gn->children, MakeUnmark, gn); Lst_ForEach(gn->children, MakeHandleUse, gn); if ((gn->type & OP_MADE) == 0) Suff_FindDeps(gn); else { /* Pretend we made all this node's children */ Lst_ForEach(gn->children, MakeFindChild, gn); if (gn->unmade != 0) printf("Warning: %s%s still has %d unmade children\n", gn->name, gn->cohort_num, gn->unmade); } if (gn->unmade != 0) Lst_ForEach(gn->children, MakeAddChild, examine); } - Lst_Destroy(examine, NULL); + Lst_Free(examine); } /*- *----------------------------------------------------------------------- * Make_ProcessWait -- * Convert .WAIT nodes into dependencies * * Input: * targs the initial list of targets * *----------------------------------------------------------------------- */ static int link_parent(void *cnp, void *pnp) { GNode *cn = cnp; GNode *pn = pnp; - Lst_AtEnd(pn->children, cn); - Lst_AtEnd(cn->parents, pn); + Lst_Append(pn->children, cn); + Lst_Append(cn->parents, pn); pn->unmade++; return 0; } static int add_wait_dep(void *v_cn, void *v_wn) { GNode *cn = v_cn; GNode *wn = v_wn; if (cn == wn) return 1; if (cn == NULL || wn == NULL) { printf("bad wait dep %p %p\n", cn, wn); exit(4); } if (DEBUG(MAKE)) fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n", cn->name, cn->cohort_num, wn->name); - Lst_AtEnd(wn->children, cn); + Lst_Append(wn->children, cn); wn->unmade++; - Lst_AtEnd(cn->parents, wn); + Lst_Append(cn->parents, wn); return 0; } static void Make_ProcessWait(Lst targs) { GNode *pgn; /* 'parent' node we are examining */ GNode *cgn; /* Each child in turn */ LstNode owln; /* Previous .WAIT node */ Lst examine; /* List of targets to examine */ LstNode ln; /* * We need all the nodes to have a common parent in order for the * .WAIT and .ORDER scheduling to work. * Perhaps this should be done earlier... */ pgn = Targ_NewGN(".MAIN"); pgn->flags = REMAKE; pgn->type = OP_PHONY | OP_DEPENDS; /* Get it displayed in the diag dumps */ - Lst_AtFront(Targ_List(), pgn); + Lst_Prepend(Targ_List(), pgn); Lst_ForEach(targs, link_parent, pgn); /* Start building with the 'dummy' .MAIN' node */ MakeBuildChild(pgn, NULL); - examine = Lst_Init(FALSE); - Lst_AtEnd(examine, pgn); + examine = Lst_Init(); + Lst_Append(examine, pgn); - while (!Lst_IsEmpty (examine)) { - pgn = Lst_DeQueue(examine); + while (!Lst_IsEmpty(examine)) { + pgn = Lst_Dequeue(examine); /* We only want to process each child-list once */ if (pgn->flags & DONE_WAIT) continue; pgn->flags |= DONE_WAIT; if (DEBUG(MAKE)) fprintf(debug_file, "Make_ProcessWait: examine %s\n", pgn->name); - if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) { - /* Append all the 'cohorts' to the list of things to examine */ - Lst new; - new = Lst_Duplicate(pgn->cohorts, NULL); - Lst_Concat(new, examine, LST_CONCLINK); - examine = new; - } + if (pgn->type & OP_DOUBLEDEP) + Lst_PrependAll(examine, pgn->cohorts); owln = Lst_First(pgn->children); Lst_Open(pgn->children); for (; (ln = Lst_Next(pgn->children)) != NULL; ) { - cgn = Lst_Datum(ln); + cgn = LstNode_Datum(ln); if (cgn->type & OP_WAIT) { /* Make the .WAIT node depend on the previous children */ Lst_ForEachFrom(pgn->children, owln, add_wait_dep, cgn); owln = ln; } else { - Lst_AtEnd(examine, cgn); + Lst_Append(examine, cgn); } } Lst_Close(pgn->children); } - Lst_Destroy(examine, NULL); + Lst_Free(examine); } /*- *----------------------------------------------------------------------- * Make_Run -- * Initialize the nodes to remake and the list of nodes which are * ready to be made by doing a breadth-first traversal of the graph * starting from the nodes in the given list. Once this traversal * is finished, all the 'leaves' of the graph are in the toBeMade * queue. * Using this queue and the Job module, work back up the graph, * calling on MakeStartJobs to keep the job table as full as * possible. * * Input: * targs the initial list of targets * * Results: * TRUE if work was done. FALSE otherwise. * * Side Effects: * The make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. *----------------------------------------------------------------------- */ Boolean Make_Run(Lst targs) { int errors; /* Number of errors the Job module reports */ /* Start trying to make the current targets... */ - toBeMade = Lst_Init(FALSE); + toBeMade = Lst_Init(); Make_ExpandUse(targs); Make_ProcessWait(targs); if (DEBUG(MAKE)) { fprintf(debug_file, "#***# full graph\n"); Targ_PrintGraph(1); } if (queryFlag) { /* * We wouldn't do any work unless we could start some jobs in the * next loop... (we won't actually start any, of course, this is just * to see if any of the targets was out of date) */ return MakeStartJobs(); } /* * Initialization. At the moment, no jobs are running and until some * get started, nothing will happen since the remaining upward * traversal of the graph is performed by the routines in job.c upon * the finishing of a job. So we fill the Job table as much as we can * before going into our loop. */ (void)MakeStartJobs(); /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for output * will cause us to be idle most of the time while our children run as * much as possible. Because the job table is kept as full as possible, * the only time when it will be empty is when all the jobs which need * running have been run, so that is the end condition of this loop. * Note that the Job module will exit if there were any errors unless the * keepgoing flag was given. */ while (!Lst_IsEmpty(toBeMade) || jobTokensRunning > 0) { Job_CatchOutput(); (void)MakeStartJobs(); } errors = Job_Finish(); /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ if (DEBUG(MAKE)) fprintf(debug_file, "done: errors %d\n", errors); if (errors == 0) { Lst_ForEach(targs, MakePrintStatus, &errors); if (DEBUG(MAKE)) { fprintf(debug_file, "done: errors %d\n", errors); if (errors) Targ_PrintGraph(4); } } return errors != 0; } Index: head/contrib/bmake/make.h =================================================================== --- head/contrib/bmake/make.h (revision 365365) +++ head/contrib/bmake/make.h (revision 365366) @@ -1,550 +1,595 @@ -/* $NetBSD: make.h,v 1.109 2020/07/02 15:14:38 rillig Exp $ */ +/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /*- * make.h -- * The global definitions for pmake */ -#ifndef _MAKE_H_ -#define _MAKE_H_ +#ifndef MAKE_MAKE_H +#define MAKE_MAKE_H #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include +#include +#include #include #include #include #include #ifdef HAVE_STRING_H #include #else #include #endif #include #include #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if defined(__GNUC__) #define MAKE_GNUC_PREREQ(x, y) \ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ (__GNUC__ > (x))) #else /* defined(__GNUC__) */ #define MAKE_GNUC_PREREQ(x, y) 0 #endif /* defined(__GNUC__) */ #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_UNUSED __attribute__((__unused__)) #else #define MAKE_ATTR_UNUSED /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 5) #define MAKE_ATTR_DEAD __attribute__((__noreturn__)) #elif defined(__GNUC__) #define MAKE_ATTR_DEAD __volatile #else #define MAKE_ATTR_DEAD /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ #endif -#include "sprite.h" +/* + * A boolean type is defined as an integer, not an enum, for historic reasons. + * The only allowed values are the constants TRUE and FALSE (1 and 0). + */ + +#ifdef USE_DOUBLE_BOOLEAN +/* During development, to find type mismatches in function declarations. */ +typedef double Boolean; +#elif defined(USE_UCHAR_BOOLEAN) +/* During development, to find code that depends on the exact value of TRUE or + * that stores other values in Boolean variables. */ +typedef unsigned char Boolean; +#define TRUE ((unsigned char)0xFF) +#define FALSE ((unsigned char)0x00) +#elif defined(USE_ENUM_BOOLEAN) +typedef enum { FALSE, TRUE} Boolean; +#else +typedef int Boolean; +#endif +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + #include "lst.h" +#include "enum.h" #include "hash.h" #include "make-conf.h" #include "buf.h" #include "make_malloc.h" /* * some vendors don't have this --sjg */ #if defined(S_IFDIR) && !defined(S_ISDIR) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #if defined(sun) && (defined(__svr4__) || defined(__SVR4)) #define POSIX_SIGNALS #endif -/*- - * The structure for an individual graph node. Each node has several - * pieces of data associated with it. - * 1) the name of the target it describes - * 2) the location of the target file in the file system. - * 3) the type of operator used to define its sources (qv. parse.c) - * 4) whether it is involved in this invocation of make - * 5) whether the target has been remade - * 6) whether any of its children has been remade - * 7) the number of its children that are, as yet, unmade - * 8) its modification time - * 9) the modification time of its youngest child (qv. make.c) - * 10) a list of nodes for which this is a source (parents) - * 11) a list of nodes on which this depends (children) - * 12) a list of nodes that depend on this, as gleaned from the - * transformation rules (iParents) - * 13) a list of ancestor nodes, which includes parents, iParents, - * and recursive parents of parents - * 14) a list of nodes of the same name created by the :: operator - * 15) a list of nodes that must be made (if they're made) before - * this node can be, but that do not enter into the datedness of - * this node. - * 16) a list of nodes that must be made (if they're made) before - * this node or any child of this node can be, but that do not - * enter into the datedness of this node. - * 17) a list of nodes that must be made (if they're made) after - * this node is, but that do not depend on this node, in the - * normal sense. - * 18) a Lst of ``local'' variables that are specific to this target - * and this target only (qv. var.c [$@ $< $?, etc.]) - * 19) a Lst of strings that are commands to be given to a shell - * to create this target. - */ +typedef enum { + UNMADE, /* Not examined yet */ + DEFERRED, /* Examined once (building child) */ + REQUESTED, /* on toBeMade list */ + BEINGMADE, /* Target is already being made. + * Indicates a cycle in the graph. */ + MADE, /* Was out-of-date and has been made */ + UPTODATE, /* Was already up-to-date */ + ERROR, /* An error occurred while it was being + * made (used only in compat mode) */ + ABORTED /* The target was aborted due to an error + * making an inferior (compat). */ +} GNodeMade; + +/* The OP_ constants are used when parsing a dependency line as a way of + * communicating to other parts of the program the way in which a target + * should be made. + * + * These constants are bitwise-OR'ed together and placed in the 'type' field + * of each node. Any node that has a 'type' field which satisfies the OP_NOP + * function was never never on the left-hand side of an operator, though it + * may have been on the right-hand side... */ +typedef enum { + /* Execution of commands depends on children (:) */ + OP_DEPENDS = 1 << 0, + /* Always execute commands (!) */ + OP_FORCE = 1 << 1, + /* Execution of commands depends on children per line (::) */ + OP_DOUBLEDEP = 1 << 2, + + OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP, + + /* Don't care if the target doesn't exist and can't be created */ + OP_OPTIONAL = 1 << 3, + /* Use associated commands for parents */ + OP_USE = 1 << 4, + /* Target is never out of date, but always execute commands anyway. + * Its time doesn't matter, so it has none...sort of */ + OP_EXEC = 1 << 5, + /* Ignore errors when creating the node */ + OP_IGNORE = 1 << 6, + /* Don't remove the target when interrupted */ + OP_PRECIOUS = 1 << 7, + /* Don't echo commands when executed */ + OP_SILENT = 1 << 8, + /* Target is a recursive make so its commands should always be executed + * when it is out of date, regardless of the state of the -n or -t flags */ + OP_MAKE = 1 << 9, + /* Target is out-of-date only if any of its children was out-of-date */ + OP_JOIN = 1 << 10, + /* Assume the children of the node have been already made */ + OP_MADE = 1 << 11, + /* Special .BEGIN, .END, .INTERRUPT */ + OP_SPECIAL = 1 << 12, + /* Like .USE, only prepend commands */ + OP_USEBEFORE = 1 << 13, + /* The node is invisible to its parents. I.e. it doesn't show up in the + * parents' local variables. */ + OP_INVISIBLE = 1 << 14, + /* The node is exempt from normal 'main target' processing in parse.c */ + OP_NOTMAIN = 1 << 15, + /* Not a file target; run always */ + OP_PHONY = 1 << 16, + /* Don't search for file in the path */ + OP_NOPATH = 1 << 17, + /* .WAIT phony node */ + OP_WAIT = 1 << 18, + /* .NOMETA do not create a .meta file */ + OP_NOMETA = 1 << 19, + /* .META we _do_ want a .meta file */ + OP_META = 1 << 20, + /* Do not compare commands in .meta file */ + OP_NOMETA_CMP = 1 << 21, + /* Possibly a submake node */ + OP_SUBMAKE = 1 << 22, + + /* Attributes applied by PMake */ + + /* The node is a transformation rule */ + OP_TRANSFORM = 1 << 31, + /* Target is a member of an archive */ + OP_MEMBER = 1 << 30, + /* Target is a library */ + OP_LIB = 1 << 29, + /* Target is an archive construct */ + OP_ARCHV = 1 << 28, + /* Target has all the commands it should. Used when parsing to catch + * multiple commands for a target. */ + OP_HAS_COMMANDS = 1 << 27, + /* Saving commands on .END (Compat) */ + OP_SAVE_CMDS = 1 << 26, + /* Already processed by Suff_FindDeps */ + OP_DEPS_FOUND = 1 << 25, + /* Node found while expanding .ALLSRC */ + OP_MARK = 1 << 24 +} GNodeType; + +typedef enum { + REMAKE = 0x0001, /* this target needs to be (re)made */ + CHILDMADE = 0x0002, /* children of this target were made */ + FORCE = 0x0004, /* children don't exist, and we pretend made */ + DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */ + DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */ + FROM_DEPEND = 0x0020, /* Node created from .depend */ + DONE_ALLSRC = 0x0040, /* We do it once only */ + CYCLE = 0x1000, /* Used by MakePrintStatus */ + DONECYCLE = 0x2000, /* Used by MakePrintStatus */ + INTERNAL = 0x4000 /* Internal use only */ +} GNodeFlags; + +/* A graph node represents a target that can possibly be made, including its + * relation to other targets and a lot of other details. */ typedef struct GNode { - char *name; /* The target's name */ - char *uname; /* The unexpanded name of a .USE node */ - char *path; /* The full pathname of the file */ - int type; /* Its type (see the OP flags, below) */ + /* The target's name, such as "clean" or "make.c" */ + char *name; + /* The unexpanded name of a .USE node */ + char *uname; + /* The full pathname of the file belonging to the target. + * XXX: What about .PHONY targets? These don't have an associated path. */ + char *path; - int flags; -#define REMAKE 0x1 /* this target needs to be (re)made */ -#define CHILDMADE 0x2 /* children of this target were made */ -#define FORCE 0x4 /* children don't exist, and we pretend made */ -#define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */ -#define DONE_ORDER 0x10 /* Build requested by .ORDER processing */ -#define FROM_DEPEND 0x20 /* Node created from .depend */ -#define DONE_ALLSRC 0x40 /* We do it once only */ -#define CYCLE 0x1000 /* Used by MakePrintStatus */ -#define DONECYCLE 0x2000 /* Used by MakePrintStatus */ -#define INTERNAL 0x4000 /* Internal use only */ - enum enum_made { - UNMADE, DEFERRED, REQUESTED, BEINGMADE, - MADE, UPTODATE, ERROR, ABORTED - } made; /* Set to reflect the state of processing - * on this node: - * UNMADE - Not examined yet - * DEFERRED - Examined once (building child) - * REQUESTED - on toBeMade list - * BEINGMADE - Target is already being made. - * Indicates a cycle in the graph. - * MADE - Was out-of-date and has been made - * UPTODATE - Was already up-to-date - * ERROR - An error occurred while it was being - * made (used only in compat mode) - * ABORTED - The target was aborted due to - * an error making an inferior (compat). - */ - int unmade; /* The number of unmade children */ + /* The type of operator used to define the sources (see the OP flags below). + * XXX: This looks like a wild mixture of type and flags. */ + GNodeType type; + /* whether it is involved in this invocation of make */ + GNodeFlags flags; - time_t mtime; /* Its modification time */ - struct GNode *cmgn; /* The youngest child */ + /* The state of processing on this node */ + GNodeMade made; + int unmade; /* The number of unmade children */ - Lst iParents; /* Links to parents for which this is an - * implied source, if any */ - Lst cohorts; /* Other nodes for the :: operator */ - Lst parents; /* Nodes that depend on this one */ - Lst children; /* Nodes on which this one depends */ - Lst order_pred; /* .ORDER nodes we need made */ - Lst order_succ; /* .ORDER nodes who need us */ + time_t mtime; /* Its modification time */ + struct GNode *cmgn; /* The youngest child */ - char cohort_num[8]; /* #n for this cohort */ - int unmade_cohorts;/* # of unmade instances on the - cohorts list */ - struct GNode *centurion; /* Pointer to the first instance of a :: - node; only set when on a cohorts list */ - unsigned int checked; /* Last time we tried to makle this node */ + /* The GNodes for which this node is an implied source. May be empty. + * For example, when there is an inference rule for .c.o, the node for + * file.c has the node for file.o in this list. */ + Lst implicitParents; - Hash_Table context; /* The local variables */ - Lst commands; /* Creation commands */ + /* Other nodes of the same name for the :: operator. */ + Lst cohorts; - struct _Suff *suffix; /* Suffix for the node (determined by - * Suff_FindDeps and opaque to everyone - * but the Suff module) */ - const char *fname; /* filename where the GNode got defined */ - int lineno; /* line number where the GNode got defined */ -} GNode; + /* The nodes that depend on this one, or in other words, the nodes for + * which this is a source. */ + Lst parents; + /* The nodes on which this one depends. */ + Lst children; -/* - * The OP_ constants are used when parsing a dependency line as a way of - * communicating to other parts of the program the way in which a target - * should be made. These constants are bitwise-OR'ed together and - * placed in the 'type' field of each node. Any node that has - * a 'type' field which satisfies the OP_NOP function was never never on - * the lefthand side of an operator, though it may have been on the - * righthand side... - */ -#define OP_DEPENDS 0x00000001 /* Execution of commands depends on - * kids (:) */ -#define OP_FORCE 0x00000002 /* Always execute commands (!) */ -#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids - * per line (::) */ -#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) + /* .ORDER nodes we need made. The nodes that must be made (if they're + * made) before this node can be made, but that do not enter into the + * datedness of this node. */ + Lst order_pred; + /* .ORDER nodes who need us. The nodes that must be made (if they're made + * at all) after this node is made, but that do not depend on this node, + * in the normal sense. */ + Lst order_succ; -#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't - * exist and can't be created */ -#define OP_USE 0x00000010 /* Use associated commands for parents */ -#define OP_EXEC 0x00000020 /* Target is never out of date, but always - * execute commands anyway. Its time - * doesn't matter, so it has none...sort - * of */ -#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ -#define OP_PRECIOUS 0x00000080 /* Don't remove the target when - * interrupted */ -#define OP_SILENT 0x00000100 /* Don't echo commands when executed */ -#define OP_MAKE 0x00000200 /* Target is a recursive make so its - * commands should always be executed when - * it is out of date, regardless of the - * state of the -n or -t flags */ -#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its - * children was out-of-date */ -#define OP_MADE 0x00000800 /* Assume the children of the node have - * been already made */ -#define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */ -#define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */ -#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. - * I.e. it doesn't show up in the parents's - * local variables. */ -#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main - * target' processing in parse.c */ -#define OP_PHONY 0x00010000 /* Not a file target; run always */ -#define OP_NOPATH 0x00020000 /* Don't search for file in the path */ -#define OP_WAIT 0x00040000 /* .WAIT phony node */ -#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ -#define OP_META 0x00100000 /* .META we _do_ want a .meta file */ -#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ -#define OP_SUBMAKE 0x00400000 /* Possibly a submake node */ -/* Attributes applied by PMake */ -#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ -#define OP_MEMBER 0x40000000 /* Target is a member of an archive */ -#define OP_LIB 0x20000000 /* Target is a library */ -#define OP_ARCHV 0x10000000 /* Target is an archive construct */ -#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. - * Used when parsing to catch multiple - * commands for a target */ -#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ -#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ -#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ + /* #n for this cohort */ + char cohort_num[8]; + /* The number of unmade instances on the cohorts list */ + int unmade_cohorts; + /* Pointer to the first instance of a '::' node; only set when on a + * cohorts list */ + struct GNode *centurion; + /* Last time (sequence number) we tried to make this node */ + unsigned int checked; + + /* The "local" variables that are specific to this target and this target + * only, such as $@, $<, $?. */ + Hash_Table context; + + /* The commands to be given to a shell to create this target. */ + Lst commands; + + /* Suffix for the node (determined by Suff_FindDeps and opaque to everyone + * but the Suff module) */ + struct Suff *suffix; + + /* filename where the GNode got defined */ + const char *fname; + /* line number where the GNode got defined */ + int lineno; +} GNode; + #define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) /* * OP_NOP will return TRUE if the node with the given type was not the * object of a dependency operator */ #define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) #define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM) /* * The TARG_ constants are used when calling the Targ_FindNode and * Targ_FindList functions in targ.c. They simply tell the functions what to * do if the desired node(s) is (are) not found. If the TARG_CREATE constant * is given, a new, empty node will be created for the target, placed in the * table of all targets and its address returned. If TARG_NOCREATE is given, * a NULL pointer will be returned. */ #define TARG_NOCREATE 0x00 /* don't create it */ #define TARG_CREATE 0x01 /* create node if not found */ #define TARG_NOHASH 0x02 /* don't look in/add to hash table */ /* - * These constants are all used by the Str_Concat function to decide how the - * final string should look. If STR_ADDSPACE is given, a space will be - * placed between the two strings. If STR_ADDSLASH is given, a '/' will - * be used instead of a space. If neither is given, no intervening characters - * will be placed between the two strings in the final output. If the - * STR_DOFREE bit is set, the two input strings will be freed before - * Str_Concat returns. - */ -#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ -#define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */ - -/* * Error levels for parsing. PARSE_FATAL means the process cannot continue * once the makefile has been parsed. PARSE_WARNING means it can. Passed * as the first argument to Parse_Error. */ #define PARSE_INFO 3 #define PARSE_WARNING 2 #define PARSE_FATAL 1 /* * Values returned by Cond_Eval. */ -#define COND_PARSE 0 /* Parse the next lines */ -#define COND_SKIP 1 /* Skip the next lines */ -#define COND_INVALID 2 /* Not a conditional statement */ +typedef enum { + COND_PARSE, /* Parse the next lines */ + COND_SKIP, /* Skip the next lines */ + COND_INVALID /* Not a conditional statement */ +} CondEvalResult; /* * Definitions for the "local" variables. Used only for clarity. */ #define TARGET "@" /* Target of dependency */ #define OODATE "?" /* All out-of-date sources */ #define ALLSRC ">" /* All sources */ #define IMPSRC "<" /* Source implied by transformation */ #define PREFIX "*" /* Common prefix */ #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ #define MEMBER "%" /* Member in "archive(member)" syntax */ #define FTARGET "@F" /* file part of TARGET */ #define DTARGET "@D" /* directory part of TARGET */ #define FIMPSRC " b) ? a : b) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ #ifdef HAVE_LIMITS_H #include #endif #ifndef MAXPATHLEN #define MAXPATHLEN BMAKE_PATH_MAX #endif #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif #if defined(SYSV) #define KILLPG(pid, sig) kill(-(pid), (sig)) #else #define KILLPG(pid, sig) killpg((pid), (sig)) #endif -#endif /* _MAKE_H_ */ +#endif /* MAKE_MAKE_H */ Index: head/contrib/bmake/make_malloc.c =================================================================== --- head/contrib/bmake/make_malloc.c (revision 365365) +++ head/contrib/bmake/make_malloc.c (revision 365366) @@ -1,119 +1,101 @@ -/* $NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * 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. */ #ifdef MAKE_NATIVE #include -__RCSID("$NetBSD: make_malloc.c,v 1.12 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $"); #endif #include #include #include #include #include "make.h" #ifndef USE_EMALLOC static MAKE_ATTR_DEAD void enomem(void); -/* - * enomem -- - * die when out of memory. - */ +/* die when out of memory. */ static MAKE_ATTR_DEAD void enomem(void) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(ENOMEM)); exit(2); } -/* - * bmake_malloc -- - * malloc, but die on error. - */ +/* malloc, but die on error. */ void * bmake_malloc(size_t len) { void *p; if ((p = malloc(len)) == NULL) enomem(); return p; } -/* - * bmake_strdup -- - * strdup, but die on error. - */ +/* strdup, but die on error. */ char * bmake_strdup(const char *str) { size_t len; char *p; len = strlen(str) + 1; if ((p = malloc(len)) == NULL) enomem(); return memcpy(p, str, len); } -/* - * bmake_strndup -- - * strndup, but die on error. - */ +/* Allocate a string starting from str with exactly len characters. */ char * -bmake_strndup(const char *str, size_t max_len) +bmake_strldup(const char *str, size_t len) { - size_t len; - char *p; - - if (str == NULL) - return NULL; - - len = strlen(str); - if (len > max_len) - len = max_len; - p = bmake_malloc(len + 1); + char *p = bmake_malloc(len + 1); memcpy(p, str, len); p[len] = '\0'; - return p; } -/* - * bmake_realloc -- - * realloc, but die on error. - */ +/* realloc, but die on error. */ void * bmake_realloc(void *ptr, size_t size) { if ((ptr = realloc(ptr, size)) == NULL) enomem(); return ptr; } #endif + +/* Allocate a string from start up to but excluding end. */ +char * +bmake_strsedup(const char *start, const char *end) +{ + return bmake_strldup(start, (size_t)(end - start)); +} Index: head/contrib/bmake/make_malloc.h =================================================================== --- head/contrib/bmake/make_malloc.h (revision 365365) +++ head/contrib/bmake/make_malloc.h (revision 365366) @@ -1,41 +1,54 @@ -/* $NetBSD: make_malloc.h,v 1.4 2009/01/24 14:43:29 dsl Exp $ */ +/* $NetBSD: make_malloc.h,v 1.10 2020/08/29 16:47:45 rillig Exp $ */ /*- * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * 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 USE_EMALLOC void *bmake_malloc(size_t); void *bmake_realloc(void *, size_t); char *bmake_strdup(const char *); -char *bmake_strndup(const char *, size_t); +char *bmake_strldup(const char *, size_t); #else #include #define bmake_malloc(x) emalloc(x) #define bmake_realloc(x,y) erealloc(x,y) #define bmake_strdup(x) estrdup(x) -#define bmake_strndup(x,y) estrndup(x,y) +#define bmake_strldup(x,y) estrndup(x,y) #endif +char *bmake_strsedup(const char *, const char *); +/* Thin wrapper around free(3) to avoid the extra function call in case + * p is NULL, which on x86_64 costs about 12 machine instructions. + * Other platforms are similarly affected. + * + * The case of a NULL pointer happens especially often after Var_Value, + * since only environment variables need to be freed, but not others. */ +static inline void MAKE_ATTR_UNUSED +bmake_free(void *p) +{ + if (p != NULL) + free(p); +} Index: head/contrib/bmake/meta.c =================================================================== --- head/contrib/bmake/meta.c (revision 365365) +++ head/contrib/bmake/meta.c (revision 365366) @@ -1,1747 +1,1730 @@ -/* $NetBSD: meta.c,v 1.86 2020/07/11 00:39:53 sjg Exp $ */ +/* $NetBSD: meta.c,v 1.113 2020/09/02 04:08:54 rillig Exp $ */ /* * Implement 'meta' mode. * Adapted from John Birrell's patches to FreeBSD make. * --sjg */ /* * Copyright (c) 2009-2016, Juniper Networks, Inc. * Portions Copyright (c) 2009, John Birrell. * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT * OWNER 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. */ #if defined(USE_META) #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_LIBGEN_H #include #elif !defined(HAVE_DIRNAME) char * dirname(char *); #endif #include #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) #include #endif #include "make.h" +#include "dir.h" #include "job.h" #ifdef USE_FILEMON #include "filemon/filemon.h" #endif static BuildMon Mybm; /* for compat */ static Lst metaBailiwick; /* our scope of control */ static char *metaBailiwickStr; /* string storage for the list */ static Lst metaIgnorePaths; /* paths we deliberately ignore */ static char *metaIgnorePathsStr; /* string storage for the list */ #ifndef MAKE_META_IGNORE_PATHS #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" #endif #ifndef MAKE_META_IGNORE_PATTERNS #define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS" #endif #ifndef MAKE_META_IGNORE_FILTER #define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER" #endif Boolean useMeta = FALSE; static Boolean useFilemon = FALSE; static Boolean writeMeta = FALSE; static Boolean metaMissing = FALSE; /* oodate if missing */ static Boolean filemonMissing = FALSE; /* oodate if missing */ static Boolean metaEnv = FALSE; /* don't save env unless asked */ static Boolean metaVerbose = FALSE; static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */ static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */ static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ extern Boolean forceJobs; extern Boolean comatMake; extern char **environ; #define MAKE_META_PREFIX ".MAKE.META.PREFIX" #ifndef N2U # define N2U(n, u) (((n) + ((u) - 1)) / (u)) #endif #ifndef ROUNDUP # define ROUNDUP(n, u) (N2U((n), (u)) * (u)) #endif #if !defined(HAVE_STRSEP) # define strsep(s, d) stresep((s), (d), 0) #endif /* * Filemon is a kernel module which snoops certain syscalls. * * C chdir * E exec * F [v]fork * L [sym]link * M rename * R read * W write * S stat * * See meta_oodate below - we mainly care about 'E' and 'R'. * * We can still use meta mode without filemon, but * the benefits are more limited. */ #ifdef USE_FILEMON /* * Open the filemon device. */ static void meta_open_filemon(BuildMon *pbm) { int dupfd; pbm->mon_fd = -1; pbm->filemon = NULL; if (!useFilemon || !pbm->mfp) return; pbm->filemon = filemon_open(); if (pbm->filemon == NULL) { useFilemon = FALSE; warn("Could not open filemon %s", filemon_path()); return; } /* * We use a file outside of '.' * to avoid a FreeBSD kernel bug where unlink invalidates * cwd causing getcwd to do a lot more work. * We only care about the descriptor. */ pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 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); } /* * Read the build monitor output file and write records to the target's * metadata file. */ static int filemon_read(FILE *mfp, int fd) { char buf[BUFSIZ]; int n; int error; /* Check if we're not writing to a meta data file.*/ if (mfp == NULL) { if (fd >= 0) close(fd); /* not interested */ return 0; } /* rewind */ if (lseek(fd, (off_t)0, SEEK_SET) < 0) { error = errno; warn("Could not rewind filemon"); fprintf(mfp, "\n"); } else { error = 0; fprintf(mfp, "\n-- filemon acquired metadata --\n"); while ((n = read(fd, buf, sizeof(buf))) > 0) { if ((int)fwrite(buf, 1, n, mfp) < n) error = EIO; } } fflush(mfp); if (close(fd) < 0) error = errno; return error; } #endif /* * when realpath() fails, * we use this, to clean up ./ and ../ */ static void eat_dots(char *buf, size_t bufsz, int dots) { char *cp; char *cp2; const char *eat; size_t eatlen; switch (dots) { case 1: eat = "/./"; eatlen = 2; break; case 2: eat = "/../"; eatlen = 3; break; default: return; } do { cp = strstr(buf, eat); if (cp) { cp2 = cp + eatlen; if (dots == 2 && cp > buf) { do { cp--; } while (cp > buf && *cp != '/'); } if (*cp == '/') { strlcpy(cp, cp2, bufsz - (cp - buf)); } else { return; /* can't happen? */ } } } while (cp); } static char * meta_name(char *mname, size_t mnamelen, const char *dname, const char *tname, const char *cwd) { char buf[MAXPATHLEN]; char *rp; char *cp; char *tp; char *dtp; size_t ldname; /* * Weed out relative paths from the target file name. * We have to be careful though since if target is a * symlink, the result will be unstable. * So we use realpath() just to get the dirname, and leave the * basename as given to us. */ if ((cp = strrchr(tname, '/'))) { if (cached_realpath(tname, buf)) { if ((rp = strrchr(buf, '/'))) { rp++; cp++; if (strcmp(cp, rp) != 0) strlcpy(rp, cp, sizeof(buf) - (rp - buf)); } tname = buf; } else { /* * We likely have a directory which is about to be made. * We pretend realpath() succeeded, to have a chance * of generating the same meta file name that we will * next time through. */ if (tname[0] == '/') { strlcpy(buf, tname, sizeof(buf)); } else { snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); } eat_dots(buf, sizeof(buf), 1); /* ./ */ eat_dots(buf, sizeof(buf), 2); /* ../ */ tname = buf; } } /* on some systems dirname may modify its arg */ tp = bmake_strdup(tname); dtp = dirname(tp); if (strcmp(dname, dtp) == 0) snprintf(mname, mnamelen, "%s.meta", tname); else { ldname = strlen(dname); if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/') snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]); else snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); /* * Replace path separators in the file name after the * current object directory path. */ cp = mname + strlen(dname) + 1; while (*cp != '\0') { if (*cp == '/') *cp = '_'; cp++; } } free(tp); return mname; } /* * Return true if running ${.MAKE} * Bypassed if target is flagged .MAKE */ static int is_submake(void *cmdp, void *gnp) { - static char *p_make = NULL; + static const char *p_make = NULL; static int p_len; char *cmd = cmdp; GNode *gn = gnp; char *mp = NULL; char *cp; char *cp2; int rc = 0; /* keep looking */ if (!p_make) { p_make = Var_Value(".MAKE", gn, &cp); p_len = strlen(p_make); } cp = strchr(cmd, '$'); if ((cp)) { - mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES); + mp = Var_Subst(cmd, gn, VARE_WANTRES); cmd = mp; } cp2 = strstr(cmd, p_make); if ((cp2)) { switch (cp2[p_len]) { case '\0': case ' ': case '\t': case '\n': rc = 1; break; } if (cp2 > cmd && rc > 0) { switch (cp2[-1]) { case ' ': case '\t': case '\n': break; default: rc = 0; /* no match */ break; } } } free(mp); return rc; } typedef struct meta_file_s { FILE *fp; GNode *gn; } meta_file_t; static int printCMD(void *cmdp, void *mfpp) { meta_file_t *mfp = mfpp; char *cmd = cmdp; - char *cp = NULL; + char *cmd_freeIt = NULL; if (strchr(cmd, '$')) { - cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES); + cmd = cmd_freeIt = Var_Subst(cmd, mfp->gn, VARE_WANTRES); } fprintf(mfp->fp, "CMD %s\n", cmd); - free(cp); + free(cmd_freeIt); return 0; } /* * Certain node types never get a .meta file */ #define SKIP_META_TYPE(_type) do { \ if ((gn->type & __CONCAT(OP_, _type))) { \ if (verbose) { \ fprintf(debug_file, "Skipping meta for %s: .%s\n", \ gn->name, __STRING(_type)); \ } \ return FALSE; \ } \ } while (0) /* * Do we need/want a .meta file ? */ static Boolean meta_needed(GNode *gn, const char *dname, char *objdir, int verbose) { - struct stat fs; + struct make_stat mst; if (verbose) verbose = DEBUG(META); /* This may be a phony node which we don't want meta data for... */ /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ /* Or it may be explicitly flagged as .NOMETA */ SKIP_META_TYPE(NOMETA); /* Unless it is explicitly flagged as .META */ if (!(gn->type & OP_META)) { SKIP_META_TYPE(PHONY); SKIP_META_TYPE(SPECIAL); SKIP_META_TYPE(MAKE); } /* Check if there are no commands to execute. */ if (Lst_IsEmpty(gn->commands)) { if (verbose) fprintf(debug_file, "Skipping meta for %s: no commands\n", gn->name); return FALSE; } if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) { /* OP_SUBMAKE is a bit too aggressive */ if (Lst_ForEach(gn->commands, is_submake, gn)) { if (DEBUG(META)) fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n", gn->name); return FALSE; } } /* The object directory may not exist. Check it.. */ - if (cached_stat(dname, &fs) != 0) { + if (cached_stat(dname, &mst) != 0) { if (verbose) fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", gn->name); return FALSE; } /* make sure these are canonical */ if (cached_realpath(dname, objdir)) dname = objdir; /* If we aren't in the object directory, don't create a meta file. */ if (!metaCurdirOk && strcmp(curdir, dname) == 0) { if (verbose) fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", gn->name); return FALSE; } return TRUE; } static FILE * meta_create(BuildMon *pbm, GNode *gn) { meta_file_t mf; char buf[MAXPATHLEN]; char objdir[MAXPATHLEN]; char **ptr; const char *dname; const char *tname; char *fname; const char *cp; char *p[5]; /* >= possible uses */ int i; mf.fp = NULL; i = 0; dname = Var_Value(".OBJDIR", gn, &p[i++]); tname = Var_Value(TARGET, gn, &p[i++]); /* if this succeeds objdir is realpath of dname */ if (!meta_needed(gn, dname, objdir, TRUE)) goto out; dname = objdir; if (metaVerbose) { char *mp; /* Describe the target we are building */ - mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES); + mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES); if (*mp) fprintf(stdout, "%s\n", mp); free(mp); } /* Get the basename of the target */ if ((cp = strrchr(tname, '/')) == NULL) { cp = tname; } else { cp++; } fflush(stdout); if (!writeMeta) /* Don't create meta data. */ goto out; fname = meta_name(pbm->meta_fname, sizeof(pbm->meta_fname), dname, tname, objdir); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_create: %s\n", fname); #endif if ((mf.fp = fopen(fname, "w")) == NULL) err(1, "Could not open meta file '%s'", fname); fprintf(mf.fp, "# Meta data file %s\n", fname); mf.gn = gn; Lst_ForEach(gn->commands, printCMD, &mf); 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); } fprintf(mf.fp, "-- command output --\n"); fflush(mf.fp); Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); gn->type |= OP_META; /* in case anyone wants to know */ if (metaSilent) { gn->type |= OP_SILENT; } out: for (i--; i >= 0; i--) { - free(p[i]); + bmake_free(p[i]); } return mf.fp; } static Boolean boolValue(char *s) { switch(*s) { case '0': case 'N': case 'n': case 'F': case 'f': return FALSE; } return TRUE; } /* * Initialization we need before reading makefiles. */ void meta_init(void) { #ifdef USE_FILEMON /* this allows makefiles to test if we have filemon support */ Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL); #endif } #define get_mode_bf(bf, token) \ if ((cp = strstr(make_mode, token))) \ bf = boolValue(&cp[sizeof(token) - 1]) /* * Initialization we need after reading makefiles. */ void meta_mode_init(const char *make_mode) { static int once = 0; char *cp; useMeta = TRUE; useFilemon = TRUE; writeMeta = TRUE; if (make_mode) { if (strstr(make_mode, "env")) metaEnv = TRUE; if (strstr(make_mode, "verb")) metaVerbose = TRUE; if (strstr(make_mode, "read")) writeMeta = FALSE; if (strstr(make_mode, "nofilemon")) useFilemon = FALSE; if (strstr(make_mode, "ignore-cmd")) metaIgnoreCMDs = TRUE; if (useFilemon) get_mode_bf(filemonMissing, "missing-filemon="); get_mode_bf(metaCurdirOk, "curdirok="); get_mode_bf(metaMissing, "missing-meta="); get_mode_bf(metaSilent, "silent="); } if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { /* * The default value for MAKE_META_PREFIX * prints the absolute path of the target. * This works be cause :H will generate '.' if there is no / * and :tA will resolve that to cwd. */ Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL); } if (once) return; once = 1; memset(&Mybm, 0, sizeof(Mybm)); /* * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} */ - metaBailiwick = Lst_Init(FALSE); - metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", - VAR_GLOBAL, VARF_WANTRES); - if (metaBailiwickStr) { - str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL); - } + metaBailiwick = Lst_Init(); + metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", + VAR_GLOBAL, VARE_WANTRES); + str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL); /* * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} */ - metaIgnorePaths = Lst_Init(FALSE); + metaIgnorePaths = Lst_Init(); Var_Append(MAKE_META_IGNORE_PATHS, "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); - metaIgnorePathsStr = Var_Subst(NULL, - "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, - VARF_WANTRES); - if (metaIgnorePathsStr) { - str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL); - } + metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", + VAR_GLOBAL, VARE_WANTRES); + str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL); /* * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} */ cp = NULL; if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) { metaIgnorePatterns = TRUE; - free(cp); + bmake_free(cp); } cp = NULL; if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) { metaIgnoreFilter = TRUE; - free(cp); + bmake_free(cp); } } /* * In each case below we allow for job==NULL */ void meta_job_start(Job *job, GNode *gn) { BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } pbm->mfp = meta_create(pbm, gn); #ifdef USE_FILEMON_ONCE /* compat mode we open the filemon dev once per command */ if (job == NULL) return; #endif #ifdef USE_FILEMON if (pbm->mfp != NULL && useFilemon) { meta_open_filemon(pbm); } else { pbm->mon_fd = -1; pbm->filemon = NULL; } #endif } /* * The child calls this before doing anything. * It does not disturb our state. */ void meta_job_child(Job *job) { #ifdef USE_FILEMON BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { close(fileno(pbm->mfp)); if (useFilemon && pbm->filemon) { pid_t pid; pid = getpid(); if (filemon_setpid_child(pbm->filemon, pid) == -1) { err(1, "Could not set filemon pid!"); } } } #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) { char cwd[MAXPATHLEN]; BuildMon *pbm; if (job != NULL) { pbm = &job->bm; if (!gn) gn = job->node; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { fprintf(pbm->mfp, "\n*** Error code %d%s\n", status, (flags & JOB_IGNERR) ? "(ignored)" : ""); } if (gn) { Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL); } getcwd(cwd, sizeof(cwd)); Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL); if (pbm->meta_fname[0]) { Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL); } meta_job_finish(job); } void meta_job_output(Job *job, char *cp, const char *nl) { BuildMon *pbm; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { if (metaVerbose) { static char *meta_prefix = NULL; static int meta_prefix_len; if (!meta_prefix) { char *cp2; - meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", - VAR_GLOBAL, VARF_WANTRES); + meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}", + VAR_GLOBAL, VARE_WANTRES); if ((cp2 = strchr(meta_prefix, '$'))) meta_prefix_len = cp2 - meta_prefix; else meta_prefix_len = strlen(meta_prefix); } if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { cp = strchr(cp+1, '\n'); if (!cp++) return; } } fprintf(pbm->mfp, "%s%s", cp, nl); } } int meta_cmd_finish(void *pbmp) { int error = 0; BuildMon *pbm = pbmp; #ifdef USE_FILEMON int x; #endif if (!pbm) pbm = &Mybm; #ifdef USE_FILEMON 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->mon_fd = -1; pbm->filemon = NULL; } else #endif fprintf(pbm->mfp, "\n"); /* ensure end with newline */ return error; } int meta_job_finish(Job *job) { BuildMon *pbm; int error = 0; int x; if (job != NULL) { pbm = &job->bm; } else { pbm = &Mybm; } if (pbm->mfp != NULL) { error = meta_cmd_finish(pbm); x = fclose(pbm->mfp); if (error == 0 && x != 0) error = errno; pbm->mfp = NULL; pbm->meta_fname[0] = '\0'; } return error; } void meta_finish(void) { - Lst_Destroy(metaBailiwick, NULL); + if (metaBailiwick != NULL) + Lst_Free(metaBailiwick); free(metaBailiwickStr); - Lst_Destroy(metaIgnorePaths, NULL); + if (metaIgnorePaths != NULL) + Lst_Free(metaIgnorePaths); free(metaIgnorePathsStr); } /* * Fetch a full line from fp - growing bufp if needed * Return length in bufp. */ static int fgetLine(char **bufp, size_t *szp, int o, FILE *fp) { char *buf = *bufp; size_t bufsz = *szp; struct stat fs; int x; if (fgets(&buf[o], bufsz - o, fp) != NULL) { check_newline: x = o + strlen(&buf[o]); if (buf[x - 1] == '\n') return x; /* * We need to grow the buffer. * The meta file can give us a clue. */ if (fstat(fileno(fp), &fs) == 0) { size_t newsz; char *p; newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); if (newsz <= bufsz) newsz = ROUNDUP(fs.st_size, BUFSIZ); if (newsz <= bufsz) return x; /* truncated */ if (DEBUG(META)) fprintf(debug_file, "growing buffer %u -> %u\n", (unsigned)bufsz, (unsigned)newsz); p = bmake_realloc(buf, newsz); if (p) { *bufp = buf = p; *szp = bufsz = newsz; /* fetch the rest */ if (!fgets(&buf[x], bufsz - x, fp)) return x; /* truncated! */ goto check_newline; } } } return 0; } /* Lst_ForEach wants 1 to stop search */ static int prefix_match(void *p, void *q) { const char *prefix = p; const char *path = q; size_t n = strlen(prefix); return strncmp(path, prefix, n) == 0; } -/* - * looking for exact or prefix/ match to - * Lst_Find wants 0 to stop search - */ -static int +/* See if the path equals prefix or starts with "prefix/". */ +static Boolean path_match(const void *p, const void *q) { - const char *prefix = q; const char *path = p; + const char *prefix = q; size_t n = strlen(prefix); - int rc; - if ((rc = strncmp(path, prefix, n)) == 0) { - switch (path[n]) { - case '\0': - case '/': - break; - default: - rc = 1; - break; - } - } - return rc; + if (strncmp(path, prefix, n) != 0) + return FALSE; + return path[n] == '\0' || path[n] == '/'; } -/* Lst_Find wants 0 to stop search */ -static int +static Boolean string_match(const void *p, const void *q) { - const char *p1 = p; - const char *p2 = q; - - return strcmp(p1, p2); + return strcmp(p, q) == 0; } static int meta_ignore(GNode *gn, const char *p) { char fname[MAXPATHLEN]; if (p == NULL) return TRUE; if (*p == '/') { cached_realpath(p, fname); /* clean it up */ if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring path: %s\n", p); #endif return TRUE; } } if (metaIgnorePatterns) { + const char *expr; char *pm; Var_Set(".p.", p, gn); - pm = Var_Subst(NULL, - "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}", - gn, VARF_WANTRES); + expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; + pm = Var_Subst(expr, gn, VARE_WANTRES); if (*pm) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n", p); #endif free(pm); return TRUE; } free(pm); } if (metaIgnoreFilter) { char *fm; /* skip if filter result is empty */ snprintf(fname, sizeof(fname), "${%s:L:${%s:ts:}}", p, MAKE_META_IGNORE_FILTER); - fm = Var_Subst(NULL, fname, gn, VARF_WANTRES); + fm = Var_Subst(fname, gn, VARE_WANTRES); if (*fm == '\0') { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n", p); #endif free(fm); return TRUE; } free(fm); } return FALSE; } /* * When running with 'meta' functionality, a target can be out-of-date * if any of the references in its meta data file is more recent. * We have to track the latestdir on a per-process basis. */ #define LCWD_VNAME_FMT ".meta.%d.lcwd" #define LDIR_VNAME_FMT ".meta.%d.ldir" /* * It is possible that a .meta file is corrupted, * if we detect this we want to reproduce it. * Setting oodate TRUE will have that effect. */ #define CHECK_VALID_META(p) if (!(p && *p)) { \ warnx("%s: %d: malformed", fname, lineno); \ oodate = TRUE; \ continue; \ } #define DEQUOTE(p) if (*p == '\'') { \ char *ep; \ p++; \ if ((ep = strchr(p, '\''))) \ *ep = '\0'; \ } Boolean meta_oodate(GNode *gn, Boolean oodate) { static char *tmpdir = NULL; static char cwd[MAXPATHLEN]; char lcwd_vname[64]; char ldir_vname[64]; char lcwd[MAXPATHLEN]; char latestdir[MAXPATHLEN]; char fname[MAXPATHLEN]; char fname1[MAXPATHLEN]; char fname2[MAXPATHLEN]; char fname3[MAXPATHLEN]; const char *dname; const char *tname; char *p; char *cp; char *link_src; char *move_target; static size_t cwdlen = 0; static size_t tmplen = 0; FILE *fp; Boolean needOODATE = FALSE; Lst missingFiles; char *pa[4]; /* >= possible uses */ int i; int have_filemon = FALSE; if (oodate) return oodate; /* we're done */ i = 0; dname = Var_Value(".OBJDIR", gn, &pa[i++]); tname = Var_Value(TARGET, gn, &pa[i++]); /* if this succeeds fname3 is realpath of dname */ if (!meta_needed(gn, dname, fname3, FALSE)) goto oodate_out; dname = fname3; - missingFiles = Lst_Init(FALSE); + missingFiles = Lst_Init(); /* * We need to check if the target is out-of-date. This includes * checking if the expanded command has changed. This in turn * requires that all variables are set in the same way that they * would be if the target needs to be re-built. */ Make_DoAllVar(gn); meta_name(fname, sizeof(fname), dname, tname, dname); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: %s\n", fname); #endif if ((fp = fopen(fname, "r")) != NULL) { static char *buf = NULL; static size_t bufsz; int lineno = 0; int lastpid = 0; int pid; int x; LstNode ln; - struct stat fs; + struct make_stat mst; if (!buf) { bufsz = 8 * BUFSIZ; buf = bmake_malloc(bufsz); } if (!cwdlen) { if (getcwd(cwd, sizeof(cwd)) == NULL) err(1, "Could not get current working directory"); cwdlen = strlen(cwd); } strlcpy(lcwd, cwd, sizeof(lcwd)); strlcpy(latestdir, cwd, sizeof(latestdir)); if (!tmpdir) { tmpdir = getTmpdir(); tmplen = strlen(tmpdir); } /* we want to track all the .meta we read */ Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); ln = Lst_First(gn->commands); while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { lineno++; if (buf[x - 1] == '\n') buf[x - 1] = '\0'; else { warnx("%s: %d: line truncated at %u", fname, lineno, x); oodate = TRUE; break; } link_src = NULL; move_target = NULL; /* Find the start of the build monitor section. */ if (!have_filemon) { if (strncmp(buf, "-- filemon", 10) == 0) { have_filemon = TRUE; continue; } if (strncmp(buf, "# buildmon", 10) == 0) { have_filemon = TRUE; continue; } } /* Delimit the record type. */ p = buf; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); #endif strsep(&p, " "); if (have_filemon) { /* * We are in the 'filemon' output section. * Each record from filemon follows the general form: * * * * Where: * is a single letter, denoting the syscall. * is the process that made the syscall. * is the arguments (of interest). */ switch(buf[0]) { case '#': /* comment */ case 'V': /* version */ break; default: /* * We need to track pathnames per-process. * * Each process run by make, starts off in the 'CWD' * recorded in the .meta file, if it chdirs ('C') * elsewhere we need to track that - but only for * that process. If it forks ('F'), we initialize * the child to have the same cwd as its parent. * * We also need to track the 'latestdir' of * interest. This is usually the same as cwd, but * not if a process is reading directories. * * Each time we spot a different process ('pid') * we save the current value of 'latestdir' in a * variable qualified by 'lastpid', and * re-initialize 'latestdir' to any pre-saved * value for the current 'pid' and 'CWD' if none. */ CHECK_VALID_META(p); pid = atoi(p); if (pid > 0 && pid != lastpid) { - char *ldir; + const char *ldir; char *tp; if (lastpid > 0) { /* We need to remember these. */ Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); Var_Set(ldir_vname, latestdir, VAR_GLOBAL); } snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid); snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); lastpid = pid; ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(latestdir, ldir, sizeof(latestdir)); - free(tp); + bmake_free(tp); } ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); if (ldir) { strlcpy(lcwd, ldir, sizeof(lcwd)); - free(tp); + bmake_free(tp); } } /* Skip past the pid. */ if (strsep(&p, " ") == NULL) continue; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, pid, buf[0], cwd, lcwd, latestdir); #endif break; } CHECK_VALID_META(p); /* Process according to record type. */ switch (buf[0]) { case 'X': /* eXit */ Var_Delete(lcwd_vname, VAR_GLOBAL); Var_Delete(ldir_vname, VAR_GLOBAL); lastpid = 0; /* no need to save ldir_vname */ break; case 'F': /* [v]Fork */ { char cldir[64]; int child; child = atoi(p); if (child > 0) { snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child); Var_Set(cldir, lcwd, VAR_GLOBAL); snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); Var_Set(cldir, latestdir, VAR_GLOBAL); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n", fname, lineno, child, cwd, lcwd, latestdir); #endif } } break; case 'C': /* Chdir */ /* Update lcwd and latest directory. */ strlcpy(latestdir, p, sizeof(latestdir)); strlcpy(lcwd, p, sizeof(lcwd)); Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); Var_Set(ldir_vname, lcwd, VAR_GLOBAL); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); #endif break; case 'M': /* renaMe */ /* * For 'M'oves we want to check * the src as for 'R'ead * and the target as for 'W'rite. */ cp = p; /* save this for a second */ /* now get target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); move_target = p; p = cp; /* 'L' and 'M' put single quotes around the args */ DEQUOTE(p); DEQUOTE(move_target); /* FALLTHROUGH */ case 'D': /* unlink */ if (*p == '/' && !Lst_IsEmpty(missingFiles)) { /* remove any missingFiles entries that match p */ - if ((ln = Lst_Find(missingFiles, p, - path_match)) != NULL) { + ln = Lst_Find(missingFiles, path_match, p); + if (ln != NULL) { LstNode nln; char *tp; do { - nln = Lst_FindFrom(missingFiles, Lst_Succ(ln), - p, path_match); - tp = Lst_Datum(ln); + nln = Lst_FindFrom(missingFiles, + LstNode_Next(ln), + path_match, p); + tp = LstNode_Datum(ln); Lst_Remove(missingFiles, ln); free(tp); } while ((ln = nln) != NULL); } } if (buf[0] == 'M') { /* the target of the mv is a file 'W'ritten */ #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: M %s -> %s\n", p, move_target); #endif p = move_target; goto check_write; } break; case 'L': /* Link */ /* * For 'L'inks check * the src as for 'R'ead * and the target as for 'W'rite. */ link_src = p; /* now get target */ if (strsep(&p, " ") == NULL) continue; CHECK_VALID_META(p); /* 'L' and 'M' put single quotes around the args */ DEQUOTE(p); DEQUOTE(link_src); #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: L %s -> %s\n", link_src, p); #endif /* FALLTHROUGH */ case 'W': /* Write */ check_write: /* * If a file we generated within our bailiwick * but outside of .OBJDIR is missing, * we need to do it again. */ /* ignore non-absolute paths */ if (*p != '/') break; if (Lst_IsEmpty(metaBailiwick)) break; /* ignore cwd - normal dependencies handle those */ if (strncmp(p, cwd, cwdlen) == 0) break; if (!Lst_ForEach(metaBailiwick, prefix_match, p)) break; /* tmpdir might be within */ if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) break; /* ignore anything containing the string "tmp" */ if ((strstr("tmp", p))) break; - if ((link_src != NULL && cached_lstat(p, &fs) < 0) || - (link_src == NULL && cached_stat(p, &fs) < 0)) { + if ((link_src != NULL && cached_lstat(p, &mst) < 0) || + (link_src == NULL && cached_stat(p, &mst) < 0)) { if (!meta_ignore(gn, p)) { - if (Lst_Find(missingFiles, p, string_match) == NULL) - Lst_AtEnd(missingFiles, bmake_strdup(p)); + if (Lst_Find(missingFiles, string_match, p) == NULL) + Lst_Append(missingFiles, bmake_strdup(p)); } } break; check_link_src: p = link_src; link_src = NULL; #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "meta_oodate: L src %s\n", p); #endif /* FALLTHROUGH */ case 'R': /* Read */ case 'E': /* Exec */ /* * Check for runtime files that can't * be part of the dependencies because * they are _expected_ to change. */ if (meta_ignore(gn, p)) break; /* * The rest of the record is the file name. * Check if it's not an absolute path. */ { char *sdirs[4]; char **sdp; int sdx = 0; int found = 0; if (*p == '/') { sdirs[sdx++] = p; /* done */ } else { if (strcmp(".", p) == 0) continue; /* no point */ /* Check vs latestdir */ snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); sdirs[sdx++] = fname1; if (strcmp(latestdir, lcwd) != 0) { /* Check vs lcwd */ snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p); sdirs[sdx++] = fname2; } if (strcmp(lcwd, cwd) != 0) { /* Check vs cwd */ snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p); sdirs[sdx++] = fname3; } } sdirs[sdx++] = NULL; for (sdp = sdirs; *sdp && !found; sdp++) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); #endif - if (cached_stat(*sdp, &fs) == 0) { + if (cached_stat(*sdp, &mst) == 0) { found = 1; p = *sdp; } } if (found) { #ifdef DEBUG_META_MODE if (DEBUG(META)) fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); #endif - if (!S_ISDIR(fs.st_mode) && - fs.st_mtime > gn->mtime) { + if (!S_ISDIR(mst.mst_mode) && + mst.mst_mtime > gn->mtime) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); oodate = TRUE; - } else if (S_ISDIR(fs.st_mode)) { + } else if (S_ISDIR(mst.mst_mode)) { /* Update the latest directory. */ cached_realpath(p, latestdir); } } else if (errno == ENOENT && *p == '/' && strncmp(p, cwd, cwdlen) != 0) { /* * A referenced file outside of CWD is missing. * We cannot catch every eventuality here... */ - if (Lst_Find(missingFiles, p, string_match) == NULL) - Lst_AtEnd(missingFiles, bmake_strdup(p)); + if (Lst_Find(missingFiles, string_match, p) == NULL) + Lst_Append(missingFiles, bmake_strdup(p)); } } if (buf[0] == 'E') { /* previous latestdir is no longer relevant */ strlcpy(latestdir, lcwd, sizeof(latestdir)); } break; default: break; } if (!oodate && buf[0] == 'L' && link_src != NULL) goto check_link_src; } else if (strcmp(buf, "CMD") == 0) { /* * Compare the current command with the one in the * meta data file. */ if (ln == NULL) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); oodate = TRUE; } else { - char *cmd = (char *)Lst_Datum(ln); + char *cmd = LstNode_Datum(ln); Boolean hasOODATE = FALSE; if (strstr(cmd, "$?")) hasOODATE = TRUE; else if ((cp = strstr(cmd, ".OODATE"))) { /* check for $[{(].OODATE[:)}] */ if (cp > cmd + 2 && cp[-2] == '$') hasOODATE = TRUE; } if (hasOODATE) { needOODATE = TRUE; if (DEBUG(META)) fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); } - cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR); + cmd = Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR); if ((cp = strchr(cmd, '\n'))) { int n; /* * This command contains newlines, we need to * fetch more from the .meta file before we * attempt a comparison. */ /* first put the newline back at buf[x - 1] */ buf[x - 1] = '\n'; do { /* now fetch the next line */ if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) break; x = n; lineno++; if (buf[x - 1] != '\n') { warnx("%s: %d: line truncated at %u", fname, lineno, x); break; } cp = strchr(++cp, '\n'); } while (cp); if (buf[x - 1] == '\n') buf[x - 1] = '\0'; } if (p && !hasOODATE && !(gn->type & OP_NOMETA_CMP) && strcmp(p, cmd) != 0) { if (DEBUG(META)) fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); if (!metaIgnoreCMDs) oodate = TRUE; } free(cmd); - ln = Lst_Succ(ln); + ln = LstNode_Next(ln); } } else if (strcmp(buf, "CWD") == 0) { /* * Check if there are extra commands now * that weren't in the meta data file. */ if (!oodate && ln != NULL) { if (DEBUG(META)) 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); oodate = TRUE; } } } fclose(fp); if (!Lst_IsEmpty(missingFiles)) { if (DEBUG(META)) fprintf(debug_file, "%s: missing files: %s...\n", - fname, (char *)Lst_Datum(Lst_First(missingFiles))); + fname, (char *)LstNode_Datum(Lst_First(missingFiles))); oodate = TRUE; } if (!oodate && !have_filemon && filemonMissing) { if (DEBUG(META)) fprintf(debug_file, "%s: missing filemon data\n", fname); oodate = TRUE; } } else { if (writeMeta && (metaMissing || (gn->type & OP_META))) { cp = NULL; /* if target is in .CURDIR we do not need a meta file */ if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) { if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) { cp = NULL; /* not in .CURDIR */ } } if (!cp) { if (DEBUG(META)) fprintf(debug_file, "%s: required but missing\n", fname); oodate = TRUE; needOODATE = TRUE; /* assume the worst */ } } } - Lst_Destroy(missingFiles, (FreeProc *)free); + Lst_Destroy(missingFiles, free); if (oodate && needOODATE) { /* * Target uses .OODATE which is empty; or we wouldn't be here. * We have decided it is oodate, so .OODATE needs to be set. * All we can sanely do is set it to .ALLSRC. */ Var_Delete(OODATE, gn); Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn); - free(cp); + bmake_free(cp); } oodate_out: for (i--; i >= 0; i--) { - free(pa[i]); + bmake_free(pa[i]); } return oodate; } /* support for compat mode */ static int childPipe[2]; void meta_compat_start(void) { #ifdef USE_FILEMON_ONCE /* * We need to re-open filemon for each cmd. */ BuildMon *pbm = &Mybm; if (pbm->mfp != NULL && useFilemon) { meta_open_filemon(pbm); } else { pbm->mon_fd = -1; pbm->filemon = NULL; } #endif if (pipe(childPipe) < 0) Punt("Cannot create pipe: %s", strerror(errno)); /* Set close-on-exec flag for both */ (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC); (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC); } void meta_compat_child(void) { meta_job_child(NULL); if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0) { execError("dup2", "pipe"); _exit(1); } } void meta_compat_parent(pid_t child) { int outfd, metafd, maxfd, nfds; char buf[BUFSIZ+1]; fd_set readfds; meta_job_parent(NULL, child); close(childPipe[1]); /* child side */ 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; } } } #endif /* USE_META */ Index: head/contrib/bmake/metachar.c =================================================================== --- head/contrib/bmake/metachar.c (revision 365365) +++ head/contrib/bmake/metachar.c (revision 365366) @@ -1,88 +1,88 @@ -/* $NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $ */ +/* $NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * 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. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #if defined(MAKE_NATIVE) || defined(HAVE_NBTOOL_CONFIG_H) #include #endif #if defined(__RCSID) && !defined(lint) -__RCSID("$NetBSD: metachar.c,v 1.5 2015/06/19 08:03:35 mlelstv Exp $"); +__RCSID("$NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $"); #endif #include "metachar.h" /* * The following array is used to make a fast determination of which * characters are interpreted specially by the shell. If a command * contains any of these characters, it is executed by the shell, not * directly by us. * * perhaps move it to ctype? */ unsigned char _metachar[128] = { -// nul soh stx etx eot enq ack bel +/* nul soh stx etx eot enq ack bel */ 1, 0, 0, 0, 0, 0, 0, 0, -// bs ht nl vt np cr so si +/* bs ht nl vt np cr so si */ 0, 0, 1, 0, 0, 0, 0, 0, -// dle dc1 dc2 dc3 dc4 nak syn etb +/* dle dc1 dc2 dc3 dc4 nak syn etb */ 0, 0, 0, 0, 0, 0, 0, 0, -// can em sub esc fs gs rs us +/* can em sub esc fs gs rs us */ 0, 0, 0, 0, 0, 0, 0, 0, -// sp ! " # $ % & ' +/* sp ! " # $ % & ' */ 0, 1, 1, 1, 1, 0, 1, 1, -// ( ) * + , - . / +/* ( ) * + , - . / */ 1, 1, 1, 0, 0, 0, 0, 0, -// 0 1 2 3 4 5 6 7 +/* 0 1 2 3 4 5 6 7 */ 0, 0, 0, 0, 0, 0, 0, 0, -// 8 9 : ; < = > ? +/* 8 9 : ; < = > ? */ 0, 0, 0, 1, 1, 0, 1, 1, -// @ A B C D E F G +/* @ A B C D E F G */ 0, 0, 0, 0, 0, 0, 0, 0, -// H I J K L M N O +/* H I J K L M N O */ 0, 0, 0, 0, 0, 0, 0, 0, -// P Q R S T U V W +/* P Q R S T U V W */ 0, 0, 0, 0, 0, 0, 0, 0, -// X Y Z [ \ ] ^ _ +/* X Y Z [ \ ] ^ _ */ 0, 0, 0, 1, 1, 1, 1, 0, -// ` a b c d e f g +/* ` a b c d e f g */ 1, 0, 0, 0, 0, 0, 0, 0, -// h i j k l m n o +/* h i j k l m n o */ 0, 0, 0, 0, 0, 0, 0, 0, -// p q r s t u v w +/* p q r s t u v w */ 0, 0, 0, 0, 0, 0, 0, 0, -// x y z { | } ~ del +/* x y z { | } ~ del */ 0, 0, 0, 1, 1, 1, 1, 0, }; Index: head/contrib/bmake/metachar.h =================================================================== --- head/contrib/bmake/metachar.h (revision 365365) +++ head/contrib/bmake/metachar.h (revision 365366) @@ -1,61 +1,52 @@ -/* $NetBSD: metachar.h,v 1.4 2015/06/21 20:26:02 christos Exp $ */ +/* $NetBSD: metachar.h,v 1.7 2020/08/25 17:37:09 rillig Exp $ */ /*- * Copyright (c) 2015 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * 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 _METACHAR_H -#define _METACHAR_H +#ifndef MAKE_METACHAR_H +#define MAKE_METACHAR_H -#include +#include "make.h" extern unsigned char _metachar[]; #define ismeta(c) _metachar[(c) & 0x7f] -static inline int -hasmeta(const char *cmd) -{ - while (!ismeta(*cmd)) - cmd++; - - return *cmd != '\0'; -} - -static inline int +static inline int MAKE_ATTR_UNUSED needshell(const char *cmd, int white) { while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') { if (white && isspace((unsigned char)*cmd)) break; cmd++; } return *cmd != '\0'; } -#endif /* _METACHAR_H */ +#endif /* MAKE_METACHAR_H */ Index: head/contrib/bmake/mk/ChangeLog =================================================================== --- head/contrib/bmake/mk/ChangeLog (revision 365365) +++ head/contrib/bmake/mk/ChangeLog (revision 365366) @@ -1,1578 +1,1681 @@ +2020-08-26 Simon J Gerraty + + * dirdeps.mk: ensure we cannot confuse a static cache for dynamic + (even more rare that use of static cache is playing clever tricks + with it) + +2020-08-16 Simon J Gerraty + + * dirdeps-cache-update.mk: allow + MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE to control when we + actually update STATIC_DIRDEPS_CACHE. + + * stage-install.sh: create dest directory if needed + before running install(1) + +2020-08-10 Simon J Gerraty + + * dirdeps-targets.mk: include Makefile.dirdeps.options + + * dirdeps.mk: use _TARGETS if defined for DIRDEPS_CACHE + +2020-08-09 Simon J Gerraty + + * dirdeps.mk: default BUILD_DIRDEPS_MAKEFILE to empty + + * dirdeps-cache-update.mk: building parallel cache update + under the context of dirdeps-cached would be ideal, but + is problematic, so it runs as a sibling. + Use cache-built target to ensure we wait for it to complete if + necessary. + +2020-08-06 Simon J Gerraty + + * install-mk (MK_VERSION): 20200806 + + * dirdeps-options: allow TARGET_SPEC to affect option values. + Use DIRDEPS_OPTIONS_QUALIFIER_LIST before using bare MK_* + + * dirdeps-targets.mk: check for MK_STATIC_DIRDEPS_CACHE defined + before looking for STATIC_DIRDEPS_CACHE + +2020-08-05 Simon J Gerraty + + * host-target.mk: Darwin use MACHINE for HOST_ARCH too + + * dirdeps-options.mk: improve debug output + +2020-07-22 Simon J Gerraty + + * dirdeps.mk: set and export DYNAMIC_DIRDEPS_CACHE + for use by dirdeps-cache-update.mk + + * dirdeps-targets.mk: set and export STATIC_DIRDEPS_CACHE + for use by dirdeps-cache-update.mk even if we don't use it. + + * dirdeps-cache-update.mk: we only need worry about the background + update case, with the above, the update from DIRDEPS_CACHE is + simple. + + * meta2deps.py: R 1234 . is not interesting + +2020-07-20 Simon J Gerraty + + * sys.mk: default MK_STATIC_DIRDEPS_CACHE from MK_DIRDEPS_CACHE + + * dirdeps-options.mk: do not :tu DIRDEPS_OPTIONS + allows use of lower case for pseudo options. + + * dirdeps-cache-update.mk: magic to deal with STATIC_DIRDEPS_CACHE + +2020-07-18 Simon J Gerraty + + * dirdeps-targets.mk: Look for Makefile.dirdeps.cache + which allows us to have a static cache for expensive targets. + Use -DWITHOUT_STATIC_DIRDEPS_CACHE -DWITH_DIRDEPS_CACHE + to regenerate the dirdeps.cache it is a copy of. + +2020-07-17 Simon J Gerraty + + * Get rid of BUILD_AT_LEVEL0, MK_DIRDEPS_BUILD makes more sense. + +2020-07-16 Simon J Gerraty + + * dirdeps.mk (DIRDEP_LOADAVG_REPORT): make it easy to record + load averages at intervals during build. + +2020-07-15 Simon J Gerraty + + * install-mk (MK_VERSION): 20200715 + + * dirdeps.mk: tweak Checking line to make matching Finished + lines for post-build analysis easier. + + * meta.autodep.mk: use !defined(WITHOUT_META_STATS) + + * progs.mk: avoid prog.mk outputting multiple Finished lines + +2020-07-11 Simon J Gerraty + + * dirdeps.mk: further optimize dirdeps.cache + generate a DIRDEPS.${.TARGET} list for other purposes + and improve the layout. + 2020-07-10 Simon J Gerraty * dirdeps.mk: optimize content of dirdeps.cache 2020-06-28 Simon J Gerraty * sys/*.mk: make it easier for local*sys.mk to customize by using ?= 2020-06-22 Simon J Gerraty * gendirdeps.mk (LOCAL_DEPENDS_GUARD): if we don't build at level 0 it is much safer to guard local depends with a simple check for .MAKE.LEVEL > 0 2020-06-10 Simon J Gerraty * install-mk (MK_VERSION): 20200610 * mkopt.sh: this needs posix shell so #!/bin/sh should be ok 2020-06-06 Simon J Gerraty * install-mk (MK_VERSION): 20200606 * dirdeps-targets.mk: allow for filtering of .TARGETS * meta2deps.py: fix bug in processing 'L'ink and 'M'ove entries - and we don't care about 'W'rite entries. Also ignore absolute paths that do not exist. 2020-05-25 Simon J Gerraty * install-mk (MK_VERSION): 20200525 * init.mk: expand and simplify handling of qualified vars like CPPFLAGS.${.TARGET:T} 2020-05-15 Simon J Gerraty * 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 * whats.mk: more easily extensible 2020-05-02 Simon J Gerraty * whats.mk: greatly simplify by adding what.c to SRCS 2020-05-01 Simon J Gerraty * whats.mk: for libs take care how we add to *OBJS * lib.mk: : works better with whats.mk 2020-04-25 Simon J Gerraty * 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 * 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 * 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 * dirdeps.mk _DIRDEP_USE: use DIRDEP_DIR and add DIRDEP_USE_PRELUDE at start - facilitates job distribution 2019-10-04 Simon J Gerraty * 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 * dirdeps-targets.mk: encapsulate logic for finding top-level targets to set initial DIRDEPS for DIRDEPS_BUILD 2019-09-27 Simon J Gerraty * install-mk (MK_VERSION): 20190911 * compiler.mk: set COMPILER_TYPE 2019-07-17 Simon J Gerraty * install-mk (MK_VERSION): 20190704 * sys/Darwin.mk: support for Objective-C and clang 2019-05-30 Simon J Gerraty * dirdeps.mk: avoid insanely long command line when generating cache 2019-05-23 Simon J Gerraty * install-mk (MK_VERSION): 20190505 * whats.mk: handle corner case SHLIB defined but not LIB 2018-09-19 Simon J Gerraty * install-mk (MK_VERSION): 20180919 * dirdeps-options.mk: .undef cannot handle var that expands to - more than one var. + more than one var. 2018-07-08 Simon J Gerraty * meta.stage.mk: allow wildcards in STAGE_FILES.* etc. 2018-06-01 Simon J Gerraty * meta.autodep.mk: export META_FILES to avoid command line limit * gendirdeps.mk: if we have lots of .meta files put them in an @list 2018-05-28 Simon J Gerraty * dirdeps-options.mk: use local.dirdeps-options.mk not local.dirdeps-option.mk 2018-04-20 Simon J Gerraty * install-mk (MK_VERSION): 20180420 * dirdeps.mk: include local.dirdeps-build.mk when .MAKE.LEVEL > 0 ie. we are building something. 2018-04-14 Simon J Gerraty * FILES: add dirdeps-options.mk to deal with optional DIRDEPS. 2018-04-05 Simon J Gerraty * install-mk (MK_VERSION): 20180405 * ldorder.mk: describe how to use LDORDER_EXTERN_BARRIER if needed. 2018-01-18 Simon J Gerraty * install-mk (MK_VERSION): 20180118 * ldorder.mk: let make compute correct link order 2017-12-12 Simon J Gerraty * install-mk (MK_VERSION): 20171212 * gendirdeps.mk: guard against bogus entries in GENDIRDEPS_FILTER 2017-11-14 Simon J. Gerraty * install-mk (MK_VERSION): 20171111 * lib.mk: ensure META_NOECHO is set 2017-10-25 Simon J. Gerraty * Allow for host32 on rare occasions. 2017-10-18 Simon J. Gerraty * install-mk (MK_VERSION): 20171018 * whats.mk: include what_thing in what_uuid to avoid problem when building multiple apps in the same directory. 2017-08-12 Simon J. Gerraty * install-mk (MK_VERSION): 20170812 * autoconf.mk: Use CONFIGURE_DEPS so Makefile can add dependencies for config.recheck and config.gen 2017-06-30 Simon J. Gerraty * install-mk (MK_VERSION): 20170630 * meta.stage.mk: avoid triggering stage_* targets with nothing to do. 2017-05-23 Simon J. Gerraty * meta2deps.py: take special care of '..' 2017-05-15 Simon J. Gerraty * install-mk (MK_VERSION): 20170515 * dirdeps.mk (DEP_EXPORT_VARS): on rare occasions it is useful/necessary for a Makefile.depend file to export some knobs. This is complicated when we are doing DIRDEPS_CACHE, so we will handle export of any variables listed in DEP_EXPORT_VARS. 2017-05-08 Simon J. Gerraty * install-mk (MK_VERSION): 20170505 * meta2deps.py: fix botched indenation. 2017-05-05 Simon J. Gerraty * sys/*.mk: Remove setting of MAKE it is unnecessary and in many cases wrong (basname rather than full path) * scripts.mk (SCRIPTSGROUPS): make this more like files.mk and inc.mk * init.mk: define realbuild to simplify logic in {lib,prog}.mk etc 2017-05-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170501 * doc.mk: fix typo in DOC_INSTALL_OWN * inc.mk: handle INCGROUPS similar to freebsd * files.mk: add something for files too * add staging logic to lib.mk prog.mk etc. 2017-04-24 Simon J. Gerraty * install-mk (MK_VERSION): 20170424 * dirdeps.mk: set NO_DIRDEPS when bootstrapping. also target of bootstrap-this when sed is needed should be ${_want:T} 2017-04-18 Simon J. Gerraty * install-mk (MK_VERSION): 20170418 * auto.obj.mk: if using MAKEOBJDIRPREFIX check if it is a prefix match for .CURDIR - in which case .CURDIR *is* __objdir. 2017-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170401 * meta2deps.py: add is_src so we can check if obj dependency is also a src dependency. 2017-03-26 Simon J. Gerraty * install-mk (MK_VERSION): 20170326 * meta.stage.mk: do nothing if NO_STAGING is defined. 2017-03-24 Simon J. Gerraty * auto.obj.mk: handle the case of __objdir=obj or obj.${MACHINE} etc. 2017-03-18 Simon J. Gerraty * mkopt.sh: treat WITH_*=NO like no; ie. WITHOUT_* 2017-03-01 Simon J. Gerraty * install-mk (MK_VERSION): 20170301 * dirdeps.mk (_build_all_dirs): update this outside test for empty DIRDEPS. * meta.stage.mk: allow multiple inclusion to the extent it makes sense. 2017-02-14 Simon J. Gerraty * prog.mk (install_links): depends on realinstall 2017-02-12 Simon J. Gerraty * install-mk (MK_VERSION): 20170212 * dpadd.mk: avoid applying :T:R twice to DPLIBS entries 2017-01-30 Simon J. Gerraty * install-mk (MK_VERSION): 20170130 * dirdeps.mk: use :range if we can. * sys.vars.mk: provide M_cmpv if MAKE_VERSION >= 20170130 * meta2deps.py: clean paths without using realpath() where possible. fix sort_unique. 2016-12-12 Simon J. Gerraty * install-mk (MK_VERSION): 20161212 - - * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir, + + * meta2deps.py: set pid_cwd[pid] when we process 'C'hdir, rather than when we detect pid change. 2016-12-07 Simon J. Gerraty * install-mk (MK_VERSION): 20161207 * meta.stage.mk: add stage_as_and_symlink for staging packages. We build foo.tgz stage_as foo-${VERSION}.tgz but want to be able to use foo.tgz to reference the latest staged version - so we make foo.tgz a symlink to it. Using a target to do both operations ensures we stay in sync. - + 2016-11-26 Simon J. Gerraty * install-mk (MK_VERSION): 20161126 * dirdeps.mk: set DIRDEPS_CACHE before we include local.dirdeps.mk so it can add dependencies. 2016-10-10 Simon J. Gerraty * dirdeps.mk: set DEP_* before we expand .MAKE.DEPENDFILE_PREFERENCE do that they can influence the result correctly. * dirdeps.mk (${DIRDEPS_CACHE}): make sure we pass on TARGET_SPEC * dirdeps.mk: Add ONLY_TARGET_SPEC_LIST and NOT_TARGET_SPEC_LIST similar to ONLY_MACHINE_LIST and NOT_MACHINE_LIST 2016-10-05 Simon J. Gerraty * dirdeps.mk: remove dependence on jot (normal situations anyway). Before we read another Makefile.depend* set DEP_* vars from _DEP_TARGET_SPEC in case it uses any of them with := When bootstrapping, trim any ,* from extention of chosen _src Makefile.depend* to get the machine value we subst for. 2016-09-30 Simon J. Gerraty * dirdeps.mk: use TARGET_SPEC_VARS to qualify components added to DEP_SKIP_DIR and DEP_DIRDEPS_FILTER * sys.mk: extract some bits to sys.{debug,vars}.mk for easier re-use by others. 2016-09-23 Simon Gerraty * lib.mk: Use ${PICO} for extension for PIC objects. default to .pico (like NetBSD) safe on case insensitive filesystem. 2016-08-19 Simon J. Gerraty * meta.sys.mk (META_COOKIE_TOUCH): use ${.OBJDIR}/${.TARGET:T} as default 2016-08-15 Simon J. Gerraty * install-mk (MK_VERSION): 20160815 * dirdeps.mk (.MAKE.META.IGNORE_FILTER): set filter to only consider Makefile.depend* when checking if DIRDEPS_CACHE is up-to-date. 2016-08-13 Simon J. Gerraty - * meta.sys.mk (.MAKE.META.IGNORE_PATHS): + * meta.sys.mk (.MAKE.META.IGNORE_PATHS): in meta mode we can ignore the mtime of makefiles 2016-08-02 Simon J. Gerraty * install-mk (MK_VERSION): 20160802 - + * lib.mk (libinstall): depends on beforinstall * prog.mk (proginstall): depends on beforinstall patch from Lauri Tirkkonen - + * dirdeps.mk (bootstrap): When bootstrapping; creat .MAKE.DEPENDFILE_DEFAULT and allow additional filtering via .MAKE.DEPENDFILE_BOOTSTRAP_SED * dirdeps.mk: move some comments to where they make sense. 2016-07-27 Simon J. Gerraty * dirdeps.mk (DIRDEPS_CACHE): no dirname. 2016-06-02 Simon J. Gerraty * install-mk (MK_VERSION): 20160602 * meta.autodep.mk: when passing META_FILES to gendirdeps.mk do not apply :T to META_XTRAS patch from Bryan Drewery at FreeBSD.org. 2016-05-30 Simon J. Gerraty * install-mk (MK_VERSION): 20160530 * meta.stage.mk: we assume ${CLEANFILES} gets .NOPATH make it so. - + 2016-05-12 Simon J. Gerraty * install-mk (MK_VERSION): 20160512 * dpadd.mk: always include local.dpadd.mk if it exists remove some things that better belong in local.dpadd.mk skip INCLUDES_* for staged libs unless SRC_* defined. * own.mk: add INCLUDEDIR - + 2016-04-18 Simon J. Gerraty * dirdeps.mk: when doing -f dirdeps.mk if target suppies no TARGET_MACHINE - :E will be empty or match part of path, use ${MACHINE} 2016-04-07 Simon J. Gerraty * meta.autodep.mk: issue a warning if UPDATE_DEPENDFILE=NO due to - NO_FILEMON_COOKIE + NO_FILEMON_COOKIE - * dirdeps.mk: move the logic that allows for + * dirdeps.mk: move the logic that allows for make -f dirdeps.mk some/dir.${TARGET_SPEC} inside the check for !target(_DIRDEP_USE) 2016-04-04 Simon J. Gerraty * Use <> when including local*.mk and others which may exist elsewhere so that user can better control what they get. - - * meta.autodep.mk (NO_FILEMON_COOKIE): + + * meta.autodep.mk (NO_FILEMON_COOKIE): create a cookie if we ever build dir with nofilemon so that UPDATE_DEPENDFILE will be forced to NO until cleaned. 2016-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20160401 - + * meta2deps.py: fix old print statement when debugging. * gendirdeps.mk: META2DEPS_CMD append M2D_EXCLUDES with -X patch from Bryan Drewery 2016-03-22 Simon J. Gerraty * install-mk (MK_VERSION): 20160317 (St. Pats) - + * warnings.mk: g++ does not like -Wimplicit - + * sys.mk sys/*.mk lib.mk prog.mk: use CXX_SUFFIXES to handle the pelthora of common suffixes for C++ - + * lib.mk: use .So for shared objects 2016-03-15 Simon J. Gerraty * install-mk (MK_VERSION): 20160315 * meta.stage.mk (LN_CP_SCRIPT): do not ln(1) if we have to chmod(1) normally only applies to scripts. - + * dirdeps.mk: NO_DIRDEPS_BELOW to supress DIRDEPS below RELDIR as - well as outside it. + well as outside it. 2016-03-10 Simon J. Gerraty * install-mk (MK_VERSION): 20160310 * dirdeps.mk: use targets rather than a list to track DIRDEPS that we have processed; the list gets very inefficient as number of DIRDEPS gets large. * sys.dependfile.mk: fix comment wrt MACHINE * meta.autodep.mk: ignore staged DPADDs when bootstrapping. patch from Bryan Drewery 2016-03-02 Simon J. Gerraty * meta2deps.sh: don't ignore subdirs. patch from Bryan Drewery 2016-02-26 Simon J. Gerraty * install-mk (MK_VERSION): 20160226 * gendirdeps.mk: mark _DEPENDFILE .NOMETA 2016-02-20 Simon J. Gerraty * dirdeps.mk: we shouldn't normally include .depend but if we do use .dinclude if we can. 2016-02-18 Simon J. Gerraty * install-mk (MK_VERSION): 20160218 * sys.clean-env.mk: with recent change to Var_Subst() we cannot use the '$$' trick, but .export-literal does the job we need. * auto.dep.mk: make use .dinclude if we can. - + 2016-02-05 Simon J. Gerraty - * dirdeps.mk: + * dirdeps.mk: Add _build_all_dirs such that local.dirdeps.mk can add fully qualified dirs to it. - These will be built normally but the current + These will be built normally but the current DEP_RELDIR will not depend on then (to avoid cycles). This makes it easy to hook things like unit-tests into build. - + 2016-01-21 Simon J. Gerraty * dirdeps.mk: add bootstrap-empty 2015-12-12 Simon J. Gerraty * install-mk (MK_VERSION): 20151212 * auto.obj.mk: do not require MAKEOBJDIRPREFIX to exist. only apply :tA to __objdir when comparing to .OBJDIR 2015-11-14 Simon J. Gerraty * install-mk (MK_VERSION): 20151111 * meta.sys.mk: include sys.dependfile.mk * sys.mk (OPTIONS_DEFAULT_NO): use options.mk to set MK_AUTO_OBJ and MK_DIRDEPS_BUILD include local.sys.env.mk early include local.sys.mk later - + * own.mk (OPTIONS_DEFAULT_NO): AUTO_OBJ etc moved to sys.mk 2015-11-13 Simon J. Gerraty * meta.sys.mk (META_COOKIE_TOUCH): add ${META_COOKIE_TOUCH} to the end of scripts to touch cookie * meta.stage.mk: stage_libs should ignore SYMLINKS. 2015-10-23 Simon J. Gerraty * install-mk (MK_VERSION): 20151022 * sys.mk: BSD/OS does not have 'type' as a shell builtin. 2015-10-20 Simon J. Gerraty * install-mk (MK_VERSION): 20151020 - - * dirdeps.mk: Add logic for + + * dirdeps.mk: Add logic for make -f dirdeps.mk some/dir.${TARGET_SPEC} 2015-10-14 Simon J. Gerraty * install-mk (MK_VERSION): 20151010 2015-10-02 Simon J. Gerraty * meta.stage.mk: use staging: ${STAGE_TARGETS:... to have stage_lins run last in non-jobs mode. Use .ORDER only for jobs mode. 2015-09-02 Simon J. Gerraty * rst2htm.mk: allow for per target flags etc. 2015-09-01 Simon J. Gerraty * install-mk (MK_VERSION): 20150901 * doc.mk: create dir if needed use DOC_INSTALL_OWN 2015-06-15 Simon J. Gerraty * install-mk (MK_VERSION): 20150615 - + * auto.obj.mk: allow use of MAKEOBJDIRPREFIX too. Follow make's normal precedence rules. - + * gendirdeps.mk: allow customization of the header. - eg. for FreeBSD: + eg. for FreeBSD: GENDIRDEPS_HEADER= echo '\# ${FreeBSD:L:@v@$$$v$$ @:M*F*}'; * meta.autodep.mk: ignore dirdeps.cache* - + * meta.stage.mk: when bootstrapping options it can be handy to throw warnings rather than errors for staging conflicts. * meta.sys.mk: include local.meta.sys.mk for customization 2015-06-06 Simon J. Gerraty * install-mk (MK_VERSION): 20150606 - + * dirdeps.mk: don't rely on manually maintained Makefile.depend to set DEP_RELDIR and reset DIRDEPS. By setting DEP_RELDIR ourselves we can skip :tA - + * gendirdeps.mk: skip setting DEP_RELDIR. 2015-05-24 Simon J. Gerraty * dirdeps.mk: avoid wildcards like make(bootstrap*) 2015-05-20 Simon J. Gerraty * install-mk (MK_VERSION): 20150520 * dirdeps.mk: when we are building dirdeps cache file we *want* meta_oodate to look at all the Makefile.depend files, so set .MAKE.DEPENDFILE to something that won't match. * meta.stage.mk: for STAGE_AS_* basename of file may not be unique so first use absolute path as key. - Also skip staging at level 0. + Also skip staging at level 0. 2015-04-30 Simon J. Gerraty * install-mk (MK_VERSION): 20150430 - + * dirdeps.mk: fix _count_dirdeps for non-cache case. 2015-04-16 Simon J. Gerraty * install-mk (MK_VERSION): 20150411 bump version - + * own.mk: put AUTO_OBJ in OPTIONS_DEFAULT_NO rather than YES. it is here mainly for documentation purposes, since if using auto.obj.mk it is better done via sys.mk 2015-04-01 Simon J. Gerraty * install-mk (MK_VERSION): 20150401 - + * meta2deps.sh: support @list - + * meta2deps.py: updates from Juniper - o add EXCLUDES + o add EXCLUDES o skip bogus input files. o treat 'M' and 'L' as both an 'R' and a 'W' 2015-03-03 Simon J. Gerraty * install-mk (MK_VERSION): 20150303 - + * dirdeps.mk: if MK_DIRDEPS_CACHE is yes, use dirdeps-cache which is built via sub-make so we have a .meta file to tell if - it is out-of-date. + it is out-of-date. The dirdeps-cache contains the same dependency rules that we normaly construct on the fly. This adds a few seconds overhead when the cache is out of date, but for a large target, the savings can be significant (10-20min). 2014-11-18 Simon J. Gerraty * install-mk (MK_VERSION): 20141118 - + * meta.stage.mk: add stale_staged - + * dirdeps.mk (_DIRDEP_USE_LEVEL): allow this to be tweaked only useful under very rare conditions such as FreeBSD's make universe. * auto.obj.mk: Allow MK_AUTO_OBJ to set MKOBJDIRS=auto - + 2014-11-11 Simon J. Gerraty * install-mk (MK_VERSION): 20141111 * mkopt.sh: use consistent semantics for _mk_opt and _mk_opts 2014-11-09 Simon J. Gerraty * FILES: include mkopt.sh which allows handling options in shell scripts in a manner compatible with options.mk 2014-10-12 Simon J. Gerraty * meta.stage.mk: ensure only _STAGED_DIRS under objroot are used for GENDIRDEPS_FILTER to avoid surprises. 2014-10-10 Simon J. Gerraty - + * dirdeps.mk (NSkipHostDir): this needs SRCTOP prepended since by the time it is applied to __depdirs they have. - + * dirdeps.mk fix filtering of _machines since M_dep_qual_fixes expects patterns like *.${MACHINE} - + * cython.mk (pyprefix?): use pyprefix to find python bits since prefix might be something else (where we install our stuff) - + 2014-09-11 Simon J. Gerraty * install-mk (MK_VERSION): 20140911 - + * dirdeps.mk: add bootstrap target to simplify adding support for new MACHINE. - + 2014-09-01 Simon J. Gerraty * gendirdeps.mk: Add handling of GENDIRDEPS_FILTER_DIR_VARS and GENDIRDEPS_FILTER_VARS to make it easier to produce sharable Makefile.depend files. 2014-08-28 Simon J. Gerraty * install-mk (MK_VERSION): 20140828 - + * cython.mk: capture logic for building python extension modules with Cython. 2014-08-08 Simon J. Gerraty * meta.stage.mk (_STAGE_AS_BASENAME_USE): Add StageAs variant 2014-08-02 Simon J. Gerraty * install-mk (MK_VERSION): 20140801 * dep.mk: use explicit MKDEP_MK rather than overload MKDEP to - identify the autodep.mk variant. - + identify the autodep.mk variant. + * sys.dependfile.mk: delete .MAKE.DEPENDFILE if its initial value does not match .MAKE.DEPENDFILE_PREFIX * meta.autodep.mk: if _bootstrap_dirdeps add RELDIR to DIRDEPS 2014-05-22 Simon J. Gerraty * install-mk (MK_VERSION): 20140522 * lib.mk: use CC to link shlib for linux too patch from Brendan MacDonell 2014-05-05 Simon J. Gerraty * meta.autodep.mk: add _reldir_{finish,failed} for gathering stats if WITH_META_STATS is defined. 2014-05-02 Simon J. Gerraty * dirdeps.mk: accept -DWITHOUT_DIRDEPS (same a as -DNO_DIRDEPS) to supress dirdeps outside of .CURDIR. 2014-04-05 Simon J. Gerraty * Fix spelling errors - patch from Pedro Giffuni 2014-03-14 Simon J. Gerraty * install-mk (MK_VERSION): 20140314 * dirdeps.mk (beforedirdeps): a handy hook * dirdeps.mk (DIRDEP_MAKE): allow the actual command we run to visit leaf dirs to be intercepted (eg. for distributed build). * dirdeps.mk (__depdirs): ensure // don't sneak in - + * gendirdeps.mk (DIRDEPS): ensure // don't sneak in 2014-02-21 Simon J. Gerraty * rst2htm.mk (RST2PDF): add support for rst2pdf 2014-02-14 Simon J. Gerraty * install-mk (MK_VERSION): bump version * dirdeps.mk (_last_dependfile): use .INCLUDEDFROMFILE if available. 2014-02-10 Simon J. Gerraty * options.mk: avoid :U so this isn't bmake dependent 2014-02-09 Simon J. Gerraty - * options.mk: cleanup and simplify semanitcs + * options.mk: cleanup and simplify semanitcs NO_* dominates all, if both WITH_* and WITHOUT_* are defined then result is DOMINATE_* which defaults to "no". Ie. WITHOUT_ normally wins. 2013-12-12 Simon J. Gerraty * install-mk (MK_VERSION): bump version * meta2deps.py: convert to print function for python3 compat. we also need to open files with mode 'r' rather than 'rb' otherwise we get bytes instead of strings. 2013-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version * dirdeps.mk: when TARGET_SPEC_VARS is more than just MACHINE apply the same filtering (M_dep_qual_fixes) when setting _machines as _build_dirs. Also fix the filtering of Makefile.depend files - for reporting what we are looking for (M_dep_qual_fixes can get confused by - Makefile.depend) + Makefile.depend) Add some more debug info. 2013-09-04 Simon J. Gerraty * gendirdeps.mk (_objtops): fix typo also while processing M2D_OBJROOTS to gather qualdir_list qualify $ql with loop iterator to ensure correct results. 2013-08-01 Simon J. Gerraty * install-mk (MK_VERSION): 20130801 * libs.mk: update to match progs.mk - + 2013-07-26 Simon J. Gerraty * install-mk (MK_VERSION): 20130726 some updates from Juniper and FreeBSD o meta2deps.py: indicate file and line number when we hit parse errors also allow @file to provide huge list of .meta files. * meta2deps.py: add try_parse() to cleanup the above. - + 2013-07-16 Simon J. Gerraty * install-mk (MK_VERSION): 20130716 * own.mk: add GPROG as an option * prog.mk: honor MK_GPROF==yes - + 2013-05-10 Simon J. Gerraty * install-mk (MK_VERSION): 20130505 * gendirdeps.mk, meta2deps.py, meta2deps.sh: handle $TARGET_SPEC for when $MACHINE isn't enough for objdir distinction. Bring meta2deps.sh closer to par with meta2deps.py. 2013-04-18 Simon J. Gerraty * meta.stage.mk: set INSTALL to STAGE_INSTALL when making 'all' also if the target 'beforeinstall' exists, make it depend on .dirdep (incase it uses STAGE_INSTALL). 2013-04-17 Simon J. Gerraty * install-mk (MK_VERSION): 20130401 ;-) * meta.stage.mk (STAGE_INSTALL_SH): add stage-install.sh as wrapper around install(1). * options.mk (OPTION_PREFIX): Allow a prefix other than MK_ 2013-03-30 Simon J. Gerraty * meta2deps.py (MetaFile.__init__): ensure self.cwd is initialized. * install-mk (MK_VERSION): bump version 2013-03-21 Simon J. Gerraty * install-mk (MK_VERSION): bump version * gendirdeps.mk: do not apply :tA to DPADD entries, since we lose any trailing /., rather apply :tA only when needed. * gendirdeps.mk: better mimic meta2deps handling of .dirdep files. * meta.stage.mk (LN_CP_SCRIPT): Add LnCp to do the ln||cp dance consistently. * dirdeps.mk: better describe the dance in sys.mk for TARGET_SPEC. - + 2013-03-18 Simon J. Gerraty * gendirdeps.mk: revert the dance around .MAKE.DEPENDFILE_DEFAULT it is simpler to just not update when say building for "host" (where we know we apply filters to DIRDEPS), and using a non-machine qualified dependfile. 2013-03-16 Simon J. Gerraty * dirdeps.mk: improve DIRDEPS filtering by allowing DEP_SKIP_DIR and DEP_DIRDEPS_FILTER to vary by DEP_MACHINE and DEP_TARGET_SPEC * gendirdeps.mk: ensure _objroot has trailing / if it needs it. * meta2deps.py: if machine is "host", then also trim self.host_target from any OBJROOTS. - + 2013-03-11 Simon J. Gerraty * gendirdeps.mk: if .MAKE.DEPENDFILE_DEFAULT is not machine qualified but _DEPENDFILE is, and .MAKE.DEPENDFILE_DEFAULT exists but _DEPENDFILE does not, compare the new _DEPENDFILE against .MAKE.DEPENDFILE_DEFAULT and discard if the same. 2013-03-08 Simon J. Gerraty * meta.stage.mk: use STAGE_TARGETS to control .ORDER and hook to all: via staging: 2013-03-07 Simon J. Gerraty - * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT): + * sys.dependfile.mk (.MAKE.DEPENDFILE_DEFAULT): use a separate variable for the default .MAKE.DEPENDFILE value so that it can be controlled independently of .MAKE.DEPENDFILE_PREFERENCE * meta.stage.mk: throw error if cp fails etc. Stage*() return early if passed no args. .ORDER stage_* 2013-03-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version * gendirdeps.mk: handle multiple M2D_OBJROOTS better. 2013-02-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20130210 - * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py - from Juniper. + * import latest dirdeps.mk, gendirdeps.mk and meta2deps.py + from Juniper. o dirdeps.mk now fully supports TARGET_SPEC consisting of more than just MACHINE. o no longer use DEP_MACHINE from Makefile.depend* so remove it. - + 2013-01-23 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20130123 * meta.stage.mk: add stage_links (hard links). if doing hard links, we add dest to link as well. Default the stage dir for [sym]links to STAGE_OBJTOP since these are typically specified as absolute paths. Add -m "mode" flag to StageFiles and StageAs. 2012-11-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121111 * autoconf.mk: avoid meta mode seeing changed commands for config.status * meta.autodep.mk: pass resolved MAKESYSPATH to gendirdeps in case we were found via .../mk * sys.clean-env.mk: move it from examples, we and others use it "as is". * FILES: add srctop.mk and options.mk * own.mk: convert to using options.mk which is modeled after FreeBSD's handling of MK_* but more flexible. This allows MK_* for boolean knobs to not be confused with MK* which can be commands. * examples/sys.clean-env.mk: add WITH[OUT]_ to MAKE_ENV_SAVE_PREFIX_LIST. Mention that HOME=/var/empty might be a good idea. 2012-11-08 Simon J. Gerraty * sys.dependfile.mk: if not depend file exists, $MACHINE specific ones are supported but not the default, check if any exist and follow suit. 2012-11-06 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121106 2012-11-05 Simon J. Gerraty * import latest dirdeps.mk and meta2deps.py from Juniper. * progs.mk: add MAN and CXXFLAGS to PROG_VARS also add PROGS_TARGETS and pass on PROG_CXX if it seems appropriate. - + 2012-11-04 Simon J. Gerraty * meta.stage.mk: update CLEANFILES remove redundant cp of .dirdep from STAGE_AS_SCRIPT. * progs.mk: Add LDADD to PROG_VARS - + 2012-10-12 Simon J. Gerraty * meta.stage.mk (STAGE_DIR_FILTER): track dirs we stage to in _STAGED_DIRS so that these can be turned into filters for GENDIRDEPS_FILTER. 2012-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20121010 * meta.stage.mk (STAGE_DIRDEP_SCRIPT): check that an existing target.dirdep matches .dirdep 2012-08-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120808 * import latest meta2deps.py from Juniper. 2012-07-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120711 * dep.mk: add explicit dependencies on SRCS after applying - SRCS_DEP_FILTER + SRCS_DEP_FILTER * meta.autodep.mk: add explicit dependencies on SRCS after applying SRCS_DEP_FILTER * meta.autodep.mk: ensure GENDIRDEPS_FILTER is exported if needed. - + 2012-06-26 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120626 * meta.sys.mk: ignore PYTHON if it does not exist compare ${.MAKE.DEPENDFILE:E} against ${MACHINE} is more reliable. * meta.stage.mk: examine .MAKE.DEPENDFILE_PREFERENCE for any entries ending in .${MACHINE} to decide if qualified _dirdep is needed. * gendirdeps.mk: only produce unqualified deps if no .MAKE.DEPENDFILE_PREFERENCE ends in .${MACHINE} * meta.subdir.mk: apply SUBDIRDEPS_FILTER - + 2012-04-20 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120420 * add sys.dependfile.mk so we can experiment with - .MAKE.DEPENDFILE_PREFERENCE + .MAKE.DEPENDFILE_PREFERENCE * meta.autodep.mk: _DEPENDFILE is precious! - + 2012-03-15 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120315 * install-new.mk: avoid being interrupted 2012-02-26 Simon J. Gerraty * man.mk: MAN might have multiple values so be careful with exists(). 2012-01-19 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20120112 * fix examples/sys.clean-env.mk so that MAKEOBJDIR is handled as: MAKEOBJDIR='${.CURDIR:S,${SRCTOP},${OBJTOP},}' 2011-12-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111201 * import dirdeps.mk from Juniper sjg@ o more consistent handling of DEP_MACHINE, especially when dealing with an odd Makefile.depend, when normally using Makefile.depend.${MACHINE} 2011-11-22 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111122 * meta.autodep.mk: add some debug output, be more crisp about updating. Use ${.ALLTARGETS:M*.o} as a clue for .depend 2011-11-13 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111111 it's too cool to miss * import meta* updates from Juniper sjg@ o dirdeps.mk set DEP_MACHINE for Makefile.depend (when we are normally using Makefile.depend.${MACHINE}), handy for read-only manually maintained dependencies. o meta2deps.py add a clear 'ERROR:' token if an exception is raised. o gendirdeps.mk if ERROR: from meta2deps.py do not update anything. - + 2011-10-30 Simon J. Gerraty * install-new.mk separate the cmp and copy logic to its own function. - + 2011-10-28 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111028 * sys.mk: include auto.obj.mk if MKOBJDIRS is set to auto * subdir.mk: ensure _SUBDIRUSE is provided - * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk + * meta.autodep.mk: remove dependency of gendirdeps.mk on auto.obj.mk * meta.subdir.mk: always allow for Makefile.depend - + 2011-10-10 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111010 o minor tweak to *dirdeps.mk from Juniper sjg@ - + 2011-10-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20111001 o add meta2deps.py from Juniper sjg@ o tweak gendirdeps.mk to work with meta2deps.py when not - cross-building - * autoconf.mk: add autoconf-input as a hook for regenerating + cross-building + * autoconf.mk: add autoconf-input as a hook for regenerating AUTOCONF_INPUTS (configure). 2011-08-24 Simon J. Gerraty * meta.autodep.mk: if we do not have OBJS, .depend isn't a useful - trigger for updating Makefile.depend* + trigger for updating Makefile.depend* 2011-08-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110808 * obj.mk: minor cleanup * auto.obj.mk: improve description of Mkdirs and honor NO_OBJ too. 2011-08-01 Simon J. Gerraty * auto.obj.mk (.OBJDIR): throw an error if we cannot use the specified dir. 2011-06-28 Simon J. Gerraty * meta.autodep.mk: if XMAKE_META_FILE is set the makefile uses a foreign make, and so dependencies can only be gathered from a clean tree build. 2011-06-24 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110622 * meta.autodep.mk: improve bootstraping 2011-06-10 Simon J. Gerraty * yacc.mk: handle the corner case of .c being removed while .h remains. 2011-06-08 Simon J. Gerraty * yacc.mk: do .y.h and .y.c separately 2011-06-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110606 * don't store SRC_DIRDEPS in Makefile.depend* by default not everyone needs it. 2011-05-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110505 first release including meta mode makefiles 2011-05-02 Simon J. Gerraty * meta.stage.mk: add STAGE_AS_SETS and stage_as for things that need to be staged with different names. 2011-05-01 Simon J. Gerraty * meta.stage.mk: add notion of STAGE_SETS so a makefile can stage to multiple dirs 2011-04-03 Simon J. Gerraty * rst2htm.mk: convert rst to s5 (slides) or plain html depending - on target name. + on target name. 2011-03-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110330 2011-03-29 Simon J. Gerraty * sys.mk (_DEBUG_MAKE_FLAGS): use indirection so that DEBUG_MAKE_FLAGS0 can be used to debug level 0 only and DEBUG_MAKE_FLAGS for the rest. * sys.mk: re-define M_whence in terms of M_type. M_type is useful for checking if something is a builtin. - + 2011-03-16 Simon J. Gerraty * meta.stage.mk: add stage_symlinks and leverage StageLinks for - stage_libs + stage_libs 2011-03-10 Simon J. Gerraty * dirdeps.mk: correct value for _depdir_files depends on - .MAKE.DEPENDFILE + .MAKE.DEPENDFILE Add our copyright - just to make it clear we have frobbed this quite a bit. DEP_MACHINE needs to be set to MACHINE each time, if using only Makefile.depend (cf. Makefile.depend.${MACHINE}) * meta.stage.mk: meta mode version of staging * init.mk, final.mk: include local.*.mk to simplify customization 2011-03-03 Simon J. Gerraty * auto.obj.mk: just because we are doing mk destroy, we should still set .OBJDIR correctly if it exists. * install-mk (mksrc): do not exclude meta.sys.mk 2011-03-01 Simon J. Gerraty * host-target.mk: set/export _HOST_ARCH etc separately, catch junk resulting from uname -p, so we can find sys/Linux.mk correctly. 2011-02-18 Simon J. Gerraty * meta.sys.mk: throw an error if /dev/filemon is missing and we expected to be updating Makefile.depend* 2011-02-14 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20110214 * meta.subdir.mk: add support for -DBOOTSTRAP_DEPENDFILES 2010-09-25 Simon J. Gerraty * meta.sys.mk: not valid for older bmake 2010-09-24 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100919 - include dirdeps.mk et al from Juniper Networks, + include dirdeps.mk et al from Juniper Networks, for meta mode - requires filemon(9). * sys.mk, subdir.mk: Add hooks for meta mode. we do this as meta.sys.mk, meta.autodep.mk and meta.subdir.mk to make turning it on/off simple. 2010-06-16 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100616 * fix typo in sys.mk 2010-06-12 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100612 * lib.mk: remove duplicate addition to SOBJS 2010-06-10 Simon J. Gerraty * sys.mk: Add a means of selectively turning on debug flags. Eg. DEBUG_MAKE_FLAGS=-dv DEBUG_MAKE_DIRS="*lib/sjg" will act as if we did make -dv if .CURDIR ends in lib/sjg DEBUG_MAKE_SYS_DIRS does the same thing, but we set the flags at the start of sys.mk rather than the end. This only makes sense for leaf dirs, so we check that .MAKE.LEVEL > 0 2010-06-09 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100608 * sys.mk: include sys.env.mk later so it can use M_ListToSkip et al. * examples/sys.clean-env.mk: require MAKE_VERIONS >= 20100606 also make it easier for folk to tweak 2010-06-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100606 do not install examples/* * FILES: add examples/sys.clean-env.mk * examples/sys.clean-env.mk: use .export-env to handle MAKEOBJDIR this requires bmake-20100606 or later to work. 2010-05-13 Simon J. Gerraty * sys.mk (M_tA): better simulate the result of :tA if not available. 2010-05-04 Simon J. Gerraty * sys.mk: canonicalize MAKE_VERSION old versions reported bmake- build- whereas we only care about 2010-04-25 Simon J. Gerraty * install-mk: just warn about FORCE_{BSD,SYS}_MK being ignored * lib.mk: we only build the shared lib if SHLIB_FULLVERSION is !empty 2010-04-22 Simon J. Gerraty * dpadd.mk: use LDADD_* if defined. 2010-04-21 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100420 * sys/NetBSD.mk: add MACHINE_CPU to keep netbsd makefiles happy * autoconf.mk allow AUTO_AUTOCONF - + 2010-04-19 Simon J. Gerraty * obj.mk: add objwarn to keep freebsd makefiles happy * auto.obj.mk: ensure Mkdirs is available. * FILES: add auto.dep.mk - a simpler version of autodep.mk * dep.mk: auto.dep.mk does not do 'make depend' so ignore it if asked to do that. fix/simplify the tests for when to run mkdep. * auto.dep.mk: add some explanation of how/what we do. * autodep.mk: skip the .OPTIONAL frobbing of .depend bmake's FROM_DEPEND flag makes it redundant. - + 2010-04-13 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100404 * subdir.mk: protect from multiple inclusion using _SUBDIRUSE. * obj.mk: protect from multiple inclusion even as bsd.obj.mk Also create a target _SUBDIRUSE so that we can be used without subdir.mk 2010-04-12 Simon J. Gerraty * dep.mk: use <> when .including so can override. 2010-01-11 Simon J. Gerraty * lib.mk (SHLIB_LINKS): ensure a string comparison. 2010-01-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20100102 * own.mk: ensure PRINTOBJDIR works * autoconf.mk: pass on CONFIGURE_ARGS * init.mk: handle COPTS.${.IMPSRC:T} etc. * lib.mk: allow sys.mk to control SHLIB_FULLVERSION fix handling of symlinks for darwin * libnames.mk: add DSHLIBEXT for libs which only exist as shared. * man.mk: suppress chown when not root. * rst2htm.mk: allow srcs from multiple locations. * sys.mk: M_whence, stop after 1st line of output. * sys/Darwin.mk: Use .dylib for DSHLIBEXT and HOST_LIBEXT * sys/SunOS.mk: we need to export PATH 2009-12-23 Simon J. Gerraty * install-mk (MK_VERSION): bump version include rst2htm.mk 2009-12-17 Simon J. Gerraty * sys.mk,libnames.mk add .-include this allows local customization without the need to edit the - distributed files. + distributed files. 2009-12-14 Simon J. Gerraty * dpadd.mk (__dpadd_libdirs): order -L's to avoid picking up older versions already installed. 2009-12-13 Simon J. Gerraty * stage.mk (.stage-install): generalize lib.mk's .libinstall * rules.mk rules for generic Makefile. * inc.mk install for includes. 2009-12-11 Simon J. Gerraty * sys/NetBSD.mk (MAKE_VERSION): some of our *.mk want to check this, so provide it if using native make. 2009-12-10 Simon J. Gerraty * FILES: move all the platform *.sys.mk files to sys/*.mk * Rename Generic.sys.mk to sys.mk - we always want it. 2009-11-17 Simon J. Gerraty * install-mk (MK_VERSION): bump version - * host-target.mk: only export the expensive stuff + * host-target.mk: only export the expensive stuff * Generic.sys.mk (sys_mk): for SunOS we need to look for ${HOST_OS}.${HOST_OSMAJOR} too! 2009-11-07 Simon J. Gerraty * install-mk (MK_VERSION): bump version * lib.mk: if sys.mk doesn't give us an lorder, don't use it. based on patch from Greg Olszewski. * Generic.sys.mk: if we have nothing to work with set LORDER etc only if we can find it. 2009-09-08 Simon J. Gerraty * install-mk (MK_VERSION): bump version * man.mk: cleanman: remove CLEANMAN if defined. 2009-09-04 Simon J. Gerraty * SunOS.5.sys.mk (CC): Use ?= like the other *sys.mk 2009-07-17 Simon J. Gerraty * install-mk (MK_VERSION): bump version include auto.obj.mk 2009-03-26 Simon J. Gerraty * prog.mk,lib.mk: ensure test of USE_DPADD_MK doesn't fail. 2008-11-11 Simon J. Gerraty * install-mk (MK_VERSION): bump version man.mk: ensure we generate *.cat1 etc in . 2008-07-16 Simon J. Gerraty * install-mk (MK_VERSION): bump version add prlist.mk 2007-11-25 Simon J. Gerraty * Generic.sys.mk: Allow os specific sys.mk to be in a subdir of ${.PARSEDIR} 2007-11-22 Simon J. Gerraty * install-mk (MK_VERSION): bump version * general cleanup - * dpadd.mk introduce DPMAGIC_LIBS_* + * dpadd.mk introduce DPMAGIC_LIBS_* 2007-04-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version * libs.mk, progs.mk, autodep.mk: allow for per lib/prog depend files and ensure clean is called for each lib/prog. 2007-03-27 Simon J. Gerraty * autodep.mk (.depend): delete lines that do not start with space and do not contain ':' 2007-02-16 Simon J. Gerraty * autodep.mk (.depend): gcc may wrap lines if pathnames are long so make sure the transform for .OPTIONAL copes. 2007-02-03 Simon J. Gerraty * install-mk (MK_VERSION): bump version * own.mk: make sure RM and LN are defined. * obj.mk: fix a typo, and objlink target. 2006-12-30 Simon J. Gerraty * install-mk (MK_VERSION): bump version * added libs.mk - analogous to progs.mk make both of them always inlcude {lib,prog}.mk 2006-12-28 Simon J. Gerraty * progs.mk: add a means of building multiple apps in one dir. 2006-11-26 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20061126 * warnings.mk: detect invalid WARNINGS_SET - + * warnings.mk: use ${.TARGET:T:R}.o when looking for target - specific warnings. - + specific warnings. + * For .cc sources, turn off warnings that g++ vomits on. 2006-11-08 Simon J. Gerraty * own.mk: if __initialized__ target doesn't exist and we are FreeBSD we got here directly from sys.mk 2006-11-06 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20061106 add scripts.mk 2006-03-18 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060318 * autodep.mk: avoid := when modifying OBJS into __dependsrcs 2006-03-02 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060302 * autodep.mk: use -MF et al to help gcc+ccache DTRT. 2006-03-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20060301 - * autodep.mk (.depend): + * autodep.mk (.depend): if MAKE_VERSION is newer than 20050530 we can make .END depend on .depend and make .depend depend on __depsrcs that exist. * dpadd.mk: add SRC_PATHADD - + 2005-11-04 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20051104 * prog.mk: remove all the LIBC?= junk, use .-include libnames.mk instead (none by default). also if USE_DPADD_MK is set, include that. 2005-10-09 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20051001 Add UnixWare.sys.mk from Klaus Heinz. 2005-04-05 Simon J. Gerraty * install-mk: always install *.sys.mk and if need be symlink one to sys.mk 2005-03-22 Simon J. Gerraty * subdir.mk, own.mk: use .MAKE rather than MAKE 2004-02-15 Simon J. Gerraty * own.mk: don't use NetBSD's _SRC_TOP_ it can - cause confusion. Also don't take just 'mk' as a + cause confusion. Also don't take just 'mk' as a srctop indicator. 2004-02-14 Simon J. Gerraty * warnings.mk: overhauled, now very powerful. 2004-02-03 Simon J. Gerraty * Generic.sys.mk: need to use ${.PARSEDIR} with exists(). 2004-02-01 Simon J. Gerraty * install-mk (MK_VERSION): bump version to 20040201 * extract HOST_TARGET stuff to host-target.mk so own.mk and Generic.sys.mk can share. * fix typo in autodep.mk _SUBDIRUSE not _SUBDIR. 2003-09-30 Simon J. Gerraty * install-mk (MK_VERSION): 20030930 * rename generic.sys.mk to Generic.sys.mk so that it does not get installed (unless being used as sys.mk) * set OS and ROOT_GROUP for those that we know the value. for others (eg. Generic.sys.mk) wrap the != in an .ifndef so we don't do it again for each sub-make. - + 2003-09-28 Simon J. Gerraty * install-mk (MK_VERSION): 20030928 Add some extra *.sys.mk from bootstrap-pkgsrc some of these likely still need work. Make everything default to root:wheel ownership, sys.mk can set ROOT_GROUP accordingly. 2003-08-07 Simon J. Gerraty * install-mk: if FORCE_BSD_MK={cp,ln} use the ones in SYS_MK_DIR not the portable ones. 2003-07-31 Simon J. Gerraty - * install-mk: add ability to use cp -f when updating - destination .mk files. Also now possible to play games with + * install-mk: add ability to use cp -f when updating + destination .mk files. Also now possible to play games with FORCE_SYS_MK=ln etc on *BSD machines to link /usr/share/mk/sys.mk into dest - not recommended unless you seriously want to. 2003-07-28 Simon J. Gerraty * own.mk (IMPFLAGS): add support for COPTS.${IMPSRC:T} etc for semi-compatability with NetBSD. 2003-07-23 Simon J. Gerraty * install-mk: add a version indicator 2003-07-22 Simon J. Gerraty * prog.mk: don't try and use ${LIBCRT0} if its /dev/null * install-mk: Allow FORCE_SYS_MK to come from env - + Index: head/contrib/bmake/mk/FILES =================================================================== --- head/contrib/bmake/mk/FILES (revision 365365) +++ head/contrib/bmake/mk/FILES (revision 365366) @@ -1,73 +1,74 @@ ChangeLog FILES LICENSE README auto.obj.mk autoconf.mk autodep.mk auto.dep.mk compiler.mk cython.mk dep.mk doc.mk dpadd.mk files.mk final.mk host-target.mk host.libnames.mk inc.mk init.mk install-mk java.mk ldorder.mk lib.mk libnames.mk libs.mk links.mk man.mk manifest.mk mk-files.txt mkopt.sh nls.mk obj.mk options.mk own.mk prlist.mk prog.mk progs.mk rst2htm.mk scripts.mk srctop.mk stage-install.sh subdir.mk sys.mk sys.clean-env.mk sys.debug.mk sys.dependfile.mk sys.vars.mk sys/AIX.mk sys/Darwin.mk sys/Generic.mk sys/HP-UX.mk sys/IRIX.mk sys/Linux.mk sys/NetBSD.mk sys/OSF1.mk sys/OpenBSD.mk sys/SunOS.mk sys/UnixWare.mk target-flags.mk warnings.mk whats.mk yacc.mk dirdeps.mk +dirdeps-cache-update.mk dirdeps-options.mk dirdeps-targets.mk gendirdeps.mk install-new.mk meta2deps.py meta2deps.sh meta.sys.mk meta.autodep.mk meta.stage.mk meta.subdir.mk Index: head/contrib/bmake/mk/README =================================================================== --- head/contrib/bmake/mk/README (revision 365365) +++ head/contrib/bmake/mk/README (revision 365366) @@ -1,401 +1,401 @@ -# $Id: README,v 1.1 1997/03/11 07:27:15 sjg Exp $ +# $Id: README,v 1.2 2020/08/19 17:51:53 sjg Exp $ This directory contains some macro's derrived from the NetBSD bsd.*.mk macros. They have the same names but without the bsd., separate macro files are needed to ensure we can make them do what we want for builing things outside of /usr/src. Nearly all the comments below -apply. +apply. # $NetBSD: bsd.README,v 1.18 1997/01/13 00:54:23 mark Exp $ # @(#)bsd.README 5.1 (Berkeley) 5/11/90 This is the README file for the new make "include" files for the BSD source tree. The files are installed in /usr/share/mk, and are, by convention, named with the suffix ".mk". Note, this file is not intended to replace reading through the .mk files for anything tricky. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= RANDOM THINGS WORTH KNOWING: The files are simply C-style #include files, and pretty much behave like you'd expect. The syntax is slightly different in that a single '.' is used instead of the hash mark, i.e. ".include ". One difference that will save you lots of debugging time is that inclusion of the file is normally done at the *end* of the Makefile. The reason for this is because .mk files often modify variables and behavior based on the values of variables set in the Makefile. To make this work, remember that the FIRST target found is the target that is used, i.e. if the Makefile has: a: echo a a: echo a number two the command "make a" will echo "a". To make things confusing, the SECOND variable assignment is the overriding one, i.e. if the Makefile has: a= foo a= bar b: echo ${a} the command "make b" will echo "bar". This is for compatibility with the way the V7 make behaved. It's fairly difficult to make the BSD .mk files work when you're building multiple programs in a single directory. It's a lot easier split up the programs than to deal with the problem. Most of the agony comes from making the "obj" directory stuff work right, not because we switch to a new version of make. So, don't get mad at us, figure out a better way to handle multiple architectures so we can quit using the symbolic link stuff. (Imake doesn't count.) The file .depend in the source directory is expected to contain dependencies for the source files. This file is read automatically by make after reading the Makefile. The variable DESTDIR works as before. It's not set anywhere but will change the tree where the file gets installed. The profiled libraries are no longer built in a different directory than the regular libraries. A new suffix, ".po", is used to denote a profiled object. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file has the default rules for all makes, in the BSD environment or otherwise. You probably don't want to touch this file. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file handles installing manual pages and their links. It has a single target: maninstall: Install the manual pages and their links. It sets/uses the following variables: MANDIR Base path for manual installation. MANGRP Manual group. MANOWN Manual owner. MANMODE Manual mode. MANSUBDIR Subdirectory under the manual page section, i.e. "/vax" or "/tahoe" for machine specific manual pages. MAN The manual pages to be installed (use a .1 - .9 suffix). MLINKS List of manual page links (using a .1 - .9 suffix). The linked-to file must come first, the linked file second, and there may be multiple pairs. The files are soft-linked. The include file includes a file named "../Makefile.inc" if it exists. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file contains source tree configuration parameters, such as the owners, groups, etc. for both manual pages and binaries, and a few global "feature configuration" parameters. It has no targets. To get system-specific configuration parameters, bsd.own.mk will try to include the file specified by the "MAKECONF" variable. If MAKECONF is not set, or no such file exists, the system make configuration file, /etc/mk.conf is included. These files may define any of the variables described below. bsd.own.mk sets the following variables, if they are not already defined (defaults are in brackets): BSDSRCDIR The real path to the system sources, so that 'make obj' will work correctly. [/usr/src] BSDOBJDIR The real path to the system 'obj' tree, so that 'make obj' will work correctly. [/usr/obj] BINGRP Binary group. [bin] BINOWN Binary owner. [bin] BINMODE Binary mode. [555] NONBINMODE Mode for non-executable files. [444] MANDIR Base path for manual installation. [/usr/share/man/cat] MANGRP Manual group. [bin] MANOWN Manual owner. [bin] MANMODE Manual mode. [${NONBINMODE}] LIBDIR Base path for library installation. [/usr/lib] LINTLIBDIR Base path for lint(1) library installation. [/usr/libdata/lint] LIBGRP Library group. [${BINGRP}] LIBOWN Library owner. [${BINOWN}] LIBMODE Library mode. [${NONBINMODE}] DOCDIR Base path for system documentation (e.g. PSD, USD, etc.) installation. [/usr/share/doc] DOCGRP Documentation group. [bin] DOCOWN Documentation owner. [bin] DOCMODE Documentation mode. [${NONBINMODE}] NLSDIR Base path for National Language Support files installation. [/usr/share/nls] NLSGRP National Language Support files group. [bin] NLSOWN National Language Support files owner. [bin] NLSMODE National Language Support files mode. [${NONBINMODE}] STRIP The flag passed to the install program to cause the binary to be stripped. This is to be used when building your own install script so that the entire system can be made stripped/not-stripped using a single knob. [-s] COPY The flag passed to the install program to cause the binary to be copied rather than moved. This is to be used when building our own install script so that the entire system can either be installed with copies, or with moves using a single knob. [-c] Additionally, the following variables may be set by bsd.own.mk or in a make configuration file to modify the behaviour of the system build process (default values are in brackets along with comments, if set by bsd.own.mk): EXPORTABLE_SYSTEM Do not build /usr/src/domestic, even if it is present. SKEY Compile in support for S/key authentication. [yes, set unconditionally] KERBEROS Compile in support for Kerberos 4 authentication. KERBEROS5 Compile in support for Kerberos 5 authentication. MANZ Compress manual pages at installation time. SYS_INCLUDE Copy or symlink kernel include files into /usr/include. Possible values are "symlinks" or "copies" (which is the same as the variable being unset). NOPROFILE Do not build profiled versions of system libraries NOPIC Do not build PIC versions of system libraries, and do not build shared libraries. [set if ${MACHINE_ARCH} is "mips", "vax", "alpha" or "arm32", unset otherwise.] NOLINT Do not build lint libraries. [set, set unconditionally] bsd.own.mk is generally useful when building your own Makefiles so that they use the same default owners etc. as the rest of the tree. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file handles building programs from one or more source files, along with their manual pages. It has a limited number of suffixes, consistent with the current needs of the BSD tree. It has eight targets: all: build the program and its manual page clean: remove the program, any object files and the files a.out, Errs, errs, mklog, and core. cleandir: remove all of the files removed by the target clean, as well as .depend, tags, and any manual pages. depend: make the dependencies for the source files, and store them in the file .depend. includes: install any header files. install: install the program and its manual pages; if the Makefile does not itself define the target install, the targets beforeinstall and afterinstall may also be used to cause actions immediately before and after the install target is executed. lint: run lint on the source files tags: create a tags file for the source files. It sets/uses the following variables: BINGRP Binary group. BINOWN Binary owner. BINMODE Binary mode. CLEANFILES Additional files to remove for the clean and cleandir targets. COPTS Additional flags to the compiler when creating C objects. HIDEGAME If HIDEGAME is defined, the binary is installed in /usr/games/hide, and a symbolic link is created to /usr/games/dm. LDADD Additional loader objects. Usually used for libraries. For example, to load with the compatibility and utility libraries, use: LDADD+=-lutil -lcompat LDFLAGS Additional loader flags. LINKS The list of binary links; should be full pathnames, the linked-to file coming first, followed by the linked file. The files are hard-linked. For example, to link /bin/test and /bin/[, use: LINKS= ${DESTDIR}/bin/test ${DESTDIR}/bin/[ MAN Manual pages (should end in .1 - .9). If no MAN variable is defined, "MAN=${PROG}.1" is assumed. PROG The name of the program to build. If not supplied, nothing is built. SRCS List of source files to build the program. If PROG is not defined, it's assumed to be ${PROG}.c. DPADD Additional dependencies for the program. Usually used for libraries. For example, to depend on the compatibility and utility libraries use: DPADD+=${LIBCOMPAT} ${LIBUTIL} The following libraries are predefined for DPADD: LIBC /lib/libc.a LIBCOMPAT /usr/lib/libcompat.a LIBCRYPT /usr/lib/libcrypt.a LIBCURSES /usr/lib/libcurses.a LIBDBM /usr/lib/libdbm.a LIBDES /usr/lib/libdes.a LIBL /usr/lib/libl.a LIBKDB /usr/lib/libkdb.a LIBKRB /usr/lib/libkrb.a LIBKVM /usr/lib/libkvm.a LIBM /usr/lib/libm.a LIBMP /usr/lib/libmp.a LIBPC /usr/lib/libpc.a LIBPLOT /usr/lib/libplot.a LIBRPC /usr/lib/sunrpc.a LIBTERM /usr/lib/libterm.a LIBUTIL /usr/lib/libutil.a SHAREDSTRINGS If defined, a new .c.o rule is used that results in shared strings, using xstr(1). Note that this will not work with parallel makes. STRIP The flag passed to the install program to cause the binary to be stripped. SUBDIR A list of subdirectories that should be built as well. Each of the targets will execute the same target in the subdirectories. The include file includes the file named "../Makefile.inc" if it exists, as well as the include file . Some simple examples: To build foo from foo.c with a manual page foo.1, use: PROG= foo .include To build foo from foo.c with a manual page foo.2, add the line: MAN= foo.2 If foo does not have a manual page at all, add the line: NOMAN= noman If foo has multiple source files, add the line: SRCS= a.c b.c c.c d.c =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file contains the default targets for building -subdirectories. It has the same eight targets as : all, +subdirectories. It has the same eight targets as : all, clean, cleandir, depend, includes, install, lint, and tags. For all of -the directories listed in the variable SUBDIRS, the specified directory +the directories listed in the variable SUBDIRS, the specified directory will be visited and the target made. There is also a default target which allows the command "make subdir" where subdir is any directory listed in the variable SUBDIRS. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file is used by and . It contains overrides that are used when building the NetBSD source tree. For instance, if "PARALLEL" is defined by the program/library Makefile, it includes a set of rules for lex and yacc that allow multiple lex and yacc targets to be built in parallel. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file has support for building libraries. It has the same eight targets as : all, clean, cleandir, depend, includes, install, lint, and tags. It has a limited number of suffixes, consistent with the current needs of the BSD tree. It sets/uses the following variables: LIB The name of the library to build. LIBDIR Target directory for libraries. LINTLIBDIR Target directory for lint libraries. LIBGRP Library group. LIBOWN Library owner. LIBMODE Library mode. LDADD Additional loader objects. MAN The manual pages to be installed (use a .1 - .9 suffix). SRCS List of source files to build the library. Suffix types .s, .c, and .f are supported. Note, .s files are preferred to .c files of the same name. (This is not the default for versions of make.) The include file includes the file named "../Makefile.inc" if it exists, as well as the include file . It has rules for building profiled objects; profiled libraries are built by default. Libraries are ranlib'd when made. Index: head/contrib/bmake/mk/auto.dep.mk =================================================================== --- head/contrib/bmake/mk/auto.dep.mk (revision 365365) +++ head/contrib/bmake/mk/auto.dep.mk (revision 365366) @@ -1,74 +1,74 @@ # # RCSid: -# $Id: auto.dep.mk,v 1.5 2016/04/05 15:58:37 sjg Exp $ +# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2010, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # This module provides automagic dependency generation along the # lines suggested in the GNU make.info # set MKDEP_MK=auto.dep.mk and dep.mk will include us -# This version differs from autodep.mk, in that +# This version differs from autodep.mk, in that # we use ${.TARGET:T}.d rather than ${.TARGET:T:R}.d # this makes it simpler to get the args to -MF and -MT right # and ensure we can simply include all the .d files. -# +# # However suffix rules do not work with something like .o.d so we # don't even try to handle 'make depend' gracefully. # dep.mk will handle that itself. # .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: # this what bmake > 20100401 will look for .MAKE.DEPENDFILE ?= .depend # set this to -MMD to ignore /usr/include # actually it ignores <> so may not be a great idea -CFLAGS_MD ?= -MD +CFLAGS_MD ?= -MD # -MF etc not available on all gcc versions. CFLAGS_MF ?= -MF ${.TARGET:T}.d -MT ${.TARGET:T} CFLAGS += ${CFLAGS_MD} ${CFLAGS_MF} CXXFLAGS += ${CFLAGS_MD} ${CFLAGS_MF} CLEANFILES += .depend ${.MAKE.DEPENDFILE} *.d .if ${MAKE_VERSION} < 20160218 # skip generating dependfile for misc targets .if ${.TARGETS:Uall:M*all} != "" .END: ${.MAKE.DEPENDFILE} .endif # doing 'make depend' isn't a big win with this model .if !target(depend) depend: ${.MAKE.DEPENDFILE} .endif # this is trivial ${.MAKE.DEPENDFILE}: ${OBJS} ${POBJS} ${SOBJS} -@for f in ${.ALLSRC:M*o:T:O:u:%=%.d}; do \ echo ".-include \"$$f\""; \ done > $@ .else # we have .dinclude .if empty(_SKIP_BUILD) _all_objs = ${OBJS} ${POBJS} ${SOBJS} .for d in ${_all_objs:M*o:T:O:u:%=%.d} .dinclude <$d> .endfor .endif .endif .endif Index: head/contrib/bmake/mk/auto.obj.mk =================================================================== --- head/contrib/bmake/mk/auto.obj.mk (revision 365365) +++ head/contrib/bmake/mk/auto.obj.mk (revision 365366) @@ -1,76 +1,76 @@ -# $Id: auto.obj.mk,v 1.15 2017/11/04 21:05:04 sjg Exp $ +# $Id: auto.obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2004, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # ECHO_TRACE ?= echo .ifndef Mkdirs -# A race condition in some versions of mkdir, means that it can bail +# A race condition in some versions of 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. # We stop looping as soon as the dir exists. # If we get to the end of the loop, a plain mkdir will issue an error. Mkdirs= Mkdirs() { \ for d in $$*; do \ for i in 1 2 3 4 5 6; do \ mkdir -p $$d; \ test -d $$d && return 0; \ done > /dev/null 2>&1; \ mkdir $$d || exit $$?; \ done; } .endif # if MKOBJDIRS is set to auto (and NOOBJ isn't defined) do some magic... # This will automatically create objdirs as needed. # Skip it if we are just doing 'clean'. .if ${MK_AUTO_OBJ:Uno} == "yes" MKOBJDIRS= auto .endif .if !defined(NOOBJ) && !defined(NO_OBJ) && ${MKOBJDIRS:Uno} == auto # Use __objdir here so it is easier to tweak without impacting # the logic. .if !empty(MAKEOBJDIRPREFIX) .if ${.CURDIR:M${MAKEOBJDIRPREFIX}/*} != "" # we are already in obj tree! __objdir?= ${.CURDIR} .endif __objdir?= ${MAKEOBJDIRPREFIX}${.CURDIR} .endif __objdir?= ${MAKEOBJDIR:Uobj} __objdir:= ${__objdir} .if ${.OBJDIR:tA} != ${__objdir:tA} # We need to chdir, make the directory if needed .if !exists(${__objdir}/) && \ (${.TARGETS} == "" || ${.TARGETS:Nclean*:N*clean:Ndestroy*} != "") -# This will actually make it... +# This will actually make it... __objdir_made != echo ${__objdir}/; umask ${OBJDIR_UMASK:U002}; \ ${ECHO_TRACE} "[Creating objdir ${__objdir}...]" >&2; \ ${Mkdirs}; Mkdirs ${__objdir} .endif # This causes make to use the specified directory as .OBJDIR .OBJDIR: ${__objdir} .if ${.OBJDIR:tA} != ${__objdir:tA} # we did not get what we want - do we care? .if ${__objdir_made:Uno:M${__objdir}/*} != "" # watch out for __objdir being relative path .if !(${__objdir:M/*} == "" && ${.OBJDIR:tA} == ${${.CURDIR}/${__objdir}:L:tA}) .error could not use ${__objdir}: .OBJDIR=${.OBJDIR} .endif .endif # apparently we can live with it # make sure we know what we have .OBJDIR: ${.CURDIR} .endif .endif .endif Index: head/contrib/bmake/mk/autoconf.mk =================================================================== --- head/contrib/bmake/mk/autoconf.mk (revision 365365) +++ head/contrib/bmake/mk/autoconf.mk (revision 365366) @@ -1,80 +1,80 @@ -# $Id: autoconf.mk,v 1.9 2017/08/13 20:03:13 sjg Exp $ +# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1996-2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .NOPATH: config.h config.status CONFIGURE_DEPS += ${.CURDIR}/config.h.in ${.CURDIR}/configure .if !target(config.h) config.h: ${CONFIGURE_DEPS} config.status ./config.status .endif .if !target(config.status) # avoid the targets behaving differently .if exists(${.OBJDIR}/config.status) config.status: config.recheck .else config.status: config.gen .endif config.recheck: ${CONFIGURE_DEPS} ./config.status --recheck @touch $@ config.gen: ${CONFIGURE_DEPS} CC="${CC} ${CCMODE}" ${.CURDIR}/configure --no-create ${CONFIGURE_ARGS} @touch $@ config.recheck CLEANFILES+= config.recheck config.gen config.status *.meta .endif # avoid things blowing up if these are not here... # this is not quite per the autoconf manual, # and is extremely convoluted - but all utterly necessary! .if make(autoconf-in) || make(configure) || make(config.h.in) || ${AUTO_AUTOCONF:Uno:tl} == "yes" AUTOCONF ?= autoconf AUTOHEADER ?= autoheader -# expand it to a full path +# expand it to a full path AUTOCONF := ${AUTOCONF:${M_whence}} .if exists(${AUTOCONF}) .PRECIOUS: configure config.h.in config.status ACLOCAL = ACCONFIG = .if exists(${.CURDIR}/aclocal.m4) ACLOCAL += aclocal.m4 .endif # use of acconfig.h is deprecated! .if exists(${.CURDIR}/acconfig.h) ACCONFIG += acconfig.h .endif config.h.in: ${.CURDIR}/configure.in ${ACCONFIG} (cd ${.CURDIR} && ${AUTOHEADER}) configure: ${.CURDIR}/configure.in ${ACLOCAL} (cd ${.CURDIR} && ${AUTOCONF}) AUTOCONF_INPUTS += configure autoconf-input: ${AUTOCONF_INPUTS} .endif .endif Index: head/contrib/bmake/mk/autodep.mk =================================================================== --- head/contrib/bmake/mk/autodep.mk (revision 365365) +++ head/contrib/bmake/mk/autodep.mk (revision 365366) @@ -1,219 +1,219 @@ # # RCSid: -# $Id: autodep.mk,v 1.37 2020/04/17 21:08:17 sjg Exp $ +# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2010, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # This module provides automagic dependency generation along the # lines suggested in the GNU make.info # The depend target is mainly for backwards compatibility, # dependencies are normally updated as part of compilation. .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: DEPENDFILE?= .depend .for d in ${DEPENDFILE:N.depend} # bmake only groks .depend .if ${MAKE_VERSION} < 20160218 .-include <$d> .else .dinclude <$d> .endif .endfor # it does nothing if SRCS is not defined or is empty .if defined(SRCS) && !empty(SRCS) DEPSRCS?=${SRCS} __depsrcs=${DEPSRCS:M*.c} __depsrcs+=${DEPSRCS:M*.y} __depsrcs+=${DEPSRCS:M*.l} __depsrcs+=${DEPSRCS:M*.s} __depsrcs+=${DEPSRCS:M*.S} __depsrcs+=${DEPSRCS:M*.cc} __depsrcs+=${DEPSRCS:M*.cpp} __depsrcs+=${DEPSRCS:M*.C} __depsrcs+=${DEPSRCS:M*.cxx} __depsrcs+=${DEPSRCS:M*.pc} .for s in ${__depsrcs} ${s:T:R}.d: $s .endfor __depsrcs:=${__depsrcs:T:R:S/$/.d/g} -# we also need to handle makefiles where the .d's from __depsrcs +# we also need to handle makefiles where the .d's from __depsrcs # don't match those from OBJS # we avoid using := here, since the modifier applied to OBJS # can cause trouble if there are any undefined vars in OBJS. __dependsrcsx?= ${__depsrcs} ${OBJS:S/.o/.d/} __dependsrcs= ${__dependsrcsx:O:u} # clean up any .c files we may have generated #__gensrcs:= ${DEPSRCS:M*.y} ${DEPSRCS:M*.l} #CLEANFILES+= ${__gensrcs:T:R:S/$/.c/g} # set this to -MMD to ignore /usr/include # actually it ignores <> so may not be a great idea -CFLAGS_MD?=-MD +CFLAGS_MD?=-MD # -MF etc not available on all gcc versions. # we "fix" the .o later 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} -CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS} +CPPFLAGS_MD=${CFLAGS:M-[IUD]*} ${CPPFLAGS} +CXXFLAGS_MD=${CXXFLAGS:M-[IUD]*} ${CPPFLAGS} # just in case these need to be different CC_MD?=${CC} CXX_MD?=${CXX} # should have been set by sys.mk CXX_SUFFIXES?= .cc .cpp .cxx .C # so we can do an explicit make depend, but not otherwise .if make(depend) .SUFFIXES: .d .if empty(CFLAGS_MD) .y.d: @echo updating dependencies for $< @${YACC} ${YFLAGS} $< @${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} $< @${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 $< @${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; } .s.d .S.d: @echo updating dependencies for $< @${MAKE_SHELL} -ec "${CC_MD} -M ${CPPFLAGS_MD} ${AINC} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; } ${CXX_SUFFIXES:%=%.d}: @echo updating dependencies for $< @${MAKE_SHELL} -ec "${CXX_MD} -M ${CXXFLAGS_MD} $< | sed '/:/s/^/$@ /' > $@" || { ${RM} -f $@; false; } .else .y.d: ${YACC} ${YFLAGS} $< ${CC_MD} ${CFLAGS_MD:S/D//} ${CPPFLAGS_MD} y.tab.c > $@ || { ${RM} -f y.tab.c $@; false; } ${RM} -f y.tab.c .l.d: ${LEX} ${LFLAGS} $< ${CC_MD} ${CFLAGS_MD:S/D//} ${CPPFLAGS_MD} lex.yy.c > $@ || { ${RM} -f lex.yy.c $@; false; } ${RM} -f lex.yy.c .c.d: ${CC_MD} ${CFLAGS_MD:S/D//} ${CPPFLAGS_MD} $< > $@ || { ${RM} -f $@; false; } .s.d .S.d: ${CC_MD} ${CFLAGS_MD:S/D//} ${CPPFLAGS_MD} ${AINC} $< > $@ || { ${RM} -f $@; false; } ${CXX_SUFFIXES:%=%.d}: ${CXX_MD} ${CFLAGS_MD:S/D//} ${CXXFLAGS_MD} $< > $@ || { ${RM} -f $@; false; } .endif .if !target(depend) depend: beforedepend ${DEPENDFILE} afterdepend _SUBDIRUSE ${DEPENDFILE}: ${DEPSRCS} ${__dependsrcs} .NOPATH: ${__dependsrcs} .OPTIONAL: ${__dependsrcs} .endif .endif # make(depend) .if empty(CFLAGS_MD) # make sure the .d's are generated/updated ${PROG} ${_LIBS}: ${DEPENDFILE} .endif .ORDER: beforedepend ${DEPENDFILE} afterdepend .if ${.OBJDIR} != ${.CURDIR} __depfiles= *.d .else __depfiles= ${__dependsrcs} .endif DEPCLEANFILES= ${DEPENDFILE} ${__depfiles} y.tab.d *.tmp.d cleandir: cleanautodepend cleanautodepend: ${RM} -f ${DEPCLEANFILES} CLEANFILES+= ${DEPCLEANFILES} .if defined(__dependsrcs) && !empty(__dependsrcs) .if make(depend) || !(make(clean*) || make(destroy*) || make(obj) || make(*install) || make(install-*)) # this ensures we do the right thing if only building a shared or # profiled lib OBJ_EXTENSIONS?=.o .po .so .So MDLIB_SED= -e '/:/s,^\([^\.:]*\)\.[psS]*o,${OBJ_EXTENSIONS:S,^,\1,},' .ifdef NOMD_SED .ifdef LIB MD_SED=sed ${MDLIB_SED} .else MD_SED=cat .endif .else # arrange to put some variable names into ${DEPENDFILE} .ifdef LIB MD_SED=sed ${MDLIB_SED} .else MD_SED=sed .endif SUBST_DEPVARS+= SB TOP BACKING SRC SRCDIR BASE BASEDIR .for v in ${SUBST_DEPVARS} .if defined(${v}) && !empty(${v}) MD_SED+= -e 's,${$v},$${$v},' .endif .endfor .endif .if (${MD_SED} == "sed") MD_SED=cat .endif # this will be done whenever make finishes successfully .if ${MAKE_VERSION:U0:[1]:C/.*-//} < 20050530 .END: .else .END: ${DEPENDFILE} # we do not want to trigger building .d's just use them if they exist ${DEPENDFILE}: ${__dependsrcs:@d@${exists($d):?$d:}@} .endif -@${MD_SED} ${__depfiles} > ${DEPENDFILE}.new 2> /dev/null && \ test -s ${DEPENDFILE}.new && mv ${DEPENDFILE}.new ${DEPENDFILE}; \ ${RM} -f ${DEPENDFILE}.new .endif .endif .else depend: beforedepend afterdepend _SUBDIRUSE .endif .if !target(beforedepend) beforedepend: .endif .if !target(afterdepend) afterdepend: .endif .endif Index: head/contrib/bmake/mk/compiler.mk =================================================================== --- head/contrib/bmake/mk/compiler.mk (revision 365365) +++ head/contrib/bmake/mk/compiler.mk (revision 365366) @@ -1,43 +1,43 @@ -# $Id: compiler.mk,v 1.6 2019/09/28 17:12:00 sjg Exp $ +# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# 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 Index: head/contrib/bmake/mk/cython.mk =================================================================== --- head/contrib/bmake/mk/cython.mk (revision 365365) +++ head/contrib/bmake/mk/cython.mk (revision 365366) @@ -1,101 +1,101 @@ # RCSid: -# $Id: cython.mk,v 1.7 2018/03/25 18:46:11 sjg Exp $ +# $Id: cython.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # pyprefix is where python bits are # which may not be where we want to put ours (prefix) .if exists(/usr/pkg/include) pyprefix?= /usr/pkg .endif pyprefix?= /usr/local PYTHON_VERSION?= 2.7 PYTHON_H?= ${pyprefix}/include/python${PYTHON_VERSION}/Python.h PYVERSION:= ${PYTHON_VERSION:C,\..*,,} CFLAGS+= -I${PYTHON_H:H} # conf.host_target() is limited to uname -m rather than uname -p _HOST_MACHINE!= uname -m .if ${HOST_TARGET:M*${_HOST_MACHINE}} == "" PY_HOST_TARGET:= ${HOST_TARGET:S,${_HOST_ARCH:U${uname -p:L:sh}}$,${_HOST_MACHINE},} .endif COMPILE.c?= ${CC} -c ${CFLAGS} PICO?= .pico .SUFFIXES: ${PICO} .c .c${PICO}: ${COMPILE.c} ${PICFLAG} ${CC_PIC} ${.IMPSRC} -o ${.TARGET} # this is what we build .if !empty(CYTHON_MODULE_NAME) CYTHON_MODULE = ${CYTHON_MODULE_NAME}${CYTHON_PYVERSION}.so CYTHON_SRCS?= ${CYTHON_MODULE_NAME}.pyx # this is where we save generated src CYTHON_SAVEGENDIR?= ${.CURDIR}/gen # set this empty if you don't want to handle multiple versions .if !defined(CYTHON_PYVERSION) CYTHON_PYVERSION:= ${PYVERSION} .endif CYTHON_GENSRCS= ${CYTHON_SRCS:R:S,$,${CYTHON_PYVERSION}.c,} SRCS+= ${CYTHON_GENSRCS} .SUFFIXES: .pyx .c .So CYTHON?= ${pyprefix}/bin/cython # if we don't have cython we can use pre-generated srcs .if ${type ${CYTHON} 2> /dev/null || echo:L:sh:M/*} == "" .PATH: ${CYTHON_SAVEGENDIR} .else .if !empty(CYTHON_PYVERSION) .for c in ${CYTHON_SRCS} ${c:R}${CYTHON_PYVERSION}.${c:E}: $c ln -sf ${.ALLSRC:M*pyx} ${.TARGET} .endfor .endif .pyx.c: ${CYTHON} ${CYTHON_FLAGS} -${PYVERSION} -o ${.TARGET} ${.IMPSRC} save-gen: ${CYTHON_GENSRCS} mkdir -p ${CYTHON_SAVEGENDIR} cp -p ${.ALLSRC} ${CYTHON_SAVEGENDIR} .endif ${CYTHON_MODULE}: ${SRCS:S,.c,${PICO},} ${CC} ${CC_SHARED:U-shared} -o ${.TARGET} ${.ALLSRC:M*${PICO}} ${LDADD} MODULE_BINDIR?= ${.CURDIR:H}/${PY_HOST_TARGET:U${HOST_TARGET}} build-cython-module: ${CYTHON_MODULE} install-cython-module: ${CYTHON_MODULE} test -d ${DESTDIR}${MODULE_BINDIR} || \ ${INSTALL} -d ${DESTDIR}${MODULE_BINDIR} ${INSTALL} -m 755 ${.ALLSRC} ${DESTDIR}${MODULE_BINDIR} CLEANFILES+= *${PICO} ${CYTHON_MODULE} .endif Index: head/contrib/bmake/mk/dirdeps-cache-update.mk =================================================================== --- head/contrib/bmake/mk/dirdeps-cache-update.mk (nonexistent) +++ head/contrib/bmake/mk/dirdeps-cache-update.mk (revision 365366) @@ -0,0 +1,179 @@ +# $Id: dirdeps-cache-update.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $ +# +# @(#) Copyright (c) 2020, 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 deals with the updating of STATIC_DIRDEPS_CACHE. +# Some targets are so huge that computing dirdeps takes a significant +# amount of time. For such targets a STATIC_DIRDEPS_CACHE can make +# sense. +# +# If the target is represented by targets/pseudo/production +# it's normal DIRDEPS would be in +# targets/pseudo/production/Makefile.depend +# and STATIC_DIRDEPS_CACHE would be +# targets/pseudo/production/Makefile.dirdeps.cache +# which is simply initialized by copying dirdeps.cache.production +# from $OBJTOP +# +# When dirdeps-targets.mk is initializing DIRDEPS it will look for +# Makefile.dirdeps.cache and unless told not to +# (MK_STATIC_DIRDEPS_CACHE=no) will use it as DIRDEPS_CACHE. +# +# If MK_STATIC_DIRDEPS_CACHE_UPDATE is "yes", then this makefile +# comes into play. +# +# We usually get included from local.dirdeps.mk +# as well as Makefile.depend of RELDIR with a static Makefile.dirdeps.cache +# +# If we see that STATIC_DIRDEPS_CACHE is in use, we need to hook a +# cache-update target into the build to regenerate dirdeps.cache +# in parallel with the rest of the build. +# If MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE is "yes" we update +# STATIC_DIRDEPS_CACHE as soon as the update is ready, +# otherwise it will be done at the end of the build. +# +# If STATIC_DIRDEPS_CACHE is not in use, but a DIRDEPS_CACHE is, +# then we need do nothing except export STATIC_DIRDEPS_CACHE and +# DYNAMIC_DIRDEPS_CACHE for use when we are include during the visit +# to the ultimate target (targets/pseudo/production). +# +# Regardless of which happens, when included at .MAKE.LEVEL > 0 +# for a target other than cache-update we simply copy +# DYNAMIC_DIRDEPS_CACHE to STATIC_DIRDEPS_CACHE with some optional +# filtering. +# +# If we are included for the target cache-update we take care of +# running dirdeps.mk again to generate the DYNAMIC_DIRDEPS_CACHE. +# + +.if !target(_${.PARSEFILE}_) +_${.PARSEFILE}_: .NOTMAIN + +STATIC_CACHE_SED += \ + -e '/Autogenerated/s,-.*,- edit with care!,' \ + -e '/cache-update/d' + +STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT ?= \ + { echo Saving ${DYNAMIC_DIRDEPS_CACHE} as ${STATIC_DIRDEPS_CACHE}; \ + sed ${STATIC_CACHE_SED} ${DYNAMIC_DIRDEPS_CACHE} > ${STATIC_DIRDEPS_CACHE}; } +.endif + +.if ${MK_DIRDEPS_CACHE:Uno} == "yes" +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes" +.if ${_debug_reldir:U0} || ${DEBUG_DIRDEPS:U:Mcache*} != "" +_debug_cache = 1 +.else +_debug_cache = 0 +.endif + +.if ${.MAKE.LEVEL} == 0 && !make(cache-update) + +.if ${_debug_cache} +.info ${MK_STATIC_DIRDEPS_CACHE_UPDATE MK_STATIC_DIRDEPS_CACHE MK_DIRDEPS_CACHE DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@} +.endif + +.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" && defined(STATIC_DIRDEPS_CACHE) && exists(${STATIC_DIRDEPS_CACHE}) +.if !make(dirdeps) +# We are using static cache and this is the only look we will get. +# We want to generate an updated cache while we build +# so need to hook cache-update to dirdeps now. +# Note: we are running as a sibling to dirdeps-cached, +# attempting to do this in that context is problematic. + +# One of these should exist - to actually kick off the cache generation +.for d in ${STATIC_DIRDEPS_CACHE:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H}/cache-update ${STATIC_DIRDEPS_CACHE:H:H:H}/cache-update +.if exists($d) +cache_update_dirdep ?= $d.${TARGET_SPEC} +.endif +.endfor +.if !target(${cache_update_dirdep}) +dirdeps: ${cache_update_dirdep} +${cache_update_dirdep}: _DIRDEP_USE +DYNAMIC_DIRDEPS_CACHE := ${OBJTOP}/dirdeps.cache.${STATIC_DIRDEPS_CACHE:H:T}-update +.export DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE +.endif +.endif # make(dirdeps) +.endif # MK_* + +.endif # .MAKE.LEVEL 0 + +.if ${.MAKE.LEVEL} > 0 && ${.CURDIR:T} == "cache-update" +# we are the background update shim + +.if ${_debug_cache} +.info level ${.MAKE.LEVEL}: ${MK_DIRDEPS_CACHE DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE:L:@v@$v=${$v}@} +.endif + +all: cache-build +cache-build: .META + @set -x; MAKELEVEL=0 \ + ${.MAKE} -C ${SRCTOP} -f ${RELDIR}/Makefile cache-update \ + -DWITHOUT_STATIC_DIRDEPS_CACHE_UPDATE + +.endif # cache-update + +.elif ${.MAKE.LEVEL} == 0 && make(cache-update) && !target(cache-update) +# we were invoked above +# we just leverage dirdeps.mk +BUILD_DIRDEPS_TARGETS := ${STATIC_DIRDEPS_CACHE:H:T} +DIRDEPS := ${STATIC_DIRDEPS_CACHE:H:S,^${SRCTOP}/,,}.${TARGET_SPEC} +DIRDEPS_CACHE := ${DYNAMIC_DIRDEPS_CACHE} + +.if ${DEBUG_DIRDEPS:U:Mcache*} != "" +.info level 0: ${MK_DIRDEPS_CACHE DIRDEPS_CACHE DIRDEPS:L:@v@$v=${$v}@} +.endif + +# so cache-built below can check on us +x!= echo; echo ${.MAKE.PID} > ${DIRDEPS_CACHE}.new.pid + +cache-update: ${DIRDEPS_CACHE} + @rm -f ${DIRDEPS_CACHE}.new.pid +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "yes" + ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT} +.endif + +all: + +.include + +.endif # MK_STATIC_DIRDEPS_CACHE_UPDATE +.endif # MK_DIRDEPS_CACHE + +.if ${.MAKE.LEVEL} > 0 && ${MK_STATIC_DIRDEPS_CACHE_UPDATE:Uno} == "yes" && \ + ${STATIC_DIRDEPS_CACHE:Uno:H} == "${SRCTOP}/${RELDIR}" +.if !defined(DYNAMIC_DIRDEPS_CACHE) +all: +.else +# This is the easy bit, time to save the cache + +all: cache-update + +# ensure the cache update is completed +cache-built: + @test -s ${DYNAMIC_DIRDEPS_CACHE}.new || exit 0; \ + pid=`cat ${DYNAMIC_DIRDEPS_CACHE}.new.pid 2> /dev/null`; \ + test $${pid:-0} -gt 1 || exit 0; \ + echo "Waiting for $$pid to finish ${DYNAMIC_DIRDEPS_CACHE} ..."; \ + while 'kill' -0 $$pid; do sleep 30; done > /dev/null 2>&1 + +cache-update: cache-built +.if ${MK_STATIC_DIRDEPS_CACHE_UPDATE_IMMEDIATE:Uno} == "no" + @test ! -s ${DYNAMIC_DIRDEPS_CACHE} || \ + ${STATIC_DIRDEPS_CACHE_UPDATE_SCRIPT} +.endif + +.endif +.endif Property changes on: head/contrib/bmake/mk/dirdeps-cache-update.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/mk/dirdeps-options.mk =================================================================== --- head/contrib/bmake/mk/dirdeps-options.mk (revision 365365) +++ head/contrib/bmake/mk/dirdeps-options.mk (revision 365366) @@ -1,73 +1,100 @@ -# $Id: dirdeps-options.mk,v 1.9 2018/09/20 00:07:19 sjg Exp $ +# $Id: dirdeps-options.mk,v 1.17 2020/08/07 01:57:38 sjg Exp $ # -# @(#) Copyright (c) 2018, Simon J. Gerraty +# @(#) Copyright (c) 2018-2020, 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 deal with optional DIRDEPS. # # It is to be included by Makefile.depend.options in a # directory which has DIRDEPS affected by optional features. # Makefile.depend.options should set DIRDEPS_OPTIONS and # may also set specific DIRDEPS.* for those options. # # If a Makefile.depend.options file exists, it will be included by # dirdeps.mk and meta.autodep.mk # # We include local.dirdeps-options.mk which may also define DIRDEPS.* # for options. # # Thus a directory, that is affected by an option FOO would have # a Makefile.depend.options that sets # DIRDEPS_OPTIONS= FOO # It can also set either/both of # DIRDEPS.FOO.yes # DIRDEPS.FOO.no # to whatever applies for that dir, or it can rely on globals # set in local.dirdeps-options.mk # Either way, we will .undef DIRDEPS.* when done. +# +# In some cases the value of MK_FOO might depend on TARGET_SPEC +# so we qualify MK_FOO with .${TARGET_SPEC} and each component +# TARGET_SPEC_VAR (in reverse order) before using MK_FOO. +# # This should have been set by Makefile.depend.options # before including us DIRDEPS_OPTIONS ?= # pickup any DIRDEPS.* we need .-include .if ${.MAKE.LEVEL} == 0 # :U below avoids potential errors when we := -.for o in ${DIRDEPS_OPTIONS:tu} -DIRDEPS += ${DIRDEPS.$o.${MK_$o:U}:U} +# some options can depend on TARGET_SPEC! +DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \ + ${DEP_TARGET_SPEC:U${TARGET_SPEC}} \ + ${TARGET_SPEC_VARSr:U${TARGET_SPEC_VARS}:@v@${DEP_$v:U${$v}}@} +# note that we need to include $o in the variable _o$o +# to ensure correct evaluation. +.for o in ${DIRDEPS_OPTIONS} +.undef _o$o _v$o +.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST} +.if defined(MK_$o.$x) +_o$o ?= MK_$o.$x +_v$o ?= ${MK_$o.$x} +.endif .endfor +_v$o ?= ${MK_$o} +.if ${_debug_reldir:U0} +.info ${DEP_RELDIR:U${RELDIR}}.${DEP_TARGET_SPEC:U${TARGET_SPEC}}: o=$o ${_o$o:UMK_$o}=${_v$o:U} DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} +.endif +DIRDEPS += ${DIRDEPS.$o.${_v$o:U}:U} +.endfor DIRDEPS := ${DIRDEPS:O:u} +.if ${_debug_reldir:U0} +.info ${DEP_RELDIR:U${RELDIR}}: DIRDEPS=${DIRDEPS} +.endif # avoid cross contamination -.for o in ${DIRDEPS_OPTIONS:tu} +.for o in ${DIRDEPS_OPTIONS} .undef DIRDEPS.$o.yes .undef DIRDEPS.$o.no +.undef _o$o +.undef _v$o .endfor .else # whether options are enabled or not, # we want to filter out the relevant DIRDEPS.* # we should only be included by meta.autodep.mk # if dependencies are to be updated -.for o in ${DIRDEPS_OPTIONS:tu} +.for o in ${DIRDEPS_OPTIONS} .for d in ${DIRDEPS.$o.yes} ${DIRDEPS.$o.no} .if exists(${SRCTOP}/$d) GENDIRDEPS_FILTER += N$d* .elif exists(${SRCTOP}/${d:R}) GENDIRDEPS_FILTER += N${d:R}* .endif .endfor .endfor .endif Index: head/contrib/bmake/mk/dirdeps-targets.mk =================================================================== --- head/contrib/bmake/mk/dirdeps-targets.mk (revision 365365) +++ head/contrib/bmake/mk/dirdeps-targets.mk (revision 365366) @@ -1,136 +1,171 @@ # RCSid: -# $Id: dirdeps-targets.mk,v 1.10 2020/06/06 22:41:02 sjg Exp $ +# $Id: dirdeps-targets.mk,v 1.22 2020/08/15 18:00:11 sjg Exp $ # # @(#) Copyright (c) 2019-2020 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# 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. -# +# +# We will check each of the initial DIRDEPS for Makefile.dirdeps.options +# and include any found. +# This makes it feasible to tweak options like MK_DIRDEPS_CACHE +# for a specific target. +# +# If MK_STATIC_DIRDEPS_CACHE is defined we will check if the +# initial DIRDEPS has a static cache (Makefile.dirdeps.cache). +# This only makes sense for seriously expensive targets. +# .if ${.MAKE.LEVEL} == 0 # pickup customizations .-include # 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- # some .TARGETS need filtering DIRDEPS_TARGETS_FILTER += Nall # matching target dirs if any tdirs := ${.TARGETS:${DIRDEPS_TARGETS_FILTER:ts:}:${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) +DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@} +# some targets what to tweak options we might want to process now +.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,} +.-include <$m> +.endfor +.if defined(MK_STATIC_DIRDEPS_CACHE) +# some targets are very expensive to compute dirdeps for +# so we may have a static cache +.for c in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.cache,} +.if exists($c) +STATIC_DIRDEPS_CACHE ?= $c +.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" +DIRDEPS_CACHE ?= $c +MK_DIRDEPS_CACHE = yes +.endif +.endif +.endfor +.if defined(STATIC_DIRDEPS_CACHE) +.export STATIC_DIRDEPS_CACHE +.endif +.endif + +# allow a top-level makefile to do other stuff +# before including dirdeps.mk +.if ${MK_DIRDEPS_TARGETS_INCLUDE_DIRDEPS:Uyes} == "yes" .include +.endif DIRDEPS_TARGETS_SKIP += all clean* destroy* .for t in ${.TARGETS:${DIRDEPS_TARGETS_SKIP:${M_ListToSkip}}} $t: dirdeps -.endfor +.endfor .endif .endif Index: head/contrib/bmake/mk/dirdeps.mk =================================================================== --- head/contrib/bmake/mk/dirdeps.mk (revision 365365) +++ head/contrib/bmake/mk/dirdeps.mk (revision 365366) @@ -1,839 +1,867 @@ -# $Id: dirdeps.mk,v 1.106 2020/07/11 16:25:17 sjg Exp $ +# $Id: dirdeps.mk,v 1.125 2020/08/26 21:49:45 sjg Exp $ # 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 # 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 COPYRIGHT HOLDERS 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 COPYRIGHT # OWNER 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. # Much of the complexity here is for supporting cross-building. # If a tree does not support that, simply using plain Makefile.depend # should provide sufficient clue. # Otherwise the recommendation is to use Makefile.depend.${MACHINE} # as expected below. # Note: this file gets multiply included. # This is what we do with DIRDEPS # DIRDEPS: # This is a list of directories - relative to SRCTOP, it is # normally only of interest to .MAKE.LEVEL 0. # In some cases the entry may be qualified with a . # or . suffix (see TARGET_SPEC_VARS below), # for example to force building something for the pseudo # machines "host" or "common" regardless of current ${MACHINE}. -# +# # All unqualified entries end up being qualified with .${TARGET_SPEC} # and partially qualified (if TARGET_SPEC_VARS has multiple # entries) are also expanded to a full .. # The _DIRDEP_USE target uses the suffix to set TARGET_SPEC # correctly when visiting each entry. # # The fully qualified directory entries are used to construct a # dependency graph that will drive the build later. -# +# # Also, for each fully qualified directory target, we will search # using ${.MAKE.DEPENDFILE_PREFERENCE} to find additional # dependencies. We use Makefile.depend (default value for # .MAKE.DEPENDFILE_PREFIX) to refer to these makefiles to # distinguish them from others. -# +# # Before each Makefile.depend file is read, we set # DEP_RELDIR to be the RELDIR (path relative to SRCTOP) for # its directory, and DEP_MACHINE etc according to the . # represented by the suffix of the corresponding target. -# +# # Since each Makefile.depend file includes dirdeps.mk, this # processing is recursive and results in .MAKE.LEVEL 0 learning the # dependencies of the tree wrt the initial directory (_DEP_RELDIR). # -# BUILD_AT_LEVEL0 -# Indicates whether .MAKE.LEVEL 0 builds anything: -# if "no" sub-makes are used to build everything, -# if "yes" sub-makes are only used to build for other machines. -# It is best to use "no", but this can require fixing some -# makefiles to not do anything at .MAKE.LEVEL 0. -# # TARGET_SPEC_VARS # The default value is just MACHINE, and for most environments # this is sufficient. The _DIRDEP_USE target actually sets # both MACHINE and TARGET_SPEC to the suffix of the current # target so that in the general case TARGET_SPEC can be ignored. # # If more than MACHINE is needed then sys.mk needs to decompose # TARGET_SPEC and set the relevant variables accordingly. # It is important that MACHINE be included in and actually be # the first member of TARGET_SPEC_VARS. This allows other # variables to be considered optional, and some of the treatment # below relies on MACHINE being the first entry. # Note: TARGET_SPEC cannot contain any '.'s so the target # triple used by compiler folk won't work (directly anyway). # # For example: # # # Always list MACHINE first, # # other variables might be optional. # TARGET_SPEC_VARS = MACHINE TARGET_OS # .if ${TARGET_SPEC:Uno:M*,*} != "" # _tspec := ${TARGET_SPEC:S/,/ /g} # MACHINE := ${_tspec:[1]} # TARGET_OS := ${_tspec:[2]} # # etc. # # We need to stop that TARGET_SPEC affecting any submakes # # and deal with MACHINE=${TARGET_SPEC} in the environment. # TARGET_SPEC = # # export but do not track # .export-env TARGET_SPEC # .export ${TARGET_SPEC_VARS} # .for v in ${TARGET_SPEC_VARS:O:u} # .if empty($v) # .undef $v # .endif # .endfor # .endif # # make sure we know what TARGET_SPEC is # # as we may need it to find Makefile.depend* # TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} -# +# # The following variables can influence the initial DIRDEPS # computation with regard to the TARGET_SPECs that will be # built. # Most should also be considered by init.mk -# +# # ONLY_TARGET_SPEC_LIST # Defines a list of TARGET_SPECs for which the current # directory can be built. # If ALL_MACHINES is defined, we build for all the # TARGET_SPECs listed. # # ONLY_MACHINE_LIST # As for ONLY_TARGET_SPEC_LIST but only specifies # MACHINEs. # # NOT_TARGET_SPEC_LIST # A list of TARGET_SPECs for which the current # directory should not be built. # # NOT_MACHINE_LIST # A list of MACHINEs the current directory should not be # built for. # +# _build_xtra_dirs +# local.dirdeps.mk can add targets to this variable. +# They will be hooked into the build, but independent of +# any other DIRDEP. +# +# This allows for adding TESTS to the build, such that the build +# if any test fails, but without the risk of introducing +# circular dependencies. +now_utc ?= ${%s:L:gmtime} +.if !defined(start_utc) +start_utc := ${now_utc} +.endif + .if !target(bootstrap) && (make(bootstrap) || \ make(bootstrap-this) || \ make(bootstrap-recurse) || \ make(bootstrap-empty)) # disable most of below .MAKE.LEVEL = 1 .endif # touch this at your peril _DIRDEP_USE_LEVEL?= 0 .if ${.MAKE.LEVEL} == ${_DIRDEP_USE_LEVEL} # only the first instance is interested in all this .if !target(_DIRDEP_USE) # do some setup we only need once _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} -now_utc = ${%s:L:gmtime} -.if !defined(start_utc) -start_utc := ${now_utc} -.endif - .if ${MAKEFILE:T} == ${.PARSEFILE} && empty(DIRDEPS) && ${.TARGETS:Uall:M*/*} != "" # This little trick let's us do # # mk -f dirdeps.mk some/dir.${TARGET_SPEC} # all: ${.TARGETS:Nall}: all DIRDEPS := ${.TARGETS:M*[/.]*} # so that -DNO_DIRDEPS works DEP_RELDIR := ${DIRDEPS:[1]:R} # this will become DEP_MACHINE below TARGET_MACHINE := ${DIRDEPS:[1]:E:C/,.*//} .if ${TARGET_MACHINE:N*/*} == "" TARGET_MACHINE := ${MACHINE} .endif # disable DIRDEPS_CACHE as it does not like this trick MK_DIRDEPS_CACHE = no .endif # make sure we get the behavior we expect .MAKE.SAVE_DOLLARS = no # make sure these are empty to start with _DEP_TARGET_SPEC = # If TARGET_SPEC_VARS is other than just MACHINE # it should be set by sys.mk or similar by now. # TARGET_SPEC must not contain any '.'s. TARGET_SPEC_VARS ?= MACHINE # this is what we started with TARGET_SPEC = ${TARGET_SPEC_VARS:@v@${$v:U}@:ts,} # this is what we mostly use below DEP_TARGET_SPEC = ${TARGET_SPEC_VARS:S,^,DEP_,:@v@${$v:U}@:ts,} # make sure we have defaults .for v in ${TARGET_SPEC_VARS} DEP_$v ?= ${$v} .endfor .if ${TARGET_SPEC_VARS:[#]} > 1 # Ok, this gets more complex (putting it mildly). # In order to stay sane, we need to ensure that all the build_dirs # we compute below are fully qualified wrt DEP_TARGET_SPEC. # The makefiles may only partially specify (eg. MACHINE only), # so we need to construct a set of modifiers to fill in the gaps. .if ${MAKE_VERSION} >= 20170130 _tspec_x := ${TARGET_SPEC_VARS:range} .elif ${TARGET_SPEC_VARS:[#]} > 10 # seriously? better have jot(1) or equivalent to produce suitable sequence _tspec_x := ${${JOT:Ujot} ${TARGET_SPEC_VARS:[#]}:L:sh} .else # we can provide the sequence ourselves _tspec_x := ${1 2 3 4 5 6 7 8 9 10:L:[1..${TARGET_SPEC_VARS:[#]}]} .endif # this handles unqualified entries M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC}; # there needs to be at least one item missing for these to make sense .for i in ${_tspec_x:[2..-1]} _tspec_m$i := ${TARGET_SPEC_VARS:[2..$i]:@w@[^,]+@:ts,} _tspec_a$i := ,${TARGET_SPEC_VARS:[$i..-1]:@v@$$$${DEP_$v}@:ts,} M_dep_qual_fixes += C;(\.${_tspec_m$i})$$;\1${_tspec_a$i}; .endfor +TARGET_SPEC_VARSr := ${TARGET_SPEC_VARS:[-1..1]} .else # A harmless? default. M_dep_qual_fixes = U .endif .if !defined(.MAKE.DEPENDFILE_PREFERENCE) # .MAKE.DEPENDFILE_PREFERENCE makes the logic below neater? # you really want this set by sys.mk or similar .MAKE.DEPENDFILE_PREFERENCE = ${_CURDIR}/${.MAKE.DEPENDFILE:T} .if ${.MAKE.DEPENDFILE:E} == "${TARGET_SPEC}" .if ${TARGET_SPEC} != ${MACHINE} .MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R}.$${MACHINE} .endif .MAKE.DEPENDFILE_PREFERENCE += ${_CURDIR}/${.MAKE.DEPENDFILE:T:R} .endif .endif _default_dependfile := ${.MAKE.DEPENDFILE_PREFERENCE:[1]:T} _machine_dependfiles := ${.MAKE.DEPENDFILE_PREFERENCE:T:M*${MACHINE}*} # for machine specific dependfiles we require ${MACHINE} to be at the end # also for the sake of sanity we require a common prefix .if !defined(.MAKE.DEPENDFILE_PREFIX) # knowing .MAKE.DEPENDFILE_PREFIX helps .if !empty(_machine_dependfiles) .MAKE.DEPENDFILE_PREFIX := ${_machine_dependfiles:[1]:T:R} .else .MAKE.DEPENDFILE_PREFIX := ${_default_dependfile:T} .endif .endif # this is how we identify non-machine specific dependfiles N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}} .endif # !target(_DIRDEP_USE) # First off, we want to know what ${MACHINE} to build for. # This can be complicated if we are using a mixture of ${MACHINE} specific # and non-specific Makefile.depend* # if we were included recursively _DEP_TARGET_SPEC should be valid. .if empty(_DEP_TARGET_SPEC) # we may or may not have included a dependfile yet .if defined(.INCLUDEDFROMFILE) _last_dependfile := ${.INCLUDEDFROMFILE:M${.MAKE.DEPENDFILE_PREFIX}*} .else _last_dependfile := ${.MAKE.MAKEFILES:M*/${.MAKE.DEPENDFILE_PREFIX}*:[-1]} .endif .if ${_debug_reldir:U0} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _last_dependfile='${_last_dependfile}' .endif .if empty(_last_dependfile) || ${_last_dependfile:E:${N_notmachine}} == "" # this is all we have to work with DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}} _DEP_TARGET_SPEC := ${DEP_TARGET_SPEC} .else _DEP_TARGET_SPEC = ${_last_dependfile:${M_dep_qual_fixes:ts:}:E} .endif .if !empty(_last_dependfile) # record that we've read dependfile for this _dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: .endif .endif # by now _DEP_TARGET_SPEC should be set, parse it. .if ${TARGET_SPEC_VARS:[#]} > 1 # we need to parse DEP_MACHINE may or may not contain more info _tspec := ${_DEP_TARGET_SPEC:S/,/ /g} .for i in ${_tspec_x} DEP_${TARGET_SPEC_VARS:[$i]} := ${_tspec:[$i]} .endfor .for v in ${TARGET_SPEC_VARS:O:u} .if empty(DEP_$v) .undef DEP_$v .endif .endfor .else DEP_MACHINE := ${_DEP_TARGET_SPEC} .endif # reset each time through _build_all_dirs = +_build_xtra_dirs = # the first time we are included the _DIRDEP_USE target will not be defined # we can use this as a clue to do initialization and other one time things. .if !target(_DIRDEP_USE) # make sure this target exists dirdeps: beforedirdeps .WAIT beforedirdeps: # We normally expect to be included by Makefile.depend.* # which sets the DEP_* macros below. DEP_RELDIR ?= ${RELDIR} # this can cause lots of output! # set to a set of glob expressions that might match RELDIR DEBUG_DIRDEPS ?= no # remember the initial value of DEP_RELDIR - we test for it below. _DEP_RELDIR := ${DEP_RELDIR} .endif # DIRDEPS_CACHE can be very handy for debugging. # Also if repeatedly building the same target, # we can avoid the overhead of re-computing the tree dependencies. MK_DIRDEPS_CACHE ?= no BUILD_DIRDEPS_CACHE ?= no BUILD_DIRDEPS ?= yes .if ${MK_DIRDEPS_CACHE} == "yes" # this is where we will cache all our work -DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${.TARGETS:Nall:O:u:ts-:S,/,_,g:S,^,.,:N.} +DIRDEPS_CACHE ?= ${_OBJDIR:tA}/dirdeps.cache${_TARGETS:U${.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. .-include .if !target(_DIRDEP_USE) # things we skip for host tools SKIP_HOSTDIR ?= NSkipHostDir = ${SKIP_HOSTDIR:N*.host*:S,$,.host*,:N.host*:S,^,${SRCTOP}/,:${M_ListToSkip}} # things we always skip # SKIP_DIRDEPS allows for adding entries on command line. SKIP_DIR += .host *.WAIT ${SKIP_DIRDEPS} SKIP_DIR.host += ${SKIP_HOSTDIR} DEP_SKIP_DIR = ${SKIP_DIR} \ ${SKIP_DIR.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${SKIP_DIR.${DEP_$v}:U}@} \ ${SKIP_DIRDEPS.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${SKIP_DIRDEPS.${DEP_$v}:U}@} NSkipDir = ${DEP_SKIP_DIR:${M_ListToSkip}} .if defined(NODIRDEPS) || defined(WITHOUT_DIRDEPS) NO_DIRDEPS = .elif defined(WITHOUT_DIRDEPS_BELOW) NO_DIRDEPS_BELOW = .endif .if defined(NO_DIRDEPS) # confine ourselves to the original dir and below. DIRDEPS_FILTER += M${_DEP_RELDIR}* .elif defined(NO_DIRDEPS_BELOW) DIRDEPS_FILTER += M${_DEP_RELDIR} .endif # this is what we run below DIRDEP_MAKE ?= ${.MAKE} DIRDEP_DIR ?= ${.TARGET:R} +# if you want us to report load averages during build +# DIRDEP_USE_PRELUDE += ${DIRDEP_LOADAVG_REPORT}; + +DIRDEP_LOADAVG_CMD ?= ${UPTIME:Uuptime} | sed 's,.*\(load\),\1,' +DIRDEP_LOADAVG_LAST = 0 +# yes the expression here is a bit complicated, +# the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}} +# when we want to report. +DIRDEP_LOADAVG_REPORT = \ + test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST}:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ + echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`" + # we suppress SUBDIR when visiting the leaves # we assume sys.mk will set MACHINE_ARCH # you can add extras to DIRDEP_USE_ENV # if there is no makefile in the target directory, we skip it. _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} ..."; \ + echo "${TRACER}Checking ${.TARGET:S,${SRCTOP}/,,} for ${.TARGET:E} ..."; \ ${DIRDEP_USE_PRELUDE} \ MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \ TARGET_SPEC=${.TARGET:E} \ MACHINE=${.TARGET:E} \ ${DIRDEP_MAKE} -C ${DIRDEP_DIR} || exit 1; \ break; \ done .ifdef ALL_MACHINES # this is how you limit it to only the machines we have been built for # previously. .if empty(ONLY_TARGET_SPEC_LIST) && empty(ONLY_MACHINE_LIST) .if !empty(ALL_MACHINE_LIST) # ALL_MACHINE_LIST is the list of all legal machines - ignore anything else _machine_list != cd ${_CURDIR} && 'ls' -1 ${ALL_MACHINE_LIST:O:u:@m@${.MAKE.DEPENDFILE:T:R}.$m@} 2> /dev/null; echo .else _machine_list != 'ls' -1 ${_CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.* 2> /dev/null; echo .endif _only_machines := ${_machine_list:${NIgnoreFiles:UN*.bak}:E:O:u} .else _only_machines := ${ONLY_TARGET_SPEC_LIST:U} ${ONLY_MACHINE_LIST:U} .endif .if empty(_only_machines) # we must be boot-strapping _only_machines := ${TARGET_MACHINE:U${ALL_MACHINE_LIST:U${DEP_MACHINE}}} .endif .else # ! ALL_MACHINES # if ONLY_TARGET_SPEC_LIST or ONLY_MACHINE_LIST is set, we are limited to that. # Note that ONLY_TARGET_SPEC_LIST should be fully qualified. # if TARGET_MACHINE is set - it is really the same as ONLY_MACHINE_LIST # otherwise DEP_MACHINE is it - so DEP_MACHINE will match. _only_machines := ${ONLY_TARGET_SPEC_LIST:U:M${DEP_MACHINE},*} .if empty(_only_machines) _only_machines := ${ONLY_MACHINE_LIST:U${TARGET_MACHINE:U${DEP_MACHINE}}:M${DEP_MACHINE}} .endif .endif .if !empty(NOT_MACHINE_LIST) _only_machines := ${_only_machines:${NOT_MACHINE_LIST:${M_ListToSkip}}} .endif .if !empty(NOT_TARGET_SPEC_LIST) # we must first qualify _dm := ${DEP_MACHINE} _only_machines := ${_only_machines:M*,*} ${_only_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,} DEP_MACHINE := ${_dm} _only_machines := ${_only_machines:${NOT_TARGET_SPEC_LIST:${M_ListToSkip}}} .endif # clean up _only_machines := ${_only_machines:O:u} # make sure we have a starting place? DIRDEPS ?= ${RELDIR} .endif # target .if !defined(NO_DIRDEPS) && !defined(NO_DIRDEPS_BELOW) .if ${MK_DIRDEPS_CACHE} == "yes" # just ensure this exists build-dirdeps: M_oneperline = @x@\\${.newline} $$x@ .if ${BUILD_DIRDEPS_CACHE} == "no" .if !target(dirdeps-cached) # we do this via sub-make BUILD_DIRDEPS = no # ignore anything but these .MAKE.META.IGNORE_FILTER = M*/${.MAKE.DEPENDFILE_PREFIX}* dirdeps: dirdeps-cached dirdeps-cached: ${DIRDEPS_CACHE} .MAKE @echo "${TRACER}Using ${DIRDEPS_CACHE}" @MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \ dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no # these should generally do -BUILD_DIRDEPS_MAKEFILE ?= ${MAKEFILE} +BUILD_DIRDEPS_MAKEFILE ?= BUILD_DIRDEPS_TARGETS ?= ${.TARGETS} +.if ${DIRDEPS_CACHE} != ${STATIC_DIRDEPS_CACHE:Uno} && ${DIRDEPS_CACHE:M${SRCTOP}/*} == "" +# export this for dirdeps-cache-update.mk +DYNAMIC_DIRDEPS_CACHE := ${DIRDEPS_CACHE} +.export DYNAMIC_DIRDEPS_CACHE # we need the .meta file to ensure we update if # any of the Makefile.depend* changed. # We do not want to compare the command line though. ${DIRDEPS_CACHE}: .META .NOMETA_CMP +@{ echo '# Autogenerated - do NOT edit!'; echo; \ echo 'BUILD_DIRDEPS=no'; echo; \ echo '.include '; echo; \ } > ${.TARGET}.new +@MAKELEVEL=${.MAKE.LEVEL} DIRDEPS_CACHE=${DIRDEPS_CACHE} \ DIRDEPS="${DIRDEPS}" \ TARGET_SPEC=${TARGET_SPEC} \ - MAKEFLAGS= ${.MAKE} -C ${_CURDIR} -f ${BUILD_DIRDEPS_MAKEFILE} \ + MAKEFLAGS= ${DIRDEP_CACHE_MAKE:U${.MAKE}} -C ${_CURDIR} \ + ${BUILD_DIRDEPS_MAKEFILE} \ ${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 && \ + 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \ mv ${.TARGET}.new ${.TARGET} .endif +.endif .elif !target(_count_dirdeps) # we want to capture the dirdeps count in the cache .END: _count_dirdeps _count_dirdeps: .NOMETA - @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]}'; } >&3 + @{ echo; echo '.info $${.newline}$${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS}'; } >&3 .endif .elif !make(dirdeps) && !target(_count_dirdeps) beforedirdeps: _count_dirdeps _count_dirdeps: .NOMETA - @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} seconds=`expr ${now_utc} - ${start_utc}`" + @echo "${TRACER}Makefiles read: total=${.MAKE.MAKEFILES:[#]} depend=${.MAKE.MAKEFILES:M*depend*:[#]} dirdeps=${.ALLTARGETS:M${SRCTOP}*:O:u:[#]} ${DIRDEP_INFO_XTRAS} seconds=`expr ${now_utc} - ${start_utc}`" .endif .endif .if ${BUILD_DIRDEPS} == "yes" # the rest is done repeatedly for every Makefile.depend we read. # if we are anything but the original dir we care only about the # machine type we were included for.. .if ${DEP_RELDIR} == "." _this_dir := ${SRCTOP} .else _this_dir := ${SRCTOP}/${DEP_RELDIR} .endif # on rare occasions, there can be a need for extra help _dep_hack := ${_this_dir}/${.MAKE.DEPENDFILE_PREFIX}.inc .-include <${_dep_hack}> .-include <${_dep_hack:R}.options> .if ${DEP_RELDIR} != ${_DEP_RELDIR} || ${DEP_TARGET_SPEC} != ${TARGET_SPEC} # this should be all _machines := ${DEP_MACHINE} .else # this is the machine list we actually use below _machines := ${_only_machines} .if defined(HOSTPROG) || ${DEP_MACHINE:Nhost*} == "" # we need to build this guy's dependencies for host as well. .if ${DEP_MACHINE:Nhost*} == "" _machines += ${DEP_MACHINE} .else _machines += host .endif .endif _machines := ${_machines:O:u} .endif .if ${TARGET_SPEC_VARS:[#]} > 1 # we need to tweak _machines _dm := ${DEP_MACHINE} # apply the same filtering that we do when qualifying DIRDEPS. # M_dep_qual_fixes expects .${MACHINE}* so add (and remove) '.' # Again we expect that any already qualified machines are fully qualified. _machines := ${_machines:M*,*} ${_machines:N*,*:@DEP_MACHINE@${DEP_TARGET_SPEC}@:S,^,.,:${M_dep_qual_fixes:ts:}:O:u:S,^.,,} DEP_MACHINE := ${_dm} _machines := ${_machines:O:u} .endif # reset each time through _build_dirs = .if ${DEP_RELDIR} == ${_DEP_RELDIR} # pickup other machines for this dir if necessary -.if ${BUILD_AT_LEVEL0:Uyes} == "no" _build_dirs += ${_machines:@m@${_CURDIR}.$m@} -.else -_build_dirs += ${_machines:N${DEP_TARGET_SPEC}:@m@${_CURDIR}.$m@} -.if ${DEP_TARGET_SPEC} == ${TARGET_SPEC} -# pickup local dependencies now -.if ${MAKE_VERSION} < 20160220 -.-include <.depend> -.else -.dinclude <.depend> .endif -.endif -.endif -.endif .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}' .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}' .endif .if !empty(DIRDEPS) # these we reset each time through as they can depend on DEP_MACHINE DEP_DIRDEPS_FILTER = \ ${DIRDEPS_FILTER.${DEP_TARGET_SPEC}:U} \ ${TARGET_SPEC_VARS:@v@${DIRDEPS_FILTER.${DEP_$v}:U}@} \ ${DIRDEPS_FILTER:U} .if empty(DEP_DIRDEPS_FILTER) # something harmless DEP_DIRDEPS_FILTER = U .endif # this is what we start with __depdirs := ${DIRDEPS:${NSkipDir}:${DEP_DIRDEPS_FILTER:ts:}:C,//+,/,g:O:u:@d@${SRCTOP}/$d@} # some entries may be qualified with . # the :M*/*/*.* just tries to limit the dirs we check to likely ones. # the ${d:E:M*/*} ensures we don't consider junos/usr.sbin/mgd __qual_depdirs := ${__depdirs:M*/*/*.*:@d@${exists($d):?:${"${d:E:M*/*}":?:${exists(${d:R}):?$d:}}}@} __unqual_depdirs := ${__depdirs:${__qual_depdirs:Uno:${M_ListToSkip}}} .if ${DEP_RELDIR} == ${_DEP_RELDIR} # if it was called out - we likely need it. __hostdpadd := ${DPADD:U.:M${HOST_OBJTOP}/*:S,${HOST_OBJTOP}/,,:H:${NSkipDir}:${DIRDEPS_FILTER:ts:}:S,$,.host,:N.*:@d@${SRCTOP}/$d@} \ ${DPADD:U.:M${HOST_OBJTOP32:Uno}/*:S,${HOST_OBJTOP32:Uno}/,,:H:${NSkipDir}:${DIRDEPS_FILTER:ts:}:S,$,.host32,:N.*:@d@${SRCTOP}/$d@} __qual_depdirs += ${__hostdpadd} .endif .if ${_debug_reldir} .info depdirs=${__depdirs} .info qualified=${__qual_depdirs} .info unqualified=${__unqual_depdirs} .endif # _build_dirs is what we will feed to _DIRDEP_USE _build_dirs += \ ${__qual_depdirs:M*.host:${NSkipHostDir}:N.host} \ ${__qual_depdirs:N*.host} \ ${_machines:Mhost*:@m@${__unqual_depdirs:@d@$d.$m@}@:${NSkipHostDir}:N.host} \ ${_machines:Nhost*:@m@${__unqual_depdirs:@d@$d.$m@}@} # qualify everything now _build_dirs := ${_build_dirs:${M_dep_qual_fixes:ts:}:O:u} .endif # empty DIRDEPS -_build_all_dirs += ${_build_dirs} +_build_all_dirs += ${_build_dirs} ${_build_xtra_dirs} _build_all_dirs := ${_build_all_dirs:O:u} # Normally if doing make -V something, # we do not want to waste time chasing DIRDEPS # but if we want to count the number of Makefile.depend* read, we do. .if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS}} == "" .if !empty(_build_all_dirs) .if ${BUILD_DIRDEPS_CACHE} == "yes" x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3 # guard against _new_dirdeps being too big for a single command line _new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@} -.if !empty(_new_dirdeps) -.export _new_dirdeps -x!= echo; { echo; echo "dirdeps: \\"; \ - for x in $$_new_dirdeps; do echo " $$x \\"; done; echo; \ - for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3 -.endif +.export _build_xtra_dirs _new_dirdeps .if !empty(DEP_EXPORT_VARS) # Discouraged, but there are always exceptions. # Handle it here rather than explain how. x!= echo; { echo; ${DEP_EXPORT_VARS:@v@echo '$v=${$v}';@} echo '.export ${DEP_EXPORT_VARS}'; echo; } >&3 .endif .else # this makes it all happen dirdeps: ${_build_all_dirs} .endif ${_build_all_dirs}: _DIRDEP_USE .if ${_debug_reldir} .info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs} .endif .if !empty(DEP_EXPORT_VARS) .export ${DEP_EXPORT_VARS} DEP_EXPORT_VARS= .endif # this builds the dependency graph .for m in ${_machines} .if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs) -x!= echo; { echo; echo "${_this_dir}.$m: \\"; } >&3 +x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3 +_cache_deps = .endif # it would be nice to do :N${.TARGET} .if !empty(__qual_depdirs) .for q in ${__qual_depdirs:${M_dep_qual_fixes:ts:}:E:O:u:N$m} .if ${_debug_reldir} || ${DEBUG_DIRDEPS:@x@${${DEP_RELDIR}.$m:L:M$x}${${DEP_RELDIR}.$q:L:M$x}@} != "" .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" -_cache_deps := ${_build_dirs:M*.$q} -.if !empty(_cache_deps) -.export _cache_deps -x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3 -.endif +_cache_deps += ${_build_dirs:M*.$q} .else ${_this_dir}.$m: ${_build_dirs:M*.$q} .endif .endfor .endif .if ${_debug_reldir} .info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m} .endif .if ${BUILD_DIRDEPS_CACHE} == "yes" -_cache_deps := ${_build_dirs:M*.$m:N${_this_dir}.$m} +.if !empty(_build_dirs) +_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m} .if !empty(_cache_deps) .export _cache_deps x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3 .endif +x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \ + echo; echo 'dirdeps: ${_this_dir}.$m \'; \ + for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \ + echo; for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3 +.endif .else ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m} .endif .endfor .endif # Now find more dependencies - and recurse. .for d in ${_build_all_dirs} .if !target(_dirdeps_checked.$d) # once only _dirdeps_checked.$d: .if ${_debug_search} .info checking $d .endif # Note: _build_all_dirs is fully qualifed so d:R is always the directory .if exists(${d:R}) # we pass _DEP_TARGET_SPEC to tell the next step what we want _DEP_TARGET_SPEC := ${d:E} # some makefiles may still look at this _DEP_MACHINE := ${d:E:C/,.*//} # set these too in case Makefile.depend* uses them .if ${TARGET_SPEC_VARS:[#]} > 1 _dtspec := ${_DEP_TARGET_SPEC:S/,/ /g} .for i in ${_tspec_x} DEP_${TARGET_SPEC_VARS:[$i]} := ${_dtspec:[$i]} .endfor .else DEP_MACHINE := ${_DEP_MACHINE} .endif # Warning: there is an assumption here that MACHINE is always # the first entry in TARGET_SPEC_VARS. # If TARGET_SPEC and MACHINE are insufficient, you have a problem. _m := ${.MAKE.DEPENDFILE_PREFERENCE:T:S;${TARGET_SPEC}$;${d:E};:S;${MACHINE};${d:E:C/,.*//};:@m@${exists(${d:R}/$m):?${d:R}/$m:}@:[1]} .if !empty(_m) # M_dep_qual_fixes isn't geared to Makefile.depend _qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}} .if ${_debug_search} .info Looking for ${_qm} .endif # set this "just in case" # we can skip :tA since we computed the path above DEP_RELDIR := ${_m:H:S,${SRCTOP}/,,} # and reset this DIRDEPS = .if ${_debug_reldir} && ${_qm} != ${_m} .info loading ${_m} for ${d:E} .endif .include <${_m}> .else .-include .endif .endif .endif .endfor .endif # -V .endif # BUILD_DIRDEPS .elif ${.MAKE.LEVEL} > 42 .error You should have stopped recursing by now. .else # we are building something DEP_RELDIR := ${RELDIR} _DEP_RELDIR := ${RELDIR} # Since we are/should be included by .MAKE.DEPENDFILE # This is a final opportunity to add/hook global rules. .-include + +# skip _reldir_{finish,failed} if not included from Makefile.depend* +# or not in meta mode +.if !defined(WITHOUT_META_STATS) && ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" && ${.MAKE.MODE:Mmeta} != "" + +meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \ + created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}} + +.if !target(_reldir_finish) +.END: _reldir_finish +_reldir_finish: .NOMETA + @echo "${TRACER}Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" +.endif + +.if !target(_reldir_failed) +.ERROR: _reldir_failed +_reldir_failed: .NOMETA + @echo "${TRACER}Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" +.endif + +.endif # pickup local dependencies .if ${MAKE_VERSION} < 20160220 .-include <.depend> .else .dinclude <.depend> .endif .endif # bootstrapping new dependencies made easy? .if !target(bootstrap) && (make(bootstrap) || \ make(bootstrap-this) || \ make(bootstrap-recurse) || \ make(bootstrap-empty)) # if we are bootstrapping create the default _want = ${.CURDIR}/${.MAKE.DEPENDFILE_DEFAULT:T} .if exists(${_want}) # stop here ${.TARGETS:Mboot*}: .elif !make(bootstrap-empty) # find a Makefile.depend to use as _src _src != cd ${.CURDIR} && for m in ${.MAKE.DEPENDFILE_PREFERENCE:T:S,${MACHINE},*,}; do test -s $$m || continue; echo $$m; break; done; echo .if empty(_src) .error cannot find any of ${.MAKE.DEPENDFILE_PREFERENCE:T}${.newline}Use: bootstrap-empty .endif _src?= ${.MAKE.DEPENDFILE} .MAKE.DEPENDFILE_BOOTSTRAP_SED+= -e 's/${_src:E:C/,.*//}/${MACHINE}/g' # just create Makefile.depend* for this dir bootstrap-this: .NOTMAIN @echo Bootstrapping ${RELDIR}/${_want:T} from ${_src:T}; \ echo You need to build ${RELDIR} to correctly populate it. .if ${_src:T} != ${.MAKE.DEPENDFILE_PREFIX:T} (cd ${.CURDIR} && sed ${.MAKE.DEPENDFILE_BOOTSTRAP_SED} ${_src} > ${_want:T}) .else cp ${.CURDIR}/${_src:T} ${_want} .endif # create Makefile.depend* for this dir and its dependencies bootstrap: bootstrap-recurse bootstrap-recurse: bootstrap-this _mf := ${.PARSEFILE} bootstrap-recurse: .NOTMAIN .MAKE @cd ${SRCTOP} && \ for d in `cd ${RELDIR} && ${.MAKE} -B -f ${"${.MAKEFLAGS:M-n}":?${_src}:${.MAKE.DEPENDFILE:T}} -V DIRDEPS`; do \ test -d $$d || d=$${d%.*}; \ test -d $$d || continue; \ echo "Checking $$d for bootstrap ..."; \ (cd $$d && ${.MAKE} -f ${_mf} bootstrap-recurse); \ done .endif # create an empty Makefile.depend* to get the ball rolling. bootstrap-empty: .NOTMAIN .NOMETA @echo Creating empty ${RELDIR}/${_want:T}; \ echo You need to build ${RELDIR} to correctly populate it. @{ echo DIRDEPS=; echo ".include "; } > ${_want} .endif Index: head/contrib/bmake/mk/dpadd.mk =================================================================== --- head/contrib/bmake/mk/dpadd.mk (revision 365365) +++ head/contrib/bmake/mk/dpadd.mk (revision 365366) @@ -1,339 +1,339 @@ -# $Id: dpadd.mk,v 1.27 2019/05/17 13:58:53 sjg Exp $ +# $Id: dpadd.mk,v 1.28 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2004, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # 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}__: # sometimes we play games with .CURDIR etc # _* hold the original values of .* _OBJDIR?= ${.OBJDIR} _CURDIR?= ${.CURDIR} .if ${_CURDIR} == ${SRCTOP} RELDIR=. RELTOP=. .else RELDIR?= ${_CURDIR:S,${SRCTOP}/,,} .if ${RELDIR} == ${_CURDIR} RELDIR?= ${_OBJDIR:S,${OBJTOP}/,,} .endif RELTOP?= ${RELDIR:C,[^/]+,..,g} .endif RELOBJTOP?= ${OBJTOP} RELSRCTOP?= ${SRCTOP} # we get included just about everywhere so this is handy... -# C*DEBUG_XTRA are for defining on cmd line etc +# C*DEBUG_XTRA are for defining on cmd line etc # so do not use in makefiles. .ifdef CFLAGS_DEBUG_XTRA CFLAGS_LAST += ${CFLAGS_DEBUG_XTRA} .endif .ifdef CXXFLAGS_DEBUG_XTRA CXXFLAGS_LAST += ${CXXFLAGS_DEBUG_XTRA} .endif .-include # DPLIBS helps us ensure we keep DPADD and LDADD in sync DPLIBS+= ${DPLIBS_LAST} DPADD+= ${DPLIBS:N-*} .for __lib in ${DPLIBS} .if "${__lib:M-*}" != "" LDADD += ${__lib} .else LDADD += ${LDADD_${__lib:T:R}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}} .endif .endfor # DPADD can contain things other than libs __dpadd_libs := ${DPADD:M*/lib*} .if defined(PROG) && ${MK_PROG_LDORDER_MK:Uno} != "no" # some libs have dependencies... # DPLIBS_* allows bsd.libnames.mk to flag libs which must be included # in DPADD for a given library. # Gather all such dependencies into __ldadd_all_xtras # dups will be dealt with later. # Note: libfoo_pic uses DPLIBS_libfoo __ldadd_all_xtras= .for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}}@} __ldadd_all_xtras+= ${LDADD_${__lib}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}} .if "${DPADD:M${__lib}}" == "" DPADD+= ${__lib} .endif .endfor .endif # Last of all... for libc and libgcc DPADD+= ${DPADD_LAST} # de-dupuplicate __ldadd_all_xtras into __ldadd_xtras # in reverse order so that libs end up listed after all that needed them. __ldadd_xtras= .for __lib in ${__ldadd_all_xtras:[-1..1]} .if "${__ldadd_xtras:M${__lib}}" == "" || ${NEED_IMPLICIT_LDADD:tl:Uno} != "no" __ldadd_xtras+= ${__lib} .endif .endfor .if !empty(__ldadd_xtras) # now back to the original order __ldadd_xtras:= ${__ldadd_xtras:[-1..1]} LDADD+= ${__ldadd_xtras} .endif # Convert DPADD into -I and -L options and add them to CPPFLAGS and LDADD # For the -I's convert the path to a relative one. For separate objdirs # the DPADD paths will be to the obj tree so we need to subst anyway. # update this __dpadd_libs := ${DPADD:M*/lib*} # Order -L's to search ours first. # Avoids picking up old versions already installed. __dpadd_libdirs := ${__dpadd_libs:R:H:S/^/-L/g:O:u:N-L} LDADD += ${__dpadd_libdirs:M-L${OBJTOP}/*} LDADD += ${__dpadd_libdirs:N-L${OBJTOP}/*:N-L${HOST_LIBDIR:U/usr/lib}} .if defined(HOST_LIBDIR) && ${HOST_LIBDIR} != "/usr/lib" LDADD+= -L${HOST_LIBDIR} .endif .if !make(dpadd) .ifdef LIB # Each lib is its own src_lib, we want to include it in SRC_LIBS # so that the correct INCLUDES_* will be picked up automatically. SRC_LIBS+= ${_OBJDIR}/lib${LIB}.a .endif .endif -# +# # This little bit of magic, assumes that SRC_libfoo will be # set if it cannot be correctly derrived from ${LIBFOO} # Note that SRC_libfoo and INCLUDES_libfoo should be named for the # actual library name not the variable name that might refer to it. # 99% of the time the two are the same, but the DPADD logic # only has the library name available, so stick to that. -# +# SRC_LIBS?= # magic_libs includes those we want to link with # as well as those we might look at __dpadd_magic_libs += ${__dpadd_libs} ${SRC_LIBS} DPMAGIC_LIBS += ${__dpadd_magic_libs} \ ${__dpadd_magic_libs:@d@${DPMAGIC_LIBS_${d:T:R}}@} # we skip this for staged libs .for __lib in ${DPMAGIC_LIBS:O:u:N${STAGE_OBJTOP:Unot}*/lib/*} -# +# # if SRC_libfoo is not set, then we assume that the srcdir corresponding # to where we found the library is correct. # SRC_${__lib:T:R} ?= ${__lib:H:S,${OBJTOP},${RELSRCTOP},} # # This is a no-brainer but just to be complete... # OBJ_${__lib:T:R} ?= ${__lib:H:S,${OBJTOP},${RELOBJTOP},} # # If INCLUDES_libfoo is not set, then we'll use ${SRC_libfoo}/h if it exists, # else just ${SRC_libfoo}. # INCLUDES_${__lib:T:R}?= -I${exists(${SRC_${__lib:T:R}}/h):?${SRC_${__lib:T:R}}/h:${SRC_${__lib:T:R}}} .endfor -# even for staged libs we sometimes -# need to allow direct -I to avoid cicular dependencies +# even for staged libs we sometimes +# need to allow direct -I to avoid cicular dependencies .for __lib in ${DPMAGIC_LIBS:O:u:T:R} .if !empty(SRC_${__lib}) && empty(INCLUDES_${__lib}) # must be a staged lib .if exists(${SRC_${__lib}}/h) INCLUDES_${__lib} = -I${SRC_${__lib}}/h .else INCLUDES_${__lib} = -I${SRC_${__lib}} .endif .endif .endfor # when linking a shared lib, avoid non pic libs SHLDADD+= ${LDADD:N-[lL]*} .for __lib in ${__dpadd_libs:u} .if defined(SHLIB_NAME) && ${LDADD:M-l${__lib:T:R:S,lib,,}} != "" .if ${__lib:T:N*_pic.a:N*.so} == "" || exists(${__lib:R}.so) SHLDADD+= -l${__lib:T:R:S,lib,,} .elif exists(${__lib:R}_pic.a) SHLDADD+= -l${__lib:T:R:S,lib,,}_pic .else .warning ${RELDIR}.${TARGET_SPEC} needs ${__lib:T:R}_pic.a SHLDADD+= -l${__lib:T:R:S,lib,,} .endif SHLDADD+= -L${__lib:H} .endif .endfor # Now for the bits we actually need __dpadd_incs= .for __lib in ${__dpadd_libs:u} .if (make(${PROG}_p) || defined(NEED_GPROF)) && exists(${__lib:R}_p.a) __ldadd=-l${__lib:T:R:S,lib,,} LDADD := ${LDADD:S,^${__ldadd}$,${__ldadd}_p,g} .endif .endfor # # We take care of duplicate suppression later. # don't apply :T:R too early __dpadd_incs += ${__dpadd_magic_libs:u:@x@${INCLUDES_${x:T:R}}@} __dpadd_incs += ${__dpadd_magic_libs:O:u:@s@${SRC_LIBS_${s:T:R}:U}@:@x@${INCLUDES_${x:T:R}}@} __dpadd_last_incs += ${__dpadd_magic_libs:u:@x@${INCLUDES_LAST_${x:T:R}}@} __dpadd_last_incs += ${__dpadd_magic_libs:O:u:@s@${SRC_LIBS_${s:T:R}:U}@:@x@${INCLUDES_LAST_${x:T:R}}@} .if defined(HOSTPROG) || ${MACHINE:Nhost*} == "" # we want any -I/usr/* last __dpadd_last_incs := \ ${__dpadd_last_incs:N-I/usr/*} \ ${__dpadd_incs:M-I/usr/*} \ - ${__dpadd_last_incs:M-I/usr/*} + ${__dpadd_last_incs:M-I/usr/*} __dpadd_incs := ${__dpadd_incs:N-I/usr/*} .endif # # eliminate any duplicates - but don't mess with the order # force evaluation now - to avoid giving make a headache # .for t in CFLAGS CXXFLAGS # avoid duplicates __$t_incs:=${$t:M-I*:O:u} .for i in ${__dpadd_incs} .if "${__$t_incs:M$i}" == "" $t+= $i __$t_incs+= $i .endif .endfor .endfor .for t in CFLAGS_LAST CXXFLAGS_LAST # avoid duplicates __$t_incs:=${$t:M-I*:u} .for i in ${__dpadd_last_incs} .if "${__$t_incs:M$i}" == "" $t+= $i __$t_incs+= $i .endif .endfor .endfor # This target is used to gather a list of # dir: ${DPADD} # entries .if make(*dpadd*) .if !target(dpadd) dpadd: .NOTMAIN .if defined(DPADD) && ${DPADD} != "" @echo "${RELDIR}: ${DPADD:S,${OBJTOP}/,,}" .endif .endif .endif .ifdef SRC_PATHADD -# We don't want to assume that we need to .PATH every element of +# We don't want to assume that we need to .PATH every element of # SRC_LIBS, but the Makefile cannot do # .PATH: ${SRC_libfoo} # since the value of SRC_libfoo must be available at the time .PATH: -# is read - and we only just worked it out. -# Further, they can't wait until after include of {lib,prog}.mk as +# is read - and we only just worked it out. +# Further, they can't wait until after include of {lib,prog}.mk as # the .PATH is needed before then. # So we let the Makefile do # SRC_PATHADD+= ${SRC_libfoo} # and we defer the .PATH: until now so that SRC_libfoo will be available. .PATH: ${SRC_PATHADD} .endif # after all that, if doing -n we don't care .if ${.MAKEFLAGS:Ux:M-n} != "" DPADD = .elif ${.MAKE.MODE:Mmeta*} != "" && exists(${.MAKE.DEPENDFILE}) DPADD_CLEAR_DPADD ?= yes .if ${DPADD_CLEAR_DPADD} == "yes" # save this __dpadd_libs := ${__dpadd_libs} # we have made what use of it we can of DPADD DPADD = .endif .endif .endif Index: head/contrib/bmake/mk/files.mk =================================================================== --- head/contrib/bmake/mk/files.mk (revision 365365) +++ head/contrib/bmake/mk/files.mk (revision 365366) @@ -1,83 +1,83 @@ -# $Id: files.mk,v 1.6 2017/05/07 02:21:02 sjg Exp $ +# $Id: files.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2017, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .include FILES_INSTALL_OWN ?= -o ${SHAREOWN} -g ${SHAREGRP} FILESMODE ?= ${SHAREMODE} FILES_COPY ?= -C FILESGROUPS ?= FILES FILESGROUPS := ${FILESGROUPS:O:u} .if !target(buildfiles) .for group in ${FILESGROUPS} buildfiles: ${${group}} .endfor .endif buildfiles: realbuild: buildfiles # there is no default FILESDIR so # ignore group if ${group}DIR is not defined .for group in ${FILESGROUPS} .if !empty(${group}) && defined(${group}DIR) .if ${group} != "FILES" ${group}_INSTALL_OWN ?= ${FILES_INSTALL_OWN} .endif # incase we are staging STAGE_DIR.${group} ?= ${STAGE_OBJTOP}${${group}DIR} .for file in ${${group}:O:u} ${group}_INSTALL_OWN.${file:T} ?= ${${group}_INSTALL_OWN} ${group}DIR.${file:T} ?= ${${group}DIR} file_mkdir_list += ${${group}DIR.${file:T}} .if defined(${group}NAME.${file:T}) STAGE_AS_SETS += ${group} STAGE_AS_${file} = ${${group}NAME.${file:T}} stage_as.${group}: ${file} installfiles: installfiles.${group}.${file:T} installfiles.${group}.${file:T}: ${file} file_mkdirs ${INSTALL} ${FILES_COPY} ${${group}_INSTALL_OWN.${file:T}} \ -m ${FILESMODE} ${.ALLSRC:Nfile_mkdirs} ${DESTDIR}${${group}DIR}/${${group}NAME.${file:T}} .else STAGE_SETS += ${group} stage_files.${group}: ${file} installfiles.${group}: ${file} installfiles: installfiles.${group} .endif .endfor # file installfiles.${group}: file_mkdirs ${INSTALL} ${FILES_COPY} ${${group}_INSTALL_OWN} -m ${FILESMODE} \ ${.ALLSRC:Nfile_mkdirs:O:u} ${DESTDIR}${${group}DIR} .endif # !empty .endfor # group file_mkdirs: @for d in ${file_mkdir_list:O:u}; do \ test -d ${DESTDIR}$$d || \ ${INSTALL} -d ${FILES_INSTALL_OWN} -m 775 ${DESTDIR}$$d; \ done beforeinstall: installfiles: realinstall: installfiles .ORDER: beforeinstall installfiles Index: head/contrib/bmake/mk/gendirdeps.mk =================================================================== --- head/contrib/bmake/mk/gendirdeps.mk (revision 365365) +++ head/contrib/bmake/mk/gendirdeps.mk (revision 365366) @@ -1,389 +1,385 @@ -# $Id: gendirdeps.mk,v 1.44 2020/06/23 04:21:51 sjg Exp $ +# $Id: gendirdeps.mk,v 1.46 2020/08/19 17:51:53 sjg Exp $ # 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 -# modification, are permitted provided that the following conditions -# are met: +# 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. +# 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. -# +# documentation and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT # OWNER 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. +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # This makefile [re]generates ${.MAKE.DEPENDFILE} # .include # Assumptions: # RELDIR is the relative path from ${SRCTOP} to ${_CURDIR} # (SRCTOP is ${SB}/src) # _CURDIR is the absolute version of ${.CURDIR} # _OBJDIR is the absolute version of ${.OBJDIR} # _objroot is realpath of ${_OBJTOP} without ${MACHINE} # this may be different from _OBJROOT if $SB/obj is a # symlink to another filesystem. # _objroot must be a prefix match for _objtop .MAIN: all # keep this simple .MAKE.MODE = compat all: _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} _OBJTOP ?= ${OBJTOP} _OBJROOT ?= ${OBJROOT:U${_OBJTOP:H}} .if ${_OBJROOT:M*/} _slash=/ .else _slash= .endif _objroot ?= ${_OBJROOT:tA}${_slash} _this = ${.PARSEDIR}/${.PARSEFILE} # remember what to make _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T} # We do _not_ want to read our own output! .MAKE.DEPENDFILE = /dev/null # caller should have set this META_FILES ?= ${.MAKE.META.FILES} .if !empty(META_FILES) .if ${.MAKE.LEVEL} > 0 && !empty(GENDIRDEPS_FILTER) # so we can compare below .-include <${_DEPENDFILE}> # yes, I mean :U with no value _DIRDEPS := ${DIRDEPS:U:O:u} .endif META_FILES := ${META_FILES:T:O:u} # pickup customizations .-include # these are actually prefixes that we'll skip # they should all be absolute paths SKIP_GENDIRDEPS ?= .if !empty(SKIP_GENDIRDEPS) _skip_gendirdeps = egrep -v '^(${SKIP_GENDIRDEPS:O:u:ts|})' | .else _skip_gendirdeps = .endif # Below we will turn _{VAR} into ${VAR} which keeps this simple # GENDIRDEPS_FILTER_DIR_VARS is a list of dirs to be substiuted for. # GENDIRDEPS_FILTER_VARS is more general. # In each case order matters. .if !empty(GENDIRDEPS_FILTER_DIR_VARS) GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_DIR_VARS:@v@S,${$v},_{${v}},@} .endif .if !empty(GENDIRDEPS_FILTER_VARS) GENDIRDEPS_FILTER += ${GENDIRDEPS_FILTER_VARS:@v@S,/${$v}/,/_{${v}}/,@:NS,//,*:u} .endif -# this (*should* be set in meta.sys.mk) +# this (*should* be set in meta.sys.mk) # is the script that extracts what we want. META2DEPS ?= ${.PARSEDIR}/meta2deps.sh META2DEPS := ${META2DEPS} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" && ${DEBUG_GENDIRDEPS:Uno:Mmeta2d*} != "" _time = time _sh_x = sh -x _py_d = -ddd .else _time = _sh_x = _py_d = .endif .if ${META2DEPS:E} == "py" # we can afford to do this all the time. DPDEPS ?= no -META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d} +META2DEPS_CMD = ${_time} ${PYTHON} ${META2DEPS} ${_py_d} .if ${DPDEPS:tl} != "no" META2DEPS_CMD += -D ${DPDEPS} .endif META2DEPS_FILTER = sed 's,^src:,${SRCTOP}/,;s,^\([^/]\),${OBJTOP}/\1,' | .elif ${META2DEPS:E} == "sh" META2DEPS_CMD = ${_time} ${_sh_x} ${META2DEPS} OBJTOP=${_OBJTOP} .else META2DEPS_CMD ?= ${META2DEPS} .endif .if ${TARGET_OBJ_SPEC:U${MACHINE}} != ${MACHINE} META2DEPS_CMD += -T ${TARGET_OBJ_SPEC} .endif META2DEPS_CMD += \ -R ${RELDIR} -H ${HOST_TARGET} \ ${M2D_OBJROOTS:O:u:@o@-O $o@} \ ${M2D_EXCLUDES:O:u:@o@-X $o@} \ M2D_OBJROOTS += ${OBJTOP} ${_OBJROOT} ${_objroot} .if defined(SB_OBJROOT) M2D_OBJROOTS += ${SB_OBJROOT} .endif .if defined(STAGE_ROOT) M2D_OBJROOTS += ${STAGE_ROOT} .endif .if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} == "" # meta2deps.py only groks objroot # so we need to give it what it expects # and tell it not to add machine qualifiers META2DEPS_ARGS += MACHINE=none .endif -.if defined(SB_BACKING_SB) -META2DEPS_CMD += -S ${SB_BACKING_SB}/src +.if defined(SB_BACKING_SB) +META2DEPS_CMD += -S ${SB_BACKING_SB}/src M2D_OBJROOTS += ${SB_BACKING_SB}/${SB_OBJPREFIX} .endif GENDIRDEPS_SEDCMDS += \ -e 's,//*$$,,;s,\.${HOST_TARGET:Uhost}$$,.host,' \ -e 's,\.${HOST_TARGET32:Uhost32}$$,.host32,' \ -e 's,\.${MACHINE}$$,,' \ -e 's:\.${TARGET_SPEC:U${MACHINE}}$$::' # we are only interested in the dirs # specifically those we read something from. # we canonicalize them to keep things simple # if we are using a split-fs sandbox, it gets a little messier. _objtop := ${_OBJTOP:tA} # some people put *.meta in META_XTRAS to make sure we get here _meta_files := ${META_FILES:N\*.meta:O:u} # assume a big list _meta_files_arg= @meta.list .if empty(_meta_files) && ${META_FILES:M\*.meta} != "" -# XXX this should be considered a bad idea, +# XXX this should be considered a bad idea, # since we cannot ignore stale .meta x != cd ${_OBJDIR} && find . -name '*.meta' -print -o \( -type d ! -name . -prune \) | sed 's,^./,,' > meta.list; echo .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 dir_list != cd ${_OBJDIR} && \ ${META2DEPS_CMD} MACHINE=${MACHINE} \ SRCTOP=${SRCTOP} RELDIR=${RELDIR} CURDIR=${_CURDIR} \ ${META2DEPS_ARGS} \ ${_meta_files_arg} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \ sed ${GENDIRDEPS_SEDCMDS} .if ${dir_list:M*ERROR\:*} != "" .warning ${dir_list:tW:C,.*(ERROR),\1,} .warning Skipping ${_DEPENDFILE:S,${SRCTOP}/,,} # we are not going to update anything .else dpadd_dir_list= .if !empty(DPADD) _nonlibs := ${DPADD:T:Nlib*:N*include} .if !empty(_nonlibs) ddep_list = .for f in ${_nonlibs:@x@${DPADD:M*/$x}@} .if exists($f.dirdep) ddep_list += $f.dirdep .elif exists(${f:H}.dirdep) ddep_list += ${f:H}.dirdep .else dir_list += ${f:H:tA} dpadd_dir_list += ${f:H:tA} .endif .endfor .if !empty(ddep_list) ddeps != cat ${ddep_list:O:u} | ${META2DEPS_FILTER} ${_skip_gendirdeps} \ sed ${GENDIRDEPS_SEDCMDS} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: raw_dir_list='${dir_list}' .info ${RELDIR}: ddeps='${ddeps}' .endif dir_list += ${ddeps} .endif .endif .endif # DIRDEPS represent things that had to have been built first # so they should all be undir OBJTOP. -# Note that ${_OBJTOP}/bsd/include/machine will get reported +# Note that ${_OBJTOP}/bsd/include/machine will get reported # to us as $SRCTOP/bsd/sys/$MACHINE_ARCH/include meaning we # will want to visit bsd/include -# so we add +# so we add # ${"${dir_list:M*bsd/sys/${MACHINE_ARCH}/include}":?bsd/include:} # to GENDIRDEPS_DIR_LIST_XTRAS _objtops = ${OBJTOP} ${_OBJTOP} ${_objtop} _objtops := ${_objtops:O:u} dirdep_list = \ ${_objtops:@o@${dir_list:M$o*/*:C,$o[^/]*/,,}@} \ ${GENDIRDEPS_DIR_LIST_XTRAS} # sort longest first M2D_OBJROOTS := ${M2D_OBJROOTS:O:u:[-1..1]} # anything we use from an object dir other than ours # needs to be qualified with its . suffix # (we used the pseudo machine "host" for the HOST_TARGET). skip_ql= ${SRCTOP}* ${_objtops:@o@$o*@} .for o in ${M2D_OBJROOTS:${skip_ql:${M_ListToSkip}}} # we need := so only skip_ql to this point applies ql.$o := ${dir_list:${skip_ql:${M_ListToSkip}}:M$o*/*/*:C,$o([^/]+)/(.*),\2.\1,:S,.${HOST_TARGET},.host,} qualdir_list += ${ql.$o} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: o=$o ${ql.$o qualdir_list:L:@v@$v=${$v}@} .endif skip_ql+= $o* .endfor dirdep_list := ${dirdep_list:O:u} qualdir_list := ${qualdir_list:N*.${MACHINE}:O:u} DIRDEPS = \ ${dirdep_list:N${RELDIR}:N${RELDIR}/*} \ ${qualdir_list:N${RELDIR}.*:N${RELDIR}/*} # We only consider things below $RELDIR/ if they have a makefile. # This is the same test that _DIRDEP_USE applies. -# We have do a double test with dirdep_list as it _may_ contain +# We have do a double test with dirdep_list as it _may_ contain # qualified dirs - if we got anything from a stage dir. # qualdir_list we know are all qualified. # It would be nice do peform this check for all of DIRDEPS, -# but we cannot assume that all of the tree is present, +# but we cannot assume that all of the tree is present, # in fact we can only assume that RELDIR is. DIRDEPS += \ ${dirdep_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/$d/$m):?$d:${exists(${SRCTOP}/${d:R}/$m):?$d:}}@}@} \ ${qualdir_list:M${RELDIR}/*:@d@${.MAKE.MAKEFILE_PREFERENCE:@m@${exists(${SRCTOP}/${d:R}/$m):?$d:}@}@} # what modifiers do we allow in GENDIRDEPS_FILTER GENDIRDEPS_FILTER_MASK += @CMNS DIRDEPS := ${DIRDEPS:${GENDIRDEPS_FILTER:UNno:M[${GENDIRDEPS_FILTER_MASK:O:u:ts}]*:ts:}:C,//+,/,g:O:u} .if ${DEBUG_GENDIRDEPS:Uno:@x@${RELDIR:M$x}@} != "" .info ${RELDIR}: M2D_OBJROOTS=${M2D_OBJROOTS} .info ${RELDIR}: M2D_EXCLUDES=${M2D_EXCLUDES} .info ${RELDIR}: dir_list='${dir_list}' .info ${RELDIR}: dpadd_dir_list='${dpadd_dir_list}' .info ${RELDIR}: dirdep_list='${dirdep_list}' .info ${RELDIR}: qualdir_list='${qualdir_list}' .info ${RELDIR}: SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS}' .info ${RELDIR}: GENDIRDEPS_FILTER='${GENDIRDEPS_FILTER}' .info ${RELDIR}: FORCE_DPADD='${DPADD}' .info ${RELDIR}: DIRDEPS='${DIRDEPS}' .endif # SRC_DIRDEPS is for checkout logic src_dirdep_list = \ ${dir_list:M${SRCTOP}/*:S,${SRCTOP}/,,} SRC_DIRDEPS = \ ${src_dirdep_list:N${RELDIR}:N${RELDIR}/*:C,(/h)/.*,,} SRC_DIRDEPS := ${SRC_DIRDEPS:${GENDIRDEPS_SRC_FILTER:UN/*:ts:}:C,//+,/,g:O:u} # if you want to capture SRC_DIRDEPS in .MAKE.DEPENDFILE put -# SRC_DIRDEPS_FILE = ${_DEPENDFILE} +# SRC_DIRDEPS_FILE = ${_DEPENDFILE} # in local.gendirdeps.mk .if ${SRC_DIRDEPS_FILE:Uno:tl} != "no" ECHO_SRC_DIRDEPS = echo 'SRC_DIRDEPS = \'; echo '${SRC_DIRDEPS:@d@ $d \\${.newline}@}'; echo; .if ${SRC_DIRDEPS_FILE:T} == ${_DEPENDFILE:T} _include_src_dirdeps = ${ECHO_SRC_DIRDEPS} .else all: ${SRC_DIRDEPS_FILE} .if !target(${SRC_DIRDEPS_FILE}) ${SRC_DIRDEPS_FILE}: ${META_FILES} ${_this} ${META2DEPS} @(${ECHO_SRC_DIRDEPS}) > $@ .endif .endif .endif -_include_src_dirdeps ?= +_include_src_dirdeps ?= all: ${_DEPENDFILE} # if this is going to exist it would be there by now .if !exists(.depend) CAT_DEPEND = /dev/null .endif CAT_DEPEND ?= .depend .if !empty(_DIRDEPS) && ${DIRDEPS} != ${_DIRDEPS} # we may have changed a filter .PHONY: ${_DEPENDFILE} .endif -.if ${BUILD_AT_LEVEL0:Uno:tl} == "no" LOCAL_DEPENDS_GUARD ?= _{.MAKE.LEVEL} > 0 -.else -LOCAL_DEPENDS_GUARD ?= _{DEP_RELDIR} == _{_DEP_RELDIR} -.endif # 'cat .depend' should suffice, but if we are mixing build modes # .depend may contain things we don't want. # The sed command at the end of the stream, allows for the filters # to output _{VAR} tokens which we will turn into proper ${VAR} references. ${_DEPENDFILE}: .NOMETA ${CAT_DEPEND:M.depend} ${META_FILES:O:u:@m@${exists($m):?$m:}@} ${_this} ${META2DEPS} @(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \ echo 'DIRDEPS = \'; \ echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \ ${_include_src_dirdeps} \ echo '.include '; \ echo; \ echo '.if ${LOCAL_DEPENDS_GUARD}'; \ echo '# local dependencies - needed for -jN in clean tree'; \ [ -s ${CAT_DEPEND} ] && { grep : ${CAT_DEPEND} | grep -v '[/\\]'; }; \ echo '.endif' ) | sed 's,_\([{(]\),$$\1,g' > $@.new${.MAKE.PID} @${InstallNew}; InstallNew -s $@.new${.MAKE.PID} .endif # meta2deps failed .elif !empty(SUBDIR) DIRDEPS := ${SUBDIR:S,^,${RELDIR}/,:O:u} all: ${_DEPENDFILE} ${_DEPENDFILE}: .NOMETA ${MAKEFILE} ${_this} @(${GENDIRDEPS_HEADER} echo '# Autogenerated - do NOT edit!'; echo; \ echo 'DIRDEPS = \'; \ echo '${DIRDEPS:@d@ $d \\${.newline}@}'; echo; \ echo '.include '; \ echo ) | sed 's,_\([{(]\),$$\1,g' > $@.new @${InstallNew}; InstallNew $@.new .else # nothing to do all ${_DEPENDFILE}: .endif ${_DEPENDFILE}: .PRECIOUS # don't waste time looking for ways to make .meta files .SUFFIXES: Index: head/contrib/bmake/mk/host-target.mk =================================================================== --- head/contrib/bmake/mk/host-target.mk (revision 365365) +++ head/contrib/bmake/mk/host-target.mk (revision 365366) @@ -1,49 +1,49 @@ # RCSid: -# $Id: host-target.mk,v 1.12 2020/07/08 23:35:29 sjg Exp $ +# $Id: host-target.mk,v 1.13 2020/08/05 23:32:08 sjg Exp $ # Host platform information; may be overridden .if !defined(_HOST_OSNAME) _HOST_OSNAME != uname -s .export _HOST_OSNAME .endif .if !defined(_HOST_OSREL) _HOST_OSREL != uname -r .export _HOST_OSREL .endif .if !defined(_HOST_MACHINE) _HOST_MACHINE != uname -m .export _HOST_MACHINE .endif .if !defined(_HOST_ARCH) # for NetBSD prefer $MACHINE (amd64 rather than x86_64) -.if ${_HOST_OSNAME:NNetBSD} == "" +.if ${_HOST_OSNAME:NDarwin:NNetBSD} == "" _HOST_ARCH := ${_HOST_MACHINE} .else _HOST_ARCH != uname -p 2> /dev/null || uname -m # uname -p may produce garbage on linux .if ${_HOST_ARCH:[\#]} > 1 || ${_HOST_ARCH:Nunknown} == "" _HOST_ARCH := ${_HOST_MACHINE} .endif .endif .export _HOST_ARCH .endif .if !defined(HOST_MACHINE) HOST_MACHINE := ${_HOST_MACHINE} .export HOST_MACHINE .endif HOST_OSMAJOR := ${_HOST_OSREL:C/[^0-9].*//} HOST_OSTYPE := ${_HOST_OSNAME:S,/,,g}-${_HOST_OSREL:C/\([^\)]*\)//}-${_HOST_ARCH} HOST_OS := ${_HOST_OSNAME} host_os := ${_HOST_OSNAME:tl} HOST_TARGET := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH} # sometimes we want HOST_TARGET32 MACHINE32.amd64 = i386 MACHINE32.x86_64 = i386 _HOST_ARCH32 := ${MACHINE32.${_HOST_ARCH}:U${_HOST_ARCH:S,64$,,}} HOST_TARGET32 := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH32} # tr is insanely non-portable, accommodate the lowest common denominator TR ?= tr toLower = ${TR} 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' toUpper = ${TR} 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' Index: head/contrib/bmake/mk/host.libnames.mk =================================================================== --- head/contrib/bmake/mk/host.libnames.mk (revision 365365) +++ head/contrib/bmake/mk/host.libnames.mk (revision 365366) @@ -1,29 +1,29 @@ -# $Id: host.libnames.mk,v 1.4 2010/01/11 23:01:31 sjg Exp $ +# $Id: host.libnames.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2007-2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # DLIBEXT ?= .a DSHLIBEXT ?= ${DLIBEXT} HOST_LIBEXT ?= ${DSHLIBEXT} HOST_LIBDIRS ?= /usr/lib /lib HOST_LIBS ?= .for x in ${HOST_LIBS:O:u} .for d in ${HOST_LIBDIRS} .if exists($d/lib$x${HOST_LIBEXT}) LIB${x:tu} ?= $d/lib$x${HOST_LIBEXT} .endif .endfor .endfor Index: head/contrib/bmake/mk/inc.mk =================================================================== --- head/contrib/bmake/mk/inc.mk (revision 365365) +++ head/contrib/bmake/mk/inc.mk (revision 365366) @@ -1,89 +1,89 @@ -# $Id: inc.mk,v 1.7 2017/05/06 17:29:45 sjg Exp $ +# $Id: inc.mk,v 1.8 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2008, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .include .if !empty(LIBOWN) INC_INSTALL_OWN ?= -o ${LIBOWN} -g ${LIBGRP} .endif INCMODE ?= 444 INC_COPY ?= -C INCSDIR ?= ${INCDIR} STAGE_INCSDIR?= ${STAGE_OBJTOP}${INCSDIR} # accommodate folk used to freebsd INCGROUPS ?= ${INCSGROUPS:UINCS} INCGROUPS := ${INCGROUPS:O:u} .if !target(buildincludes) .for group in ${INCGROUPS} buildincludes: ${${group}} .endfor .endif buildincludes: includes: buildincludes .if !target(incinstall) .for group in ${INCGROUPS} .if !empty(${group}) .if ${group} != "INC" ${group}_INSTALL_OWN ?= ${INC_INSTALL_OWN} ${group}DIR ?= ${INCDIR} .endif # incase we are staging STAGE_DIR.${group} ?= ${STAGE_OBJTOP}${${group}DIR} .for header in ${${group}:O:u} ${group}_INSTALL_OWN.${header:T} ?= ${${group}_INSTALL_OWN} ${group}DIR.${header:T} ?= ${${group}DIR} inc_mkdir_list += ${${group}DIR.${header:T}} .if defined(${group}NAME.${header:T}) STAGE_AS_SETS += ${group} STAGE_AS_${header} = ${${group}NAME.${header:T}} stage_as.${group}: ${header} incinstall: incinstall.${group}.${header:T} incinstall.${group}.${header:T}: ${header} inc_mkdirs ${INSTALL} ${INC_COPY} ${${group}_INSTALL_OWN.${header:T}} -m ${INCMODE} ${.ALLSRC:Ninc_mkdirs} ${DESTDIR}${${group}DIR}/${${group}NAME.${header:T}} .else STAGE_SETS += ${group} stage_files.${group}: ${header} incinstall.${group}: ${header} incinstall: incinstall.${group} .endif .endfor # header incinstall.${group}: inc_mkdirs ${INSTALL} ${INC_COPY} ${${group}_INSTALL_OWN} -m ${INCMODE} \ ${.ALLSRC:Ninc_mkdirs:O:u} ${DESTDIR}${${group}DIR} .endif # !empty .endfor # group inc_mkdirs: @for d in ${inc_mkdir_list:O:u}; do \ test -d ${DESTDIR}$$d || \ ${INSTALL} -d ${INC_INSTALL_OWN} -m 775 ${DESTDIR}$$d; \ done .endif # !target(incinstall) beforeinstall: realinstall: incinstall .ORDER: beforeinstall incinstall Index: head/contrib/bmake/mk/init.mk =================================================================== --- head/contrib/bmake/mk/init.mk (revision 365365) +++ head/contrib/bmake/mk/init.mk (revision 365366) @@ -1,89 +1,93 @@ -# $Id: init.mk,v 1.17 2020/05/25 20:15:07 sjg Exp $ +# $Id: init.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2002, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .if ${MAKE_VERSION:U0} > 20100408 _this_mk_dir := ${.PARSEDIR:tA} .else _this_mk_dir := ${.PARSEDIR} .endif .-include .-include <${.CURDIR:H}/Makefile.inc> .include .include .MAIN: all # should have been set by sys.mk CXX_SUFFIXES?= .cc .cpp .cxx .C .if !empty(WARNINGS_SET) || !empty(WARNINGS_SET_${MACHINE_ARCH}) .include .endif # these are applied in order, least specific to most VAR_QUALIFIER_LIST += \ ${TARGET_SPEC_VARS:UMACHINE:@v@${$v}@} \ ${COMPILER_TYPE} \ ${.TARGET:T:R} \ ${.TARGET:T} \ ${.IMPSRC:T} \ ${VAR_QUALIFIER_XTRA_LIST} QUALIFIED_VAR_LIST += \ CFLAGS \ COPTS \ CPPFLAGS \ CPUFLAGS \ LDFLAGS \ # a final :U avoids errors if someone uses := .for V in ${QUALIFIED_VAR_LIST:O:u:@q@$q $q_LAST@} .for Q in ${VAR_QUALIFIER_LIST:u} $V += ${$V.$Q:U} ${$V.$Q.${COMPILER_TYPE}:U} .endfor .endfor CC_PG?= -pg CXX_PG?= ${CC_PG} CC_PIC?= -DPIC CXX_PIC?= ${CC_PIC} PROFFLAGS?= -DGPROF -DPROF -.if ${.MAKE.LEVEL:U1} == 0 && ${BUILD_AT_LEVEL0:Uyes:tl} == "no" +# targets that are ok at level 0 +LEVEL0_TARGETS += clean* destory* +M_ListToSkip= O:u:S,^,N,:ts: + +.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != "" # this tells lib.mk and prog.mk to not actually build anything _SKIP_BUILD = not building at level 0 .endif .if !defined(.PARSEDIR) # no-op is the best we can do if not bmake. .WAIT: .endif # define this once for consistency .if empty(_SKIP_BUILD) # beforebuild is a hook for things that must be done early all: beforebuild .WAIT realbuild .else all: .PHONY .warning ${_SKIP_BUILD} .endif beforebuild: realbuild: .endif Index: head/contrib/bmake/mk/install-mk =================================================================== --- head/contrib/bmake/mk/install-mk (revision 365365) +++ head/contrib/bmake/mk/install-mk (revision 365366) @@ -1,185 +1,185 @@ : # NAME: # install-mk - install mk files # # SYNOPSIS: # install-mk [options] [var=val] [dest] # # DESCRIPTION: # This tool installs mk files in a semi-intelligent manner into # "dest". # # Options: # # -n just say what we want to do, but don't touch anything. # # -f use -f when copying sys,mk. # # -v be verbose # # -q be quiet # # -m "mode" # Use "mode" for installed files (444). # # -o "owner" # Use "owner" for installed files. # # -g "group" # Use "group" for installed files. # # var=val # Set "var" to "val". See below. # # All our *.mk files are copied to "dest" with appropriate # ownership and permissions. -# +# # By default if a sys.mk can be found in a standard location # (that bmake will find) then no sys.mk will be put in "dest". # # SKIP_SYS_MK: # If set, we will avoid installing our 'sys.mk' # This is probably a bad idea. # # SKIP_BSD_MK: # If set, we will skip making bsd.*.mk links to *.mk # # sys.mk: # # By default (and provided we are not installing to the system # mk dir - '/usr/share/mk') we install our own 'sys.mk' which # includes a sys specific file, or a generic one. # # # AUTHOR: # Simon J. Gerraty # RCSid: -# $Id: install-mk,v 1.174 2020/07/10 21:50:14 sjg Exp $ +# $Id: install-mk,v 1.179 2020/08/26 21:49:45 sjg Exp $ # # @(#) Copyright (c) 1994 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # -MK_VERSION=20200710 +MK_VERSION=20200826 OWNER= GROUP= MODE=444 BINMODE=555 ECHO=: SKIP= cp_f=-f while : do case "$1" in *=*) eval "$1"; shift;; +f) cp_f=; shift;; -f) cp_f=-f; shift;; -m) MODE=$2; shift 2;; -o) OWNER=$2; shift 2;; -g) GROUP=$2; shift 2;; -v) ECHO=echo; shift;; -q) ECHO=:; shift;; -n) ECHO=echo SKIP=:; shift;; --) shift; break;; *) break;; esac done case $# in 0) echo "$0 [options] []" echo "eg." echo "$0 -o bin -g bin -m 444 /usr/local/share/mk" exit 1 ;; esac dest=$1 os=${2:-`uname`} osrel=${3:-`uname -r`} Do() { $ECHO "$@" $SKIP "$@" } Error() { echo "ERROR: $@" >&2 exit 1 } Warning() { echo "WARNING: $@" >&2 } [ "$FORCE_SYS_MK" ] && Warning "ignoring: FORCE_{BSD,SYS}_MK (no longer supported)" SYS_MK_DIR=${SYS_MK_DIR:-/usr/share/mk} SYS_MK=${SYS_MK:-$SYS_MK_DIR/sys.mk} realpath() { [ -d $1 ] && cd $1 && 'pwd' && return echo $1 } if [ -s $SYS_MK -a -d $dest ]; then # if this is a BSD system we don't want to touch $SYS_MK dest=`realpath $dest` sys_mk_dir=`realpath $SYS_MK_DIR` if [ $dest = $sys_mk_dir ]; then case "$os" in - *BSD*) SKIP_SYS_MK=: + *BSD*) SKIP_SYS_MK=: SKIP_BSD_MK=: ;; *) # could be fake? if [ ! -d $dest/sys -a ! -s $dest/Generic.sys.mk ]; then SKIP_SYS_MK=: # play safe SKIP_BSD_MK=: fi ;; esac fi fi [ -d $dest/sys ] || Do mkdir -p $dest/sys [ -d $dest/sys ] || Do mkdir $dest/sys || exit 1 [ -z "$SKIP" ] && dest=`realpath $dest` cd `dirname $0` mksrc=`'pwd'` if [ $mksrc = $dest ]; then SKIP_MKFILES=: else # we do not install the examples mk_files=`grep '^[a-z].*\.mk' FILES | egrep -v '(examples/|^sys\.mk|sys/)'` mk_scripts=`egrep '^[a-z].*\.(sh|py)' FILES | egrep -v '/'` sys_mk_files=`grep 'sys/.*\.mk' FILES` SKIP_MKFILES= [ -z "$SKIP_SYS_MK" ] && mk_files="sys.mk $mk_files" fi $SKIP_MKFILES Do cp $cp_f $mk_files $dest $SKIP_MKFILES Do cp $cp_f $sys_mk_files $dest/sys $SKIP_MKFILES Do cp $cp_f $mk_scripts $dest $SKIP cd $dest $SKIP_MKFILES Do chmod $MODE $mk_files $sys_mk_files $SKIP_MKFILES Do chmod $BINMODE $mk_scripts [ "$GROUP" ] && $SKIP_MKFILES Do chgrp $GROUP $mk_files $sys_mk_files [ "$OWNER" ] && $SKIP_MKFILES Do chown $OWNER $mk_files $sys_mk_files # if this is a BSD system the bsd.*.mk should exist and be used. if [ -z "$SKIP_BSD_MK" ]; then for f in dep doc files inc init lib links man nls obj own prog subdir do b=bsd.$f.mk [ -s $b ] || Do ln -s $f.mk $b done fi exit 0 Index: head/contrib/bmake/mk/install-new.mk =================================================================== --- head/contrib/bmake/mk/install-new.mk (revision 365365) +++ head/contrib/bmake/mk/install-new.mk (revision 365366) @@ -1,53 +1,53 @@ -# $Id: install-new.mk,v 1.3 2012/03/24 18:25:49 sjg Exp $ +# $Id: install-new.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !defined(InstallNew) # copy if src and target are different making a backup if desired CmpCp= CmpCp() { \ src=$$1 target=$$2 _bak=$$3; \ if ! test -s $$target || ! cmp -s $$target $$src; then \ trap "" 1 2 3 15; \ if test -s $$target; then \ if test "x$$_bak" != x; then \ rm -f $$target$$_bak; \ mv $$target $$target$$_bak; \ else \ rm -f $$target; \ fi; \ fi; \ cp $$src $$target; \ fi; } # If the .new file is different, we want it. # Note: this function will work as is for *.new$RANDOM" InstallNew= ${CmpCp}; InstallNew() { \ _t=-e; _bak=; \ while :; do \ case "$$1" in \ -?) _t=$$1; shift;; \ --bak) _bak=$$2; shift 2;; \ *) break;; \ esac; \ done; \ for new in "$$@"; do \ if test $$_t $$new; then \ target=`expr $$new : '\(.*\).new'`; \ CmpCp $$new $$target $$_bak; \ fi; \ rm -f $$new; \ done; :; } .endif Index: head/contrib/bmake/mk/java.mk =================================================================== --- head/contrib/bmake/mk/java.mk (revision 365365) +++ head/contrib/bmake/mk/java.mk (revision 365366) @@ -1,97 +1,97 @@ # # RCSid: -# $Id: java.mk,v 1.14 2007/11/22 08:16:25 sjg Exp $ +# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ # @(#) Copyright (c) 1998-2001, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .include CLASSPATH?=. .if defined(PROG) SRCS?= ${PROG:.class=.java} .endif .if !defined(SRCS) || empty(SRCS) SRCS!=cd ${.CURDIR} && echo *.java .endif .SUFFIXES: .class .java CLEANFILES+= *.class JAVAC?= javac JAVADOC?= javadoc .if !target(docs) docs: ${JAVADOC} ${JAVADOC_FLAGS} ${SRCS} .endif .if defined(JAVADESTDIR) && !empty(JAVADESTDIR) JAVASRCDIR?=${JAVADESTDIR:H}/src __classdest:=${JAVADESTDIR}${.CURDIR:S,${JAVASRCDIR},,}/ CLASSPATH:=${CLASSPATH}:${JAVADESTDIR} JAVAC_FLAGS+= -d ${JAVADESTDIR} .else __classdest= .endif JAVAC_FLAGS+= ${JAVAC_DBG} .if defined(MAKE_VERSION) && !defined(NO_CLASSES_COOKIE) # java works best by compiling a bunch of classes at once. -# this lot does that but needs a recent netbsd make or +# this lot does that but needs a recent netbsd make or # or its portable cousin bmake. .for __s in ${SRCS} __c:= ${__classdest}${__s:.java=.class} .if !target(${__c}) # We need to do something to force __c's parent to be made. ${__c}: ${__s} @rm -f ${.TARGET} .endif SRCS_${__c}=${__s} __classes:= ${__classes} ${__c} .endfor __classes_cookie=${__classdest}.classes.done CLEANFILES+= ${__classes} ${__classes_cookie} ${__classes_cookie}: ${__classes} CLASSPATH=${CLASSPATH} ${JAVAC} ${JAVAC_FLAGS} ${.OODATE:@c@${SRCS_$c}@} @touch ${.TARGET} all: ${__classes_cookie} .else # this will work with other BSD make's .for __s in ${SRCS} __c:= ${__classdest}${__s:.java=.class} ${__c}: ${__s} CLASSPATH=${CLASSPATH} ${JAVAC} ${JAVAC_FLAGS} ${.OODATE} .endfor all: ${SRCS:%.java=${__classdest}%.class} .endif .if !target(cleanjava) cleanjava: rm -f [Ee]rrs mklog core *.core ${PROG} ${CLEANFILES} clean: cleanjava cleandir: cleanjava .endif .endif Index: head/contrib/bmake/mk/lib.mk =================================================================== --- head/contrib/bmake/mk/lib.mk (revision 365365) +++ head/contrib/bmake/mk/lib.mk (revision 365366) @@ -1,609 +1,609 @@ -# $Id: lib.mk,v 1.70 2020/05/02 02:10:20 sjg Exp $ +# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .include .if ${OBJECT_FMT} == "ELF" NEED_SOLINKS?= yes .endif SHLIB_VERSION_FILE?= ${.CURDIR}/shlib_version .if !defined(SHLIB_MAJOR) && exists(${SHLIB_VERSION_FILE}) SHLIB_MAJOR != . ${SHLIB_VERSION_FILE} ; echo $$major SHLIB_MINOR != . ${SHLIB_VERSION_FILE} ; echo $$minor SHLIB_TEENY != . ${SHLIB_VERSION_FILE} ; echo $$teeny .endif .for x in major minor teeny print-shlib-$x: .if defined(SHLIB_${x:tu}) && ${MK_PIC} != "no" @echo ${SHLIB_${x:tu}} .else @false .endif .endfor SHLIB_FULLVERSION ?= ${${SHLIB_MAJOR} ${SHLIB_MINOR} ${SHLIB_TEENY}:L:ts.} SHLIB_FULLVERSION := ${SHLIB_FULLVERSION} # add additional suffixes not exported. # .po is used for profiling object files. # ${PICO} is used for PIC object files. PICO?= .pico .SUFFIXES: .out .a .ln ${PICO} .po .o .s .S .c .cc .C .m .F .f .r .y .l .cl .p .h .SUFFIXES: .sh .m4 .m CFLAGS+= ${COPTS} META_NOECHO?= echo # Originally derrived from NetBSD-1.6 # Set PICFLAGS to cc flags for producing position-independent code, # if not already set. Includes -DPIC, if required. # Data-driven table using make variables to control how shared libraries # are built for different platforms and object formats. # OBJECT_FMT: currently either "ELF" or "a.out", from # SHLIB_SOVERSION: version number to be compiled into a shared library # via -soname. Usually ${SHLIB_MAJOR} on ELF. # NetBSD/pmax used to use ${SHLIB_MAJOR}[.${SHLIB_MINOR} # [.${SHLIB_TEENY}]] # SHLIB_SHFLAGS: Flags to tell ${LD} to emit shared library. # with ELF, also set shared-lib version for ld.so. # SHLIB_LDSTARTFILE: support .o file, call C++ file-level constructors # SHLIB_LDENDFILE: support .o file, call C++ file-level destructors # FPICFLAGS: flags for ${FC} to compile .[fF] files to ${PICO} objects. # CPPICFLAGS: flags for ${CPP} to preprocess .[sS] files for ${AS} # CPICFLAGS: flags for ${CC} to compile .[cC] files to ${PICO} objects. # CAPICFLAGS flags for {$CC} to compiling .[Ss] files # (usually just ${CPPPICFLAGS} ${CPICFLAGS}) # APICFLAGS: flags for ${AS} to assemble .[sS] to ${PICO} objects. .if ${TARGET_OSNAME} == "NetBSD" .if ${MACHINE_ARCH} == "alpha" # Alpha-specific shared library flags FPICFLAGS ?= -fPIC CPICFLAGS ?= -fPIC -DPIC -CPPPICFLAGS?= -DPIC +CPPPICFLAGS?= -DPIC CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS} APICFLAGS ?= .elif ${MACHINE_ARCH} == "mipsel" || ${MACHINE_ARCH} == "mipseb" # mips-specific shared library flags # On mips, all libs are compiled with ABIcalls, not just sharedlibs. MKPICLIB= no # so turn shlib PIC flags on for ${AS}. AINC+=-DABICALLS AFLAGS+= -fPIC AS+= -KPIC .elif ${MACHINE_ARCH} == "vax" && ${OBJECT_FMT} == "ELF" # On the VAX, all object are PIC by default, not just sharedlibs. MKPICLIB= no .elif (${MACHINE_ARCH} == "sparc" || ${MACHINE_ARCH} == "sparc64") && \ ${OBJECT_FMT} == "ELF" -# If you use -fPIC you need to define BIGPIC to turn on 32-bit +# If you use -fPIC you need to define BIGPIC to turn on 32-bit # relocations in asm code FPICFLAGS ?= -fPIC CPICFLAGS ?= -fPIC -DPIC CPPPICFLAGS?= -DPIC -DBIGPIC CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS} APICFLAGS ?= -KPIC .else # Platform-independent flags for NetBSD a.out shared libraries SHLIB_SOVERSION=${SHLIB_FULLVERSION} SHLIB_SHFLAGS= FPICFLAGS ?= -fPIC CPICFLAGS?= -fPIC -DPIC -CPPPICFLAGS?= -DPIC +CPPPICFLAGS?= -DPIC CAPICFLAGS?= ${CPPPICFLAGS} ${CPICFLAGS} APICFLAGS?= -k .endif # Platform-independent linker flags for ELF shared libraries .if ${OBJECT_FMT} == "ELF" SHLIB_SOVERSION= ${SHLIB_MAJOR} SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION} SHLIB_LDSTARTFILE?= /usr/lib/crtbeginS.o SHLIB_LDENDFILE?= /usr/lib/crtendS.o .endif # for compatibility with the following CC_PIC?= ${CPICFLAGS} LD_shared=${SHLIB_SHFLAGS} .endif # NetBSD .if ${TARGET_OSNAME} == "FreeBSD" .if ${OBJECT_FMT} == "ELF" SHLIB_SOVERSION= ${SHLIB_MAJOR} SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION} .else SHLIB_SHFLAGS= -assert pure-text .endif SHLIB_LDSTARTFILE= SHLIB_LDENDFILE= CC_PIC?= -fpic LD_shared=${SHLIB_SHFLAGS} .endif # FreeBSD MKPICLIB?= yes # sys.mk can override these LD_X?=-X LD_x?=-x LD_r?=-r # Non BSD machines will be using bmake. .if ${TARGET_OSNAME} == "SunOS" LD_shared=-assert pure-text .if ${OBJECT_FMT} == "ELF" || ${MACHINE} == "solaris" # Solaris LD_shared=-h lib${LIB}.so.${SHLIB_MAJOR} -G .endif .elif ${TARGET_OSNAME} == "HP-UX" LD_shared=-b LD_so=sl DLLIB= # HPsUX lorder does not grok anything but .o LD_sobjs=`${LORDER} ${OBJS} | ${TSORT} | sed 's,\.o,${PICO},'` LD_pobjs=`${LORDER} ${OBJS} | ${TSORT} | sed 's,\.o,.po,'` .elif ${TARGET_OSNAME} == "OSF1" LD_shared= -msym -shared -expect_unresolved '*' LD_solib= -all lib${LIB}_pic.a DLLIB= # lorder does not grok anything but .o LD_sobjs=`${LORDER} ${OBJS} | ${TSORT} | sed 's,\.o,${PICO},'` LD_pobjs=`${LORDER} ${OBJS} | ${TSORT} | sed 's,\.o,.po,'` AR_cq= -cqs .elif ${TARGET_OSNAME} == "FreeBSD" LD_solib= lib${LIB}_pic.a .elif ${TARGET_OSNAME} == "Linux" SHLIB_LD = ${CC} # this is ambiguous of course 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} SHLIB_COMPATABILITY_VERSION ?= ${SHLIB_MAJOR}.${SHLIB_MINOR:U0} SHLIB_COMPATABILITY ?= \ -compatibility_version ${SHLIB_COMPATABILITY_VERSION} \ -current_version ${SHLIB_FULLVERSION} LD_shared = -dynamiclib \ -flat_namespace -undefined suppress \ -install_name ${LIBDIR}/lib${LIB}.${SHLIB_INSTALL_VERSION}.${LD_solink} \ ${SHLIB_COMPATABILITY} SHLIB_LINKS = .for v in ${SHLIB_COMPATABILITY_VERSION} ${SHLIB_INSTALL_VERSION} .if "$v" != "${SHLIB_FULLVERSION}" SHLIB_LINKS += lib${LIB}.$v.${LD_solink} .endif .endfor .if ${MK_LINKLIB} != "no" SHLIB_LINKS += lib${LIB}.${LD_solink} .endif LD_so = ${SHLIB_FULLVERSION}.dylib LD_sobjs = ${SOBJS:O:u} LD_solib = ${LD_sobjs} SOLIB = ${LD_sobjs} LD_solink = dylib .if ${MACHINE_ARCH} == "i386" PICFLAG ?= -fPIC .else PICFLAG ?= -fPIC -fno-common .endif RANLIB = : .endif SHLIB_LD ?= ${LD} .if !empty(SHLIB_MAJOR) .if ${NEED_SOLINKS} && empty(SHLIB_LINKS) .if ${MK_LINKLIB} != "no" SHLIB_LINKS = lib${LIB}.${LD_solink} .endif .if "${SHLIB_FULLVERSION}" != "${SHLIB_MAJOR}" SHLIB_LINKS += lib${LIB}.${LD_solink}.${SHLIB_MAJOR} .endif .endif .endif LIBTOOL?=libtool LD_shared ?= -Bshareable -Bforcearchive LD_so ?= so.${SHLIB_FULLVERSION} LD_solink ?= so .if empty(LORDER) LD_objs ?= ${OBJS} LD_pobjs ?= ${POBJS} LD_sobjs ?= ${SOBJS} .else LD_objs ?= `${LORDER} ${OBJS} | ${TSORT}` LD_sobjs ?= `${LORDER} ${SOBJS} | ${TSORT}` LD_pobjs ?= `${LORDER} ${POBJS} | ${TSORT}` .endif LD_solib ?= ${LD_sobjs} AR_cq ?= cq .if exists(/netbsd) && exists(${DESTDIR}/usr/lib/libdl.so) DLLIB ?= -ldl .endif # some libs have lots of objects, and scanning all .o, .po and ${PICO} meta files -# is a waste of time, this tells meta.autodep.mk to just pick one +# is a waste of time, this tells meta.autodep.mk to just pick one # (typically ${PICO}) # yes, 42 is a random number. .if ${MK_DIRDEPS_BUILD} == "yes" && ${SRCS:Uno:[\#]} > 42 OPTIMIZE_OBJECT_META_FILES ?= yes .endif .if ${MK_LIBTOOL} == "yes" # because libtool is so fascist about naming the object files, # we cannot (yet) build profiled libs MK_PROFILE=no _LIBS=lib${LIB}.a .if exists(${.CURDIR}/shlib_version) SHLIB_AGE != . ${.CURDIR}/shlib_version ; echo $$age .endif .else # for the normal .a we do not want to strip symbols .c.o: ${COMPILE.c} ${.IMPSRC} # for the normal .a we do not want to strip symbols ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} .S.o .s.o: - ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} + ${COMPILE.S} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} .if (${LD_X} == "") .c.po: ${COMPILE.c} ${CC_PG} ${PROFFLAGS} ${.IMPSRC} -o ${.TARGET} ${CXX_SUFFIXES:%=%.po}: ${COMPILE.cc} -pg ${.IMPSRC} -o ${.TARGET} .S${PICO} .s${PICO}: ${COMPILE.S} ${PICFLAG} ${CC_PIC} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} -o ${.TARGET} .else .c.po: ${COMPILE.c} ${CC_PG} ${PROFFLAGS} ${.IMPSRC} -o ${.TARGET}.o @${LD} ${LD_X} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o ${CXX_SUFFIXES:%=%.po}: ${COMPILE.cc} ${CXX_PG} ${.IMPSRC} -o ${.TARGET}.o ${LD} ${LD_X} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o .S${PICO} .s${PICO}: ${COMPILE.S} ${PICFLAG} ${CC_PIC} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} -o ${.TARGET}.o ${LD} ${LD_x} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o .endif .if (${LD_x} == "") .c${PICO}: ${COMPILE.c} ${PICFLAG} ${CC_PIC} ${.IMPSRC} -o ${.TARGET} ${CXX_SUFFIXES:%=%${PICO}}: ${COMPILE.cc} ${PICFLAG} ${CC_PIC} ${.IMPSRC} -o ${.TARGET} .S.po .s.po: ${COMPILE.S} ${PROFFLAGS} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} -o ${.TARGET} .else .c${PICO}: ${COMPILE.c} ${PICFLAG} ${CC_PIC} ${.IMPSRC} -o ${.TARGET}.o ${LD} ${LD_x} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o ${CXX_SUFFIXES:%=%${PICO}}: ${COMPILE.cc} ${PICFLAG} ${CC_PIC} ${.IMPSRC} -o ${.TARGET}.o ${LD} ${LD_x} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o .S.po .s.po: ${COMPILE.S} ${PROFFLAGS} ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} -o ${.TARGET}.o ${LD} ${LD_X} ${LD_r} ${.TARGET}.o -o ${.TARGET} @rm -f ${.TARGET}.o .endif .endif .c.ln: ${LINT} ${LINTFLAGS} ${CFLAGS:M-[IDU]*} -i ${.IMPSRC} .if ${MK_LIBTOOL} != "yes" .if !defined(PICFLAG) PICFLAG=-fpic .endif _LIBS= .if ${MK_ARCHIVE} != "no" _LIBS += lib${LIB}.a .endif .if ${MK_PROFILE} != "no" _LIBS+=lib${LIB}_p.a POBJS+=${OBJS:.o=.po} .endif .if ${MK_PIC} != "no" .if ${MK_PICLIB} == "no" SOLIB ?= lib${LIB}.a .else SOLIB=lib${LIB}_pic.a _LIBS+=${SOLIB} .endif .if !empty(SHLIB_FULLVERSION) _LIBS+=lib${LIB}.${LD_so} .endif .endif .if ${MK_LINT} != "no" _LIBS+=llib-l${LIB}.ln .endif # here is where you can define what LIB* are .-include .if ${MK_DPADD_MK} == "yes" # lots of cool magic, but might not suit everyone. .include .endif .if empty(LIB) _LIBS= .elif ${MK_LDORDER_MK} != "no" # Record any libs that we need to be linked with _LIBS+= ${libLDORDER_INC} .include .endif .if !defined(_SKIP_BUILD) -realbuild: ${_LIBS} +realbuild: ${_LIBS} .endif all: _SUBDIRUSE .for s in ${SRCS:N*.h:M*/*} ${.o ${PICO} .po .lo:L:@o@${s:T:R}$o@}: $s .endfor OBJS+= ${SRCS:T:N*.h:R:S/$/.o/g} .NOPATH: ${OBJS} .if ${MK_LIBTOOL} == "yes" .if ${MK_PIC} == "no" LT_STATIC=-static .else LT_STATIC= .endif SHLIB_AGE?=0 # .lo's are created as a side effect .s.o .S.o .c.o: ${LIBTOOL} --mode=compile ${CC} ${LT_STATIC} ${CFLAGS} ${CPPFLAGS} ${IMPFLAGS} -c ${.IMPSRC} # can't really do profiled libs with libtool - its too fascist about # naming the output... 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 @[ -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} @${META_NOECHO} building standard ${LIB} library @rm -f ${.TARGET} @${AR} ${AR_cq} ${.TARGET} ${LD_objs} ${RANLIB} ${.TARGET} POBJS+= ${OBJS:.o=.po} .NOPATH: ${POBJS} lib${LIB}_p.a: ${POBJS} @${META_NOECHO} building profiled ${LIB} library @rm -f ${.TARGET} @${AR} ${AR_cq} ${.TARGET} ${LD_pobjs} ${RANLIB} ${.TARGET} SOBJS+= ${OBJS:.o=${PICO}} .NOPATH: ${SOBJS} lib${LIB}_pic.a: ${SOBJS} @${META_NOECHO} building shared object ${LIB} library @rm -f ${.TARGET} @${AR} ${AR_cq} ${.TARGET} ${LD_sobjs} ${RANLIB} ${.TARGET} #SHLIB_LDADD?= ${LDADD} # bound to be non-portable... # this is known to work for NetBSD 1.6 and FreeBSD 4.2 lib${LIB}.${LD_so}: ${SOLIB} ${DPADD} @${META_NOECHO} building shared ${LIB} library \(version ${SHLIB_FULLVERSION}\) @rm -f ${.TARGET} .if ${TARGET_OSNAME} == "NetBSD" || ${TARGET_OSNAME} == "FreeBSD" .if ${OBJECT_FMT} == "ELF" ${SHLIB_LD} -x -shared ${SHLIB_SHFLAGS} -o ${.TARGET} \ ${SHLIB_LDSTARTFILE} \ --whole-archive ${SOLIB} --no-whole-archive ${SHLIB_LDADD} \ ${SHLIB_LDENDFILE} .else ${SHLIB_LD} ${LD_x} ${LD_shared} \ -o ${.TARGET} ${SOLIB} ${SHLIB_LDADD} .endif .else ${SHLIB_LD} -o ${.TARGET} ${LD_shared} ${LD_solib} ${DLLIB} ${SHLIB_LDADD} .endif .endif .if !empty(SHLIB_LINKS) rm -f ${SHLIB_LINKS}; ${SHLIB_LINKS:O:u:@x@ln -s ${.TARGET} $x;@} .endif LOBJS+= ${LSRCS:.c=.ln} ${SRCS:M*.c:.c=.ln} .NOPATH: ${LOBJS} LLIBS?= -lc llib-l${LIB}.ln: ${LOBJS} @${META_NOECHO} building llib-l${LIB}.ln @rm -f llib-l${LIB}.ln @${LINT} -C${LIB} ${LOBJS} ${LLIBS} .if !target(clean) cleanlib: .PHONY rm -f a.out [Ee]rrs mklog core *.core ${CLEANFILES} rm -f lib${LIB}.a ${OBJS} rm -f lib${LIB}_p.a ${POBJS} rm -f lib${LIB}_pic.a lib${LIB}.so.*.* ${SOBJS} rm -f llib-l${LIB}.ln ${LOBJS} .if !empty(SHLIB_LINKS) rm -f ${SHLIB_LINKS} .endif clean: _SUBDIRUSE cleanlib cleandir: _SUBDIRUSE cleanlib .else cleandir: _SUBDIRUSE clean .endif .if defined(SRCS) && (!defined(MKDEP) || ${MKDEP} != autodep) afterdepend: .depend @(TMP=/tmp/_depend$$$$; \ sed -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.po \1${PICO} \1.ln:/' \ < .depend > $$TMP; \ mv $$TMP .depend) .endif .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !empty(LIBOWN) LIB_INSTALL_OWN ?= -o ${LIBOWN} -g ${LIBGRP} .endif .include .if !target(libinstall) && !empty(LIB) realinstall: libinstall libinstall: [ -d ${DESTDIR}/${LIBDIR} ] || \ ${INSTALL} -d ${LIB_INSTALL_OWN} -m 775 ${DESTDIR}${LIBDIR} .if ${MK_ARCHIVE} != "no" ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m 644 lib${LIB}.a \ ${DESTDIR}${LIBDIR} ${RANLIB} ${DESTDIR}${LIBDIR}/lib${LIB}.a chmod ${LIBMODE} ${DESTDIR}${LIBDIR}/lib${LIB}.a .endif .if ${MK_PROFILE} != "no" ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m 644 \ lib${LIB}_p.a ${DESTDIR}${LIBDIR} ${RANLIB} ${DESTDIR}${LIBDIR}/lib${LIB}_p.a chmod ${LIBMODE} ${DESTDIR}${LIBDIR}/lib${LIB}_p.a .endif .if ${MK_LDORDER_MK} != "no" ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m 644 \ lib${LIB}.ldorder.inc ${DESTDIR}${LIBDIR} .endif .if ${MK_PIC} != "no" .if ${MK_PICLIB} != "no" ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m 644 \ lib${LIB}_pic.a ${DESTDIR}${LIBDIR} ${RANLIB} ${DESTDIR}${LIBDIR}/lib${LIB}_pic.a chmod ${LIBMODE} ${DESTDIR}${LIBDIR}/lib${LIB}_pic.a .endif .if !empty(SHLIB_MAJOR) ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m ${LIBMODE} \ lib${LIB}.${LD_so} ${DESTDIR}${LIBDIR} .if !empty(SHLIB_LINKS) (cd ${DESTDIR}${LIBDIR} && { rm -f ${SHLIB_LINKS}; ${SHLIB_LINKS:O:u:@x@ln -s lib${LIB}.${LD_so} $x;@} }) .endif .endif .endif .if ${MK_LINT} != "no" && ${MK_LINKLIB} != "no" && !empty(LOBJS) ${INSTALL} ${COPY} ${LIB_INSTALL_OWN} -m ${LIBMODE} \ llib-l${LIB}.ln ${DESTDIR}${LINTLIBDIR} .endif .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; ${_LINKS_SCRIPT} .endif .endif .if ${MK_MAN} != "no" install: maninstall _SUBDIRUSE maninstall: afterinstall .endif afterinstall: realinstall libinstall: beforeinstall realinstall: beforeinstall .endif .if defined(FILES) || defined(FILESGROUPS) .include .endif .if ${MK_MAN} != "no" .include .endif .if ${MK_NLS} != "no" .include .endif .include .include .include .include .endif # during building we usually need/want to install libs somewhere central # note that we do NOT ch{own,grp} as that would likely fail at this point. # otherwise it is the same as realinstall # Note that we don't need this when using dpadd.mk .libinstall: ${_LIBS} test -d ${DESTDIR}${LIBDIR} || ${INSTALL} -d -m775 ${DESTDIR}${LIBDIR} .for _lib in ${_LIBS:M*.a} ${INSTALL} ${COPY} -m 644 ${_lib} ${DESTDIR}${LIBDIR} ${RANLIB} ${DESTDIR}${LIBDIR}/${_lib} .endfor .for _lib in ${_LIBS:M*.${LD_solink}*:O:u} ${INSTALL} ${COPY} -m ${LIBMODE} ${_lib} ${DESTDIR}${LIBDIR} .if !empty(SHLIB_LINKS) (cd ${DESTDIR}${LIBDIR} && { ${SHLIB_LINKS:O:u:@x@ln -sf ${_lib} $x;@}; }) .endif .endfor @touch ${.TARGET} .if !empty(LIB) STAGE_LIBDIR?= ${STAGE_OBJTOP}${LIBDIR} stage_libs: ${_LIBS} .endif .include .endif Index: head/contrib/bmake/mk/libnames.mk =================================================================== --- head/contrib/bmake/mk/libnames.mk (revision 365365) +++ head/contrib/bmake/mk/libnames.mk (revision 365366) @@ -1,22 +1,22 @@ -# $Id: libnames.mk,v 1.8 2016/04/05 15:58:37 sjg Exp $ +# $Id: libnames.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2007-2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # DLIBEXT ?= .a DSHLIBEXT ?= .so .-include .-include .-include .-include Index: head/contrib/bmake/mk/libs.mk =================================================================== --- head/contrib/bmake/mk/libs.mk (revision 365365) +++ head/contrib/bmake/mk/libs.mk (revision 365366) @@ -1,93 +1,99 @@ -# $Id: libs.mk,v 1.3 2013/08/02 18:28:48 sjg Exp $ +# $Id: libs.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .MAIN: all .if defined(LIBS) # In meta mode, we can capture dependenices for _one_ of the progs. # if makefile doesn't nominate one, we use the first. .ifndef UPDATE_DEPENDFILE_LIB UPDATE_DEPENDFILE_LIB = ${LIBS:[1]} .export UPDATE_DEPENDFILE_LIB .endif .ifndef LIB # They may have asked us to build just one .for t in ${LIBS:R:T:S,^lib,,} .if make(lib$t) LIB?= $t lib$t: all .endif .endfor .endif .if defined(LIB) # just one of many LIB_VARS += \ LIBDIR \ CFLAGS \ COPTS \ CPPFLAGS \ CXXFLAGS \ DPADD \ DPLIBS \ LDADD \ LDFLAGS \ MAN \ SRCS .for v in ${LIB_VARS:O:u} .if defined(${v}.${LIB}) || defined(${v}_${LIB}) $v += ${${v}_${LIB}:U${${v}.${LIB}}} .endif .endfor # for meta mode, there can be only one! .if ${LIB} == ${UPDATE_DEPENDFILE_LIB:Uno} UPDATE_DEPENDFILE ?= yes .endif UPDATE_DEPENDFILE ?= NO # ensure that we don't clobber each other's dependencies DEPENDFILE?= .depend.${LIB} # lib.mk will do the rest .else all: ${LIBS:S,^lib,,:@t@lib$t.a@} .MAKE # We cannot capture dependencies for meta mode here UPDATE_DEPENDFILE = NO # nor can we safely run in parallel. .NOTPARALLEL: .endif .endif # handle being called [bsd.]libs.mk .include <${.PARSEFILE:S,libs,lib,}> .ifndef LIB # tell libs.mk we might want to install things LIBS_TARGETS+= cleandepend cleandir cleanobj depend install .for b in ${LIBS:R:T:S,^lib,,} -lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b} - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b) +lib$b.a: ${SRCS} ${DPADD} ${SRCS_lib$b} ${DPADD_lib$b} + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b -DWITHOUT_META_STATS) .for t in ${LIBS_TARGETS:O:u} $b.$t: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} LIB=$b ${@:E} -DWITHOUT_META_STATS) .endfor .endfor + +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 +.END: _reldir_finish +.ERROR: _reldir_failed +.endif + .endif Index: head/contrib/bmake/mk/links.mk =================================================================== --- head/contrib/bmake/mk/links.mk (revision 365365) +++ head/contrib/bmake/mk/links.mk (revision 365366) @@ -1,80 +1,80 @@ -# $Id: links.mk,v 1.6 2014/09/29 17:14:40 sjg Exp $ +# $Id: links.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2005, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # some platforms need something special LN?= ln ECHO?= echo LINKS?= SYMLINKS?= __SYMLINK_SCRIPT= \ ${ECHO} "$$t -> $$l"; \ case `'ls' -l $$t 2> /dev/null` in \ *"> $$l") ;; \ *) \ mkdir -p `dirname $$t`; \ rm -f $$t; \ ${LN} -s $$l $$t;; \ esac __LINK_SCRIPT= \ ${ECHO} "$$t -> $$l"; \ mkdir -p `dirname $$t`; \ rm -f $$t; \ ${LN} $$l $$t _SYMLINKS_SCRIPT= \ while test $$\# -ge 2; do \ l=$$1; shift; \ t=${DESTDIR}$$1; shift; \ ${__SYMLINK_SCRIPT}; \ done; :; _LINKS_SCRIPT= \ while test $$\# -ge 2; do \ l=${DESTDIR}$$1; shift; \ t=${DESTDIR}$$1; shift; \ ${__LINK_SCRIPT}; \ done; :; _SYMLINKS_USE: .USE @set ${$@_SYMLINKS:U${SYMLINKS}}; ${_SYMLINKS_SCRIPT} _LINKS_USE: .USE @set ${$@_LINKS:U${LINKS}}; ${_LINKS_SCRIPT} # sometimes we want to ensure DESTDIR is ignored _BUILD_SYMLINKS_SCRIPT= \ while test $$\# -ge 2; do \ l=$$1; shift; \ t=$$1; shift; \ ${__SYMLINK_SCRIPT}; \ done; :; _BUILD_LINKS_SCRIPT= \ while test $$\# -ge 2; do \ l=$$1; shift; \ t=$$1; shift; \ ${__LINK_SCRIPT}; \ done; :; _BUILD_SYMLINKS_USE: .USE @set ${$@_SYMLINKS:U${SYMLINKS}}; ${_BUILD_SYMLINKS_SCRIPT} _BUILD_LINKS_USE: .USE @set ${$@_LINKS:U${LINKS}}; ${_BUILD_LINKS_SCRIPT} Index: head/contrib/bmake/mk/manifest.mk =================================================================== --- head/contrib/bmake/mk/manifest.mk (revision 365365) +++ head/contrib/bmake/mk/manifest.mk (revision 365366) @@ -1,66 +1,66 @@ -# $Id: manifest.mk,v 1.2 2014/10/31 18:06:17 sjg Exp $ +# $Id: manifest.mk,v 1.3 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2014, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # generate mtree style manifest supported by makefs in FreeBSD # input looks like # MANIFEST= my.mtree # for each MANIFEST we have a list of dirs # ${MANIFEST}.DIRS += bin sbin usr/bin ... # for each dir we have a ${MANIFEST}.SRCS.$dir # that provides the absolute path to the contents -# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh +# ${MANIFEST}.SRCS.bin += ${OBJTOP}/bin/sh/sh # ${MANIFEST}.SYMLINKS is a list of src target pairs # for each file/dir there are a number of attributes # UID GID MODE FLAGS # which can be set per dir, per file or we use defaults -# eg. +# eg. # MODE.sbin = 550 # MODE.usr/sbin = 550 # MODE.dirs = 555 # means that sbin and usr/sbin get 550 all other dirs get 555 # MODE.usr/bin/passwd = 4555 # MODE.usr/bin.files = 555 # MODE.usr/sbin.files = 500 # means passwd gets 4555 other files in usr/bin get 555 and # files in usr/sbin get 500 # STORE defaults to basename of src and target directory -# but we can use +# but we can use # ${MANIFEST}.SRCS.sbin += ${OBJTOP}/bin/sh-static/sh-static # STORE.sbin/sh-static = sbin/sh # # the above is a little overkill but means we can easily adapt to # different formats UID.dirs ?= 0 GID.dirs ?= 0 MODE.dirs ?= 775 -FLAGS.dirs ?= +FLAGS.dirs ?= UID.files ?= 0 GID.files ?= 0 MODE.files ?= 555 # a is attribute name d is dirname M_DIR_ATTR = L:@a@$${$$a.$$d:U$${$$a.dirs}}@ # as above and s is set to the name we store f as M_FILE_ATTR = L:@a@$${$$a.$$s:U$${$$a.$$d.files:U$${$$a.files}}}@ # this produces the body of the manifest # there should typically be a header prefixed _GEN_MTREE_MANIFEST_USE: .USE @(${${.TARGET}.DIRS:O:u:@d@echo '$d type=dir uid=${UID:${M_DIR_ATTR}} gid=${GID:${M_DIR_ATTR}} mode=${MODE:${M_DIR_ATTR}} ${FLAGS:${M_DIR_ATTR}}';@} \ ${${.TARGET}.DIRS:O:u:@d@${${.TARGET}.SRCS.$d:O:u:@f@echo '${s::=${STORE.$d/${f:T}:U$d/${f:T}}}$s contents="$f" type=file uid=${UID:${M_FILE_ATTR}} gid=${GID:${M_FILE_ATTR}} mode=${MODE:${M_FILE_ATTR}} ${FLAGS:${M_FILE_ATTR}}';@}@} \ set ${${.TARGET}.SYMLINKS}; while test $$# -ge 2; do echo "$$2 type=link link=$$1"; shift 2; done) > ${.TARGET} Index: head/contrib/bmake/mk/meta.autodep.mk =================================================================== --- head/contrib/bmake/mk/meta.autodep.mk (revision 365365) +++ head/contrib/bmake/mk/meta.autodep.mk (revision 365366) @@ -1,320 +1,316 @@ -# $Id: meta.autodep.mk,v 1.50 2018/06/08 01:25:31 sjg Exp $ +# $Id: meta.autodep.mk,v 1.52 2020/07/18 05:57:57 sjg Exp $ # # @(#) Copyright (c) 2010, 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 ?= ${.PARSEFILE} .if !target(__${_this}__) __${_this}__: .NOTMAIN .-include PICO?= .pico .if defined(SRCS) # it would be nice to be able to query .SUFFIXES OBJ_EXTENSIONS+= .o .po .lo ${PICO} # explicit dependencies help short-circuit .SUFFIX searches SRCS_DEP_FILTER+= N*.[hly] .for s in ${SRCS:${SRCS_DEP_FILTER:O:u:ts:}} .for e in ${OBJ_EXTENSIONS:O:u} .if !target(${s:T:R}$e) ${s:T:R}$e: $s .endif .endfor .endfor .endif .if make(gendirdeps) # you are supposed to know what you are doing! UPDATE_DEPENDFILE = yes .elif !empty(.TARGETS) && !make(all) # do not update the *depend* files # unless we are building the entire directory or the default target. # NO means don't update .depend - or Makefile.depend* # no means update .depend but not Makefile.depend* UPDATE_DEPENDFILE = NO .elif ${.MAKEFLAGS:M-k} != "" # it is a bad idea to update anything UPDATE_DEPENDFILE = NO .endif _CURDIR ?= ${.CURDIR} _OBJDIR ?= ${.OBJDIR} _OBJTOP ?= ${OBJTOP} _OBJROOT ?= ${OBJROOT:U${_OBJTOP}} _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T} -.if ${.MAKE.LEVEL} > 0 || ${BUILD_AT_LEVEL0:Uyes:tl} == "yes" +.if ${.MAKE.LEVEL} > 0 # do not allow auto update if we ever built this dir without filemon NO_FILEMON_COOKIE = .nofilemon CLEANFILES += ${NO_FILEMON_COOKIE} .if ${.MAKE.MODE:Uno:Mnofilemon} != "" UPDATE_DEPENDFILE = NO all: ${NO_FILEMON_COOKIE} ${NO_FILEMON_COOKIE}: .NOMETA @echo UPDATE_DEPENDFILE=NO > ${.TARGET} .elif exists(${NO_FILEMON_COOKIE}) UPDATE_DEPENDFILE = NO .warning ${RELDIR} built with nofilemon; UPDATE_DEPENDFILE=NO .endif .endif .if ${.MAKE.LEVEL} == 0 -.if ${BUILD_AT_LEVEL0:Uyes:tl} == "no" UPDATE_DEPENDFILE = NO .endif -.endif .if !exists(${_DEPENDFILE}) _bootstrap_dirdeps = yes .endif _bootstrap_dirdeps ?= no UPDATE_DEPENDFILE ?= yes .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} update=${UPDATE_DEPENDFILE} .endif .if !empty(XMAKE_META_FILE) .if exists(${.OBJDIR}/${XMAKE_META_FILE}) # we cannot get accurate dependencies from an update build UPDATE_DEPENDFILE = NO .else META_XTRAS += ${XMAKE_META_FILE} .endif .endif .if ${_bootstrap_dirdeps} == "yes" || exists(${_DEPENDFILE}) # if it isn't supposed to be touched by us the Makefile should have # UPDATE_DEPENDFILE = no WANT_UPDATE_DEPENDFILE ?= yes .endif .if ${WANT_UPDATE_DEPENDFILE:Uno:tl} != "no" .if ${.MAKE.MODE:Uno:Mmeta*} == "" || ${.MAKE.MODE:Uno:M*read*} != "" UPDATE_DEPENDFILE = no .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} update=${UPDATE_DEPENDFILE} .endif .if ${UPDATE_DEPENDFILE:tl} == "yes" # sometimes we want .meta files generated to aid debugging/error detection # but do not want to consider them for dependencies # for example the result of running configure # just make sure this is not empty META_FILE_FILTER ?= N.meta # never consider these META_FILE_FILTER += Ndirdeps.cache* .if !empty(DPADD) # if we have any non-libs in DPADD, # they probably need to be paid attention to .if !empty(DPLIBS) FORCE_DPADD = ${DPADD:${DPLIBS:${M_ListToSkip}}:${DPADD_LAST:${M_ListToSkip}}} .else _nonlibs := ${DPADD:T:Nlib*:N*include} .if !empty(_nonlibs) FORCE_DPADD += ${_nonlibs:@x@${DPADD:M*/$x}@} .endif .endif .endif .if !make(gendirdeps) .END: gendirdeps .endif # if we don't have OBJS, then .depend isn't useful .if !target(.depend) && (!empty(OBJS) || ${.ALLTARGETS:M*.o} != "") # some makefiles and/or targets contain # circular dependencies if you dig too deep # (as meta mode is apt to do) # so we provide a means of suppressing them. # the input to the loop below is target: dependency # with just one dependency per line. # Also some targets are not really local, or use random names. # Use local.autodep.mk to provide local additions! SUPPRESS_DEPEND += \ ${SB:S,/,_,g}* \ *:y.tab.c \ *.c:*.c \ *.h:*.h .NOPATH: .depend # we use ${.MAKE.META.CREATED} to trigger an update but # we process using ${.MAKE.META.FILES} # the double $$ defers initial evaluation # if necessary, we fake .po dependencies, just so the result # in Makefile.depend* is stable # The current objdir may be referred to in various ways OBJDIR_REFS += ${.OBJDIR} ${.OBJDIR:tA} ${_OBJDIR} ${RELOBJTOP}/${RELDIR} _depend = .depend # it would be nice to be able to get .SUFFIXES as ${.SUFFIXES} # we actually only care about the .SUFFIXES of files that might be # generated by tools like yacc. DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh .depend: .NOMETA $${.MAKE.META.CREATED} ${_this} @echo "Updating $@: ${.OODATE:T:[1..8]}" @egrep -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \ sed -e 's, \./, ,${OBJDIR_REFS:O:u:@d@;s, $d/, ,@};/\//d' \ -e 's,^\([^/][^/]*\).meta...[0-9]* ,\1: ,' | \ sort -u | \ while read t d; do \ case "$$d:" in $$t) continue;; esac; \ case "$$t$$d" in ${SUPPRESS_DEPEND:U.:O:u:ts|}) continue;; esac; \ echo $$t $$d; \ done > $@.${.MAKE.PID} @case "${.MAKE.META.FILES:T:M*.po.*}" in \ *.po.*) mv $@.${.MAKE.PID} $@;; \ *) { cat $@.${.MAKE.PID}; \ sed 's,\${PICO}:,.o:,;s,\.o:,.po:,' $@.${.MAKE.PID}; } | sort -u > $@; \ rm -f $@.${.MAKE.PID};; \ esac .else # make sure this exists .depend: # do _not_ assume that .depend is in any fit state for us to use CAT_DEPEND = /dev/null .if ${.MAKE.LEVEL} > 0 .export CAT_DEPEND .endif _depend = .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,} _depend=${_depend} .endif .if ${UPDATE_DEPENDFILE} == "yes" gendirdeps: ${_DEPENDFILE} .endif .if !target(${_DEPENDFILE}) .if ${_bootstrap_dirdeps} == "yes" # We are boot-strapping a new directory # Use DPADD to seed DIRDEPS .if !empty(DPADD) # anything which matches ${_OBJROOT}* but not ${_OBJTOP}* # needs to be qualified in DIRDEPS # The pseudo machine "host" is used for HOST_TARGET DIRDEPS += \ ${DPADD:M${_OBJTOP}*:H:C,${_OBJTOP}[^/]*/,,:N.:O:u} \ ${DPADD:M${_OBJROOT}*:N${_OBJTOP}*:N${STAGE_ROOT:U${_OBJTOP}}/*:H:S,${_OBJROOT},,:C,^([^/]+)/(.*),\2.\1,:S,${HOST_TARGET}$,host,:N.*:O:u} .endif .endif _gendirdeps_mutex = .if defined(NEED_GENDIRDEPS_MUTEX) # If a src dir gets built with multiple object dirs, # we need a mutex. Obviously, this is best avoided. # Note if .MAKE.DEPENDFILE is common for all ${MACHINE} # you either need to mutex, or ensure only one machine builds at a time! # lockf is an example of a suitable tool LOCKF ?= /usr/bin/lockf .if exists(${LOCKF}) GENDIRDEPS_MUTEXER ?= ${LOCKF} -k .endif .if empty(GENDIRDEPS_MUTEXER) .error NEED_GENDIRDEPS_MUTEX defined, but GENDIRDEPS_MUTEXER not set .else _gendirdeps_mutex = ${GENDIRDEPS_MUTEXER} ${GENDIRDEPS_MUTEX:U${_CURDIR}/Makefile} .endif .endif # If we have META_XTRAS we most likely did not create them # but we need to behave as if we did. # Avoid adding glob patterns to .MAKE.META.CREATED though. .MAKE.META.CREATED += ${META_XTRAS:N*\**:O:u} .if make(gendirdeps) META_FILES = *.meta .elif ${OPTIMIZE_OBJECT_META_FILES:Uno:tl} == "no" META_FILES = ${.MAKE.META.FILES:T:N.depend*:O:u} .else # if we have 1000's of .o.meta, ${PICO}.meta etc we need only look at one set # it is left as an exercise for the reader to work out what this does META_FILES = ${.MAKE.META.FILES:T:N.depend*:N*o.meta:O:u} \ ${.MAKE.META.FILES:T:M*.${.MAKE.META.FILES:M*o.meta:R:E:O:u:[1]}.meta:O:u} .endif .if ${DEBUG_AUTODEP:Uno:@m@${RELDIR:M$m}@} != "" .info ${_DEPENDFILE:S,${SRCTOP}/,,}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} xtras=${META_XTRAS} .endif .if ${.MAKE.LEVEL} > 0 .if ${UPDATE_DEPENDFILE} == "yes" .-include <${.CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.options> .endif .if !empty(GENDIRDEPS_FILTER) .export GENDIRDEPS_FILTER .endif # export to avoid blowing command line limit META_FILES := ${META_XTRAS:U:O:u} ${META_FILES:U:T:O:u:${META_FILE_FILTER:ts:}} .export META_FILES .endif # we might have .../ in MAKESYSPATH _makesyspath:= ${_PARSEDIR} ${_DEPENDFILE}: ${_depend} ${.PARSEDIR}/gendirdeps.mk ${META2DEPS} $${.MAKE.META.CREATED} @echo Checking $@: ${.OODATE:T:[1..8]} @(cd . && ${GENDIRDEPS_ENV} \ SKIP_GENDIRDEPS='${SKIP_GENDIRDEPS:O:u}' \ DPADD='${FORCE_DPADD:O:u}' ${_gendirdeps_mutex} \ MAKESYSPATH=${_makesyspath} \ ${.MAKE} -f gendirdeps.mk RELDIR=${RELDIR} _DEPENDFILE=${_DEPENDFILE}) @test -s $@ && touch $@; : .endif .endif .endif .if ${_bootstrap_dirdeps} == "yes" -.if ${BUILD_AT_LEVEL0:Uno} == "no" DIRDEPS+= ${RELDIR}.${TARGET_SPEC:U${MACHINE}} -.endif # make sure this is included at least once .include .else ${_DEPENDFILE}: .PRECIOUS .endif CLEANFILES += *.meta filemon.* *.db # these make it easy to gather some stats now_utc = ${%s:L:gmtime} start_utc := ${now_utc} meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \ created=${empty(.MAKE.META.CREATED):?0:${.MAKE.META.CREATED:[#]}} #.END: _reldir_finish .if target(gendirdeps) _reldir_finish: gendirdeps .endif _reldir_finish: .NOMETA @echo "${TIME_STAMP} Finished ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" #.ERROR: _reldir_failed _reldir_failed: .NOMETA @echo "${TIME_STAMP} Failed ${RELDIR}.${TARGET_SPEC} seconds=$$(( ${now_utc} - ${start_utc} )) ${meta_stats}" -.if defined(WITH_META_STATS) && ${.MAKE.LEVEL} > 0 +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 .END: _reldir_finish .ERROR: _reldir_failed .endif .endif Index: head/contrib/bmake/mk/meta.stage.mk =================================================================== --- head/contrib/bmake/mk/meta.stage.mk (revision 365365) +++ head/contrib/bmake/mk/meta.stage.mk (revision 365366) @@ -1,366 +1,366 @@ -# $Id: meta.stage.mk,v 1.59 2020/04/25 18:18:27 sjg Exp $ +# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2011-2017, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .ifndef NO_STAGING .if !target(__${.PARSEFILE}__) # the guard target is defined later .-include .if ${.MAKE.DEPENDFILE_PREFERENCE:U${.MAKE.DEPENDFILE}:M*.${MACHINE}} != "" # this is generally safer anyway _dirdep ?= ${RELDIR}.${TARGET_SPEC:U${MACHINE}} .else _dirdep ?= ${RELDIR} .endif CLEANFILES+= .dirdep # this allows us to trace dependencies back to their src dir .dirdep: .NOPATH @echo '${_dirdep}' > $@ .if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == "" _stage_file_basename = `basename $$f` _stage_target_dirname = `dirname $$t` .else _stage_file_basename = $${f\#\#*/} _stage_target_dirname = $${t%/*} .endif _OBJROOT ?= ${OBJROOT:U${OBJTOP:H}} .if ${_OBJROOT:M*/} != "" _objroot ?= ${_OBJROOT:tA}/ .else _objroot ?= ${_OBJROOT:tA} .endif # make sure this is global _STAGED_DIRS ?= .export _STAGED_DIRS # add each dir we stage to to _STAGED_DIRS # and make sure we have absolute paths so that bmake # will match against .MAKE.META.BAILIWICK STAGE_DIR_FILTER = tA:@d@$${_STAGED_DIRS::+=$$d}$$d@ # convert _STAGED_DIRS into suitable filters GENDIRDEPS_FILTER += Nnot-empty-is-important \ ${_STAGED_DIRS:O:u:M${OBJTOP}*:S,${OBJTOP}/,N,} \ ${_STAGED_DIRS:O:u:M${_objroot}*:N${OBJTOP}*:S,${_objroot},,:C,^([^/]+)/(.*),N\2.\1,:S,${HOST_TARGET},.host,} LN_CP_SCRIPT = LnCp() { \ rm -f $$2 2> /dev/null; \ { [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \ cp -p $$1 $$2; } # a staging conflict should cause an error # a warning is handy when bootstapping different options. STAGE_CONFLICT?= ERROR .if ${STAGE_CONFLICT:tl} == "error" STAGE_CONFLICT_ACTION= exit 1 .else STAGE_CONFLICT_ACTION= .endif # it is an error for more than one src dir to try and stage # the same file STAGE_DIRDEP_SCRIPT = ${LN_CP_SCRIPT}; StageDirdep() { \ t=$$1; \ if [ -s $$t.dirdep ]; then \ cmp -s .dirdep $$t.dirdep && return; \ 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; } # common logic for staging files # this all relies on RELDIR being set to a subdir of SRCTOP # we use ln(1) if we can, else cp(1) STAGE_FILE_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageFiles() { \ case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ for f in "$$@"; do \ case "$$f" in */*) t=$$dest/${_stage_file_basename};; *) t=$$dest/$$f;; esac; \ StageDirdep $$t; \ LnCp $$f $$t || exit 1; \ [ -z "$$mode" ] || chmod $$mode $$t; \ done; :; } STAGE_LINKS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageLinks() { \ case "$$1" in "") return;; --) shift;; -*) ldest= lnf=$$1; shift;; /*) ldest=$$1/;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ while test $$\# -ge 2; do \ l=$$ldest$$1; shift; \ t=$$dest/$$1; \ case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \ shift; \ StageDirdep $$t; \ rm -f $$t 2>/dev/null; \ ln $$lnf $$l $$t || exit 1; \ done; :; } STAGE_AS_SCRIPT = ${STAGE_DIRDEP_SCRIPT}; StageAs() { \ case "$$1" in "") return;; -m) mode=$$2; shift 2;; *) mode=;; esac; \ dest=$$1; shift; \ mkdir -p $$dest; \ [ -s .dirdep ] || echo '${_dirdep}' > .dirdep; \ while test $$\# -ge 2; do \ s=$$1; shift; \ t=$$dest/$$1; \ case "$$1" in */*) mkdir -p ${_stage_target_dirname};; esac; \ shift; \ StageDirdep $$t; \ LnCp $$s $$t || exit 1; \ [ -z "$$mode" ] || chmod $$mode $$t; \ done; :; } # this is simple, a list of the "staged" files depends on this, _STAGE_BASENAME_USE: .USE .dirdep ${.TARGET:T} @${STAGE_FILE_SCRIPT}; StageFiles ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T} _STAGE_AS_BASENAME_USE: .USE .dirdep ${.TARGET:T} @${STAGE_AS_SCRIPT}; StageAs ${.TARGET:H:${STAGE_DIR_FILTER}} ${.TARGET:T} ${STAGE_AS_${.TARGET:T}:U${.TARGET:T}} .endif # first time .if !empty(STAGE_INCSDIR) .if !empty(STAGE_INCS) stage_incs: ${STAGE_INCS:N*\**} .endif .if target(stage_incs) || !empty(.ALLTARGETS:Mstage_includes) STAGE_TARGETS += stage_incs STAGE_INCS ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_includes: stage_incs stage_incs: .dirdep @${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_INCSDIR:${STAGE_DIR_FILTER}} ${STAGE_INCS} @touch $@ .endif .endif .if !empty(STAGE_LIBDIR) .if !empty(STAGE_LIBS) stage_libs: ${STAGE_LIBS:N*\**} .endif .if target(stage_libs) STAGE_TARGETS += stage_libs STAGE_LIBS ?= ${.ALLSRC:N.dirdep:Nstage_*} stage_libs: .dirdep @${STAGE_FILE_SCRIPT}; StageFiles ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${STAGE_LIBS} .if !defined(NO_SHLIB_LINKS) .if !empty(SHLIB_LINKS) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} \ ${SHLIB_LINKS:@t@${STAGE_LIBS:T:M$t.*} $t@} .elif !empty(SHLIB_LINK) && !empty(SHLIB_NAME) @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_LIBDIR:${STAGE_DIR_FILTER}} ${SHLIB_NAME} ${SHLIB_LINK} .endif .endif @touch $@ .endif .endif .if !empty(STAGE_DIR) STAGE_SETS += _default STAGE_DIR._default = ${STAGE_DIR} STAGE_LINKS_DIR._default = ${STAGE_LINKS_DIR:U${STAGE_OBJTOP}} STAGE_SYMLINKS_DIR._default = ${STAGE_SYMLINKS_DIR:U${STAGE_OBJTOP}} STAGE_FILES._default = ${STAGE_FILES} STAGE_LINKS._default = ${STAGE_LINKS} STAGE_SYMLINKS._default = ${STAGE_SYMLINKS} .endif .if !empty(STAGE_SETS) CLEANFILES += ${STAGE_SETS:@s@stage*$s@} # some makefiles need to populate multiple directories .for s in ${STAGE_SETS:O:u} .if !empty(STAGE_FILES.$s) stage_files.$s: ${STAGE_FILES.$s:N*\**} .endif .if target(stage_files.$s) || target(stage_files${s:S,^,.,:N._default}) STAGE_TARGETS += stage_files STAGE_FILES.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_files.$s) .stage_files.$s: .if $s != "_default" stage_files: stage_files.$s stage_files.$s: .dirdep .else 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:O} @touch $@ .endif .endif .if !empty(STAGE_LINKS.$s) stage_links.$s: .endif .if target(stage_links.$s) || target(stage_links${s:S,^,.,:N._default}) STAGE_LINKS_DIR.$s ?= ${STAGE_OBJTOP} STAGE_TARGETS += stage_links .if !target(.stage_links.$s) .stage_links.$s: .if $s != "_default" stage_links: stage_links.$s stage_links.$s: .dirdep .else stage_links: .dirdep .endif @${STAGE_LINKS_SCRIPT}; StageLinks ${STAGE_LINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_LINKS.$s} @touch $@ .endif .endif .if !empty(STAGE_SYMLINKS.$s) stage_symlinks.$s: .endif .if target(stage_symlinks.$s) || target(stage_symlinks${s:S,^,.,:N._default}) STAGE_SYMLINKS_DIR.$s ?= ${STAGE_OBJTOP} STAGE_TARGETS += stage_symlinks .if !target(.stage_symlinks.$s) .stage_symlinks.$s: .if $s != "_default" stage_symlinks: stage_symlinks.$s stage_symlinks.$s: .dirdep .else stage_symlinks: .dirdep .endif @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_SYMLINKS_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_SYMLINKS.$s} @touch $@ .endif .endif .endfor .endif .if !empty(STAGE_AS_SETS) CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@} # sometimes things need to be renamed as they are staged # each ${file} will be staged as ${STAGE_AS_${file:T}} # one could achieve the same with SYMLINKS # stage_as_and_symlink makes the original name a symlink to the new name # it is the same as using stage_as and stage_symlinks but ensures # both operations happen together .for s in ${STAGE_AS_SETS:O:u} .if !empty(STAGE_AS.$s) stage_as.$s: ${STAGE_AS.$s:N*\**} .endif .if target(stage_as.$s) STAGE_TARGETS += stage_as STAGE_AS.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_as.$s) .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:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@} @touch $@ .endif .endif .if !empty(STAGE_AS_AND_SYMLINK.$s) stage_as_and_symlink.$s: ${STAGE_AS_AND_SYMLINK.$s:N*\**} .endif .if target(stage_as_and_symlink.$s) STAGE_TARGETS += stage_as_and_symlink STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*} .if !target(.stage_as_and_symlink.$s) .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: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 .endfor .endif CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes # this lot also only makes sense the first time... .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: # stage_*links usually needs to follow any others. # for non-jobs mode the order here matters staging: ${STAGE_TARGETS:N*_links} ${STAGE_TARGETS:M*_links} .if ${.MAKE.JOBS:U0} > 0 && ${STAGE_TARGETS:U:M*_links} != "" # the above isn't sufficient .for t in ${STAGE_TARGETS:N*links:O:u} .ORDER: $t stage_links .endfor .endif # generally we want staging to wait until everything else is done STAGING_WAIT ?= .WAIT .if ${.MAKE.LEVEL} > 0 all: ${STAGING_WAIT} staging .endif .if exists(${.PARSEDIR}/stage-install.sh) && !defined(STAGE_INSTALL) # this will run install(1) and then followup with .dirdep files. STAGE_INSTALL := sh ${.PARSEDIR:tA}/stage-install.sh INSTALL="${INSTALL}" OBJDIR=${.OBJDIR:tA} .endif # if ${INSTALL} gets run during 'all' assume it is for staging? .if ${.TARGETS:Nall} == "" && defined(STAGE_INSTALL) INSTALL := ${STAGE_INSTALL} .if target(beforeinstall) beforeinstall: .dirdep .endif .endif .NOPATH: ${STAGE_FILES} .if !empty(STAGE_TARGETS) # for backwards compat make sure they exist ${STAGE_TARGETS}: .NOPATH: ${CLEANFILES} MK_STALE_STAGED?= no .if ${MK_STALE_STAGED} == "yes" all: stale_staged # get a list of paths that we have just staged # get a list of paths that we have previously staged to those same dirs # anything in the 2nd list but not the first is stale - remove it. stale_staged: staging .NOMETA @egrep '^[WL] .*${STAGE_OBJTOP}' /dev/null ${.MAKE.META.FILES:M*stage_*} | \ sed "/\.dirdep/d;s,.* '*\(${STAGE_OBJTOP}/[^ '][^ ']*\).*,\1," | \ sort > ${.TARGET}.staged1 @grep -l '${_dirdep}' /dev/null ${_STAGED_DIRS:M${STAGE_OBJTOP}*:O:u:@d@$d/*.dirdep@} | \ sed 's,\.dirdep,,' | sort > ${.TARGET}.staged2 @comm -13 ${.TARGET}.staged1 ${.TARGET}.staged2 > ${.TARGET}.stale @test ! -s ${.TARGET}.stale || { \ echo "Removing stale staged files..."; \ sed 's,.*,& &.dirdep,' ${.TARGET}.stale | xargs rm -f; } .endif .endif .endif .endif Index: head/contrib/bmake/mk/meta.subdir.mk =================================================================== --- head/contrib/bmake/mk/meta.subdir.mk (revision 365365) +++ head/contrib/bmake/mk/meta.subdir.mk (revision 365366) @@ -1,79 +1,79 @@ -# $Id: meta.subdir.mk,v 1.11 2015/11/24 22:26:51 sjg Exp $ +# $Id: meta.subdir.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2010, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !defined(NO_SUBDIR) && !empty(SUBDIR) .if make(destroy*) || make(clean*) .MAKE.MODE = compat .if !commands(destroy) .-include .endif .elif ${.MAKE.LEVEL} == 0 .MAIN: all .if !exists(${.CURDIR}/${.MAKE.DEPENDFILE:T}) || make(gendirdeps) # start with this DIRDEPS = ${SUBDIR:N.WAIT:O:u:@d@${RELDIR}/$d@} .if make(gendirdeps) .include .else # this is the cunning bit -# actually it is probably a bit risky +# actually it is probably a bit risky # since we may pickup subdirs which are not relevant # the alternative is a walk through the tree though # which is difficult without a sub-make. .if defined(BOOTSTRAP_DEPENDFILES) _find_name = ${.MAKE.MAKEFILE_PREFERENCE:@m@-o -name $m@:S,^-o,,1} DIRDEPS = ${_subdeps:H:O:u:@d@${RELDIR}/$d@} .elif ${.MAKE.DEPENDFILE:E} == ${MACHINE} && defined(ALL_MACHINES) # we want to find Makefile.depend.* ie for all machines # and turn the dirs into dir. _find_name = -name '${.MAKE.DEPENDFILE:T:R}*' DIRDEPS = ${_subdeps:O:u:${NIgnoreFiles}:@d@${RELDIR}/${d:H}.${d:E}@:S,.${MACHINE}$,,:S,.depend$,,} .else # much simpler _find_name = -name ${.MAKE.DEPENDFILE:T} .if ${.MAKE.DEPENDFILE:E} == ${MACHINE} _find_name += -o -name ${.MAKE.DEPENDFILE:T:R} .endif DIRDEPS = ${_subdeps:H:O:u:@d@${RELDIR}/$d@} .endif _subdeps != cd ${.CURDIR} && \ find ${SUBDIR:N.WAIT} -type f \( ${_find_name} \) -print -o \ -name .svn -prune 2> /dev/null; echo .if empty(_subdeps) DIRDEPS = .else # clean up if needed DIRDEPS := ${DIRDEPS:S,^./,,:S,/./,/,g:${SUBDIRDEPS_FILTER:Uu}} .endif # we just dealt with it, if we leave it defined, # dirdeps.mk will compute some interesting combinations. .undef ALL_MACHINES DEP_RELDIR = ${RELDIR} .include .endif .endif .else all: .PHONY .endif .endif Index: head/contrib/bmake/mk/meta.sys.mk =================================================================== --- head/contrib/bmake/mk/meta.sys.mk (revision 365365) +++ head/contrib/bmake/mk/meta.sys.mk (revision 365366) @@ -1,173 +1,167 @@ -# $Id: meta.sys.mk,v 1.36 2020/05/16 23:21:48 sjg Exp $ +# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $ # # @(#) 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. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # include this if you want to enable meta mode # for maximum benefit, requires filemon(4) driver. .if ${MAKE_VERSION:U0} > 20100901 .if !target(.ERROR) .-include # absolute path to what we are reading. _PARSEDIR = ${.PARSEDIR:tA} .if !defined(SYS_MK_DIR) SYS_MK_DIR := ${_PARSEDIR} .endif META_MODE += meta verbose .MAKE.MODE ?= ${META_MODE} .if ${.MAKE.LEVEL} == 0 _make_mode := ${.MAKE.MODE} ${META_MODE} .if ${_make_mode:M*read*} != "" || ${_make_mode:M*nofilemon*} != "" # tell everyone we are not updating Makefile.depend* UPDATE_DEPENDFILE = NO .export UPDATE_DEPENDFILE .endif .if ${UPDATE_DEPENDFILE:Uyes:tl} == "no" && !exists(/dev/filemon) # we should not get upset META_MODE += nofilemon .export META_MODE .endif .endif .if !defined(NO_SILENT) .if ${MAKE_VERSION} > 20110818 # only be silent when we have a .meta file META_MODE += silent=yes .else .SILENT: .endif .endif # we use the pseudo machine "host" for the build host. # this should be taken care of before we get here .if ${OBJTOP:Ua} == ${HOST_OBJTOP:Ub} MACHINE = host .endif .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 .else META2DEPS ?= ${.PARSEDIR}/meta2deps.sh .endif META2DEPS := ${META2DEPS} .export META2DEPS .endif MAKE_PRINT_VAR_ON_ERROR += \ .ERROR_TARGET \ .ERROR_META_FILE \ .MAKE.LEVEL \ MAKEFILE \ .MAKE.MODE .if !defined(SB) && defined(SRCTOP) SB = ${SRCTOP:H} .endif ERROR_LOGDIR ?= ${SB}/error meta_error_log = ${ERROR_LOGDIR}/meta-${.MAKE.PID}.log # we are not interested in make telling us a failure happened elsewhere .ERROR: _metaError _metaError: .NOMETA .NOTMAIN -@[ "${.ERROR_META_FILE}" ] && { \ grep -q 'failure has been detected in another branch' ${.ERROR_META_FILE} && exit 0; \ mkdir -p ${meta_error_log:H}; \ cp ${.ERROR_META_FILE} ${meta_error_log}; \ echo "ERROR: log ${meta_error_log}" >&2; }; : .endif # Are we, after all, in meta mode? .if ${.MAKE.MODE:Uno:Mmeta*} != "" MKDEP_MK = meta.autodep.mk .if ${.MAKE.MAKEFILES:M*sys.dependfile.mk} == "" # this does all the smarts of setting .MAKE.DEPENDFILE .-include # check if we got anything sane .if ${.MAKE.DEPENDFILE} == ".depend" .undef .MAKE.DEPENDFILE .endif .MAKE.DEPENDFILE ?= Makefile.depend .endif # we can afford to use cookies to prevent some targets # re-running needlessly META_COOKIE_TOUCH?= touch ${COOKIE.${.TARGET}:U${.OBJDIR}/${.TARGET:T}} META_NOPHONY= META_NOECHO= : # some targets involve old pre-built targets # ignore mtime of shell # and mtime of makefiles does not matter in meta mode .MAKE.META.IGNORE_PATHS += \ ${MAKEFILE} \ ${MAKE_SHELL} \ ${SHELL} \ ${SYS_MK_DIR} \ .if ${UPDATE_DEPENDFILE:Uyes:tl} != "no" .if ${.MAKEFLAGS:Uno:M-k} != "" # make this more obvious .warning Setting UPDATE_DEPENDFILE=NO due to -k UPDATE_DEPENDFILE= NO .export UPDATE_DEPENDFILE .elif !exists(/dev/filemon) .error ${.newline}ERROR: The filemon module (/dev/filemon) is not loaded. .endif .endif .if ${.MAKE.LEVEL} == 0 # make sure dirdeps target exists and do it first all: dirdeps .WAIT dirdeps: .NOPATH: dirdeps .if defined(ALL_MACHINES) # the first .MAIN: is what counts # by default dirdeps is all we want at level0 .MAIN: dirdeps -# tell dirdeps.mk what we want -BUILD_AT_LEVEL0 = no -.endif -.if ${.TARGETS:Nall} == "" -# it works best if we do everything via sub-makes -BUILD_AT_LEVEL0 ?= no .endif .endif .else META_COOKIE_TOUCH= # some targets need to be .PHONY in non-meta mode META_NOPHONY= .PHONY META_NOECHO= echo .endif .endif Index: head/contrib/bmake/mk/meta2deps.py =================================================================== --- head/contrib/bmake/mk/meta2deps.py (revision 365365) +++ head/contrib/bmake/mk/meta2deps.py (revision 365366) @@ -1,765 +1,767 @@ #!/usr/bin/env python from __future__ import print_function """ This script parses each "meta" file and extracts the information needed to deduce build and src dependencies. It works much the same as the original shell script, but is *much* more efficient. The parsing work is handled by the class MetaFile. We only pay attention to a subset of the information in the "meta" files. Specifically: 'CWD' to initialize our notion. 'C' to track chdir(2) on a per process basis 'R' files read are what we really care about. directories read, provide a clue to resolving subsequent relative paths. That is if we cannot find them relative to 'cwd', we check relative to the last dir read. 'W' files opened for write or read-write, for filemon V3 and earlier. - + 'E' files executed. 'L' files linked 'V' the filemon version, this record is used as a clue that we have reached the interesting bit. """ """ RCSid: - $Id: meta2deps.py,v 1.30 2020/06/08 23:05:00 sjg Exp $ + $Id: meta2deps.py,v 1.33 2020/08/19 17:51:53 sjg Exp $ - Copyright (c) 2011-2019, Simon J. Gerraty + Copyright (c) 2011-2020, Simon J. Gerraty Copyright (c) 2011-2017, Juniper Networks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: + 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. + 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. + documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. - + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ import os, re, sys def getv(dict, key, d=None): """Lookup key in dict and return value or the supplied default.""" if key in dict: return dict[key] return d def resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): """ Return an absolute path, resolving via cwd or last_dir if needed. """ if path.endswith('/.'): path = path[0:-2] if len(path) > 0 and path[0] == '/': if os.path.exists(path): return path if debug > 2: print("skipping non-existent:", path, file=debug_out) return None if path == '.': return cwd if path.startswith('./'): return cwd + path[1:] if last_dir == cwd: last_dir = None for d in [last_dir, cwd]: if not d: continue if path == '..': dw = d.split('/') p = '/'.join(dw[:-1]) if not p: p = '/' return p p = '/'.join([d,path]) if debug > 2: print("looking for:", p, end=' ', file=debug_out) if not os.path.exists(p): if debug > 2: print("nope", file=debug_out) p = None continue if debug > 2: print("found:", p, file=debug_out) return p return None def cleanpath(path): """cleanup path without using realpath(3)""" if path.startswith('/'): r = '/' else: r = '' p = [] w = path.split('/') for d in w: if not d or d == '.': continue if d == '..': try: p.pop() continue except: break p.append(d) return r + '/'.join(p) def abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): """ Return an absolute path, resolving via cwd or last_dir if needed. this gets called a lot, so we try to avoid calling realpath. """ rpath = resolve(path, cwd, last_dir, debug, debug_out) if rpath: path = rpath elif len(path) > 0 and path[0] == '/': return None if (path.find('/') < 0 or path.find('./') > 0 or path.endswith('/..')): path = cleanpath(path) return path def sort_unique(list, cmp=None, key=None, reverse=False): list.sort(cmp, key, reverse) nl = [] le = None for e in list: if e == le: continue le = e nl.append(e) return nl def add_trims(x): return ['/' + x + '/', - '/' + x, + '/' + x, x + '/', x] class MetaFile: """class to parse meta files generated by bmake.""" conf = None dirdep_re = None host_target = None srctops = [] objroots = [] excludes = [] seen = {} obj_deps = [] src_deps = [] file_deps = [] - + def __init__(self, name, conf={}): """if name is set we will parse it now. conf can have the follwing keys: SRCTOPS list of tops of the src tree(s). CURDIR the src directory 'bmake' was run from. RELDIR the relative path from SRCTOP to CURDIR MACHINE the machine we built for. set to 'none' if we are not cross-building. More specifically if machine cannot be deduced from objdirs. TARGET_SPEC Sometimes MACHINE isn't enough. - + HOST_TARGET when we build for the pseudo machine 'host' the object tree uses HOST_TARGET rather than MACHINE. OBJROOTS a list of the common prefix for all obj dirs it might end in '/' or '-'. DPDEPS names an optional file to which per file dependencies will be appended. For example if 'some/path/foo.h' is read from SRCTOP then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output. This can allow 'bmake' to learn all the dirs within the tree that depend on 'foo.h' EXCLUDES A list of paths to ignore. ccache(1) can otherwise be trouble. debug desired debug level debug_out open file to send debug output to (sys.stderr) """ - + self.name = name self.debug = getv(conf, 'debug', 0) self.debug_out = getv(conf, 'debug_out', sys.stderr) self.machine = getv(conf, 'MACHINE', '') self.machine_arch = getv(conf, 'MACHINE_ARCH', '') self.target_spec = getv(conf, 'TARGET_SPEC', '') self.curdir = getv(conf, 'CURDIR') self.reldir = getv(conf, 'RELDIR') self.dpdeps = getv(conf, 'DPDEPS') self.line = 0 if not self.conf: # some of the steps below we want to do only once self.conf = conf self.host_target = getv(conf, 'HOST_TARGET') for srctop in getv(conf, 'SRCTOPS', []): if srctop[-1] != '/': srctop += '/' if not srctop in self.srctops: self.srctops.append(srctop) _srctop = os.path.realpath(srctop) if _srctop[-1] != '/': _srctop += '/' if not _srctop in self.srctops: self.srctops.append(_srctop) trim_list = add_trims(self.machine) if self.machine == 'host': trim_list += add_trims(self.host_target) if self.target_spec: trim_list += add_trims(self.target_spec) for objroot in getv(conf, 'OBJROOTS', []): for e in trim_list: if objroot.endswith(e): # this is not what we want - fix it objroot = objroot[0:-len(e)] if objroot[-1] != '/': objroot += '/' if not objroot in self.objroots: self.objroots.append(objroot) _objroot = os.path.realpath(objroot) if objroot[-1] == '/': _objroot += '/' if not _objroot in self.objroots: self.objroots.append(_objroot) # we want the longest match self.srctops.sort(reverse=True) self.objroots.sort(reverse=True) self.excludes = getv(conf, 'EXCLUDES', []) if self.debug: print("host_target=", self.host_target, file=self.debug_out) print("srctops=", self.srctops, file=self.debug_out) print("objroots=", self.objroots, file=self.debug_out) print("excludes=", self.excludes, file=self.debug_out) self.dirdep_re = re.compile(r'([^/]+)/(.+)') if self.dpdeps and not self.reldir: if self.debug: print("need reldir:", end=' ', file=self.debug_out) if self.curdir: srctop = self.find_top(self.curdir, self.srctops) if srctop: self.reldir = self.curdir.replace(srctop,'') if self.debug: print(self.reldir, file=self.debug_out) if not self.reldir: self.dpdeps = None # we cannot do it? self.cwd = os.getcwd() # make sure this is initialized self.last_dir = self.cwd if name: self.try_parse() def reset(self): """reset state if we are being passed meta files from multiple directories.""" self.seen = {} self.obj_deps = [] self.src_deps = [] self.file_deps = [] - + def dirdeps(self, sep='\n'): """return DIRDEPS""" return sep.strip() + sep.join(self.obj_deps) - + def src_dirdeps(self, sep='\n'): """return SRC_DIRDEPS""" return sep.strip() + sep.join(self.src_deps) def file_depends(self, out=None): """Append DPDEPS_${file} += ${RELDIR} for each file we saw, to the output file.""" if not self.reldir: return None for f in sort_unique(self.file_deps): print('DPDEPS_%s += %s' % (f, self.reldir), file=out) # these entries provide for reverse DIRDEPS lookup for f in self.obj_deps: print('DEPDIRS_%s += %s' % (f, self.reldir), file=out) def seenit(self, dir): """rememer that we have seen dir.""" self.seen[dir] = 1 - + def add(self, list, data, clue=''): """add data to list if it isn't already there.""" if data not in list: list.append(data) if self.debug: print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out) def find_top(self, path, list): """the logical tree may be split across multiple trees""" for top in list: if path.startswith(top): if self.debug > 2: print("found in", top, file=self.debug_out) return top return None def find_obj(self, objroot, dir, path, input): """return path within objroot, taking care of .dirdep files""" ddep = None for ddepf in [path + '.dirdep', dir + '/.dirdep']: if not ddep and os.path.exists(ddepf): ddep = open(ddepf, 'r').readline().strip('# \n') if self.debug > 1: print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out) if ddep.endswith(self.machine): ddep = ddep[0:-(1+len(self.machine))] elif self.target_spec and ddep.endswith(self.target_spec): ddep = ddep[0:-(1+len(self.target_spec))] if not ddep: # no .dirdeps, so remember that we've seen the raw input self.seenit(input) self.seenit(dir) if self.machine == 'none': if dir.startswith(objroot): return dir.replace(objroot,'') return None m = self.dirdep_re.match(dir.replace(objroot,'')) if m: ddep = m.group(2) dmachine = m.group(1) if dmachine != self.machine: if not (self.machine == 'host' and dmachine == self.host_target): if self.debug > 2: print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out) ddep += '.' + dmachine return ddep def try_parse(self, name=None, file=None): """give file and line number causing exception""" try: self.parse(name, file) except: # give a useful clue print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr) raise - + def parse(self, name=None, file=None): """A meta file looks like: - + # Meta data file "path" CMD "command-line" CWD "cwd" TARGET "target" -- command output -- -- filemon acquired metadata -- # buildmon version 3 V 3 C "pid" "cwd" E "pid" "path" F "pid" "child" R "pid" "path" W "pid" "path" X "pid" "status" D "pid" "path" L "pid" "src" "target" M "pid" "old" "new" S "pid" "path" # Bye bye We go to some effort to avoid processing a dependency more than once. Of the above record types only C,E,F,L,R,V and W are of interest. """ version = 0 # unknown if name: self.name = name; if file: f = file cwd = self.last_dir = self.cwd else: f = open(self.name, 'r') skip = True pid_cwd = {} pid_last_dir = {} last_pid = 0 self.line = 0 if self.curdir: self.seenit(self.curdir) # we ignore this interesting = 'CEFLRV' for line in f: self.line += 1 # ignore anything we don't care about if not line[0] in interesting: continue if self.debug > 2: print("input:", line, end=' ', file=self.debug_out) w = line.split() if skip: if w[0] == 'V': skip = False version = int(w[1]) """ if version < 4: # we cannot ignore 'W' records # as they may be 'rw' interesting += 'W' """ elif w[0] == 'CWD': self.cwd = cwd = self.last_dir = w[1] self.seenit(cwd) # ignore this if self.debug: print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out) continue pid = int(w[1]) if pid != last_pid: if last_pid: pid_last_dir[last_pid] = self.last_dir cwd = getv(pid_cwd, pid, self.cwd) self.last_dir = getv(pid_last_dir, pid, self.cwd) last_pid = pid # process operations if w[0] == 'F': npid = int(w[2]) pid_cwd[npid] = cwd pid_last_dir[npid] = cwd last_pid = npid continue elif w[0] == 'C': cwd = abspath(w[2], cwd, None, self.debug, self.debug_out) if not cwd: cwd = w[2] if self.debug > 1: print("missing cwd=", cwd, file=self.debug_out) if cwd.endswith('/.'): cwd = cwd[0:-2] self.last_dir = pid_last_dir[pid] = cwd pid_cwd[pid] = cwd if self.debug > 1: print("cwd=", cwd, file=self.debug_out) continue if w[2] in self.seen: if self.debug > 2: print("seen:", w[2], file=self.debug_out) continue # file operations if w[0] in 'ML': # these are special, tread src as read and # target as write self.parse_path(w[2].strip("'"), cwd, 'R', w) self.parse_path(w[3].strip("'"), cwd, 'W', w) continue elif w[0] in 'ERWS': path = w[2] + if path == '.': + continue self.parse_path(path, cwd, w[0], w) if not file: f.close() def is_src(self, base, dir, rdir): """is base in srctop""" for dir in [dir,rdir]: if not dir: continue path = '/'.join([dir,base]) srctop = self.find_top(path, self.srctops) if srctop: if self.dpdeps: self.add(self.file_deps, path.replace(srctop,''), 'file') self.add(self.src_deps, dir.replace(srctop,''), 'src') self.seenit(dir) return True return False def parse_path(self, path, cwd, op=None, w=[]): """look at a path for the op specified""" if not op: op = w[0] # we are never interested in .dirdep files as dependencies if path.endswith('.dirdep'): return for p in self.excludes: if p and path.startswith(p): if self.debug > 2: print("exclude:", p, path, file=self.debug_out) return # we don't want to resolve the last component if it is # a symlink path = resolve(path, cwd, self.last_dir, self.debug, self.debug_out) if not path: return dir,base = os.path.split(path) if dir in self.seen: if self.debug > 2: print("seen:", dir, file=self.debug_out) return # we can have a path in an objdir which is a link # to the src dir, we may need to add dependencies for each rdir = dir dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out) rdir = os.path.realpath(dir) if rdir == dir: rdir = None # now put path back together path = '/'.join([dir,base]) if self.debug > 1: print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out) if op in 'RWS': if path in [self.last_dir, cwd, self.cwd, self.curdir]: if self.debug > 1: print("skipping:", path, file=self.debug_out) return if os.path.isdir(path): if op in 'RW': self.last_dir = path; if self.debug > 1: print("ldir=", self.last_dir, file=self.debug_out) return if op in 'ER': # finally, we get down to it if dir == self.cwd or dir == self.curdir: return if self.is_src(base, dir, rdir): self.seenit(w[2]) if not rdir: return objroot = None for dir in [dir,rdir]: if not dir: continue objroot = self.find_top(dir, self.objroots) if objroot: break if objroot: ddep = self.find_obj(objroot, dir, path, w[2]) if ddep: self.add(self.obj_deps, ddep, 'obj') if self.dpdeps and objroot.endswith('/stage/'): sp = '/'.join(path.replace(objroot,'').split('/')[1:]) self.add(self.file_deps, sp, 'file') else: # don't waste time looking again self.seenit(w[2]) self.seenit(dir) - + def main(argv, klass=MetaFile, xopts='', xoptf=None): """Simple driver for class MetaFile. Usage: script [options] [key=value ...] "meta" ... - + Options and key=value pairs contribute to the dictionary passed to MetaFile. -S "SRCTOP" add "SRCTOP" to the "SRCTOPS" list. -C "CURDIR" - + -O "OBJROOT" add "OBJROOT" to the "OBJROOTS" list. -m "MACHINE" -a "MACHINE_ARCH" -H "HOST_TARGET" -D "DPDEPS" - + -d bumps debug level """ import getopt # import Psyco if we can # it can speed things up quite a bit have_psyco = 0 try: import psyco psyco.full() have_psyco = 1 except: pass conf = { 'SRCTOPS': [], 'OBJROOTS': [], 'EXCLUDES': [], } try: machine = os.environ['MACHINE'] if machine: conf['MACHINE'] = machine machine_arch = os.environ['MACHINE_ARCH'] if machine_arch: conf['MACHINE_ARCH'] = machine_arch srctop = os.environ['SB_SRC'] if srctop: conf['SRCTOPS'].append(srctop) objroot = os.environ['SB_OBJROOT'] if objroot: conf['OBJROOTS'].append(objroot) except: pass debug = 0 output = True - + opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts) for o, a in opts: if o == '-a': conf['MACHINE_ARCH'] = a elif o == '-d': debug += 1 elif o == '-q': output = False elif o == '-H': conf['HOST_TARGET'] = a elif o == '-S': if a not in conf['SRCTOPS']: conf['SRCTOPS'].append(a) elif o == '-C': conf['CURDIR'] = a elif o == '-O': if a not in conf['OBJROOTS']: conf['OBJROOTS'].append(a) elif o == '-R': conf['RELDIR'] = a elif o == '-D': conf['DPDEPS'] = a elif o == '-m': conf['MACHINE'] = a elif o == '-T': conf['TARGET_SPEC'] = a elif o == '-X': if a not in conf['EXCLUDES']: conf['EXCLUDES'].append(a) elif xoptf: xoptf(o, a, conf) conf['debug'] = debug # get any var=val assignments eaten = [] for a in args: if a.find('=') > 0: k,v = a.split('=') if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']: if k == 'SRCTOP': k = 'SRCTOPS' elif k == 'OBJROOT': k = 'OBJROOTS' if v not in conf[k]: conf[k].append(v) else: conf[k] = v eaten.append(a) continue break for a in eaten: args.remove(a) debug_out = getv(conf, 'debug_out', sys.stderr) if debug: print("config:", file=debug_out) print("psyco=", have_psyco, file=debug_out) for k,v in list(conf.items()): print("%s=%s" % (k,v), file=debug_out) m = None for a in args: if a.endswith('.meta'): if not os.path.exists(a): continue m = klass(a, conf) elif a.startswith('@'): # there can actually multiple files per line for line in open(a[1:]): for f in line.strip().split(): if not os.path.exists(f): continue m = klass(f, conf) if output and m: print(m.dirdeps()) print(m.src_dirdeps('\nsrc:')) dpdeps = getv(conf, 'DPDEPS') if dpdeps: m.file_depends(open(dpdeps, 'wb')) return m if __name__ == '__main__': try: main(sys.argv) except: # yes, this goes to stdout print("ERROR: ", sys.exc_info()[1]) raise Index: head/contrib/bmake/mk/meta2deps.sh =================================================================== --- head/contrib/bmake/mk/meta2deps.sh (revision 365365) +++ head/contrib/bmake/mk/meta2deps.sh (revision 365366) @@ -1,403 +1,403 @@ #!/bin/sh # NAME: # meta2deps.sh - extract useful info from .meta files # # SYNOPSIS: # meta2deps.sh SB="SB" "meta" ... -# +# # DESCRIPTION: # This script looks each "meta" file and extracts the # information needed to deduce build and src dependencies. -# +# # To do this, we extract the 'CWD' record as well as all the # syscall traces which describe 'R'ead, 'C'hdir and 'E'xec # syscalls. # # The typical meta file looks like:: #.nf # # # Meta data file "path" # CMD "command-line" # CWD "cwd" # TARGET "target" # -- command output -- # -- filemon acquired metadata -- # # buildmon version 2 # V 2 # E "pid" "path" # R "pid" "path" # C "pid" "cwd" # R "pid" "path" # X "pid" "status" #.fi # # The fact that all the syscall entry lines start with a single # character make these files quite easy to process using sed(1). # # To simplify the logic the 'CWD' line is made to look like a # normal 'C'hdir entry, and "cwd" is remembered so that it can # be prefixed to any "path" which is not absolute. # # If the "path" being read ends in '.srcrel' it is the content # of (actually the first line of) that file that we are # interested in. # # Any "path" which lies outside of the sandbox "SB" is generally # not of interest and is ignored. # # The output, is a set of absolute paths with "SB" like: #.nf # # $SB/obj-i386/bsd/include # $SB/obj-i386/bsd/lib/csu/i386 # $SB/obj-i386/bsd/lib/libc # $SB/src/bsd/include # $SB/src/bsd/sys/i386/include # $SB/src/bsd/sys/sys # $SB/src/pan-release/rtsock # $SB/src/pfe-shared/include/jnx #.fi # # Which can then be further processed by 'gendirdeps.mk' # # If we are passed 'DPDEPS='"dpdeps", then for each src file # outside of "CURDIR" we read, we output a line like: #.nf # # DPDEPS_$path += $RELDIR #.fi # # with "$path" geting turned into reldir's, so that we can end # up with a list of all the directories which depend on each src # file in another directory. This can allow for efficient yet # complete testing of changes. # RCSid: -# $Id: meta2deps.sh,v 1.12 2016/12/13 20:44:16 sjg Exp $ +# $Id: meta2deps.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $ # Copyright (c) 2010-2013, Juniper Networks, Inc. # All rights reserved. -# +# # Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: +# 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. +# 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. -# +# documentation and/or other materials provided with the distribution. +# # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT # OWNER 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. +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. meta2src() { cat /dev/null "$@" | sed -n '/^R .*\.[chyl]$/s,^..[0-9]* ,,p' | sort -u } - + meta2dirs() { cat /dev/null "$@" | sed -n '/^R .*\/.*\.[a-z0-9][^\/]*$/s,^..[0-9]* \(.*\)/[^/]*$,\1,p' | sort -u } add_list() { sep=' ' suffix= while : do case "$1" in "|") sep="$1"; shift;; -s) suffix="$2"; shift 2;; *) break;; esac done name=$1 shift eval list="\$$name" for top in "$@" do case "$sep$list$sep" in *"$sep$top$suffix$sep"*) continue;; esac list="${list:+$list$sep}$top$suffix" done eval "$name=\"$list\"" } _excludes_f() { egrep -v "$EXCLUDES" } meta2deps() { DPDEPS= SRCTOPS=$SRCTOP OBJROOTS= EXCLUDES= while : do case "$1" in *=*) eval export "$1"; shift;; -a) MACHINE_ARCH=$2; shift 2;; -m) MACHINE=$2; shift 2;; -C) CURDIR=$2; shift 2;; -H) HOST_TARGET=$2; shift 2;; -S) add_list SRCTOPS $2; shift 2;; -O) add_list OBJROOTS $2; shift 2;; -X) add_list EXCLUDES '|' $2; shift 2;; -R) RELDIR=$2; shift 2;; -T) TARGET_SPEC=$2; shift 2;; *) break;; esac done _th= _o= case "$MACHINE" in host) _ht=$HOST_TARGET;; esac - + for o in $OBJROOTS do case "$MACHINE,/$o/" in host,*$HOST_TARGET*) ;; *$MACHINE*|*${TARGET_SPEC:-$MACHINE}*) ;; *) add_list _o $o; continue;; esac for x in $_ht $TARGET_SPEC $MACHINE do case "$o" in "") continue;; */$x/) add_list _o ${o%$x/}; o=;; */$x) add_list _o ${o%$x}; o=;; *$x/) add_list _o ${o%$x/}; o=;; *$x) add_list _o ${o%$x}; o=;; esac done done OBJROOTS="$_o" case "$OBJTOP" in "") for o in $OBJROOTS do OBJTOP=$o${TARGET_SPEC:-$MACHINE} break done ;; esac src_re= obj_re= add_list '|' -s '/*' src_re $SRCTOPS add_list '|' -s '*' obj_re $OBJROOTS - + [ -z "$RELDIR" ] && unset DPDEPS tf=/tmp/m2d$$-$USER rm -f $tf.* trap 'rm -f $tf.*; trap 0' 0 > $tf.dirdep > $tf.qual > $tf.srcdep > $tf.srcrel > $tf.dpdeps seenit= seensrc= lpid= case "$EXCLUDES" in "") _excludes=cat;; *) _excludes=_excludes_f;; esac # handle @list files case "$@" in *@[!.]*) for f in "$@" do case "$f" in *.meta) cat $f;; @*) xargs cat < ${f#@};; *) cat $f;; esac done ;; *) cat /dev/null "$@";; esac 2> /dev/null | sed -e 's,^CWD,C C,;/^[CREFLM] /!d' -e "s,',,g" | $_excludes | while read op pid path junk do : op=$op pid=$pid path=$path # we track cwd and ldir (of interest) per pid # CWD is bmake's cwd case "$lpid,$pid" in ,C) CWD=$path cwd=$path ldir=$path if [ -z "$SB" ]; then SB=`echo $CWD | sed 's,/obj.*,,'` fi SRCTOP=${SRCTOP:-$SB/src} continue ;; $pid,$pid) ;; *) case "$lpid" in "") ;; *) eval ldir_$lpid=$ldir;; esac eval ldir=\${ldir_$pid:-$CWD} cwd=\${cwd_$pid:-$CWD} lpid=$pid ;; esac case "$op,$path" in W,*srcrel|*.dirdep) continue;; C,*) case "$path" in /*) cwd=$path;; *) cwd=`cd $cwd/$path 2> /dev/null && /bin/pwd`;; esac # watch out for temp dirs that no longer exist test -d ${cwd:-/dev/null/no/such} || cwd=$CWD eval cwd_$pid=$cwd continue ;; - F,*) # $path is new pid + F,*) # $path is new pid eval cwd_$path=$cwd ldir_$path=$ldir continue - ;; + ;; *) dir=${path%/*} case "$path" in $src_re|$obj_re) ;; /*/stage/*) ;; /*) continue;; *) for path in $ldir/$path $cwd/$path do test -e $path && break done dir=${path%/*} ;; esac ;; esac # avoid repeating ourselves... case "$DPDEPS,$seensrc," in ,*) case ",$seenit," in *,$dir,*) continue;; esac ;; *,$path,*) continue;; esac # canonicalize if needed case "/$dir/" in */../*|*/./*) rdir=$dir dir=`cd $dir 2> /dev/null && /bin/pwd` seen="$rdir,$dir" ;; *) seen=$dir;; esac case "$dir" in ${CURDIR:-.}|"") continue;; $src_re) # avoid repeating ourselves... case "$DPDEPS,$seensrc," in ,*) case ",$seenit," in *,$dir,*) continue;; esac ;; esac ;; *) case ",$seenit," in *,$dir,*) continue;; esac ;; esac if [ -d $path ]; then case "$path" in */..) ldir=${dir%/*};; *) ldir=$path;; esac continue fi [ -f $path ] || continue case "$dir" in $CWD) continue;; # ignore $src_re) seenit="$seenit,$seen" echo $dir >> $tf.srcdep case "$DPDEPS,$reldir,$seensrc," in ,*) ;; *) seensrc="$seensrc,$path" echo "DPDEPS_$dir/${path##*/} += $RELDIR" >> $tf.dpdeps ;; esac continue ;; esac # if there is a .dirdep we cannot skip # just because we've seen the dir before. if [ -s $path.dirdep ]; then # this file contains: # '# ${RELDIR}.' echo $path.dirdep >> $tf.qual continue elif [ -s $dir.dirdep ]; then echo $dir.dirdep >> $tf.qual seenit="$seenit,$seen" continue fi seenit="$seenit,$seen" case "$dir" in $obj_re) echo $dir;; esac done > $tf.dirdep _nl=echo for f in $tf.dirdep $tf.qual $tf.srcdep do [ -s $f ] || continue case $f in *qual) # a list of .dirdep files # we can prefix everything with $OBJTOP to # tell gendirdeps.mk that these are # DIRDEP entries, since they are already # qualified with . as needed. # We strip .$MACHINE though xargs cat < $f | sort -u | sed "s,^# ,,;s,^,$OBJTOP/,;s,\.${TARGET_SPEC:-$MACHINE}\$,,;s,\.$MACHINE\$,," ;; *) sort -u $f;; esac _nl=: done if [ -s $tf.dpdeps ]; then case "$DPDEPS" in */*) ;; *) echo > $DPDEPS;; # the echo is needed! esac sort -u $tf.dpdeps | sed "s,${SRCTOP}/,,;s,${SB_BACKING_SB:-$SB}/src/,," >> $DPDEPS fi # ensure we produce _something_ else egrep -v gets upset $_nl } case /$0 in */meta2dep*) meta2deps "$@";; */meta2dirs*) meta2dirs "$@";; */meta2src*) meta2src "$@";; esac Index: head/contrib/bmake/mk/mk-files.txt =================================================================== --- head/contrib/bmake/mk/mk-files.txt (revision 365365) +++ head/contrib/bmake/mk/mk-files.txt (revision 365366) @@ -1,503 +1,500 @@ mk-files ******** The term ``mk-files`` refers to a collection of ``*.mk`` files. You need bmake_ or a *recent* NetBSD_ make. If in doubt use bmake_. Introduction ============ Many years ago, when building large software projects, I used GNU make (or my own patched version of it), and had developed a set of macros to simplify developing complex build trees. Since the early 90's my main development machines, run BSD -(NetBSD_ to be precise), and the BSD source tree is good example of a -large software project. It quickly became clear that -``/usr/share/mk/*.mk`` were a great model, but were quite tightly -linked to building the BSD tree. +(NetBSD_ to be precise, and more recently FreeBSD), and the BSD source +tree is good example of a large software project. +It quickly became clear that ``/usr/share/mk/*.mk`` were a great +model, but at the time were quite tightly linked to building the BSD tree. Much as I liked using NetBSD, my customers were more likely to be using SunOS, HP-UX etc, so I started on bmake_ and a portable collection of mk-files (mk.tar.gz_). NetBSD provided much of the original structure. Since then I've added a lot of features to NetBSD's make and hence to -bmake which is kept closely in sync. The mk-files however have +bmake which is kept closely in sync. The mk-files however have diverged quite a bit, though ideas are still picked up from NetBSD and FreeBSD. Basics ------ The BSD build model is very simple. A directory produces one component, which is generally either a library or a program. Library makefiles include ``lib.mk`` and programs include ``prog.mk`` and they *do the right thing*. A simple library makefile might look like:: LIB = sig SRCS = \ sigaction.c \ sigcompat.c \ sighdl.c .include a simple program makefile:: PROG = cat SRCS = cat.c .include in such cases even the ``SRCS`` line is unnecessary as ``prog.mk`` will default it to ``${PROG}.c``. It is the sensible use of defaults and the plethora of macro modifiers -provided by bmake_ that allow simple makefiles such as the above +provided by bmake_ that allow simple makefiles such as the above to *just work* on many different systems. mk-files ======== This section provides a brief description of some of the ``*.mk`` -files. +files. sys.mk ------ When bmake starts, it looks for ``sys.mk`` and reads it before doing anything else. Thus, this is the place to setup the environment for everyone else. -In this distribution, sys.mk avoids doing anything platform dependent. +In this distribution, ``sys.mk`` avoids doing anything platform dependent. It is quite short, and includes a number of other files (which may or may not exists) sys.env.mk If it exists, is expected to do things like conditioning the environment. Since it will only be included by the initial instance of bmake, it should ``.export`` anything that sub-makes might need. examples/sys.clean-env.mk An example of how to clean the environment. See the file for all the details:: .if ${MAKE_VERSION} >= 20100606 && ${.MAKE.LEVEL} == 0 # we save any env var that starts with these MAKE_SAVE_ENV_PREFIX += SB MK MAKE MACHINE NEED_ CCACHE DISTCC USE_ SSH MAKE_SAVE_ENV_VARS += \ PATH HOME USER LOGNAME \ SRCTOP OBJTOP OBJROOT \ ${_env_vars} - + _env_vars != env | egrep '^(${MAKE_SAVE_ENV_PREFIX:ts|})' | sed 's,=.*,,'; echo _export_list = .for v in ${MAKE_SAVE_ENV_VARS:O:u} .if !empty($v) _export_list += $v $v := ${$v} .endif .endfor # now clobber the environment .unexport-env # list of vars that we handle specially below _tricky_env_vars = MAKEOBJDIR # export our selection - sans tricky ones .export ${_export_list:${_tricky_env_vars:${M_ListToSkip}}} # this next bit may need tweaking .if defined(MAKEOBJDIR) srctop := ${SRCTOP:U${SB_SRC:U${SB}/src}} objroot := ${OBJROOT:U${SB_OBJROOT:U${SB}/${SB_OBJPREFIX}}} # we'll take care of MACHINE below objtop := ${OBJTOP:U${objroot}${MACHINE}} .if !empty(objtop) # we would normally want something like (/bin/sh): # MAKEOBJDIR="\${.CURDIR:S,${SRCTOP},${OBJROOT}\${MACHINE},}" # the $$ below is how we achieve the same result here. # since everything saved from the environment above # has run through := we need to compensate for ${MACHINE} MAKEOBJDIR = $${.CURDIR:S,${srctop},${objtop:S,${MACHINE},\${MACHINE},},} # export these as-is, and do not track... .export-env ${_tricky_env_vars} # now evaluate for ourselves .for v in ${_tricky_env_vars} $v := ${$v} .endfor .endif .endif .endif host-target.mk Is used to set macros like ``HOST_TARGET``, ``HOST_OS`` and ``host_os`` which are used to find the next step. sys/\*.mk Platform specific additions, such as ``Darwin.mk`` or ``SunOS.mk`` set things like ``HOST_LIBEXT = .dylib`` for Darwin or ``SHLIB_FULLVERSION = ${SHLIB_MAJOR}`` for SunOS 5. If there is no OS specific file, ``sys/Generic.mk`` is used. local.sys.mk Any ``local.*.mk`` file is not part of the distribution. This provides a hook for sites to do extra setup without having to edit the distributed files. The above arrangement makes it easy for the mk files to be part of a src tree on an NFS volume and to allow building on multiple platforms. lib.mk ------ This file is used to build a number of different libraries from the same SRCS. lib${LIB}.a An archive lib of ``.o`` files, this is the default lib${LIB}_p.a - A profiled lib of ``.po`` files. + A profiled lib of ``.po`` files. Still an archive lib, but all the objects are built with profiling in mind - hence the different extension. It is skipped if ``MKPROFILE`` is "no". lib${LIB}_pic.a An archive of ``.so`` objects compiled for relocation. On NetBSD this is the input to ``lib${LIB}.${LD_so}``, it is skipped if ``MKPICLIB`` is "no". lib${LIB}.${LD_so} A shared library. The value of ``LD_so`` is very platform specific. For example:: # SunOS 5 and most other ELF systems libsslfd.so.1 # Darwin libsslfd.1.dylib This library will only be built if ``SHLIB_MAJOR`` has a value, and ``MKPIC`` is not set to "no". There is a lot of platform specific tweaking in ``lib.mk``, largely the result of the original distributions trying to avoid interfering with -the system's ``sys.mk``. +the system's ``sys.mk``. libnames.mk ----------- This is included by both ``prog.mk`` and ``lib.mk`` and tries to include ``*.libnames.mk`` of which: local.libnames.mk does not exist unless you create it. It is a handy way for you - to customize without touching the distributed files. + to customize without touching the distributed files. For example, on a test machine I needed to build openssl but - not install it, so put the following in ``local.libnames.mk``:: + not install it, so put the following in ``local.libnames.mk``:: .if ${host_os} == "sunos" LIBCRYPTO = ${OBJTOP}/openssl/lib/crypto/libcrypto${DLIBEXT} LIBSSL = ${OBJTOP}/openssl/lib/ssl/libssl${DLIBEXT} INCLUDES_libcrypto = -I${OBJ_libcrypto} .endif - + The makefile created an openssl dir in ``${OBJ_libcrypto}`` to gather all the headers. dpadd.mk_ did the rest. -sjg.libnames.mk - not part of the mk-files distribution. - host.libnames.mk contains logic to find any libs named in ``HOST_LIBS`` in ``HOST_LIBDIRS``. Each file above gets an opportunity to define things like:: LIBSSLFD ?= ${OBJTOP}/ssl/lib/sslfd/libsslfd${DLIBEXT} INCLUDES_libsslfd = -I${SRC_libsslfd}/h -I${OBJ_libslfd} these are used by dpadd.mk_ and will be explained below. dpadd.mk -------- This file looks like line noise, and is best considered read-only. However it provides some very useful functionality, which simplifies the build. Makefiles can use the LIB* macros defined via libnames.mk_ or anywhere else in various ways:: # indicate that we need to include headers from LIBCRYPTO # this would result in ${INCLUDES_libcrypto} being added to CFLAGS. SRC_LIBS += ${LIBCRYPTO} # indicate that libsslfd must be built already. # it also has the same effect as SRC_LIBS DPADD += ${LIBSSLFD} - # indicate that not only must libsslfd be built, + # indicate that not only must libsslfd be built, # but that we need to link with it. # this is almost exactly equivalent to # DPADD += ${LIBSSLFD} # LDADD += -L${LIBSSLFD:H} -lsslfd # and mostly serves to ensure that DPADD and LDADD are in sync. DPLIBS += ${LIBSSLFD} Any library (referenced by its full path) in any of the above, is added to ``DPMAGIC_LIBS`` with the following results, for each lib *foo*. SRC_libfoo Is set to indicate where the src for libfoo is. By default it is derived from ``LIBFOO`` by replacing ``${OBJTOP}`` with ``${SRCTOP}``. OBJ_libfoo Not very exciting, is just the dir where libfoo lives. INCLUDES_libfoo What to add to ``CFLAGS`` to find the public headers. The default varies. If ``${SRC_libfoo}/h`` exists, it is assumed to be the home of all public headers and thus the default is ``-I${SRC_libfoo}/h`` Otherwise we make no assumptions and the default is ``-I${SRC_libfoo} -I${OBJ_libfoo}`` LDADD_libfoo This only applies to libs reference via ``DPLIBS``. The default is ``-lfoo``, ``LDADD_*`` provides a hook to instantiate other linker flags at the appropriate point without losing the benfits of ``DPLIBS``. prog.mk ------- Compiles the specified SRCS and links them and the nominated libraries into a program. Prog makefiles usually need to list the libraries that need to be linked. We prefer use of ``DPLIBS`` but the more traditional ``DPADD`` and ``LDADD`` work just as well. That is:: DPLIBS += ${LIBCRYPTO} is equivalent to:: DPADD += ${LIBCRYPTO} LDADD += -lcrypto obj.mk ------ One of the cool aspects of BSD make, is its support for separating object files from the src tree. This is also the source of much confusion to some. Traditionally one had to do a separate ``make obj`` pass through the tree. If ``MKOBJDIRS`` is "auto", we include auto.obj.mk_. auto.obj.mk ----------- This leverages the ``.OBJDIR`` target introduced some years ago to NetBSD make, to automatically create the desired object dir. subdir.mk --------- This is the traditional means of walking the tree. A makefile sets ``SUBDIR`` to the list of sub-dirs to visit. If ``SUBDIR_MUST_EXIST`` is set, missing directories cause an error, otherwise a warning is issued. If you don't even want the warning, set ``MISSING_DIR=continue``. -Traditionally, ``subdir.mk`` prints clue as it visits each subdir:: +Traditionally, ``subdir.mk`` prints clues as it visits each subdir:: ===> ssl ===> ssl/lib ===> ssl/lib/sslfd you can suppress that - or enhance it by setting ``ECHO_DIR``:: # suppress subdir noise ECHO_DIR=: # print time stamps ECHO_DIR=echo @ `date "+%s [%Y-%m-%d %T] "` links.mk -------- Provides rules for processing lists of ``LINKS`` and ``SYMLINKS``. Each is expected to be a list of ``link`` and ``target`` pairs -(``link`` -> ``target``). +(``link`` -> ``target``). The logic is generally in a ``_*_SCRIPT`` which is referenced in a ``_*_USE`` (``.USE``) target. The ``_BUILD_*`` forms are identical, but do not use ``${DESTDIR}`` and so are useful for creating symlinks during the build phase. For example:: SYMLINKS += ${.CURDIR}/${MACHINE_ARCH}/include machine header_links: _BUILD_SYMLINKS_USE - + md.o: header_links would create a symlink called ``machine`` in ``${.OBJDIR}`` pointing to ``${.CURDIR}/${MACHINE_ARCH}/include`` before compiling ``md.o`` autoconf.mk ----------- Deals with running (or generating) GNU autoconf ``configure`` scripts. dep.mk ------ Deals with collecting dependencies. Another useful feature of BSD make is the separation of this sort of information into a ``.depend`` file. ``MKDEP`` needs to point to a suitable tool (like mkdeps.sh_) If ``USE_AUTODEP_MK`` is "yes" includes autodep.mk_ autodep.mk ---------- Leverages the ``-MD`` feature of recent GCC to collect dependency information as a side effect of compilation. With this GCC puts dependency info into a ``.d`` file. Unfortunately GCC bases the name of the ``.d`` file on the name of the input rather than the output file, which causes problems when the same source is compiled different ways. The latest GCC supports ``-MF`` to name the ``.d`` file and ``-MT`` to control the name to put as the dependent. Recent bmake allows dependencies for the ``.END`` target (run at the end if everything was successful), and ``autodep.mk`` uses this to post process the ``.d`` files into ``.depend``. auto.dep.mk ----------- -A much simpler implementation than autodep.mk_ it uses +A much simpler implementation than autodep.mk_ it uses ``-MF ${.TARGET:T}.d`` -to avoid possible conflicts during parallel builds. -This precludes the use of suffix rules to drive ``make depend``, so +to avoid possible conflicts during parallel builds. +This precludes the use of suffix rules to drive ``make depend``, so dep.mk_ handles that if specifically requested. options.mk ---------- Inspired by FreeBSD's ``bsd.own.mk`` more flexible. FreeBSD now have similar functionality in ``bsd.mkopt.mk``. It allows users to express their intent with respect to options ``MK_*`` by setting ``WITH_*`` or ``WITHOUT_*``. Note: ``WITHOUT_*`` wins if both are set, and makefiles can set ``NO_*`` to say they cannot handle that option, or even ``MK_*`` if they really need to. own.mk ------ Normally included by ``init.mk`` (included by ``lib.mk`` and ``prog.mk`` etc), sets macros for default ownership etc. It includes ``${MAKECONF}`` if it is defined and exists. ldorder.mk ---------- Leverages ``bmake`` to compute optimal link order for libraries. This works nicely and makes refactoring a breeze - so long as you have not (or few) cicular dependencies between libraries. man.mk ------ Deals with man pages. warnings.mk ----------- This provides a means of fine grained control over warnings on a per ``${MACHINE}`` or even file basis. A makefile sets ``WARNINGS_SET`` to name a list of warnings and individual ``W_*`` macros can be used to tweak them. For example:: WARNINGS_SET = HIGH W_unused_sparc = -Wno-unused would add all the warnings in ``${HIGH_WARNINGS}`` to CFLAGS, but on sparc, ``-Wno-unused`` would replace ``-Wunused``. You should never need to edit ``warnings.mk``, it will include ``warnings-sets.mk`` if it exists and you use that to make any local -customizations. +customizations. rst2htm.mk ---------- Logic to simplify generating HTML (and PDF) documents from ReStructuredText. cython.mk --------- Logic to build Python C interface modules using Cython_ .. _Cython: http://www.cython.org/ Meta mode ========= The 20110505 and later versions of ``mk-files`` include a number of -makefiles contributed by Juniper Networks, Inc. +makefiles contributed by Juniper Networks, Inc. These allow the latest version of bmake_ to run in `meta mode`_ see `dirdeps.mk`_ .. _`dirdeps.mk`: /help/sjg/dirdeps.htm .. _`meta mode`: bmake-meta-mode.htm Install ======= You can use the content of mk.tar.gz_ without installing at all. The script ``install-mk`` takes care of copying ``*.mk`` into a destination directory, and unless told not to, create ``bsd.*.mk`` links for ``lib.mk`` etc. If you just want to create the ``bsd.*.mk`` links in the directory where you unpacked the tar file, you can:: ./mk/install-mk ./mk ------ .. _bmake: bmake.htm .. _NetBSD: http://www.netbsd.org/ .. _mkdeps.sh: http://www.crufty.net/ftp/pub/sjg/mkdeps.sh .. _mk.tar.gz: http://www.crufty.net/ftp/pub/sjg/mk.tar.gz :Author: sjg@crufty.net -:Revision: $Id: mk-files.txt,v 1.18 2018/12/08 07:27:15 sjg Exp $ +:Revision: $Id: mk-files.txt,v 1.20 2020/08/19 17:51:53 sjg Exp $ :Copyright: Crufty.NET Index: head/contrib/bmake/mk/mkopt.sh =================================================================== --- head/contrib/bmake/mk/mkopt.sh (revision 365365) +++ head/contrib/bmake/mk/mkopt.sh (revision 365366) @@ -1,104 +1,104 @@ #!/bin/sh -# $Id: mkopt.sh,v 1.12 2020/06/23 04:16:35 sjg Exp $ +# $Id: mkopt.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $ # # @(#) 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. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # handle WITH[OUT]_* options in a manner compatible with # options.mk and bsd.mkopt.mk in recent FreeBSD # no need to be included more than once _MKOPT_SH=: _MKOPT_PREFIX=${_MKOPT_PREFIX:-MK_} # # _mk_opt default OPT # # Set MK_$OPT # # The semantics are simple, if MK_$OPT has no value # WITHOUT_$OPT results in MK_$OPT=no # otherwise WITH_$OPT results in MK_$OPT=yes. # Note WITHOUT_$OPT overrides WITH_$OPT. # # For backwards compatability reasons we treat WITH_$OPT=no # the same as WITHOUT_$OPT. # _mk_opt() { _d=$1 _mo=${_MKOPT_PREFIX}$2 _wo=WITHOUT_$2 _wi=WITH_$2 eval "_mov=\$$_mo _wov=\$$_wo _wiv=\$$_wi" case "$_wiv" in [Nn][Oo]) _wov=no;; esac _v=${_mov:-${_wov:+no}} _v=${_v:-${_wiv:+yes}} _v=${_v:-$_d} _opt_list="$_opt_list $_mo" case "$_v" in yes|no) ;; # sane 0|[NnFf]*) _v=no;; # they mean no 1|[YyTt]*) _v=yes;; # they mean yes *) _v=$_d;; # ignore bogus value esac eval "$_mo=$_v" } # # _mk_opts default opt ... [default [opt] ...] # # see _mk_opts_defaults for example # _mk_opts() { _d=no for _o in "$@" do case "$_o" in */*) # option is dirname default comes from basename eval "_d=\$${_MKOPT_PREFIX}${_o#*/}" _o=${_o%/*} ;; yes|no) _d=$_o; continue;; esac _mk_opt $_d $_o done } # handle either options.mk style OPTIONS_DEFAULT_* # or FreeBSD's new bsd.mkopt.mk style __DEFAULT_*_OPTIONS _mk_opts_defaults() { _mk_opts no $OPTIONS_DEFAULT_NO $__DEFAULT_NO_OPTIONS \ yes $OPTIONS_DEFAULT_YES $__DEFAULT_YES_OPTIONS \ $OPTIONS_DEFAULT_DEPENDENT $__DEFAULT_DEPENDENT_OPTIONS } case "/$0" in */mkopt*) _list=no while : do case "$1" in *=*) eval "$1"; shift;; --no|no) _list="$_list no"; shift;; --yes|yes) _list="$_list yes"; shift;; -DWITH*) eval "${1#-D}=1"; shift;; [A-Z]*) _list="$_list $1"; shift;; *) break;; esac done _mk_opts $_list ;; esac Index: head/contrib/bmake/mk/obj.mk =================================================================== --- head/contrib/bmake/mk/obj.mk (revision 365365) +++ head/contrib/bmake/mk/obj.mk (revision 365366) @@ -1,113 +1,113 @@ -# $Id: obj.mk,v 1.15 2012/11/11 22:37:02 sjg Exp $ +# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2010, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !target(__${.PARSEFILE:S,bsd.,,}__) __${.PARSEFILE:S,bsd.,,}__: .include ECHO_TRACE ?= echo .if ${MK_OBJDIRS} == "no" obj: objlink: objwarn: .else # this has to match how make behaves .if defined(MAKEOBJDIRPREFIX) || defined(MAKEOBJDIR) .if defined(MAKEOBJDIRPREFIX) __objdir:= ${MAKEOBJDIRPREFIX}${.CURDIR} .else __objdir:= ${MAKEOBJDIR} .endif .else __objdir= ${__objlink} .endif .if defined(OBJMACHINE) __objlink= obj.${MACHINE} .else __objlink= obj .endif .if ${MK_AUTO_OBJ} == "yes" .-include "auto.obj.mk" .endif .NOPATH: ${__objdir} .PHONY: obj obj: _SUBDIRUSE @if test ! -d ${__objdir}/.; then \ mkdir -p ${__objdir}; \ if test ! -d ${__objdir}; then \ mkdir ${__objdir}; exit 1; \ fi; \ ${ECHO_TRACE} "[Creating objdir ${__objdir}...]" >&2; \ fi .if !target(_SUBDIRUSE) # this just allows us to be included by ourselves _SUBDIRUSE: .endif # so we can interact with freebsd makefiles .if !target(objwarn) objwarn: .if ${.OBJDIR} == ${.CURDIR} @echo "Warning Object directory is ${.CURDIR}" .elif ${.OBJDIR} != ${__objdir} @echo "Warning Object directory is ${.OBJDIR} vs. ${__objdir}" .endif .endif .if !target(objlink) objlink: .if ${__objdir:T} != ${__objlink} @if test -d ${__objdir}/.; then \ ${RM} -f ${.CURDIR}/${__objlink}; \ ${LN} -s ${__objdir} ${.CURDIR}/${__objlink}; \ echo "${__objlink} -> ${__objdir}"; \ else \ echo "No ${__objdir} to link to - do a 'make obj'"; \ fi .endif .endif .endif _CURDIR?= ${.CURDIR} _OBJDIR?= ${.OBJDIR} .if !target(print-objdir) print-objdir: @echo ${_OBJDIR} .endif .if !target(whereobj) whereobj: @echo ${_OBJDIR} .endif .if !target(destroy) .if ${.CURDIR} != ${.OBJDIR} destroy: (cd ${_CURDIR} && rm -rf ${_OBJDIR}) .else destroy: clean .endif .endif .endif Index: head/contrib/bmake/mk/options.mk =================================================================== --- head/contrib/bmake/mk/options.mk (revision 365365) +++ head/contrib/bmake/mk/options.mk (revision 365366) @@ -1,80 +1,82 @@ -# $Id: options.mk,v 1.11 2020/05/02 21:23:52 sjg Exp $ +# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # Inspired by FreeBSD bsd.own.mk, but intentionally simpler and more flexible. # Options are normally listed in either OPTIONS_DEFAULT_{YES,NO} # We convert these to ${OPTION}/{yes,no} in OPTIONS_DEFAULT_VALUES. # We add the OPTIONS_DEFAULT_NO first so they take precedence. # This allows override of an OPTIONS_DEFAULT_YES by adding it to # OPTIONS_DEFAULT_NO or adding ${OPTION}/no to OPTIONS_DEFAULT_VALUES. # An OPTIONS_DEFAULT_NO option can only be overridden by putting # ${OPTION}/yes in OPTIONS_DEFAULT_VALUES. # A makefile may set NO_* (or NO*) to indicate it cannot do something. # User sets WITH_* and WITHOUT_* to indicate what they want. # We set ${OPTION_PREFIX:UMK_}* which is then all we need care about. OPTIONS_DEFAULT_VALUES += \ ${OPTIONS_DEFAULT_NO:O:u:S,$,/no,} \ ${OPTIONS_DEFAULT_YES:O:u:S,$,/yes,} OPTION_PREFIX ?= MK_ # NO_* takes precedence # If both WITH_* and WITHOUT_* are defined, WITHOUT_ wins unless # DOMINANT_* is set to "yes" # Otherwise WITH_* and WITHOUT_* override the default. .for o in ${OPTIONS_DEFAULT_VALUES:M*/*} .if defined(NO_${o:H}) || defined(NO${o:H}) # we cannot do it ${OPTION_PREFIX}${o:H} ?= no .elif defined(WITH_${o:H}) && defined(WITHOUT_${o:H}) # normally WITHOUT_ wins DOMINANT_${o:H} ?= no ${OPTION_PREFIX}${o:H} ?= ${DOMINANT_${o:H}} .elif ${o:T:tl} == "no" .if defined(WITH_${o:H}) ${OPTION_PREFIX}${o:H} ?= yes .else ${OPTION_PREFIX}${o:H} ?= no .endif .else .if defined(WITHOUT_${o:H}) ${OPTION_PREFIX}${o:H} ?= no .else ${OPTION_PREFIX}${o:H} ?= yes .endif .endif .endfor # OPTIONS_DEFAULT_DEPENDENT += FOO_UTILS/FOO # If neither WITH[OUT]_FOO_UTILS is set, (see rules above) # use the value of ${OPTION_PREFIX}FOO .for o in ${OPTIONS_DEFAULT_DEPENDENT:M*/*:O:u} .if defined(NO_${o:H}) || defined(NO${o:H}) # we cannot do it ${OPTION_PREFIX}${o:H} ?= no .elif defined(WITH_${o:H}) && defined(WITHOUT_${o:H}) # normally WITHOUT_ wins DOMINANT_${o:H} ?= no ${OPTION_PREFIX}${o:H} ?= ${DOMINANT_${o:H}} .elif defined(WITH_${o:H}) ${OPTION_PREFIX}${o:H} ?= yes .elif defined(WITHOUT_${o:H}) ${OPTION_PREFIX}${o:H} ?= no .else ${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}} .endif .endfor -.undef OPTIONS_DEFAULT_VALUES OPTIONS_DEFAULT_NO OPTIONS_DEFAULT_YES +.undef OPTIONS_DEFAULT_VALUES +.undef OPTIONS_DEFAULT_NO +.undef OPTIONS_DEFAULT_YES Index: head/contrib/bmake/mk/own.mk =================================================================== --- head/contrib/bmake/mk/own.mk (revision 365365) +++ head/contrib/bmake/mk/own.mk (revision 365366) @@ -1,271 +1,271 @@ -# $Id: own.mk,v 1.40 2018/04/23 04:53:57 sjg Exp $ +# $Id: own.mk,v 1.41 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .if !target(__init.mk__) .include "init.mk" .endif .if !defined(NOMAKECONF) && !defined(NO_MAKECONF) MAKECONF?= /etc/mk.conf .-include "${MAKECONF}" .endif .include TARGET_OSNAME?= ${_HOST_OSNAME} TARGET_OSREL?= ${_HOST_OSREL} TARGET_OSTYPE?= ${HOST_OSTYPE} TARGET_HOST?= ${HOST_TARGET} # these may or may not exist .-include <${TARGET_HOST}.mk> .-include RM?= rm LN?= ln INSTALL?= install prefix?= /usr .if exists(${prefix}/lib) libprefix?= ${prefix} .else libprefix?= /usr .endif # FreeBSD at least does not set this MACHINE_ARCH?= ${MACHINE} # we need to make sure these are defined too in case sys.mk fails to. COMPILE.s?= ${CC} ${AFLAGS} -c LINK.s?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp LINK.S?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.c?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXXFLAGS?= ${CFLAGS} COMPILE.cc?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.m?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.f?= ${FC} ${FFLAGS} -c LINK.f?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX.l?= ${LEX} ${LFLAGS} COMPILE.p?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} YACC.y?= ${YACC} ${YFLAGS} # for suffix rules IMPFLAGS?= ${COPTS.${.IMPSRC:T}} ${CPUFLAGS.${.IMPSRC:T}} ${CPPFLAGS.${.IMPSRC:T}} -.for s in .c .cc +.for s in .c .cc COMPILE.$s += ${IMPFLAGS} LINK.$s += ${IMPFLAGS} .endfor PRINT.VAR.MAKE = MAKESYSPATH=${MAKESYSPATH:U${.PARSEDIR}} ${.MAKE} .if empty(.MAKEFLAGS:M-V*) .if defined(MAKEOBJDIRPREFIX) || defined(MAKEOBJDIR) PRINTOBJDIR= ${PRINT.VAR.MAKE} -r -V .OBJDIR -f /dev/null xxx .else PRINTOBJDIR= ${PRINT.VAR.MAKE} -V .OBJDIR .endif .else PRINTOBJDIR= echo # prevent infinite recursion .endif # we really like to have SRCTOP and OBJTOP defined... .if !defined(SRCTOP) || !defined(OBJTOP) .-include .endif .if !defined(SRCTOP) || !defined(OBJTOP) # dpadd.mk is rather pointless without these OPTIONS_DEFAULT_NO+= DPADD_MK .endif # process options OPTIONS_DEFAULT_NO+= \ INSTALL_AS_USER \ GPROF \ PROG_LDORDER_MK \ LIBTOOL \ LINT \ OPTIONS_DEFAULT_YES+= \ ARCHIVE \ AUTODEP \ CRYPTO \ DOC \ DPADD_MK \ GDB \ KERBEROS \ LINKLIB \ MAN \ NLS \ OBJ \ PIC \ SHARE \ SKEY \ YP \ OPTIONS_DEFAULT_DEPENDENT+= \ CATPAGES/MAN \ LDORDER_MK/PROG_LDORDER_MK \ OBJDIRS/OBJ \ PICINSTALL/LINKLIB \ PICLIB/PIC \ PROFILE/LINKLIB \ STAGING_PROG/STAGING \ .include .if ${MK_INSTALL_AS_USER} == "yes" # We ignore this if user is root. _uid!= id -u .if ${_uid} != 0 .if !defined(USERGRP) USERGRP!= id -g .export USERGRP .endif .for x in BIN CONF DOC INC INFO FILES KMOD LIB MAN NLS PROG SHARE $xOWN= ${USER} $xGRP= ${USERGRP} $x_INSTALL_OWN= .endfor .endif .endif # override this in sys.mk ROOT_GROUP?= wheel BINGRP?= ${ROOT_GROUP} BINOWN?= root BINMODE?= 555 NONBINMODE?= 444 DIRMODE?= 755 INCLUDEDIR?= ${prefix}/include INCDIR?= ${INCLUDEDIR} # Define MANZ to have the man pages compressed (gzip) #MANZ= 1 MANTARGET?= cat MANDIR?= ${prefix}/share/man/${MANTARGET} MANGRP?= ${BINGRP} MANOWN?= ${BINOWN} MANMODE?= ${NONBINMODE} INCLUDEDIR?= ${libprefix}/include LIBDIR?= ${libprefix}/lib SHLIBDIR?= ${libprefix}/lib .if ${USE_SHLIBDIR:Uno} == "yes" _LIBSODIR?= ${SHLIBDIR} .else _LIBSODIR?= ${LIBDIR} .endif # this is where ld.*so lives SHLINKDIR?= /usr/libexec LINTLIBDIR?= ${libprefix}/libdata/lint LIBGRP?= ${BINGRP} LIBOWN?= ${BINOWN} LIBMODE?= ${NONBINMODE} DOCDIR?= ${prefix}/share/doc DOCGRP?= ${BINGRP} DOCOWN?= ${BINOWN} DOCMODE?= ${NONBINMODE} NLSDIR?= ${prefix}/share/nls NLSGRP?= ${BINGRP} NLSOWN?= ${BINOWN} NLSMODE?= ${NONBINMODE} KMODDIR?= ${prefix}/lkm KMODGRP?= ${BINGRP} KMODOWN?= ${BINOWN} KMODMODE?= ${NONBINMODE} SHAREGRP?= ${BINGRP} SHAREOWN?= ${BINOWN} SHAREMODE?= ${NONBINMODE} COPY?= -c STRIP_FLAG?= -s .if ${TARGET_OSNAME} == "NetBSD" .if exists(/usr/libexec/ld.elf_so) OBJECT_FMT=ELF .endif OBJECT_FMT?=a.out .endif # sys.mk should set something appropriate if need be. OBJECT_FMT?=ELF .if (${_HOST_OSNAME} == "FreeBSD") CFLAGS+= ${CPPFLAGS} .endif # allow for per target flags # apply the :T:R first, so the more specific :T can override if needed -CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}} -CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}} +CPPFLAGS += ${CPPFLAGS_${.TARGET:T:R}} ${CPPFLAGS_${.TARGET:T}} +CFLAGS += ${CFLAGS_${.TARGET:T:R}} ${CFLAGS_${.TARGET:T}} # Define SYS_INCLUDE to indicate whether you want symbolic links to the system # source (``symlinks''), or a separate copy (``copies''); (latter useful # in environments where it's not possible to keep /sys publicly readable) #SYS_INCLUDE= symlinks # don't try to generate PIC versions of libraries on machines # which don't support PIC. .if (${MACHINE_ARCH} == "vax") || \ ((${MACHINE_ARCH} == "mips") && defined(STATIC_TOOLCHAIN)) || \ ((${MACHINE_ARCH} == "alpha") && defined(ECOFF_TOOLCHAIN)) MK_PIC=no .endif # No lint, for now. NOLINT= .if ${MK_LINKLIB} == "no" MK_PICINSTALL= no MK_PROFILE= no .endif .if ${MK_MAN} == "no" MK_CATPAGES= no .endif .if ${MK_OBJ} == "no" MK_OBJDIRS= no MK_AUTO_OBJ= no .endif .if ${MK_SHARE} == "no" MK_CATPAGES= no MK_DOC= no MK_INFO= no MK_MAN= no MK_NLS= no .endif # :U incase not using our sys.mk .if ${MK_META_MODE:Uno} == "yes" # should all be set by sys.mk if not default TARGET_SPEC_VARS ?= MACHINE .if ${TARGET_SPEC_VARS:[#]} > 1 TARGET_SPEC_VARS_REV := ${TARGET_SPEC_VARS:[-1..1]} .else TARGET_SPEC_VARS_REV = ${TARGET_SPEC_VARS} .endif .if ${MK_STAGING} == "yes" STAGE_ROOT?= ${OBJROOT}/stage STAGE_OBJTOP?= ${STAGE_ROOT}/${TARGET_SPEC_VARS_REV:ts/} .endif .endif .endif Index: head/contrib/bmake/mk/prlist.mk =================================================================== --- head/contrib/bmake/mk/prlist.mk (revision 365365) +++ head/contrib/bmake/mk/prlist.mk (revision 365366) @@ -1,36 +1,36 @@ -# $Id: prlist.mk,v 1.3 2008/07/17 16:24:57 sjg Exp $ +# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: # this needs to be included after all the lists it will process # are defined - which is why it is a separate file. # Usage looks like: # MAKEFLAGS= ${.MAKE} -f ${MAKEFILE} prlist.SOMETHING_HUGE | xargs whatever # .if make(prlist.*) .for t in ${.TARGETS:Mprlist.*:E} .if empty($t) prlist.$t: .else prlist.$t: ${$t:O:u:S,^,prlist-,} ${$t:O:u:S,^,prlist-,}: .PHONY @echo "${.TARGET:S,prlist-,,}" .endif .endfor .endif .endif Index: head/contrib/bmake/mk/prog.mk =================================================================== --- head/contrib/bmake/mk/prog.mk (revision 365365) +++ head/contrib/bmake/mk/prog.mk (revision 365366) @@ -1,251 +1,251 @@ -# $Id: prog.mk,v 1.35 2018/01/26 20:04:07 sjg Exp $ +# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $ .if !target(__${.PARSEFILE}__) __${.PARSEFILE}__: .include # FreeBSD at least expects MAN8 etc. .if defined(MAN) && !empty(MAN) _sect:=${MAN:E} MAN${_sect}=${MAN} .endif .SUFFIXES: .out .o .c .cc .C .y .l .s .8 .7 .6 .5 .4 .3 .2 .1 .0 CFLAGS+= ${COPTS} .if ${TARGET_OSNAME} == "NetBSD" .if ${MACHINE_ARCH} == "sparc64" CFLAGS+= -mcmodel=medlow .endif # ELF platforms depend on crtbegin.o and crtend.o .if ${OBJECT_FMT} == "ELF" .ifndef LIBCRTBEGIN LIBCRTBEGIN= ${DESTDIR}/usr/lib/crtbegin.o .MADE: ${LIBCRTBEGIN} .endif .ifndef LIBCRTEND LIBCRTEND= ${DESTDIR}/usr/lib/crtend.o .MADE: ${LIBCRTEND} .endif _SHLINKER= ${SHLINKDIR}/ld.elf_so .else LIBCRTBEGIN?= LIBCRTEND?= _SHLINKER= ${SHLINKDIR}/ld.so .endif .ifndef LIBCRT0 LIBCRT0= ${DESTDIR}/usr/lib/crt0.o .MADE: ${LIBCRT0} .endif .endif # NetBSD # here is where you can define what LIB* are .-include .if ${MK_DPADD_MK} == "yes" # lots of cool magic, but might not suit everyone. .include .endif .if ${MK_GPROF} == "yes" CFLAGS+= ${CC_PG} ${PROFFLAGS} LDADD+= ${CC_PG} .if ${MK_DPADD_MK} == "no" LDADD_LIBC_P?= -lc_p LDADD_LAST+= ${LDADD_LIBC_P} .endif .endif .if defined(SHAREDSTRINGS) CLEANFILES+=strings .c.o: ${CC} -E ${CFLAGS} ${.IMPSRC} | xstr -c - @${CC} ${CFLAGS} -c x.c -o ${.TARGET} @rm -f x.c ${CXX_SUFFIXES:%=%.o}: ${CXX} -E ${CXXFLAGS} ${.IMPSRC} | xstr -c - @mv -f x.c x.cc @${CXX} ${CXXFLAGS} -c x.cc -o ${.TARGET} @rm -f x.cc .endif .if defined(PROG) BINDIR ?= ${prefix}/bin SRCS?= ${PROG}.c .for s in ${SRCS:N*.h:N*.sh:M*/*} ${.o .po .lo:L:@o@${s:T:R}$o@}: $s .endfor .if !empty(SRCS:N*.h:N*.sh) OBJS+= ${SRCS:T:N*.h:N*.sh:R:S/$/.o/g} LOBJS+= ${LSRCS:.c=.ln} ${SRCS:M*.c:.c=.ln} .endif .if defined(OBJS) && !empty(OBJS) .NOPATH: ${OBJS} ${PROG} ${SRCS:M*.[ly]:C/\..$/.c/} ${YHEADER:D${SRCS:M*.y:.y=.h}} # this is known to work for NetBSD 1.6 and FreeBSD 4.2 .if ${TARGET_OSNAME} == "NetBSD" || ${TARGET_OSNAME} == "FreeBSD" _PROGLDOPTS= .if ${SHLINKDIR} != "/usr/libexec" # XXX: change or remove if ld.so moves _PROGLDOPTS+= -Wl,-dynamic-linker=${_SHLINKER} .endif .if defined(LIBDIR) && ${SHLIBDIR} != ${LIBDIR} _PROGLDOPTS+= -Wl,-rpath-link,${DESTDIR}${SHLIBDIR}:${DESTDIR}/usr/lib \ -L${DESTDIR}${SHLIBDIR} .endif -_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib +_PROGLDOPTS+= -Wl,-rpath,${SHLIBDIR}:/usr/lib .if defined(PROG_CXX) _CCLINK= ${CXX} _SUPCXX= -lstdc++ -lm .endif .endif # NetBSD _CCLINK?= ${CC} .if ${MK_PROG_LDORDER_MK} != "no" ${PROG}: ldorder .include .endif .if defined(DESTDIR) && exists(${LIBCRT0}) && ${LIBCRT0} != "/dev/null" ${PROG}: ${LIBCRT0} ${OBJS} ${LIBC} ${DPADD} ${_CCLINK} ${LDFLAGS} ${LDSTATIC} -o ${.TARGET} -nostdlib ${_PROGLDOPTS} -L${DESTDIR}/usr/lib ${LIBCRT0} ${LIBCRTBEGIN} ${OBJS} ${LDADD_LDORDER} ${LDADD} -L${DESTDIR}/usr/lib ${_SUPCXX} -lgcc -lc -lgcc ${LIBCRTEND} .else ${PROG}: ${LIBCRT0} ${OBJS} ${LIBC} ${DPADD} ${_CCLINK} ${LDFLAGS} ${LDSTATIC} -o ${.TARGET} ${_PROGLDOPTS} ${OBJS} ${LDADD_LDORDER} ${LDADD} .endif # defined(DESTDIR) .endif # defined(OBJS) && !empty(OBJS) .if !defined(MAN) MAN= ${PROG}.1 .endif # !defined(MAN) .endif # defined(PROG) .if !defined(_SKIP_BUILD) realbuild: ${PROG} .endif all: _SUBDIRUSE .if !target(clean) cleanprog: rm -f a.out [Ee]rrs mklog core *.core \ ${PROG} ${OBJS} ${LOBJS} ${CLEANFILES} clean: _SUBDIRUSE cleanprog cleandir: _SUBDIRUSE cleanprog .else cleandir: _SUBDIRUSE clean .endif .if defined(SRCS) && (!defined(MKDEP) || ${MKDEP} != autodep) afterdepend: .depend @(TMP=/tmp/_depend$$$$; \ sed -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.ln:/' \ < .depend > $$TMP; \ mv $$TMP .depend) .endif .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif .if !empty(BINOWN) PROG_INSTALL_OWN ?= -o ${BINOWN} -g ${BINGRP} .endif .if !target(realinstall) realinstall: proginstall .endif .if !target(proginstall) proginstall: .if defined(PROG) [ -d ${DESTDIR}${BINDIR} ] || \ ${INSTALL} -d ${PROG_INSTALL_OWN} -m 775 ${DESTDIR}${BINDIR} ${INSTALL} ${COPY} ${STRIP_FLAG} ${PROG_INSTALL_OWN} -m ${BINMODE} \ ${PROG} ${DESTDIR}${BINDIR}/${PROG_NAME} .endif .if defined(HIDEGAME) (cd ${DESTDIR}/usr/games; rm -f ${PROG}; ln -s dm ${PROG}) .endif .endif .include install: maninstall install_links _SUBDIRUSE install_links: .if !empty(SYMLINKS) @set ${SYMLINKS}; ${_SYMLINKS_SCRIPT} .endif .if !empty(LINKS) @set ${LINKS}; ${_LINKS_SCRIPT} .endif maninstall: afterinstall afterinstall: realinstall install_links: realinstall proginstall: beforeinstall realinstall: beforeinstall .endif .if !target(lint) lint: ${LOBJS} .if defined(LOBJS) && !empty(LOBJS) @${LINT} ${LINTFLAGS} ${LDFLAGS:M-L*} ${LOBJS} ${LDADD} .endif .endif .NOPATH: ${PROG} .if defined(OBJS) && !empty(OBJS) .NOPATH: ${OBJS} .endif .if defined(FILES) || defined(FILESGROUPS) .include .endif .if ${MK_MAN} != "no" .include .endif .if ${MK_NLS} != "no" .include .endif .include .include .include .if !empty(PROG) && ${MK_STAGING_PROG} == "yes" STAGE_BINDIR ?= ${STAGE_OBJTOP}${BINDIR} STAGE_DIR.prog ?= ${STAGE_BINDIR} .if ${PROG_NAME:U${PROG}} != ${PROG} STAGE_AS_SETS += prog STAGE_AS_${PROG} = ${PROG_NAME} stage_as.prog: ${PROG} .else STAGE_SETS += prog stage_files.prog: ${PROG} .endif .endif .include .endif Index: head/contrib/bmake/mk/progs.mk =================================================================== --- head/contrib/bmake/mk/progs.mk (revision 365365) +++ head/contrib/bmake/mk/progs.mk (revision 365366) @@ -1,102 +1,107 @@ -# $Id: progs.mk,v 1.13 2013/08/02 18:28:48 sjg Exp $ +# $Id: progs.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .MAIN: all .if defined(PROGS) # In meta mode, we can capture dependenices for _one_ of the progs. # if makefile doesn't nominate one, we use the first. .ifndef UPDATE_DEPENDFILE_PROG UPDATE_DEPENDFILE_PROG = ${PROGS:[1]} .export UPDATE_DEPENDFILE_PROG .endif .ifndef PROG # They may have asked us to build just one .for t in ${PROGS} .if make($t) PROG ?= $t .endif .endfor .endif .if defined(PROG) # just one of many PROG_VARS += \ BINDIR \ CFLAGS \ COPTS \ CPPFLAGS \ CXXFLAGS \ DPADD \ DPLIBS \ LDADD \ LDFLAGS \ MAN \ SRCS .for v in ${PROG_VARS:O:u} .if defined(${v}.${PROG}) || defined(${v}_${PROG}) $v += ${${v}_${PROG}:U${${v}.${PROG}}} .endif .endfor # for meta mode, there can be only one! .if ${PROG} == ${UPDATE_DEPENDFILE_PROG:Uno} UPDATE_DEPENDFILE ?= yes .endif UPDATE_DEPENDFILE ?= NO # ensure that we don't clobber each other's dependencies DEPENDFILE?= .depend.${PROG} # prog.mk will do the rest .else all: ${PROGS} # We cannot capture dependencies for meta mode here UPDATE_DEPENDFILE = NO # nor can we safely run in parallel. .NOTPARALLEL: .endif .endif # handle being called [bsd.]progs.mk .include <${.PARSEFILE:S,progs,prog,}> .ifndef PROG # tell progs.mk we might want to install things PROGS_TARGETS+= cleandepend cleandir cleanobj depend install .for p in ${PROGS} .if defined(PROGS_CXX) && !empty(PROGS_CXX:M$p) # bsd.prog.mk may need to know this x.$p= PROG_CXX=$p .endif $p ${p}_p: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} -DWITHOUT_META_STATS) .for t in ${PROGS_TARGETS:O:u} $p.$t: .PHONY .MAKE - (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E}) + (cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} PROG=$p ${x.$p} ${@:E} -DWITHOUT_META_STATS) .endfor .endfor .for t in ${PROGS_TARGETS:O:u} $t: ${PROGS:%=%.$t} .endfor + +.if !defined(WITHOUT_META_STATS) && ${.MAKE.LEVEL} > 0 +.END: _reldir_finish +.ERROR: _reldir_failed +.endif .endif Index: head/contrib/bmake/mk/rst2htm.mk =================================================================== --- head/contrib/bmake/mk/rst2htm.mk (revision 365365) +++ head/contrib/bmake/mk/rst2htm.mk (revision 365366) @@ -1,53 +1,53 @@ -# $Id: rst2htm.mk,v 1.10 2015/09/08 22:17:46 sjg Exp $ +# $Id: rst2htm.mk,v 1.11 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # convert reStructuredText to HTML, using rst2html.py from # docutils - http://docutils.sourceforge.net/ .if empty(TXTSRCS) TXTSRCS != 'ls' -1t ${.CURDIR}/*.txt ${.CURDIR}/*.rst 2>/dev/null; echo .endif RSTSRCS ?= ${TXTSRCS} HTMFILES ?= ${RSTSRCS:R:T:O:u:%=%.htm} RST2HTML ?= rst2html.py RST2PDF ?= rst2pdf RST2S5 ?= rst2s5.py # the following will run RST2S5 if the target name contains the word 'slides' # otherwise it uses RST2HTML RST2HTM = ${"${.TARGET:T:M*slides*}":?${RST2S5}:${RST2HTML}} RST2HTM_SLIDES_FLAGS ?= ${RST2S5_FLAGS} RST2HTM_DOC_FLAGS ?= ${RST2HTML_FLAGS} RST2HTM_FLAGS ?= ${"${.TARGET:T:M*slides*}":?${RST2HTM_SLIDES_FLAGS}:${RST2HTM_DOC_FLAGS}} RST2PDF_FLAGS ?= ${"${.TARGET:T:M*slides*}":?${RST2PDF_SLIDES_FLAGS}:${RST2PDF_DOC_FLAGS}} RST_SUFFIXES ?= .rst .txt CLEANFILES += ${HTMFILES} html: ${HTMFILES} .SUFFIXES: ${RST_SUFFIXES} .htm .pdf ${RST_SUFFIXES:@s@$s.htm@}: ${RST2HTM} ${RST2HTM_FLAGS} ${FLAGS.${.TARGET}} ${.IMPSRC} ${.TARGET} ${RST_SUFFIXES:@s@$s.pdf@}: ${RST2PDF} ${RST2PDF_FLAGS} ${FLAGS.${.TARGET}} ${.IMPSRC} ${.TARGET} .for s in ${RSTSRCS:O:u} ${s:R:T}.htm: $s ${s:R:T}.pdf: $s .endfor Index: head/contrib/bmake/mk/scripts.mk =================================================================== --- head/contrib/bmake/mk/scripts.mk (revision 365365) +++ head/contrib/bmake/mk/scripts.mk (revision 365366) @@ -1,91 +1,91 @@ -# $Id: scripts.mk,v 1.3 2017/05/06 17:29:45 sjg Exp $ +# $Id: scripts.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2006, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .include SCRIPTSGROUPS ?= SCRIPTS SCRIPTSGROUPS := ${SCRIPTSGROUPS:O:u} SCRIPTSDIR?= ${BINDIR} SCRIPTSOWN?= ${BINOWN} SCRIPTSGRP?= ${BINGRP} SCRIPTSMODE?= ${BINMODE} SCRIPTS_INSTALL_OWN?= -o ${SCRIPTSOWN} -g ${SCRIPTSGRP} SCRIPTS_COPY ?= -C # how we get script name from src SCRIPTSNAME_MOD?=T:R .if !target(buildfiles) .for group in ${SCRIPTSGROUPS} buildfiles: ${${group}} .endfor .endif buildfiles: realbuild: buildfiles .for group in ${SCRIPTSGROUPS} .if !empty(${group}) && defined(${group}DIR) .if ${group} != "SCRIPTS" ${group}_INSTALL_OWN ?= ${SCRIPTS_INSTALL_OWN} .endif # incase we are staging STAGE_DIR.${group} ?= ${STAGE_OBJTOP}${${group}DIR} .for script in ${${group}:O:u} ${group}_INSTALL_OWN.${script:T} ?= ${${group}_INSTALL_OWN} ${group}DIR.${script:T} ?= ${${group}DIR_${script:T}:U${${group}DIR}} script_mkdir_list += ${${group}DIR.${script:T}} ${group}NAME.${script} ?= ${${group}NAME_${script:T}:U${script:${SCRIPTSNAME_MOD}}} .if ${${group}NAME.${script}:T} != ${script:T} STAGE_AS_SETS += ${group} STAGE_AS_${script} = ${${group}NAME.${script:T}} stage_as.${group}: ${script} installscripts: installscripts.${group}.${script:T} installscripts.${group}.${script:T}: ${script} script_mkdirs ${INSTALL} ${SCRIPTS_COPY} ${${group}_INSTALL_OWN.${script:T}} \ -m ${SCRIPTSMODE} ${.ALLSRC:Nscript_mkdirs} ${DESTDIR}${${group}DIR}/${${group}NAME.${script:T}} .else STAGE_SETS += ${group} stage_files.${group}: ${script} installscripts.${group}: ${script} installscripts: installscripts.${group} .endif .endfor # script installscripts.${group}: script_mkdirs ${INSTALL} ${SCRIPTS_COPY} ${${group}_INSTALL_OWN} -m ${SCRIPTSMODE} \ ${.ALLSRC:Nscript_mkdirs:O:u} ${DESTDIR}${${group}DIR} .endif # !empty .endfor # group script_mkdirs: @for d in ${script_mkdir_list:O:u}; do \ test -d ${DESTDIR}$$d || \ ${INSTALL} -d ${SCRIPTS_INSTALL_OWN} -m 775 ${DESTDIR}$$d; \ done beforeinstall: installscripts: realinstall: installscripts .ORDER: beforeinstall installscripts Index: head/contrib/bmake/mk/srctop.mk =================================================================== --- head/contrib/bmake/mk/srctop.mk (revision 365365) +++ head/contrib/bmake/mk/srctop.mk (revision 365366) @@ -1,66 +1,66 @@ -# $Id: srctop.mk,v 1.3 2012/11/11 23:20:18 sjg Exp $ +# $Id: srctop.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if !defined(SRCTOP) # if using mk(1) SB will be set. .ifdef SB .if ${.CURDIR:S,${SB},,} != ${.CURDIR} # we are actually within SB .ifdef SB_SRC SRCTOP:= ${SB_SRC} .elif exists(${SB}/src) SRCTOP:= ${SB}/src .else SRCTOP:= ${SB} .endif .endif .endif .if !defined(SRCTOP) .for rd in share/mk build/mk mk .if ${_this_mk_dir:M*${rd}} != "" .if ${.CURDIR:S,${_this_mk_dir:${rd:C,[^/]+,H,g:S,/, ,g:ts:}},,} != ${.CURDIR} SRCTOP:= ${_this_mk_dir:${rd:C,[^/]+,H,g:S,/, ,g:ts:}} .endif .endif .endfor .endif .if !defined(SRCTOP) _SRCTOP_TEST_?= [ -f ../.sandbox-env -o -d share/mk ] # Linux at least has a bug where attempting to check an automounter # directory will hang. So avoid looking above /a/b SRCTOP!= cd ${.CURDIR}; while :; do \ here=`pwd`; \ ${_SRCTOP_TEST_} && { echo $$here; break; }; \ case $$here in /*/*/*) cd ..;; *) echo ""; break;; esac; \ - done + done .endif .if defined(SRCTOP) && exists(${SRCTOP}/.) .export SRCTOP .endif .endif .if !defined(OBJTOP) && !empty(SRCTOP) .if defined(MAKEOBJDIRPREFIX) && exists(${MAKEOBJDIRPREFIX}${SRCTOP}) OBJTOP= ${MAKEOBJDIRPREFIX}${SRCTOP} .elif (exists(${SRCTOP}/Makefile) || exists(${SRCTOP}/makefile)) OBJTOP!= cd ${SRCTOP} && ${PRINTOBJDIR} .endif .if empty(OBJTOP) OBJTOP= ${SRCTOP} .endif .export OBJTOP .endif Index: head/contrib/bmake/mk/stage-install.sh =================================================================== --- head/contrib/bmake/mk/stage-install.sh (revision 365365) +++ head/contrib/bmake/mk/stage-install.sh (revision 365366) @@ -1,98 +1,135 @@ #!/bin/sh # NAME: # stage-install.sh - wrapper around install # # SYNOPSIS: # stage-install.sh [variable="value"] "args" "dest" # # DESCRIPTION: # This script is a wrapper around the normal install(1). # Its role is to add '.dirdep' files to the destination. # The variables we might use are: # # INSTALL # Path to actual install(1), default is # $REAL_INSTALL # # OBJDIR # Path to the dir where '.dirdep' was generated, # default is '.' # # _DIRDEP # Path to actual '.dirdep' file, default is # $OBJDIR/.dirdep # # The "args" and "dest" are passed as is to install(1), and if a # '.dirdep' file exists it will be linked or copied to each # "file".dirdep placed in "dest" or "dest".dirdep if it happed # to be a file rather than a directory. # +# Before we run install(1), we check if "dest" needs to be a +# directory (more than one file in "args") and create it +# if necessary. +# # SEE ALSO: # meta.stage.mk -# +# # RCSid: -# $Id: stage-install.sh,v 1.5 2013/04/19 16:32:24 sjg Exp $ +# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $ # -# @(#) Copyright (c) 2013, Simon J. Gerraty +# @(#) Copyright (c) 2013-2020, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # INSTALL=${REAL_INSTALL:-install} OBJDIR=. while : do case "$1" in *=*) eval "$1"; shift;; *) break;; esac done +# get last entry from "$@" without side effects +last_entry() { + while [ $# -gt 8 ] + do + shift 8 + done + eval last=\$$# + echo $last +} + +# mkdir $dest if needed (more than one file) +mkdir_if_needed() { + ( + lf= + while [ $# -gt 8 ] + do + shift 4 + done + for f in "$@" + do + [ -f $f ] || continue + [ $f = $dest ] && continue + if [ -n "$lf" ]; then + # dest must be a directory + mkdir -p $dest + break + fi + lf=$f + done + ) +} + +args="$@" +dest=`last_entry "$@"` +case " $args " in +*" -d "*) ;; +*) [ -e $dest ] || mkdir_if_needed "$@";; +esac + # if .dirdep doesn't exist, just run install and be done _DIRDEP=${_DIRDEP:-$OBJDIR/.dirdep} [ -s $_DIRDEP ] && EXEC= || EXEC=exec $EXEC $INSTALL "$@" || exit 1 # from meta.stage.mk LnCp() { rm -f $2 2> /dev/null ln $1 $2 2> /dev/null || cp -p $1 $2 } StageDirdep() { t=$1 if [ -s $t.dirdep ]; then cmp -s $_DIRDEP $t.dirdep && return echo "ERROR: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2 exit 1 fi LnCp $_DIRDEP $t.dirdep || exit 1 } -args="$@" -while [ $# -gt 8 ] -do - shift 8 -done -eval dest=\$$# if [ -f $dest ]; then # a file, there can be only one .dirdep needed StageDirdep $dest elif [ -d $dest ]; then for f in $args do test -f $f || continue StageDirdep $dest/${f##*/} done fi Index: head/contrib/bmake/mk/sys/AIX.mk =================================================================== --- head/contrib/bmake/mk/sys/AIX.mk (revision 365365) +++ head/contrib/bmake/mk/sys/AIX.mk (revision 365366) @@ -1,184 +1,184 @@ # $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $ # @(#)sys.mk 5.11 (Berkeley) 3/13/91 OS ?= AIX unix ?= We run ${OS}. ROOT_GROUP ?= system # This needs a lot of work yet... NOPIC ?=no # no shared libs? .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/gcc) CC ?= gcc DBG ?= -O -g STATIC ?= -static .else -CC ?= cc +CC ?= cc DBG ?= -g -STATIC ?= +STATIC ?= .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} YACC ?= yacc YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/Darwin.mk =================================================================== --- head/contrib/bmake/mk/sys/Darwin.mk (revision 365365) +++ head/contrib/bmake/mk/sys/Darwin.mk (revision 365366) @@ -1,222 +1,222 @@ # $NetBSD: Darwin.sys.mk,v 1.3 2003/02/16 09:44:41 grant Exp $ # @(#)sys.mk 8.2 (Berkeley) 3/21/94 OS ?= Darwin unix ?= We run ${OS}. .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 SHLIBEXT ?= .dylib HOST_LIBEXT ?= .dylib DSHLIBEXT ?= .dylib AR ?= ar ARFLAGS ?= rl RANLIB = AS ?= as AFLAGS ?= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} PIPE ?= -pipe .if exists(/usr/bin/clang) CC ?= cc ${PIPE} CXX ?= c++ .elif exists(/usr/bin/gcc) CC ?= gcc ${PIPE} .else CC ?= cc ${PIPE} .endif DBG ?= -O2 CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/bin/g++) CXX ?= g++ .else CXX ?= c++ .endif CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} OBJC ?= ${CC} OBJCFLAGS ?= ${CFLAGS} COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp NOLINT ?= 1 CPPFLAGS ?= MK_DEP ?= mkdep FC ?= f77 FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= install LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= SHLIB_TEENY ?= 0 SHLIB_MINOR ?= 0 MKPICLIB ?= no LIBEXT ?= .dylib LINT ?= lint LINTFLAGS ?= -chapbx LORDER ?= lorder NM ?= nm PC ?= pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size TSORT ?= tsort -q YACC ?= bison -y YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${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} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/Generic.mk =================================================================== --- head/contrib/bmake/mk/sys/Generic.mk (revision 365365) +++ head/contrib/bmake/mk/sys/Generic.mk (revision 365366) @@ -1,204 +1,204 @@ -# $Id: Generic.mk,v 1.16 2020/06/29 14:34:42 sjg Exp $ +# $Id: Generic.mk,v 1.17 2020/08/19 17:51:53 sjg Exp $ # # some reasonable defaults .SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h .SUFFIXES: .sh .m4 .LIBS: .a tools ?= \ ar \ lorder \ ranlib \ tsort .for t in ${tools} ${t:tu} := ${t:${M_whence}} .endfor # need to make sure this is set MACHINE_ARCH.${MACHINE} ?= ${MACHINE} .if empty(MACHINE_ARCH) MACHINE_ARCH = ${MACHINE_ARCH.${MACHINE}} .endif .if !empty(TSORT) TSORT += -q .endif ARFLAGS ?= rl AS ?= as AFLAGS ?= -.if ${MACHINE_ARCH} == "sparc64" +.if ${MACHINE_ARCH} == "sparc64" AFLAGS+= -Wa,-Av9a .endif COMPILE.s ?= ${CC} ${AFLAGS} -c LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} CC ?= cc DBG ?= -O2 CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= c++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} OBJC ?= ${CC} OBJCFLAGS ?= ${CFLAGS} COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= install LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= LINT ?= lint LINTFLAGS ?= -chapbxzF NM ?= nm PC ?= pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size YACC ?= yacc YFLAGS ?= YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .c.ln: ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC} # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/HP-UX.mk =================================================================== --- head/contrib/bmake/mk/sys/HP-UX.mk (revision 365365) +++ head/contrib/bmake/mk/sys/HP-UX.mk (revision 365366) @@ -1,226 +1,226 @@ -# $Id: HP-UX.mk,v 1.14 2020/06/29 14:34:42 sjg Exp $ +# $Id: HP-UX.mk,v 1.15 2020/08/19 17:51:53 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 OS ?= HP-UX ROOT_GROUP ?= root unix ?= We run ${OS}. # HP-UX's cc does not provide any clues as to wether this is 9.x or 10.x # nor does sys/param.h, so we'll use the existence of /hp-ux -.if exists("/hp-ux") +.if exists("/hp-ux") OSMAJOR ?=9 .endif OSMAJOR ?=10 __HPUX_VERSION ?=${OSMAJOR} .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 LIBMODE ?= 755 LIBCRT0 ?= /lib/crt0.o .LIBS: .a # +b is needed to stop the binaries from insisting on having # the build tree available :-) # +s tells the dynamic loader to use SHLIB_PATH if set LD_bpath ?=-Wl,+b/lib:/usr/lib:/usr/local/lib LD_spath ?=-Wl,+s LDADD+= ${LD_bpath} ${LD_spath} .if exists(/usr/lib/end.o) LDADD+= /usr/lib/end.o .endif AR ?= ar ARFLAGS ?= rl RANLIB ?= : AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/gcc) PIPE ?= -pipe CC ?= gcc ${PIPE} AS ?= gas DBG ?= -O -g STATIC ?= -static .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif .else # HP's bundled compiler knows not -g or -O AS ?= as CC ?= cc .if exists(/opt/ansic/bin/cc) CCMODE ?=-Ae +ESlit PICFLAG ?= +z LD_x= DBG ?=-g -O .endif -DBG ?= +DBG ?= STATIC ?= -Wl,-a,archive .endif .if (${__HPUX_VERSION} == "10") CCSOURCE_FLAGS ?= -D_HPUX_SOURCE .else CCSOURCE_FLAGS ?= -D_HPUX_SOURCE -D_INCLUDE_POSIX_SOURCE -D_INCLUDE_XOPEN_SOURCE -D_INCLUDE_XOPEN_SOURCE_EXTENDED .endif CFLAGS ?= ${DBG} CFLAGS+ ?= ${CCMODE} -D__hpux__ -D__HPUX_VERSION=${__HPUX_VERSION} ${CCSOURCE_FLAGS} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} # HP's sh sucks ENV= MAKE_SHELL ?= /bin/ksh .if exists(/usr/local/bin/bison) YACC ?= bison -y .else YACC ?= yacc .endif YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/IRIX.mk =================================================================== --- head/contrib/bmake/mk/sys/IRIX.mk (revision 365365) +++ head/contrib/bmake/mk/sys/IRIX.mk (revision 365366) @@ -1,195 +1,195 @@ # $NetBSD: IRIX.sys.mk,v 1.2 2002/12/24 23:03:27 jschauma Exp $ # @(#)sys.mk 8.2 (Berkeley) 3/21/94 .if ${.PARSEFILE} == "sys.mk" .ifndef ROOT_GROUP OS!= uname -s ROOT_GROUP!= sed -n /:0:/s/:.*//p /etc/group .MAKEOVERRIDES+= OS ROOT_GROUP .endif unix ?= We run ${OS}. .endif .SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h .SUFFIXES: .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AFLAGS ?= COMPILE.s ?= ${CC} ${AFLAGS} -c LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} CC ?= cc NOGCCERROR ?= # defined DBG ?= -O2 CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= CC CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} OBJC ?= ${CC} OBJCFLAGS ?= ${CFLAGS} COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= CC -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= ${PREFIX}/bin/install-sh LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= LINT ?= lint LINTFLAGS ?= -chapbxzF LORDER ?= lorder NM ?= nm PC ?= pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size TSORT ?= tsort -q YACC ?= yacc YFLAGS ?= YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .c.ln: ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC} # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/Linux.mk =================================================================== --- head/contrib/bmake/mk/sys/Linux.mk (revision 365365) +++ head/contrib/bmake/mk/sys/Linux.mk (revision 365366) @@ -1,187 +1,187 @@ -# $Id: Linux.mk,v 1.12 2020/06/29 14:34:42 sjg Exp $ +# $Id: Linux.mk,v 1.13 2020/08/19 17:51:53 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 OS ?= Linux unix ?= We run ${OS}. ROOT_GROUP ?= root # would be better to work out where it is... LIBCRT0 ?= /dev/null NEED_SOLINKS ?=yes .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/gcc) CC ?= gcc -pipe DBG ?= -O -g STATIC ?= -static .else CC ?= cc -pipe DBG ?= -g STATIC ?= -Bstatic .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} YACC ?= yacc YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: - ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} + ${COMPILE.s} -o ${.TARGET} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/NetBSD.mk =================================================================== --- head/contrib/bmake/mk/sys/NetBSD.mk (revision 365365) +++ head/contrib/bmake/mk/sys/NetBSD.mk (revision 365366) @@ -1,230 +1,230 @@ # $NetBSD: sys.mk,v 1.66.2.1 2002/06/05 03:31:01 lukem Exp $ # @(#)sys.mk 8.2 (Berkeley) 3/21/94 OS ?= NetBSD unix ?= We run ${OS}. .if !defined(MAKE_VERSION) # we are running native make # which defined MAKE_VERSION between 20010609 and 20090324 # so we can make a rough guess .if defined(.MAKE.LEVEL) MAKE_VERSION ?= 20090908 .elif defined(.MAKE.MAKEFILES) # introduced 20071008 MAKE_VERSION ?= 20090324 .else # this just before when MAKE_VERSION was introduced MAKE_VERSION ?= 20010606 .endif .endif .SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h .SUFFIXES: .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AFLAGS ?= COMPILE.s ?= ${CC} ${AFLAGS} -c LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} CC ?= cc # need to make sure this is set MACHINE_ARCH.${MACHINE} ?= ${MACHINE} .if empty(MACHINE_ARCH) MACHINE_ARCH ?= ${MACHINE_ARCH.${MACHINE}} .endif # # CPU model, derived from MACHINE_ARCH # MACHINE_CPU ?= ${MACHINE_ARCH:C/mipse[bl]/mips/:C/mips64e[bl]/mips/:C/sh3e[bl]/sh3/:S/m68000/m68k/:S/armeb/arm/} .if ${MACHINE_CPU} == "alpha" || \ ${MACHINE_CPU} == "arm" || \ ${MACHINE_CPU} == "i386" || \ ${MACHINE_CPU} == "m68k" || \ ${MACHINE_CPU} == "mips" || \ ${MACHINE_CPU} == "powerpc" || \ ${MACHINE_CPU} == "sparc" || \ ${MACHINE_CPU} == "vax" DBG ?= -O2 .elif ${MACHINE_ARCH} == "x86_64" DBG ?= .elif ${MACHINE_ARCH} == "sparc64" DBG ?= -O -ffixed-g4 #Hack for embedany memory model compatibility .else DBG ?= -O .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= c++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} OBJC ?= ${CC} OBJCFLAGS ?= ${CFLAGS} COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= install LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= LINT ?= lint LINTFLAGS ?= -chapbxzF LORDER ?= lorder NM ?= nm PC ?= pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size TSORT ?= tsort -q YACC ?= yacc YFLAGS ?= YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .c.ln: ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC} # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/OSF1.mk =================================================================== --- head/contrib/bmake/mk/sys/OSF1.mk (revision 365365) +++ head/contrib/bmake/mk/sys/OSF1.mk (revision 365366) @@ -1,198 +1,198 @@ -# $Id: OSF1.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $ +# $Id: OSF1.mk,v 1.12 2020/08/19 17:51:53 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 OS ?= OSF1 unix ?= We run ${OS}. ROOT_GROUP ?= system # can't fine one anywhere, so just stop the dependency LIBCRT0 ?= /dev/null PATH ?=/usr/sbin:/usr/bin:/usr/ucb:/opt/gnu/bin:/usr/ccs/bin .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a # no -X LD_X= LD_x ?= -x LD_r ?= -r AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AS_STDIN ?= - AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/opt/gnu/bin/gcc) || exists(/usr/local/bin/gcc) -CC ?= gcc +CC ?= gcc .else CC ?= cc -std .endif .if (${CC:T} == "gcc") DBG ?= -O -g STATIC ?= -static DBG ?= -g STATIC ?= -non_shared .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= /usr/ccs/lib/cpp .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/bison) || exists(/opt/gnu/bin/bison) YACC ?= bison -y .else YACC ?= yacc .endif YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/OpenBSD.mk =================================================================== --- head/contrib/bmake/mk/sys/OpenBSD.mk (revision 365365) +++ head/contrib/bmake/mk/sys/OpenBSD.mk (revision 365366) @@ -1,205 +1,205 @@ # $NetBSD: OpenBSD.sys.mk,v 1.1 2002/11/17 09:18:00 cjep Exp $ # @(#)sys.mk 8.2 (Berkeley) 3/21/94 OS ?= OpenBSD unix ?= We run ${OS}. .SUFFIXES: .out .a .ln .o .s .S .c ${CXX_SUFFIXES} .F .f .r .y .l .cl .p .h .SUFFIXES: .sh .m4 .LIBS: .a # need to make sure this is set MACHINE_ARCH.${MACHINE} ?= ${MACHINE} .if empty(MACHINE_ARCH) MACHINE_ARCH ?= ${MACHINE_ARCH.${MACHINE}} .endif AR ?= ar ARFLAGS ?= rl RANLIB ?= ranlib AS ?= as AFLAGS ?= -.if ${MACHINE_ARCH} == "sparc64" +.if ${MACHINE_ARCH} == "sparc64" AFLAGS+= -Wa,-Av9a .endif COMPILE.s ?= ${CC} ${AFLAGS} -c LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c -traditional-cpp LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} CC ?= cc .if ${MACHINE_ARCH} == "alpha" || \ ${MACHINE_ARCH} == "i386" || \ ${MACHINE_ARCH} == "m68k" || \ ${MACHINE_ARCH} == "sparc" || \ ${MACHINE_ARCH} == "vax" DBG ?= -O2 .else DBG ?= -O .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= c++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} OBJC ?= ${CC} OBJCFLAGS ?= ${CFLAGS} COMPILE.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} -c LINK.m ?= ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= cpp -CPPFLAGS ?= +CPPFLAGS ?= FC ?= f77 FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= install LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= LINT ?= lint LINTFLAGS ?= -chapbxzF LORDER ?= lorder NM ?= nm PC ?= pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size TSORT ?= tsort -q YACC ?= yacc YFLAGS ?= YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .c.ln: ${LINT} ${LINTFLAGS} ${CPPFLAGS:M-[IDU]*} -i ${.IMPSRC} # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/SunOS.mk =================================================================== --- head/contrib/bmake/mk/sys/SunOS.mk (revision 365365) +++ head/contrib/bmake/mk/sys/SunOS.mk (revision 365366) @@ -1,219 +1,219 @@ -# $Id: SunOS.mk,v 1.11 2020/06/29 14:34:42 sjg Exp $ +# $Id: SunOS.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ .if ${.PARSEFILE} == "sys.mk" .include OS ?= SunOS.${HOST_OSMAJOR} unix ?= We run ${OS}. .endif .if ${HOST_OSMAJOR} > 4 ROOT_GROUP ?= root SHLIB_FULLVERSION ?= ${SHLIB_MAJOR} # suppress the dependency LIBCRT0 ?= /dev/null .ifndef CC # the PATH below may find an ancient gcc CC := ${gcc:L:${M_whence}} .export CC .endif # the stuff in /usr/xpg4/bin is usually more sane. PATH ?=/usr/xpg4/bin:/usr/sbin:/usr/bin:/usr/ucb:/usr/sfw/bin:/opt/gnu/bin:/usr/ccs/bin:/usr/local/bin .export PATH DSHLIBEXT ?= .so HOST_LIBDIRS ?= /usr/lib /lib /usr/sfw/lib # no -X LD_X= LD_x= RANLIB ?= : CPP ?= /usr/ccs/lib/cpp .else ROOT_GROUP ?= wheel RANLIB ?= ranlib CPP ?= cpp .endif # the rest is common .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a AR ?= ar ARFLAGS ?= rl AS ?= as AS_STDIN ?= - AFLAGS= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/opt/gnu/bin/gcc) || exists(/usr/local/bin/gcc) CC ?= gcc -pipe DBG ?= -O -g STATIC ?= -static .else CC ?= cc DBG ?= -g STATIC ?= -Bstatic .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} CXX ?= g++ CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N FC ?= f77 FFLAGS ?= -O RFLAGS= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} LEX ?= lex LFLAGS= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS= LINT ?= lint LINTFLAGS ?= -chapbx PC ?= pc PFLAGS= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/bison) || exists(/opt/gnu/bin/bison) YACC ?= bison -y .else YACC ?= yacc .endif YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys/UnixWare.mk =================================================================== --- head/contrib/bmake/mk/sys/UnixWare.mk (revision 365365) +++ head/contrib/bmake/mk/sys/UnixWare.mk (revision 365366) @@ -1,241 +1,241 @@ -# $Id: UnixWare.mk,v 1.6 2020/06/29 14:34:42 sjg Exp $ +# $Id: UnixWare.mk,v 1.7 2020/08/19 17:51:53 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 OS ?= UnixWare unix ?= We run ${OS}. ROOT_GROUP ?= root # can't fine one anywhere, so just stop the dependency LIBCRT0 ?= /dev/null PATH ?=/usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:/usr/local/bin .SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4 .LIBS: .a # no -X LD_X= LD_x= AR ?= ar ARFLAGS ?= rl RANLIB ?= : AS ?= as AS_STDIN ?= - AFLAGS ?= COMPILE.s ?= ${AS} ${AFLAGS} LINK.s ?= ${CC} ${AFLAGS} ${LDFLAGS} COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS} # at least gcc 2.95 on UnixWare has no internal macro to identify the system .if exists(/usr/local/bin/gcc) CC ?= gcc -pipe -DUNIXWARE DBG ?= -O -g STATIC ?= -static .else CC ?= cc DBG ?= -g STATIC ?= -Bstatic # XXX: don't know about UDK compilers .endif CFLAGS ?= ${DBG} COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} .if exists(/usr/local/bin/g++) CXX ?= g++ -DUNIXWARE .else CXX ?= c++ # XXX: don't know about UDK compilers .endif CXXFLAGS ?= ${CFLAGS} COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} CPP ?= /usr/ccs/lib/cpp .if defined(DESTDIR) CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include .endif MK_DEP ?= mkdeps.sh -N .if exists(/usr/local/bin/g77) FC ?= g77 .else FC ?= f77 # XXX: don't know about UDK compilers .endif FFLAGS ?= -O RFLAGS ?= COMPILE.f ?= ${FC} ${FFLAGS} -c LINK.f ?= ${FC} ${FFLAGS} ${LDFLAGS} COMPILE.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} -c LINK.F ?= ${FC} ${FFLAGS} ${CPPFLAGS} ${LDFLAGS} COMPILE.r ?= ${FC} ${FFLAGS} ${RFLAGS} -c LINK.r ?= ${FC} ${FFLAGS} ${RFLAGS} ${LDFLAGS} INSTALL ?= /usr/ucb/install # BSD install LEX ?= lex LFLAGS ?= LEX.l ?= ${LEX} ${LFLAGS} LD ?= ld LDFLAGS ?= LIBC ?= ${DESTDIR}/usr/ccs/lib/libc.a LIBCOMPAT ?= LIBCRYPT ?= ${DESTDIR}/usr/lib/libcrypt.a LIBCURSES ?= ${DESTDIR}/usr/ccs/lib/libcurses.a LIBDBM ?= LIBDES ?= LIBEDIT ?= LIBGCC ?= LIBKDB ?= LIBKRB ?= LIBKVM ?= LIBL ?= ${DESTDIR}/usr/ccs/lib/libl.a LIBM ?= ${DESTDIR}/usr/ccs/lib/libm.a LIBMP ?= LIBPC ?= LIBPCAP ?= LIBPLOT ?= LIBRESOLV ?= LIBRPCSVC ?= ${DESTDIR}/usr/lib/librpcsvc.a LIBSKEY ?= ${DESTDIR}/usr/lib/libskey.a LIBTERMCAP ?= ${DESTDIR}/usr/ccs/lib/libtermcap.a LIBUTIL ?= LIBWRAP ?= LIBY ?= ${DESTDIR}/usr/ccs/lib/liby.a LIBZ ?= LINT ?= lint LINTFLAGS ?= -pF LORDER ?= lorder NM ?= nm PC ?= pc # XXX: UDK probably does not have pc PFLAGS ?= COMPILE.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} -c LINK.p ?= ${PC} ${PFLAGS} ${CPPFLAGS} ${LDFLAGS} SIZE ?= size TSORT ?= tsort .if exists(/usr/local/bin/bison) YACC ?= bison -y .else YACC ?= yacc .endif YFLAGS ?= -d YACC.y ?= ${YACC} ${YFLAGS} # C .c: ${LINK.c} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .c.o: ${COMPILE.c} ${.IMPSRC} .c.a: ${COMPILE.c} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # C++ ${CXX_SUFFIXES}: ${LINK.cc} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} ${CXX_SUFFIXES:%=%.o}: ${COMPILE.cc} ${.IMPSRC} ${CXX_SUFFIXES:%=%.a}: ${COMPILE.cc} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Fortran/Ratfor .f: ${LINK.f} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .f.o: ${COMPILE.f} ${.IMPSRC} .f.a: ${COMPILE.f} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .F: ${LINK.F} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .F.o: ${COMPILE.F} ${.IMPSRC} .F.a: ${COMPILE.F} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .r: ${LINK.r} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .r.o: ${COMPILE.r} ${.IMPSRC} .r.a: ${COMPILE.r} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Pascal .p: ${LINK.p} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .p.o: ${COMPILE.p} ${.IMPSRC} .p.a: ${COMPILE.p} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Assembly .s: ${LINK.s} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .s.o: ${COMPILE.s} ${.IMPSRC} .s.a: ${COMPILE.s} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o .S: ${LINK.S} -o ${.TARGET} ${.IMPSRC} ${LDLIBS} .S.o: ${COMPILE.S} ${.IMPSRC} .S.a: ${COMPILE.S} ${.IMPSRC} ${AR} ${ARFLAGS} $@ $*.o rm -f $*.o # Lex .l: ${LEX.l} ${.IMPSRC} ${LINK.c} -o ${.TARGET} lex.yy.c ${LDLIBS} -ll rm -f lex.yy.c .l.c: ${LEX.l} ${.IMPSRC} mv lex.yy.c ${.TARGET} .l.o: ${LEX.l} ${.IMPSRC} - ${COMPILE.c} -o ${.TARGET} lex.yy.c + ${COMPILE.c} -o ${.TARGET} lex.yy.c rm -f lex.yy.c # Yacc .y: ${YACC.y} ${.IMPSRC} ${LINK.c} -o ${.TARGET} y.tab.c ${LDLIBS} rm -f y.tab.c .y.c: ${YACC.y} ${.IMPSRC} mv y.tab.c ${.TARGET} .y.o: ${YACC.y} ${.IMPSRC} ${COMPILE.c} -o ${.TARGET} y.tab.c rm -f y.tab.c # Shell .sh: rm -f ${.TARGET} cp ${.IMPSRC} ${.TARGET} Index: head/contrib/bmake/mk/sys.clean-env.mk =================================================================== --- head/contrib/bmake/mk/sys.clean-env.mk (revision 365365) +++ head/contrib/bmake/mk/sys.clean-env.mk (revision 365366) @@ -1,130 +1,130 @@ -# $Id: sys.clean-env.mk,v 1.22 2017/10/25 23:44:20 sjg Exp $ +# $Id: sys.clean-env.mk,v 1.23 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # This makefile would normally be included by sys.env.mk # The variables used by this makefile include: # # MAKE_ENV_SAVE_VAR_LIST # The actuall list of variables from the environment that will be # preserved. # MAKE_ENV_SAVE_PREFIX_LIST # A list of prefixes to match against the environment - the results # are added to MAKE_ENV_SAVE_VAR_LIST after being filtered by... # MAKE_ENV_SAVE_EXCLUDE_LIST # A list of words or patterns which is turned into a list of :N # modifiers. .if ${.MAKE.LEVEL} == 0 && ${MAKE_VERSION} >= 20100606 # We save any env var that starts with the words in MAKE_ENV_SAVE_PREFIX_LIST. # This gets expanded to an egrep expression like '^(A|B|C...)' # and added to MAKE_ENV_SAVE_VAR_LIST below. # If any of these end up being too greedy, MAKE_ENV_SAVE_EXCLUDE_LIST # can be used to filter. MAKE_ENV_SAVE_PREFIX_LIST += \ CCACHE \ CVS \ DEBUG \ DISTCC \ HOST \ MACHINE \ MAKE \ MK \ NEED_ \ SB_ \ SSH \ SVN \ USE_ \ WITH_ \ WITHOUT_ \ # This could be a list of vars or patterns to explicitly exclude. MAKE_ENV_SAVE_EXCLUDE_LIST ?= _ # This is the actual list that we will save -# HOME is probably something worth clobbering eg. +# HOME is probably something worth clobbering eg. # HOME=/var/empty MAKE_ENV_SAVE_VAR_LIST += \ HOME \ LOGNAME \ OBJROOT \ OBJTOP \ PATH \ SB \ SRCTOP \ USER \ ${_env_vars:${MAKE_ENV_SAVE_EXCLUDE_LIST:${M_ListToSkip}}} _env_vars != env | egrep '^(${MAKE_ENV_SAVE_PREFIX_LIST:ts|})' | sed 's,=.*,,'; echo _export_list = .for v in ${MAKE_ENV_SAVE_VAR_LIST:O:u} .if defined($v) _export_list += $v # Save current value $v := ${$v} .endif .endfor # Now, clobber the environment .unexport-env # This is a list of vars that we handle specially below _tricky_env_vars = MAKEOBJDIR OBJTOP # Export our selection - sans tricky ones .export ${_export_list:${_tricky_env_vars:${M_ListToSkip}}} # This next bit may need tweaking # if you don't happen to like the way I set it. .if defined(MAKEOBJDIR) # We are going to set this to the equivalent of the shell's # MAKEOBJDIR='${.CURDIR:S,${SRCTOP},${OBJTOP},}' _srctop := ${SRCTOP:U${SB_SRC:U${SB}/src}} _objroot := ${OBJROOT:U${SB_OBJROOT:U${SB}/${SB_OBJPREFIX}}} .if ${MAKE_VERSION} < 20160218 _objtop := ${OBJTOP:U${_objroot}${MACHINE}} # Take care of ${MACHINE} .if ${MACHINE:Nhost*} == "" || ${OBJTOP} == ${HOST_OBJTOP:Uno} OBJTOP = ${_objtop:S,${HOST_TARGET}$,\${MACHINE},} .else OBJTOP = ${_objtop:S,${MACHINE}$,\${MACHINE},} .endif # Export like this MAKEOBJDIR = $${.CURDIR:S,${_srctop},$${OBJTOP},} #.info ${MAKE_SAVE_ENV_VARS _srctop _objroot _objtop OBJTOP MAKEOBJDIR:L:@v@${.newline}$v=${$v}@} # Export these as-is, and do not track... # otherwise the environment will be ruined when we evaluate them below. .export-env ${_tricky_env_vars} # Now evaluate for ourselves .for v in ${_tricky_env_vars} $v := ${$v} .endfor .else # we cannot use the '$$' trick, anymore # but we can export a literal (unexpanded) value SRCTOP := ${_srctop} OBJROOT := ${_objroot} OBJTOP = ${OBJROOT}${MACHINE} MAKEOBJDIR = ${.CURDIR:S,${SRCTOP},${OBJTOP},} .export-literal SRCTOP OBJROOT ${_tricky_env_vars} .endif #.info ${_tricky_env_vars:@v@${.newline}$v=${$v}@} #showenv: # @env | egrep 'OBJ|SRC' .endif # MAKEOBJDIR .endif # level 0 Index: head/contrib/bmake/mk/sys.debug.mk =================================================================== --- head/contrib/bmake/mk/sys.debug.mk (revision 365365) +++ head/contrib/bmake/mk/sys.debug.mk (revision 365366) @@ -1,33 +1,33 @@ -# $Id: sys.debug.mk,v 1.1 2016/10/01 19:11:55 sjg Exp $ +# $Id: sys.debug.mk,v 1.2 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # Sometimes we want to turn on debugging in just one or two places # if .CURDIR is matched by any entry in DEBUG_MAKE_SYS_DIRS we # will apply DEBUG_MAKE_FLAGS now. # if an entry in DEBUG_MAKE_DIRS matches, we at the end of sys.mk # eg. DEBUG_MAKE_FLAGS=-dv DEBUG_MAKE_SYS_DIRS="*lib/sjg" # use DEBUG_MAKE_FLAGS0 to apply only to .MAKE.LEVEL 0 # .if ${.MAKE.LEVEL:U1} == 0 # we use indirection, to simplify the tests below, and incase # DEBUG_* were given on our command line. _DEBUG_MAKE_FLAGS = ${DEBUG_MAKE_FLAGS0} _DEBUG_MAKE_SYS_DIRS = ${DEBUG_MAKE_SYS_DIRS0:U${DEBUG_MAKE_SYS_DIRS}} _DEBUG_MAKE_DIRS = ${DEBUG_MAKE_DIRS0:U${DEBUG_MAKE_DIRS}} .else _DEBUG_MAKE_FLAGS = ${DEBUG_MAKE_FLAGS} _DEBUG_MAKE_SYS_DIRS = ${DEBUG_MAKE_SYS_DIRS} _DEBUG_MAKE_DIRS = ${DEBUG_MAKE_DIRS} .endif Index: head/contrib/bmake/mk/sys.dependfile.mk =================================================================== --- head/contrib/bmake/mk/sys.dependfile.mk (revision 365365) +++ head/contrib/bmake/mk/sys.dependfile.mk (revision 365366) @@ -1,59 +1,59 @@ -# $Id: sys.dependfile.mk,v 1.8 2016/03/11 01:34:13 sjg Exp $ +# $Id: sys.dependfile.mk,v 1.9 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2012, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # This only makes sense in meta mode. # This allows a mixture of auto generated as well as manually edited # dependency files, which can be differentiated by their names. # As per dirdeps.mk we only require: # 1. a common prefix # 2. that machine specific files end in .${MACHINE} # # The .MAKE.DEPENDFILE_PREFERENCE below is an example. # All depend file names should start with this .MAKE.DEPENDFILE_PREFIX ?= Makefile.depend .if !empty(.MAKE.DEPENDFILE) && \ ${.MAKE.DEPENDFILE:M${.MAKE.DEPENDFILE_PREFIX}*} == "" # let us do our thing below... .undef .MAKE.DEPENDFILE .endif # The order of preference: we will use the first one of these we find. # It usually makes sense to order from most specific to least. .MAKE.DEPENDFILE_PREFERENCE ?= \ ${.CURDIR}/${.MAKE.DEPENDFILE_PREFIX}.${MACHINE} \ ${.CURDIR}/${.MAKE.DEPENDFILE_PREFIX} # Normally the 1st entry is our default choice # Another useful default is ${.MAKE.DEPENDFILE_PREFIX} .MAKE.DEPENDFILE_DEFAULT ?= ${.MAKE.DEPENDFILE_PREFERENCE:[1]} _e := ${.MAKE.DEPENDFILE_PREFERENCE:@m@${exists($m):?$m:}@} .if !empty(_e) .MAKE.DEPENDFILE := ${_e:[1]} .elif ${.MAKE.DEPENDFILE_PREFERENCE:M*${MACHINE}} != "" && ${.MAKE.DEPENDFILE_DEFAULT:E} != ${MACHINE} # MACHINE specific depend files are supported, but *not* default. # If any already exist, we should follow suit. _aml = ${ALL_MACHINE_LIST:Uarm amd64 i386 powerpc:N${MACHINE}} ${MACHINE} # make sure we restore MACHINE _m := ${MACHINE} _e := ${_aml:@MACHINE@${.MAKE.DEPENDFILE_PREFERENCE:@m@${exists($m):?$m:}@}@} MACHINE := ${_m} .if !empty(_e) .MAKE.DEPENDFILE ?= ${.MAKE.DEPENDFILE_PREFERENCE:M*${MACHINE}:[1]} .endif .endif .MAKE.DEPENDFILE ?= ${.MAKE.DEPENDFILE_DEFAULT} Index: head/contrib/bmake/mk/sys.mk =================================================================== --- head/contrib/bmake/mk/sys.mk (revision 365365) +++ head/contrib/bmake/mk/sys.mk (revision 365366) @@ -1,155 +1,156 @@ -# $Id: sys.mk,v 1.47 2020/04/17 21:08:17 sjg Exp $ +# $Id: sys.mk,v 1.51 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2003-2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # Avoid putting anything platform specific in here. # _DEBUG_MAKE_FLAGS etc. .include .if !empty(_DEBUG_MAKE_FLAGS) .if ${_DEBUG_MAKE_SYS_DIRS:Uno:@x@${.CURDIR:M$x}@} != "" .MAKEFLAGS: ${_DEBUG_MAKE_FLAGS} .endif .endif # useful modifiers .include # we expect a recent bmake .if !defined(_TARGETS) # some things we do only once _TARGETS := ${.TARGETS} .-include .endif # we need HOST_TARGET etc below. .include # early customizations .-include # Popular suffixes for C++ CXX_SUFFIXES += .cc .cpp .cxx .C CXX_SUFFIXES := ${CXX_SUFFIXES:O:u} # find the OS specifics .if defined(SYS_OS_MK) .include <${SYS_OS_MK}> .else _sys_mk = .for x in ${HOST_OSTYPE} ${HOST_TARGET} ${HOST_OS} ${MACHINE} Generic .if empty(_sys_mk) .-include _sys_mk := ${.MAKE.MAKEFILES:M*/$x.mk} .if !empty(_sys_mk) _sys_mk := sys/${_sys_mk:T} .endif .endif .if empty(_sys_mk) # might be an old style .-include <$x.sys.mk> _sys_mk := ${.MAKE.MAKEFILES:M*/$x.sys.mk:T} .endif .endfor SYS_OS_MK := ${_sys_mk} .export SYS_OS_MK .endif # some options we need to know early OPTIONS_DEFAULT_NO += \ DIRDEPS_BUILD \ DIRDEPS_CACHE OPTIONS_DEFAULT_DEPENDENT += \ AUTO_OBJ/DIRDEPS_BUILD \ META_MODE/DIRDEPS_BUILD \ STAGING/DIRDEPS_BUILD \ + STATIC_DIRDEPS_CACHE/DIRDEPS_CACHE \ .-include .if ${MK_DIRDEPS_BUILD:Uno} == "yes" MK_META_MODE = yes .-include .elif ${MK_META_MODE:Uno} == "yes" .MAKE.MODE = meta verbose ${META_MODE} .endif # make sure we have a harmless value .MAKE.MODE ?= normal # if you want objdirs make them automatic # and do it early before we compute .PATH .if ${MK_AUTO_OBJ:Uno} == "yes" || ${MKOBJDIRS:Uno} == "auto" .include .endif .if !empty(SRCTOP) .if ${.CURDIR} == ${SRCTOP} RELDIR = . .elif ${.CURDIR:M${SRCTOP}/*} RELDIR := ${.CURDIR:S,${SRCTOP}/,,} .endif .endif MACHINE_ARCH.host ?= ${_HOST_ARCH} MACHINE_ARCH.${MACHINE} ?= ${MACHINE} .if empty(MACHINE_ARCH) MACHINE_ARCH = ${MACHINE_ARCH.${MACHINE}} .endif .ifndef ROOT_GROUP ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group .export ROOT_GROUP .endif unix ?= We run ${_HOST_OSNAME}. -# We need a Bourne/POSIX shell +# 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. # We stop looping as soon as the dir exists. # If we get to the end of the loop, a plain mkdir will issue an error. Mkdirs= Mkdirs() { \ for d in $$*; do \ for i in 1 2 3 4 5 6; do \ mkdir -p $$d; \ test -d $$d && return 0; \ done; \ mkdir $$d || exit $$?; \ done; } # this often helps with debugging .SUFFIXES: .cpp-out .c.cpp-out: @${COMPILE.c:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$' ${CXX_SUFFIXES:%=%.cpp-out}: @${COMPILE.cc:N-c} -E ${.IMPSRC} | grep -v '^[ ]*$$' # late customizations .-include # if .CURDIR is matched by any entry in DEBUG_MAKE_DIRS we # will apply DEBUG_MAKE_FLAGS, now. .if !empty(_DEBUG_MAKE_FLAGS) .if ${_DEBUG_MAKE_DIRS:Uno:@x@${.CURDIR:M$x}@} != "" .MAKEFLAGS: ${_DEBUG_MAKE_FLAGS} .endif .endif Index: head/contrib/bmake/mk/sys.vars.mk =================================================================== --- head/contrib/bmake/mk/sys.vars.mk (revision 365365) +++ head/contrib/bmake/mk/sys.vars.mk (revision 365366) @@ -1,85 +1,85 @@ -# $Id: sys.vars.mk,v 1.4 2019/05/27 20:22:52 sjg Exp $ +# $Id: sys.vars.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2003-2009, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # We use the following paradigm for preventing multiple inclusion. -# It relies on the fact that conditionals and dependencies are resolved +# It relies on the fact that conditionals and dependencies are resolved # at the time they are read. # # _this ?= ${.PARSEFILE} # .if !target(__${_this}__) # __${_this}__: # .if ${MAKE_VERSION:U0} > 20100408 _this = ${.PARSEDIR:tA}/${.PARSEFILE} .else _this = ${.PARSEDIR}/${.PARSEFILE} .endif # if this is an ancient version of bmake MAKE_VERSION ?= 0 .if ${MAKE_VERSION:M*make-*} # turn it into what we want - just the date MAKE_VERSION := ${MAKE_VERSION:[1]:C,.*-,,} .endif # some useful modifiers # A useful trick for testing multiple :M's against something # :L says to use the variable's name as its value - ie. literal # got = ${clean* destroy:${M_ListToMatch:S,V,.TARGETS,}} M_ListToMatch = L:@m@$${V:M$$m}@ # match against our initial targets (see above) M_L_TARGETS = ${M_ListToMatch:S,V,_TARGETS,} # turn a list into a set of :N modifiers # NskipFoo = ${Foo:${M_ListToSkip}} M_ListToSkip= O:u:S,^,N,:ts: # type should be a builtin in any sh since about 1980, # but sadly there are exceptions! .if ${.MAKE.OS:Unknown:NBSD/OS} == "" _type_sh = which .endif # AUTOCONF := ${autoconf:L:${M_whence}} M_type = @x@(${_type_sh:Utype} $$x) 2> /dev/null; echo;@:sh:[0]:N* found*:[@]:C,[()],,g M_whence = ${M_type}:M/*:[1] # convert a path to a valid shell variable M_P2V = tu:C,[./-],_,g # convert path to absolute .if ${MAKE_VERSION:U0} > 20100408 M_tA = tA .else M_tA = C,.*,('cd' & \&\& 'pwd') 2> /dev/null || echo &,:sh .endif .if ${MAKE_VERSION:U0} >= 20170130 # M_cmpv allows comparing dotted versions like 3.1.2 # ${3.1.2:L:${M_cmpv}} -> 3001002 # we use big jumps to handle 3 digits per dot: # ${123.456.789:L:${M_cmpv}} -> 123456789 M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh .endif # 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. Index: head/contrib/bmake/mk/target-flags.mk =================================================================== --- head/contrib/bmake/mk/target-flags.mk (revision 365365) +++ head/contrib/bmake/mk/target-flags.mk (revision 365366) @@ -1,62 +1,62 @@ # NAME: # target-flags.mk - target specific flags # # DESCRIPTION: # Include this macro file after all others in a makefile and # follow it with any target specific flag settings. # For each such variable v in TARGET_FLAG_VARS we set: #.nf # # _$v := ${$v} # $v = ${${v}_${.TARGET:T}:U${_$v}} #.fi # # This allows one to do things like: #.nf # # TARGET_FLAG_VARS= CFLAGS # .include # CFLAGS_fu.o = ${_CFLAGS:N-Wall} #.fi # # To turn off -Wall for just the target fu.o # Actually CFLAGS is the default value for TARGET_FLAG_VARS. # # BUGS: # One must be careful to avoid creating circular references in # variables. The original version of this macro file did # elaborate things with CFLAGS. The current, simpler # implementation is ultimately more flexible. -# +# # It is important that target-flags.mk is included after other # macro files and that target specific flags that may reference # _$v are set after that. -# +# # Only works with a make(1) that does nested evaluation correctly. # RCSid: -# $Id: target-flags.mk,v 1.9 2014/04/05 22:56:54 sjg Exp $ +# $Id: target-flags.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1998-2002, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # TARGET_FLAG_VARS?= CFLAGS .for v in ${TARGET_FLAG_VARS} .ifndef _$v _$v := ${$v} $v = ${${v}_${.TARGET:T}:U${_$v}} .endif .endfor Index: head/contrib/bmake/mk/warnings.mk =================================================================== --- head/contrib/bmake/mk/warnings.mk (revision 365365) +++ head/contrib/bmake/mk/warnings.mk (revision 365366) @@ -1,139 +1,139 @@ # RCSid: -# $Id: warnings.mk,v 1.14 2016/04/05 15:58:37 sjg Exp $ +# $Id: warnings.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 2002, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .ifndef _w_cflags # make sure we get the behavior we expect .MAKE.SAVE_DOLLARS = no # Any number of warnings sets can be added. .-include # Modest defaults - put more elaborate sets in warnings-sets.mk # -Wunused etc are here so you can set # W_unused=-Wno-unused etc. MIN_WARNINGS?= -Wall \ -Wformat \ -Wimplicit \ -Wunused \ - -Wuninitialized + -Wuninitialized LOW_WARNINGS?= ${MIN_WARNINGS} -W -Wstrict-prototypes -Wmissing-prototypes - + MEDIUM_WARNINGS?= ${LOW_WARNINGS} -Werror HIGH_WARNINGS?= ${MEDIUM_WARNINGS} \ -Wcast-align \ -Wcast-qual \ -Wparentheses \ -Wpointer-arith \ -Wmissing-declarations \ -Wreturn-type \ -Wswitch \ -Wwrite-strings EXTRA_WARNINGS?= ${HIGH_WARNINGS} -Wextra # The two step default makes it easier to test build with different defaults. DEFAULT_WARNINGS_SET?= MIN WARNINGS_SET?= ${DEFAULT_WARNINGS_SET} # There is always someone who wants more... .if !empty(WARNINGS_XTRAS) ${WARNINGS_SET}_WARNINGS += ${WARNINGS_XTRAS} .endif # If you add sets, besure to list them (you don't have to touch this list). ALL_WARNINGS_SETS+= MIN LOW MEDIUM HIGH EXTRA .if !empty(WARNINGS_SET) .for ws in ${WARNINGS_SET} .if empty(${ws}_WARNINGS) .if ${MAKE_VERSION:[1]:C/.*-//} >= 20050530 .BEGIN: _empty_warnings _empty_warnings: .PHONY .else .BEGIN: .endif @echo "ERROR: Invalid: WARNINGS_SET=${ws}" @echo "ERROR: Try one of: ${ALL_WARNINGS_SETS:O:u}"; exit 1 .endif .endfor .endif # Without -O or if we've set -O0 somewhere - to make debugging more effective, # we need to turn off -Wuninitialized as otherwise we get a warning that # -Werror turns into an error. To be safe, set W_uninitialized blank. _w_cflags= ${CFLAGS} ${CFLAGS_LAST} ${CPPFLAGS} .if ${_w_cflags:M-O*} == "" || ${_w_cflags:M-O0} != "" W_uninitialized= .endif # .for loops have the [dis]advantage of being evaluated when read, -# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is +# so adding to WARNINGS_SET[_${MACHINE_ARCH}] after this file is # read has no effect. # Replacing the above .for loops with the WARNINGS+= below solves that # but tiggers a double free bug in bmake-20040118 and earlier. # Don't try and read this too fast! # # The first :@ "loop" handles multiple sets in WARNINGS_SET # # In the second :@ "loop", the ::?= noise sets W_foo?=-Wfoo etc # which makes it easy to turn off override individual flags # (see W_uninitialized above). -# +# # The last bit expands to ${W_foo_${.TARGET:T}:U${W_foo}} # which is the bit we ultimately want. It allows W_* to be set on a # per target basis. -# +# # NOTE: that we force the target extension to be .o # # define this once, we use it a couple of times below (hence the doubled $$). M_warnings_list = @s@$${$$s_WARNINGS}@:O:u:@w@$${$${w:C/-(.)/\1_/}::?=$$w} $${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}_${.TARGET:T:R}.o:U$${$${w:C/-(.)/\1_/}_${.TARGET:T:R}.o:U$${$${w:C/-(.)/\1_/}_${MACHINE_ARCH}:U$${$${w:C/-(.)/\1_/}}}}}@ # first a list of warnings from the chosen set _warnings = ${WARNINGS_SET_${MACHINE_ARCH}:U${WARNINGS_SET}:${M_warnings_list}} # now a list of all -Wno-* overrides not just those defined by WARNINGS_SET # since things like -Wall imply lots of others. -# this should be a super-set of the -Wno-* in _warnings, but +# this should be a super-set of the -Wno-* in _warnings, but # just in case... _no_warnings = ${_warnings:M-Wno-*} ${ALL_WARNINGS_SETS:${M_warnings_list}:M-Wno-*} # -Wno-* must follow any others WARNINGS += ${_warnings:N-Wno-*} ${_no_warnings:O:u} .ifndef NO_CFLAGS_WARNINGS # Just ${WARNINGS} should do, but this is more flexible? CFLAGS+= ${WARNINGS_${.TARGET:T:R}.o:U${WARNINGS}} .endif # it is rather silly that g++ blows up on some warning flags NO_CXX_WARNINGS+= \ implicit \ missing-declarations \ missing-prototypes \ nested-externs \ shadow \ strict-prototypes .for s in ${SRCS:M*.c*:N*.c:N*h} .for w in ${NO_CXX_WARNINGS} W_$w_${s:T:R}.o= .endfor .endfor .endif # _w_cflags Index: head/contrib/bmake/mk/whats.mk =================================================================== --- head/contrib/bmake/mk/whats.mk (revision 365365) +++ head/contrib/bmake/mk/whats.mk (revision 365366) @@ -1,69 +1,69 @@ -# $Id: whats.mk,v 1.9 2020/05/09 19:48:53 sjg Exp $ +# $Id: whats.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $ # # @(#) 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. # Permission to copy, redistribute or otherwise -# use this file is hereby granted provided that +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # .if ${MK_WHATSTRING:Uno} == "yes" -# it can be useful to embed a what(1) string in binaries +# 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(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) # a unique name that won't conflict with anything what_uuid = what_${what_thing}_${.CURDIR:T:hash} what_var = what_${.CURDIR:T:hash} SRCS += ${what_uuid}.c CLEANFILES += ${what_uuid}.c # we do not need to capture this SUPPRESS_DEPEND += *${what_uuid}.c 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"))) 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 '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 Index: head/contrib/bmake/mk/yacc.mk =================================================================== --- head/contrib/bmake/mk/yacc.mk (revision 365365) +++ head/contrib/bmake/mk/yacc.mk (revision 365366) @@ -1,57 +1,57 @@ -# $Id: yacc.mk,v 1.6 2011/06/10 22:45:46 sjg Exp $ +# $Id: yacc.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $ # # @(#) Copyright (c) 1999-2011, 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 +# use this file is hereby granted provided that # the above copyright notice and this notice are -# left intact. -# +# left intact. +# # Please send copies of changes and bug-fixes to: # sjg@crufty.net # # this file contains rules to DTRT when SRCS contains foo.y or foo.c # when only a foo.y exists. YACC?= yacc YFLAGS?= -v -t RM?= rm YACC.y?= ${YACC} ${YFLAGS} .if ${YACC.y:M-d} == "" || defined(NO_RENAME_Y_TAB_H) .y.c: ${YACC.y} ${.IMPSRC} [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET} ${RM} -f y.tab.[!h] .else # the touch of the .c is to ensure it is newer than .h (paranoia) .y.h: ${YACC.y} ${.IMPSRC} [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET:T:R}.c [ ! -s y.tab.h ] || cmp -s y.tab.h ${.TARGET:T:R}.h \ || mv y.tab.h ${.TARGET:T:R}.h touch ${.TARGET:T:R}.c ${RM} -f y.tab.* # Normally the .y.h rule does the work - to avoid races. # If for any reason the .c is lost but the .h remains, # regenerate the .c .y.c: ${.TARGET:T:R}.h [ -s ${.TARGET} ] || { \ ${YACC.y} ${.IMPSRC} && \ { [ ! -s y.tab.c ] || mv y.tab.c ${.TARGET}; \ ${RM} y.tab.*; }; } .endif beforedepend: ${SRCS:T:M*.y:S/.y/.c/g} CLEANFILES+= ${SRCS:T:M*.y:S/.y/.[ch]/g} CLEANFILES+= y.tab.[ch] Index: head/contrib/bmake/nonints.h =================================================================== --- head/contrib/bmake/nonints.h (revision 365365) +++ head/contrib/bmake/nonints.h (revision 365366) @@ -1,212 +1,233 @@ -/* $NetBSD: nonints.h,v 1.78 2020/07/03 07:40:13 rillig Exp $ */ +/* $NetBSD: nonints.h,v 1.102 2020/08/30 19:56:02 rillig Exp $ */ /*- * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 */ /*- * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 */ /* arch.c */ -ReturnStatus Arch_ParseArchive(char **, Lst, GNode *); +Boolean Arch_ParseArchive(char **, Lst, GNode *); void Arch_Touch(GNode *); void Arch_TouchLib(GNode *); time_t Arch_MTime(GNode *); time_t Arch_MemMTime(GNode *); void Arch_FindLib(GNode *, Lst); Boolean Arch_LibOODate(GNode *); void Arch_Init(void); void Arch_End(void); -int Arch_IsLib(GNode *); +Boolean Arch_IsLib(GNode *); /* compat.c */ int CompatRunCommand(void *, void *); void Compat_Run(Lst); int Compat_Make(void *, void *); /* cond.c */ struct If; -int Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean); -int Cond_Eval(char *); +CondEvalResult Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean); +CondEvalResult Cond_Eval(char *); void Cond_restore_depth(unsigned int); unsigned int Cond_save_depth(void); /* for.c */ int For_Eval(char *); int For_Accum(char *); void For_Run(int); /* job.c */ #ifdef WAIT_T void JobReapChild(pid_t, WAIT_T, Boolean); #endif /* main.c */ void Main_ParseArgLine(const char *); void MakeMode(const char *); char *Cmd_Exec(const char *, const char **); void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; void DieHorribly(void) MAKE_ATTR_DEAD; int PrintAddr(void *, void *); void Finish(int) MAKE_ATTR_DEAD; int eunlink(const char *); void execError(const char *, const char *); char *getTmpdir(void); Boolean s2Boolean(const char *, Boolean); Boolean getBoolean(const char *, Boolean); char *cached_realpath(const char *, char *); /* parse.c */ void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); -Boolean Parse_AnyExport(void); Boolean Parse_IsVar(char *); void Parse_DoVar(char *, GNode *); void Parse_AddIncludeDir(char *); void Parse_File(const char *, int); void Parse_Init(void); void Parse_End(void); void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *); Lst Parse_MainName(void); /* str.c */ -char *str_concat(const char *, const char *, int); -char **brk_string(const char *, int *, Boolean, char **); +typedef struct { + char **words; + size_t len; + void *freeIt; +} Words; + +Words Str_Words(const char *, Boolean); +static inline void MAKE_ATTR_UNUSED +Words_Free(Words w) { + free(w.words); + free(w.freeIt); +} + +char *str_concat2(const char *, const char *); +char *str_concat3(const char *, const char *, const char *); +char *str_concat4(const char *, const char *, const char *, const char *); char *Str_FindSubstring(const char *, const char *); Boolean Str_Match(const char *, const char *); -char *Str_SYSVMatch(const char *, const char *, size_t *, Boolean *); -void Str_SYSVSubst(Buffer *, char *, char *, size_t, Boolean); #ifndef HAVE_STRLCPY /* strlcpy.c */ size_t strlcpy(char *, const char *, size_t); #endif /* suff.c */ void Suff_ClearSuffixes(void); Boolean Suff_IsTransform(char *); GNode *Suff_AddTransform(char *); int Suff_EndTransform(void *, void *); -void Suff_AddSuffix(char *, GNode **); +void Suff_AddSuffix(const char *, GNode **); Lst Suff_GetPath(char *); void Suff_DoPaths(void); void Suff_AddInclude(char *); -void Suff_AddLib(char *); +void Suff_AddLib(const char *); void Suff_FindDeps(GNode *); Lst Suff_FindPath(GNode *); void Suff_SetNull(char *); void Suff_Init(void); void Suff_End(void); void Suff_PrintAll(void); /* targ.c */ void Targ_Init(void); void Targ_End(void); +void Targ_Stats(void); Lst Targ_List(void); GNode *Targ_NewGN(const char *); GNode *Targ_FindNode(const char *, int); Lst Targ_FindList(Lst, int); Boolean Targ_Ignore(GNode *); Boolean Targ_Silent(GNode *); Boolean Targ_Precious(GNode *); void Targ_SetMain(GNode *); int Targ_PrintCmd(void *, void *); int Targ_PrintNode(void *, void *); char *Targ_FmtTime(time_t); void Targ_PrintType(int); void Targ_PrintGraph(int); void Targ_Propagate(void); -void Targ_Propagate_Wait(void); /* var.c */ typedef enum { - VARF_UNDEFERR = 1, - VARF_WANTRES = 2, - VARF_ASSIGN = 4 -} Varf_Flags; + /* Treat undefined variables as errors. */ + VARE_UNDEFERR = 0x01, + /* Expand and evaluate variables during parsing. */ + VARE_WANTRES = 0x02, + VARE_ASSIGN = 0x04 +} VarEvalFlags; +typedef enum { + VAR_NO_EXPORT = 0x01, /* do not export */ + /* Make the variable read-only. No further modification is possible, + * except for another call to Var_Set with the same flag. */ + VAR_SET_READONLY = 0x02 +} VarSet_Flags; + + void Var_Delete(const char *, GNode *); void Var_Set(const char *, const char *, GNode *); +void Var_Set_with_flags(const char *, const char *, GNode *, VarSet_Flags); void Var_Append(const char *, const char *, GNode *); Boolean Var_Exists(const char *, GNode *); -char *Var_Value(const char *, GNode *, char **); -char *Var_Parse(const char *, GNode *, Varf_Flags, int *, void **); -char *Var_Subst(const char *, const char *, GNode *, Varf_Flags); -char *Var_GetTail(const char *); -char *Var_GetHead(const char *); +const char *Var_Value(const char *, GNode *, char **); +const char *Var_Parse(const char *, GNode *, VarEvalFlags, int *, void **); +char *Var_Subst(const char *, GNode *, VarEvalFlags); void Var_Init(void); void Var_End(void); +void Var_Stats(void); void Var_Dump(GNode *); void Var_ExportVars(void); -void Var_Export(char *, int); -void Var_UnExport(char *); +void Var_Export(const char *, Boolean); +void Var_UnExport(const char *); /* util.c */ void (*bmake_signal(int, void (*)(int)))(int); Index: head/contrib/bmake/os.sh =================================================================== --- head/contrib/bmake/os.sh (revision 365365) +++ head/contrib/bmake/os.sh (revision 365366) @@ -1,255 +1,258 @@ : # NAME: # os.sh - operating system specifics # # DESCRIPTION: # This file is included at the start of processing. Its role is # to set the variables OS, OSREL, OSMAJOR, MACHINE and MACHINE_ARCH to # reflect the current system. # # It also sets variables such as MAILER, LOCAL_FS, PS_AXC to hide # certain aspects of different UNIX flavours. # # SEE ALSO: # site.sh,funcs.sh # # AUTHOR: # Simon J. Gerraty # RCSid: -# $Id: os.sh,v 1.55 2017/12/11 20:31:41 sjg Exp $ +# $Id: os.sh,v 1.56 2020/08/05 23:25:22 sjg Exp $ # # @(#) Copyright (c) 1994 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 lets us skip sourcing it again _OS_SH=: OS=`uname` OSREL=`uname -r` OSMAJOR=`IFS=.; set $OSREL; echo $1` MACHINE=`uname -m` MACHINE_ARCH=`uname -p 2>/dev/null || echo $MACHINE` # there is at least one case of `uname -p` outputting # a bunch of usless drivel case "$MACHINE_ARCH" in unknown|*[!A-Za-z0-9_-]*) MACHINE_ARCH="$MACHINE";; esac # we need this here, and it is not always available... Which() { case "$1" in -*) t=$1; shift;; *) t=-x;; esac case "$1" in /*) test $t $1 && echo $1;; *) # some shells cannot correctly handle `IFS` # in conjunction with the for loop. _dirs=`IFS=:; echo ${2:-$PATH}` for d in $_dirs do test $t $d/$1 && { echo $d/$1; break; } done ;; esac } # tr is insanely non-portable wrt char classes, so we need to # spell out the alphabet. sed y/// would work too. toUpper() { ${TR:-tr} abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ } toLower() { ${TR:-tr} ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz } K= case $OS in AIX) # everyone loves to be different... OSMAJOR=`uname -v` OSREL="$OSMAJOR.`uname -r`" LOCAL_FS=jfs PS_AXC=-e SHARE_ARCH=$OS/$OSMAJOR.X ;; +Darwin) # a bit like BSD + HOST_ARCH=$MACHINE + ;; SunOS) CHOWN=`Which chown /usr/etc:/usr/bin` export CHOWN # Great! Solaris keeps moving arch(1) # should just bite the bullet and use uname -p arch=`Which arch /usr/bin:/usr/ucb` MAILER=/usr/ucb/Mail LOCAL_FS=4.2 case "$OSREL" in 4.0*) # uname -m just says sun which could be anything # so use arch(1). MACHINE_ARCH=`arch` MACHINE=$MACHINE_ARCH ;; 4*) MACHINE_ARCH=`arch` ;; 5*) K=-k LOCAL_FS=ufs MAILER=mailx PS_AXC=-e # can you believe that ln on Solaris defaults to # overwriting an existing file!!!!! We want one that works! test -x /usr/xpg4/bin/ln && LN=${LN:-/usr/xpg4/bin/ln} # wonderful, 5.8's tr again require's []'s # but /usr/xpg4/bin/tr causes problems if LC_COLLATE is set! # use toUpper/toLower instead. ;; esac case "$OS/$MACHINE_ARCH" in *sun386) SHARE_ARCH=$MACHINE_ARCH;; esac ;; *BSD) K=-k MAILER=/usr/bin/Mail LOCAL_FS=local : $-,$ENV case "$-,$ENV" in *i*,*) ;; *,|*ENVFILE*) ;; *) ENV=;; esac # NetBSD at least has good backward compatibility # so NetBSD/i386 is good enough case $OS in NetBSD) LOCALBASE=/usr/pkg HOST_ARCH=$MACHINE SHARE_ARCH=$OS/$HOST_ARCH ;; OpenBSD) arch=`Which arch /usr/bin:/usr/ucb:$PATH` MACHINE_ARCH=`$arch -s` ;; esac NAWK=awk export NAWK ;; HP-UX) TMP_DIRS="/tmp /usr/tmp" LOCAL_FS=hfs MAILER=mailx # don't rely on /bin/sh, its broken _shell=/bin/ksh; ENV= # also, no one would be interested in OSMAJOR=A case "$OSREL" in ?.09*) OSMAJOR=9; PS_AXC=-e;; ?.10*) OSMAJOR=10; PS_AXC=-e;; esac ;; IRIX) LOCAL_FS=efs ;; Interix) MACHINE=i386 MACHINE_ARCH=i386 ;; UnixWare) OSREL=`uname -v` OSMAJOR=`IFS=.; set $OSREL; echo $1` MACHINE_ARCH=`uname -m` ;; Linux) # Not really any such thing as Linux, but # this covers red-hat and hopefully others. case $MACHINE in i?86) MACHINE_ARCH=i386;; # we don't care about i686 vs i586 esac LOCAL_FS=ext2 PS_AXC=axc [ -x /usr/bin/md5sum ] && { MD5=/usr/bin/md5sum; export MD5; } ;; QNX) case $MACHINE in x86pc) MACHINE_ARCH=i386;; esac ;; Haiku) case $MACHINE in BeBox) MACHINE_ARCH=powerpc;; BeMac) MACHINE_ARCH=powerpc;; BePC) MACHINE_ARCH=i386;; esac ;; esac LOCALBASE=${LOCALBASE:-/usr/local} HOSTNAME=${HOSTNAME:-`( hostname ) 2>/dev/null`} HOSTNAME=${HOSTNAME:-`( uname -n ) 2>/dev/null`} case "$HOSTNAME" in *.*) HOST=`IFS=.; set -- $HOSTNAME; echo $1`;; *) HOST=$HOSTNAME;; esac TMP_DIRS=${TMP_DIRS:-"/tmp /var/tmp"} MACHINE_ARCH=${MACHINE_ARCH:-$MACHINE} -case "$MACHINE_ARCH" in +HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH} +case "$HOST_ARCH" in x86*64|amd64) MACHINE32_ARCH=i386;; *64) MACHINE32_ARCH=`echo $MACHINE_ARCH | sed 's,64,32,'`;; *) MACHINE32_ARCH=$MACHINE_ARCH;; esac -HOST_ARCH=${HOST_ARCH:-$MACHINE_ARCH} HOST_ARCH32=${HOST_ARCH32:-$MACHINE32_ARCH} # we mount server:/share/arch/$SHARE_ARCH as /usr/local SHARE_ARCH_DEFAULT=$OS/$OSMAJOR.X/$HOST_ARCH SHARE_ARCH=${SHARE_ARCH:-$SHARE_ARCH_DEFAULT} LN=${LN:-ln} TR=${TR:-tr} # Some people like have /share/$HOST_TARGET/bin etc. HOST_TARGET=`echo ${OS}${OSMAJOR}-$HOST_ARCH | tr -d / | toLower` HOST_TARGET32=`echo ${OS}${OSMAJOR}-$HOST_ARCH32 | tr -d / | toLower` export HOST_TARGET HOST_TARGET32 case `echo -n .` in -n*) N=; C="\c";; *) N=-n; C=;; esac Echo() { case "$1" in -n) _n=$N _c=$C; shift;; *) _n= _c=;; esac echo $_n "$@" $_c } export HOSTNAME HOST export OS MACHINE MACHINE_ARCH OSREL OSMAJOR LOCAL_FS TMP_DIRS MAILER N C K PS_AXC export LN SHARE_ARCH TR export LOCALBASE case /$0 in */os.sh) for v in $* do eval vv=\$$v echo "$v='$vv'" done ;; */host_target32) echo $HOST_TARGET32;; */host_target) echo $HOST_TARGET;; esac Index: head/contrib/bmake/parse.c =================================================================== --- head/contrib/bmake/parse.c (revision 365365) +++ head/contrib/bmake/parse.c (revision 365366) @@ -1,3372 +1,3376 @@ -/* $NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: parse.c,v 1.236 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: parse.c,v 1.275 2020/09/01 17:38:26 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * parse.c -- * Functions to parse a makefile. * * One function, Parse_Init, must be called before any functions * in this module are used. After that, the function Parse_File is the * main entry point and controls most of the other functions in this * module. * * Most important structures are kept in Lsts. Directories for * the .include "..." function are kept in the 'parseIncPath' Lst, while * those for the .include <...> are kept in the 'sysIncPath' Lst. The * targets currently being defined are kept in the 'targets' Lst. * * The variables 'fname' and 'lineno' are used to track the name * of the current file and the line number in that file so that error * messages can be more meaningful. * * Interface: * Parse_Init Initialization function which must be * called before anything else in this module * is used. * * Parse_End Cleanup the module * * Parse_File Function used to parse a makefile. It must * be given the name of the file, which should * already have been opened, and a function * to call to read a character from the file. * * Parse_IsVar Returns TRUE if the given line is a * variable assignment. Used by MainParseArgs * to determine if an argument is a target * or a variable assignment. Used internally * for pretty much the same thing... * * Parse_Error Function called when an error occurs in * parsing. Used by the variable and * conditional modules. * Parse_MainName Returns a Lst of the main target to create. */ #include #include -#include -#include #include #include #include #include "make.h" -#include "hash.h" #include "dir.h" #include "job.h" -#include "buf.h" #include "pathnames.h" #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_MMAP #include #ifndef MAP_COPY #define MAP_COPY MAP_PRIVATE #endif #ifndef MAP_FILE #define MAP_FILE 0 #endif #endif -//////////////////////////////////////////////////////////// -// types and constants +/* types and constants */ /* * Structure for a file being read ("included file") */ typedef struct IFile { char *fname; /* name of file */ int lineno; /* current line number in file */ int first_lineno; /* line number of start of text */ int cond_depth; /* 'if' nesting when file opened */ Boolean depending; /* state of doing_depend on EOF */ char *P_str; /* point to base of string buffer */ char *P_ptr; /* point to next char of string buffer */ char *P_end; /* point to the end of string buffer */ char *(*nextbuf)(void *, size_t *); /* Function to get more data */ void *nextbuf_arg; /* Opaque arg for nextbuf() */ struct loadedfile *lf; /* loadedfile object, if any */ } IFile; /* * These values are returned by ParseEOF to tell Parse_File whether to * CONTINUE parsing, i.e. it had only reached the end of an include file, * or if it's DONE. */ #define CONTINUE 1 #define DONE 0 /* * Tokens for target attributes */ typedef enum { Begin, /* .BEGIN */ Default, /* .DEFAULT */ DeleteOnError, /* .DELETE_ON_ERROR */ End, /* .END */ dotError, /* .ERROR */ Ignore, /* .IGNORE */ Includes, /* .INCLUDES */ Interrupt, /* .INTERRUPT */ Libs, /* .LIBS */ Meta, /* .META */ MFlags, /* .MFLAGS or .MAKEFLAGS */ Main, /* .MAIN and we don't have anything user-specified to * make */ NoExport, /* .NOEXPORT */ NoMeta, /* .NOMETA */ NoMetaCmp, /* .NOMETA_CMP */ NoPath, /* .NOPATH */ Not, /* Not special */ NotParallel, /* .NOTPARALLEL */ Null, /* .NULL */ ExObjdir, /* .OBJDIR */ Order, /* .ORDER */ Parallel, /* .PARALLEL */ ExPath, /* .PATH */ Phony, /* .PHONY */ #ifdef POSIX Posix, /* .POSIX */ #endif Precious, /* .PRECIOUS */ ExShell, /* .SHELL */ Silent, /* .SILENT */ SingleShell, /* .SINGLESHELL */ Stale, /* .STALE */ Suffixes, /* .SUFFIXES */ Wait, /* .WAIT */ Attribute /* Generic attribute */ } ParseSpecial; /* * Other tokens */ #define LPAREN '(' #define RPAREN ')' -//////////////////////////////////////////////////////////// -// result data +/* result data */ /* * The main target to create. This is the first target on the first * dependency line in the first makefile. */ static GNode *mainNode; -//////////////////////////////////////////////////////////// -// eval state +/* eval state */ /* targets we're working on */ static Lst targets; #ifdef CLEANUP /* command lines for targets */ static Lst targCmds; #endif /* * specType contains the SPECial TYPE of the current target. It is * Not if the target is unspecial. If it *is* special, however, the children * are linked as children of the parent but not vice versa. This variable is * set in ParseDoDependency */ static ParseSpecial specType; /* * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER * seen, then set to each successive source on the line. */ static GNode *predecessor; -//////////////////////////////////////////////////////////// -// parser state +/* parser state */ /* true if currently in a dependency line or its commands */ static Boolean inLine; /* number of fatal errors */ static int fatals = 0; /* * Variables for doing includes */ /* current file being read */ static IFile *curFile; /* stack of IFiles generated by .includes */ static Lst includes; /* include paths (lists of directories) */ Lst parseIncPath; /* dirs for "..." includes */ Lst sysIncPath; /* dirs for <...> includes */ Lst defIncPath; /* default for sysIncPath */ -//////////////////////////////////////////////////////////// -// parser tables +/* parser tables */ /* * The parseKeywords table is searched using binary search when deciding * if a target or source is special. The 'spec' field is the ParseSpecial * type of the keyword ("Not" if the keyword isn't special as a target) while * the 'op' field is the operator to apply to the list of targets if the * keyword is used as a source ("0" if the keyword isn't special as a source) */ static const struct { const char *name; /* Name of keyword */ ParseSpecial spec; /* Type when used as a target */ int op; /* Operator when used as a source */ } parseKeywords[] = { { ".BEGIN", Begin, 0 }, { ".DEFAULT", Default, 0 }, { ".DELETE_ON_ERROR", DeleteOnError, 0 }, { ".END", End, 0 }, { ".ERROR", dotError, 0 }, { ".EXEC", Attribute, OP_EXEC }, { ".IGNORE", Ignore, OP_IGNORE }, { ".INCLUDES", Includes, 0 }, { ".INTERRUPT", Interrupt, 0 }, { ".INVISIBLE", Attribute, OP_INVISIBLE }, { ".JOIN", Attribute, OP_JOIN }, { ".LIBS", Libs, 0 }, { ".MADE", Attribute, OP_MADE }, { ".MAIN", Main, 0 }, { ".MAKE", Attribute, OP_MAKE }, { ".MAKEFLAGS", MFlags, 0 }, { ".META", Meta, OP_META }, { ".MFLAGS", MFlags, 0 }, { ".NOMETA", NoMeta, OP_NOMETA }, { ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, { ".NOPATH", NoPath, OP_NOPATH }, { ".NOTMAIN", Attribute, OP_NOTMAIN }, { ".NOTPARALLEL", NotParallel, 0 }, { ".NO_PARALLEL", NotParallel, 0 }, { ".NULL", Null, 0 }, { ".OBJDIR", ExObjdir, 0 }, { ".OPTIONAL", Attribute, OP_OPTIONAL }, { ".ORDER", Order, 0 }, { ".PARALLEL", Parallel, 0 }, { ".PATH", ExPath, 0 }, { ".PHONY", Phony, OP_PHONY }, #ifdef POSIX { ".POSIX", Posix, 0 }, #endif { ".PRECIOUS", Precious, OP_PRECIOUS }, { ".RECURSIVE", Attribute, OP_MAKE }, { ".SHELL", ExShell, 0 }, { ".SILENT", Silent, OP_SILENT }, { ".SINGLESHELL", SingleShell, 0 }, { ".STALE", Stale, 0 }, { ".SUFFIXES", Suffixes, 0 }, { ".USE", Attribute, OP_USE }, { ".USEBEFORE", Attribute, OP_USEBEFORE }, { ".WAIT", Wait, 0 }, }; -//////////////////////////////////////////////////////////// -// local functions +/* local functions */ static int ParseIsEscaped(const char *, const char *); static void ParseErrorInternal(const char *, size_t, int, const char *, ...) MAKE_ATTR_PRINTFLIKE(4,5); static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list) MAKE_ATTR_PRINTFLIKE(5, 0); static int ParseFindKeyword(const char *); static int ParseLinkSrc(void *, void *); static int ParseDoOp(void *, void *); static void ParseDoSrc(int, const char *); static int ParseFindMain(void *, void *); static int ParseAddDir(void *, void *); static int ParseClearPath(void *, void *); static void ParseDoDependency(char *); static int ParseAddCmd(void *, void *); static void ParseHasCommands(void *); static void ParseDoInclude(char *); static void ParseSetParseFile(const char *); static void ParseSetIncludedFile(void); #ifdef GMAKEEXPORT static void ParseGmakeExport(char *); #endif static int ParseEOF(void); static char *ParseReadLine(void); static void ParseFinishLine(void); static void ParseMark(GNode *); -//////////////////////////////////////////////////////////// -// file loader +/* file loader */ struct loadedfile { const char *path; /* name, for error reports */ char *buf; /* contents buffer */ size_t len; /* length of contents */ size_t maplen; /* length of mmap area, or 0 */ Boolean used; /* XXX: have we used the data yet */ }; /* * Constructor/destructor for loadedfile */ static struct loadedfile * loadedfile_create(const char *path) { struct loadedfile *lf; lf = bmake_malloc(sizeof(*lf)); - lf->path = (path == NULL ? "(stdin)" : path); + lf->path = path == NULL ? "(stdin)" : path; lf->buf = NULL; lf->len = 0; lf->maplen = 0; lf->used = FALSE; return lf; } static void loadedfile_destroy(struct loadedfile *lf) { if (lf->buf != NULL) { if (lf->maplen > 0) { #ifdef HAVE_MMAP munmap(lf->buf, lf->maplen); #endif } else { free(lf->buf); } } free(lf); } /* * nextbuf() operation for loadedfile, as needed by the weird and twisted * logic below. Once that's cleaned up, we can get rid of lf->used... */ static char * loadedfile_nextbuf(void *x, size_t *len) { struct loadedfile *lf = x; if (lf->used) { return NULL; } lf->used = TRUE; *len = lf->len; return lf->buf; } /* * Try to get the size of a file. */ -static ReturnStatus +static Boolean load_getsize(int fd, size_t *ret) { struct stat st; if (fstat(fd, &st) < 0) { - return FAILURE; + return FALSE; } if (!S_ISREG(st.st_mode)) { - return FAILURE; + return FALSE; } /* * st_size is an off_t, which is 64 bits signed; *ret is * size_t, which might be 32 bits unsigned or 64 bits * unsigned. Rather than being elaborate, just punt on * files that are more than 2^31 bytes. We should never * see a makefile that size in practice... * * While we're at it reject negative sizes too, just in case. */ if (st.st_size < 0 || st.st_size > 0x7fffffff) { - return FAILURE; + return FALSE; } *ret = (size_t) st.st_size; - return SUCCESS; + return TRUE; } /* * Read in a file. * * Until the path search logic can be moved under here instead of * being in the caller in another source file, we need to have the fd * passed in already open. Bleh. * * If the path is NULL use stdin and (to insure against fd leaks) * assert that the caller passed in -1. */ static struct loadedfile * loadfile(const char *path, int fd) { struct loadedfile *lf; #ifdef HAVE_MMAP static long pagesize = 0; #endif ssize_t result; size_t bufpos; lf = loadedfile_create(path); if (path == NULL) { assert(fd == -1); fd = STDIN_FILENO; } else { #if 0 /* notyet */ fd = open(path, O_RDONLY); if (fd < 0) { ... Error("%s: %s", path, strerror(errno)); exit(1); } #endif } #ifdef HAVE_MMAP - if (load_getsize(fd, &lf->len) == SUCCESS) { + if (load_getsize(fd, &lf->len)) { /* found a size, try mmap */ #ifdef _SC_PAGESIZE if (pagesize == 0) pagesize = sysconf(_SC_PAGESIZE); #endif if (pagesize <= 0) { pagesize = 0x1000; } /* round size up to a page */ lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize); /* * XXX hack for dealing with empty files; remove when * we're no longer limited by interfacing to the old * logic elsewhere in this file. */ if (lf->maplen == 0) { lf->maplen = pagesize; } /* * FUTURE: remove PROT_WRITE when the parser no longer * needs to scribble on the input. */ lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, MAP_FILE|MAP_COPY, fd, 0); if (lf->buf != MAP_FAILED) { /* succeeded */ if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { char *b = bmake_malloc(lf->len + 1); b[lf->len] = '\n'; memcpy(b, lf->buf, lf->len++); munmap(lf->buf, lf->maplen); lf->maplen = 0; lf->buf = b; } goto done; } } #endif /* cannot mmap; load the traditional way */ lf->maplen = 0; lf->len = 1024; lf->buf = bmake_malloc(lf->len); bufpos = 0; while (1) { assert(bufpos <= lf->len); if (bufpos == lf->len) { if (lf->len > SIZE_MAX/2) { errno = EFBIG; Error("%s: file too large", path); exit(1); } lf->len *= 2; lf->buf = bmake_realloc(lf->buf, lf->len); } assert(bufpos < lf->len); result = read(fd, lf->buf + bufpos, lf->len - bufpos); if (result < 0) { Error("%s: read error: %s", path, strerror(errno)); exit(1); } if (result == 0) { break; } bufpos += result; } assert(bufpos <= lf->len); lf->len = bufpos; /* truncate malloc region to actual length (maybe not useful) */ if (lf->len > 0) { /* as for mmap case, ensure trailing \n */ if (lf->buf[lf->len - 1] != '\n') lf->len++; lf->buf = bmake_realloc(lf->buf, lf->len); lf->buf[lf->len - 1] = '\n'; } #ifdef HAVE_MMAP done: #endif if (path != NULL) { close(fd); } return lf; } -//////////////////////////////////////////////////////////// -// old code +/* old code */ /*- *---------------------------------------------------------------------- * ParseIsEscaped -- * Check if the current character is escaped on the current line * * Results: * 0 if the character is not backslash escaped, 1 otherwise * * Side Effects: * None *---------------------------------------------------------------------- */ static int ParseIsEscaped(const char *line, const char *c) { int active = 0; for (;;) { if (line == c) return active; if (*--c != '\\') return active; active = !active; } } /*- *---------------------------------------------------------------------- * ParseFindKeyword -- * Look in the table of keywords for one matching the given string. * * Input: * str String to find * * Results: * The index of the keyword, or -1 if it isn't there. * * Side Effects: * None *---------------------------------------------------------------------- */ static int ParseFindKeyword(const char *str) { int start, end, cur; int diff; start = 0; end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; do { cur = start + ((end - start) / 2); diff = strcmp(str, parseKeywords[cur].name); if (diff == 0) { return cur; } else if (diff < 0) { end = cur - 1; } else { start = cur + 1; } } while (start <= end); return -1; } /*- * ParseVErrorInternal -- * Error message abort function for parsing. Prints out the context * of the error (line number and file) as well as the message with * two optional arguments. * * Results: * None * * Side Effects: * "fatals" is incremented if the level is PARSE_FATAL. */ /* VARARGS */ static void 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); if (cfname != NULL) { (void)fprintf(f, "\""); if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) { char *cp, *cp2; const char *dir, *fname; /* * Nothing is more annoying than not knowing * which Makefile is the culprit; we try ${.PARSEDIR} * and apply realpath(3) if not absolute. */ dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp); if (dir == NULL) dir = "."; if (*dir != '/') { dir = realpath(dir, dirbuf); } fname = Var_Value(".PARSEFILE", VAR_GLOBAL, &cp2); if (fname == NULL) { if ((fname = strrchr(cfname, '/'))) fname++; else fname = cfname; } (void)fprintf(f, "%s/%s", dir, fname); - free(cp2); - free(cp); + bmake_free(cp2); + bmake_free(cp); } else (void)fprintf(f, "%s", cfname); (void)fprintf(f, "\" line %d: ", (int)clineno); } if (type == PARSE_WARNING) (void)fprintf(f, "warning: "); (void)vfprintf(f, fmt, ap); (void)fprintf(f, "\n"); (void)fflush(f); if (type == PARSE_INFO) return; if (type == PARSE_FATAL || parseWarnFatal) fatals += 1; if (parseWarnFatal && !fatal_warning_error_printed) { Error("parsing warnings being treated as errors"); fatal_warning_error_printed = TRUE; } } /*- * ParseErrorInternal -- * Error function * * Results: * None * * Side Effects: * None */ /* VARARGS */ static void ParseErrorInternal(const char *cfname, size_t clineno, int type, const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)fflush(stdout); ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); va_end(ap); if (debug_file != stderr && debug_file != stdout) { va_start(ap, fmt); ParseVErrorInternal(debug_file, cfname, clineno, type, fmt, ap); va_end(ap); } } /*- * Parse_Error -- * External interface to ParseErrorInternal; uses the default filename * Line number. * * Results: * None * * Side Effects: * None */ /* VARARGS */ void Parse_Error(int type, const char *fmt, ...) { va_list ap; const char *fname; size_t lineno; if (curFile == NULL) { fname = NULL; lineno = 0; } else { fname = curFile->fname; lineno = curFile->lineno; } va_start(ap, fmt); (void)fflush(stdout); ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); va_end(ap); if (debug_file != stderr && debug_file != stdout) { va_start(ap, fmt); ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap); va_end(ap); } } /* * ParseMessage * Parse a .info .warning or .error directive * * The input is the line minus the ".". We substitute * variables, print the message and exit(1) (for .error) or just print * a warning if the directive is malformed. */ static Boolean ParseMessage(char *line) { int mtype; switch(*line) { case 'i': mtype = PARSE_INFO; break; case 'w': mtype = PARSE_WARNING; break; case 'e': mtype = PARSE_FATAL; break; default: Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line); return FALSE; } while (isalpha((unsigned char)*line)) line++; if (!isspace((unsigned char)*line)) return FALSE; /* not for us */ while (isspace((unsigned char)*line)) line++; - line = Var_Subst(NULL, line, VAR_CMD, VARF_WANTRES); + line = Var_Subst(line, VAR_CMD, VARE_WANTRES); Parse_Error(mtype, "%s", line); free(line); if (mtype == PARSE_FATAL) { /* Terminate immediately. */ exit(1); } return TRUE; } /*- *--------------------------------------------------------------------- * ParseLinkSrc -- * Link the parent node to its new child. Used in a Lst_ForEach by * ParseDoDependency. If the specType isn't 'Not', the parent * isn't linked as a parent of the child. * * Input: * pgnp The parent node * cgpn The child node * * Results: * Always = 0 * * Side Effects: * New elements are added to the parents list of cgn and the * children list of cgn. the unmade field of pgn is updated * to reflect the additional child. *--------------------------------------------------------------------- */ static int ParseLinkSrc(void *pgnp, void *cgnp) { GNode *pgn = (GNode *)pgnp; GNode *cgn = (GNode *)cgnp; - if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) - pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts)); - (void)Lst_AtEnd(pgn->children, cgn); + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(pgn->cohorts)) + pgn = LstNode_Datum(Lst_Last(pgn->cohorts)); + Lst_Append(pgn->children, cgn); if (specType == Not) - (void)Lst_AtEnd(cgn->parents, pgn); + Lst_Append(cgn->parents, pgn); pgn->unmade += 1; if (DEBUG(PARSE)) { fprintf(debug_file, "# %s: added child %s - %s\n", __func__, pgn->name, cgn->name); Targ_PrintNode(pgn, 0); Targ_PrintNode(cgn, 0); } return 0; } /*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Lst_ForEach call by ParseDoDependency once all targets have * been found and their operator parsed. If the previous and new * operators are incompatible, a major error is taken. * * Input: * gnp The node to which the operator is to be applied * opp The operator to apply * * Results: * Always 0 * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */ static int ParseDoOp(void *gnp, void *opp) { GNode *gn = (GNode *)gnp; int op = *(int *)opp; /* * If the dependency mask of the operator and the node don't match and * the node has actually had an operator applied to it before, and * the operator actually has some dependency information in it, complain. */ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && !OP_NOP(gn->type) && !OP_NOP(op)) { Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); return 1; } - if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { + if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) { /* * If the node was the object of a :: operator, we need to create a * new instance of it for the children and commands on this dependency * line. The new instance is placed on the 'cohorts' list of the * initial one (note the initial one is not on its own cohorts list) * and the new instance is linked to all parents of the initial * instance. */ GNode *cohort; /* * Propagate copied bits to the initial node. They'll be propagated * back to the rest of the cohorts later. */ gn->type |= op & ~OP_OPMASK; cohort = Targ_FindNode(gn->name, TARG_NOHASH); if (doing_depend) ParseMark(cohort); /* * Make the cohort invisible as well to avoid duplicating it into * other variables. True, parents of this target won't tend to do * anything with their local variables, but better safe than * sorry. (I think this is pointless now, since the relevant list * traversals will no longer see this node anyway. -mycroft) */ cohort->type = op | OP_INVISIBLE; - (void)Lst_AtEnd(gn->cohorts, cohort); + Lst_Append(gn->cohorts, cohort); cohort->centurion = gn; gn->unmade_cohorts += 1; snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", gn->unmade_cohorts); } else { /* * We don't want to nuke any previous flags (whatever they were) so we * just OR the new operator into the old */ gn->type |= op; } return 0; } /*- *--------------------------------------------------------------------- * ParseDoSrc -- * Given the name of a source, figure out if it is an attribute * and apply it to the targets if it is. Else decide if there is * some attribute which should be applied *to* the source because * of some special target and apply it if so. Otherwise, make the * source be a child of the targets in the list 'targets' * * Input: * tOp operator (if any) from special targets * src name of the source to handle * * Results: * None * * Side Effects: * Operator bits may be added to the list of targets or to the source. * The targets may have a new source added to their lists of children. *--------------------------------------------------------------------- */ static void ParseDoSrc(int tOp, const char *src) { GNode *gn = NULL; static int wait_number = 0; char wait_src[16]; if (*src == '.' && isupper ((unsigned char)src[1])) { int keywd = ParseFindKeyword(src); if (keywd != -1) { int op = parseKeywords[keywd].op; if (op != 0) { - Lst_ForEach(targets, ParseDoOp, &op); + if (targets != NULL) + Lst_ForEach(targets, ParseDoOp, &op); return; } if (parseKeywords[keywd].spec == Wait) { /* * We add a .WAIT node in the dependency list. * After any dynamic dependencies (and filename globbing) * have happened, it is given a dependency on the each * previous child back to and previous .WAIT node. * The next child won't be scheduled until the .WAIT node * is built. * We give each .WAIT node a unique name (mainly for diag). */ snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); gn = Targ_FindNode(wait_src, TARG_NOHASH); if (doing_depend) ParseMark(gn); gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; - Lst_ForEach(targets, ParseLinkSrc, gn); + if (targets != NULL) + Lst_ForEach(targets, ParseLinkSrc, gn); return; } } } switch (specType) { case Main: /* * If we have noted the existence of a .MAIN, it means we need * to add the sources of said target to the list of things * to create. The string 'src' is likely to be free, so we * must make a new copy of it. Note that this will only be * invoked if the user didn't specify a target on the command * line. This is to allow #ifmake's to succeed, or something... */ - (void)Lst_AtEnd(create, bmake_strdup(src)); + Lst_Append(create, bmake_strdup(src)); /* * Add the name to the .TARGETS variable as well, so the user can * employ that, if desired. */ Var_Append(".TARGETS", src, VAR_GLOBAL); return; case Order: /* * Create proper predecessor/successor links between the previous * source and the current one. */ gn = Targ_FindNode(src, TARG_CREATE); if (doing_depend) ParseMark(gn); if (predecessor != NULL) { - (void)Lst_AtEnd(predecessor->order_succ, gn); - (void)Lst_AtEnd(gn->order_pred, predecessor); + Lst_Append(predecessor->order_succ, gn); + Lst_Append(gn->order_pred, predecessor); if (DEBUG(PARSE)) { fprintf(debug_file, "# %s: added Order dependency %s - %s\n", __func__, predecessor->name, gn->name); Targ_PrintNode(predecessor, 0); Targ_PrintNode(gn, 0); } } /* * The current source now becomes the predecessor for the next one. */ predecessor = gn; break; default: /* * If the source is not an attribute, we need to find/create * a node for it. After that we can apply any operator to it * from a special target or link it to its parents, as * appropriate. * * In the case of a source that was the object of a :: operator, * the attribute is applied to all of its instances (as kept in * the 'cohorts' list of the node) or all the cohorts are linked * to all the targets. */ /* Find/create the 'src' node and attach to all targets */ gn = Targ_FindNode(src, TARG_CREATE); if (doing_depend) ParseMark(gn); if (tOp) { gn->type |= tOp; } else { - Lst_ForEach(targets, ParseLinkSrc, gn); + if (targets != NULL) + Lst_ForEach(targets, ParseLinkSrc, gn); } break; } } /*- *----------------------------------------------------------------------- * ParseFindMain -- * Find a real target in the list and set it to be the main one. * Called by ParseDoDependency when a main target hasn't been found * yet. * * Input: * gnp Node to examine * * Results: * 0 if main not found yet, 1 if it is. * * Side Effects: * mainNode is changed and Targ_SetMain is called. * *----------------------------------------------------------------------- */ static int ParseFindMain(void *gnp, void *dummy MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; - if ((gn->type & OP_NOTARGET) == 0) { + if (!(gn->type & OP_NOTARGET)) { mainNode = gn; Targ_SetMain(gn); return 1; } else { return 0; } } /*- *----------------------------------------------------------------------- * ParseAddDir -- * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_AddDir. * *----------------------------------------------------------------------- */ static int ParseAddDir(void *path, void *name) { (void)Dir_AddDir((Lst) path, (char *)name); return 0; } /*- *----------------------------------------------------------------------- * ParseClearPath -- * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_ClearPath * *----------------------------------------------------------------------- */ static int ParseClearPath(void *path, void *dummy MAKE_ATTR_UNUSED) { Dir_ClearPath((Lst) path); return 0; } /*- *--------------------------------------------------------------------- * ParseDoDependency -- * Parse the dependency line in line. * * Input: * line the line to parse * * Results: * None * * Side Effects: * The nodes of the sources are linked as children to the nodes of the * targets. Some nodes may be created. * * We parse a dependency line by first extracting words from the line and * finding nodes in the list of all targets with that name. This is done * until a character is encountered which is an operator character. Currently * these are only ! and :. At this point the operator is parsed and the * pointer into the line advanced until the first source is encountered. * The parsed operator is applied to each node in the 'targets' list, * which is where the nodes found for the targets are kept, by means of * the ParseDoOp function. * The sources are read in much the same way as the targets were except * that now they are expanded using the wildcarding scheme of the C-Shell * and all instances of the resulting words in the list of all targets * are found. Each of the resulting nodes is then linked to each of the * targets as one of its children. * Certain targets are handled specially. These are the ones detailed * by the specType variable. * The storing of transformation rules is also taken care of here. * A target is recognized as a transformation rule by calling * Suff_IsTransform. If it is a transformation rule, its node is gotten * from the suffix module via Suff_AddTransform rather than the standard * Targ_FindNode in the target module. *--------------------------------------------------------------------- */ static void ParseDoDependency(char *line) { char *cp; /* our current position */ GNode *gn = NULL; /* a general purpose temporary node */ int op; /* the operator on the line */ char savec; /* a place to save a character */ Lst paths; /* List of search paths to alter when parsing * a list of .PATH targets */ int tOp; /* operator from special target */ Lst sources; /* list of archive source names after * expansion */ Lst curTargs; /* list of target names to be found and added * to the targets list */ char *lstart = line; if (DEBUG(PARSE)) fprintf(debug_file, "ParseDoDependency(%s)\n", line); tOp = 0; specType = Not; paths = NULL; - curTargs = Lst_Init(FALSE); + curTargs = Lst_Init(); /* * First, grind through the targets. */ do { /* * Here LINE points to the beginning of the next word, and * LSTART points to the actual beginning of the line. */ /* Find the end of the next word. */ for (cp = line; *cp && (ParseIsEscaped(lstart, cp) || !(isspace((unsigned char)*cp) || *cp == '!' || *cp == ':' || *cp == LPAREN)); cp++) { if (*cp == '$') { /* * Must be a dynamic source (would have been expanded * otherwise), so call the Var module to parse the puppy * so we can safely advance beyond it...There should be * no errors in this, as they would have been discovered * in the initial Var_Subst and we wouldn't be here. */ int length; void *freeIt; - (void)Var_Parse(cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES, + (void)Var_Parse(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES, &length, &freeIt); free(freeIt); - cp += length-1; + cp += length - 1; } } /* * If the word is followed by a left parenthesis, it's the * name of an object file inside an archive (ar file). */ if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) { /* * Archives must be handled specially to make sure the OP_ARCHV * flag is set in their 'type' field, for one thing, and because * things like "archive(file1.o file2.o file3.o)" are permissible. * Arch_ParseArchive will set 'line' to be the first non-blank * after the archive-spec. It creates/finds nodes for the members - * and places them on the given list, returning SUCCESS if all - * went well and FAILURE if there was an error in the + * and places them on the given list, returning TRUE if all + * went well and FALSE if there was an error in the * specification. On error, line should remain untouched. */ - if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) { + if (!Arch_ParseArchive(&line, targets, VAR_CMD)) { Parse_Error(PARSE_FATAL, "Error in archive specification: \"%s\"", line); goto out; } else { /* Done with this word; on to the next. */ cp = line; continue; } } if (!*cp) { /* * We got to the end of the line while we were still * looking at targets. * * Ending a dependency line without an operator is a Bozo * no-no. As a heuristic, this is also often triggered by * undetected conflicts from cvs/rcs merges. */ if ((strncmp(line, "<<<<<<", 6) == 0) || (strncmp(line, "======", 6) == 0) || (strncmp(line, ">>>>>>", 6) == 0)) Parse_Error(PARSE_FATAL, "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); - else - Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" - : "Need an operator"); + else if (lstart[0] == '.') { + const char *dirstart = lstart + 1; + const char *dirend; + while (isspace((unsigned char)*dirstart)) + dirstart++; + dirend = dirstart; + while (isalnum((unsigned char)*dirend) || *dirend == '-') + dirend++; + Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", + (int)(dirend - dirstart), dirstart); + } else + Parse_Error(PARSE_FATAL, "Need an operator"); goto out; } /* Insert a null terminator. */ savec = *cp; *cp = '\0'; /* * Got the word. See if it's a special target and if so set * specType to match it. */ if (*line == '.' && isupper ((unsigned char)line[1])) { /* * See if the target is a special target that must have it * or its sources handled specially. */ int keywd = ParseFindKeyword(line); if (keywd != -1) { if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { Parse_Error(PARSE_FATAL, "Mismatched special targets"); goto out; } specType = parseKeywords[keywd].spec; tOp = parseKeywords[keywd].op; /* * Certain special targets have special semantics: * .PATH Have to set the dirSearchPath * variable too * .MAIN Its sources are only used if * nothing has been specified to * create. * .DEFAULT Need to create a node to hang * commands on, but we don't want * it in the graph, nor do we want * it to be the Main Target, so we * create it, set OP_NOTMAIN and * add it to the list, setting * DEFAULT to the new node for * later use. We claim the node is * A transformation rule to make * life easier later, when we'll * use Make_HandleUse to actually * apply the .DEFAULT commands. * .PHONY The list of targets * .NOPATH Don't search for file in the path * .STALE * .BEGIN * .END * .ERROR * .DELETE_ON_ERROR * .INTERRUPT Are not to be considered the * main target. * .NOTPARALLEL Make only one target at a time. * .SINGLESHELL Create a shell for each command. * .ORDER Must set initial predecessor to NULL */ switch (specType) { case ExPath: if (paths == NULL) { - paths = Lst_Init(FALSE); + paths = Lst_Init(); } - (void)Lst_AtEnd(paths, dirSearchPath); + Lst_Append(paths, dirSearchPath); break; case Main: if (!Lst_IsEmpty(create)) { specType = Not; } break; case Begin: case End: case Stale: case dotError: case Interrupt: gn = Targ_FindNode(line, TARG_CREATE); if (doing_depend) ParseMark(gn); gn->type |= OP_NOTMAIN|OP_SPECIAL; - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); break; case Default: gn = Targ_NewGN(".DEFAULT"); gn->type |= (OP_NOTMAIN|OP_TRANSFORM); - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); DEFAULT = gn; break; case DeleteOnError: deleteOnError = TRUE; break; case NotParallel: maxJobs = 1; break; case SingleShell: compatMake = TRUE; break; case Order: predecessor = NULL; break; default: break; } } else if (strncmp(line, ".PATH", 5) == 0) { /* * .PATH has to be handled specially. * Call on the suffix module to give us a path to * modify. */ Lst path; specType = ExPath; path = Suff_GetPath(&line[5]); if (path == NULL) { Parse_Error(PARSE_FATAL, "Suffix '%s' not defined (yet)", &line[5]); goto out; } else { if (paths == NULL) { - paths = Lst_Init(FALSE); + paths = Lst_Init(); } - (void)Lst_AtEnd(paths, path); + Lst_Append(paths, path); } } } /* * Have word in line. Get or create its node and stick it at * the end of the targets list */ - if ((specType == Not) && (*line != '\0')) { + if (specType == Not && *line != '\0') { if (Dir_HasWildcards(line)) { /* * Targets are to be sought only in the current directory, * so create an empty path for the thing. Note we need to * use Dir_Destroy in the destruction of the path as the * Dir module could have added a directory to the path... */ - Lst emptyPath = Lst_Init(FALSE); + Lst emptyPath = Lst_Init(); Dir_Expand(line, emptyPath, curTargs); Lst_Destroy(emptyPath, Dir_Destroy); } else { /* * No wildcards, but we want to avoid code duplication, * so create a list with the word on it. */ - (void)Lst_AtEnd(curTargs, line); + Lst_Append(curTargs, line); } /* Apply the targets. */ while(!Lst_IsEmpty(curTargs)) { - char *targName = (char *)Lst_DeQueue(curTargs); + char *targName = Lst_Dequeue(curTargs); if (!Suff_IsTransform (targName)) { gn = Targ_FindNode(targName, TARG_CREATE); } else { gn = Suff_AddTransform(targName); } if (doing_depend) ParseMark(gn); - (void)Lst_AtEnd(targets, gn); + Lst_Append(targets, gn); } } else if (specType == ExPath && *line != '.' && *line != '\0') { Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); } /* Don't need the inserted null terminator any more. */ *cp = savec; /* * If it is a special type and not .PATH, it's the only target we * allow on this line... */ if (specType != Not && specType != ExPath) { Boolean warning = FALSE; while (*cp && (ParseIsEscaped(lstart, cp) || - ((*cp != '!') && (*cp != ':')))) { + (*cp != '!' && *cp != ':'))) { if (ParseIsEscaped(lstart, cp) || (*cp != ' ' && *cp != '\t')) { warning = TRUE; } cp++; } if (warning) { Parse_Error(PARSE_WARNING, "Extra target ignored"); } } else { while (*cp && isspace ((unsigned char)*cp)) { cp++; } } line = cp; } while (*line && (ParseIsEscaped(lstart, line) || - ((*line != '!') && (*line != ':')))); + (*line != '!' && *line != ':'))); /* * Don't need the list of target names anymore... */ - Lst_Destroy(curTargs, NULL); + Lst_Free(curTargs); curTargs = NULL; - if (!Lst_IsEmpty(targets)) { + if (targets != NULL && !Lst_IsEmpty(targets)) { switch(specType) { default: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); break; case Default: case Stale: case Begin: case End: case dotError: case Interrupt: /* * These four create nodes on which to hang commands, so * targets shouldn't be empty... */ case Not: /* * Nothing special here -- targets can be empty if it wants. */ break; } } /* * Have now parsed all the target names. Must parse the operator next. The * result is left in op . */ if (*cp == '!') { op = OP_FORCE; } else if (*cp == ':') { if (cp[1] == ':') { op = OP_DOUBLEDEP; cp++; } else { op = OP_DEPENDS; } } else { Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" : "Missing dependency operator"); goto out; } /* Advance beyond the operator */ cp++; /* * Apply the operator to the target. This is how we remember which * operator a target was defined with. It fails if the operator * used isn't consistent across all references. */ - Lst_ForEach(targets, ParseDoOp, &op); + if (targets != NULL) + Lst_ForEach(targets, ParseDoOp, &op); /* * Onward to the sources. * * LINE will now point to the first source word, if any, or the * end of the string if not. */ while (*cp && isspace ((unsigned char)*cp)) { cp++; } line = cp; /* * Several special targets take different actions if present with no * sources: * a .SUFFIXES line with no sources clears out all old suffixes * a .PRECIOUS line makes all targets precious * a .IGNORE line ignores errors for all targets * a .SILENT line creates silence when making all targets * a .PATH removes all directories from the search path(s). */ if (!*line) { switch (specType) { case Suffixes: Suff_ClearSuffixes(); break; case Precious: allPrecious = TRUE; break; case Ignore: ignoreErrors = TRUE; break; case Silent: beSilent = TRUE; break; case ExPath: - Lst_ForEach(paths, ParseClearPath, NULL); + if (paths != NULL) + Lst_ForEach(paths, ParseClearPath, NULL); Dir_SetPATH(); break; #ifdef POSIX - case Posix: - Var_Set("%POSIX", "1003.2", VAR_GLOBAL); - break; + case Posix: + Var_Set("%POSIX", "1003.2", VAR_GLOBAL); + break; #endif default: break; } } else if (specType == MFlags) { /* * Call on functions in main.c to deal with these arguments and * set the initial character to a null-character so the loop to * get sources won't get anything */ Main_ParseArgLine(line); *line = '\0'; } else if (specType == ExShell) { - if (Job_ParseShell(line) != SUCCESS) { + if (!Job_ParseShell(line)) { Parse_Error(PARSE_FATAL, "improper shell specification"); goto out; } *line = '\0'; - } else if ((specType == NotParallel) || (specType == SingleShell) || - (specType == DeleteOnError)) { + } else if (specType == NotParallel || specType == SingleShell || + specType == DeleteOnError) { *line = '\0'; } /* * NOW GO FOR THE SOURCES */ - if ((specType == Suffixes) || (specType == ExPath) || - (specType == Includes) || (specType == Libs) || - (specType == Null) || (specType == ExObjdir)) + if (specType == Suffixes || specType == ExPath || + specType == Includes || specType == Libs || + specType == Null || specType == ExObjdir) { while (*line) { /* * If the target was one that doesn't take files as its sources * but takes something like suffixes, we take each * space-separated word on the line as a something and deal * with it accordingly. * * If the target was .SUFFIXES, we take each source as a * suffix and add it to the list of suffixes maintained by the * Suff module. * * If the target was a .PATH, we add the source as a directory * to search on the search path. * * If it was .INCLUDES, the source is taken to be the suffix of * files which will be #included and whose search path should * be present in the .INCLUDES variable. * * If it was .LIBS, the source is taken to be the suffix of * files which are considered libraries and whose search path * should be present in the .LIBS variable. * * If it was .NULL, the source is the suffix to use when a file * has no valid suffix. * * If it was .OBJDIR, the source is a new definition for .OBJDIR, * and will cause make to do a new chdir to that path. */ while (*cp && !isspace ((unsigned char)*cp)) { cp++; } savec = *cp; *cp = '\0'; switch (specType) { case Suffixes: Suff_AddSuffix(line, &mainNode); break; case ExPath: - Lst_ForEach(paths, ParseAddDir, line); + if (paths != NULL) + Lst_ForEach(paths, ParseAddDir, line); break; case Includes: Suff_AddInclude(line); break; case Libs: Suff_AddLib(line); break; case Null: Suff_SetNull(line); break; case ExObjdir: Main_SetObjdir("%s", line); break; default: break; } *cp = savec; if (savec != '\0') { cp++; } while (*cp && isspace ((unsigned char)*cp)) { cp++; } line = cp; } if (paths) { - Lst_Destroy(paths, NULL); + Lst_Free(paths); paths = NULL; } if (specType == ExPath) Dir_SetPATH(); } else { assert(paths == NULL); while (*line) { /* * The targets take real sources, so we must beware of archive * specifications (i.e. things with left parentheses in them) * and handle them accordingly. */ for (; *cp && !isspace ((unsigned char)*cp); cp++) { - if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) { + if (*cp == LPAREN && cp > line && cp[-1] != '$') { /* * Only stop for a left parenthesis if it isn't at the * start of a word (that'll be for variable changes * later) and isn't preceded by a dollar sign (a dynamic * source). */ break; } } if (*cp == LPAREN) { - sources = Lst_Init(FALSE); - if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) { + sources = Lst_Init(); + if (!Arch_ParseArchive(&line, sources, VAR_CMD)) { Parse_Error(PARSE_FATAL, "Error in source archive spec \"%s\"", line); goto out; } - while (!Lst_IsEmpty (sources)) { - gn = (GNode *)Lst_DeQueue(sources); + while (!Lst_IsEmpty(sources)) { + gn = Lst_Dequeue(sources); ParseDoSrc(tOp, gn->name); } - Lst_Destroy(sources, NULL); + Lst_Free(sources); cp = line; } else { if (*cp) { *cp = '\0'; cp += 1; } ParseDoSrc(tOp, line); } while (*cp && isspace ((unsigned char)*cp)) { cp++; } line = cp; } } - if (mainNode == NULL) { + if (mainNode == NULL && targets != NULL) { /* * If we have yet to decide on a main target to make, in the * absence of any user input, we want the first target on * the first dependency line that is actually a real target * (i.e. isn't a .USE or .EXEC rule) to be made. */ Lst_ForEach(targets, ParseFindMain, NULL); } out: - if (paths) - Lst_Destroy(paths, NULL); - if (curTargs) - Lst_Destroy(curTargs, NULL); + if (paths != NULL) + Lst_Free(paths); + if (curTargs != NULL) + Lst_Free(curTargs); } /*- *--------------------------------------------------------------------- * Parse_IsVar -- * Return TRUE if the passed line is a variable assignment. A variable * assignment consists of a single word followed by optional whitespace * followed by either a += or an = operator. * This function is used both by the Parse_File function and main when * parsing the command-line arguments. * * Input: * line the line to check * * Results: * TRUE if it is. FALSE if it ain't * * Side Effects: * none *--------------------------------------------------------------------- */ Boolean Parse_IsVar(char *line) { Boolean wasSpace = FALSE; /* set TRUE if found a space */ char ch; int level = 0; #define ISEQOPERATOR(c) \ (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) /* * Skip to variable name */ - for (;(*line == ' ') || (*line == '\t'); line++) - continue; + while (*line == ' ' || *line == '\t') + line++; /* Scan for one of the assignment operators outside a variable expansion */ while ((ch = *line++) != 0) { if (ch == '(' || ch == '{') { level++; continue; } if (ch == ')' || ch == '}') { level--; continue; } if (level != 0) continue; while (ch == ' ' || ch == '\t') { ch = *line++; wasSpace = TRUE; } #ifdef SUNSHCMD if (ch == ':' && strncmp(line, "sh", 2) == 0) { line += 2; continue; } #endif if (ch == '=') return TRUE; if (*line == '=' && ISEQOPERATOR(ch)) return TRUE; if (wasSpace) return FALSE; } return FALSE; } /*- *--------------------------------------------------------------------- * Parse_DoVar -- * Take the variable assignment in the passed line and do it in the * global context. * * Note: There is a lexical ambiguity with assignment modifier characters * in variable names. This routine interprets the character before the = * as a modifier. Therefore, an assignment like * C++=/usr/bin/CC * is interpreted as "C+ +=" instead of "C++ =". * * Input: * line a line guaranteed to be a variable assignment. * This reduces error checks * ctxt Context in which to do the assignment * * Results: * none * * Side Effects: * the variable structure of the given variable name is altered in the * global context. *--------------------------------------------------------------------- */ void Parse_DoVar(char *line, GNode *ctxt) { char *cp; /* pointer into line */ enum { VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL } type; /* Type of assignment */ char *opc; /* ptr to operator character to * null-terminate the variable name */ Boolean freeCp = FALSE; /* TRUE if cp needs to be freed, * i.e. if any variable expansion was * performed */ int depth; /* * Skip to variable name */ - while ((*line == ' ') || (*line == '\t')) { + while (*line == ' ' || *line == '\t') line++; - } /* * Skip to operator character, nulling out whitespace as we go * XXX Rather than counting () and {} we should look for $ and * then expand the variable. */ - for (depth = 0, cp = line + 1; depth > 0 || *cp != '='; cp++) { + for (depth = 0, cp = line; depth > 0 || *cp != '='; cp++) { if (*cp == '(' || *cp == '{') { depth++; continue; } if (*cp == ')' || *cp == '}') { depth--; continue; } if (depth == 0 && isspace ((unsigned char)*cp)) { *cp = '\0'; } } opc = cp-1; /* operator is the previous character */ *cp++ = '\0'; /* nuke the = */ /* * Check operator type */ switch (*opc) { case '+': type = VAR_APPEND; *opc = '\0'; break; case '?': /* * If the variable already has a value, we don't do anything. */ *opc = '\0'; if (Var_Exists(line, ctxt)) { return; } else { type = VAR_NORMAL; } break; case ':': type = VAR_SUBST; *opc = '\0'; break; case '!': type = VAR_SHELL; *opc = '\0'; break; default: #ifdef SUNSHCMD while (opc > line && *opc != ':') opc--; if (strncmp(opc, ":sh", 3) == 0) { type = VAR_SHELL; *opc = '\0'; break; } #endif type = VAR_NORMAL; break; } - while (isspace ((unsigned char)*cp)) { + while (isspace((unsigned char)*cp)) cp++; + + if (DEBUG(LINT)) { + if (type != VAR_SUBST && strchr(cp, '$') != NULL) { + /* sanity check now */ + char *cp2; + + cp2 = Var_Subst(cp, ctxt, VARE_ASSIGN); + free(cp2); + } } if (type == VAR_APPEND) { Var_Append(line, cp, ctxt); } else if (type == VAR_SUBST) { /* * Allow variables in the old value to be undefined, but leave their * invocation alone -- this is done by forcing oldVars to be false. * XXX: This can cause recursive variables, but that's not hard to do, * and this allows someone to do something like * * CFLAGS = $(.INCLUDES) * CFLAGS := -I.. $(CFLAGS) * * And not get an error. */ Boolean oldOldVars = oldVars; oldVars = FALSE; /* * make sure that we set the variable the first time to nothing * so that it gets substituted! */ if (!Var_Exists(line, ctxt)) Var_Set(line, "", ctxt); - cp = Var_Subst(NULL, cp, ctxt, VARF_WANTRES|VARF_ASSIGN); + cp = Var_Subst(cp, ctxt, VARE_WANTRES|VARE_ASSIGN); oldVars = oldOldVars; freeCp = TRUE; Var_Set(line, cp, ctxt); } else if (type == VAR_SHELL) { char *res; const char *error; if (strchr(cp, '$') != NULL) { /* * There's a dollar sign in the command, so perform variable * expansion on the whole thing. The resulting string will need - * freeing when we're done, so set freeCmd to TRUE. + * freeing when we're done. */ - cp = Var_Subst(NULL, cp, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); + cp = Var_Subst(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES); freeCp = TRUE; } res = Cmd_Exec(cp, &error); Var_Set(line, res, ctxt); free(res); if (error) Parse_Error(PARSE_WARNING, error, cp); } else { /* * Normal assignment -- just do it. */ Var_Set(line, cp, ctxt); } if (strcmp(line, MAKEOVERRIDES) == 0) Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ else if (strcmp(line, ".CURDIR") == 0) { /* * Somone is being (too?) clever... * Let's pretend they know what they are doing and * re-initialize the 'cur' Path. */ Dir_InitCur(cp); Dir_SetPATH(); } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) { Job_SetPrefix(); } else if (strcmp(line, MAKE_EXPORTED) == 0) { - Var_Export(cp, 0); + Var_Export(cp, FALSE); } if (freeCp) free(cp); } /* * ParseMaybeSubMake -- * Scan the command string to see if it a possible submake node * Input: * cmd the command to scan * Results: * TRUE if the command is possibly a submake, FALSE if not. */ static Boolean ParseMaybeSubMake(const char *cmd) { size_t i; static struct { const char *name; size_t len; } vals[] = { #define MKV(A) { A, sizeof(A) - 1 } MKV("${MAKE}"), MKV("${.MAKE}"), MKV("$(MAKE)"), MKV("$(.MAKE)"), MKV("make"), }; for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { char *ptr; if ((ptr = strstr(cmd, vals[i].name)) == NULL) continue; if ((ptr == cmd || !isalnum((unsigned char)ptr[-1])) && !isalnum((unsigned char)ptr[vals[i].len])) return TRUE; } return FALSE; } /*- * ParseAddCmd -- * Lst_ForEach function to add a command line to all targets * * Input: * gnp the node to which the command is to be added * cmd the command to add * * Results: * Always 0 * * Side Effects: * A new element is added to the commands list of the node, * and the node can be marked as a submake node if the command is * determined to be that. */ static int ParseAddCmd(void *gnp, void *cmd) { GNode *gn = (GNode *)gnp; /* Add to last (ie current) cohort for :: targets */ - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) - gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts)) + gn = LstNode_Datum(Lst_Last(gn->cohorts)); /* if target already supplied, ignore commands */ if (!(gn->type & OP_HAS_COMMANDS)) { - (void)Lst_AtEnd(gn->commands, cmd); + Lst_Append(gn->commands, cmd); if (ParseMaybeSubMake(cmd)) gn->type |= OP_SUBMAKE; ParseMark(gn); } else { #ifdef notyet /* XXX: We cannot do this until we fix the tree */ - (void)Lst_AtEnd(gn->commands, cmd); + Lst_Append(gn->commands, cmd); Parse_Error(PARSE_WARNING, "overriding commands for target \"%s\"; " "previous commands defined at %s: %d ignored", gn->name, gn->fname, gn->lineno); #else Parse_Error(PARSE_WARNING, "duplicate script for target \"%s\" ignored", gn->name); ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING, "using previous script for \"%s\" defined here", gn->name); #endif } return 0; } /*- *----------------------------------------------------------------------- * ParseHasCommands -- * Callback procedure for Parse_File when destroying the list of * targets on the last dependency line. Marks a target as already * having commands if it does, to keep from having shell commands * on multiple dependency lines. * * Input: * gnp Node to examine * * Results: * None * * Side Effects: * OP_HAS_COMMANDS may be set for the target. * *----------------------------------------------------------------------- */ static void ParseHasCommands(void *gnp) { GNode *gn = (GNode *)gnp; if (!Lst_IsEmpty(gn->commands)) { gn->type |= OP_HAS_COMMANDS; } } /*- *----------------------------------------------------------------------- * Parse_AddIncludeDir -- * Add a directory to the path searched for included makefiles * bracketed by double-quotes. Used by functions in main.c * * Input: * dir The name of the directory to add * * Results: * None. * * Side Effects: * The directory is appended to the list. * *----------------------------------------------------------------------- */ void Parse_AddIncludeDir(char *dir) { (void)Dir_AddDir(parseIncPath, dir); } /*- *--------------------------------------------------------------------- * ParseDoInclude -- * Push to another file. * * The input is the line minus the `.'. A file spec is a string * enclosed in <> or "". The former is looked for only in sysIncPath. * The latter in . and the directories specified by -I command line * options * * Results: * None * * Side Effects: * A structure is added to the includes Lst and readProc, lineno, * fname and curFILE are altered for the new file *--------------------------------------------------------------------- */ static void Parse_include_file(char *file, Boolean isSystem, Boolean depinc, int silent) { struct loadedfile *lf; char *fullname; /* full pathname of file */ char *newName; char *prefEnd, *incdir; int fd; int i; /* * Now we know the file's name and its search path, we attempt to * find the durn thing. A return of NULL indicates the file don't * exist. */ fullname = file[0] == '/' ? bmake_strdup(file) : NULL; if (fullname == NULL && !isSystem) { /* * Include files contained in double-quotes are first searched for * relative to the including file's location. We don't want to * cd there, of course, so we just tack on the old file's * leading path components and call Dir_FindFile to see if * we can locate the beast. */ incdir = bmake_strdup(curFile->fname); prefEnd = strrchr(incdir, '/'); if (prefEnd != NULL) { *prefEnd = '\0'; /* Now do lexical processing of leading "../" on the filename */ for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { prefEnd = strrchr(incdir + 1, '/'); if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) break; *prefEnd = '\0'; } - newName = str_concat(incdir, file + i, STR_ADDSLASH); + newName = str_concat3(incdir, "/", file + i); fullname = Dir_FindFile(newName, parseIncPath); if (fullname == NULL) fullname = Dir_FindFile(newName, dirSearchPath); free(newName); } free(incdir); if (fullname == NULL) { /* - * Makefile wasn't found in same directory as included makefile. + * Makefile wasn't found in same directory as included makefile. * Search for it first on the -I search path, * then on the .PATH search path, if not found in a -I directory. * If we have a suffix specific path we should use that. */ char *suff; Lst suffPath = NULL; if ((suff = strrchr(file, '.'))) { suffPath = Suff_GetPath(suff); if (suffPath != NULL) { fullname = Dir_FindFile(file, suffPath); } } if (fullname == NULL) { fullname = Dir_FindFile(file, parseIncPath); if (fullname == NULL) { fullname = Dir_FindFile(file, dirSearchPath); } } } } /* Looking for a system file or file still not found */ if (fullname == NULL) { /* * Look for it on the system path */ fullname = Dir_FindFile(file, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); } if (fullname == NULL) { if (!silent) Parse_Error(PARSE_FATAL, "Could not find %s", file); return; } /* Actually open the file... */ fd = open(fullname, O_RDONLY); if (fd == -1) { if (!silent) Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); free(fullname); return; } /* load it */ lf = loadfile(fullname, fd); ParseSetIncludedFile(); /* Start reading from this file next */ Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); curFile->lf = lf; if (depinc) doing_depend = depinc; /* only turn it on */ } static void ParseDoInclude(char *line) { char endc; /* the character which ends the file spec */ char *cp; /* current position in file spec */ - int silent = (*line != 'i') ? 1 : 0; + int silent = *line != 'i'; char *file = &line[7 + silent]; /* Skip to delimiter character so we know where to look */ while (*file == ' ' || *file == '\t') file++; if (*file != '"' && *file != '<') { Parse_Error(PARSE_FATAL, ".include filename must be delimited by '\"' or '<'"); return; } /* * Set the search path on which to find the include file based on the * characters which bracket its name. Angle-brackets imply it's * a system Makefile while double-quotes imply it's a user makefile */ if (*file == '<') { endc = '>'; } else { endc = '"'; } /* Skip to matching delimiter */ for (cp = ++file; *cp && *cp != endc; cp++) continue; if (*cp != endc) { Parse_Error(PARSE_FATAL, "Unclosed %cinclude filename. '%c' expected", '.', endc); return; } *cp = '\0'; /* * Substitute for any variables in the file name before trying to * find the thing. */ - file = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); + file = Var_Subst(file, VAR_CMD, VARE_WANTRES); - Parse_include_file(file, endc == '>', (*line == 'd'), silent); + Parse_include_file(file, endc == '>', *line == 'd', silent); free(file); } /*- *--------------------------------------------------------------------- * ParseSetIncludedFile -- * Set the .INCLUDEDFROMFILE variable to the contents of .PARSEFILE * and the .INCLUDEDFROMDIR variable to the contents of .PARSEDIR * * Results: * None * * Side Effects: * The .INCLUDEDFROMFILE variable is overwritten by the contents * of .PARSEFILE and the .INCLUDEDFROMDIR variable is overwriten * by the contents of .PARSEDIR *--------------------------------------------------------------------- */ static void ParseSetIncludedFile(void) { - char *pf, *fp = NULL; - char *pd, *dp = NULL; + const char *pf, *pd; + char *pf_freeIt, *pd_freeIt; - pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp); + pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &pf_freeIt); Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL); - pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp); + pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &pd_freeIt); Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL); if (DEBUG(PARSE)) fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' " "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf); - free(fp); - free(dp); + bmake_free(pf_freeIt); + bmake_free(pd_freeIt); } /*- *--------------------------------------------------------------------- * ParseSetParseFile -- * Set the .PARSEDIR and .PARSEFILE variables to the dirname and * basename of the given filename * * Results: * None * * Side Effects: * The .PARSEDIR and .PARSEFILE variables are overwritten by the * dirname and basename of the given filename. *--------------------------------------------------------------------- */ static void ParseSetParseFile(const char *filename) { char *slash, *dirname; const char *pd, *pf; - int len; slash = strrchr(filename, '/'); if (slash == NULL) { Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL); Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL); - dirname= NULL; + dirname = NULL; } else { - len = slash - filename; - dirname = bmake_malloc(len + 1); - memcpy(dirname, filename, len); - dirname[len] = '\0'; + dirname = bmake_strsedup(filename, slash); Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL); Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL); } if (DEBUG(PARSE)) fprintf(debug_file, "%s: ${.PARSEDIR} = `%s' ${.PARSEFILE} = `%s'\n", __func__, pd, pf); free(dirname); } /* * Track the makefiles we read - so makefiles can * set dependencies on them. * Avoid adding anything more than once. */ static void ParseTrackInput(const char *name) { - char *old; - char *ep; char *fp = NULL; - size_t name_len = strlen(name); - old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); + const char *old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); if (old) { - ep = old + strlen(old) - name_len; + size_t name_len = strlen(name); + const char *ep = old + strlen(old) - name_len; /* does it contain name? */ for (; old != NULL; old = strchr(old, ' ')) { if (*old == ' ') old++; if (old >= ep) break; /* cannot contain name */ if (memcmp(old, name, name_len) == 0 && (old[name_len] == 0 || old[name_len] == ' ')) goto cleanup; } } Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL); cleanup: - if (fp) { - free(fp); - } + bmake_free(fp); } /*- *--------------------------------------------------------------------- * Parse_setInput -- * Start Parsing from the given source * * Results: * None * * Side Effects: * A structure is added to the includes Lst and readProc, lineno, * fname and curFile are altered for the new file *--------------------------------------------------------------------- */ void Parse_SetInput(const char *name, int line, int fd, char *(*nextbuf)(void *, size_t *), void *arg) { char *buf; size_t len; if (name == NULL) name = curFile->fname; else ParseTrackInput(name); if (DEBUG(PARSE)) fprintf(debug_file, "%s: file %s, line %d, fd %d, nextbuf %p, arg %p\n", __func__, name, line, fd, nextbuf, arg); if (fd == -1 && nextbuf == NULL) /* sanity */ return; if (curFile != NULL) /* Save exiting file info */ - Lst_AtFront(includes, curFile); + Lst_Prepend(includes, curFile); /* Allocate and fill in new structure */ curFile = bmake_malloc(sizeof *curFile); /* * Once the previous state has been saved, we can get down to reading * the new file. We set up the name of the file to be the absolute * name of the include file so error messages refer to the right * place. */ curFile->fname = bmake_strdup(name); curFile->lineno = line; curFile->first_lineno = line; curFile->nextbuf = nextbuf; curFile->nextbuf_arg = arg; curFile->lf = NULL; curFile->depending = doing_depend; /* restore this on EOF */ assert(nextbuf != NULL); /* Get first block of input data */ buf = curFile->nextbuf(curFile->nextbuf_arg, &len); if (buf == NULL) { - /* Was all a waste of time ... */ + /* Was all a waste of time ... */ if (curFile->fname) free(curFile->fname); free(curFile); return; } curFile->P_str = buf; curFile->P_ptr = buf; curFile->P_end = buf+len; curFile->cond_depth = Cond_save_depth(); ParseSetParseFile(name); } /*- *----------------------------------------------------------------------- * IsInclude -- * Check if the line is an include directive * * Results: * TRUE if it is. * * Side Effects: * None * *----------------------------------------------------------------------- */ static Boolean IsInclude(const char *line, Boolean sysv) { static const char inc[] = "include"; static const size_t inclen = sizeof(inc) - 1; - // 'd' is not valid for sysv - int o = strchr(&("ds-"[sysv]), *line) != NULL; + /* 'd' is not valid for sysv */ + int o = strchr(sysv ? "s-" : "ds-", *line) != NULL; if (strncmp(line + o, inc, inclen) != 0) return FALSE; - // Space is not mandatory for BSD .include + /* Space is not mandatory for BSD .include */ return !sysv || isspace((unsigned char)line[inclen + o]); } #ifdef SYSVINCLUDE /*- *----------------------------------------------------------------------- * IsSysVInclude -- * Check if the line is a SYSV include directive * * Results: * TRUE if it is. * * Side Effects: * None * *----------------------------------------------------------------------- */ static Boolean IsSysVInclude(const char *line) { const char *p; if (!IsInclude(line, TRUE)) return FALSE; /* Avoid interpeting a dependency line as an include */ for (p = line; (p = strchr(p, ':')) != NULL;) { if (*++p == '\0') { /* end of line -> dependency */ return FALSE; } if (*p == ':' || isspace((unsigned char)*p)) { /* :: operator or ': ' -> dependency */ return FALSE; } } return TRUE; } /*- *--------------------------------------------------------------------- * ParseTraditionalInclude -- * Push to another file. * * The input is the current line. The file name(s) are * following the "include". * * Results: * None * * Side Effects: * A structure is added to the includes Lst and readProc, lineno, * fname and curFILE are altered for the new file *--------------------------------------------------------------------- */ static void ParseTraditionalInclude(char *line) { char *cp; /* current position in file spec */ int done = 0; - int silent = (line[0] != 'i') ? 1 : 0; + int silent = line[0] != 'i'; char *file = &line[silent + 7]; char *all_files; if (DEBUG(PARSE)) { fprintf(debug_file, "%s: %s\n", __func__, file); } /* * Skip over whitespace */ while (isspace((unsigned char)*file)) file++; /* * Substitute for any variables in the file name before trying to * find the thing. */ - all_files = Var_Subst(NULL, file, VAR_CMD, VARF_WANTRES); + all_files = Var_Subst(file, VAR_CMD, VARE_WANTRES); if (*file == '\0') { Parse_Error(PARSE_FATAL, "Filename missing from \"include\""); goto out; } for (file = all_files; !done; file = cp + 1) { /* Skip to end of line or next whitespace */ for (cp = file; *cp && !isspace((unsigned char) *cp); cp++) continue; if (*cp) *cp = '\0'; else done = 1; Parse_include_file(file, FALSE, FALSE, silent); } out: free(all_files); } #endif #ifdef GMAKEEXPORT /*- *--------------------------------------------------------------------- * ParseGmakeExport -- * Parse export = * * And set the environment with it. * * Results: * None * * Side Effects: * None *--------------------------------------------------------------------- */ static void ParseGmakeExport(char *line) { char *variable = &line[6]; char *value; if (DEBUG(PARSE)) { fprintf(debug_file, "%s: %s\n", __func__, variable); } /* * Skip over whitespace */ while (isspace((unsigned char)*variable)) variable++; for (value = variable; *value && *value != '='; value++) continue; if (*value != '=') { Parse_Error(PARSE_FATAL, "Variable/Value missing from \"export\""); return; } *value++ = '\0'; /* terminate variable */ /* * Expand the value before putting it in the environment. */ - value = Var_Subst(NULL, value, VAR_CMD, VARF_WANTRES); + value = Var_Subst(value, VAR_CMD, VARE_WANTRES); setenv(variable, value, 1); free(value); } #endif /*- *--------------------------------------------------------------------- * ParseEOF -- * Called when EOF is reached in the current file. If we were reading * an include file, the includes stack is popped and things set up * to go back to reading the previous file at the previous location. * * Results: * CONTINUE if there's more to do. DONE if not. * * Side Effects: * The old curFILE, is closed. The includes list is shortened. * lineno, curFILE, and fname are changed if CONTINUE is returned. *--------------------------------------------------------------------- */ static int ParseEOF(void) { char *ptr; size_t len; assert(curFile->nextbuf != NULL); doing_depend = curFile->depending; /* restore this */ /* get next input buffer, if any */ ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); curFile->P_ptr = ptr; curFile->P_str = ptr; curFile->P_end = ptr + len; curFile->lineno = curFile->first_lineno; if (ptr != NULL) { /* Iterate again */ return CONTINUE; } /* Ensure the makefile (or loop) didn't have mismatched conditionals */ Cond_restore_depth(curFile->cond_depth); if (curFile->lf != NULL) { loadedfile_destroy(curFile->lf); curFile->lf = NULL; } /* Dispose of curFile info */ /* Leak curFile->fname because all the gnodes have pointers to it */ free(curFile->P_str); free(curFile); - curFile = Lst_DeQueue(includes); - - if (curFile == NULL) { + if (Lst_IsEmpty(includes)) { + curFile = NULL; /* We've run out of input */ Var_Delete(".PARSEDIR", VAR_GLOBAL); Var_Delete(".PARSEFILE", VAR_GLOBAL); Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); return DONE; } + curFile = Lst_Dequeue(includes); if (DEBUG(PARSE)) fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", curFile->fname, curFile->lineno); /* Restore the PARSEDIR/PARSEFILE variables */ ParseSetParseFile(curFile->fname); return CONTINUE; } #define PARSE_RAW 1 #define PARSE_SKIP 2 static char * ParseGetLine(int flags, int *length) { IFile *cf = curFile; char *ptr; char ch; char *line; char *line_end; char *escaped; char *comment; char *tp; /* Loop through blank lines and comment lines */ for (;;) { cf->lineno++; line = cf->P_ptr; ptr = line; line_end = line; escaped = NULL; comment = NULL; for (;;) { if (cf->P_end != NULL && ptr == cf->P_end) { /* end of buffer */ ch = 0; break; } ch = *ptr; if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { if (cf->P_end == NULL) /* End of string (aka for loop) data */ break; /* see if there is more we can parse */ while (ptr++ < cf->P_end) { if ((ch = *ptr) == '\n') { if (ptr > line && ptr[-1] == '\\') continue; Parse_Error(PARSE_WARNING, "Zero byte read from file, skipping rest of line."); break; } } if (cf->nextbuf != NULL) { /* * End of this buffer; return EOF and outer logic * will get the next one. (eww) */ break; } Parse_Error(PARSE_FATAL, "Zero byte read from file"); return NULL; } if (ch == '\\') { /* Don't treat next character as special, remember first one */ if (escaped == NULL) escaped = ptr; if (ptr[1] == '\n') cf->lineno++; ptr += 2; line_end = ptr; continue; } if (ch == '#' && comment == NULL) { /* Remember first '#' for comment stripping */ /* Unless previous char was '[', as in modifier :[#] */ if (!(ptr > line && ptr[-1] == '[')) comment = line_end; } ptr++; if (ch == '\n') break; if (!isspace((unsigned char)ch)) /* We are not interested in trailing whitespace */ line_end = ptr; } /* Save next 'to be processed' location */ cf->P_ptr = ptr; /* Check we have a non-comment, non-blank line */ if (line_end == line || comment == line) { if (ch == 0) /* At end of file */ return NULL; /* Parse another line */ continue; } /* We now have a line of data */ *line_end = 0; if (flags & PARSE_RAW) { /* Leave '\' (etc) in line buffer (eg 'for' lines) */ *length = line_end - line; return line; } if (flags & PARSE_SKIP) { /* Completely ignore non-directives */ if (line[0] != '.') continue; /* We could do more of the .else/.elif/.endif checks here */ } break; } /* Brutally ignore anything after a non-escaped '#' in non-commands */ if (comment != NULL && line[0] != '\t') { line_end = comment; *line_end = 0; } /* If we didn't see a '\\' then the in-situ data is fine */ if (escaped == NULL) { *length = line_end - line; return line; } /* Remove escapes from '\n' and '#' */ tp = ptr = escaped; escaped = line; for (; ; *tp++ = ch) { ch = *ptr++; if (ch != '\\') { if (ch == 0) break; continue; } ch = *ptr++; if (ch == 0) { /* Delete '\\' at end of buffer */ tp--; break; } if (ch == '#' && line[0] != '\t') /* Delete '\\' from before '#' on non-command lines */ continue; if (ch != '\n') { /* Leave '\\' in buffer for later */ *tp++ = '\\'; /* Make sure we don't delete an escaped ' ' from the line end */ escaped = tp + 1; continue; } /* Escaped '\n' replace following whitespace with a single ' ' */ while (ptr[0] == ' ' || ptr[0] == '\t') ptr++; ch = ' '; } /* Delete any trailing spaces - eg from empty continuations */ while (tp > escaped && isspace((unsigned char)tp[-1])) tp--; *tp = 0; *length = tp - line; return line; } /*- *--------------------------------------------------------------------- * ParseReadLine -- * Read an entire line from the input file. Called only by Parse_File. * * Results: * A line w/o its newline * * Side Effects: * Only those associated with reading a character *--------------------------------------------------------------------- */ static char * ParseReadLine(void) { char *line; /* Result */ int lineLength; /* Length of result */ int lineno; /* Saved line # */ int rval; for (;;) { line = ParseGetLine(0, &lineLength); if (line == NULL) return NULL; if (line[0] != '.') return line; /* * The line might be a conditional. Ask the conditional module * about it and act accordingly */ switch (Cond_Eval(line)) { case COND_SKIP: /* Skip to next conditional that evaluates to COND_PARSE. */ do { line = ParseGetLine(PARSE_SKIP, &lineLength); } while (line && Cond_Eval(line) != COND_PARSE); if (line == NULL) break; continue; case COND_PARSE: continue; case COND_INVALID: /* Not a conditional line */ /* Check for .for loops */ rval = For_Eval(line); if (rval == 0) /* Not a .for line */ break; if (rval < 0) /* Syntax error - error printed, ignore line */ continue; /* Start of a .for loop */ lineno = curFile->lineno; /* Accumulate loop lines until matching .endfor */ do { line = ParseGetLine(PARSE_RAW, &lineLength); if (line == NULL) { Parse_Error(PARSE_FATAL, "Unexpected end of file in for loop."); break; } } while (For_Accum(line)); /* Stash each iteration as a new 'input file' */ For_Run(lineno); /* Read next line from for-loop buffer */ continue; } return line; } } /*- *----------------------------------------------------------------------- * ParseFinishLine -- * Handle the end of a dependency group. * * Results: * Nothing. * * Side Effects: * inLine set FALSE. 'targets' list destroyed. * *----------------------------------------------------------------------- */ static void ParseFinishLine(void) { if (inLine) { - Lst_ForEach(targets, Suff_EndTransform, NULL); - Lst_Destroy(targets, ParseHasCommands); + if (targets != NULL) { + Lst_ForEach(targets, Suff_EndTransform, NULL); + Lst_Destroy(targets, ParseHasCommands); + } targets = NULL; inLine = FALSE; } } /*- *--------------------------------------------------------------------- * Parse_File -- * Parse a file into its component parts, incorporating it into the * current dependency graph. This is the main function and controls * almost every other function in this module * * Input: * name the name of the file being read * fd Open file to makefile to parse * * Results: * None * * Side Effects: * closes fd. * Loads. Nodes are added to the list of all targets, nodes and links * are added to the dependency graph. etc. etc. etc. *--------------------------------------------------------------------- */ void Parse_File(const char *name, int fd) { char *cp; /* pointer into the line */ char *line; /* the line we're working on */ struct loadedfile *lf; lf = loadfile(name, fd); inLine = FALSE; fatals = 0; - if (name == NULL) { - name = "(stdin)"; - } + if (name == NULL) + name = "(stdin)"; Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); curFile->lf = lf; do { for (; (line = ParseReadLine()) != NULL; ) { if (DEBUG(PARSE)) fprintf(debug_file, "ParseReadLine (%d): '%s'\n", curFile->lineno, line); if (*line == '.') { /* * Lines that begin with the special character may be * include or undef directives. * On the other hand they can be suffix rules (.c.o: ...) * or just dependencies for filenames that start '.'. */ for (cp = line + 1; isspace((unsigned char)*cp); cp++) { continue; } if (IsInclude(cp, FALSE)) { ParseDoInclude(cp); continue; } if (strncmp(cp, "undef", 5) == 0) { char *cp2; for (cp += 5; isspace((unsigned char) *cp); cp++) continue; for (cp2 = cp; !isspace((unsigned char) *cp2) && - (*cp2 != '\0'); cp2++) + *cp2 != '\0'; cp2++) continue; *cp2 = '\0'; Var_Delete(cp, VAR_GLOBAL); continue; } else if (strncmp(cp, "export", 6) == 0) { for (cp += 6; isspace((unsigned char) *cp); cp++) continue; - Var_Export(cp, 1); + Var_Export(cp, TRUE); continue; } else if (strncmp(cp, "unexport", 8) == 0) { Var_UnExport(cp); continue; } else if (strncmp(cp, "info", 4) == 0 || strncmp(cp, "error", 5) == 0 || strncmp(cp, "warning", 7) == 0) { if (ParseMessage(cp)) continue; } } if (*line == '\t') { /* * If a line starts with a tab, it can only hope to be * a creation command. */ cp = line + 1; shellCommand: for (; isspace ((unsigned char)*cp); cp++) { continue; } if (*cp) { if (!inLine) Parse_Error(PARSE_FATAL, "Unassociated shell command \"%s\"", cp); /* * So long as it's not a blank line and we're actually * in a dependency spec, add the command to the list of * commands of all targets in the dependency spec */ if (targets) { cp = bmake_strdup(cp); Lst_ForEach(targets, ParseAddCmd, cp); #ifdef CLEANUP - Lst_AtEnd(targCmds, cp); + Lst_Append(targCmds, cp); #endif } } continue; } #ifdef SYSVINCLUDE if (IsSysVInclude(line)) { /* * It's an S3/S5-style "include". */ ParseTraditionalInclude(line); continue; } #endif #ifdef GMAKEEXPORT if (strncmp(line, "export", 6) == 0 && isspace((unsigned char) line[6]) && strchr(line, ':') == NULL) { /* * It's a Gmake "export". */ ParseGmakeExport(line); continue; } #endif if (Parse_IsVar(line)) { ParseFinishLine(); Parse_DoVar(line, VAR_GLOBAL); continue; } #ifndef POSIX /* * To make life easier on novices, if the line is indented we * first make sure the line has a dependency operator in it. * If it doesn't have an operator and we're in a dependency * line's script, we assume it's actually a shell command * and add it to the current list of targets. */ cp = line; if (isspace((unsigned char) line[0])) { - while ((*cp != '\0') && isspace((unsigned char) *cp)) + while (isspace((unsigned char) *cp)) cp++; while (*cp && (ParseIsEscaped(line, cp) || - (*cp != ':') && (*cp != '!'))) { + *cp != ':' && *cp != '!')) { cp++; } if (*cp == '\0') { if (inLine) { Parse_Error(PARSE_WARNING, "Shell command needs a leading tab"); goto shellCommand; } } } #endif ParseFinishLine(); /* * For some reason - probably to make the parser impossible - * a ';' can be used to separate commands from dependencies. * Attempt to avoid ';' inside substitution patterns. */ { int level = 0; for (cp = line; *cp != 0; cp++) { if (*cp == '\\' && cp[1] != 0) { cp++; continue; } if (*cp == '$' && (cp[1] == '(' || cp[1] == '{')) { level++; continue; } if (level > 0) { if (*cp == ')' || *cp == '}') { level--; continue; } } else if (*cp == ';') { break; } } } if (*cp != 0) /* Terminate the dependency list at the ';' */ *cp++ = 0; else cp = NULL; /* * We now know it's a dependency line so it needs to have all * variables expanded before being parsed. Tell the variable * module to complain if some variable is undefined... */ - line = Var_Subst(NULL, line, VAR_CMD, VARF_UNDEFERR|VARF_WANTRES); + line = Var_Subst(line, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES); /* * Need a non-circular list for the target nodes */ - if (targets) - Lst_Destroy(targets, NULL); + if (targets != NULL) + Lst_Free(targets); - targets = Lst_Init(FALSE); + targets = Lst_Init(); inLine = TRUE; ParseDoDependency(line); free(line); /* If there were commands after a ';', add them now */ if (cp != NULL) { goto shellCommand; } } /* * Reached EOF, but it may be just EOF of an include file... */ } while (ParseEOF() == CONTINUE); if (fatals) { (void)fflush(stdout); (void)fprintf(stderr, "%s: Fatal errors encountered -- cannot continue", progname); PrintOnError(NULL, NULL); exit(1); } } /*- *--------------------------------------------------------------------- * Parse_Init -- * initialize the parsing module * * Results: * none * * Side Effects: * the parseIncPath list is initialized... *--------------------------------------------------------------------- */ void Parse_Init(void) { mainNode = NULL; - parseIncPath = Lst_Init(FALSE); - sysIncPath = Lst_Init(FALSE); - defIncPath = Lst_Init(FALSE); - includes = Lst_Init(FALSE); + parseIncPath = Lst_Init(); + sysIncPath = Lst_Init(); + defIncPath = Lst_Init(); + includes = Lst_Init(); #ifdef CLEANUP - targCmds = Lst_Init(FALSE); + targCmds = Lst_Init(); #endif } void Parse_End(void) { #ifdef CLEANUP - Lst_Destroy(targCmds, (FreeProc *)free); + Lst_Destroy(targCmds, free); if (targets) - Lst_Destroy(targets, NULL); + Lst_Free(targets); Lst_Destroy(defIncPath, Dir_Destroy); Lst_Destroy(sysIncPath, Dir_Destroy); Lst_Destroy(parseIncPath, Dir_Destroy); - Lst_Destroy(includes, NULL); /* Should be empty now */ + Lst_Free(includes); /* Should be empty now */ #endif } /*- *----------------------------------------------------------------------- * Parse_MainName -- * Return a Lst of the main target to create for main()'s sake. If * no such target exists, we Punt with an obnoxious error message. * * Results: * A Lst of the single node to create. * * Side Effects: * None. * *----------------------------------------------------------------------- */ Lst Parse_MainName(void) { Lst mainList; /* result list */ - mainList = Lst_Init(FALSE); + mainList = Lst_Init(); if (mainNode == NULL) { Punt("no target to make."); - /*NOTREACHED*/ + /*NOTREACHED*/ } else if (mainNode->type & OP_DOUBLEDEP) { - (void)Lst_AtEnd(mainList, mainNode); - Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); + Lst_Append(mainList, mainNode); + Lst_AppendAll(mainList, mainNode->cohorts); } else - (void)Lst_AtEnd(mainList, mainNode); + Lst_Append(mainList, mainNode); Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); return mainList; } /*- *----------------------------------------------------------------------- * ParseMark -- * Add the filename and lineno to the GNode so that we remember * where it was first defined. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static void ParseMark(GNode *gn) { gn->fname = curFile->fname; gn->lineno = curFile->lineno; } Index: head/contrib/bmake/str.c =================================================================== --- head/contrib/bmake/str.c (revision 365365) +++ head/contrib/bmake/str.c (revision 365366) @@ -1,529 +1,424 @@ -/* $NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $ */ +/* $NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $ */ /*- * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /*- * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $"; +static char rcsid[] = "$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; #else -__RCSID("$NetBSD: str.c,v 1.51 2020/07/03 07:40:13 rillig Exp $"); +__RCSID("$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $"); #endif #endif /* not lint */ #endif #include "make.h" -/*- - * str_concat -- - * concatenate the two strings, inserting a space or slash between them, - * freeing them if requested. - * - * returns -- - * the resulting string in allocated space. - */ +/* Return the concatenation of s1 and s2, freshly allocated. */ char * -str_concat(const char *s1, const char *s2, int flags) +str_concat2(const char *s1, const char *s2) { - int len1, len2; - char *result; - - /* get the length of both strings */ - len1 = strlen(s1); - len2 = strlen(s2); - - /* allocate length plus separator plus EOS */ - result = bmake_malloc((unsigned int)(len1 + len2 + 2)); - - /* copy first string into place */ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + char *result = bmake_malloc(len1 + len2 + 1); memcpy(result, s1, len1); - - /* add separator character */ - if (flags & STR_ADDSPACE) { - result[len1] = ' '; - ++len1; - } else if (flags & STR_ADDSLASH) { - result[len1] = '/'; - ++len1; - } - - /* copy second string plus EOS into place */ memcpy(result + len1, s2, len2 + 1); + return result; +} +/* Return the concatenation of s1, s2 and s3, freshly allocated. */ +char * +str_concat3(const char *s1, const char *s2, const char *s3) +{ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + size_t len3 = strlen(s3); + char *result = bmake_malloc(len1 + len2 + len3 + 1); + memcpy(result, s1, len1); + memcpy(result + len1, s2, len2); + memcpy(result + len1 + len2, s3, len3 + 1); return result; } -/*- - * brk_string -- - * Fracture a string into an array of words (as delineated by tabs or - * spaces) taking quotation marks into account. Leading tabs/spaces - * are ignored. +/* Return the concatenation of s1, s2, s3 and s4, freshly allocated. */ +char * +str_concat4(const char *s1, const char *s2, const char *s3, const char *s4) +{ + size_t len1 = strlen(s1); + size_t len2 = strlen(s2); + size_t len3 = strlen(s3); + size_t len4 = strlen(s4); + char *result = bmake_malloc(len1 + len2 + len3 + len4 + 1); + memcpy(result, s1, len1); + memcpy(result + len1, s2, len2); + memcpy(result + len1 + len2, s3, len3); + memcpy(result + len1 + len2 + len3, s4, len4 + 1); + return result; +} + +/* Fracture a string into an array of words (as delineated by tabs or spaces) + * taking quotation marks into account. Leading tabs/spaces are ignored. * - * If expand is TRUE, quotes are removed and escape sequences - * such as \r, \t, etc... are expanded. + * If expand is TRUE, quotes are removed and escape sequences such as \r, \t, + * etc... are expanded. In this case, the return value is NULL on parse + * errors. * - * returns -- - * Pointer to the array of pointers to the words. - * Memory containing the actual words in *store_words_buf. - * Both of these must be free'd by the caller. - * Number of words in *store_words_len. + * Returns the fractured words, which must be freed later using Words_Free. + * If expand was TRUE and there was a parse error, words is NULL, and in that + * case, nothing needs to be freed. */ -char ** -brk_string(const char *str, int *store_words_len, Boolean expand, - char **store_words_buf) +Words +Str_Words(const char *str, Boolean expand) { + size_t str_len; + char *words_buf; + size_t words_cap; + char **words; + size_t words_len; char inquote; + char *word_start; + char *word_end; 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; /* words_buf holds the words, separated by '\0'. */ str_len = strlen(str); words_buf = bmake_malloc(strlen(str) + 1); 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 word list. */ words_len = 0; inquote = '\0'; - word_start = word_end = words_buf; + word_start = words_buf; + word_end = words_buf; for (str_p = str;; ++str_p) { char ch = *str_p; - switch(ch) { + switch (ch) { case '"': case '\'': if (inquote) { if (inquote == ch) inquote = '\0'; else break; - } - else { - inquote = (char) ch; + } else { + inquote = (char)ch; /* Don't miss "" or '' */ if (word_start == NULL && str_p[1] == inquote) { if (!expand) { word_start = word_end; *word_end++ = ch; } else word_start = word_end + 1; str_p++; inquote = '\0'; break; } } if (!expand) { if (word_start == NULL) word_start = word_end; *word_end++ = ch; } continue; case ' ': case '\t': case '\n': if (inquote) break; if (word_start == NULL) continue; /* FALLTHROUGH */ case '\0': /* * end of a token -- make sure there's enough words * space and save off a pointer. */ if (word_start == NULL) - goto done; + goto done; *word_end++ = '\0'; if (words_len == words_cap) { + size_t new_size; words_cap *= 2; /* ramp up fast */ - words = (char **)bmake_realloc(words, - (words_cap + 1) * sizeof(char *)); + new_size = (words_cap + 1) * sizeof(char *); + words = bmake_realloc(words, new_size); } words[words_len++] = word_start; word_start = NULL; if (ch == '\n' || ch == '\0') { if (expand && inquote) { free(words); free(words_buf); - *store_words_buf = NULL; - return NULL; + return (Words){ NULL, 0, NULL }; } goto done; } continue; case '\\': if (!expand) { if (word_start == NULL) word_start = word_end; *word_end++ = '\\'; /* catch '\' at end of line */ if (str_p[1] == '\0') continue; ch = *++str_p; break; } switch (ch = *++str_p) { case '\0': case '\n': /* hmmm; fix it up as best we can */ ch = '\\'; --str_p; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; } break; } if (word_start == NULL) word_start = word_end; *word_end++ = ch; } -done: words[words_len] = NULL; - *store_words_len = words_len; - *store_words_buf = words_buf; - return words; +done: + words[words_len] = NULL; + return (Words){ words, words_len, words_buf }; } /* * Str_FindSubstring -- See if a string contains a particular substring. * * Input: * string String to search. * substring Substring to find in string. * * Results: If string contains substring, the return value is the location of * the first matching instance of substring in string. If string doesn't * contain substring, the return value is NULL. Matching is done on an exact * character-for-character basis with no wildcards or special characters. * * Side effects: None. */ char * Str_FindSubstring(const char *string, const char *substring) { const char *a, *b; /* * First scan quickly through the two strings looking for a single- * character match. When it's found, then compare the rest of the * substring. */ for (b = substring; *string != 0; string++) { if (*string != *b) continue; a = string; for (;;) { if (*b == 0) return UNCONST(string); if (*a++ != *b++) break; } b = substring; } return NULL; } /* * Str_Match -- Test if a string matches a pattern like "*.[ch]". * * XXX this function does not detect or report malformed patterns. * * Results: * Non-zero is returned if string matches the pattern, 0 otherwise. The * matching operation permits the following special characters in the * pattern: *?\[] (as in fnmatch(3)). * * Side effects: None. */ Boolean Str_Match(const char *str, const char *pat) { for (;;) { /* * See if we're at the end of both the pattern and the * string. If, we succeeded. If we're at the end of the * pattern but not at the end of the string, we failed. */ if (*pat == 0) return *str == 0; if (*str == 0 && *pat != '*') return FALSE; /* * A '*' in the pattern matches any substring. We handle this * by calling ourselves for each suffix of the string. */ if (*pat == '*') { pat++; while (*pat == '*') pat++; if (*pat == 0) return TRUE; while (*str != 0) { if (Str_Match(str, pat)) return TRUE; str++; } return FALSE; } /* A '?' in the pattern matches any single character. */ if (*pat == '?') goto thisCharOK; /* * A '[' in the pattern matches a character from a list. * The '[' is followed by the list of acceptable characters, * or by ranges (two characters separated by '-'). In these * character lists, the backslash is an ordinary character. */ if (*pat == '[') { Boolean neg = pat[1] == '^'; - pat += 1 + neg; + pat += neg ? 2 : 1; for (;;) { if (*pat == ']' || *pat == 0) { if (neg) break; return FALSE; } if (*pat == *str) break; if (pat[1] == '-') { if (pat[2] == 0) return neg; if (*pat <= *str && pat[2] >= *str) break; if (*pat >= *str && pat[2] <= *str) break; pat += 2; } pat++; } if (neg && *pat != ']' && *pat != 0) return FALSE; while (*pat != ']' && *pat != 0) pat++; if (*pat == 0) pat--; goto thisCharOK; } /* * A backslash in the pattern matches the character following * it exactly. */ if (*pat == '\\') { pat++; if (*pat == 0) return FALSE; } if (*pat != *str) return FALSE; thisCharOK: pat++; str++; } -} - -/*- - *----------------------------------------------------------------------- - * Str_SYSVMatch -- - * Check word against pattern for a match (% is wild), - * - * Input: - * word Word to examine - * pattern Pattern to examine against - * len Number of characters to substitute - * - * Results: - * Returns the beginning position of a match or null. The number - * of characters matched is returned in len. - * - * Side Effects: - * None - * - *----------------------------------------------------------------------- - */ -char * -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); - return UNCONST(w); - } - - 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; - - if (p != m) - return NULL; /* No match */ - - if (*++p == '\0') { - /* No more pattern, return the rest of the string */ - *len = strlen(w); - return UNCONST(w); - } - } - - m = w; - - /* Find a matching tail */ - do - if (strcmp(p, w) == 0) { - *len = w - m; - return UNCONST(m); - } - while (*w++ != '\0'); - - return NULL; -} - - -/*- - *----------------------------------------------------------------------- - * Str_SYSVSubst -- - * Substitute '%' on the pattern with len characters from src. - * If the pattern does not contain a '%' prepend len characters - * from src. - * - * Results: - * None - * - * Side Effects: - * Places result on buf - * - *----------------------------------------------------------------------- - */ -void -Str_SYSVSubst(Buffer *buf, char *pat, char *src, size_t len, - Boolean lhsHasPercent) -{ - char *m; - - if ((m = strchr(pat, '%')) != NULL && lhsHasPercent) { - /* Copy the prefix */ - Buf_AddBytes(buf, m - pat, pat); - /* skip the % */ - pat = m + 1; - } - if (m != NULL || !lhsHasPercent) { - /* Copy the pattern */ - Buf_AddBytes(buf, len, src); - } - - /* append the rest */ - Buf_AddBytes(buf, strlen(pat), pat); } Index: head/contrib/bmake/strlist.c =================================================================== --- head/contrib/bmake/strlist.c (revision 365365) +++ head/contrib/bmake/strlist.c (revision 365366) @@ -1,93 +1,91 @@ -/* $NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $ */ +/* $NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $ */ /*- * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by David Laight. * * 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. * 3. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"; +static char rcsid[] = "$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $"; #else #include #ifndef lint -__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"); +__RCSID("$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $"); #endif /* not lint */ #endif -#include -#include +#include "make.h" #include "strlist.h" -#include "make_malloc.h" void strlist_init(strlist_t *sl) { sl->sl_num = 0; sl->sl_max = 0; sl->sl_items = NULL; } void strlist_clean(strlist_t *sl) { char *str; int i; STRLIST_FOREACH(str, sl, i) free(str); free(sl->sl_items); sl->sl_num = 0; sl->sl_max = 0; sl->sl_items = NULL; } void strlist_add_str(strlist_t *sl, char *str, unsigned int info) { unsigned int n; strlist_item_t *items; if (str == NULL) - return; + return; n = sl->sl_num + 1; sl->sl_num = n; items = sl->sl_items; if (n >= sl->sl_max) { - items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); - sl->sl_items = items; - sl->sl_max = n + 6; + items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); + sl->sl_items = items; + sl->sl_max = n + 6; } items += n - 1; items->si_str = str; items->si_info = info; - items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ + items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ } Index: head/contrib/bmake/strlist.h =================================================================== --- head/contrib/bmake/strlist.h (revision 365365) +++ head/contrib/bmake/strlist.h (revision 365366) @@ -1,62 +1,62 @@ -/* $NetBSD: strlist.h,v 1.3 2009/01/16 21:15:34 dsl Exp $ */ +/* $NetBSD: strlist.h,v 1.4 2020/08/13 03:54:57 rillig Exp $ */ /*- * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by David Laight. * * 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. * 3. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 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 _STRLIST_H -#define _STRLIST_H +#ifndef MAKE_STRLIST_H +#define MAKE_STRLIST_H typedef struct { char *si_str; unsigned int si_info; } strlist_item_t; typedef struct { unsigned int sl_num; unsigned int sl_max; strlist_item_t *sl_items; } strlist_t; void strlist_init(strlist_t *); void strlist_clean(strlist_t *); void strlist_add_str(strlist_t *, char *, unsigned int); #define strlist_num(sl) ((sl)->sl_num) #define strlist_str(sl, n) ((sl)->sl_items[n].si_str) #define strlist_info(sl, n) ((sl)->sl_items[n].si_info) #define strlist_set_info(sl, n, v) ((void)((sl)->sl_items[n].si_info = (v))) #define STRLIST_FOREACH(v, sl, index) \ if ((sl)->sl_items != NULL) \ for (index = 0; (v = strlist_str(sl, index)) != NULL; index++) -#endif /* _STRLIST_H */ +#endif /* MAKE_STRLIST_H */ Index: head/contrib/bmake/suff.c =================================================================== --- head/contrib/bmake/suff.c (revision 365365) +++ head/contrib/bmake/suff.c (revision 365366) @@ -1,2676 +1,2253 @@ -/* $NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94"; #else -__RCSID("$NetBSD: suff.c,v 1.88 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: suff.c,v 1.142 2020/08/31 16:44:25 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * suff.c -- * Functions to maintain suffix lists and find implicit dependents * using suffix transformation rules * * Interface: * Suff_Init Initialize all things to do with suffixes. * * Suff_End Cleanup the module * * Suff_DoPaths This function is used to make life easier * when searching for a file according to its * suffix. It takes the global search path, * as defined using the .PATH: target, and appends * its directories to the path of each of the * defined suffixes, as specified using * .PATH: targets. In addition, all * directories given for suffixes labeled as * include files or libraries, using the .INCLUDES * or .LIBS targets, are played with using * Dir_MakeFlags to create the .INCLUDES and * .LIBS global variables. * * Suff_ClearSuffixes Clear out all the suffixes and defined * transformations. * * Suff_IsTransform Return TRUE if the passed string is the lhs * of a transformation rule. * * Suff_AddSuffix Add the passed string as another known suffix. * * Suff_GetPath Return the search path for the given suffix. * * Suff_AddInclude Mark the given suffix as denoting an include * file. * * Suff_AddLib Mark the given suffix as denoting a library. * * Suff_AddTransform Add another transformation to the suffix * graph. Returns GNode suitable for framing, I * mean, tacking commands, attributes, etc. on. * * Suff_SetNull Define the suffix to consider the suffix of * any file that doesn't have a known one. * * Suff_FindDeps Find implicit sources for and the location of * a target based on its suffix. Returns the * bottom-most node added to the graph or NULL * if the target had no implicit sources. * * Suff_FindPath Return the appropriate path to search in * order to find the node. */ -#include -#include #include "make.h" -#include "hash.h" #include "dir.h" +#define SUFF_DEBUG0(fmt) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt) + +#define SUFF_DEBUG1(fmt, arg1) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1) + +#define SUFF_DEBUG2(fmt, arg1, arg2) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) + +#define SUFF_DEBUG3(fmt, arg1, arg2, arg3) \ + if (!DEBUG(SUFF)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2, arg3) + static Lst sufflist; /* Lst of suffixes */ #ifdef CLEANUP static Lst suffClean; /* Lst of suffixes to be cleaned */ #endif static Lst srclist; /* Lst of sources */ static Lst transforms; /* Lst of transformation rules */ static int sNum = 0; /* Counter for assigning suffix numbers */ +typedef enum { + SUFF_INCLUDE = 0x01, /* One which is #include'd */ + SUFF_LIBRARY = 0x02, /* One which contains a library */ + SUFF_NULL = 0x04 /* The empty suffix */ + /* XXX: Why is SUFF_NULL needed? Wouldn't nameLen == 0 mean the same? */ +} SuffFlags; + +ENUM_FLAGS_RTTI_3(SuffFlags, + SUFF_INCLUDE, SUFF_LIBRARY, SUFF_NULL); + /* * Structure describing an individual suffix. */ -typedef struct _Suff { - char *name; /* The suffix itself */ - int nameLen; /* Length of the suffix */ - short flags; /* Type of suffix */ -#define SUFF_INCLUDE 0x01 /* One which is #include'd */ -#define SUFF_LIBRARY 0x02 /* One which contains a library */ -#define SUFF_NULL 0x04 /* The empty suffix */ +typedef struct Suff { + char *name; /* The suffix itself, such as ".c" */ + int nameLen; /* Length of the name, to avoid strlen calls */ + SuffFlags flags; /* Type of suffix */ Lst searchPath; /* The path along which files of this suffix * may be found */ int sNum; /* The suffix number */ int refCount; /* Reference count of list membership */ Lst parents; /* Suffixes we have a transformation to */ Lst children; /* Suffixes we have a transformation from */ Lst ref; /* List of lists this suffix is referenced */ } Suff; /* - * for SuffSuffIsSuffix - */ -typedef struct { - char *ename; /* The end of the name */ - int len; /* Length of the name */ -} SuffixCmpData; - -/* * Structure used in the search for implied sources. */ typedef struct _Src { char *file; /* The file to look for */ char *pref; /* Prefix from which file was formed */ Suff *suff; /* The suffix on the file */ struct _Src *parent; /* The Src for which this is a source */ GNode *node; /* The node describing the file */ int children; /* Count of existing children (so we don't free * this thing too early or never nuke it) */ #ifdef DEBUG_SRC Lst cp; /* Debug; children list */ #endif } Src; /* * A structure for passing more than one argument to the Lst-library-invoked * function... */ typedef struct { Lst l; Src *s; } LstSrc; typedef struct { GNode **gn; Suff *s; Boolean r; } GNodeSuff; static Suff *suffNull; /* The NULL suffix for this run */ static Suff *emptySuff; /* The empty suffix required for POSIX * single-suffix transformation rules */ -static const char *SuffStrIsPrefix(const char *, const char *); -static char *SuffSuffIsSuffix(const Suff *, const SuffixCmpData *); -static int SuffSuffIsSuffixP(const void *, const void *); -static int SuffSuffHasNameP(const void *, const void *); -static int SuffSuffIsPrefix(const void *, const void *); -static int SuffGNHasNameP(const void *, const void *); static void SuffUnRef(void *, void *); static void SuffFree(void *); static void SuffInsert(Lst, Suff *); static void SuffRemove(Lst, Suff *); static Boolean SuffParseTransform(char *, Suff **, Suff **); static int SuffRebuildGraph(void *, void *); static int SuffScanTargets(void *, void *); static int SuffAddSrc(void *, void *); -static int SuffRemoveSrc(Lst); static void SuffAddLevel(Lst, Src *); -static Src *SuffFindThem(Lst, Lst); -static Src *SuffFindCmds(Src *, Lst); static void SuffExpandChildren(LstNode, GNode *); static void SuffExpandWildcards(LstNode, GNode *); static Boolean SuffApplyTransform(GNode *, GNode *, Suff *, Suff *); static void SuffFindDeps(GNode *, Lst); static void SuffFindArchiveDeps(GNode *, Lst); static void SuffFindNormalDeps(GNode *, Lst); static int SuffPrintName(void *, void *); static int SuffPrintSuff(void *, void *); static int SuffPrintTrans(void *, void *); /*************** Lst Predicates ****************/ /*- *----------------------------------------------------------------------- * SuffStrIsPrefix -- * See if pref is a prefix of str. * * Input: * pref possible prefix * str string to check * * Results: * NULL if it ain't, pointer to character in str after prefix if so * * Side Effects: * None *----------------------------------------------------------------------- */ static const char * SuffStrIsPrefix(const char *pref, const char *str) { while (*str && *pref == *str) { pref++; str++; } return *pref ? NULL : str; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsSuffix -- - * See if suff is a suffix of str. sd->ename should point to THE END - * of the string to check. (THE END == the null byte) +typedef struct { + char *ename; /* The end of the name */ + int len; /* Length of the name */ +} SuffSuffGetSuffixArgs; + +/* See if suff is a suffix of str. str->ename should point to THE END + * of the string to check. (THE END == the null byte) * * Input: * s possible suffix - * sd string to examine + * str string to examine * * Results: * NULL if it ain't, pointer to character in str before suffix if * it is. - * - * Side Effects: - * None - *----------------------------------------------------------------------- */ static char * -SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd) +SuffSuffGetSuffix(const Suff *s, const SuffSuffGetSuffixArgs *str) { char *p1; /* Pointer into suffix name */ char *p2; /* Pointer into string being examined */ - if (sd->len < s->nameLen) + if (str->len < s->nameLen) return NULL; /* this string is shorter than the suffix */ p1 = s->name + s->nameLen; - p2 = sd->ename; + p2 = str->ename; while (p1 >= s->name && *p1 == *p2) { p1--; p2--; } return p1 == s->name - 1 ? p2 : NULL; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsSuffixP -- - * Predicate form of SuffSuffIsSuffix. Passed as the callback function - * to Lst_Find. - * - * Results: - * 0 if the suffix is the one desired, non-zero if not. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static int -SuffSuffIsSuffixP(const void *s, const void *sd) +/* Predicate form of SuffSuffGetSuffix, for Lst_Find. */ +static Boolean +SuffSuffIsSuffix(const void *s, const void *sd) { - return !SuffSuffIsSuffix(s, sd); + return SuffSuffGetSuffix(s, sd) != NULL; } -/*- - *----------------------------------------------------------------------- - * SuffSuffHasNameP -- - * Callback procedure for finding a suffix based on its name. Used by - * Suff_GetPath. - * - * Input: - * s Suffix to check - * sd Desired name - * - * Results: - * 0 if the suffix is of the given name. non-zero otherwise. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -SuffSuffHasNameP(const void *s, const void *sname) +/* See if the suffix has the desired name. */ +static Boolean +SuffSuffHasName(const void *s, const void *desiredName) { - return strcmp(sname, ((const Suff *)s)->name); + return strcmp(((const Suff *)s)->name, desiredName) == 0; } -/*- - *----------------------------------------------------------------------- - * SuffSuffIsPrefix -- - * See if the suffix described by s is a prefix of the string. Care - * must be taken when using this to search for transformations and - * what-not, since there could well be two suffixes, one of which - * is a prefix of the other... - * - * Input: - * s suffix to compare - * str string to examine - * - * Results: - * 0 if s is a prefix of str. non-zero otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int +/* See if the suffix name is a prefix of the string. Care must be taken when + * using this to search for transformations and what-not, since there could + * well be two suffixes, one of which is a prefix of the other... */ +static Boolean SuffSuffIsPrefix(const void *s, const void *str) { - return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL; + return SuffStrIsPrefix(((const Suff *)s)->name, str) != NULL; } -/*- - *----------------------------------------------------------------------- - * SuffGNHasNameP -- - * See if the graph node has the desired name - * - * Input: - * gn current node we're looking at - * name name we're looking for - * - * Results: - * 0 if it does. non-zero if it doesn't - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ -static int -SuffGNHasNameP(const void *gn, const void *name) +/* See if the graph node has the desired name. */ +static Boolean +SuffGNHasName(const void *gn, const void *desiredName) { - return strcmp(name, ((const GNode *)gn)->name); + return strcmp(((const GNode *)gn)->name, desiredName) == 0; } - /*********** Maintenance Functions ************/ + /*********** Maintenance Functions ************/ static void SuffUnRef(void *lp, void *sp) { Lst l = (Lst) lp; - LstNode ln = Lst_Member(l, sp); + LstNode ln = Lst_FindDatum(l, sp); if (ln != NULL) { Lst_Remove(l, ln); ((Suff *)sp)->refCount--; } } -/*- - *----------------------------------------------------------------------- - * SuffFree -- - * Free up all memory associated with the given suffix structure. - * - * Results: - * none - * - * Side Effects: - * the suffix entry is detroyed - *----------------------------------------------------------------------- - */ +/* Free up all memory associated with the given suffix structure. */ static void SuffFree(void *sp) { - Suff *s = (Suff *)sp; + Suff *s = (Suff *)sp; if (s == suffNull) suffNull = NULL; if (s == emptySuff) emptySuff = NULL; #ifdef notdef /* We don't delete suffixes in order, so we cannot use this */ if (s->refCount) Punt("Internal error deleting suffix `%s' with refcount = %d", s->name, s->refCount); #endif - Lst_Destroy(s->ref, NULL); - Lst_Destroy(s->children, NULL); - Lst_Destroy(s->parents, NULL); + Lst_Free(s->ref); + Lst_Free(s->children); + Lst_Free(s->parents); Lst_Destroy(s->searchPath, Dir_Destroy); free(s->name); free(s); } -/*- - *----------------------------------------------------------------------- - * SuffRemove -- - * Remove the suffix into the list - * - * Results: - * None - * - * Side Effects: - * The reference count for the suffix is decremented and the - * suffix is possibly freed - *----------------------------------------------------------------------- - */ +/* Remove the suffix from the list, and free if it is otherwise unused. */ static void SuffRemove(Lst l, Suff *s) { SuffUnRef(l, s); if (s->refCount == 0) { SuffUnRef(sufflist, s); SuffFree(s); } } - -/*- - *----------------------------------------------------------------------- - * SuffInsert -- - * Insert the suffix into the list keeping the list ordered by suffix - * numbers. + +/* Insert the suffix into the list keeping the list ordered by suffix numbers. * * Input: * l the list where in s should be inserted * s the suffix to insert - * - * Results: - * None - * - * Side Effects: - * The reference count of the suffix is incremented - *----------------------------------------------------------------------- */ static void SuffInsert(Lst l, Suff *s) { LstNode ln; /* current element in l we're examining */ Suff *s2 = NULL; /* the suffix descriptor in this element */ - if (Lst_Open(l) == FAILURE) { - return; - } + Lst_Open(l); while ((ln = Lst_Next(l)) != NULL) { - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); if (s2->sNum >= s->sNum) { break; } } - Lst_Close(l); - if (DEBUG(SUFF)) { - fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum); - } + + SUFF_DEBUG2("inserting %s(%d)...", s->name, s->sNum); + if (ln == NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "at end of list\n"); - } - (void)Lst_AtEnd(l, s); + SUFF_DEBUG0("at end of list\n"); + Lst_Append(l, s); s->refCount++; - (void)Lst_AtEnd(s->ref, l); + Lst_Append(s->ref, l); } else if (s2->sNum != s->sNum) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum); - } - (void)Lst_InsertBefore(l, ln, s); + SUFF_DEBUG2("before %s(%d)\n", s2->name, s2->sNum); + Lst_InsertBefore(l, ln, s); s->refCount++; - (void)Lst_AtEnd(s->ref, l); - } else if (DEBUG(SUFF)) { - fprintf(debug_file, "already there\n"); + Lst_Append(s->ref, l); + } else { + SUFF_DEBUG0("already there\n"); } } -/*- - *----------------------------------------------------------------------- - * Suff_ClearSuffixes -- - * This is gross. Nuke the list of suffixes but keep all transformation - * rules around. The transformation graph is destroyed in this process, - * but we leave the list of rules so when a new graph is formed the rules - * will remain. - * This function is called from the parse module when a - * .SUFFIXES:\n line is encountered. - * - * Results: - * none - * - * Side Effects: - * the sufflist and its graph nodes are destroyed - *----------------------------------------------------------------------- - */ +static Suff * +SuffNew(const char *name) +{ + Suff *s = bmake_malloc(sizeof(Suff)); + + s->name = bmake_strdup(name); + s->nameLen = strlen(s->name); + s->searchPath = Lst_Init(); + s->children = Lst_Init(); + s->parents = Lst_Init(); + s->ref = Lst_Init(); + s->sNum = sNum++; + s->flags = 0; + s->refCount = 1; + + return s; +} + +/* This is gross. Nuke the list of suffixes but keep all transformation + * rules around. The transformation graph is destroyed in this process, but + * we leave the list of rules so when a new graph is formed the rules will + * remain. This function is called from the parse module when a .SUFFIXES:\n + * line is encountered. */ void Suff_ClearSuffixes(void) { #ifdef CLEANUP - Lst_Concat(suffClean, sufflist, LST_CONCLINK); + Lst_MoveAll(suffClean, sufflist); #endif - sufflist = Lst_Init(FALSE); + sufflist = Lst_Init(); sNum = 0; if (suffNull) SuffFree(suffNull); - emptySuff = suffNull = bmake_malloc(sizeof(Suff)); + emptySuff = suffNull = SuffNew(""); - suffNull->name = bmake_strdup(""); - suffNull->nameLen = 0; - suffNull->searchPath = Lst_Init(FALSE); Dir_Concat(suffNull->searchPath, dirSearchPath); - suffNull->children = Lst_Init(FALSE); - suffNull->parents = Lst_Init(FALSE); - suffNull->ref = Lst_Init(FALSE); - suffNull->sNum = sNum++; suffNull->flags = SUFF_NULL; - suffNull->refCount = 1; } -/*- - *----------------------------------------------------------------------- - * SuffParseTransform -- - * Parse a transformation string to find its two component suffixes. +/* Parse a transformation string to find its two component suffixes. * * Input: * str String being parsed - * srcPtr Place to store source of trans. - * targPtr Place to store target of trans. + * out_src Place to store source of trans. + * out_targ Place to store target of trans. * * Results: - * TRUE if the string is a valid transformation and FALSE otherwise. - * - * Side Effects: - * The passed pointers are overwritten. - * - *----------------------------------------------------------------------- + * TRUE if the string is a valid transformation, FALSE otherwise. */ static Boolean -SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) +SuffParseTransform(char *str, Suff **out_src, Suff **out_targ) { LstNode srcLn; /* element in suffix list of trans source*/ Suff *src; /* Source of transformation */ LstNode targLn; /* element in suffix list of trans target*/ char *str2; /* Extra pointer (maybe target suffix) */ LstNode singleLn; /* element in suffix list of any suffix * that exactly matches str */ Suff *single = NULL;/* Source of possible transformation to * null suffix */ srcLn = NULL; singleLn = NULL; /* * Loop looking first for a suffix that matches the start of the * string and then for one that exactly matches the rest of it. If * we can find two that meet these criteria, we've successfully * parsed the string. */ for (;;) { if (srcLn == NULL) { - srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix); + srcLn = Lst_Find(sufflist, SuffSuffIsPrefix, str); } else { - srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str, - SuffSuffIsPrefix); + srcLn = Lst_FindFrom(sufflist, LstNode_Next(srcLn), + SuffSuffIsPrefix, str); } if (srcLn == NULL) { /* * Ran out of source suffixes -- no such rule */ if (singleLn != NULL) { /* * Not so fast Mr. Smith! There was a suffix that encompassed * the entire string, so we assume it was a transformation * to the null suffix (thank you POSIX). We still prefer to * find a double rule over a singleton, hence we leave this * check until the end. * * XXX: Use emptySuff over suffNull? */ - *srcPtr = single; - *targPtr = suffNull; + *out_src = single; + *out_targ = suffNull; return TRUE; } return FALSE; } - src = (Suff *)Lst_Datum(srcLn); + src = LstNode_Datum(srcLn); str2 = str + src->nameLen; if (*str2 == '\0') { single = src; singleLn = srcLn; } else { - targLn = Lst_Find(sufflist, str2, SuffSuffHasNameP); + targLn = Lst_Find(sufflist, SuffSuffHasName, str2); if (targLn != NULL) { - *srcPtr = src; - *targPtr = (Suff *)Lst_Datum(targLn); + *out_src = src; + *out_targ = LstNode_Datum(targLn); return TRUE; } } } } -/*- - *----------------------------------------------------------------------- - * Suff_IsTransform -- - * Return TRUE if the given string is a transformation rule - * - * - * Input: - * str string to check - * - * Results: - * TRUE if the string is a concatenation of two known suffixes. - * FALSE otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return TRUE if the given string is a transformation rule, that is, a + * concatenation of two known suffixes. */ Boolean Suff_IsTransform(char *str) { Suff *src, *targ; return SuffParseTransform(str, &src, &targ); } -/*- - *----------------------------------------------------------------------- - * Suff_AddTransform -- - * Add the transformation rule described by the line to the - * list of rules and place the transformation itself in the graph +/* Add the transformation rule described by the line to the list of rules + * and place the transformation itself in the graph. * + * The node is placed on the end of the transforms Lst and links are made + * between the two suffixes mentioned in the target name. + * Input: * line name of transformation to add * * Results: * The node created for the transformation in the transforms list - * - * Side Effects: - * The node is placed on the end of the transforms Lst and links are - * made between the two suffixes mentioned in the target name - *----------------------------------------------------------------------- */ GNode * Suff_AddTransform(char *line) { GNode *gn; /* GNode of transformation rule */ Suff *s, /* source suffix */ - *t; /* target suffix */ + *t; /* target suffix */ LstNode ln; /* Node for existing transformation */ + Boolean ok; - ln = Lst_Find(transforms, line, SuffGNHasNameP); + ln = Lst_Find(transforms, SuffGNHasName, line); if (ln == NULL) { /* * Make a new graph node for the transformation. It will be filled in * by the Parse module. */ gn = Targ_NewGN(line); - (void)Lst_AtEnd(transforms, gn); + Lst_Append(transforms, gn); } else { /* * New specification for transformation rule. Just nuke the old list * of commands so they can be filled in again... We don't actually * free the commands themselves, because a given command can be * attached to several different transformations. */ - gn = (GNode *)Lst_Datum(ln); - Lst_Destroy(gn->commands, NULL); - Lst_Destroy(gn->children, NULL); - gn->commands = Lst_Init(FALSE); - gn->children = Lst_Init(FALSE); + gn = LstNode_Datum(ln); + Lst_Free(gn->commands); + Lst_Free(gn->children); + gn->commands = Lst_Init(); + gn->children = Lst_Init(); } gn->type = OP_TRANSFORM; - (void)SuffParseTransform(line, &s, &t); + ok = SuffParseTransform(line, &s, &t); + assert(ok); + (void)ok; /* * link the two together in the proper relationship and order */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "defining transformation from `%s' to `%s'\n", + SUFF_DEBUG2("defining transformation from `%s' to `%s'\n", s->name, t->name); - } SuffInsert(t->children, s); SuffInsert(s->parents, t); return gn; } -/*- - *----------------------------------------------------------------------- - * Suff_EndTransform -- - * Handle the finish of a transformation definition, removing the - * transformation from the graph if it has neither commands nor - * sources. This is a callback procedure for the Parse module via - * Lst_ForEach +/* Handle the finish of a transformation definition, removing the + * transformation from the graph if it has neither commands nor sources. + * This is a callback procedure for the Parse module via Lst_ForEach. * + * If the node has no commands or children, the children and parents lists + * of the affected suffixes are altered. + * * Input: * gnp Node for transformation - * dummy Node for transformation * * Results: - * === 0 - * - * Side Effects: - * If the node has no commands or children, the children and parents - * lists of the affected suffixes are altered. - * - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ int Suff_EndTransform(void *gnp, void *dummy MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; - if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) - gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty(gn->cohorts)) + gn = LstNode_Datum(Lst_Last(gn->cohorts)); if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) && Lst_IsEmpty(gn->children)) { Suff *s, *t; /* * SuffParseTransform() may fail for special rules which are not * actual transformation rules. (e.g. .DEFAULT) */ if (SuffParseTransform(gn->name, &s, &t)) { Lst p; - if (DEBUG(SUFF)) { - fprintf(debug_file, "deleting transformation from `%s' to `%s'\n", - s->name, t->name); - } + SUFF_DEBUG2("deleting transformation from `%s' to `%s'\n", + s->name, t->name); /* * Store s->parents because s could be deleted in SuffRemove */ p = s->parents; /* * Remove the source from the target's children list. We check for a * nil return to handle a beanhead saying something like * .c.o .c.o: * * We'll be called twice when the next target is seen, but .c and .o * are only linked once... */ SuffRemove(t->children, s); /* * Remove the target from the source's parents list */ SuffRemove(p, t); } - } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { - fprintf(debug_file, "transformation %s complete\n", gn->name); + } else if (gn->type & OP_TRANSFORM) { + SUFF_DEBUG1("transformation %s complete\n", gn->name); } return 0; } -/*- - *----------------------------------------------------------------------- - * SuffRebuildGraph -- - * Called from Suff_AddSuffix via Lst_ForEach to search through the - * list of existing transformation rules and rebuild the transformation - * graph when it has been destroyed by Suff_ClearSuffixes. If the - * given rule is a transformation involving this suffix and another, - * existing suffix, the proper relationship is established between - * the two. +/* Called from Suff_AddSuffix via Lst_ForEach to search through the list of + * existing transformation rules and rebuild the transformation graph when + * it has been destroyed by Suff_ClearSuffixes. If the given rule is a + * transformation involving this suffix and another, existing suffix, the + * proper relationship is established between the two. * + * The appropriate links will be made between this suffix and others if + * transformation rules exist for it. + * * Input: * transformp Transformation to test * sp Suffix to rebuild * * Results: - * Always 0. - * - * Side Effects: - * The appropriate links will be made between this suffix and - * others if transformation rules exist for it. - * - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ static int SuffRebuildGraph(void *transformp, void *sp) { GNode *transform = (GNode *)transformp; Suff *s = (Suff *)sp; char *cp; LstNode ln; Suff *s2; - SuffixCmpData sd; + SuffSuffGetSuffixArgs sd; /* * First see if it is a transformation from this suffix. */ cp = UNCONST(SuffStrIsPrefix(s->name, transform->name)); if (cp != NULL) { - ln = Lst_Find(sufflist, cp, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, cp); if (ln != NULL) { /* * Found target. Link in and return, since it can't be anything * else. */ - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); SuffInsert(s2->children, s); SuffInsert(s->parents, s2); return 0; } } /* * Not from, maybe to? */ sd.len = strlen(transform->name); sd.ename = transform->name + sd.len; - cp = SuffSuffIsSuffix(s, &sd); + cp = SuffSuffGetSuffix(s, &sd); if (cp != NULL) { /* * Null-terminate the source suffix in order to find it. */ cp[1] = '\0'; - ln = Lst_Find(sufflist, transform->name, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, transform->name); /* * Replace the start of the target suffix */ cp[1] = s->name[0]; if (ln != NULL) { /* * Found it -- establish the proper relationship */ - s2 = (Suff *)Lst_Datum(ln); + s2 = LstNode_Datum(ln); SuffInsert(s->children, s2); SuffInsert(s2->parents, s); } } return 0; } -/*- - *----------------------------------------------------------------------- - * SuffScanTargets -- - * Called from Suff_AddSuffix via Lst_ForEach to search through the - * list of existing targets and find if any of the existing targets - * can be turned into a transformation rule. +/* Called from Suff_AddSuffix via Lst_ForEach to search through the list of + * existing targets and find if any of the existing targets can be turned + * into a transformation rule. * + * If such a target is found and the target is the current main target, the + * main target is set to NULL and the next target examined (if that exists) + * becomes the main target. + * * Results: * 1 if a new main target has been selected, 0 otherwise. - * - * Side Effects: - * If such a target is found and the target is the current main - * target, the main target is set to NULL and the next target - * examined (if that exists) becomes the main target. - * - *----------------------------------------------------------------------- */ static int SuffScanTargets(void *targetp, void *gsp) { GNode *target = (GNode *)targetp; GNodeSuff *gs = (GNodeSuff *)gsp; Suff *s, *t; char *ptr; if (*gs->gn == NULL && gs->r && (target->type & OP_NOTARGET) == 0) { *gs->gn = target; Targ_SetMain(target); return 1; } - if ((unsigned int)target->type == OP_TRANSFORM) + if (target->type == OP_TRANSFORM) return 0; if ((ptr = strstr(target->name, gs->s->name)) == NULL || ptr == target->name) return 0; if (SuffParseTransform(target->name, &s, &t)) { if (*gs->gn == target) { gs->r = TRUE; *gs->gn = NULL; Targ_SetMain(NULL); } - Lst_Destroy(target->children, NULL); - target->children = Lst_Init(FALSE); + Lst_Free(target->children); + target->children = Lst_Init(); target->type = OP_TRANSFORM; /* * link the two together in the proper relationship and order */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "defining transformation from `%s' to `%s'\n", - s->name, t->name); - } + SUFF_DEBUG2("defining transformation from `%s' to `%s'\n", + s->name, t->name); SuffInsert(t->children, s); SuffInsert(s->parents, t); } return 0; } -/*- - *----------------------------------------------------------------------- - * Suff_AddSuffix -- - * Add the suffix in string to the end of the list of known suffixes. - * Should we restructure the suffix graph? Make doesn't... +/* Add the suffix to the end of the list of known suffixes. + * Should we restructure the suffix graph? Make doesn't... * - * Input: - * str the name of the suffix to add + * A GNode is created for the suffix and a Suff structure is created and + * added to the suffixes list unless the suffix was already known. + * The mainNode passed can be modified if a target mutated into a + * transform and that target happened to be the main target. * - * Results: - * None - * - * Side Effects: - * A GNode is created for the suffix and a Suff structure is created and - * added to the suffixes list unless the suffix was already known. - * The mainNode passed can be modified if a target mutated into a - * transform and that target happened to be the main target. - *----------------------------------------------------------------------- + * Input: + * name the name of the suffix to add */ void -Suff_AddSuffix(char *str, GNode **gn) +Suff_AddSuffix(const char *name, GNode **gn) { Suff *s; /* new suffix descriptor */ LstNode ln; GNodeSuff gs; - ln = Lst_Find(sufflist, str, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, name); if (ln == NULL) { - s = bmake_malloc(sizeof(Suff)); + s = SuffNew(name); - s->name = bmake_strdup(str); - s->nameLen = strlen(s->name); - s->searchPath = Lst_Init(FALSE); - s->children = Lst_Init(FALSE); - s->parents = Lst_Init(FALSE); - s->ref = Lst_Init(FALSE); - s->sNum = sNum++; - s->flags = 0; - s->refCount = 1; - - (void)Lst_AtEnd(sufflist, s); + Lst_Append(sufflist, s); /* * We also look at our existing targets list to see if adding * this suffix will make one of our current targets mutate into * a suffix rule. This is ugly, but other makes treat all targets * that start with a . as suffix rules. */ gs.gn = gn; gs.s = s; gs.r = FALSE; Lst_ForEach(Targ_List(), SuffScanTargets, &gs); /* * Look for any existing transformations from or to this suffix. * XXX: Only do this after a Suff_ClearSuffixes? */ Lst_ForEach(transforms, SuffRebuildGraph, s); } } -/*- - *----------------------------------------------------------------------- - * Suff_GetPath -- - * Return the search path for the given suffix, if it's defined. - * - * Results: - * The searchPath for the desired suffix or NULL if the suffix isn't - * defined. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return the search path for the given suffix, or NULL. */ Lst Suff_GetPath(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln == NULL) { return NULL; } else { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); return s->searchPath; } } -/*- - *----------------------------------------------------------------------- - * Suff_DoPaths -- - * Extend the search paths for all suffixes to include the default - * search path. +/* Extend the search paths for all suffixes to include the default search + * path. * - * Results: - * None. - * - * Side Effects: - * The searchPath field of all the suffixes is extended by the - * directories in dirSearchPath. If paths were specified for the - * ".h" suffix, the directories are stuffed into a global variable - * called ".INCLUDES" with each directory preceded by a -I. The same - * is done for the ".a" suffix, except the variable is called - * ".LIBS" and the flag is -L. - *----------------------------------------------------------------------- + * The searchPath field of all the suffixes is extended by the directories + * in dirSearchPath. If paths were specified for the ".h" suffix, the + * directories are stuffed into a global variable called ".INCLUDES" with + * each directory preceded by a -I. The same is done for the ".a" suffix, + * except the variable is called ".LIBS" and the flag is -L. */ void Suff_DoPaths(void) { Suff *s; LstNode ln; char *ptr; Lst inIncludes; /* Cumulative .INCLUDES path */ Lst inLibs; /* Cumulative .LIBS path */ - if (Lst_Open(sufflist) == FAILURE) { - return; - } - inIncludes = Lst_Init(FALSE); - inLibs = Lst_Init(FALSE); + inIncludes = Lst_Init(); + inLibs = Lst_Init(); + Lst_Open(sufflist); while ((ln = Lst_Next(sufflist)) != NULL) { - s = (Suff *)Lst_Datum(ln); - if (!Lst_IsEmpty (s->searchPath)) { + s = LstNode_Datum(ln); + if (!Lst_IsEmpty(s->searchPath)) { #ifdef INCLUDES if (s->flags & SUFF_INCLUDE) { Dir_Concat(inIncludes, s->searchPath); } #endif /* INCLUDES */ #ifdef LIBRARIES if (s->flags & SUFF_LIBRARY) { Dir_Concat(inLibs, s->searchPath); } #endif /* LIBRARIES */ Dir_Concat(s->searchPath, dirSearchPath); } else { Lst_Destroy(s->searchPath, Dir_Destroy); - s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); + s->searchPath = Lst_Copy(dirSearchPath, Dir_CopyDir); } } + Lst_Close(sufflist); Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL); free(ptr); Var_Set(".LIBS", ptr = Dir_MakeFlags("-L", inLibs), VAR_GLOBAL); free(ptr); Lst_Destroy(inIncludes, Dir_Destroy); Lst_Destroy(inLibs, Dir_Destroy); - - Lst_Close(sufflist); } -/*- - *----------------------------------------------------------------------- - * Suff_AddInclude -- - * Add the given suffix as a type of file which gets included. - * Called from the parse module when a .INCLUDES line is parsed. - * The suffix must have already been defined. +/* Add the given suffix as a type of file which gets included. + * Called from the parse module when a .INCLUDES line is parsed. + * The suffix must have already been defined. + * The SUFF_INCLUDE bit is set in the suffix's flags field. * * Input: * sname Name of the suffix to mark - * - * Results: - * None. - * - * Side Effects: - * The SUFF_INCLUDE bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- */ void Suff_AddInclude(char *sname) { LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); s->flags |= SUFF_INCLUDE; } } -/*- - *----------------------------------------------------------------------- - * Suff_AddLib -- - * Add the given suffix as a type of file which is a library. - * Called from the parse module when parsing a .LIBS line. The - * suffix must have been defined via .SUFFIXES before this is - * called. +/* Add the given suffix as a type of file which is a library. + * Called from the parse module when parsing a .LIBS line. + * The suffix must have been defined via .SUFFIXES before this is called. + * The SUFF_LIBRARY bit is set in the suffix's flags field. * * Input: * sname Name of the suffix to mark - * - * Results: - * None. - * - * Side Effects: - * The SUFF_LIBRARY bit is set in the suffix's flags field - * - *----------------------------------------------------------------------- */ void -Suff_AddLib(char *sname) +Suff_AddLib(const char *sname) { LstNode ln; Suff *s; - ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, sname); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); s->flags |= SUFF_LIBRARY; } } - /********** Implicit Source Search Functions *********/ + /********** Implicit Source Search Functions *********/ -/*- - *----------------------------------------------------------------------- - * SuffAddSrc -- - * Add a suffix as a Src structure to the given list with its parent - * being the given Src structure. If the suffix is the null suffix, - * the prefix is used unaltered as the file name in the Src structure. +/* Add a suffix as a Src structure to the given list with its parent + * being the given Src structure. If the suffix is the null suffix, + * the prefix is used unaltered as the file name in the Src structure. * * Input: * sp suffix for which to create a Src structure * lsp list and parent for the new Src * * Results: - * always returns 0 - * - * Side Effects: - * A Src structure is created and tacked onto the end of the list - *----------------------------------------------------------------------- + * 0, so that Lst_ForEach continues */ static int SuffAddSrc(void *sp, void *lsp) { Suff *s = (Suff *)sp; LstSrc *ls = (LstSrc *)lsp; Src *s2; /* new Src structure */ Src *targ; /* Target structure */ targ = ls->s; if ((s->flags & SUFF_NULL) && (*s->name != '\0')) { /* * If the suffix has been marked as the NULL suffix, also create a Src * structure for a file with no suffix attached. Two birds, and all * that... */ s2 = bmake_malloc(sizeof(Src)); s2->file = bmake_strdup(targ->pref); s2->pref = targ->pref; s2->parent = targ; s2->node = NULL; s2->suff = s; s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd(ls->l, s2); + Lst_Append(ls->l, s2); #ifdef DEBUG_SRC - s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, s2); + s2->cp = Lst_Init(); + Lst_Append(targ->cp, s2); fprintf(debug_file, "1 add %p %p to %p:", targ, s2, ls->l); Lst_ForEach(ls->l, PrintAddr, NULL); fprintf(debug_file, "\n"); #endif } s2 = bmake_malloc(sizeof(Src)); - s2->file = str_concat(targ->pref, s->name, 0); + s2->file = str_concat2(targ->pref, s->name); s2->pref = targ->pref; s2->parent = targ; s2->node = NULL; s2->suff = s; s->refCount++; s2->children = 0; targ->children += 1; - (void)Lst_AtEnd(ls->l, s2); + Lst_Append(ls->l, s2); #ifdef DEBUG_SRC - s2->cp = Lst_Init(FALSE); - Lst_AtEnd(targ->cp, s2); + s2->cp = Lst_Init(); + Lst_Append(targ->cp, s2); fprintf(debug_file, "2 add %p %p to %p:", targ, s2, ls->l); Lst_ForEach(ls->l, PrintAddr, NULL); fprintf(debug_file, "\n"); #endif return 0; } -/*- - *----------------------------------------------------------------------- - * SuffAddLevel -- - * Add all the children of targ as Src structures to the given list +/* Add all the children of targ as Src structures to the given list. * * Input: * l list to which to add the new level * targ Src structure to use as the parent - * - * Results: - * None - * - * Side Effects: - * Lots of structures are created and added to the list - *----------------------------------------------------------------------- */ static void SuffAddLevel(Lst l, Src *targ) { LstSrc ls; ls.s = targ; ls.l = l; Lst_ForEach(targ->suff->children, SuffAddSrc, &ls); } -/*- - *---------------------------------------------------------------------- - * SuffRemoveSrc -- - * Free all src structures in list that don't have a reference count - * - * Results: - * Ture if an src was removed - * - * Side Effects: - * The memory is free'd. - *---------------------------------------------------------------------- - */ -static int +/* Free the first Src in the list that doesn't have a reference count. + * Return whether a Src was removed. */ +static Boolean SuffRemoveSrc(Lst l) { LstNode ln; Src *s; - int t = 0; - if (Lst_Open(l) == FAILURE) { - return 0; - } + Lst_Open(l); + #ifdef DEBUG_SRC fprintf(debug_file, "cleaning %lx: ", (unsigned long) l); Lst_ForEach(l, PrintAddr, NULL); fprintf(debug_file, "\n"); #endif - while ((ln = Lst_Next(l)) != NULL) { - s = (Src *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (s->children == 0) { free(s->file); if (!s->parent) free(s->pref); else { #ifdef DEBUG_SRC - LstNode ln2 = Lst_Member(s->parent->cp, s); + LstNode ln2 = Lst_FindDatum(s->parent->cp, s); if (ln2 != NULL) Lst_Remove(s->parent->cp, ln2); #endif --s->parent->children; } #ifdef DEBUG_SRC fprintf(debug_file, "free: [l=%p] p=%p %d\n", l, s, s->children); - Lst_Destroy(s->cp, NULL); + Lst_Free(s->cp); #endif Lst_Remove(l, ln); free(s); - t |= 1; Lst_Close(l); return TRUE; } #ifdef DEBUG_SRC else { fprintf(debug_file, "keep: [l=%p] p=%p %d: ", l, s, s->children); Lst_ForEach(s->cp, PrintAddr, NULL); fprintf(debug_file, "\n"); } #endif } Lst_Close(l); - return t; + return FALSE; } -/*- - *----------------------------------------------------------------------- - * SuffFindThem -- - * Find the first existing file/target in the list srcs +/* Find the first existing file/target in the list srcs. * * Input: * srcs list of Src structures to search through * * Results: - * The lowest structure in the chain of transformations - * - * Side Effects: - * None - *----------------------------------------------------------------------- + * The lowest structure in the chain of transformations, or NULL. */ static Src * SuffFindThem(Lst srcs, Lst slst) { Src *s; /* current Src */ Src *rs; /* returned Src */ char *ptr; rs = NULL; - while (!Lst_IsEmpty (srcs)) { - s = (Src *)Lst_DeQueue(srcs); + while (!Lst_IsEmpty(srcs)) { + s = Lst_Dequeue(srcs); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\ttrying %s...", s->file); - } + SUFF_DEBUG1("\ttrying %s...", s->file); /* * A file is considered to exist if either a node exists in the * graph for it or the file actually exists. */ if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) { #ifdef DEBUG_SRC fprintf(debug_file, "remove %p from %p\n", s, srcs); #endif rs = s; break; } if ((ptr = Dir_FindFile(s->file, s->suff->searchPath)) != NULL) { rs = s; #ifdef DEBUG_SRC fprintf(debug_file, "remove %p from %p\n", s, srcs); #endif free(ptr); break; } - if (DEBUG(SUFF)) { - fprintf(debug_file, "not there\n"); - } + SUFF_DEBUG0("not there\n"); SuffAddLevel(srcs, s); - Lst_AtEnd(slst, s); + Lst_Append(slst, s); } - if (DEBUG(SUFF) && rs) { - fprintf(debug_file, "got it\n"); + if (rs) { + SUFF_DEBUG0("got it\n"); } return rs; } -/*- - *----------------------------------------------------------------------- - * SuffFindCmds -- - * See if any of the children of the target in the Src structure is - * one from which the target can be transformed. If there is one, - * a Src structure is put together for it and returned. +/* See if any of the children of the target in the Src structure is one from + * which the target can be transformed. If there is one, a Src structure is + * put together for it and returned. * * Input: - * targ Src structure to play with + * targ Src to play with * * Results: - * The Src structure of the "winning" child, or NULL if no such beast. - * - * Side Effects: - * A Src structure may be allocated. - * - *----------------------------------------------------------------------- + * The Src of the "winning" child, or NULL. */ static Src * SuffFindCmds(Src *targ, Lst slst) { LstNode ln; /* General-purpose list node */ GNode *t, /* Target GNode */ - *s; /* Source GNode */ + *s; /* Source GNode */ int prefLen;/* The length of the defined prefix */ Suff *suff; /* Suffix on matching beastie */ Src *ret; /* Return value */ char *cp; t = targ->node; - (void)Lst_Open(t->children); + Lst_Open(t->children); prefLen = strlen(targ->pref); for (;;) { ln = Lst_Next(t->children); if (ln == NULL) { Lst_Close(t->children); return NULL; } - s = (GNode *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (s->type & OP_OPTIONAL && Lst_IsEmpty(t->commands)) { /* * We haven't looked to see if .OPTIONAL files exist yet, so * don't use one as the implicit source. * This allows us to use .OPTIONAL in .depend files so make won't * complain "don't know how to make xxx.h' when a dependent file * has been moved/deleted. */ continue; } cp = strrchr(s->name, '/'); if (cp == NULL) { cp = s->name; } else { cp++; } if (strncmp(cp, targ->pref, prefLen) != 0) continue; /* * The node matches the prefix ok, see if it has a known * suffix. */ - ln = Lst_Find(sufflist, &cp[prefLen], SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, &cp[prefLen]); if (ln == NULL) continue; /* * It even has a known suffix, see if there's a transformation * defined between the node's suffix and the target's suffix. * * XXX: Handle multi-stage transformations here, too. */ - suff = (Suff *)Lst_Datum(ln); + suff = LstNode_Datum(ln); - if (Lst_Member(suff->parents, targ->suff) != NULL) + /* XXX: Can targ->suff be NULL here? */ + if (targ->suff != NULL && + Lst_FindDatum(suff->parents, targ->suff) != NULL) break; } /* * Hot Damn! Create a new Src structure to describe * this transformation (making sure to duplicate the * source node's name so Suff_FindDeps can free it * again (ick)), and return the new structure. */ ret = bmake_malloc(sizeof(Src)); ret->file = bmake_strdup(s->name); ret->pref = targ->pref; ret->suff = suff; suff->refCount++; ret->parent = targ; ret->node = s; ret->children = 0; targ->children += 1; #ifdef DEBUG_SRC - ret->cp = Lst_Init(FALSE); + ret->cp = Lst_Init(); fprintf(debug_file, "3 add %p %p\n", targ, ret); - Lst_AtEnd(targ->cp, ret); + Lst_Append(targ->cp, ret); #endif - Lst_AtEnd(slst, ret); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tusing existing source %s\n", s->name); - } + Lst_Append(slst, ret); + SUFF_DEBUG1("\tusing existing source %s\n", s->name); + Lst_Close(t->children); return ret; } -/*- - *----------------------------------------------------------------------- - * SuffExpandChildren -- - * Expand the names of any children of a given node that contain - * variable invocations or file wildcards into actual targets. +/* Expand the names of any children of a given node that contain variable + * invocations or file wildcards into actual targets. * + * The expanded node is removed from the parent's list of children, and the + * parent's unmade counter is decremented, but other nodes may be added. + * * Input: * cln Child to examine * pgn Parent node being processed - * - * Results: - * === 0 (continue) - * - * Side Effects: - * The expanded node is removed from the parent's list of children, - * and the parent's unmade counter is decremented, but other nodes - * may be added. - * - *----------------------------------------------------------------------- */ static void SuffExpandChildren(LstNode cln, GNode *pgn) { - GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *cgn = LstNode_Datum(cln); GNode *gn; /* New source 8) */ char *cp; /* Expanded value */ if (!Lst_IsEmpty(cgn->order_pred) || !Lst_IsEmpty(cgn->order_succ)) /* It is all too hard to process the result of .ORDER */ return; if (cgn->type & OP_WAIT) /* Ignore these (& OP_PHONY ?) */ return; /* * First do variable expansion -- this takes precedence over * wildcard expansion. If the result contains wildcards, they'll be gotten * to later since the resulting words are tacked on to the end of * the children list. */ if (strchr(cgn->name, '$') == NULL) { SuffExpandWildcards(cln, pgn); return; } - if (DEBUG(SUFF)) { - fprintf(debug_file, "Expanding \"%s\"...", cgn->name); - } - cp = Var_Subst(NULL, cgn->name, pgn, VARF_UNDEFERR|VARF_WANTRES); + SUFF_DEBUG1("Expanding \"%s\"...", cgn->name); + cp = Var_Subst(cgn->name, pgn, VARE_UNDEFERR|VARE_WANTRES); - if (cp != NULL) { - Lst members = Lst_Init(FALSE); + { + Lst members = Lst_Init(); if (cgn->type & OP_ARCHV) { /* * Node was an archive(member) target, so we want to call * on the Arch module to find the nodes for us, expanding * variables in the parent's context. */ char *sacrifice = cp; (void)Arch_ParseArchive(&sacrifice, members, pgn); } else { /* * Break the result into a vector of strings whose nodes * we can find, then add those nodes to the members list. * Unfortunately, we can't use brk_string b/c it * doesn't understand about variable specifications with * spaces in them... */ char *start; char *initcp = cp; /* For freeing... */ for (start = cp; *start == ' ' || *start == '\t'; start++) continue; for (cp = start; *cp != '\0'; cp++) { if (*cp == ' ' || *cp == '\t') { /* * White-space -- terminate element, find the node, * add it, skip any further spaces. */ *cp++ = '\0'; gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, gn); + Lst_Append(members, gn); while (*cp == ' ' || *cp == '\t') { cp++; } /* * Adjust cp for increment at start of loop, but * set start to first non-space. */ start = cp--; } else if (*cp == '$') { /* * Start of a variable spec -- contact variable module * to find the end so we can skip over it. */ - char *junk; + const char *junk; int len; void *freeIt; - junk = Var_Parse(cp, pgn, VARF_UNDEFERR|VARF_WANTRES, - &len, &freeIt); + junk = Var_Parse(cp, pgn, VARE_UNDEFERR|VARE_WANTRES, + &len, &freeIt); if (junk != var_Error) { cp += len - 1; } free(freeIt); } else if (*cp == '\\' && cp[1] != '\0') { /* * Escaped something -- skip over it */ cp++; } } if (cp != start) { /* * Stuff left over -- add it to the list too */ gn = Targ_FindNode(start, TARG_CREATE); - (void)Lst_AtEnd(members, gn); + Lst_Append(members, gn); } /* * Point cp back at the beginning again so the variable value * can be freed. */ cp = initcp; } /* * Add all elements of the members list to the parent node. */ while(!Lst_IsEmpty(members)) { - gn = (GNode *)Lst_DeQueue(members); + gn = Lst_Dequeue(members); - if (DEBUG(SUFF)) { - fprintf(debug_file, "%s...", gn->name); - } + SUFF_DEBUG1("%s...", gn->name); /* Add gn to the parents child list before the original child */ - (void)Lst_InsertBefore(pgn->children, cln, gn); - (void)Lst_AtEnd(gn->parents, pgn); + Lst_InsertBefore(pgn->children, cln, gn); + Lst_Append(gn->parents, pgn); pgn->unmade++; /* Expand wildcards on new node */ - SuffExpandWildcards(Lst_Prev(cln), pgn); + SuffExpandWildcards(LstNode_Prev(cln), pgn); } - Lst_Destroy(members, NULL); + Lst_Free(members); /* * Free the result */ free(cp); } - if (DEBUG(SUFF)) { - fprintf(debug_file, "\n"); - } + SUFF_DEBUG0("\n"); + /* * Now the source is expanded, remove it from the list of children to * keep it from being processed. */ pgn->unmade--; Lst_Remove(pgn->children, cln); - Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); + Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn)); } static void SuffExpandWildcards(LstNode cln, GNode *pgn) { - GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *cgn = LstNode_Datum(cln); GNode *gn; /* New source 8) */ char *cp; /* Expanded value */ Lst explist; /* List of expansions */ if (!Dir_HasWildcards(cgn->name)) return; /* * Expand the word along the chosen path */ - explist = Lst_Init(FALSE); + explist = Lst_Init(); Dir_Expand(cgn->name, Suff_FindPath(cgn), explist); while (!Lst_IsEmpty(explist)) { /* * Fetch next expansion off the list and find its GNode */ - cp = (char *)Lst_DeQueue(explist); + cp = Lst_Dequeue(explist); - if (DEBUG(SUFF)) { - fprintf(debug_file, "%s...", cp); - } + SUFF_DEBUG1("%s...", cp); gn = Targ_FindNode(cp, TARG_CREATE); /* Add gn to the parents child list before the original child */ - (void)Lst_InsertBefore(pgn->children, cln, gn); - (void)Lst_AtEnd(gn->parents, pgn); + Lst_InsertBefore(pgn->children, cln, gn); + Lst_Append(gn->parents, pgn); pgn->unmade++; } - /* - * Nuke what's left of the list - */ - Lst_Destroy(explist, NULL); + Lst_Free(explist); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\n"); - } + SUFF_DEBUG0("\n"); /* * Now the source is expanded, remove it from the list of children to * keep it from being processed. */ pgn->unmade--; Lst_Remove(pgn->children, cln); - Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); + Lst_Remove(cgn->parents, Lst_FindDatum(cgn->parents, pgn)); } -/*- - *----------------------------------------------------------------------- - * Suff_FindPath -- - * Find a path along which to expand the node. +/* Find a path along which to expand the node. * - * If the word has a known suffix, use that path. - * If it has no known suffix, use the default system search path. + * If the word has a known suffix, use that path. + * If it has no known suffix, use the default system search path. * * Input: * gn Node being examined * * Results: * The appropriate path to search for the GNode. - * - * Side Effects: - * XXX: We could set the suffix here so that we don't have to scan - * again. - * - *----------------------------------------------------------------------- */ Lst Suff_FindPath(GNode* gn) { Suff *suff = gn->suffix; if (suff == NULL) { - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ LstNode ln; sd.len = strlen(gn->name); sd.ename = gn->name + sd.len; - ln = Lst_Find(sufflist, &sd, SuffSuffIsSuffixP); + ln = Lst_Find(sufflist, SuffSuffIsSuffix, &sd); - if (DEBUG(SUFF)) { - fprintf(debug_file, "Wildcard expanding \"%s\"...", gn->name); - } + SUFF_DEBUG1("Wildcard expanding \"%s\"...", gn->name); if (ln != NULL) - suff = (Suff *)Lst_Datum(ln); + suff = LstNode_Datum(ln); /* XXX: Here we can save the suffix so we don't have to do this again */ } if (suff != NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "suffix is \"%s\"...", suff->name); - } + SUFF_DEBUG1("suffix is \"%s\"...", suff->name); return suff->searchPath; } else { /* * Use default search path */ return dirSearchPath; } } -/*- - *----------------------------------------------------------------------- - * SuffApplyTransform -- - * Apply a transformation rule, given the source and target nodes - * and suffixes. +/* Apply a transformation rule, given the source and target nodes and + * suffixes. * * Input: * tGn Target node * sGn Source node * t Target suffix * s Source suffix * * Results: * TRUE if successful, FALSE if not. * * Side Effects: * The source and target are linked and the commands from the * transformation are added to the target node's commands list. * All attributes but OP_DEPMASK and OP_TRANSFORM are applied * to the target. The target also inherits all the sources for * the transformation rule. - * - *----------------------------------------------------------------------- */ static Boolean SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) { LstNode ln, nln; /* General node */ char *tname; /* Name of transformation rule */ GNode *gn; /* Node for same */ /* * Form the proper links between the target and source. */ - (void)Lst_AtEnd(tGn->children, sGn); - (void)Lst_AtEnd(sGn->parents, tGn); + Lst_Append(tGn->children, sGn); + Lst_Append(sGn->parents, tGn); tGn->unmade += 1; /* * Locate the transformation rule itself */ - tname = str_concat(s->name, t->name, 0); - ln = Lst_Find(transforms, tname, SuffGNHasNameP); + tname = str_concat2(s->name, t->name); + ln = Lst_Find(transforms, SuffGNHasName, tname); free(tname); if (ln == NULL) { /* * Not really such a transformation rule (can happen when we're * called to link an OP_MEMBER and OP_ARCHV node), so return * FALSE. */ return FALSE; } - gn = (GNode *)Lst_Datum(ln); + gn = LstNode_Datum(ln); - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); - } + SUFF_DEBUG3("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); /* * Record last child for expansion purposes */ ln = Lst_Last(tGn->children); /* * Pass the buck to Make_HandleUse to apply the rule */ (void)Make_HandleUse(gn, tGn); /* * Deal with wildcards and variables in any acquired sources */ - for (ln = Lst_Succ(ln); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + for (ln = ln != NULL ? LstNode_Next(ln) : NULL; ln != NULL; ln = nln) { + nln = LstNode_Next(ln); SuffExpandChildren(ln, tGn); } /* * Keep track of another parent to which this beast is transformed so * the .IMPSRC variable can be set correctly for the parent. */ - (void)Lst_AtEnd(sGn->iParents, tGn); + Lst_Append(sGn->implicitParents, tGn); return TRUE; } -/*- - *----------------------------------------------------------------------- - * SuffFindArchiveDeps -- - * Locate dependencies for an OP_ARCHV node. +/* Locate dependencies for an OP_ARCHV node. * * Input: * gn Node for which to locate dependencies * - * Results: - * None - * * Side Effects: * Same as Suff_FindDeps - * - *----------------------------------------------------------------------- */ static void SuffFindArchiveDeps(GNode *gn, Lst slst) { char *eoarch; /* End of archive portion */ char *eoname; /* End of member portion */ GNode *mem; /* Node for member */ - static const char *copy[] = { - /* Variables to be copied from the member node */ - TARGET, /* Must be first */ - PREFIX, /* Must be second */ - }; LstNode ln, nln; /* Next suffix node to check */ - int i; /* Index into copy and vals */ Suff *ms; /* Suffix descriptor for member */ char *name; /* Start of member's name */ /* * The node is an archive(member) pair. so we must find a * suffix for both of them. */ eoarch = strchr(gn->name, '('); eoname = strchr(eoarch, ')'); /* * Caller guarantees the format `libname(member)', via * Arch_ParseArchive. */ assert(eoarch != NULL); assert(eoname != NULL); *eoname = '\0'; /* Nuke parentheses during suffix search */ *eoarch = '\0'; /* So a suffix can be found */ name = eoarch + 1; /* * To simplify things, call Suff_FindDeps recursively on the member now, * so we can simply compare the member's .PREFIX and .TARGET variables * to locate its suffix. This allows us to figure out the suffix to * use for the archive without having to do a quadratic search over the * suffix list, backtracking for each one... */ mem = Targ_FindNode(name, TARG_CREATE); SuffFindDeps(mem, slst); /* * Create the link between the two nodes right off */ - (void)Lst_AtEnd(gn->children, mem); - (void)Lst_AtEnd(mem->parents, gn); + Lst_Append(gn->children, mem); + Lst_Append(mem->parents, gn); gn->unmade += 1; /* * Copy in the variables from the member node to this one. */ - for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) { - char *p1; - Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn); - free(p1); - + { + char *freeIt; + Var_Set(PREFIX, Var_Value(PREFIX, mem, &freeIt), gn); + bmake_free(freeIt); + Var_Set(TARGET, Var_Value(TARGET, mem, &freeIt), gn); + bmake_free(freeIt); } ms = mem->suffix; if (ms == NULL) { /* * Didn't know what it was -- use .NULL suffix if not in make mode */ - if (DEBUG(SUFF)) { - fprintf(debug_file, "using null suffix\n"); - } + SUFF_DEBUG0("using null suffix\n"); ms = suffNull; } /* * Set the other two local variables required for this target. */ Var_Set(MEMBER, name, gn); Var_Set(ARCHIVE, gn->name, gn); /* * Set $@ for compatibility with other makes */ Var_Set(TARGET, gn->name, gn); /* * Now we've got the important local variables set, expand any sources * that still contain variables or wildcards in their names. */ for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + nln = LstNode_Next(ln); SuffExpandChildren(ln, gn); } if (ms != NULL) { /* * Member has a known suffix, so look for a transformation rule from * it to a possible suffix of the archive. Rather than searching * through the entire list, we just look at suffixes to which the * member's suffix may be transformed... */ - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ /* * Use first matching suffix... */ sd.len = eoarch - gn->name; sd.ename = eoarch; - ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP); + ln = Lst_Find(ms->parents, SuffSuffIsSuffix, &sd); if (ln != NULL) { /* * Got one -- apply it */ - if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && - DEBUG(SUFF)) - { - fprintf(debug_file, "\tNo transformation from %s -> %s\n", - ms->name, ((Suff *)Lst_Datum(ln))->name); + Suff *suff = LstNode_Datum(ln); + if (!SuffApplyTransform(gn, mem, suff, ms)) { + SUFF_DEBUG2("\tNo transformation from %s -> %s\n", + ms->name, suff->name); } } } /* * Replace the opening and closing parens now we've no need of the separate * pieces. */ *eoarch = '('; *eoname = ')'; /* * Pretend gn appeared to the left of a dependency operator so * the user needn't provide a transformation from the member to the * archive. */ if (OP_NOP(gn->type)) { gn->type |= OP_DEPENDS; } /* * Flag the member as such so we remember to look in the archive for * its modification time. The OP_JOIN | OP_MADE is needed because this * target should never get made. */ mem->type |= OP_MEMBER | OP_JOIN | OP_MADE; } -/*- - *----------------------------------------------------------------------- - * SuffFindNormalDeps -- - * Locate implicit dependencies for regular targets. +/* Locate implicit dependencies for regular targets. * * Input: * gn Node for which to find sources * - * Results: - * None. - * * Side Effects: - * Same as Suff_FindDeps... - * - *----------------------------------------------------------------------- + * Same as Suff_FindDeps */ static void SuffFindNormalDeps(GNode *gn, Lst slst) { char *eoname; /* End of name */ char *sopref; /* Start of prefix */ LstNode ln, nln; /* Next suffix node to check */ Lst srcs; /* List of sources at which to look */ Lst targs; /* List of targets to which things can be * transformed. They all have the same file, * but different suff and pref fields */ Src *bottom; /* Start of found transformation path */ Src *src; /* General Src pointer */ char *pref; /* Prefix to use */ Src *targ; /* General Src target pointer */ - SuffixCmpData sd; /* Search string data */ + SuffSuffGetSuffixArgs sd; /* Search string data */ sd.len = strlen(gn->name); sd.ename = eoname = gn->name + sd.len; sopref = gn->name; /* * Begin at the beginning... */ ln = Lst_First(sufflist); - srcs = Lst_Init(FALSE); - targs = Lst_Init(FALSE); + srcs = Lst_Init(); + targs = Lst_Init(); /* * We're caught in a catch-22 here. On the one hand, we want to use any * transformation implied by the target's sources, but we can't examine * the sources until we've expanded any variables/wildcards they may hold, * and we can't do that until we've set up the target's local variables * and we can't do that until we know what the proper suffix for the * target is (in case there are two suffixes one of which is a suffix of * the other) and we can't know that until we've found its implied * source, which we may not want to use if there's an existing source * that implies a different transformation. * * In an attempt to get around this, which may not work all the time, * but should work most of the time, we look for implied sources first, * checking transformations to all possible suffixes of the target, * use what we find to set the target's local variables, expand the * children, then look for any overriding transformations they imply. * Should we find one, we discard the one we found before. */ bottom = NULL; targ = NULL; if (!(gn->type & OP_PHONY)) { while (ln != NULL) { /* * Look for next possible suffix... */ - ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); + ln = Lst_FindFrom(sufflist, ln, SuffSuffIsSuffix, &sd); if (ln != NULL) { - int prefLen; /* Length of the prefix */ + const char *eopref; /* * Allocate a Src structure to which things can be transformed */ targ = bmake_malloc(sizeof(Src)); targ->file = bmake_strdup(gn->name); - targ->suff = (Suff *)Lst_Datum(ln); + targ->suff = LstNode_Datum(ln); targ->suff->refCount++; targ->node = gn; targ->parent = NULL; targ->children = 0; #ifdef DEBUG_SRC - targ->cp = Lst_Init(FALSE); + targ->cp = Lst_Init(); #endif - /* - * Allocate room for the prefix, whose end is found by - * subtracting the length of the suffix from - * the end of the name. - */ - prefLen = (eoname - targ->suff->nameLen) - sopref; - targ->pref = bmake_malloc(prefLen + 1); - memcpy(targ->pref, sopref, prefLen); - targ->pref[prefLen] = '\0'; + eopref = eoname - targ->suff->nameLen; + targ->pref = bmake_strsedup(sopref, eopref); /* * Add nodes from which the target can be made */ SuffAddLevel(srcs, targ); /* * Record the target so we can nuke it */ - (void)Lst_AtEnd(targs, targ); + Lst_Append(targs, targ); /* * Search from this suffix's successor... */ - ln = Lst_Succ(ln); + ln = LstNode_Next(ln); } } /* * Handle target of unknown suffix... */ if (Lst_IsEmpty(targs) && suffNull != NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name); - } + SUFF_DEBUG1("\tNo known suffix on %s. Using .NULL suffix\n", + gn->name); targ = bmake_malloc(sizeof(Src)); targ->file = bmake_strdup(gn->name); targ->suff = suffNull; targ->suff->refCount++; targ->node = gn; targ->parent = NULL; targ->children = 0; targ->pref = bmake_strdup(sopref); #ifdef DEBUG_SRC - targ->cp = Lst_Init(FALSE); + targ->cp = Lst_Init(); #endif /* * Only use the default suffix rules if we don't have commands * defined for this gnode; traditional make programs used to * not define suffix rules if the gnode had children but we * don't do this anymore. */ if (Lst_IsEmpty(gn->commands)) SuffAddLevel(srcs, targ); else { - if (DEBUG(SUFF)) - fprintf(debug_file, "not "); + SUFF_DEBUG0("not "); } - if (DEBUG(SUFF)) - fprintf(debug_file, "adding suffix rules\n"); + SUFF_DEBUG0("adding suffix rules\n"); - (void)Lst_AtEnd(targs, targ); + Lst_Append(targs, targ); } /* * Using the list of possible sources built up from the target * suffix(es), try and find an existing file/target that matches. */ bottom = SuffFindThem(srcs, slst); if (bottom == NULL) { /* * No known transformations -- use the first suffix found * for setting the local variables. */ if (!Lst_IsEmpty(targs)) { - targ = (Src *)Lst_Datum(Lst_First(targs)); + targ = LstNode_Datum(Lst_First(targs)); } else { targ = NULL; } } else { /* * Work up the transformation path to find the suffix of the * target to which the transformation was made. */ for (targ = bottom; targ->parent != NULL; targ = targ->parent) continue; } } Var_Set(TARGET, gn->path ? gn->path : gn->name, gn); pref = (targ != NULL) ? targ->pref : gn->name; Var_Set(PREFIX, pref, gn); /* * Now we've got the important local variables set, expand any sources * that still contain variables or wildcards in their names. */ for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { - nln = Lst_Succ(ln); + nln = LstNode_Next(ln); SuffExpandChildren(ln, gn); } if (targ == NULL) { - if (DEBUG(SUFF)) { - fprintf(debug_file, "\tNo valid suffix on %s\n", gn->name); - } + SUFF_DEBUG1("\tNo valid suffix on %s\n", gn->name); sfnd_abort: /* * Deal with finding the thing on the default search path. We * always do that, not only if the node is only a source (not * on the lhs of a dependency operator or [XXX] it has neither * children or commands) as the old pmake did. */ if ((gn->type & (OP_PHONY|OP_NOPATH)) == 0) { free(gn->path); gn->path = Dir_FindFile(gn->name, (targ == NULL ? dirSearchPath : targ->suff->searchPath)); if (gn->path != NULL) { char *ptr; Var_Set(TARGET, gn->path, gn); if (targ != NULL) { /* * Suffix known for the thing -- trim the suffix off * the path to form the proper .PREFIX variable. */ int savep = strlen(gn->path) - targ->suff->nameLen; char savec; if (gn->suffix) gn->suffix->refCount--; gn->suffix = targ->suff; gn->suffix->refCount++; savec = gn->path[savep]; gn->path[savep] = '\0'; if ((ptr = strrchr(gn->path, '/')) != NULL) ptr++; else ptr = gn->path; Var_Set(PREFIX, ptr, gn); gn->path[savep] = savec; } else { /* * The .PREFIX gets the full path if the target has * no known suffix. */ if (gn->suffix) gn->suffix->refCount--; gn->suffix = NULL; if ((ptr = strrchr(gn->path, '/')) != NULL) ptr++; else ptr = gn->path; Var_Set(PREFIX, ptr, gn); } } } goto sfnd_return; } /* * If the suffix indicates that the target is a library, mark that in * the node's type field. */ if (targ->suff->flags & SUFF_LIBRARY) { gn->type |= OP_LIB; } /* * Check for overriding transformation rule implied by sources */ if (!Lst_IsEmpty(gn->children)) { src = SuffFindCmds(targ, slst); if (src != NULL) { /* * Free up all the Src structures in the transformation path * up to, but not including, the parent node. */ while (bottom && bottom->parent != NULL) { - if (Lst_Member(slst, bottom) == NULL) { - Lst_AtEnd(slst, bottom); + if (Lst_FindDatum(slst, bottom) == NULL) { + Lst_Append(slst, bottom); } bottom = bottom->parent; } bottom = src; } } if (bottom == NULL) { /* * No idea from where it can come -- return now. */ goto sfnd_abort; } /* * We now have a list of Src structures headed by 'bottom' and linked via * their 'parent' pointers. What we do next is create links between * source and target nodes (which may or may not have been created) * and set the necessary local variables in each target. The * commands for each target are set from the commands of the * transformation rule used to get from the src suffix to the targ * suffix. Note that this causes the commands list of the original * node, gn, to be replaced by the commands of the final * transformation rule. Also, the unmade field of gn is incremented. * Etc. */ if (bottom->node == NULL) { bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); } for (src = bottom; src->parent != NULL; src = src->parent) { targ = src->parent; if (src->node->suffix) src->node->suffix->refCount--; src->node->suffix = src->suff; src->node->suffix->refCount++; if (targ->node == NULL) { targ->node = Targ_FindNode(targ->file, TARG_CREATE); } SuffApplyTransform(targ->node, src->node, targ->suff, src->suff); if (targ->node != gn) { /* * Finish off the dependency-search process for any nodes * between bottom and gn (no point in questing around the * filesystem for their implicit source when it's already * known). Note that the node can't have any sources that * need expanding, since SuffFindThem will stop on an existing * node, so all we need to do is set the standard and System V * variables. */ targ->node->type |= OP_DEPS_FOUND; Var_Set(PREFIX, targ->pref, targ->node); Var_Set(TARGET, targ->node->name, targ->node); } } if (gn->suffix) gn->suffix->refCount--; gn->suffix = src->suff; gn->suffix->refCount++; /* * Nuke the transformation path and the Src structures left over in the * two lists. */ sfnd_return: if (bottom) - if (Lst_Member(slst, bottom) == NULL) - Lst_AtEnd(slst, bottom); + if (Lst_FindDatum(slst, bottom) == NULL) + Lst_Append(slst, bottom); while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs)) continue; - Lst_Concat(slst, srcs, LST_CONCLINK); - Lst_Concat(slst, targs, LST_CONCLINK); + Lst_MoveAll(slst, srcs); + Lst_MoveAll(slst, targs); } -/*- - *----------------------------------------------------------------------- - * Suff_FindDeps -- - * Find implicit sources for the target described by the graph node - * gn +/* Find implicit sources for the target described by the graph node. * - * Results: - * Nothing. + * Nodes are added to the graph below the passed-in node. The nodes are + * marked to have their IMPSRC variable filled in. The PREFIX variable is set + * for the given node and all its implied children. * - * Side Effects: - * Nodes are added to the graph below the passed-in node. The nodes - * are marked to have their IMPSRC variable filled in. The - * PREFIX variable is set for the given node and all its - * implied children. - * - * Notes: - * The path found by this target is the shortest path in the - * transformation graph, which may pass through non-existent targets, - * to an existing target. The search continues on all paths from the - * root suffix until a file is found. I.e. if there's a path - * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but - * the .c and .l files don't, the search will branch out in - * all directions from .o and again from all the nodes on the - * next level until the .l,v node is encountered. - * - *----------------------------------------------------------------------- + * The path found by this target is the shortest path in the transformation + * graph, which may pass through non-existent targets, to an existing target. + * The search continues on all paths from the root suffix until a file is + * found. I.e. if there's a path .o -> .c -> .l -> .l,v from the root and the + * .l,v file exists but the .c and .l files don't, the search will branch out + * in all directions from .o and again from all the nodes on the next level + * until the .l,v node is encountered. */ - void Suff_FindDeps(GNode *gn) { SuffFindDeps(gn, srclist); while (SuffRemoveSrc(srclist)) continue; } - -/* - * Input: - * gn node we're dealing with - * - */ static void SuffFindDeps(GNode *gn, Lst slst) { - if (gn->type & OP_DEPS_FOUND) { - /* - * If dependencies already found, no need to do it again... - */ + if (gn->type & OP_DEPS_FOUND) return; - } else { - gn->type |= OP_DEPS_FOUND; - } + gn->type |= OP_DEPS_FOUND; + /* * Make sure we have these set, may get revised below. */ Var_Set(TARGET, gn->path ? gn->path : gn->name, gn); Var_Set(PREFIX, gn->name, gn); - if (DEBUG(SUFF)) { - fprintf(debug_file, "SuffFindDeps (%s)\n", gn->name); - } + SUFF_DEBUG1("SuffFindDeps (%s)\n", gn->name); if (gn->type & OP_ARCHV) { SuffFindArchiveDeps(gn, slst); } else if (gn->type & OP_LIB) { /* * If the node is a library, it is the arch module's job to find it * and set the TARGET variable accordingly. We merely provide the * search path, assuming all libraries end in ".a" (if the suffix * hasn't been defined, there's nothing we can do for it, so we just * set the TARGET variable to the node's name in order to give it a * value). */ LstNode ln; Suff *s; - ln = Lst_Find(sufflist, LIBSUFF, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, LIBSUFF); if (gn->suffix) gn->suffix->refCount--; if (ln != NULL) { - gn->suffix = s = (Suff *)Lst_Datum(ln); + gn->suffix = s = LstNode_Datum(ln); gn->suffix->refCount++; Arch_FindLib(gn, s->searchPath); } else { gn->suffix = NULL; Var_Set(TARGET, gn->name, gn); } /* * Because a library (-lfoo) target doesn't follow the standard * filesystem conventions, we don't set the regular variables for * the thing. .PREFIX is simply made empty... */ Var_Set(PREFIX, "", gn); } else { SuffFindNormalDeps(gn, slst); } } -/*- - *----------------------------------------------------------------------- - * Suff_SetNull -- - * Define which suffix is the null suffix. +/* Define which suffix is the null suffix. * + * Need to handle the changing of the null suffix gracefully so the old + * transformation rules don't just go away. + * * Input: * name Name of null suffix - * - * Results: - * None. - * - * Side Effects: - * 'suffNull' is altered. - * - * Notes: - * Need to handle the changing of the null suffix gracefully so the - * old transformation rules don't just go away. - * - *----------------------------------------------------------------------- */ void Suff_SetNull(char *name) { Suff *s; LstNode ln; - ln = Lst_Find(sufflist, name, SuffSuffHasNameP); + ln = Lst_Find(sufflist, SuffSuffHasName, name); if (ln != NULL) { - s = (Suff *)Lst_Datum(ln); + s = LstNode_Datum(ln); if (suffNull != NULL) { suffNull->flags &= ~SUFF_NULL; } s->flags |= SUFF_NULL; /* * XXX: Here's where the transformation mangling would take place */ suffNull = s; } else { Parse_Error(PARSE_WARNING, "Desired null suffix %s not defined.", name); } } -/*- - *----------------------------------------------------------------------- - * Suff_Init -- - * Initialize suffixes module - * - * Results: - * None - * - * Side Effects: - * Many - *----------------------------------------------------------------------- - */ +/* Initialize the suffixes module. */ void Suff_Init(void) { #ifdef CLEANUP - suffClean = Lst_Init(FALSE); + suffClean = Lst_Init(); + sufflist = Lst_Init(); #endif - srclist = Lst_Init(FALSE); - transforms = Lst_Init(FALSE); + srclist = Lst_Init(); + transforms = Lst_Init(); /* * Create null suffix for single-suffix rules (POSIX). The thing doesn't * actually go on the suffix list or everyone will think that's its * suffix. */ Suff_ClearSuffixes(); } -/*- - *---------------------------------------------------------------------- - * Suff_End -- - * Cleanup the this module - * - * Results: - * None - * - * Side Effects: - * The memory is free'd. - *---------------------------------------------------------------------- - */ - +/* Clean up the suffixes module. */ void Suff_End(void) { #ifdef CLEANUP Lst_Destroy(sufflist, SuffFree); Lst_Destroy(suffClean, SuffFree); if (suffNull) SuffFree(suffNull); - Lst_Destroy(srclist, NULL); - Lst_Destroy(transforms, NULL); + Lst_Free(srclist); + Lst_Free(transforms); #endif } /********************* DEBUGGING FUNCTIONS **********************/ static int SuffPrintName(void *s, void *dummy MAKE_ATTR_UNUSED) { fprintf(debug_file, "%s ", ((Suff *)s)->name); return 0; } static int SuffPrintSuff(void *sp, void *dummy MAKE_ATTR_UNUSED) { Suff *s = (Suff *)sp; - int flags; - int flag; fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount); - flags = s->flags; - if (flags) { - fputs(" (", debug_file); - while (flags) { - flag = 1 << (ffs(flags) - 1); - flags &= ~flag; - switch (flag) { - case SUFF_NULL: - fprintf(debug_file, "NULL"); - break; - case SUFF_INCLUDE: - fprintf(debug_file, "INCLUDE"); - break; - case SUFF_LIBRARY: - fprintf(debug_file, "LIBRARY"); - break; - } - fputc(flags ? '|' : ')', debug_file); - } + if (s->flags != 0) { + char flags_buf[SuffFlags_ToStringSize]; + + fprintf(debug_file, " (%s)", + Enum_FlagsToString(flags_buf, sizeof flags_buf, + s->flags, SuffFlags_ToStringSpecs)); } fputc('\n', debug_file); fprintf(debug_file, "#\tTo: "); Lst_ForEach(s->parents, SuffPrintName, NULL); fputc('\n', debug_file); fprintf(debug_file, "#\tFrom: "); Lst_ForEach(s->children, SuffPrintName, NULL); fputc('\n', debug_file); fprintf(debug_file, "#\tSearch Path: "); Dir_PrintPath(s->searchPath); fputc('\n', debug_file); return 0; } static int SuffPrintTrans(void *tp, void *dummy MAKE_ATTR_UNUSED) { GNode *t = (GNode *)tp; fprintf(debug_file, "%-16s: ", t->name); Targ_PrintType(t->type); fputc('\n', debug_file); Lst_ForEach(t->commands, Targ_PrintCmd, NULL); fputc('\n', debug_file); return 0; } void Suff_PrintAll(void) { fprintf(debug_file, "#*** Suffixes:\n"); Lst_ForEach(sufflist, SuffPrintSuff, NULL); fprintf(debug_file, "#*** Transformations:\n"); Lst_ForEach(transforms, SuffPrintTrans, NULL); } Index: head/contrib/bmake/targ.c =================================================================== --- head/contrib/bmake/targ.c (revision 365365) +++ head/contrib/bmake/targ.c (revision 365366) @@ -1,846 +1,598 @@ -/* $NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"; +static char rcsid[] = "$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: targ.c,v 1.63 2020/07/03 08:02:55 rillig Exp $"); +__RCSID("$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * targ.c -- * Functions for maintaining the Lst allTargets. Target nodes are - * kept in two structures: a Lst, maintained by the list library, and a - * hash table, maintained by the hash library. + * kept in two structures: a Lst and a hash table. * * Interface: * Targ_Init Initialization procedure. * * Targ_End Cleanup the module * * Targ_List Return the list of all targets so far. * * Targ_NewGN Create a new GNode for the passed target * (string). The node is *not* placed in the * hash table, though all its fields are * initialized. * * Targ_FindNode Find the node for a given target, creating * and storing it if it doesn't exist and the * flags are right (TARG_CREATE) * * Targ_FindList Given a list of names, find nodes for all * of them. If a name doesn't exist and the * TARG_NOCREATE flag was given, an error message * is printed. Else, if a name doesn't exist, * its node is created. * * Targ_Ignore Return TRUE if errors should be ignored when * creating the given target. * * Targ_Silent Return TRUE if we should be silent when * creating the given target. * * Targ_Precious Return TRUE if the target is precious and * should not be removed if we are interrupted. * * Targ_Propagate Propagate information between related * nodes. Should be called after the * makefiles are parsed but before any * action is taken. * * Debugging: * Targ_PrintGraph Print out the entire graphm all variables * and statistics for the directory cache. Should * print something for suffixes, too, but... */ #include #include #include "make.h" -#include "hash.h" #include "dir.h" static Lst allTargets; /* the list of all targets found so far */ #ifdef CLEANUP static Lst allGNs; /* List of all the GNodes */ #endif static Hash_Table targets; /* a hash table of same */ -#define HTSIZE 191 /* initial size of hash table */ - static int TargPrintOnlySrc(void *, void *); static int TargPrintName(void *, void *); #ifdef CLEANUP static void TargFreeGN(void *); #endif -static int TargPropagateCohort(void *, void *); -static int TargPropagateNode(void *, void *); -/*- - *----------------------------------------------------------------------- - * Targ_Init -- - * Initialize this module - * - * Results: - * None - * - * Side Effects: - * The allTargets list and the targets hash table are initialized - *----------------------------------------------------------------------- - */ void Targ_Init(void) { - allTargets = Lst_Init(FALSE); - Hash_InitTable(&targets, HTSIZE); + allTargets = Lst_Init(); + Hash_InitTable(&targets, 191); } -/*- - *----------------------------------------------------------------------- - * Targ_End -- - * Finalize this module - * - * Results: - * None - * - * Side Effects: - * All lists and gnodes are cleared - *----------------------------------------------------------------------- - */ void Targ_End(void) { + Targ_Stats(); #ifdef CLEANUP - Lst_Destroy(allTargets, NULL); - if (allGNs) + Lst_Free(allTargets); + if (allGNs != NULL) Lst_Destroy(allGNs, TargFreeGN); Hash_DeleteTable(&targets); #endif } -/*- - *----------------------------------------------------------------------- - * Targ_List -- - * Return the list of all targets - * - * Results: - * The list of all targets. - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +void +Targ_Stats(void) +{ + Hash_DebugStats(&targets, "targets"); +} + +/* Return the list of all targets. */ Lst Targ_List(void) { return allTargets; } -/*- - *----------------------------------------------------------------------- - * Targ_NewGN -- - * Create and initialize a new graph node +/* Create and initialize a new graph node. The gnode is added to the list of + * all gnodes. * * Input: - * name the name to stick in the new node - * - * Results: - * An initialized graph node with the name field filled with a copy - * of the passed name - * - * Side Effects: - * The gnode is added to the list of all gnodes. - *----------------------------------------------------------------------- + * name the name of the node, such as "clean", "src.c" */ GNode * Targ_NewGN(const char *name) { GNode *gn; gn = bmake_malloc(sizeof(GNode)); gn->name = bmake_strdup(name); gn->uname = NULL; gn->path = NULL; - if (name[0] == '-' && name[1] == 'l') { - gn->type = OP_LIB; - } else { - gn->type = 0; - } + gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0; gn->unmade = 0; gn->unmade_cohorts = 0; gn->cohort_num[0] = 0; gn->centurion = NULL; gn->made = UNMADE; gn->flags = 0; gn->checked = 0; gn->mtime = 0; gn->cmgn = NULL; - gn->iParents = Lst_Init(FALSE); - gn->cohorts = Lst_Init(FALSE); - gn->parents = Lst_Init(FALSE); - gn->children = Lst_Init(FALSE); - gn->order_pred = Lst_Init(FALSE); - gn->order_succ = Lst_Init(FALSE); + gn->implicitParents = Lst_Init(); + gn->cohorts = Lst_Init(); + gn->parents = Lst_Init(); + gn->children = Lst_Init(); + gn->order_pred = Lst_Init(); + gn->order_succ = Lst_Init(); Hash_InitTable(&gn->context, 0); - gn->commands = Lst_Init(FALSE); + gn->commands = Lst_Init(); gn->suffix = NULL; - gn->lineno = 0; gn->fname = NULL; + gn->lineno = 0; #ifdef CLEANUP if (allGNs == NULL) - allGNs = Lst_Init(FALSE); - Lst_AtEnd(allGNs, gn); + allGNs = Lst_Init(); + Lst_Append(allGNs, gn); #endif return gn; } #ifdef CLEANUP -/*- - *----------------------------------------------------------------------- - * TargFreeGN -- - * Destroy a GNode - * - * Results: - * None. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- - */ static void TargFreeGN(void *gnp) { GNode *gn = (GNode *)gnp; - free(gn->name); free(gn->uname); free(gn->path); - /* gn->fname points to name allocated when file was opened, don't free */ - Lst_Destroy(gn->iParents, NULL); - Lst_Destroy(gn->cohorts, NULL); - Lst_Destroy(gn->parents, NULL); - Lst_Destroy(gn->children, NULL); - Lst_Destroy(gn->order_succ, NULL); - Lst_Destroy(gn->order_pred, NULL); + Lst_Free(gn->implicitParents); + Lst_Free(gn->cohorts); + Lst_Free(gn->parents); + Lst_Free(gn->children); + Lst_Free(gn->order_succ); + Lst_Free(gn->order_pred); Hash_DeleteTable(&gn->context); - Lst_Destroy(gn->commands, NULL); + Lst_Free(gn->commands); + + /* XXX: does gn->suffix need to be freed? It is reference-counted. */ + /* gn->fname points to name allocated when file was opened, don't free */ + free(gn); } #endif -/*- - *----------------------------------------------------------------------- - * Targ_FindNode -- - * Find a node in the list using the given name for matching +/* Find a node in the list using the given name for matching. + * If the node is created, it is added to the .ALLTARGETS list. * * Input: * name the name to find - * flags flags governing events when target not - * found + * flags flags governing events when target not found * * Results: - * The node in the list if it was. If it wasn't, return NULL of + * The node in the list if it was. If it wasn't, return NULL if * flags was TARG_NOCREATE or the newly created and initialized node * if it was TARG_CREATE - * - * Side Effects: - * Sometimes a node is created and added to the list - *----------------------------------------------------------------------- */ GNode * Targ_FindNode(const char *name, int flags) { GNode *gn; /* node in that element */ Hash_Entry *he = NULL; /* New or used hash entry for node */ Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ /* an entry for the node */ if (!(flags & (TARG_CREATE | TARG_NOHASH))) { he = Hash_FindEntry(&targets, name); if (he == NULL) return NULL; return (GNode *)Hash_GetValue(he); } if (!(flags & TARG_NOHASH)) { he = Hash_CreateEntry(&targets, name, &isNew); if (!isNew) return (GNode *)Hash_GetValue(he); } gn = Targ_NewGN(name); if (!(flags & TARG_NOHASH)) Hash_SetValue(he, gn); Var_Append(".ALLTARGETS", name, VAR_GLOBAL); - (void)Lst_AtEnd(allTargets, gn); + Lst_Append(allTargets, gn); if (doing_depend) gn->flags |= FROM_DEPEND; return gn; } -/*- - *----------------------------------------------------------------------- - * Targ_FindList -- - * Make a complete list of GNodes from the given list of names +/* Make a complete list of GNodes from the given list of names. + * If flags is TARG_CREATE, nodes will be created for all names in + * names which do not yet have graph nodes. If flags is TARG_NOCREATE, + * an error message will be printed for each name which can't be found. * * Input: * name list of names to find * flags flags used if no node is found for a given name * * Results: * A complete list of graph nodes corresponding to all instances of all * the names in names. - * - * Side Effects: - * If flags is TARG_CREATE, nodes will be created for all names in - * names which do not yet have graph nodes. If flags is TARG_NOCREATE, - * an error message will be printed for each name which can't be found. - * ----------------------------------------------------------------------- */ Lst Targ_FindList(Lst names, int flags) { Lst nodes; /* result list */ LstNode ln; /* name list element */ GNode *gn; /* node in tLn */ char *name; - nodes = Lst_Init(FALSE); + nodes = Lst_Init(); - if (Lst_Open(names) == FAILURE) { - return nodes; - } + Lst_Open(names); while ((ln = Lst_Next(names)) != NULL) { - name = (char *)Lst_Datum(ln); + name = LstNode_Datum(ln); gn = Targ_FindNode(name, flags); if (gn != NULL) { /* - * Note: Lst_AtEnd must come before the Lst_Concat so the nodes + * Note: Lst_Append must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ - (void)Lst_AtEnd(nodes, gn); + Lst_Append(nodes, gn); } else if (flags == TARG_NOCREATE) { Error("\"%s\" -- target unknown.", name); } } Lst_Close(names); return nodes; } -/*- - *----------------------------------------------------------------------- - * Targ_Ignore -- - * Return true if should ignore errors when creating gn - * - * Input: - * gn node to check for - * - * Results: - * TRUE if should ignore errors - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return true if should ignore errors when creating gn. */ Boolean Targ_Ignore(GNode *gn) { - if (ignoreErrors || gn->type & OP_IGNORE) { - return TRUE; - } else { - return FALSE; - } + return ignoreErrors || gn->type & OP_IGNORE; } -/*- - *----------------------------------------------------------------------- - * Targ_Silent -- - * Return true if be silent when creating gn - * - * Input: - * gn node to check for - * - * Results: - * TRUE if should be silent - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* Return true if be silent when creating gn. */ Boolean Targ_Silent(GNode *gn) { - if (beSilent || gn->type & OP_SILENT) { - return TRUE; - } else { - return FALSE; - } + return beSilent || gn->type & OP_SILENT; } -/*- - *----------------------------------------------------------------------- - * Targ_Precious -- - * See if the given target is precious - * - * Input: - * gn the node to check - * - * Results: - * TRUE if it is precious. FALSE otherwise - * - * Side Effects: - * None - *----------------------------------------------------------------------- - */ +/* See if the given target is precious. */ Boolean Targ_Precious(GNode *gn) { - if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { - return TRUE; - } else { - return FALSE; - } + return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); } /******************* DEBUG INFO PRINTING ****************/ static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ -/*- - *----------------------------------------------------------------------- - * Targ_SetMain -- - * Set our idea of the main target we'll be creating. Used for - * debugging output. - * - * Input: - * gn The main target we'll create - * - * Results: - * None. - * - * Side Effects: - * "mainTarg" is set to the main target's node. - *----------------------------------------------------------------------- - */ + +/* Set our idea of the main target we'll be creating. Used for debugging + * output. */ void Targ_SetMain(GNode *gn) { mainTarg = gn; } static int TargPrintName(void *gnp, void *pflags MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; fprintf(debug_file, "%s%s ", gn->name, gn->cohort_num); return 0; } int Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED) { fprintf(debug_file, "\t%s\n", (char *)cmd); return 0; } -/*- - *----------------------------------------------------------------------- - * Targ_FmtTime -- - * Format a modification time in some reasonable way and return it. - * - * Results: - * The time reformatted. - * - * Side Effects: - * The time is placed in a static area, so it is overwritten - * with each call. - * - *----------------------------------------------------------------------- - */ +/* Format a modification time in some reasonable way and return it. + * The time is placed in a static area, so it is overwritten with each call. */ char * Targ_FmtTime(time_t tm) { struct tm *parts; static char buf[128]; parts = localtime(&tm); (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); return buf; } -/*- - *----------------------------------------------------------------------- - * Targ_PrintType -- - * Print out a type field giving only those attributes the user can - * set. - * - * Results: - * - * Side Effects: - * - *----------------------------------------------------------------------- - */ +/* Print out a type field giving only those attributes the user can set. */ void Targ_PrintType(int type) { int tbit; #define PRINTBIT(attr) case CONCAT(OP_,attr): fprintf(debug_file, "." #attr " "); break #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))fprintf(debug_file, "." #attr " "); break type &= ~OP_OPMASK; while (type) { tbit = 1 << (ffs(type) - 1); type &= ~tbit; switch(tbit) { PRINTBIT(OPTIONAL); PRINTBIT(USE); PRINTBIT(EXEC); PRINTBIT(IGNORE); PRINTBIT(PRECIOUS); PRINTBIT(SILENT); PRINTBIT(MAKE); PRINTBIT(JOIN); PRINTBIT(INVISIBLE); PRINTBIT(NOTMAIN); PRINTDBIT(LIB); /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ case OP_MEMBER: if (DEBUG(TARG))fprintf(debug_file, ".MEMBER "); break; PRINTDBIT(ARCHV); PRINTDBIT(MADE); PRINTDBIT(PHONY); } } } static const char * -made_name(enum enum_made made) +made_name(GNodeMade made) { switch (made) { case UNMADE: return "unmade"; case DEFERRED: return "deferred"; case REQUESTED: return "requested"; case BEINGMADE: return "being made"; case MADE: return "made"; case UPTODATE: return "up-to-date"; case ERROR: return "error when made"; case ABORTED: return "aborted"; default: return "unknown enum_made value"; } } -/*- - *----------------------------------------------------------------------- - * TargPrintNode -- - * print the contents of a node - *----------------------------------------------------------------------- - */ +/* Print the contents of a node. */ int Targ_PrintNode(void *gnp, void *passp) { GNode *gn = (GNode *)gnp; int pass = passp ? *(int *)passp : 0; - fprintf(debug_file, "# %s%s, flags %x, type %x, made %d\n", - gn->name, gn->cohort_num, gn->flags, gn->type, gn->made); + fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num); + GNode_FprintDetails(debug_file, ", ", gn, "\n"); if (gn->flags == 0) return 0; if (!OP_NOP(gn->type)) { fprintf(debug_file, "#\n"); if (gn == mainTarg) { fprintf(debug_file, "# *** MAIN TARGET ***\n"); } if (pass >= 2) { if (gn->unmade) { fprintf(debug_file, "# %d unmade children\n", gn->unmade); } else { fprintf(debug_file, "# No unmade children\n"); } if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { if (gn->mtime != 0) { fprintf(debug_file, "# last modified %s: %s\n", Targ_FmtTime(gn->mtime), made_name(gn->made)); } else if (gn->made != UNMADE) { fprintf(debug_file, "# non-existent (maybe): %s\n", made_name(gn->made)); } else { fprintf(debug_file, "# unmade\n"); } } - if (!Lst_IsEmpty (gn->iParents)) { + if (!Lst_IsEmpty(gn->implicitParents)) { fprintf(debug_file, "# implicit parents: "); - Lst_ForEach(gn->iParents, TargPrintName, NULL); + Lst_ForEach(gn->implicitParents, TargPrintName, NULL); fprintf(debug_file, "\n"); } } else { if (gn->unmade) fprintf(debug_file, "# %d unmade children\n", gn->unmade); } - if (!Lst_IsEmpty (gn->parents)) { + if (!Lst_IsEmpty(gn->parents)) { fprintf(debug_file, "# parents: "); Lst_ForEach(gn->parents, TargPrintName, NULL); fprintf(debug_file, "\n"); } - if (!Lst_IsEmpty (gn->order_pred)) { + if (!Lst_IsEmpty(gn->order_pred)) { fprintf(debug_file, "# order_pred: "); Lst_ForEach(gn->order_pred, TargPrintName, NULL); fprintf(debug_file, "\n"); } - if (!Lst_IsEmpty (gn->order_succ)) { + if (!Lst_IsEmpty(gn->order_succ)) { fprintf(debug_file, "# order_succ: "); Lst_ForEach(gn->order_succ, TargPrintName, NULL); fprintf(debug_file, "\n"); } fprintf(debug_file, "%-16s", gn->name); switch (gn->type & OP_OPMASK) { case OP_DEPENDS: fprintf(debug_file, ": "); break; case OP_FORCE: fprintf(debug_file, "! "); break; case OP_DOUBLEDEP: fprintf(debug_file, ":: "); break; } Targ_PrintType(gn->type); Lst_ForEach(gn->children, TargPrintName, NULL); fprintf(debug_file, "\n"); Lst_ForEach(gn->commands, Targ_PrintCmd, NULL); fprintf(debug_file, "\n\n"); if (gn->type & OP_DOUBLEDEP) { Lst_ForEach(gn->cohorts, Targ_PrintNode, &pass); } } return 0; } -/*- - *----------------------------------------------------------------------- - * TargPrintOnlySrc -- - * Print only those targets that are just a source. - * - * Results: - * 0. - * - * Side Effects: - * The name of each file is printed preceded by #\t - * - *----------------------------------------------------------------------- - */ +/* Print only those targets that are just a source. + * The name of each file is printed, preceded by #\t. */ static int TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED) { GNode *gn = (GNode *)gnp; if (!OP_NOP(gn->type)) return 0; fprintf(debug_file, "#\t%s [%s] ", gn->name, gn->path ? gn->path : gn->name); Targ_PrintType(gn->type); fprintf(debug_file, "\n"); return 0; } -/*- - *----------------------------------------------------------------------- - * Targ_PrintGraph -- - * print the entire graph. heh heh - * - * Input: - * pass Which pass this is. 1 => no processing - * 2 => processing done - * - * Results: - * none - * - * Side Effects: - * lots o' output - *----------------------------------------------------------------------- +/* Input: + * pass 1 => before processing + * 2 => after processing + * 3 => after processing, an error occurred */ void Targ_PrintGraph(int pass) { fprintf(debug_file, "#*** Input graph:\n"); Lst_ForEach(allTargets, Targ_PrintNode, &pass); fprintf(debug_file, "\n\n"); fprintf(debug_file, "#\n# Files that are only sources:\n"); Lst_ForEach(allTargets, TargPrintOnlySrc, NULL); fprintf(debug_file, "#*** Global Variables:\n"); Var_Dump(VAR_GLOBAL); fprintf(debug_file, "#*** Command-line Variables:\n"); Var_Dump(VAR_CMD); fprintf(debug_file, "\n"); Dir_PrintDirectories(); fprintf(debug_file, "\n"); Suff_PrintAll(); } -/*- - *----------------------------------------------------------------------- - * TargPropagateNode -- - * Propagate information from a single node to related nodes if - * appropriate. +/* Propagate some type information to cohort nodes (those from the :: + * dependency operator). * - * Input: - * gnp The node that we are processing. - * - * Results: - * Always returns 0, for the benefit of Lst_ForEach(). - * - * Side Effects: - * Information is propagated from this node to cohort or child - * nodes. - * - * If the node was defined with "::", then TargPropagateCohort() - * will be called for each cohort node. - * - * If the node has recursive predecessors, then - * TargPropagateRecpred() will be called for each recursive - * predecessor. - *----------------------------------------------------------------------- - */ -static int -TargPropagateNode(void *gnp, void *junk MAKE_ATTR_UNUSED) + * Should be called after the makefiles are parsed but before any action is + * taken. */ +void +Targ_Propagate(void) { - GNode *gn = (GNode *)gnp; + LstNode pn, cn; - if (gn->type & OP_DOUBLEDEP) - Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp); - return 0; -} + for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) { + GNode *pgn = LstNode_Datum(pn); -/*- - *----------------------------------------------------------------------- - * TargPropagateCohort -- - * Propagate some bits in the type mask from a node to - * a related cohort node. - * - * Input: - * cnp The node that we are processing. - * gnp Another node that has cnp as a cohort. - * - * Results: - * Always returns 0, for the benefit of Lst_ForEach(). - * - * Side Effects: - * cnp's type bitmask is modified to incorporate some of the - * bits from gnp's type bitmask. (XXX need a better explanation.) - *----------------------------------------------------------------------- - */ -static int -TargPropagateCohort(void *cgnp, void *pgnp) -{ - GNode *cgn = (GNode *)cgnp; - GNode *pgn = (GNode *)pgnp; + if (!(pgn->type & OP_DOUBLEDEP)) + continue; - cgn->type |= pgn->type & ~OP_OPMASK; - return 0; -} + for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) { + GNode *cgn = LstNode_Datum(cn); -/*- - *----------------------------------------------------------------------- - * Targ_Propagate -- - * Propagate information between related nodes. Should be called - * after the makefiles are parsed but before any action is taken. - * - * Results: - * none - * - * Side Effects: - * Information is propagated between related nodes throughout the - * graph. - *----------------------------------------------------------------------- - */ -void -Targ_Propagate(void) -{ - Lst_ForEach(allTargets, TargPropagateNode, NULL); + cgn->type |= pgn->type & ~OP_OPMASK; + } + } } Index: head/contrib/bmake/trace.c =================================================================== --- head/contrib/bmake/trace.c (revision 365365) +++ head/contrib/bmake/trace.c (revision 365366) @@ -1,116 +1,116 @@ -/* $NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Bill Sommerfeld * * 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $"; #else #include #ifndef lint -__RCSID("$NetBSD: trace.c,v 1.12 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $"); #endif /* not lint */ #endif /*- * trace.c -- * handle logging of trace events generated by various parts of make. * * Interface: * Trace_Init Initialize tracing (called once during * the lifetime of the process) * * Trace_End Finalize tracing (called before make exits) * * Trace_Log Log an event about a particular make job. */ #include #include #include #include "make.h" #include "job.h" #include "trace.h" static FILE *trfile; static pid_t trpid; -char *trwd; +const char *trwd; static const char *evname[] = { "BEG", "END", "ERR", "JOB", "DON", "INT", }; void Trace_Init(const char *pathname) { - char *p1; if (pathname != NULL) { + char *dontFreeIt; trpid = getpid(); - trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1); + trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt); trfile = fopen(pathname, "a"); } } void Trace_Log(TrEvent event, Job *job) { struct timeval rightnow; if (trfile == NULL) return; gettimeofday(&rightnow, NULL); fprintf(trfile, "%lld.%06ld %d %s %d %s", (long long)rightnow.tv_sec, (long)rightnow.tv_usec, jobTokensRunning, evname[event], trpid, trwd); if (job != NULL) { fprintf(trfile, " %s %d %x %x", job->node->name, job->pid, job->flags, job->node->type); } fputc('\n', trfile); fflush(trfile); } void Trace_End(void) { if (trfile != NULL) fclose(trfile); } Index: head/contrib/bmake/unit-tests/modorder.mk =================================================================== --- head/contrib/bmake/unit-tests/modorder.mk (revision 365365) +++ head/contrib/bmake/unit-tests/modorder.mk (nonexistent) @@ -1,24 +0,0 @@ -# $NetBSD: modorder.mk,v 1.3 2020/06/09 01:48:17 sjg Exp $ - -LIST= one two three four five six seven eight nine ten -LISTX= ${LIST:Ox} -LISTSX:= ${LIST:Ox} -TEST_RESULT= && echo Ok || echo Failed - -# unit-tests have to produce the same results on each run -# so we cannot actually include :Ox output. -all: - @echo "LIST = ${LIST}" - @echo "LIST:O = ${LIST:O}" - @echo "LIST:Or = ${LIST:Or}" - # 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 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}`" - @echo "LISTSX = `test '${LISTSX}' = '${LISTSX}' ${TEST_RESULT}`" - @echo "BADMOD 1 = ${LIST:OX}" - @echo "BADMOD 2 = ${LIST:OxXX}" Property changes on: head/contrib/bmake/unit-tests/modorder.mk ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/modorder.exp =================================================================== --- head/contrib/bmake/unit-tests/modorder.exp (revision 365365) +++ head/contrib/bmake/unit-tests/modorder.exp (nonexistent) @@ -1,12 +0,0 @@ -LIST = one two three four five six seven eight nine ten -LIST:O = eight five four nine one seven six ten three two -LIST:Or = two three ten six seven one nine four five eight -LIST:Ox = Ok -LIST:O:Ox = Ok -LISTX = Ok -LISTSX = Ok -make: Bad modifier `:OX' for LIST -BADMOD 1 = } -make: Bad modifier `:OxXX' for LIST -BADMOD 2 = XX} -exit status 0 Index: head/contrib/bmake/unit-tests/Makefile =================================================================== --- head/contrib/bmake/unit-tests/Makefile (revision 365365) +++ head/contrib/bmake/unit-tests/Makefile (revision 365366) @@ -1,174 +1,480 @@ -# $Id: Makefile,v 1.60 2020/07/10 00:48:32 sjg Exp $ +# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $ # -# $NetBSD: Makefile,v 1.63 2020/07/09 22:40:14 sjg Exp $ +# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $ # # Unit tests for make(1) # # The main targets are: # # all: # run all the tests # test: # run 'all', and compare to expected results # accept: # move generated output to expected results # # Settable variables # # TEST_MAKE # The make program to be tested. # # # Adding a test case # # Each feature should get its own set of tests in its own suitably # named makefile (*.mk), with its own set of expected results (*.exp), # and it should be added to the TESTS list. # -# Any added files must also be added to src/distrib/sets/lists/tests/mi. -# Makefiles that are not added to TESTS must be ignored in -# src/tests/usr.bin/make/t_make.sh (example: include-sub). +# A few *.mk files are helper files for other tests (such as include-sub.mk) +# and are thus not added to TESTS. Such files must be ignored in +# src/tests/usr.bin/make/t_make.sh. # # Each test is in a sub-makefile. # Keep the list sorted. +# Any test that is commented out must be ignored in +# src/tests/usr.bin/make/t_make.sh as well. +TESTS+= # archive # broken on FreeBSD, enabled in t_make.sh +TESTS+= archive-suffix +TESTS+= cmd-interrupt +TESTS+= cmdline TESTS+= comment +TESTS+= cond-cmp-numeric +TESTS+= cond-cmp-numeric-eq +TESTS+= cond-cmp-numeric-ge +TESTS+= cond-cmp-numeric-gt +TESTS+= cond-cmp-numeric-le +TESTS+= cond-cmp-numeric-lt +TESTS+= cond-cmp-numeric-ne +TESTS+= cond-cmp-string +TESTS+= cond-func +TESTS+= cond-func-commands +TESTS+= cond-func-defined +TESTS+= cond-func-empty +TESTS+= cond-func-exists +TESTS+= cond-func-make +TESTS+= cond-func-target TESTS+= cond-late +TESTS+= cond-op +TESTS+= cond-op-and +TESTS+= cond-op-not +TESTS+= cond-op-or +TESTS+= cond-op-parentheses TESTS+= cond-short +TESTS+= cond-token-number +TESTS+= cond-token-plain +TESTS+= cond-token-string +TESTS+= cond-token-var TESTS+= cond1 TESTS+= cond2 +TESTS+= counter +TESTS+= dep +TESTS+= dep-colon +TESTS+= dep-double-colon +TESTS+= dep-exclam +TESTS+= dep-none +TESTS+= dep-var +TESTS+= dep-wildcards +TESTS+= depsrc +TESTS+= depsrc-exec +TESTS+= depsrc-ignore +TESTS+= depsrc-made +TESTS+= depsrc-make +TESTS+= depsrc-meta +TESTS+= depsrc-nometa +TESTS+= depsrc-nometa_cmp +TESTS+= depsrc-nopath +TESTS+= depsrc-notmain +TESTS+= depsrc-optional +TESTS+= depsrc-phony +TESTS+= depsrc-precious +TESTS+= depsrc-recursive +TESTS+= depsrc-silent +TESTS+= depsrc-use +TESTS+= depsrc-usebefore +TESTS+= depsrc-usebefore-double-colon +TESTS+= depsrc-wait +TESTS+= deptgt +TESTS+= deptgt-begin +TESTS+= deptgt-default +TESTS+= deptgt-delete_on_error +TESTS+= deptgt-end +TESTS+= deptgt-error +TESTS+= deptgt-ignore +TESTS+= deptgt-interrupt +TESTS+= deptgt-main +TESTS+= deptgt-makeflags +TESTS+= deptgt-no_parallel +TESTS+= deptgt-nopath +TESTS+= deptgt-notparallel +TESTS+= deptgt-objdir +TESTS+= deptgt-order +TESTS+= deptgt-path +TESTS+= deptgt-path-suffix +TESTS+= deptgt-phony +TESTS+= deptgt-precious +TESTS+= deptgt-shell +TESTS+= deptgt-silent +TESTS+= deptgt-stale +TESTS+= deptgt-suffixes +TESTS+= dir +TESTS+= dir-expand-path +TESTS+= directive +TESTS+= directive-elif +TESTS+= directive-elifdef +TESTS+= directive-elifmake +TESTS+= directive-elifndef +TESTS+= directive-elifnmake +TESTS+= directive-else +TESTS+= directive-endif +TESTS+= directive-error +TESTS+= directive-export +TESTS+= directive-export-env +TESTS+= directive-export-literal +TESTS+= directive-for +TESTS+= directive-for-generating-endif +TESTS+= directive-if +TESTS+= directive-ifdef +TESTS+= directive-ifmake +TESTS+= directive-ifndef +TESTS+= directive-ifnmake +TESTS+= directive-info +TESTS+= directive-undef +TESTS+= directive-unexport +TESTS+= directive-unexport-env +TESTS+= directive-warning +TESTS+= directives TESTS+= dollar TESTS+= doterror TESTS+= dotwait +TESTS+= envfirst TESTS+= error TESTS+= # escape # broken by reverting POSIX changes TESTS+= export TESTS+= export-all TESTS+= export-env +TESTS+= export-variants TESTS+= forloop TESTS+= forsubst TESTS+= hash -TESTS+= # impsrc # broken by reverting POSIX changes +TESTS+= impsrc TESTS+= include-main +TESTS+= lint +TESTS+= make-exported TESTS+= misc TESTS+= moderrs TESTS+= modmatch TESTS+= modmisc -TESTS+= modorder TESTS+= modts TESTS+= modword +TESTS+= opt +TESTS+= opt-backwards +TESTS+= opt-chdir +TESTS+= opt-debug +TESTS+= opt-debug-g1 +TESTS+= opt-define +TESTS+= opt-env +TESTS+= opt-file +TESTS+= opt-ignore +TESTS+= opt-include-dir +TESTS+= opt-jobs +TESTS+= opt-jobs-internal +TESTS+= opt-keep-going +TESTS+= opt-m-include-dir +TESTS+= opt-no-action +TESTS+= opt-no-action-at-all +TESTS+= opt-query +TESTS+= opt-raw +TESTS+= opt-silent +TESTS+= opt-touch +TESTS+= opt-tracefile +TESTS+= opt-var-expanded +TESTS+= opt-var-literal +TESTS+= opt-warnings-as-errors +TESTS+= opt-where-am-i +TESTS+= opt-x-reduce-exported TESTS+= order -TESTS+= # phony-end # broken by reverting POSIX changes +TESTS+= phony-end TESTS+= posix TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= qequals -TESTS+= # suffixes # broken by reverting POSIX changes +TESTS+= recursive +TESTS+= sh +TESTS+= sh-dots +TESTS+= sh-jobs +TESTS+= sh-jobs-error +TESTS+= sh-leading-at +TESTS+= sh-leading-hyphen +TESTS+= sh-leading-plus +TESTS+= sh-meta-chars +TESTS+= sh-multi-line +TESTS+= sh-single-line +TESTS+= # suffixes # runs into an endless loop (try -dA) TESTS+= sunshcmd TESTS+= sysv TESTS+= ternary TESTS+= unexport TESTS+= unexport-env +TESTS+= use-inference +TESTS+= var-class +TESTS+= var-class-cmdline +TESTS+= var-class-env +TESTS+= var-class-global +TESTS+= var-class-local +TESTS+= var-class-local-legacy +TESTS+= var-op +TESTS+= var-op-append +TESTS+= var-op-assign +TESTS+= var-op-default +TESTS+= var-op-expand +TESTS+= var-op-shell TESTS+= varcmd +TESTS+= vardebug +TESTS+= varfind TESTS+= varmisc +TESTS+= varmod +TESTS+= varmod-assign +TESTS+= varmod-defined TESTS+= varmod-edge +TESTS+= varmod-exclam-shell +TESTS+= varmod-extension +TESTS+= varmod-gmtime +TESTS+= varmod-hash +TESTS+= varmod-head +TESTS+= varmod-ifelse +TESTS+= varmod-l-name-to-value +TESTS+= varmod-localtime +TESTS+= varmod-loop +TESTS+= varmod-match +TESTS+= varmod-match-escape +TESTS+= varmod-no-match +TESTS+= varmod-order +TESTS+= varmod-order-reverse +TESTS+= varmod-order-shuffle +TESTS+= varmod-path +TESTS+= varmod-quote +TESTS+= varmod-quote-dollar +TESTS+= varmod-range +TESTS+= varmod-remember +TESTS+= varmod-root +TESTS+= varmod-select-words +TESTS+= varmod-shell +TESTS+= varmod-subst +TESTS+= varmod-subst-regex +TESTS+= varmod-sysv +TESTS+= varmod-tail +TESTS+= varmod-to-abs +TESTS+= varmod-to-lower +TESTS+= varmod-to-many-words +TESTS+= varmod-to-one-word +TESTS+= varmod-to-separator +TESTS+= varmod-to-upper +TESTS+= varmod-undefined +TESTS+= varmod-unique +TESTS+= varname +TESTS+= varname-dollar +TESTS+= varname-dot-alltargets +TESTS+= varname-dot-curdir +TESTS+= varname-dot-includes +TESTS+= varname-dot-includedfromdir +TESTS+= varname-dot-includedfromfile +TESTS+= varname-dot-libs +TESTS+= varname-dot-make-dependfile +TESTS+= varname-dot-make-expand_variables +TESTS+= varname-dot-make-exported +TESTS+= varname-dot-make-jobs +TESTS+= varname-dot-make-jobs-prefix +TESTS+= varname-dot-make-level +TESTS+= varname-dot-make-makefile_preference +TESTS+= varname-dot-make-makefiles +TESTS+= varname-dot-make-meta-bailiwick +TESTS+= varname-dot-make-meta-created +TESTS+= varname-dot-make-meta-files +TESTS+= varname-dot-make-meta-ignore_filter +TESTS+= varname-dot-make-meta-ignore_paths +TESTS+= varname-dot-make-meta-ignore_patterns +TESTS+= varname-dot-make-meta-prefix +TESTS+= varname-dot-make-mode +TESTS+= varname-dot-make-path_filemon +TESTS+= varname-dot-make-pid +TESTS+= varname-dot-make-ppid +TESTS+= varname-dot-make-save_dollars +TESTS+= varname-dot-makeoverrides +TESTS+= varname-dot-newline +TESTS+= varname-dot-objdir +TESTS+= varname-dot-parsedir +TESTS+= varname-dot-parsefile +TESTS+= varname-dot-path +TESTS+= varname-dot-shell +TESTS+= varname-dot-targets +TESTS+= varname-empty +TESTS+= varname-make +TESTS+= varname-make_print_var_on_error +TESTS+= varname-makeflags +TESTS+= varname-pwd +TESTS+= varname-vpath +TESTS+= varparse-dynamic TESTS+= varquote TESTS+= varshell -# Override make flags for certain tests; default is -k. +# Additional environment variables for some of the tests. +# The base environment is -i PATH="$PATH". +ENV.envfirst= FROM_ENV=value-from-env +ENV.varmisc= FROM_ENV=env +ENV.varmisc+= FROM_ENV_BEFORE=env +ENV.varmisc+= FROM_ENV_AFTER=env + +# Override make flags for some of the tests; default is -k. +# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of +# settings FLAGS.test=-dv here, since that is closer to the test code. +FLAGS.archive= -dA +FLAGS.counter= -dv +FLAGS.directive-ifmake= first second FLAGS.doterror= # none +FLAGS.envfirst= -e +FLAGS.export= # none +FLAGS.lint= -dL -k +FLAGS.opt-debug-g1= -dg1 +FLAGS.opt-ignore= -i +FLAGS.opt-keep-going= -k +FLAGS.opt-no-action= -n +FLAGS.opt-query= -q +FLAGS.opt-var-expanded= -v VAR -v VALUE +FLAGS.opt-var-literal= -V VAR -V VALUE +FLAGS.opt-warnings-as-errors= -W FLAGS.order= -j1 +FLAGS.recursive= -dL +FLAGS.sh-leading-plus= -n +FLAGS.vardebug= -k -dv FROM_CMDLINE= +FLAGS.varmod-match-escape= -dv +FLAGS.varname-dot-shell= -dpv +FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain' # Some tests need extra post-processing. -SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),' -SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,' +SED_CMDS.opt-debug-g1= -e 's,${.CURDIR},CURDIR,' +SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d' +SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,,' +SED_CMDS.varmod-subst-regex+= \ + -e 's,\(Regex compilation error:\).*,\1 (details omitted),' +SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,' +SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,' SED_CMDS.varshell+= -e '/command/s,No such.*,not found,' +SED_CMDS.varname-dot-shell= -e 's, = /.*, = (details omitted),' +SED_CMDS.varname-dot-shell+= -e 's,"/[^"]*","(details omitted)",' +SED_CMDS.varname-dot-shell+= -e 's,\[/[^]]*\],[(details omitted)],' +# Some tests need an additional round of postprocessing. +POSTPROC.counter= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.deptgt-suffixes= \ + ${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p' +POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.varmod-match-escape= ${TOOL_SED} -n -e '/^Pattern/p' +POSTPROC.varname-dot-shell= \ + awk '/\.SHELL/ || /^ParseReadLine/' +POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p' + +# Some tests reuse other tests, which makes them unnecessarily fragile. +export-all.rawout: export.mk +unexport.rawout: export.mk +unexport-env.rawout: export.mk + # End of the configuration section. .MAIN: all +.-include "Makefile.inc" .-include "Makefile.config" UNIT_TESTS:= ${.PARSEDIR} .PATH: ${UNIT_TESTS} OUTFILES= ${TESTS:=.out} all: ${OUTFILES} -CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp +CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp CLEANFILES+= obj*.[och] lib*.a # posix1.mk CLEANFILES+= issue* .[ab]* # suffixes.mk -CLEANRECURSIVE+= dir dummy # posix1.mk +CLEANDIRS= dir dummy # posix1.mk clean: rm -f ${CLEANFILES} -.if !empty(CLEANRECURSIVE) - rm -rf ${CLEANRECURSIVE} -.endif + rm -rf ${CLEANDIRS} TEST_MAKE?= ${.MAKE} TOOL_SED?= sed TOOL_TR?= tr TOOL_DIFF?= diff DIFF_FLAGS?= -u .if defined(.PARSEDIR) # ensure consistent results from sort(1) LC_ALL= C LANG= C .export LANG LC_ALL .endif +.if ${.MAKE.MODE:Unormal:Mmeta} != "" +# we don't need the noise +_MKMSG_TEST= : +.endif + # the tests are actually done with sub-makes. .SUFFIXES: .mk .rawout .out .mk.rawout: - @echo ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} - -@cd ${.OBJDIR} && \ - { ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \ - 2>&1 ; echo $$? >${.TARGET:R}.status ; } > ${.TARGET}.tmp + @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} + @set -eu; \ + cd ${.OBJDIR}; \ + env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \ + ${TEST_MAKE} \ + -r -C ${.CURDIR} -f ${.IMPSRC} \ + ${FLAGS.${.TARGET:R}:U-k} \ + > ${.TARGET}.tmp 2>&1 \ + && status=$$? || status=$$?; \ + echo $$status > ${.TARGET:R}.status @mv ${.TARGET}.tmp ${.TARGET} # Post-process the test output so that the results can be compared. # # always pretend .MAKE was called 'make' _SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' _SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' # strip ${.CURDIR}/ from the output _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' .rawout.out: - @echo postprocess ${.TARGET} @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \ - < ${.IMPSRC} > ${.TARGET}.tmp - @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp - @mv ${.TARGET}.tmp ${.TARGET} + < ${.IMPSRC} > ${.TARGET}.tmp1 + @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 + @rm ${.TARGET}.tmp1 + @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 + @mv ${.TARGET}.tmp2 ${.TARGET} # Compare all output files test: ${OUTFILES} .PHONY @failed= ; \ for test in ${TESTS}; do \ ${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \ || failed="$${failed}$${failed:+ }$${test}" ; \ done ; \ if [ -n "$${failed}" ]; then \ echo "Failed tests: $${failed}" ; false ; \ else \ echo "All tests passed" ; \ fi accept: @for test in ${TESTS}; do \ cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ || { echo "Replacing $${test}.exp" ; \ cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ done .if exists(${TEST_MAKE}) -${TESTS:=.rawout}: ${TEST_MAKE} +${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile .endif .-include Index: head/contrib/bmake/unit-tests/archive-suffix.exp =================================================================== --- head/contrib/bmake/unit-tests/archive-suffix.exp (nonexistent) +++ head/contrib/bmake/unit-tests/archive-suffix.exp (revision 365366) @@ -0,0 +1,2 @@ +`all' is up to date. +exit status 0 Property changes on: head/contrib/bmake/unit-tests/archive-suffix.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/archive-suffix.mk =================================================================== --- head/contrib/bmake/unit-tests/archive-suffix.mk (nonexistent) +++ head/contrib/bmake/unit-tests/archive-suffix.mk (revision 365366) @@ -0,0 +1,23 @@ +# $NetBSD: archive-suffix.mk,v 1.1 2020/08/29 14:47:26 rillig Exp $ +# +# Between 2020-08-23 and 2020-08-30, the below code produced an assertion +# failure in Var_Set_with_flags, triggered by Compat_Make, when setting the +# .IMPSRC of an archive node to its .TARGET. +# +# The code assumed that the .TARGET variable of every node would be set, but +# but that is not guaranteed. +# +# Between 2016-03-15 and 2016-03-16 the behavior of the below code changed. +# Until 2016-03-15, it remade the target, starting with 2016-03-16 it says +# "`all' is up to date". + +.SUFFIXES: +.SUFFIXES: .c .o + +all: lib.a(obj1.o) + +.c.o: + : making $@ + +obj1.c: + : $@ Property changes on: head/contrib/bmake/unit-tests/archive-suffix.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/archive.exp =================================================================== --- head/contrib/bmake/unit-tests/archive.exp (nonexistent) +++ head/contrib/bmake/unit-tests/archive.exp (revision 365366) @@ -0,0 +1,13 @@ +rm -f libprog.a +ar cru libprog.a archive.mk modmisc.mk varmisc.mk +ranlib libprog.a +ar t libprog.a +archive.mk +modmisc.mk +varmisc.mk +list-archive-wildcard: archive.mk +list-archive-wildcard: ternary.mk +depend-on-existing-member +`depend-on-nonexistent-member' is up to date. +rm -f libprog.a +exit status 0 Index: head/contrib/bmake/unit-tests/archive.mk =================================================================== --- head/contrib/bmake/unit-tests/archive.mk (nonexistent) +++ head/contrib/bmake/unit-tests/archive.mk (revision 365366) @@ -0,0 +1,45 @@ +# $NetBSD: archive.mk,v 1.5 2020/08/23 17:51:24 rillig Exp $ +# +# Very basic demonstration of handling archives, based on the description +# in PSD.doc/tutorial.ms. + +ARCHIVE= libprog.${EXT.a} +FILES= archive.${EXT.mk} modmisc.${EXT.mk} varmisc.mk + +EXT.a= a +EXT.mk= mk + +MAKE_CMD= ${.MAKE} -f ${MAKEFILE} +RUN?= @set -eu; + +all: + ${RUN} ${MAKE_CMD} remove-archive + ${RUN} ${MAKE_CMD} create-archive + ${RUN} ${MAKE_CMD} list-archive + ${RUN} ${MAKE_CMD} list-archive-wildcard + ${RUN} ${MAKE_CMD} depend-on-existing-member + ${RUN} ${MAKE_CMD} depend-on-nonexistent-member + ${RUN} ${MAKE_CMD} remove-archive + +create-archive: ${ARCHIVE} +${ARCHIVE}: ${ARCHIVE}(${FILES}) + ar cru ${.TARGET} ${.OODATE} + ranlib ${.TARGET} + +list-archive: ${ARCHIVE} + ar t ${.ALLSRC} + +# XXX: I had expected that this dependency would select all *.mk files from +# the archive. Instead, the globbing is done in the current directory. +# To prevent an overly long file list, the pattern is restricted to [at]*.mk. +list-archive-wildcard: ${ARCHIVE}([at]*.mk) + ${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@} + +depend-on-existing-member: ${ARCHIVE}(archive.mk) + ${RUN} echo $@ + +depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) + ${RUN} echo $@ + +remove-archive: + rm -f ${ARCHIVE} Property changes on: head/contrib/bmake/unit-tests/archive.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cmd-interrupt.exp =================================================================== --- head/contrib/bmake/unit-tests/cmd-interrupt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cmd-interrupt.exp (revision 365366) @@ -0,0 +1,9 @@ +> cmd-interrupt-ordinary +make: *** cmd-interrupt-ordinary removed +interrupt-ordinary: ok +> cmd-interrupt-phony +make: *** cmd-interrupt-phony removed +interrupt-phony: ok +> cmd-interrupt-precious +interrupt-precious: ok +exit status 0 Property changes on: head/contrib/bmake/unit-tests/cmd-interrupt.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/cmd-interrupt.mk =================================================================== --- head/contrib/bmake/unit-tests/cmd-interrupt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cmd-interrupt.mk (revision 365366) @@ -0,0 +1,50 @@ +# $NetBSD: cmd-interrupt.mk,v 1.2 2020/08/28 18:16:22 rillig Exp $ +# +# Tests for interrupting a command. +# +# If a command is interrupted (usually by the user, here by itself), the +# target is removed. This is to avoid having an unfinished target that +# would be newer than all of its sources and would therefore not be +# tried again in the next run. +# +# This happens for ordinary targets as well as for .PHONY targets, even +# though the .PHONY targets usually do not correspond to a file. +# +# To protect the target from being removed, the target has to be marked with +# the special source .PRECIOUS. These targets need to ensure for themselves +# that interrupting them does not leave an inconsistent state behind. +# +# See also: +# CompatDeleteTarget + +all: clean-before interrupt-ordinary interrupt-phony interrupt-precious clean-after + +clean-before clean-after: .PHONY + @rm -f cmd-interrupt-ordinary cmd-interrupt-phony cmd-interrupt-precious + +interrupt-ordinary: .PHONY + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-ordinary || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-ordinary) :? error : ok } + +interrupt-phony: .PHONY + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-phony || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-phony) :? error : ok } + +interrupt-precious: .PRECIOUS + @${.MAKE} ${MAKEFLAGS} -f ${MAKEFILE} cmd-interrupt-precious || true + # The ././ is necessary to work around the file cache. + @echo ${.TARGET}: ${exists(././cmd-interrupt-precious) :? ok : error } + +cmd-interrupt-ordinary: + > ${.TARGET} + @kill -INT ${.MAKE.PID} + +cmd-interrupt-phony: .PHONY + > ${.TARGET} + @kill -INT ${.MAKE.PID} + +cmd-interrupt-precious: .PRECIOUS + > ${.TARGET} + @kill -INT ${.MAKE.PID} Property changes on: head/contrib/bmake/unit-tests/cmd-interrupt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cmdline.exp =================================================================== --- head/contrib/bmake/unit-tests/cmdline.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cmdline.exp (revision 365366) @@ -0,0 +1,5 @@ +makeobjdir-direct: +show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 +makeobjdir-indirect: +show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/ +exit status 0 Index: head/contrib/bmake/unit-tests/cmdline.mk =================================================================== --- head/contrib/bmake/unit-tests/cmdline.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cmdline.mk (revision 365366) @@ -0,0 +1,37 @@ +# $NetBSD: cmdline.mk,v 1.1 2020/07/28 22:44:44 rillig Exp $ +# +# Tests for command line parsing and related special variables. + +RUN?= @set -eu; +TMPBASE?= /tmp +SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID +SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID +MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r +DIR2= ${TMPBASE}/${SUB2} +DIR12= ${TMPBASE}/${SUB1}/${SUB2} + +all: prepare-dirs +all: makeobjdir-direct makeobjdir-indirect + +prepare-dirs: + ${RUN} rm -rf ${DIR2} ${DIR12} + ${RUN} mkdir -p ${DIR2} ${DIR12} + +# The .OBJDIR can be set via the MAKEOBJDIR command line variable. +# It must be a command line variable; an environment variable would not work. +makeobjdir-direct: + @echo $@: + ${RUN} ${MAKE_CMD} MAKEOBJDIR=${DIR2} show-objdir + +# The .OBJDIR can be set via the MAKEOBJDIR command line variable, +# and that variable could even contain the usual modifiers. +# Since the .OBJDIR=MAKEOBJDIR assignment happens very early, +# the SUB2 variable in the modifier is not defined yet and is therefore empty. +# The SUB1 in the resulting path comes from the environment variable TMPBASE, +# see MAKE_CMD. +makeobjdir-indirect: + @echo $@: + ${RUN} ${MAKE_CMD} MAKEOBJDIR='$${TMPBASE}/$${SUB2}' show-objdir + +show-objdir: + @echo $@: ${.OBJDIR:Q} Property changes on: head/contrib/bmake/unit-tests/cmdline.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk (revision 365366) @@ -0,0 +1,53 @@ +# $NetBSD: cond-cmp-numeric-eq.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the == operator in .if conditions. + +# This comparison yields the same result, whether numeric or character-based. +.if 1 == 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 == 2 +.error +.endif + +.if 2 == 1 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 == 2000e4 +.else +.error +.endif + +.if 2000e4 == 2e7 +.else +.error +.endif + +# Trailing zeroes after the decimal point are irrelevant for the numeric +# value. +.if 3.30000 == 3.3 +.else +.error +.endif + +.if 3.3 == 3.30000 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 == 1.000000000000000002 +.else +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-eq.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk (revision 365366) @@ -0,0 +1,75 @@ +# $NetBSD: cond-cmp-numeric-ge.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the >= operator in .if conditions. + +# When both sides are equal, the >= operator always yields true. +.if 1 >= 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 >= 2 +.error +.endif + +.if 2 >= 1 +.else +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be >= 14 since its first digit is greater. +.if 5 >= 14 +.error +.endif + +.if 14 >= 5 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 >= 1e8 +.error +.endif + +.if 1e8 >= 2e7 +.else +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 >= 111.222 +.error +.endif + +.if 111.222 >= 3.141 +.else +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 >= 3.7 +.error +.endif + +.if 3.7 >= 3.30 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 >= 1.000000000000000002 +.else +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-ge.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk (revision 365366) @@ -0,0 +1,73 @@ +# $NetBSD: cond-cmp-numeric-gt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the > operator in .if conditions. + +# When both sides are equal, the > operator always yields false. +.if 1 > 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 > 2 +.error +.endif + +.if 2 > 1 +.else +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be > 14 since its first digit is greater. +.if 5 > 14 +.error +.endif + +.if 14 > 5 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 > 1e8 +.error +.endif + +.if 1e8 > 2e7 +.else +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 > 111.222 +.error +.endif + +.if 111.222 > 3.141 +.else +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 > 3.7 +.error +.endif + +.if 3.7 > 3.30 +.else +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 > 1.000000000000000002 +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-gt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-le.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk (revision 365366) @@ -0,0 +1,75 @@ +# $NetBSD: cond-cmp-numeric-le.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the <= operator in .if conditions. + +# When both sides are equal, the <= operator always yields true. +.if 1 <= 1 +.else +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 <= 2 +.else +.error +.endif + +.if 2 <= 1 +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be >= 14 since its first digit is greater. +.if 5 <= 14 +.else +.error +.endif + +.if 14 <= 5 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 <= 1e8 +.else +.error +.endif + +.if 1e8 <= 2e7 +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 <= 111.222 +.else +.error +.endif + +.if 111.222 <= 3.141 +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 <= 3.7 +.else +.error +.endif + +.if 3.7 <= 3.30 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 <= 1.000000000000000002 +.else +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-le.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk (revision 365366) @@ -0,0 +1,73 @@ +# $NetBSD: cond-cmp-numeric-lt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the < operator in .if conditions. + +# When both sides are equal, the < operator always yields false. +.if 1 < 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 < 2 +.else +.error +.endif + +.if 2 < 1 +.error +.endif + +# If this comparison were character-based instead of numerical, the +# 5 would be > 14 since its first digit is greater. +.if 5 < 14 +.else +.error +.endif + +.if 14 < 5 +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 < 1e8 +.else +.error +.endif + +.if 1e8 < 2e7 +.error +.endif + +# Floating pointer numbers can be compared as well. +# This might be tempting to use for version numbers, but there are a few pitfalls. +.if 3.141 < 111.222 +.else +.error +.endif + +.if 111.222 < 3.141 +.error +.endif + +# When parsed as a version number, 3.30 is greater than 3.7. +# Since make parses numbers as plain numbers, that leads to wrong results. +# Numeric comparisons are not suited for comparing version number. +.if 3.30 < 3.7 +.else +.error +.endif + +.if 3.7 < 3.30 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 < 1.000000000000000002 +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-lt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk (revision 365366) @@ -0,0 +1,49 @@ +# $NetBSD: cond-cmp-numeric-ne.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $ +# +# Tests for numeric comparisons with the != operator in .if conditions. + +# When both sides are equal, the != operator always yields false. +.if 1 != 1 +.error +.endif + +# This comparison yields the same result, whether numeric or character-based. +.if 1 != 2 +.else +.error +.endif + +.if 2 != 1 +.else +.error +.endif + +# Scientific notation is supported, as per strtod. +.if 2e7 != 2000e4 +.error +.endif + +.if 2000e4 != 2e7 +.error +.endif + +# Trailing zeroes after the decimal point are irrelevant for the numeric +# value. +.if 3.30000 != 3.3 +.error +.endif + +.if 3.3 != 3.30000 +.error +.endif + +# As of 2020-08-23, numeric comparison is implemented as parsing both sides +# as double, and then performing a normal comparison. The range of double is +# typically 16 or 17 significant digits, therefore these two numbers seem to +# be equal. +.if 1.000000000000000001 != 1.000000000000000002 +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric-ne.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-numeric.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-cmp-numeric.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-numeric.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-numeric.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-cmp-numeric.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for numeric comparisons in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-cmp-numeric.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-cmp-string.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-string.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-string.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) +make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing") +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/cond-cmp-string.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-cmp-string.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-cmp-string.mk (revision 365366) @@ -0,0 +1,39 @@ +# $NetBSD: cond-cmp-string.mk,v 1.3 2020/08/20 18:43:19 rillig Exp $ +# +# Tests for string comparisons in .if conditions. + +# This is a simple comparison of string literals. +# Nothing surprising here. +.if "str" != "str" +.error +.endif + +# The right-hand side of the comparison may be written without quotes. +.if "str" != str +.error +.endif + +# The left-hand side of the comparison must be enclosed in quotes. +# This one is not enclosed in quotes and thus generates an error message. +.if str != str +.error +.endif + +# The left-hand side of the comparison requires a defined variable. +# The variable named "" is not defined, but applying the :U modifier to it +# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here. +.if ${:Ustr} != "str" +.error +.endif + +# Any character in a string literal may be escaped using a backslash. +# This means that "\n" does not mean a newline but a simple "n". +.if "string" != "\s\t\r\i\n\g" +.error +.endif + +# It is not possible to concatenate two string literals to form a single +# string. +.if "string" != "str""ing" +.error +.endif Property changes on: head/contrib/bmake/unit-tests/cond-cmp-string.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-commands.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-commands.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-commands.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-func-commands.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-commands.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-commands.mk (revision 365366) @@ -0,0 +1,36 @@ +# $NetBSD: cond-func-commands.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $ +# +# Tests for the commands() function in .if conditions. + +.MAIN: all + +# The target "target" does not exist yet, therefore it cannot have commands. +.if commands(target) +.error +.endif + +target: + +# Now the target exists, but it still has no commands. +.if commands(target) +.error +.endif + +target: + # not a command + +# Even after the comment, the target still has no commands. +.if commands(target) +.error +.endif + +target: + @:; + +# Finally the target has commands. +.if !commands(target) +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-commands.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-defined.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-defined.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-defined.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined() +make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B)) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/cond-func-defined.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-defined.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-defined.mk (revision 365366) @@ -0,0 +1,33 @@ +# $NetBSD: cond-func-defined.mk,v 1.3 2020/08/20 17:23:43 rillig Exp $ +# +# Tests for the defined() function in .if conditions. + +DEF= defined +${:UA B}= variable name with spaces + +.if !defined(DEF) +.error +.endif + +# Horizontal whitespace after the opening parenthesis is ignored. +.if !defined( DEF) +.error +.endif + +# Horizontal whitespace before the closing parenthesis is ignored. +.if !defined(DEF ) +.error +.endif + +# The argument of a function must not directly contain whitespace. +.if !defined(A B) +.error +.endif + +# If necessary, the whitespace can be generated by a variable expression. +.if !defined(${:UA B}) +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-defined.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-empty.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-empty.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-empty.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-func-empty.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-empty.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-empty.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-func-empty.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the empty() function in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-empty.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-exists.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-exists.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-exists.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-func-exists.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-exists.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-exists.mk (revision 365366) @@ -0,0 +1,42 @@ +# $NetBSD: cond-func-exists.mk,v 1.4 2020/08/28 12:59:36 rillig Exp $ +# +# Tests for the exists() function in .if conditions. + +.if !exists(.) +.error +.endif + +# The argument to the function must not be enclosed in quotes. +# Neither double quotes nor single quotes are allowed. +.if exists(".") +.error +.endif + +.if exists('.') +.error +.endif + +# The only way to escape characters that would otherwise influence the parser +# is to enclose them in a variable expression. For function arguments, +# neither the backslash nor the dollar sign act as escape character. +.if exists(\.) +.error +.endif + +.if !exists(${:U.}) +.error +.endif + +# The argument to the function can have several variable expressions. +# See cond-func.mk for the characters that cannot be used directly. +.if !exists(${.PARSEDIR}/${.PARSEFILE}) +.error +.endif + +# Whitespace is trimmed on both sides of the function argument. +.if !exists( . ) +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-exists.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-make.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-make.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-make.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-func-make.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-make.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-make.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-func-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the make() function in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-make.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func-target.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func-target.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-target.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-func-target.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func-target.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func-target.mk (revision 365366) @@ -0,0 +1,38 @@ +# $NetBSD: cond-func-target.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $ +# +# Tests for the target() function in .if conditions. + +.MAIN: all + +# The target "target" does not exist yet. +.if target(target) +.error +.endif + +target: + +# The target exists, even though it does not have any commands. +.if !target(target) +.error +.endif + +target: + # not a command + +# Adding a comment to an existing target does not change whether the target +# is defined or not. +.if !target(target) +.error +.endif + +target: + @:; + +# Adding a command to an existing target does not change whether the target +# is defined or not. +.if !target(target) +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func-target.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-func.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-func.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func.exp (revision 365366) @@ -0,0 +1,9 @@ +make: "cond-func.mk" line 29: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 29: Malformed conditional (!defined(A B)) +make: "cond-func.mk" line 44: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 44: Malformed conditional (!defined(A&B)) +make: "cond-func.mk" line 47: warning: Missing closing parenthesis for defined() +make: "cond-func.mk" line 47: Malformed conditional (!defined(A|B)) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/cond-func.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-func.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-func.mk (revision 365366) @@ -0,0 +1,63 @@ +# $NetBSD: cond-func.mk,v 1.1 2020/08/20 17:45:47 rillig Exp $ +# +# Tests for those parts of the functions in .if conditions that are common +# among several functions. +# +# The below test uses the function defined(...) since it has no side-effects, +# the other functions (except empty(...)) would work equally well. + +DEF= defined +${:UA B}= variable name with spaces +${:UVAR(value)}= variable name with parentheses +${:UVAR{value}}= variable name with braces + +.if !defined(DEF) +.error +.endif + +# Horizontal whitespace after the opening parenthesis is ignored. +.if !defined( DEF) +.error +.endif + +# Horizontal whitespace before the closing parenthesis is ignored. +.if !defined(DEF ) +.error +.endif + +# The argument of a function must not directly contain whitespace. +.if !defined(A B) +.error +.endif + +# If necessary, the whitespace can be generated by a variable expression. +.if !defined(${:UA B}) +.error +.endif + +# Characters that could be mistaken for operators must not appear directly +# in a function argument. As with whitespace, these can be generated +# indirectly. +# +# It's not entirely clear why these characters are forbidden. +# The most plausible reason seems to be typo detection. +.if !defined(A&B) +.error +.endif +.if !defined(A|B) +.error +.endif + +# Even parentheses may appear in variable names. +# They must be balanced though. +.if !defined(VAR(value)) +.error +.endif + +# Braces do not have any special meaning when parsing arguments. +.if !defined(VAR{value}) +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-func.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-late.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-late.exp (revision 365365) +++ head/contrib/bmake/unit-tests/cond-late.exp (revision 365366) @@ -1,3 +1,4 @@ +make: Bad conditional expression ` != "no"' in != "no"?: yes no exit status 0 Index: head/contrib/bmake/unit-tests/cond-late.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-late.mk (revision 365365) +++ head/contrib/bmake/unit-tests/cond-late.mk (revision 365366) @@ -1,23 +1,29 @@ -# $NetBSD: cond-late.mk,v 1.1 2020/04/29 23:15:21 rillig Exp $ +# $NetBSD: cond-late.mk,v 1.2 2020/07/25 20:37:46 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. +all: cond-literal + COND.true= "yes" == "yes" COND.false= "yes" != "yes" -all: +cond-literal: @echo ${ ${COND.true} :?yes:no} @echo ${ ${COND.false} :?yes:no} + +VAR+= ${${UNDEF} != "no":?:} +.if empty(VAR:Mpattern) +.endif Index: head/contrib/bmake/unit-tests/cond-op-and.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-op-and.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-and.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-op-and.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-op-and.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-and.mk (revision 365366) @@ -0,0 +1,27 @@ +# $NetBSD: cond-op-and.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the && operator in .if conditions. + +.if 0 && 0 +.error +.endif + +.if 1 && 0 +.error +.endif + +.if 0 && 1 +.error +.endif + +.if !(1 && 1) +.error +.endif + +# The right-hand side is not evaluated since the left-hand side is already +# false. +.if 0 && ${UNDEF} +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-op-and.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-op-not.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-op-not.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-not.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-op-not.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-op-not.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-not.mk (revision 365366) @@ -0,0 +1,22 @@ +# $NetBSD: cond-op-not.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the ! operator in .if conditions. + +# The exclamation mark negates its operand. +.if !1 +.error +.endif + +# Exclamation marks can be chained. +# This doesn't happen in practice though. +.if !!!1 +.error +.endif + +# The ! binds more tightly than the &&. +.if !!0 && 1 +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-op-not.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-op-or.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-op-or.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-or.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-op-or.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-op-or.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-or.mk (revision 365366) @@ -0,0 +1,27 @@ +# $NetBSD: cond-op-or.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $ +# +# Tests for the || operator in .if conditions. + +.if 0 || 0 +.error +.endif + +.if !(1 || 0) +.error +.endif + +.if !(0 || 1) +.error +.endif + +.if !(1 || 1) +.error +.endif + +# The right-hand side is not evaluated since the left-hand side is already +# true. +.if 1 || ${UNDEF} +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-op-or.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-op-parentheses.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-op-parentheses.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-parentheses.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-op-parentheses.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-op-parentheses.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op-parentheses.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-op-parentheses.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for parentheses in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-op-parentheses.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-op.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-op.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word) +make: "cond-op.mk" line 57: Parsing continues until here. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/cond-op.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-op.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-op.mk (revision 365366) @@ -0,0 +1,60 @@ +# $NetBSD: cond-op.mk,v 1.4 2020/08/28 14:07:51 rillig Exp $ +# +# Tests for operators like &&, ||, ! in .if conditions. +# +# See also: +# cond-op-and.mk +# cond-op-not.mk +# cond-op-or.mk +# cond-op-parentheses.mk + +# In make, && binds more tightly than ||, like in C. +# If make had the same precedence for both && and ||, the result would be +# different. +# If || were to bind more tightly than &&, the result would be different +# as well. +.if !(1 || 1 && 0) +.error +.endif + +# If make were to interpret the && and || operators like the shell, the +# implicit binding would be this: +.if (1 || 1) && 0 +.error +.endif + +# The precedence of the ! operator is different from C though. It has a +# lower precedence than the comparison operators. +.if !"word" == "word" +.error +.endif + +# This is how the above condition is actually interpreted. +.if !("word" == "word") +.error +.endif + +# TODO: Demonstrate that the precedence of the ! and == operators actually +# makes a difference. There is a simple example for sure, I just cannot +# wrap my head around it. + +# This condition is malformed because the '!' on the right-hand side must not +# appear unquoted. If any, it must be enclosed in quotes. +# In any case, it is not interpreted as a negation of an unquoted string. +# See CondGetString. +.if "!word" == !word +.error +.endif + +# Surprisingly, the ampersand and pipe are allowed in bare strings. +# That's another opportunity for writing confusing code. +# See CondGetString, which only has '!' in the list of stop characters. +.if "a&&b||c" != a&&b||c +.error +.endif + +# Just in case that parsing should ever stop on the first error. +.info Parsing continues until here. + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-op.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-short.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-short.mk (revision 365365) +++ head/contrib/bmake/unit-tests/cond-short.mk (revision 365366) @@ -1,153 +1,171 @@ -# $NetBSD: cond-short.mk,v 1.7 2020/07/09 22:34:09 sjg Exp $ +# $NetBSD: cond-short.mk,v 1.9 2020/08/19 22:47:09 rillig Exp $ # # Demonstrates that in conditions, the right-hand side of an && or || # is only evaluated if it can actually influence the result. # # Between 2015-10-11 and 2020-06-28, the right-hand side of an && or || # operator was always evaluated, which was wrong. # # The && operator. .if 0 && ${echo "unexpected and" 1>&2 :L:sh} .endif .if 1 && ${echo "expected and" 1>&2 :L:sh} .endif .if 0 && exists(nonexistent${echo "unexpected and exists" 1>&2 :L:sh}) .endif .if 1 && exists(nonexistent${echo "expected and exists" 1>&2 :L:sh}) .endif .if 0 && empty(${echo "unexpected and empty" 1>&2 :L:sh}) .endif .if 1 && empty(${echo "expected and empty" 1>&2 :L:sh}) .endif # "VAR U11" is not evaluated; it was evaluated before 2020-07-02. # The whole !empty condition is only parsed and then discarded. VAR= ${VAR${:U11${echo "unexpected VAR U11" 1>&2 :L:sh}}} VAR13= ${VAR${:U12${echo "unexpected VAR13" 1>&2 :L:sh}}} .if 0 && !empty(VAR${:U13${echo "unexpected U13 condition" 1>&2 :L:sh}}) .endif VAR= ${VAR${:U21${echo "unexpected VAR U21" 1>&2 :L:sh}}} VAR23= ${VAR${:U22${echo "expected VAR23" 1>&2 :L:sh}}} .if 1 && !empty(VAR${:U23${echo "expected U23 condition" 1>&2 :L:sh}}) .endif VAR= # empty again, for the following tests # The :M modifier is only parsed, not evaluated. # Before 2020-07-02, it was wrongly evaluated. .if 0 && !empty(VAR:M${:U${echo "unexpected M pattern" 1>&2 :L:sh}}) .endif .if 1 && !empty(VAR:M${:U${echo "expected M pattern" 1>&2 :L:sh}}) .endif .if 0 && !empty(VAR:S,from,${:U${echo "unexpected S modifier" 1>&2 :L:sh}},) .endif .if 0 && !empty(VAR:C,from,${:U${echo "unexpected C modifier" 1>&2 :L:sh}},) .endif .if 0 && !empty("" == "" :? ${:U${echo "unexpected ? modifier" 1>&2 :L:sh}} :) .endif .if 0 && !empty(VAR:old=${:U${echo "unexpected = modifier" 1>&2 :L:sh}}) .endif .if 0 && !empty(1 2 3:L:@var@${:U${echo "unexpected @ modifier" 1>&2 :L:sh}}@) .endif .if 0 && !empty(:U${:!echo "unexpected exclam modifier" 1>&2 !}) .endif +# Irrelevant assignment modifiers are skipped as well. +.if 0 && ${1 2 3:L:@i@${FIRST::?=$i}@} +.endif +.if 0 && ${1 2 3:L:@i@${LAST::=$i}@} +.endif +.if 0 && ${1 2 3:L:@i@${APPENDED::+=$i}@} +.endif +.if 0 && ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} +.endif +.if defined(FIRST) || defined(LAST) || defined(APPENDED) || defined(RAN) +.warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN} +.endif + # The || operator. .if 1 || ${echo "unexpected or" 1>&2 :L:sh} .endif .if 0 || ${echo "expected or" 1>&2 :L:sh} .endif .if 1 || exists(nonexistent${echo "unexpected or exists" 1>&2 :L:sh}) .endif .if 0 || exists(nonexistent${echo "expected or exists" 1>&2 :L:sh}) .endif .if 1 || empty(${echo "unexpected or empty" 1>&2 :L:sh}) .endif .if 0 || empty(${echo "expected or empty" 1>&2 :L:sh}) .endif # Unreachable nested conditions are skipped completely as well. .if 0 . if ${echo "unexpected nested and" 1>&2 :L:sh} . endif .endif .if 1 .elif ${echo "unexpected nested or" 1>&2 :L:sh} .endif # make sure these do not cause complaint #.MAKEFLAGS: -dc V42 = 42 iV1 = ${V42} iV2 = ${V66} .if defined(V42) && ${V42} > 0 x=Ok .else x=Fail .endif x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo + # this one throws both String comparison operator and # Malformed conditional with cond.c 1.78 # indirect iV2 would expand to "" and treated as 0 .if defined(V66) && ( ${iV2} < ${V42} ) x=Fail .else x=Ok .endif x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo + # next two thow String comparison operator with cond.c 1.78 # indirect iV1 would expand to 42 .if 1 || ${iV1} < ${V42} x=Ok .else x=Fail .endif x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo + .if 1 || ${iV2:U2} < ${V42} x=Ok .else x=Fail .endif x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo + # the same expressions are fine when the lhs is expanded # ${iV1} expands to 42 .if 0 || ${iV1} <= ${V42} x=Ok .else x=Fail .endif x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo + # ${iV2:U2} expands to 2 .if 0 || ${iV2:U2} < ${V42} x=Ok .else x=Fail .endif x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo all: @:;: Index: head/contrib/bmake/unit-tests/cond-token-number.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-token-number.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-number.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-token-number.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-token-number.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-number.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-token-number.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for number tokens in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-token-number.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-token-plain.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-token-plain.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-plain.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-token-plain.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-token-plain.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-plain.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: cond-token-plain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for plain tokens (that is, string literals without quotes) +# in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-token-plain.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-token-string.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-token-string.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-string.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/cond-token-string.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-token-string.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-string.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: cond-token-string.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for quoted and unquoted string literals in .if conditions. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/cond-token-string.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/cond-token-var.exp =================================================================== --- head/contrib/bmake/unit-tests/cond-token-var.exp (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-var.exp (revision 365366) @@ -0,0 +1,7 @@ +make: "cond-token-var.mk" line 9: ok +make: "cond-token-var.mk" line 15: Malformed conditional (${UNDEF} == ${DEF}) +make: "cond-token-var.mk" line 20: Malformed conditional (${DEF} == ${UNDEF}) +make: "cond-token-var.mk" line 29: Malformed conditional (${UNDEF}) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/cond-token-var.mk =================================================================== --- head/contrib/bmake/unit-tests/cond-token-var.mk (nonexistent) +++ head/contrib/bmake/unit-tests/cond-token-var.mk (revision 365366) @@ -0,0 +1,34 @@ +# $NetBSD: cond-token-var.mk,v 1.3 2020/08/20 19:43:42 rillig Exp $ +# +# Tests for variables in .if conditions. + +DEF= defined + +# A defined variable may appear on either side of the comparison. +.if ${DEF} == ${DEF} +.info ok +.else +.error +.endif + +# A variable that appears on the left-hand side must be defined. +.if ${UNDEF} == ${DEF} +.error +.endif + +# A variable that appears on the right-hand side must be defined. +.if ${DEF} == ${UNDEF} +.error +.endif + +# A defined variable may appear as an expression of its own. +.if ${DEF} +.endif + +# An undefined variable generates a warning. +.if ${UNDEF} +.endif + +# The :U modifier turns an undefined variable into an ordinary expression. +.if ${UNDEF:U} +.endif Property changes on: head/contrib/bmake/unit-tests/cond-token-var.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/counter.exp =================================================================== --- head/contrib/bmake/unit-tests/counter.exp (nonexistent) +++ head/contrib/bmake/unit-tests/counter.exp (revision 365366) @@ -0,0 +1,88 @@ +Global:RELEVANT = yes (load-time part) +Global:COUNTER = +Global:NEXT = ${COUNTER::=${COUNTER} a}${COUNTER:[#]} +Global:A = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a" +Global:COUNTER = a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "1" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:A = ${COUNTER::= a a}1 +Global:B = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to " a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a a" +Global:COUNTER = a a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "2" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:B = ${COUNTER::= a a a}2 +Global:C = +Var_Parse: ${NEXT} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER::=${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES +Modifier part: " a a a" +Global:COUNTER = a a a +Result of ${COUNTER::=${COUNTER} a} is "" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Var_Parse: ${COUNTER} a}${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Var_Parse: ${COUNTER:[#]} with VARE_WANTRES|VARE_ASSIGN +Applying ${COUNTER:[...} to " a a a" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "3" (eflags = VARE_WANTRES|VARE_ASSIGN, vflags = none) +Global:C = ${COUNTER::= a a a a}3 +Global:RELEVANT = no +Global:RELEVANT = yes (run-time part) +Result of ${RELEVANT::=yes (run-time part)} is "" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a}1 with VARE_WANTRES +Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a" +Global:COUNTER = a a +Result of ${COUNTER::= a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${A:Q} to "1" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [1] +Result of ${A:Q} is "1" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a a}2 with VARE_WANTRES +Applying ${COUNTER::...} to " a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a a" +Global:COUNTER = a a a +Result of ${COUNTER::= a a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${B:Q} to "2" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [2] +Result of ${B:Q} is "2" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${C:Q} COUNTER=${COUNTER:[#]:Q} with VARE_WANTRES +Var_Parse: ${COUNTER::= a a a a}3 with VARE_WANTRES +Applying ${COUNTER::...} to " a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: " a a a a" +Global:COUNTER = a a a a +Result of ${COUNTER::= a a a a} is "" (eflags = VARE_WANTRES, vflags = none) +Applying ${C:Q} to "3" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [3] +Result of ${C:Q} is "3" (eflags = VARE_WANTRES, vflags = none) +Var_Parse: ${COUNTER:[#]:Q} with VARE_WANTRES +Applying ${COUNTER:[...} to " a a a a" (eflags = VARE_WANTRES, vflags = none) +Modifier part: "#" +Result of ${COUNTER:[#]} is "4" (eflags = VARE_WANTRES, vflags = none) +Applying ${COUNTER:Q} to "4" (eflags = VARE_WANTRES, vflags = none) +QuoteMeta: [4] +Result of ${COUNTER:Q} is "4" (eflags = VARE_WANTRES, vflags = none) +A=1 B=2 C=3 COUNTER=4 +Var_Parse: ${RELEVANT::=no} with VARE_WANTRES +Applying ${RELEVANT::...} to "yes (run-time part)" (eflags = VARE_WANTRES, vflags = none) +Modifier part: "no" +Global:RELEVANT = no +exit status 0 Index: head/contrib/bmake/unit-tests/counter.mk =================================================================== --- head/contrib/bmake/unit-tests/counter.mk (nonexistent) +++ head/contrib/bmake/unit-tests/counter.mk (revision 365366) @@ -0,0 +1,31 @@ +# $NetBSD: counter.mk,v 1.1 2020/08/02 14:53:02 rillig Exp $ +# +# Demonstrates that it is not easily possible to let make count +# the number of times a variable is actually accessed. +# +# As of 2020-08-02, the counter ends up at having 4 words, even +# though the NEXT variable is only accessed 3 times. This is +# surprising. +# +# A hint to this surprising behavior is that the variables don't +# get fully expanded. For example, A does not simply contain the +# value "1" but an additional unexpanded ${COUNTER:...} before it. + +RELEVANT= yes (load-time part) # just to filter the output + +COUNTER= # zero + +NEXT= ${COUNTER::=${COUNTER} a}${COUNTER:[#]} + +# This variable is first set to empty and then expanded. +# See parse.c, function Parse_DoVar, keyword "!Var_Exists". +A:= ${NEXT} +B:= ${NEXT} +C:= ${NEXT} + +RELEVANT= no + +all: + @: ${RELEVANT::=yes (run-time part)} + @echo A=${A:Q} B=${B:Q} C=${C:Q} COUNTER=${COUNTER:[#]:Q} + @: ${RELEVANT::=no} Property changes on: head/contrib/bmake/unit-tests/counter.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-colon.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-colon.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-colon.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/dep-colon.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-colon.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-colon.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: dep-colon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the : operator in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/dep-colon.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-double-colon.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-double-colon.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-double-colon.exp (revision 365366) @@ -0,0 +1,5 @@ +command 1a +command 1b +command 2a +command 2b +exit status 0 Index: head/contrib/bmake/unit-tests/dep-double-colon.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-double-colon.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-double-colon.mk (revision 365366) @@ -0,0 +1,11 @@ +# $NetBSD: dep-double-colon.mk,v 1.3 2020/08/22 12:42:32 rillig Exp $ +# +# Tests for the :: operator in dependency declarations. + +all:: + @echo 'command 1a' + @echo 'command 1b' + +all:: + @echo 'command 2a' + @echo 'command 2b' Property changes on: head/contrib/bmake/unit-tests/dep-double-colon.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-exclam.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-exclam.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-exclam.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/dep-exclam.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-exclam.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-exclam.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: dep-exclam.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ! operator in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/dep-exclam.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-none.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-none.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-none.exp (revision 365366) @@ -0,0 +1,4 @@ +make: no target to make. + +make: stopped in unit-tests +exit status 2 Property changes on: head/contrib/bmake/unit-tests/dep-none.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-none.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-none.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-none.mk (revision 365366) @@ -0,0 +1,3 @@ +# $NetBSD: dep-none.mk,v 1.1 2020/08/22 12:51:11 rillig Exp $ +# +# Test for a Makefile that declares no target at all. Property changes on: head/contrib/bmake/unit-tests/dep-none.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-var.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-var.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-var.exp (revision 365366) @@ -0,0 +1,2 @@ +def2 +exit status 0 Property changes on: head/contrib/bmake/unit-tests/dep-var.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-var.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-var.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-var.mk (revision 365366) @@ -0,0 +1,33 @@ +# $NetBSD: dep-var.mk,v 1.1 2020/08/22 16:51:26 rillig Exp $ +# +# Tests for variable references in dependency declarations. +# +# Uh oh, this feels so strange that probably nobody uses it. But it seems to +# be the only way to reach the lower half of SuffExpandChildren. + +# XXX: The -dv log says: +# Var_Parse: ${UNDEF1} with VARE_UNDEFERR|VARE_WANTRES +# but no error message is generated for this line. +# The variable expression ${UNDEF1} simply expands to an empty string. +all: ${UNDEF1} + +# Using a double dollar in order to circumvent immediate variable expansion +# feels like unintended behavior. At least the manual page says nothing at +# all about defined or undefined variables in dependency lines. +# +# At the point where the expression ${DEF2} is expanded, the variable DEF2 +# is defined, so everything's fine. +all: $${DEF2} + +# This variable is not defined at all. +# XXX: The -dv log says: +# Var_Parse: ${UNDEF3} with VARE_UNDEFERR|VARE_WANTRES +# but no error message is generated for this line, just like for UNDEF1. +# The variable expression ${UNDEF3} simply expands to an empty string. +all: $${UNDEF3} + +UNDEF1= undef1 +DEF2= def2 + +undef1 def2: + @echo ${.TARGET} Property changes on: head/contrib/bmake/unit-tests/dep-var.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep-wildcards.exp =================================================================== --- head/contrib/bmake/unit-tests/dep-wildcards.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep-wildcards.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/dep-wildcards.mk =================================================================== --- head/contrib/bmake/unit-tests/dep-wildcards.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep-wildcards.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: dep-wildcards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for wildcards such as *.c in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/dep-wildcards.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dep.exp =================================================================== --- head/contrib/bmake/unit-tests/dep.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dep.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/dep.mk =================================================================== --- head/contrib/bmake/unit-tests/dep.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dep.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: dep.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for dependency declarations, such as "target: sources". + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/dep.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-exec.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-exec.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-exec.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-exec.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-exec.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-exec.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-exec.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .EXEC in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-exec.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-ignore.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-ignore.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-ignore.exp (revision 365366) @@ -0,0 +1,11 @@ +ignore-errors begin +false ignore-errors +ignore-errors end +all begin +*** Error code 1 (ignored) +false all +*** Error code 1 (continuing) + +Stop. +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/depsrc-ignore.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-ignore.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-ignore.mk (revision 365366) @@ -0,0 +1,67 @@ +# $NetBSD: depsrc-ignore.mk,v 1.4 2020/08/29 16:13:27 rillig Exp $ +# +# Tests for the special source .IGNORE in dependency declarations, +# which ignores any command failures for that target. +# +# Even though ignore-errors fails, the all target is still made. +# Since the all target is not marked with .IGNORE, it stops at the +# first failing command. +# +# XXX: The ordering of the messages in the output is confusing. +# The "ignored" comes much too late to be related to the "false +# ignore-errors". This is due to stdout being buffered. +# +# The "continuing" message comes from the -k option. If there had been +# other targets independent of "all", these would be built as well. +# +# Enabling the debugging option -de changes the order in which the messages +# appear. Now the "ignored" message is issued in the correct position. +# The explanation for the output reordering is that the output is buffered. +# As the manual page says, in debugging mode stdout is line buffered. +# In these tests the output is redirected to a file, therefore stdout is +# fully buffered. +# +# This is what actually happens, as of 2020-08-29. To verify it, set the +# following breakpoints in CompatRunCommand: +# +# * the "!silent" line, to see all commands. +# * the "fflush" line, to see stdout being flushed. +# * the "status = WEXITSTATUS" line +# * the "(continuing)" line +# * the "(ignored)" line +# +# The breakpoints are visited in the following order: +# +# "ignore-errors begin" +# Goes directly to STDOUT_FILENO since it is run in a child process. +# "false ignore-errors" +# Goes to the stdout buffer (CompatRunCommand, keyword "!silent") and +# the immediate call to fflush(stdout) copies it to STDOUT_FILENO. +# "*** Error code 1 (ignored)" +# Goes to the stdout buffer but is not flushed (CompatRunCommand, near +# the end). +# "ignore-errors end" +# Goes directly to STDOUT_FILENO. +# "all begin" +# Goes directly to STDOUT_FILENO. +# "false all" +# Goes to the stdout buffer, where the "*** Error code 1 (ignored)" is +# still waiting to be flushed. These two lines are flushed now. +# "*** Error code 1 (continuing)" +# Goes to the stdout buffer. +# "Stop." +# Goes to the stdout buffer. +# exit(1) +# Flushes the stdout buffer to STDOUT_FILENO. + +all: ignore-errors + +ignore-errors: .IGNORE + @echo $@ begin + false $@ + @echo $@ end + +all: + @echo $@ begin + false $@ + @echo $@ end Property changes on: head/contrib/bmake/unit-tests/depsrc-ignore.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-made.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-made.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-made.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-made.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-made.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-made.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-made.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .MADE in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-made.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-make.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-make.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-make.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-make.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-make.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-make.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .MAKE in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-make.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-meta.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-meta.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-meta.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-meta.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-meta.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-meta.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .META in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-meta.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-nometa.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nometa.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nometa.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-nometa.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nometa.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nometa.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nometa.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOMETA in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-nometa.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nometa_cmp.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nometa_cmp.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOMETA_CMP in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-nometa_cmp.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-nopath.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nopath.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nopath.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-nopath.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-nopath.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-nopath.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOPATH in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-nopath.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-notmain.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-notmain.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-notmain.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-notmain.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-notmain.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-notmain.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-notmain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .NOTMAIN in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-notmain.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-optional.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-optional.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-optional.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-optional.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-optional.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-optional.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-optional.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .OPTIONAL in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-optional.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-phony.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-phony.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-phony.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-phony.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-phony.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-phony.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .PHONY in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-phony.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-precious.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-precious.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-precious.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-precious.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-precious.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-precious.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .PRECIOUS in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-precious.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-recursive.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-recursive.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-recursive.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-recursive.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-recursive.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-recursive.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-recursive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .RECURSIVE in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-recursive.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-silent.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-silent.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-silent.exp (revision 365366) @@ -0,0 +1,4 @@ +one +two +three +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-silent.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-silent.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-silent.mk (revision 365366) @@ -0,0 +1,12 @@ +# $NetBSD: depsrc-silent.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special source .SILENT in dependency declarations, +# which hides the commands, no matter whether they are prefixed with +# '@' or not. + +# Without the .SILENT, the commands 'echo one' and 'echo two' would be +# written to stdout. +all: .SILENT + echo one + echo two + @echo three Property changes on: head/contrib/bmake/unit-tests/depsrc-silent.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-use.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-use.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-use.exp (revision 365366) @@ -0,0 +1,6 @@ +first 1 +first 2 +second 1 +second 2 +directly +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-use.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-use.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-use.mk (revision 365366) @@ -0,0 +1,24 @@ +# $NetBSD: depsrc-use.mk,v 1.4 2020/08/22 12:30:57 rillig Exp $ +# +# Tests for the special source .USE in dependency declarations, +# which allows to append common commands to other targets. + +all: action directly + +first: .USE + @echo first 1 # Using ${.TARGET} here would expand to "action" + @echo first 2 + +second: .USE + @echo second 1 + @echo second 2 + +# It's possible but uncommon to have a .USE target with no commands. +# This may happen as the result of expanding a .for loop. +empty: .USE + +# It's possible but uncommon to directly make a .USEBEFORE target. +directly: .USE + @echo directly + +action: first second empty Property changes on: head/contrib/bmake/unit-tests/depsrc-use.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp (revision 365366) @@ -0,0 +1,2 @@ +double-colon early 1 +exit status 0 Property changes on: head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk (revision 365366) @@ -0,0 +1,30 @@ +# $NetBSD: depsrc-usebefore-double-colon.mk,v 1.1 2020/08/22 08:29:13 rillig Exp $ +# +# Tests for the special source .USEBEFORE in dependency declarations, +# combined with the double-colon dependency operator. + +all: action + +# The dependency operator :: allows commands to be added later to the same +# target. +double-colon:: .USEBEFORE + @echo double-colon early 1 + +# This command is ignored, which kind of makes sense since this dependency +# declaration has no .USEBEFORE source. +double-colon:: + @echo double-colon early 2 + +# XXX: This command is ignored even though it has a .USEBEFORE source. +# This is unexpected. +double-colon:: .USEBEFORE + @echo double-colon early 3 + +# At this point, the commands from the .USEBEFORE targets are copied to +# the "action" target. +action: double-colon + +# This command is not added to the "action" target since it comes too late. +# The commands had been copied in the previous line already. +double-colon:: + @echo double-colon late Property changes on: head/contrib/bmake/unit-tests/depsrc-usebefore-double-colon.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-usebefore.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-usebefore.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-usebefore.exp (revision 365366) @@ -0,0 +1,6 @@ +first 1 +first 2 +second 1 +second 2 +directly +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-usebefore.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-usebefore.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-usebefore.mk (revision 365366) @@ -0,0 +1,24 @@ +# $NetBSD: depsrc-usebefore.mk,v 1.5 2020/08/22 11:53:18 rillig Exp $ +# +# Tests for the special source .USEBEFORE in dependency declarations, +# which allows to prepend common commands to other targets. + +all: action directly + +first: .USEBEFORE + @echo first 1 # Using ${.TARGET} here would expand to "action" + @echo first 2 # Using ${.TARGET} here would expand to "action" + +second: .USEBEFORE + @echo second 1 + @echo second 2 + +# It is possible but uncommon to have a .USEBEFORE target with no commands. +# This may happen as the result of expanding a .for loop. +empty: .USEBEFORE + +# It is possible but uncommon to directly make a .USEBEFORE target. +directly: .USEBEFORE + @echo directly + +action: second first empty Property changes on: head/contrib/bmake/unit-tests/depsrc-usebefore.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc-wait.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc-wait.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-wait.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc-wait.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc-wait.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc-wait.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: depsrc-wait.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special source .WAIT in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc-wait.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/depsrc.exp =================================================================== --- head/contrib/bmake/unit-tests/depsrc.exp (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/depsrc.mk =================================================================== --- head/contrib/bmake/unit-tests/depsrc.mk (nonexistent) +++ head/contrib/bmake/unit-tests/depsrc.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: depsrc.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special sources (those starting with a dot, followed by +# uppercase letters) in dependency declarations, such as .PHONY. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/depsrc.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-begin.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-begin.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-begin.exp (revision 365366) @@ -0,0 +1,4 @@ +: parse time +: .BEGIN +: all +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-begin.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-begin.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-begin.mk (revision 365366) @@ -0,0 +1,13 @@ +# $NetBSD: deptgt-begin.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special target .BEGIN in dependency declarations, +# which is a container for commands that are run before any other +# commands from the shell lines. + +.BEGIN: + : $@ + +all: + : $@ + +_!= echo : parse time 1>&2 Property changes on: head/contrib/bmake/unit-tests/deptgt-begin.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-default.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-default.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-default.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-default.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-default.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-default.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .DEFAULT in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-default.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-delete_on_error.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-delete_on_error.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-delete_on_error.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-delete_on_error.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-delete_on_error.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-delete_on_error.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-delete_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .DELETE_ON_ERROR in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-delete_on_error.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-end.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-end.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-end.exp (revision 365366) @@ -0,0 +1,4 @@ +: .BEGIN +: all +: .END +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-end.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-end.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-end.mk (revision 365366) @@ -0,0 +1,13 @@ +# $NetBSD: deptgt-end.mk,v 1.3 2020/08/29 17:34:21 rillig Exp $ +# +# Tests for the special target .END in dependency declarations, +# which is run after making the desired targets. + +.BEGIN: + : $@ + +.END: + : $@ + +all: + : $@ Property changes on: head/contrib/bmake/unit-tests/deptgt-end.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-error.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-error.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-error.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-error.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-error.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-error.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .ERROR in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-error.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-ignore.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-ignore.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-ignore.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-ignore.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-ignore.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-ignore.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-ignore.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .IGNORE in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-ignore.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-interrupt.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-interrupt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-interrupt.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-interrupt.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-interrupt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-interrupt.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-interrupt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .INTERRUPT in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-interrupt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-main.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-main.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-main.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-main.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-main.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-main.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-main.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .MAIN in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-main.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-makeflags.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-makeflags.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-makeflags.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-makeflags.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-makeflags.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-makeflags.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .MAKEFLAGS in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-makeflags.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-no_parallel.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-no_parallel.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-no_parallel.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-no_parallel.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-no_parallel.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-no_parallel.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-no_parallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NO_PARALLEL in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-no_parallel.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-nopath.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-nopath.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-nopath.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-nopath.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-nopath.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-nopath.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-nopath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NOPATH in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-nopath.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-notparallel.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-notparallel.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-notparallel.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-notparallel.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-notparallel.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-notparallel.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-notparallel.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .NOTPARALLEL in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-notparallel.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-objdir.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-objdir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-objdir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-objdir.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-objdir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-objdir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .OBJDIR in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-objdir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-order.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-order.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-order.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-order.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-order.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-order.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-order.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .ORDER in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-order.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-path-suffix.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-path-suffix.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-path-suffix.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-path-suffix.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-path-suffix.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-path-suffix.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-path-suffix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PATH.suffix in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-path-suffix.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-path.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-path.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-path.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-path.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-path.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-path.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PATH in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-path.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-phony.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-phony.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-phony.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-phony.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-phony.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-phony.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-phony.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PHONY in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-phony.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-precious.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-precious.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-precious.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-precious.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-precious.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-precious.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-precious.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .PRECIOUS in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-precious.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-shell.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-shell.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-shell.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-shell.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-shell.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-shell.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .SHELL in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-shell.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-silent.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-silent.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-silent.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-silent.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-silent.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-silent.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .SILENT in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-silent.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-stale.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-stale.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-stale.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-stale.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-stale.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-stale.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: deptgt-stale.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special target .STALE in dependency declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-stale.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt-suffixes.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt-suffixes.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-suffixes.exp (revision 365366) @@ -0,0 +1,7 @@ +#*** Suffixes: +# `.custom-null' [1] (SUFF_NULL) +# To: +# From: +# Search Path: . .. +#*** Transformations: +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt-suffixes.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt-suffixes.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt-suffixes.mk (revision 365366) @@ -0,0 +1,18 @@ +# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $ +# +# Tests for the special target .SUFFIXES in dependency declarations. +# +# See also: +# varname-dot-includes.mk +# varname-dot-libs.mk + +.MAKEFLAGS: -dg1 + +.SUFFIXES: .custom-null + +# TODO: What is the effect of this? How is it useful? +.NULL: .custom-null +.PATH.custom-null: . .. + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt-suffixes.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/deptgt.exp =================================================================== --- head/contrib/bmake/unit-tests/deptgt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/deptgt.mk =================================================================== --- head/contrib/bmake/unit-tests/deptgt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/deptgt.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: deptgt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special targets like .BEGIN or .SUFFIXES in dependency +# declarations. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/deptgt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dir-expand-path.exp =================================================================== --- head/contrib/bmake/unit-tests/dir-expand-path.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dir-expand-path.exp (revision 365366) @@ -0,0 +1,4 @@ +dir-expand-path.dir.1/file1.src +dir-expand-path.dir.1/file2.src +dir-expand-path.dir.2/file3.src +exit status 0 Index: head/contrib/bmake/unit-tests/dir-expand-path.mk =================================================================== --- head/contrib/bmake/unit-tests/dir-expand-path.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dir-expand-path.mk (revision 365366) @@ -0,0 +1,19 @@ +# $NetBSD: dir-expand-path.mk,v 1.1 2020/08/22 21:55:54 rillig Exp $ +# +# Tests for filename expansion in the search path. + +_!= rm -rf dir-expand-path.dir.* +_!= mkdir dir-expand-path.dir.1 +_!= mkdir dir-expand-path.dir.2 +_!= touch dir-expand-path.dir.1/file1.src +_!= touch dir-expand-path.dir.1/file2.src +_!= touch dir-expand-path.dir.2/file3.src + +.PATH: dir-expand-path.dir.1 +.PATH: dir-expand-path.dir.2 + +all: *.src + @printf '%s\n' ${.ALLSRC:O} + +.END: + @rm -rf dir-expand-path.dir.* Property changes on: head/contrib/bmake/unit-tests/dir-expand-path.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/dir.exp =================================================================== --- head/contrib/bmake/unit-tests/dir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/dir.exp (revision 365366) @@ -0,0 +1,19 @@ +1 +2 +3 +4 +5 +13 +14 +15 +pre-patch +pre-configure +patch +configure +fetch +fetch-post +extract +extract-post +dup-1 +single-word +exit status 0 Index: head/contrib/bmake/unit-tests/dir.mk =================================================================== --- head/contrib/bmake/unit-tests/dir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/dir.mk (revision 365366) @@ -0,0 +1,58 @@ +# $NetBSD: dir.mk,v 1.4 2020/07/31 20:16:21 rillig Exp $ +# +# Tests for dir.c. + +# Dependency lines may use braces for expansion. +all: {one,two,three} + +one: + @echo 1 +two: + @echo 2 +three: + @echo 3 + +# The braces may start in the middle of a word. +all: f{our,ive} + +four: + @echo 4 +five: + @echo 5 +six: + @echo 6 + +# But nested braces don't work. +all: {{thi,fou}r,fif}teen + +thirteen: + @echo 13 +fourteen: + @echo 14 +fifteen: + @echo 15 + +# There may be multiple brace groups side by side. +all: {pre-,}{patch,configure} + +pre-patch patch pre-configure configure: + @echo $@ + +# Empty pieces are allowed in the braces. +all: {fetch,extract}{,-post} + +fetch fetch-post extract extract-post: + @echo $@ + +# The expansions may have duplicates. +# These are merged together because of the dependency line. +all: dup-{1,1,1,1,1,1,1} + +dup-1: + @echo $@ + +# Other than in Bash, the braces are also expanded if there is no comma. +all: {{{{{{{{{{single-word}}}}}}}}}} + +single-word: + @echo $@ Property changes on: head/contrib/bmake/unit-tests/dir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-elif.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-elif.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elif.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-elif.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-elif.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elif.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-elif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elif directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-elif.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-elifdef.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-elifdef.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifdef.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-elifdef.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-elifdef.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifdef.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifdef directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-elifdef.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-elifmake.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-elifmake.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifmake.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-elifmake.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-elifmake.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifmake.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifmake directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-elifmake.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-elifndef.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-elifndef.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifndef.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-elifndef.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-elifndef.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifndef.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifndef directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-elifndef.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-elifnmake.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-elifnmake.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifnmake.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-elifnmake.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-elifnmake.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-elifnmake.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-elifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .elifnmake directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-elifnmake.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-else.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-else.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-else.exp (revision 365366) @@ -0,0 +1,8 @@ +make: "directive-else.mk" line 10: ok +make: "directive-else.mk" line 14: ok +make: "directive-else.mk" line 20: if-less else +make: "directive-else.mk" line 26: ok +make: "directive-else.mk" line 27: warning: extra else +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/directive-else.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-else.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-else.mk (revision 365366) @@ -0,0 +1,32 @@ +# $NetBSD: directive-else.mk,v 1.3 2020/08/29 18:50:25 rillig Exp $ +# +# Tests for the .else directive. + +# The .else directive does not take any arguments. +# As of 2020-08-29, make doesn't warn about this. +.if 0 +.warning must not be reached +.else 123 +.info ok +.endif + +.if 1 +.info ok +.else 123 +.warning must not be reached +.endif + +# An .else without a corresponding .if is an error. +.else + +# Accidental extra .else directives are detected too. +.if 0 +.warning must not be reached +.else +.info ok +.else +.info After an extra .else, everything is skipped. +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-else.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-endif.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-endif.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-endif.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-endif.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-endif.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-endif.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-endif.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .endif directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-endif.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-error.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-error.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-error.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-error.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-error.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-error.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .error directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-error.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-export-env.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-export-env.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export-env.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-export-env.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-export-env.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export-env.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-export-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export-env directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-export-env.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-export-literal.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-export-literal.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export-literal.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-export-literal.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-export-literal.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export-literal.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-export-literal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export-literal directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-export-literal.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-export.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-export.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-export.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-export.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-export.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-export.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .export directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-export.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-for-generating-endif.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-for-generating-endif.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-for-generating-endif.exp (revision 365366) @@ -0,0 +1,7 @@ +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 21: if-less endif +make: "directive-for-generating-endif.mk" line 0: 3 open conditionals +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Property changes on: head/contrib/bmake/unit-tests/directive-for-generating-endif.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-for-generating-endif.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-for-generating-endif.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-for-generating-endif.mk (revision 365366) @@ -0,0 +1,25 @@ +# $NetBSD: directive-for-generating-endif.mk,v 1.1 2020/08/29 18:50:25 rillig Exp $ +# +# Test whether a .for loop can be used to generate multiple .endif +# directives to close nested .if directives. Depending on the exact +# implementation, this might have been possible. +# +# If it were possible, the 3 .if directives would perfectly match the +# 3 .endif directives generated by the .for loop. +# +# After the "included file" from the .for loop, the 3 .if directives +# are still open. +# +# See For_Run and ParseReadLine. Each .for loop is treated like a separately +# included file, and in each included file the .if/.endif directives must be +# balanced. + +.if 1 +. if 2 +. if 3 +.for i in 3 2 1 +.endif +.endfor + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-for-generating-endif.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-for.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-for.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-for.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Property changes on: head/contrib/bmake/unit-tests/directive-for.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-for.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-for.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-for.mk (revision 365366) @@ -0,0 +1,97 @@ +# $NetBSD: directive-for.mk,v 1.2 2020/09/02 22:58:59 rillig Exp $ +# +# Tests for the .for directive. + +# Using the .for loop, lists of values can be produced. +# In simple cases, the :@var@${var}@ variable modifier can be used to +# reach the same effects. +# +.undef NUMBERS +.for num in 1 2 3 +NUMBERS+= ${num} +.endfor +.if ${NUMBERS} != "1 2 3" +. error +.endif + +# The .for loop also works for multiple iteration variables. +.for name value in VARNAME value NAME2 value2 +${name}= ${value} +.endfor +.if ${VARNAME} != "value" || ${NAME2} != "value2" +. error +.endif + +# The .for loop splits the items at whitespace, taking quotes into account, +# just like the :M or :S variable modifiers. +# +# Until 2012-06-03, it had split the items exactly at whitespace, without +# taking the quotes into account. +# +.undef WORDS +.for var in one t\ w\ o "three three" 'four four' `five six` +WORDS+= counted +.endfor +.if ${WORDS:[#]} != 6 +. error +.endif + +# In the body of the .for loop, the iteration variables can be accessed +# like normal variables, even though they are not really variables. +# +# Instead, the expression ${var} is transformed into ${:U1}, ${:U2} and so +# on, before the loop body is evaluated. +# +# A notable effect of this implementation technique is that the .for +# iteration variables and the normal global variables live in separate +# namespaces and do not influence each other. +# +var= value before +var2= value before +.for var var2 in 1 2 3 4 +.endfor +.if ${var} != "value before" +. warning After the .for loop, var must still have its original value. +.endif +.if ${var2} != "value before" +. warning After the .for loop, var2 must still have its original value. +.endif + +# Everything from the paragraph above also applies if the loop body is +# empty, even if there is no actual iteration since the loop items are +# also empty. +# +var= value before +var2= value before +.for var var2 in ${:U} +.endfor +.if ${var} != "value before" +. warning After the .for loop, var must still have its original value. +.endif +.if ${var2} != "value before" +. warning After the .for loop, var2 must still have its original value. +.endif + +# Until 2008-12-21, the values of the iteration variables were simply +# inserted as plain text and then parsed as usual, which made it possible +# to achieve all kinds of strange effects. +# +# Before that date, the .for loop expanded to: +# EXPANSION+= value +# Since that date, the .for loop expands to: +# EXPANSION${:U+}= value +# +EXPANSION= before +EXPANSION+ = before +.for plus in + +EXPANSION${plus}= value +.endfor +.if ${EXPANSION} != "before" +. error This must be a make from before 2009. +.endif +.if ${EXPANSION+} != "value" +. error This must be a make from before 2009. +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-for.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-if.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-if.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-if.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-if.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-if.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-if.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-if.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .if directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-if.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-ifdef.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-ifdef.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifdef.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-ifdef.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-ifdef.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifdef.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifdef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifdef directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-ifdef.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-ifmake.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-ifmake.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifmake.exp (revision 365366) @@ -0,0 +1,10 @@ +make: "directive-ifmake.mk" line 8: ok: positive condition works +make: "directive-ifmake.mk" line 19: ok: negation works +make: "directive-ifmake.mk" line 25: ok: double negation works +make: "directive-ifmake.mk" line 32: ok: both mentioned +make: "directive-ifmake.mk" line 39: ok: only those mentioned +make: "directive-ifmake.mk" line 49: Targets can even be added at parse time. +: first +: second +: late-target +exit status 0 Index: head/contrib/bmake/unit-tests/directive-ifmake.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-ifmake.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifmake.mk (revision 365366) @@ -0,0 +1,55 @@ +# $NetBSD: directive-ifmake.mk,v 1.4 2020/08/30 14:25:45 rillig Exp $ +# +# Tests for the .ifmake directive, which provides a shortcut for asking +# whether a certain target is requested to be made from the command line. + +# This is the most basic form. +.ifmake first +.info ok: positive condition works +.else +.warning positive condition fails +.endif + +# The not operator works as expected. +# An alternative interpretation were that this condition is asking whether +# the target "!first" was requested. To distinguish this, see the next test. +.ifmake !first +.warning unexpected +.else +.info ok: negation works +.endif + +# See if the exclamation mark really means "not", or if it is just part of +# the target name. +.ifmake !!first +.info ok: double negation works +.else +.warning double negation fails +.endif + +# Multiple targets can be combined using the && and || operators. +.ifmake first && second +.info ok: both mentioned +.else +.warning && does not work as expected +.endif + +# Negation also works in complex conditions. +.ifmake first && !unmentioned +.info ok: only those mentioned +.else +.warning && with ! does not work as expected +.endif + +# Using the .MAKEFLAGS special dependency target, arbitrary command +# line options can be added at parse time. This means that it is +# possible to extend the targets to be made. +.MAKEFLAGS: late-target +.ifmake late-target +.info Targets can even be added at parse time. +.else +.info No, targets cannot be added at parse time anymore. +.endif + +first second unmentioned late-target: + : $@ Property changes on: head/contrib/bmake/unit-tests/directive-ifmake.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-ifndef.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-ifndef.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifndef.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-ifndef.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-ifndef.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifndef.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifndef.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifndef directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-ifndef.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-ifnmake.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-ifnmake.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifnmake.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-ifnmake.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-ifnmake.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-ifnmake.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-ifnmake.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .ifnmake directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-ifnmake.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-info.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-info.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-info.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-info.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-info.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-info.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-info.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .info directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-info.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-undef.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-undef.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-undef.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-undef.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-undef.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-undef.mk (revision 365366) @@ -0,0 +1,17 @@ +# $NetBSD: directive-undef.mk,v 1.3 2020/08/23 19:30:13 rillig Exp $ +# +# Tests for the .undef directive. + +# As of 2020-07-28, .undef only undefines the first variable. +# All further variable names are silently ignored. +# See parse.c, string literal "undef". +1= 1 +2= 2 +3= 3 +.undef 1 2 3 +.if ${1:U_}${2:U_}${3:U_} != _23 +.warning $1$2$3 +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-undef.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-unexport-env.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-unexport-env.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-unexport-env.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-unexport-env.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-unexport-env.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-unexport-env.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-unexport-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .unexport-env directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-unexport-env.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-unexport.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-unexport.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-unexport.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-unexport.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-unexport.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-unexport.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-unexport.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .unexport directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-unexport.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive-warning.exp =================================================================== --- head/contrib/bmake/unit-tests/directive-warning.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive-warning.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive-warning.mk =================================================================== --- head/contrib/bmake/unit-tests/directive-warning.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive-warning.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive-warning.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the .warning directive. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive-warning.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directive.exp =================================================================== --- head/contrib/bmake/unit-tests/directive.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directive.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/directive.mk =================================================================== --- head/contrib/bmake/unit-tests/directive.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directive.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: directive.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the preprocessing directives, such as .if or .info. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/directive.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/directives.exp =================================================================== --- head/contrib/bmake/unit-tests/directives.exp (nonexistent) +++ head/contrib/bmake/unit-tests/directives.exp (revision 365366) @@ -0,0 +1,42 @@ +make: "directives.mk" line 10: begin .export tests +make: "directives.mk" line 11: Unknown directive "expor" +make: "directives.mk" line 20: begin .export-env tests +make: "directives.mk" line 30: begin .export-literal tests +make: "directives.mk" line 40: begin .info tests +make: "directives.mk" line 41: Unknown directive "inf" +make: "directives.mk" line 42: Unknown directive "info" +make: "directives.mk" line 43: message +make: "directives.mk" line 44: indented message +make: "directives.mk" line 45: Unknown directive "information" +make: "directives.mk" line 46: message +make: "directives.mk" line 50: begin .undef tests +make: "directives.mk" line 51: Unknown directive "unde" +make: "directives.mk" line 60: begin .unexport tests +make: "directives.mk" line 61: Unknown directive "unexpor" +make: "directives.mk" line 70: begin .unexport-env tests +make: "directives.mk" line 80: begin .warning tests +make: "directives.mk" line 81: Unknown directive "warn" +make: "directives.mk" line 82: Unknown directive "warnin" +make: "directives.mk" line 83: Unknown directive "warning" +make: "directives.mk" line 84: warning: message +make: "directives.mk" line 85: Unknown directive "warnings" +make: "directives.mk" line 86: warning: messages +make: "directives.mk" line 90: begin .elif misspellings tests, part 1 +make: "directives.mk" line 100: begin .elif misspellings tests, part 2 +make: "directives.mk" line 110: begin .elif misspellings tests, part 3 +make: "directives.mk" line 120: which branch is taken on misspelling after false? +make: "directives.mk" line 127: else taken +make: "directives.mk" line 130: which branch is taken on misspelling after true? +make: "directives.mk" line 132: Unknown directive "elsif" +make: "directives.mk" line 133: 1 taken +make: "directives.mk" line 134: Unknown directive "elsif" +make: "directives.mk" line 135: 2 taken +make: "directives.mk" line 140: Unknown directive "indented" +make: "directives.mk" line 141: Unknown directive "indented" +make: "directives.mk" line 142: Unknown directive "indented" +make: "directives.mk" line 143: Unknown directive "info" +make: "directives.mk" line 150: Could not find nonexistent.mk +make: "directives.mk" line 160: end of the tests +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/directives.mk =================================================================== --- head/contrib/bmake/unit-tests/directives.mk (nonexistent) +++ head/contrib/bmake/unit-tests/directives.mk (revision 365366) @@ -0,0 +1,163 @@ +# $NetBSD: directives.mk,v 1.5 2020/07/28 20:57:59 rillig Exp $ +# +# Tests for parsing directives, in the same order as in the manual page. +# +# Each test group has 10 lines, to keep the line numbers in directives.exp +# stable. +# +# no tests for .error since it exits immediately, see ParseMessage. + +.info begin .export tests +.expor # misspelled +.export # oops: missing argument +.export VARNAME +.exporting works # oops: misspelled + + + + + +.info begin .export-env tests +.export-en # oops: misspelled +.export-env +.export-environment # oops: misspelled + + + + + + +.info begin .export-literal tests +.export-litera # oops: misspelled +.export-literal # oops: missing argument +.export-literal VARNAME +.export-literally # oops: misspelled + + + + + +.info begin .info tests +.inf # misspelled +.info # oops: message should be "missing parameter" +.info message +.info indented message +.information +.information message # oops: misspelled +.info.man: # not a message, but a suffix rule + + +.info begin .undef tests +.unde # misspelled +.undef # oops: missing argument +.undefined # oops: misspelled +.undef VARNAME + + + + + +.info begin .unexport tests +.unexpor # misspelled +.unexport # oops: missing argument +.unexport VARNAME # ok +.unexporting works # oops: misspelled + + + + + +.info begin .unexport-env tests +.unexport-en # misspelled +.unexport-env # ok +.unexport-environment # oops: misspelled + + + + + + +.info begin .warning tests +.warn # misspelled +.warnin # misspelled +.warning # oops: should be "missing argument" +.warning message # ok +.warnings # misspelled +.warnings messages # oops + + + +.info begin .elif misspellings tests, part 1 +.if 1 +.elif 1 # ok +.elsif 1 # oops: misspelled +.elseif 1 # oops: misspelled +.endif + + + + +.info begin .elif misspellings tests, part 2 +.if 0 +.elif 0 # ok +.elsif 0 # oops: misspelled +.elseif 0 # oops: misspelled +.endif + + + + +.info begin .elif misspellings tests, part 3 +.if 0 +.elsif 0 # oops: misspelled +.endif +.if 0 +.elseif 0 # oops: misspelled +.endif + + + +.info which branch is taken on misspelling after false? +.if 0 +.elsif 1 +.info 1 taken +.elsif 2 +.info 2 taken +.else +.info else taken +.endif + +.info which branch is taken on misspelling after true? +.if 1 +.elsif 1 +.info 1 taken +.elsif 2 +.info 2 taken +.else +.info else taken +.endif + +.indented none +. indented 2 spaces +. indented tab +.${:Uinfo} directives cannot be indirect + + + + + + +.include "nonexistent.mk" +.include "/dev/null" # size 0 +# including a directory technically succeeds, but shouldn't. +#.include "." # directory + + + + + + +.info end of the tests + +all: + @: Property changes on: head/contrib/bmake/unit-tests/directives.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/envfirst.exp =================================================================== --- head/contrib/bmake/unit-tests/envfirst.exp (nonexistent) +++ head/contrib/bmake/unit-tests/envfirst.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/envfirst.mk =================================================================== --- head/contrib/bmake/unit-tests/envfirst.mk (nonexistent) +++ head/contrib/bmake/unit-tests/envfirst.mk (revision 365366) @@ -0,0 +1,42 @@ +# $NetBSD: envfirst.mk,v 1.2 2020/07/27 18:57:42 rillig Exp $ +# +# The -e option makes environment variables stronger than global variables. + +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Try to override the variable; this does not have any effect. +FROM_ENV= value-from-mk +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Try to append to the variable; this also doesn't have any effect. +FROM_ENV+= appended +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# The default assignment also cannot change the variable. +FROM_ENV?= default +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Neither can the assignment modifiers. +.if ${FROM_ENV::=from-condition} +.endif +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +# Even .undef doesn't work since it only affects the global context, +# which is independent from the environment variables. +.undef FROM_ENV +.if ${FROM_ENV} != value-from-env +.error ${FROM_ENV} +.endif + +all: + @: nothing Property changes on: head/contrib/bmake/unit-tests/envfirst.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/export-all.mk =================================================================== --- head/contrib/bmake/unit-tests/export-all.mk (revision 365365) +++ head/contrib/bmake/unit-tests/export-all.mk (revision 365366) @@ -1,23 +1,24 @@ -# $Id: export-all.mk,v 1.1.1.2 2015/04/10 20:43:38 sjg Exp $ +# $Id: export-all.mk,v 1.1.1.3 2020/07/28 16:57:18 sjg Exp $ UT_OK=good UT_F=fine # the old way to do :tA M_tAbad = C,.*,cd & \&\& 'pwd',:sh # the new M_tA = tA here := ${.PARSEDIR} # this will cause trouble (recursing if we let it) UT_BADDIR = ${${here}/../${here:T}:L:${M_tAbad}:T} # this will be ok UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T} .export +FILTER_CMD= grep ^UT_ .include "export.mk" UT_TEST=export-all UT_ALL=even this gets exported Index: head/contrib/bmake/unit-tests/export-variants.exp =================================================================== --- head/contrib/bmake/unit-tests/export-variants.exp (nonexistent) +++ head/contrib/bmake/unit-tests/export-variants.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Property changes on: head/contrib/bmake/unit-tests/export-variants.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/export-variants.mk =================================================================== --- head/contrib/bmake/unit-tests/export-variants.mk (nonexistent) +++ head/contrib/bmake/unit-tests/export-variants.mk (revision 365366) @@ -0,0 +1,40 @@ +# $NetBSD: export-variants.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $ +# +# Test whether exported variables apply to each variant of running +# external commands: +# +# The != assignments. +# The :!cmd! modifier. +# The :sh modifier. + +SHVAR!= env | grep ^UT_ || true +.if ${SHVAR} != "" +.warning At this point, no variable should be exported. +.endif + +.if ${:!env | grep ^UT_ || true!} != "" +.warning At this point, no variable should be exported. +.endif + +.if ${env | grep ^UT_ || true:L:sh} != "" +.warning At this point, no variable should be exported. +.endif + +UT_VAR= value +.export UT_VAR + +SHVAR!= env | grep ^UT_ || true +.if ${SHVAR} != "UT_VAR=value" +.warning At this point, no variable should be exported. +.endif + +.if ${:!env | grep ^UT_ || true!} != "UT_VAR=value" +.warning At this point, some variables should be exported. +.endif + +.if ${env | grep ^UT_ || true:L:sh} != "UT_VAR=value" +.warning At this point, some variables should be exported. +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/export-variants.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/export.exp =================================================================== --- head/contrib/bmake/unit-tests/export.exp (revision 365365) +++ head/contrib/bmake/unit-tests/export.exp (revision 365366) @@ -1,6 +1,8 @@ +&=ampersand +MAKELEVEL=1 UT_DOLLAR=This is $UT_FU UT_FOO=foobar is fubar UT_FU=fubar UT_TEST=export UT_ZOO=hoopie exit status 0 Index: head/contrib/bmake/unit-tests/export.mk =================================================================== --- head/contrib/bmake/unit-tests/export.mk (revision 365365) +++ head/contrib/bmake/unit-tests/export.mk (revision 365366) @@ -1,22 +1,43 @@ -# $Id: export.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: export.mk,v 1.1.1.4 2020/08/08 22:34:25 sjg Exp $ UT_TEST=export UT_FOO=foo${BAR} UT_FU=fubar UT_ZOO=hoopie UT_NO=all -# belive it or not, we expect this one to come out with $UT_FU unexpanded. +# believe it or not, we expect this one to come out with $UT_FU unexpanded. UT_DOLLAR= This is $$UT_FU .export UT_FU UT_FOO .export UT_DOLLAR -# this one will be ignored + +.if !defined(.MAKE.PID) +.error .MAKE.PID must be defined +.endif +@= at +%= percent +*= asterisk +${:U!}= exclamation # A direct != would try to run "exclamation" + # as a shell command and assign its output + # to the empty variable. +&= ampersand + +# This is ignored because it is internal. .export .MAKE.PID +# These are ignored because they are local to the target. +.export @ +.export % +.export * +.export ! +.export & +# This is ignored because it is undefined. +.export UNDEFINED BAR=bar is ${UT_FU} .MAKE.EXPORTED+= UT_ZOO UT_TEST -all: - @env | grep '^UT_' | sort +FILTER_CMD?= egrep -v '^(MAKEFLAGS|PATH|PWD|SHLVL|_)=' +all: + @env | ${FILTER_CMD} | sort Index: head/contrib/bmake/unit-tests/forloop.mk =================================================================== --- head/contrib/bmake/unit-tests/forloop.mk (revision 365365) +++ head/contrib/bmake/unit-tests/forloop.mk (revision 365366) @@ -1,53 +1,53 @@ -# $Id: forloop.mk,v 1.1.1.2 2020/05/05 00:54:40 sjg Exp $ +# $Id: forloop.mk,v 1.1.1.3 2020/09/02 18:35:14 sjg Exp $ all: for-loop LIST = one "two and three" four "five" .if make(for-fail) for-fail: XTRA_LIST = xtra .else .for x in ${LIST} X!= echo 'x=$x' >&2; echo .endfor CFL = -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" cfl= .for x in ${CFL} X!= echo 'x=$x' >&2; echo .if empty(cfl) cfl= $x .else cfl+= $x .endif .endfor X!= echo 'cfl=${cfl}' >&2; echo .if ${cfl} != ${CFL} .error ${.newline}'${cfl}' != ${.newline}'${CFL}' .endif .for a b in ${EMPTY} X!= echo 'a=$a b=$b' >&2; echo .endfor # 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. +# that a newline ever occurred. .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 .endfor for-loop: @echo We expect an error next: @(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \ { echo "Oops that should have failed!"; exit 1; } || echo OK Index: head/contrib/bmake/unit-tests/impsrc.exp =================================================================== --- head/contrib/bmake/unit-tests/impsrc.exp (revision 365365) +++ head/contrib/bmake/unit-tests/impsrc.exp (revision 365366) @@ -1,13 +1,13 @@ -expected: source4 -actual: source4 +expected: +actual: expected: target1.x actual: target1.x expected: target1.y actual: target1.y -expected: source1 -actual: source1 -expected: source2 -actual: source2 -expected: source1 -actual: source1 +expected: +actual: +expected: +actual: +expected: +actual: exit status 0 Index: head/contrib/bmake/unit-tests/impsrc.mk =================================================================== --- head/contrib/bmake/unit-tests/impsrc.mk (revision 365365) +++ head/contrib/bmake/unit-tests/impsrc.mk (revision 365366) @@ -1,43 +1,60 @@ -# $NetBSD: impsrc.mk,v 1.2 2014/08/30 22:21:07 sjg Exp $ +# $NetBSD: impsrc.mk,v 1.3 2020/08/07 13:43:50 rillig Exp $ # Does ${.IMPSRC} work properly? # It should be set, in order of precedence, to ${.TARGET} of: # 1) the implied source of a transformation rule, # 2) the first prerequisite from the dependency line of an explicit rule, or # 3) the first prerequisite of an explicit rule. # +# Items 2 and 3 work in GNU make. +# Items 2 and 3 are not required by POSIX 2018. all: target1.z target2 target3 target4 .SUFFIXES: .x .y .z .x.y: source1 @echo 'expected: target1.x' @echo 'actual: $<' .y.z: source2 @echo 'expected: target1.y' @echo 'actual: $<' +# (3) Making target1.z out of target1.y is done because of an inference rule. +# Therefore $< is available here. + +# (2) This is an additional dependency on the inference rule .x.y. +# The dependency target1.x comes from the inference rule, +# therefore it is available as $<. target1.y: source3 +# (1) This is an explicit dependency, not an inference rule. +# Therefore POSIX does not specify that $< be available here. target1.x: source4 - @echo 'expected: source4' + @echo 'expected: ' # either 'source4' or '' @echo 'actual: $<' +# (4) This is an explicit dependency, independent of any inference rule. +# Therefore $< is not available here. target2: source1 source2 - @echo 'expected: source1' + @echo 'expected: ' @echo 'actual: $<' +# (5) These are two explicit dependency rules. +# The first doesn't have any dependencies, only the second has. +# If any, the value of $< would be 'source2'. target3: source1 target3: source2 source3 - @echo 'expected: source2' + @echo 'expected: ' @echo 'actual: $<' +# (6) The explicit rule does not have associated commands. +# The value of $< might come from that rule, +# but it's equally fine to leave $< undefined. target4: source1 target4: - @echo 'expected: source1' + @echo 'expected: ' @echo 'actual: $<' source1 source2 source3 source4: - Index: head/contrib/bmake/unit-tests/include-main.mk =================================================================== --- head/contrib/bmake/unit-tests/include-main.mk (revision 365365) +++ head/contrib/bmake/unit-tests/include-main.mk (revision 365366) @@ -1,30 +1,30 @@ -# $NetBSD: include-main.mk,v 1.1 2020/05/17 12:36:26 rillig Exp $ +# $NetBSD: include-main.mk,v 1.2 2020/07/27 20:55:59 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". +# latest .include happened". See parse.c, function ParseSetIncludeFile. # .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} Index: head/contrib/bmake/unit-tests/lint.exp =================================================================== --- head/contrib/bmake/unit-tests/lint.exp (nonexistent) +++ head/contrib/bmake/unit-tests/lint.exp (revision 365366) @@ -0,0 +1,4 @@ +make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar. +y@:Q} +xvaluey +exit status 0 Property changes on: head/contrib/bmake/unit-tests/lint.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/lint.mk =================================================================== --- head/contrib/bmake/unit-tests/lint.mk (nonexistent) +++ head/contrib/bmake/unit-tests/lint.mk (revision 365366) @@ -0,0 +1,17 @@ +# $NetBSD: lint.mk,v 1.2 2020/08/08 13:00:07 rillig Exp $ +# +# Demonstrates stricter checks that are only enabled in the lint mode, +# using the -dL option. + +# Ouch: as of 2020-08-03, make exits successfully even though the error +# message has been issued as PARSE_FATAL. + +# Ouch: as of 2020-08-03, the variable is malformed and parsing stops +# for a moment, but is continued after the wrongly-guessed end of the +# variable, which echoes "y@:Q}". + +all: mod-loop-varname + +mod-loop-varname: + @echo ${VAR:Uvalue:@${:Ubar:S,b,v,}@x${var}y@:Q} + @echo ${VAR:Uvalue:@!@x$!y@:Q} # surprisingly allowed Property changes on: head/contrib/bmake/unit-tests/lint.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/make-exported.exp =================================================================== --- head/contrib/bmake/unit-tests/make-exported.exp (nonexistent) +++ head/contrib/bmake/unit-tests/make-exported.exp (revision 365366) @@ -0,0 +1,3 @@ +-literal=make-exported-value +UT_VAR= +exit status 0 Property changes on: head/contrib/bmake/unit-tests/make-exported.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/make-exported.mk =================================================================== --- head/contrib/bmake/unit-tests/make-exported.mk (nonexistent) +++ head/contrib/bmake/unit-tests/make-exported.mk (revision 365366) @@ -0,0 +1,16 @@ +# $NetBSD: make-exported.mk,v 1.1 2020/08/09 12:59:16 rillig Exp $ +# +# As of 2020-08-09, the code in Var_Export is shared between the .export +# directive and the .MAKE.EXPORTED variable. This leads to non-obvious +# behavior for certain variable assignments. + +-env= make-exported-value +-literal= make-exported-value +UT_VAR= ${UNEXPANDED} + +# The following behavior is probably not intended. +.MAKE.EXPORTED= -env # like .export-env +.MAKE.EXPORTED= -literal UT_VAR # like .export-literal PATH + +all: + @env | sort | grep -E '^UT_|make-exported-value' || true Property changes on: head/contrib/bmake/unit-tests/make-exported.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/moderrs.exp =================================================================== --- head/contrib/bmake/unit-tests/moderrs.exp (revision 365365) +++ head/contrib/bmake/unit-tests/moderrs.exp (revision 365366) @@ -1,16 +1,141 @@ Expect: Unknown modifier 'Z' make: Unknown modifier 'Z' VAR:Z= Expect: Unknown modifier 'Z' make: Unknown modifier 'Z' VAR:Z= Expect: Unclosed variable specification for VAR make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S VAR:S,V,v,=Thevariable Expect: Unclosed variable specification for VAR make: Unclosed variable specification after complex modifier (expecting '}') for VAR VAR:S,V,v,=Thevariable -Expect: Unclosed substitution for VAR (, missing) -make: Unclosed substitution for VAR (, missing) +Expect: Unfinished modifier for VAR (',' missing) +make: Unfinished modifier for VAR (',' missing) VAR:S,V,v= +Expect: 2 errors about missing @ delimiter +make: Unfinished modifier for UNDEF ('@' missing) + +make: Unfinished modifier for UNDEF ('@' missing) + +1 2 3 +modloop-close: +make: Unclosed variable specification (expecting '}') for "UNDEF" (value "1}... 2}... 3}...") modifier @ +1}... 2}... 3}... +1}... 2}... 3}... +Expect: 2 errors about missing ] delimiter +make: Unfinished modifier for UNDEF (']' missing) + +make: Unfinished modifier for UNDEF (']' missing) + +13= +12345=ok +Expect: 2 errors about missing ! delimiter +make: Unfinished modifier for VARNAME ('!' missing) + +make: Unfinished modifier for ! ('!' missing) + +mod-subst-delimiter: +make: Missing delimiter for :S modifier + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S +TheVariable +TheVariable +make: Missing delimiter for :S modifier +1: +make: Unfinished modifier for VAR (',' missing) +2: +make: Unfinished modifier for VAR (',' missing) +3: +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier S +TheVariable +TheVariable +mod-regex-delimiter: +make: Missing delimiter for :C modifier + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C +TheVariable +TheVariable +make: Missing delimiter for :C modifier +1: +make: Unfinished modifier for VAR (',' missing) +2: +make: Unfinished modifier for VAR (',' missing) +3: +make: Unfinished modifier for VAR (',' missing) + +make: Unfinished modifier for VAR (',' missing) + +make: Unclosed variable specification (expecting '}') for "VAR" (value "TheVariable") modifier C +TheVariable +TheVariable +mod-regex-undefined-subexpression: +one one 2 3 5 8 one3 2one 34 +make: No match for subexpression \2 +make: No match for subexpression \2 +make: No match for subexpression \1 +make: No match for subexpression \2 +make: No match for subexpression \1 +()+() ()+() ()+() 3 5 8 (3)+() ()+(1) 34 +mod-ts-parse: +112358132134 +15152535558513521534 +make: Bad modifier `:ts\65oct' for FIB +65oct} +make: Bad modifier `:tsxy' for FIB +xy} +mod-t-parse: +make: Bad modifier `:t' for FIB + +make: Bad modifier `:txy' for FIB +y} +make: Bad modifier `:t' for FIB + +make: Bad modifier `:t' for FIB +M*} +mod-ifelse-parse: +make: Unfinished modifier for FIB (':' missing) + +make: Unfinished modifier for FIB (':' missing) + +make: Unfinished modifier for FIB ('}' missing) + +make: Unfinished modifier for FIB ('}' missing) + +then +mod-remember-parse: +1 1 2 3 5 8 13 21 34 +make: Unknown modifier '_' + +mod-sysv-parse: +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +make: Unknown modifier '3' +make: Unclosed variable specification (expecting '}') for "FIB" (value "") modifier 3 + +1 1 2 x3 5 8 1x3 21 34 exit status 0 Index: head/contrib/bmake/unit-tests/moderrs.mk =================================================================== --- head/contrib/bmake/unit-tests/moderrs.mk (revision 365365) +++ head/contrib/bmake/unit-tests/moderrs.mk (revision 365366) @@ -1,31 +1,176 @@ -# $Id: moderrs.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: moderrs.mk,v 1.1.1.8 2020/08/26 16:40:43 sjg Exp $ # # various modifier error tests VAR=TheVariable # incase we have to change it ;-) MOD_UNKN=Z MOD_TERM=S,V,v MOD_S:= ${MOD_TERM}, -all: modunkn modunknV varterm vartermV modtermV +FIB= 1 1 2 3 5 8 13 21 34 +all: modunkn modunknV varterm vartermV modtermV modloop +all: modloop-close +all: modwords +all: modexclam +all: mod-subst-delimiter +all: mod-regex-delimiter +all: mod-regex-undefined-subexpression +all: mod-ts-parse +all: mod-t-parse +all: mod-ifelse-parse +all: mod-remember-parse +all: mod-sysv-parse + modunkn: @echo "Expect: Unknown modifier 'Z'" @echo "VAR:Z=${VAR:Z}" modunknV: @echo "Expect: Unknown modifier 'Z'" @echo "VAR:${MOD_UNKN}=${VAR:${MOD_UNKN}}" varterm: @echo "Expect: Unclosed variable specification for VAR" @echo VAR:S,V,v,=${VAR:S,V,v, vartermV: @echo "Expect: Unclosed variable specification for VAR" @echo VAR:${MOD_TERM},=${VAR:${MOD_S} modtermV: - @echo "Expect: Unclosed substitution for VAR (, missing)" + @echo "Expect: Unfinished modifier for VAR (',' missing)" -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}" + +modloop: + @echo "Expect: 2 errors about missing @ delimiter" + @echo ${UNDEF:U1 2 3:@var} + @echo ${UNDEF:U1 2 3:@var@...} + @echo ${UNDEF:U1 2 3:@var@${var}@} + +# The closing brace after the ${var} is part of the replacement string. +# In ParseModifierPart, braces and parentheses don't have to be balanced. +# This is contrary to the :M, :N modifiers, where both parentheses and +# braces must be balanced. +# This is also contrary to the SysV modifier, where only the actually +# used delimiter (either braces or parentheses) must be balanced. +modloop-close: + @echo $@: + @echo ${UNDEF:U1 2 3:@var@${var}}...@ + @echo ${UNDEF:U1 2 3:@var@${var}}...@} + +modwords: + @echo "Expect: 2 errors about missing ] delimiter" + @echo ${UNDEF:U1 2 3:[} + @echo ${UNDEF:U1 2 3:[#} + + # out of bounds => empty + @echo 13=${UNDEF:U1 2 3:[13]} + + # Word index out of bounds. + # + # On LP64I32, strtol returns LONG_MAX, + # which is then truncated to int (undefined behavior), + # typically resulting in -1. + # This -1 is interpreted as "the last word". + # + # On ILP32, strtol returns LONG_MAX, + # which is a large number. + # This results in a range from LONG_MAX - 1 to 3, + # which is empty. + @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,} + +modexclam: + @echo "Expect: 2 errors about missing ! delimiter" + @echo ${VARNAME:!echo} + # When the final exclamation mark is missing, there is no + # fallback to the SysV substitution modifier. + # If there were a fallback, the output would be "exclam", + # and the above would have produced an "Unknown modifier '!'". + @echo ${!:L:!=exclam} + +mod-subst-delimiter: + @echo $@: + @echo ${VAR:S + @echo ${VAR:S, + @echo ${VAR:S,from + @echo ${VAR:S,from, + @echo ${VAR:S,from,to + @echo ${VAR:S,from,to, + @echo ${VAR:S,from,to,} + @echo 1: ${VAR:S + @echo 2: ${VAR:S, + @echo 3: ${VAR:S,from + @echo ${VAR:S,from, + @echo ${VAR:S,from,to + @echo ${VAR:S,from,to, + @echo ${VAR:S,from,to,} + +mod-regex-delimiter: + @echo $@: + @echo ${VAR:C + @echo ${VAR:C, + @echo ${VAR:C,from + @echo ${VAR:C,from, + @echo ${VAR:C,from,to + @echo ${VAR:C,from,to, + @echo ${VAR:C,from,to,} + @echo 1: ${VAR:C + @echo 2: ${VAR:C, + @echo 3: ${VAR:C,from + @echo ${VAR:C,from, + @echo ${VAR:C,from,to + @echo ${VAR:C,from,to, + @echo ${VAR:C,from,to,} + +# In regular expressions with alternatives, not all capturing groups are +# always set; some may be missing. Warn about these. +# +# Since there is no way to turn off this warning, the combination of +# alternative matches and capturing groups is not widely used. +# +# A newly added modifier 'U' such as in :C,(a.)|(b.),\1\2,U might be added +# for treating undefined capturing groups as empty, but that would create a +# syntactical ambiguity since the :S and :C modifiers are open-ended (see +# mod-subst-chain). Luckily the modifier :U does not make sense after :C, +# therefore this case does not happen in practice. +# The sub-modifier for the :C modifier would have to be chosen wisely. +mod-regex-undefined-subexpression: + @echo $@: + @echo ${FIB:C,1(.*),one\1,} # all ok + @echo ${FIB:C,1(.*)|2(.*),(\1)+(\2),:Q} # no match for subexpression + +mod-ts-parse: + @echo $@: + @echo ${FIB:ts} + @echo ${FIB:ts\65} # octal 065 == U+0035 == '5' + @echo ${FIB:ts\65oct} # bad modifier + @echo ${FIB:tsxy} # modifier too long + +mod-t-parse: + @echo $@: + @echo ${FIB:t + @echo ${FIB:txy} + @echo ${FIB:t} + @echo ${FIB:t:M*} + +mod-ifelse-parse: + @echo $@: + @echo ${FIB:? + @echo ${FIB:?then + @echo ${FIB:?then: + @echo ${FIB:?then:else + @echo ${FIB:?then:else} + +mod-remember-parse: + @echo $@: + @echo ${FIB:_} # ok + @echo ${FIB:__} # modifier name too long + +mod-sysv-parse: + @echo $@: + @echo ${FIB:3 + @echo ${FIB:3= + @echo ${FIB:3=x3 + @echo ${FIB:3=x3} # ok Index: head/contrib/bmake/unit-tests/modmatch.exp =================================================================== --- head/contrib/bmake/unit-tests/modmatch.exp (revision 365365) +++ head/contrib/bmake/unit-tests/modmatch.exp (revision 365366) @@ -1,20 +1,17 @@ LIB=a X_LIBS:M${LIB${LIB:tu}} is "/tmp/liba.a" LIB=a X_LIBS:M*/lib${LIB}.a is "/tmp/liba.a" LIB=a X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBA.A" LIB=b X_LIBS:M${LIB${LIB:tu}} is "" LIB=b X_LIBS:M*/lib${LIB}.a is "" LIB=b X_LIBS:M*/lib${LIB}.a:tu is "" LIB=c X_LIBS:M${LIB${LIB:tu}} is "" LIB=c X_LIBS:M*/lib${LIB}.a is "" LIB=c X_LIBS:M*/lib${LIB}.a:tu is "" LIB=d X_LIBS:M${LIB${LIB:tu}} is "/tmp/libd.a" LIB=d X_LIBS:M*/lib${LIB}.a is "/tmp/libd.a" LIB=d X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBD.A" LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a" LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a" LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A" Mscanner=OK -Upper=One Two Three Four -Lower=five six seven -nose=One Three five exit status 0 Index: head/contrib/bmake/unit-tests/modmatch.mk =================================================================== --- head/contrib/bmake/unit-tests/modmatch.mk (revision 365365) +++ head/contrib/bmake/unit-tests/modmatch.mk (revision 365366) @@ -1,39 +1,30 @@ +# $NetBSD: modmatch.mk,v 1.8 2020/08/16 20:03:53 rillig Exp $ +# +# Tests for the :M and :S modifiers. X=a b c d e .for x in $X LIB${x:tu}=/tmp/lib$x.a .endfor X_LIBS= ${LIBA} ${LIBD} ${LIBE} LIB?=a var = head res = no .if !empty(var:M${:Uhead\:tail:C/:.*//}) res = OK .endif -all: show-libs check-cclass slow +all: show-libs show-libs: @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done @echo "Mscanner=${res}" show: @echo 'LIB=${LIB} X_LIBS:M$${LIB$${LIB:tu}} is "${X_LIBS:M${LIB${LIB:tu}}}"' @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a is "${X_LIBS:M*/lib${LIB}.a}"' @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a:tu is "${X_LIBS:M*/lib${LIB}.a:tu}"' - -LIST= One Two Three Four five six seven - -check-cclass: - @echo Upper=${LIST:M[A-Z]*} - @echo Lower=${LIST:M[^A-Z]*} - @echo nose=${LIST:M[^s]*[ex]} - -# Before 2020-06-13, this expression took quite a long time in Str_Match, -# calling itself 601080390 times for 16 asterisks. -slow: .PHONY - @:;: ${:U****************:M****************b:Q} Index: head/contrib/bmake/unit-tests/modmisc.exp =================================================================== --- head/contrib/bmake/unit-tests/modmisc.exp (revision 365365) +++ head/contrib/bmake/unit-tests/modmisc.exp (revision 365366) @@ -1,51 +1,21 @@ +make: Unknown modifier '$' path=':/bin:/tmp::/:.:/no/such/dir:.' path='/bin:/tmp:/:/no/such/dir' path='/bin:/tmp:/:/no/such/dir' path='/bin':'/tmp':'/':'/no/such/dir' path='/bin':'/tmp':'/':'/no/such/dir' path_/usr/xbin=/opt/xbin/ paths=/bin /tmp / /no/such/dir /opt/xbin PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN The answer is 42 -dirname of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b . . a.b . . . . .' -basename of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c def a.b.c c a a.a .gitignore a a.a' -suffix of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'c b/c a gitignore a' -root of 'a/b/c def a.b.c a.b/c a a.a .gitignore a a.a' is 'a/b/c def a.b a a a a a' S: C: @: S:empty C:empty @: -:a b b c: -:a b b c: -: b c: -:a c: -:x__ 3 x__ 3: -:a b b c: -:a b b c: -: b c: -make: RE substitution error: (details omitted) -make: Unclosed substitution for (, missing) -:C,word,____,:Q}: -:a c: -:x__ 3 x__ 3: -:+one+ +two+ +three+: -mod-at-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w: -mod-subst-dollar:$1: -mod-subst-dollar:$2: -mod-subst-dollar:$3: -mod-subst-dollar:$4: -mod-subst-dollar:$5: -mod-subst-dollar:$6: -mod-subst-dollar:$7: -mod-subst-dollar:$8: -mod-subst-dollar:U8: -mod-subst-dollar:$$$$: -mod-loop-dollar:1: -mod-loop-dollar:${word}: -mod-loop-dollar:$3$: -mod-loop-dollar:$${word}$: -mod-loop-dollar:$$5$$: -mod-loop-dollar:$$${word}$$: +mod-quote: new + +line +mod-break-many-words: 500 exit status 0 Index: head/contrib/bmake/unit-tests/modmisc.mk =================================================================== --- head/contrib/bmake/unit-tests/modmisc.mk (revision 365365) +++ head/contrib/bmake/unit-tests/modmisc.mk (revision 365366) @@ -1,127 +1,92 @@ -# $Id: modmisc.mk,v 1.1.1.3 2020/07/09 22:35:19 sjg Exp $ +# $Id: modmisc.mk,v 1.1.1.15 2020/08/23 15:52:08 sjg Exp $ # # miscellaneous modifier tests # do not put any dirs in this list which exist on some # but not all target systems - an exists() check is below. path=:/bin:/tmp::/:.:/no/such/dir:. # strip cwd from path. MOD_NODOT=S/:/ /g:N.:ts: # and decorate, note that $'s need to be doubled. Also note that # the modifier_variable can be used with other modifiers. MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@ # another mod - pretend it is more interesting MOD_HOMES=S,/home/,/homes/, MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@ MOD_SEP=S,:, ,g -all: modvar modvarloop modsysv mod-HTE emptyvar undefvar -all: mod-S mod-C mod-at-varname mod-at-resolve -all: mod-subst-dollar mod-loop-dollar +all: modvar modvarloop modsysv emptyvar undefvar +all: mod-quote +all: mod-break-many-words +# See also sysv.mk. modsysv: @echo "The answer is ${libfoo.a:L:libfoo.a=42}" +# Demonstrates modifiers that are given indirectly from a variable. modvar: @echo "path='${path}'" @echo "path='${path:${MOD_NODOT}}'" @echo "path='${path:S,home,homes,:${MOD_NODOT}}'" @echo "path=${path:${MOD_NODOTX}:ts:}" @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}" .for d in ${path:${MOD_SEP}:N.} /usr/xbin path_$d?= ${d:${MOD_OPT}:${MOD_HOMES}}/ paths+= ${d:${MOD_OPT}:${MOD_HOMES}} .endfor modvarloop: @echo "path_/usr/xbin=${path_/usr/xbin}" @echo "paths=${paths}" @echo "PATHS=${paths:tu}" -PATHNAMES= a/b/c def a.b.c a.b/c a a.a .gitignore a a.a -mod-HTE: - @echo "dirname of '"${PATHNAMES:Q}"' is '"${PATHNAMES:H:Q}"'" - @echo "basename of '"${PATHNAMES:Q}"' is '"${PATHNAMES:T:Q}"'" - @echo "suffix of '"${PATHNAMES:Q}"' is '"${PATHNAMES:E:Q}"'" - @echo "root of '"${PATHNAMES:Q}"' is '"${PATHNAMES:R:Q}"'" - # When a modifier is applied to the "" variable, the result is discarded. emptyvar: @echo S:${:S,^$,empty,} @echo C:${:C,^$,empty,} @echo @:${:@var@${var}@} # The :U modifier turns even the "" variable into something that has a value. # The resulting variable is empty, but is still considered to contain a # single empty word. This word can be accessed by the :S and :C modifiers, # but not by the :@ modifier since it explicitly skips empty words. undefvar: @echo S:${:U:S,^$,empty,} @echo C:${:U:C,^$,empty,} @echo @:${:U:@var@empty@} -mod-S: - @echo :${:Ua b b c:S,a b,,:Q}: - @echo :${:Ua b b c:S,a b,,1:Q}: - @echo :${:Ua b b c:S,a b,,W:Q}: - @echo :${:Ua b b c:S,b,,g:Q}: - @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: -mod-C: - @echo :${:Ua b b c:C,a b,,:Q}: - @echo :${:Ua b b c:C,a b,,1:Q}: - @echo :${:Ua b b c:C,a b,,W:Q}: - @echo :${:Uword1 word2:C,****,____,g:C,word,____,:Q}: - @echo :${:Ua b b c:C,b,,g:Q}: - @echo :${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,:Q}: +mod-quote: + @echo $@: new${.newline:Q}${.newline:Q}line -# In the :@ modifier, the name of the loop variable can even be generated -# dynamically. There's no practical use-case for this, and hopefully nobody -# will ever depend on this, but technically it's possible. -mod-at-varname: - @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}: +# Cover the bmake_realloc in brk_string. +mod-break-many-words: + @echo $@: ${UNDEF:U:range=500:[#]} -# The :@ modifier resolves the variables a little more often than expected. -# In particular, it resolves _all_ variables from the context, and not only -# the loop variable (in this case v). -# -# The d means direct reference, the i means indirect reference. -RESOLVE= ${RES1} $${RES1} -RES1= 1d${RES2} 1i$${RES2} -RES2= 2d${RES3} 2i$${RES3} -RES3= 3 +# To apply a modifier indirectly via another variable, the whole +# modifier must be put into a single variable. +.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}" +.warning unexpected +.endif -mod-at-resolve: - @echo $@:${RESOLVE:@v@w${v}w@:Q}: +# Adding another level of indirection (the 2 nested :U expressions) helps. +.if ${value:L:${:U${:US}${:U,value,replacement,}}} != "replacement" +.warning unexpected +.endif -# No matter how many dollar characters there are, they all get merged -# into a single dollar by the :S modifier. -mod-subst-dollar: - @echo $@:${:U1:S,^,$,:Q}: - @echo $@:${:U2:S,^,$$,:Q}: - @echo $@:${:U3:S,^,$$$,:Q}: - @echo $@:${:U4:S,^,$$$$,:Q}: - @echo $@:${:U5:S,^,$$$$$,:Q}: - @echo $@:${:U6:S,^,$$$$$$,:Q}: - @echo $@:${:U7:S,^,$$$$$$$,:Q}: - @echo $@:${:U8:S,^,$$$$$$$$,:Q}: -# This generates no dollar at all: - @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: -# Here is an alternative way to generate dollar characters. -# It's unexpectedly complicated though. - @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: +# Multiple indirect modifiers can be applied one after another as long as +# they are separated with colons. +.if ${value:L:${:US,a,A,}:${:US,e,E,}} != "vAluE" +.warning unexpected +.endif -# Demonstrate that it is possible to generate dollar characters using the -# :@ modifier. +# An indirect variable that evaluates to the empty string is allowed though. +# This makes it possible to define conditional modifiers, like this: # -# These are edge cases that could have resulted in a parse error as well -# since the $@ at the end could have been interpreted as a variable, which -# would mean a missing closing @ delimiter. -mod-loop-dollar: - @echo $@:${:U1:@word@${word}$@:Q}: - @echo $@:${:U2:@word@$${word}$$@:Q}: - @echo $@:${:U3:@word@$$${word}$$$@:Q}: - @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: - @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: - @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: +# M.little-endian= S,1234,4321, +# M.big-endian= # none +.if ${value:L:${:Dempty}S,a,A,} != "vAlue" +.warning unexpected +.endif + Index: head/contrib/bmake/unit-tests/modts.exp =================================================================== --- head/contrib/bmake/unit-tests/modts.exp (revision 365365) +++ head/contrib/bmake/unit-tests/modts.exp (revision 365366) @@ -1,39 +1,14 @@ -LIST="one two three four five six" -LIST:ts,="one,two,three,four,five,six" -LIST:ts/:tu="ONE/TWO/THREE/FOUR/FIVE/SIX" -LIST:ts::tu="ONE:TWO:THREE:FOUR:FIVE:SIX" -LIST:ts:tu="ONETWOTHREEFOURFIVESIX" -LIST:tu:ts/="ONE/TWO/THREE/FOUR/FIVE/SIX" -LIST:ts:="one:two:three:four:five:six" -LIST:ts="onetwothreefourfivesix" -LIST:ts:S/two/2/="one2threefourfivesix" -LIST:S/two/2/:ts="one2threefourfivesix" -LIST:ts/:S/two/2/="one/2/three/four/five/six" -Pretend the '/' in '/n' etc. below are back-slashes. -LIST:ts/n="one -two -three -four -five -six" -LIST:ts/t="one two three four five six" -LIST:ts/012:tu="ONE -TWO -THREE -FOUR -FIVE -SIX" -LIST:ts/xa:tu="ONE -TWO -THREE -FOUR -FIVE -SIX" make: Bad modifier `:tx' for LIST LIST:tx="}" make: Bad modifier `:ts\X' for LIST LIST:ts/x:tu="\X:tu}" FU_mod-ts="a/b/cool" FU_mod-ts:ts:T="cool" == cool? B.${AAA:ts}="Baaa" == Baaa? +:ts :S => aaxBbxaaxbbxaaxbb +:ts :S space => axa a axc +:ts :S space :M => axaxaxaxc +:ts :S => axa a axc +:ts :S :@ => axa a axc +:ts :S :@ :M => axaxaxaxc exit status 0 Index: head/contrib/bmake/unit-tests/modts.mk =================================================================== --- head/contrib/bmake/unit-tests/modts.mk (revision 365365) +++ head/contrib/bmake/unit-tests/modts.mk (revision 365366) @@ -1,44 +1,47 @@ LIST= one two three LIST+= four five six FU_mod-ts = a / b / cool AAA= a a a B.aaa= Baaa -all: mod-ts +all: mod-ts mod-ts-space # Use print or printf iff they are builtin. # XXX note that this causes problems, when make decides # there is no need to use a shell, so avoid where possible. .if ${(type print) 2> /dev/null || echo:L:sh:Mbuiltin} != "" PRINT= print -r -- .elif ${(type printf) 2> /dev/null || echo:L:sh:Mbuiltin} != "" PRINT= printf '%s\n' .else PRINT= echo .endif mod-ts: - @echo 'LIST="${LIST}"' - @echo 'LIST:ts,="${LIST:ts,}"' - @echo 'LIST:ts/:tu="${LIST:ts/:tu}"' - @echo 'LIST:ts::tu="${LIST:ts::tu}"' - @echo 'LIST:ts:tu="${LIST:ts:tu}"' - @echo 'LIST:tu:ts/="${LIST:tu:ts/}"' - @echo 'LIST:ts:="${LIST:ts:}"' - @echo 'LIST:ts="${LIST:ts}"' - @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"' - @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"' - @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"' - @echo "Pretend the '/' in '/n' etc. below are back-slashes." - @${PRINT} 'LIST:ts/n="${LIST:ts\n}"' - @${PRINT} 'LIST:ts/t="${LIST:ts\t}"' - @${PRINT} 'LIST:ts/012:tu="${LIST:ts\012:tu}"' - @${PRINT} 'LIST:ts/xa:tu="${LIST:ts\xa:tu}"' @${PRINT} 'LIST:tx="${LIST:tx}"' @${PRINT} 'LIST:ts/x:tu="${LIST:ts\X:tu}"' @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"' @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?' @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?' + +mod-ts-space: + # After the :ts modifier, the whole string is interpreted as a single + # word since all spaces have been replaced with x. + @${PRINT} ':ts :S => '${aa bb aa bb aa bb:L:tsx:S,b,B,:Q} + + # The :ts modifier also applies to word separators that are added + # afterwards. + @${PRINT} ':ts :S space => '${a ababa c:L:tsx:S,b, ,g:Q} + @${PRINT} ':ts :S space :M => '${a ababa c:L:tsx:S,b, ,g:M*:Q} + + # Not all modifiers behave this way though. Some of them always use + # a space as word separator instead of the :ts separator. + # This seems like an oversight during implementation. + @${PRINT} ':ts :S => '${a ababa c:L:tsx:S,b, ,g:Q} + @${PRINT} ':ts :S :@ => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:Q} + + # A final :M* modifier applies the :ts separator again, though. + @${PRINT} ':ts :S :@ :M => '${a ababa c:L:tsx:S,b, ,g:@v@${v}@:M*:Q} Index: head/contrib/bmake/unit-tests/opt-backwards.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-backwards.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-backwards.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-backwards.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-backwards.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-backwards.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-backwards.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -B command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-backwards.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-chdir.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-chdir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-chdir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-chdir.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-chdir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-chdir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-chdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -C command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-chdir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-debug-g1.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-debug-g1.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-debug-g1.exp (revision 365366) @@ -0,0 +1,15 @@ +#*** Input graph: +# all, made UNMADE, type OP_DEPENDS, flags none +# made-target, made UNMADE, type OP_DEPENDS, flags none +# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none +# made-source, made UNMADE, type OP_DEPENDS, flags none +# unmade-target, made UNMADE, type OP_DEPENDS, flags none +# unmade-sources, made UNMADE, type none, flags none +# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none + + +# +# Files that are only sources: +# unmade-sources [unmade-sources] +#*** Transformations: +exit status 0 Property changes on: head/contrib/bmake/unit-tests/opt-debug-g1.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-debug-g1.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-debug-g1.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-debug-g1.mk (revision 365366) @@ -0,0 +1,19 @@ +# $NetBSD: opt-debug-g1.mk,v 1.1 2020/08/27 19:00:17 rillig Exp $ +# +# Tests for the -dg1 command line option, which prints the input +# graph before making anything. + +all: made-target made-target-no-sources + +made-target: made-source + +made-source: + +made-target-no-sources: + +unmade-target: unmade-sources + +unmade-target-no-sources: + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-debug-g1.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-debug.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-debug.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-debug.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-debug.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-debug.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-debug.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-debug.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -d command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-debug.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-define.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-define.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-define.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-define.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-define.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-define.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-define.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -D command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-define.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-env.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-env.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-env.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-env.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-env.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-env.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -e command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-env.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-file.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-file.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-file.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-file.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-file.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-file.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-file.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -f command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-file.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-ignore.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-ignore.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-ignore.exp (revision 365366) @@ -0,0 +1,12 @@ +dependency 1 +dependency 2 +dependency 3 +other 1 +other 2 +main 1 +main 2 +*** Error code 1 (ignored) +*** Error code 7 (ignored) +*** Error code 1 (ignored) +*** Error code 1 (ignored) +exit status 0 Index: head/contrib/bmake/unit-tests/opt-ignore.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-ignore.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-ignore.mk (revision 365366) @@ -0,0 +1,30 @@ +# $NetBSD: opt-ignore.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -i command line option, which ignores the exit status of the +# shell commands, and just continues with the next command, even from the same +# target. +# +# Is there a situation in which this option is useful? +# +# Why are the "Error code" lines all collected at the bottom of the output +# file, where they cannot be related to the individual shell commands that +# failed? + +all: dependency other + +dependency: + @echo dependency 1 + @false + @echo dependency 2 + @:; exit 7 + @echo dependency 3 + +other: + @echo other 1 + @false + @echo other 2 + +all: + @echo main 1 + @false + @echo main 2 Property changes on: head/contrib/bmake/unit-tests/opt-ignore.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-include-dir.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-include-dir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-include-dir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-include-dir.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-include-dir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-include-dir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-include-dir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -I command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-include-dir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-jobs-internal.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-jobs-internal.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-jobs-internal.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-jobs-internal.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-jobs-internal.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-jobs-internal.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-jobs-internal.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the (intentionally undocumented) -J command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-jobs-internal.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-jobs.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-jobs.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-jobs.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-jobs.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-jobs.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-jobs.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -j command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-jobs.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-keep-going.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-keep-going.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-keep-going.exp (revision 365366) @@ -0,0 +1,6 @@ +dependency 1 +other 1 +*** Error code 1 (continuing) +*** Error code 1 (continuing) +`all' not remade because of errors. +exit status 0 Index: head/contrib/bmake/unit-tests/opt-keep-going.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-keep-going.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-keep-going.mk (revision 365366) @@ -0,0 +1,24 @@ +# $NetBSD: opt-keep-going.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -k command line option, which stops building a target as soon +# as an error is detected, but continues building the other, independent +# targets, as far as possible. + +all: dependency other + +dependency: + @echo dependency 1 + @false + @echo dependency 2 + @:; exit 7 + @echo dependency 3 + +other: + @echo other 1 + @false + @echo other 2 + +all: + @echo main 1 + @false + @echo main 2 Property changes on: head/contrib/bmake/unit-tests/opt-keep-going.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-m-include-dir.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-m-include-dir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-m-include-dir.exp (revision 365366) @@ -0,0 +1,2 @@ +ok +exit status 0 Index: head/contrib/bmake/unit-tests/opt-m-include-dir.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-m-include-dir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-m-include-dir.mk (revision 365366) @@ -0,0 +1,61 @@ +# $NetBSD: opt-m-include-dir.mk,v 1.4 2020/09/01 20:14:34 rillig Exp $ +# +# Tests for the -m command line option, which adds a directory to the +# search path for the .include <...> directive. +# +# The .../canary.mk special argument starts searching in the current +# directory and walks towards the file system root, until it finds a +# directory that contains a file called canary.mk. +# +# To set up this scenario, the file step2.mk is created deep in a hierarchy +# of subdirectories. Another file called opt-m-step3.mk is created a few +# steps up in the directory hierarchy, serving as the canary file. +# +# Next to the canary file, there is opt-m-step3.mk. This file is found +# by mentioning its simple name in an .include directive. It defines the +# target "step2" that is needed by "step2.mk". + +.if ${.PARSEFILE:T} == "opt-m-include-dir.mk" + +# Set up the other files needed for this test. + +TEST_DIR:= ${.PARSEFILE:R}.tmp/sub/sub/sub/workdir +CANARY_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-canary.mk +ACTUAL_FILE:= ${.PARSEFILE:R}.tmp/sub/opt-m-step3.mk + +_!= mkdir -p ${TEST_DIR} +_!= > ${CANARY_FILE} +_!= cp ${MAKEFILE} ${TEST_DIR}/step2.mk +_!= cp ${MAKEFILE} ${ACTUAL_FILE} + +step1: + @${.MAKE} -C ${TEST_DIR} -f step2.mk step2 + +.END: + @rm -rf ${MAKEFILE:R}.tmp + +.elif ${.PARSEFILE:T} == "step2.mk" + +# This is the file deep in the directory hierarchy. It sets up the +# search path for the .include <...> directive and then includes a +# single file from that search path. + +# This option adds .tmp/sub to the search path for .include <...>. +.MAKEFLAGS: -m .../opt-m-canary.mk + +# This option does not add any directory to the search path since the +# canary file does not exist. +.MAKEFLAGS: -m .../does-not-exist + +.include + +.elif ${.PARSEFILE:T} == "opt-m-step3.mk" + +# This file is included by step2.mk. + +step2: + @echo ok + +.else +. error +.endif Property changes on: head/contrib/bmake/unit-tests/opt-m-include-dir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-no-action-at-all.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-no-action-at-all.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-no-action-at-all.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-no-action-at-all.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-no-action-at-all.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-no-action-at-all.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-no-action-at-all.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -N command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-no-action-at-all.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-no-action.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-no-action.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-no-action.exp (revision 365366) @@ -0,0 +1,13 @@ +command during parsing +echo '.BEGIN: hidden command' +echo '.BEGIN: run always' +.BEGIN: run always +echo 'main: hidden command' +echo 'main: run always' +main: run always +run-always: hidden command +run-always: run always +echo '.END: hidden command' +echo '.END: run always' +.END: run always +exit status 0 Index: head/contrib/bmake/unit-tests/opt-no-action.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-no-action.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-no-action.mk (revision 365366) @@ -0,0 +1,33 @@ +# $NetBSD: opt-no-action.mk,v 1.3 2020/08/19 05:25:26 rillig Exp $ +# +# Tests for the -n command line option, which runs almost no commands. +# It just outputs them, to be inspected by human readers. +# Only commands that are in a .MAKE target or prefixed by '+' are run. + +# This command cannot be prevented from being run since it is used at parse +# time, and any later variable assignments may depend on its result. +!= echo 'command during parsing' 1>&2; echo + +all: main +all: run-always + +# Both of these commands are printed, but only the '+' command is run. +.BEGIN: + @echo '$@: hidden command' + @+echo '$@: run always' + +# Both of these commands are printed, but only the '+' command is run. +main: + @echo '$@: hidden command' + @+echo '$@: run always' + +# None of these commands is printed, but both are run, because this target +# depends on the special source ".MAKE". +run-always: .MAKE + @echo '$@: hidden command' + @+echo '$@: run always' + +# Both of these commands are printed, but only the '+' command is run. +.END: + @echo '$@: hidden command' + @+echo '$@: run always' Property changes on: head/contrib/bmake/unit-tests/opt-no-action.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-query.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-query.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-query.exp (revision 365366) @@ -0,0 +1,2 @@ +command during parsing +exit status 1 Index: head/contrib/bmake/unit-tests/opt-query.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-query.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-query.mk (revision 365366) @@ -0,0 +1,24 @@ +# $NetBSD: opt-query.mk,v 1.3 2020/08/19 05:13:18 rillig Exp $ +# +# Tests for the -q command line option. +# +# The -q option only looks at the dependencies between the targets. +# None of the commands in the targets are run, not even those that are +# prefixed with '+'. + +# This command cannot be prevented from being run since it is used at parse +# time, and any later variable assignments may depend on its result. +!= echo 'command during parsing' 1>&2; echo + +# None of these commands are run. +.BEGIN: + @echo '$@: hidden command' + @+echo '$@: run always' + +# None of these commands are run. +all: + @echo '$@: hidden command' + @+echo '$@: run always' + +# The exit status 1 is because the "all" target has to be made, that is, +# it is not up-to-date. Property changes on: head/contrib/bmake/unit-tests/opt-query.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-raw.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-raw.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-raw.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-raw.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-raw.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-raw.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-raw.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -r command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-raw.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-silent.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-silent.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-silent.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-silent.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-silent.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-silent.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-silent.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -s command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-silent.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-touch.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-touch.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-touch.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-touch.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-touch.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-touch.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-touch.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -t command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-touch.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-tracefile.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-tracefile.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-tracefile.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-tracefile.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-tracefile.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-tracefile.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -T command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-tracefile.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-var-expanded.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-var-expanded.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-var-expanded.exp (revision 365366) @@ -0,0 +1,3 @@ +other value $$ +value +exit status 0 Index: head/contrib/bmake/unit-tests/opt-var-expanded.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-var-expanded.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-var-expanded.mk (revision 365366) @@ -0,0 +1,6 @@ +# $NetBSD: opt-var-expanded.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -v command line option. + +VAR= other ${VALUE} $$$$ +VALUE= value Property changes on: head/contrib/bmake/unit-tests/opt-var-expanded.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-var-literal.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-var-literal.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-var-literal.exp (revision 365366) @@ -0,0 +1,3 @@ +other ${VALUE} $$$$ +value +exit status 0 Index: head/contrib/bmake/unit-tests/opt-var-literal.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-var-literal.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-var-literal.mk (revision 365366) @@ -0,0 +1,6 @@ +# $NetBSD: opt-var-literal.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -V command line option. + +VAR= other ${VALUE} $$$$ +VALUE= value Property changes on: head/contrib/bmake/unit-tests/opt-var-literal.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-warnings-as-errors.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-warnings-as-errors.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-warnings-as-errors.exp (revision 365366) @@ -0,0 +1,7 @@ +make: "opt-warnings-as-errors.mk" line 5: warning: message 1 +make: parsing warnings being treated as errors +make: "opt-warnings-as-errors.mk" line 6: warning: message 2 +parsing continues +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/opt-warnings-as-errors.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-warnings-as-errors.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-warnings-as-errors.mk (revision 365366) @@ -0,0 +1,11 @@ +# $NetBSD: opt-warnings-as-errors.mk,v 1.3 2020/08/23 14:28:04 rillig Exp $ +# +# Tests for the -W command line option, which turns warnings into errors. + +.warning message 1 +.warning message 2 + +_!= echo 'parsing continues' 1>&2 + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-warnings-as-errors.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-where-am-i.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-where-am-i.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-where-am-i.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-where-am-i.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-where-am-i.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-where-am-i.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-where-am-i.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -w command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-where-am-i.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt-x-reduce-exported.exp =================================================================== --- head/contrib/bmake/unit-tests/opt-x-reduce-exported.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt-x-reduce-exported.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt-x-reduce-exported.mk =================================================================== --- head/contrib/bmake/unit-tests/opt-x-reduce-exported.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt-x-reduce-exported.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt-x-reduce-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the -x command line option. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt-x-reduce-exported.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/opt.exp =================================================================== --- head/contrib/bmake/unit-tests/opt.exp (nonexistent) +++ head/contrib/bmake/unit-tests/opt.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/opt.mk =================================================================== --- head/contrib/bmake/unit-tests/opt.mk (nonexistent) +++ head/contrib/bmake/unit-tests/opt.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: opt.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the command line options. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/opt.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/phony-end.exp =================================================================== --- head/contrib/bmake/unit-tests/phony-end.exp (revision 365365) +++ head/contrib/bmake/unit-tests/phony-end.exp (revision 365366) @@ -1,6 +1,6 @@ .TARGET="phony" .PREFIX="phony" .IMPSRC="" -.TARGET="all" .PREFIX="all" .IMPSRC="phony" +.TARGET="all" .PREFIX="all" .IMPSRC="" .TARGET="ok" .PREFIX="ok" .IMPSRC="" .TARGET="also.ok" .PREFIX="also.ok" .IMPSRC="" .TARGET="bug" .PREFIX="bug" .IMPSRC="" exit status 0 Index: head/contrib/bmake/unit-tests/posix1.mk =================================================================== --- head/contrib/bmake/unit-tests/posix1.mk (revision 365365) +++ head/contrib/bmake/unit-tests/posix1.mk (revision 365366) @@ -1,184 +1,186 @@ -# $NetBSD: posix1.mk,v 1.3 2014/08/30 22:21:08 sjg Exp $ +# $NetBSD: posix1.mk,v 1.4 2020/08/10 18:19:58 rillig Exp $ # Keep the default suffixes from interfering, just in case. .SUFFIXES: all: line-continuations suffix-substitution localvars # we need to clean for repeatable results .BEGIN: clean clean: + @rm -f lib.a dir/* dummy obj* +.END: @rm -f lib.a dir/* dummy obj* # # Line continuations # # Escaped newlines and leading whitespace from the next line are replaced # with single space, except in commands, where the escape and the newline # are retained, but a single leading tab (if any) from the next line is # removed. (PR 49085) # Expect: # ${VAR} = "foo bar baz" # a # b # c VAR = foo\ \ bar\ baz line-continuations: @echo '$${VAR} = "${VAR}"' @echo 'aXbXc' | sed -e 's/X/\ /g' # # Suffix substitution # # The only variable modifier accepted by POSIX. # ${VAR:s1=s2}: replace s1, if found, with s2 at end of each word in # ${VAR}. s1 and s2 may contain macro expansions. # Expect: foo baR baz, bar baz, foo bar baz, fooadd baradd bazadd suffix-substitution: @echo '${VAR:r=R}, ${VAR:foo=}, ${VAR:not_there=wrong}, ${VAR:=add}' # # Local variables: regular forms, D/F forms and suffix substitution. # # In the past substitutions did not work with the D/F forms and those # forms were not available for $?. (PR 49085) ARFLAGS = -rcv localvars: lib.a # $@ = target or archive name $< = implied source # $* = target without suffix $? = sources newer than target # $% = archive member name LOCALS = \ "Local variables\n\ \$${@}=\"${@}\" \$${<}=\"${<}\"\n\ \$${*}=\"${*}\" \$${?}=\"${?}\"\n\ \$${%%}=\"${%}\"\n\n" # $XD = directory part of X $XF = file part of X # X is one of the local variables. LOCAL_ALTERNATIVES = \ "Directory and filename parts of local variables\n\ \$${@D}=\"${@D}\" \$${@F}=\"${@F}\"\n\ \$${'${@}' dir/obj_1.h obj_2.h obj3.h dummy dir/dummy: mkdir -p '${@D}' touch '${@}' Index: head/contrib/bmake/unit-tests/recursive.exp =================================================================== --- head/contrib/bmake/unit-tests/recursive.exp (nonexistent) +++ head/contrib/bmake/unit-tests/recursive.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "recursive.mk" line 34: Unclosed variable "MISSING_PAREN" +make: "recursive.mk" line 35: Unclosed variable "MISSING_BRACE" +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/recursive.mk =================================================================== --- head/contrib/bmake/unit-tests/recursive.mk (nonexistent) +++ head/contrib/bmake/unit-tests/recursive.mk (revision 365366) @@ -0,0 +1,37 @@ +# $NetBSD: recursive.mk,v 1.2 2020/08/06 05:52:45 rillig Exp $ +# +# In -dL mode, a variable may get expanded before it makes sense. +# This would stop make from doing anything since the "recursive" error +# is fatal and exits immediately. +# +# The purpose of evaluating that variable early was just to detect +# whether there are unclosed variables. It might be enough to parse the +# variable value without VARE_WANTRES for that purpose. +# +# Seen in pkgsrc/x11/libXfixes, and probably many more package that use +# GNU Automake. + +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +am__v_lt_1 = + +# On 2020-08-06, make reported: "Variable am__v_lt_ is recursive." +libXfixes_la_LINK = ... $(AM_V_lt) ... + +# somewhere later ... +AM_DEFAULT_VERBOSITY = 1 + + +# The purpose of the -dL flag is to detect unclosed variables. This +# can be achieved by just parsing the variable and not evaluating it. +# +# When the variable is only parsed but not evaluated, bugs in nested +# variables are not discovered. But these are hard to produce anyway, +# therefore that's acceptable. In most practical cases, the missing +# brace would be detected directly in the line where it is produced. +MISSING_BRACE_INDIRECT:= ${:U\${MISSING_BRACE} +UNCLOSED = $(MISSING_PAREN +UNCLOSED = ${MISSING_BRACE +UNCLOSED = ${MISSING_BRACE_INDIRECT} + Property changes on: head/contrib/bmake/unit-tests/recursive.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-dots.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-dots.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-dots.exp (revision 365366) @@ -0,0 +1,15 @@ +first first +hidden hidden +make: exec(...) failed (No such file or directory) +hidden delayed hidden +repeated repeated +commented commented +*** Error code 1 (ignored) +... # Run the below commands later + +commented delayed commented +first delayed first +repeated delayed repeated +repeated delayed twice repeated +*** Error code 127 (ignored) +exit status 0 Property changes on: head/contrib/bmake/unit-tests/sh-dots.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-dots.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-dots.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-dots.mk (revision 365366) @@ -0,0 +1,37 @@ +# $NetBSD: sh-dots.mk,v 1.1 2020/08/22 11:27:02 rillig Exp $ +# +# Tests for the special shell command line "...", which does not run the +# commands below it but appends them to the list of commands that are run +# at the end. + +all: first hidden repeated commented + +# The ${.TARGET} correctly expands to the target name, even though the +# commands are run separately from the main commands. +first: + @echo first ${.TARGET} + ... + @echo first delayed ${.TARGET} + +# The dots cannot be prefixed by the usual @-+ characters. +# They must be written exactly as dots. +hidden: .IGNORE + @echo hidden ${.TARGET} + @... + @echo hidden delayed ${.TARGET} + +# Since the shell command lines don't recognize '#' as comment character, +# the "..." is not interpreted specially here. +commented: .IGNORE + @echo commented ${.TARGET} + ... # Run the below commands later + @echo commented delayed ${.TARGET} + +# The "..." can appear more than once, even though that doesn't make sense. +# The second "..." is a no-op. +repeated: .IGNORE + @echo repeated ${.TARGET} + ... + @echo repeated delayed ${.TARGET} + ... + @echo repeated delayed twice ${.TARGET} Property changes on: head/contrib/bmake/unit-tests/sh-dots.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-jobs-error.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-jobs-error.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-jobs-error.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-jobs-error.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-jobs-error.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-jobs-error.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: sh-jobs-error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for error handling in the "run in jobs mode" part of the "Shell +# Commands" section from the manual page. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-jobs-error.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-jobs.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-jobs.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-jobs.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-jobs.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-jobs.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-jobs.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: sh-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the "run in jobs mode" part of the "Shell Commands" section +# from the manual page. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-jobs.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-leading-at.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-at.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-at.exp (revision 365366) @@ -0,0 +1,5 @@ +ok +space after @ +echo 'echoed' +echoed +exit status 0 Index: head/contrib/bmake/unit-tests/sh-leading-at.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-at.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-at.mk (revision 365366) @@ -0,0 +1,10 @@ +# $NetBSD: sh-leading-at.mk,v 1.3 2020/08/22 09:16:08 rillig Exp $ +# +# Tests for shell commands preceded by an '@', to suppress printing +# the command to stdout. + +all: + @ + @echo 'ok' + @ echo 'space after @' + echo 'echoed' Property changes on: head/contrib/bmake/unit-tests/sh-leading-at.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-leading-hyphen.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-hyphen.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-hyphen.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-leading-hyphen.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-hyphen.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-hyphen.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: sh-leading-hyphen.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for shell commands preceded by a '-', to ignore the exit status of +# the command line. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-leading-hyphen.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-leading-plus.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-plus.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-plus.exp (revision 365366) @@ -0,0 +1,4 @@ +echo 'this command is not run' +echo 'this command is run' +this command is run +exit status 0 Index: head/contrib/bmake/unit-tests/sh-leading-plus.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-leading-plus.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-leading-plus.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: sh-leading-plus.mk,v 1.3 2020/08/23 14:46:33 rillig Exp $ +# +# Tests for shell commands preceded by a '+', to run them even if +# the command line option -n is given. + +all: + @echo 'this command is not run' + @+echo 'this command is run' Property changes on: head/contrib/bmake/unit-tests/sh-leading-plus.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-meta-chars.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-meta-chars.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-meta-chars.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-meta-chars.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-meta-chars.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-meta-chars.mk (revision 365366) @@ -0,0 +1,11 @@ +# $NetBSD: sh-meta-chars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running shell commands that contain meta-characters. +# +# These meta-characters decide whether the command is run by the shell +# or executed directly via execv. See Cmd_Exec for details. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-meta-chars.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-multi-line.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-multi-line.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-multi-line.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-multi-line.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-multi-line.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-multi-line.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: sh-multi-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for multi-line shell commands, to ensure that the line breaks +# are preserved. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-multi-line.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh-single-line.exp =================================================================== --- head/contrib/bmake/unit-tests/sh-single-line.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh-single-line.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh-single-line.mk =================================================================== --- head/contrib/bmake/unit-tests/sh-single-line.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh-single-line.mk (revision 365366) @@ -0,0 +1,12 @@ +# $NetBSD: sh-single-line.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running single-line shell commands. +# +# In jobs mode, the shell commands are combined into a single shell +# program, as described in the manual page, section "Shell Commands", +# "the entire script". + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh-single-line.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sh.exp =================================================================== --- head/contrib/bmake/unit-tests/sh.exp (nonexistent) +++ head/contrib/bmake/unit-tests/sh.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/sh.mk =================================================================== --- head/contrib/bmake/unit-tests/sh.mk (nonexistent) +++ head/contrib/bmake/unit-tests/sh.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: sh.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for running shell commands from the targets, or from the != variable +# assignment operator or the :sh variable modifier. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/sh.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/sysv.exp =================================================================== --- head/contrib/bmake/unit-tests/sysv.exp (revision 365365) +++ head/contrib/bmake/unit-tests/sysv.exp (revision 365366) @@ -1,16 +1,15 @@ FOOBAR = FOOBAR = foobar fubar fun fun fun In the Sun acme aam.d sam.c a%.c asam.c.c asam.c a.c.c -ax:Q b c d eb exit status 0 Index: head/contrib/bmake/unit-tests/sysv.mk =================================================================== --- head/contrib/bmake/unit-tests/sysv.mk (revision 365365) +++ head/contrib/bmake/unit-tests/sysv.mk (revision 365366) @@ -1,48 +1,43 @@ -# $Id: sysv.mk,v 1.5 2020/07/04 18:16:55 sjg Exp $ +# $Id: sysv.mk,v 1.9 2020/08/23 16:08:32 sjg Exp $ +all: foo fun sam bla + FOO ?= FOOBAR = ${FOO:=bar} _this := ${.PARSEDIR}/${.PARSEFILE} B = /b S = / FUN = ${B}${S}fun SUN = the Sun # we expect nothing when FOO is empty -all: foo fun sam bla words - foo: @echo FOOBAR = ${FOOBAR} .if empty(FOO) @FOO="foo fu" ${.MAKE} -f ${_this} foo .endif fun: @echo ${FUN:T} @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) - -# The :Q looks like a modifier but isn't. -# It is part of the replacement string. -words: - @echo a${a b c d e:L:%a=x:Q}b Index: head/contrib/bmake/unit-tests/unexport-env.mk =================================================================== --- head/contrib/bmake/unit-tests/unexport-env.mk (revision 365365) +++ head/contrib/bmake/unit-tests/unexport-env.mk (revision 365366) @@ -1,14 +1,15 @@ -# $Id: unexport-env.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: unexport-env.mk,v 1.1.1.2 2020/07/28 16:57:18 sjg Exp $ # pick up a bunch of exported vars +FILTER_CMD= grep ^UT_ .include "export.mk" # an example of setting up a minimal environment. PATH = /bin:/usr/bin:/sbin:/usr/sbin # now clobber the environment to just PATH and UT_TEST UT_TEST = unexport-env # this removes everything .unexport-env .export PATH UT_TEST Index: head/contrib/bmake/unit-tests/unexport.mk =================================================================== --- head/contrib/bmake/unit-tests/unexport.mk (revision 365365) +++ head/contrib/bmake/unit-tests/unexport.mk (revision 365366) @@ -1,8 +1,19 @@ -# $Id: unexport.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $ +# $Id: unexport.mk,v 1.1.1.3 2020/08/08 22:34:25 sjg Exp $ # pick up a bunch of exported vars +FILTER_CMD= grep ^UT_ .include "export.mk" .unexport UT_ZOO UT_FOO UT_TEST = unexport + +# Until 2020-08-08, Var_UnExport had special handling for '\n', that code +# was not reachable though. At that point, backslash-newline has already +# been replaced with a simple space, and variables are not yet expanded. +UT_BEFORE_NL= before +UT_AFTER_NL= after +.export UT_BEFORE_NL UT_AFTER_NL +.unexport \ + UT_BEFORE_NL +.unexport ${.newline} UT_AFTER_NL Index: head/contrib/bmake/unit-tests/use-inference.exp =================================================================== --- head/contrib/bmake/unit-tests/use-inference.exp (nonexistent) +++ head/contrib/bmake/unit-tests/use-inference.exp (revision 365366) @@ -0,0 +1,4 @@ +Building use-inference.from from nothing +make: don't know how to make use-inference.to (continuing) +`all' not remade because of errors. +exit status 0 Index: head/contrib/bmake/unit-tests/use-inference.mk =================================================================== --- head/contrib/bmake/unit-tests/use-inference.mk (nonexistent) +++ head/contrib/bmake/unit-tests/use-inference.mk (revision 365366) @@ -0,0 +1,35 @@ +# $NetBSD: use-inference.mk,v 1.1 2020/08/09 16:32:28 rillig Exp $ +# +# Demonstrate that .USE rules do not have an effect on inference rules. +# At least not in the special case where the inference rule does not +# have any associated commands. + +.SUFFIXES: +.SUFFIXES: .from .to + +all: use-inference.to + +verbose: .USE + @echo 'Verbosely making $@ out of $>' + +.from.to: verbose +# Since this inference rule does not have any associated commands, it +# is ignored. +# +# @echo 'Building $@ from $<' + +use-inference.from: # assume it exists + @echo 'Building $@ from nothing' + +# Possible but unproven explanation: +# +# The main target is "all", which depends on "use-inference.to". +# The inference connects the .from to the .to file, otherwise make +# would not know that the .from file would need to be built. +# +# The .from file is then built. +# +# After this, make stops since it doesn't know how to make the .to file. +# This is strange since make definitely knows about the .from.to suffix +# inference rule. But it seems to ignore it, maybe because it doesn't +# have any associated commands. Property changes on: head/contrib/bmake/unit-tests/use-inference.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-class-cmdline.exp =================================================================== --- head/contrib/bmake/unit-tests/var-class-cmdline.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-cmdline.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-class-cmdline.mk =================================================================== --- head/contrib/bmake/unit-tests/var-class-cmdline.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-cmdline.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: var-class-cmdline.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variables specified on the command line. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-class-cmdline.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-class-env.exp =================================================================== --- head/contrib/bmake/unit-tests/var-class-env.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-env.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-class-env.mk =================================================================== --- head/contrib/bmake/unit-tests/var-class-env.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-env.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: var-class-env.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variables specified in the process environment. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-class-env.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-class-global.exp =================================================================== --- head/contrib/bmake/unit-tests/var-class-global.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-global.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-class-global.mk =================================================================== --- head/contrib/bmake/unit-tests/var-class-global.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-global.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: var-class-global.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for global variables, which are the most common variables. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-class-global.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-class-local-legacy.exp =================================================================== --- head/contrib/bmake/unit-tests/var-class-local-legacy.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-local-legacy.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-class-local-legacy.mk =================================================================== --- head/contrib/bmake/unit-tests/var-class-local-legacy.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-class-local-legacy.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: var-class-local-legacy.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for legacy target-local variables, such as ${&2 !} +VAR= ${:! echo 'this will be evaluated later' 1>&2 !} + +# Now force the variable to be evaluated. +# This outputs the line to stderr. +.if ${VAR} +.endif + +# In a variable assignment, the variable name must consist of a single word. +# +VARIABLE NAME= variable value + +# But if the whitespace appears inside parentheses or braces, everything is +# fine. +# +# XXX: This was not an intentional decision, as variable names typically +# neither contain parentheses nor braces. This is only a side-effect from +# the implementation of the parser, which cheats when parsing a variable +# name. It only counts parentheses and braces instead of properly parsing +# nested variable expressions such as VAR.${param}. +# +VAR(spaces in parentheses)= () +VAR{spaces in braces}= {} + +# Be careful and use indirect variable names here, to prevent accidentally +# accepting the test in case the parser just uses "VAR" as the variable name, +# ignoring all the rest. +# +VARNAME_PAREN= VAR(spaces in parentheses) +VARNAME_BRACES= VAR{spaces in braces} + +.if ${${VARNAME_PAREN}} != "()" +.error +.endif + +.if ${${VARNAME_BRACES}} != "{}" +.error +.endif + +# In safe mode, parsing would stop immediately after the "VARIABLE NAME=" +# line, since any commands run after that are probably working with +# unexpected variable values. +# +# Therefore, just output an info message. +.info Parsing still continues until here. + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-op-assign.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-op-default.exp =================================================================== --- head/contrib/bmake/unit-tests/var-op-default.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-default.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-op-default.mk =================================================================== --- head/contrib/bmake/unit-tests/var-op-default.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-default.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: var-op-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ?= variable assignment operator, which only assigns +# if the variable is still undefined. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-op-default.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-op-expand.exp =================================================================== --- head/contrib/bmake/unit-tests/var-op-expand.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-expand.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-op-expand.mk =================================================================== --- head/contrib/bmake/unit-tests/var-op-expand.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-expand.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: var-op-expand.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the := variable assignment operator, which expands its +# right-hand side. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-op-expand.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-op-shell.exp =================================================================== --- head/contrib/bmake/unit-tests/var-op-shell.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-shell.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-op-shell.mk =================================================================== --- head/contrib/bmake/unit-tests/var-op-shell.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-op-shell.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: var-op-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the != variable assignment operator, which runs its right-hand +# side through the shell. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-op-shell.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/var-op.exp =================================================================== --- head/contrib/bmake/unit-tests/var-op.exp (nonexistent) +++ head/contrib/bmake/unit-tests/var-op.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/var-op.mk =================================================================== --- head/contrib/bmake/unit-tests/var-op.mk (nonexistent) +++ head/contrib/bmake/unit-tests/var-op.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: var-op.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the variable assignment operators. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/var-op.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/vardebug.exp =================================================================== --- head/contrib/bmake/unit-tests/vardebug.exp (nonexistent) +++ head/contrib/bmake/unit-tests/vardebug.exp (revision 365366) @@ -0,0 +1,80 @@ +Global:RELEVANT = yes +Global:VAR = added +Global:VAR = overwritten +Global:delete VAR +Global:delete VAR (not found) +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:U} is "" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored +Global:FROM_CMDLINE = overwritten ignored! +Global:VAR = 1 +Global:VAR = 1 2 +Global:VAR = 1 2 3 +Var_Parse: ${VAR:M[2]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:M...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Pattern[VAR] for [1 2 3] is [[2]] +ModifyWords: split "1 2 3" into 3 words +VarMatch [1] [[2]] +VarMatch [2] [[2]] +VarMatch [3] [[2]] +Result of ${VAR:M[2]} is "2" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:N[2]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:N...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Pattern[VAR] for [1 2 3] is [[2]] +ModifyWords: split "1 2 3" into 3 words +Result of ${VAR:N[2]} is "1 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:S,2,two,} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:S...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Modifier part: "2" +Modifier part: "two" +ModifyWords: split "1 2 3" into 3 words +Result of ${VAR:S,2,two,} is "1 two 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:Q} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +QuoteMeta: [1\ 2\ 3] +Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${VAR:tu:tl:Q} with VARE_UNDEFERR|VARE_WANTRES +Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Result of ${VAR:tu} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Applying ${VAR:t...} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Result of ${VAR:tl} is "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Applying ${VAR:Q} to "1 2 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +QuoteMeta: [1\ 2\ 3] +Result of ${VAR:Q} is "1\ 2\ 3" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = none) +Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:Uvalue} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:UM*e} is "M*e" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Indirect modifier "M*e" from "${:UM*e}" +Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Pattern[] for [value] is [*e] +ModifyWords: split "value" into 1 words +VarMatch [value] [*e] +Result of ${:M*e} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Applying ${:M...} to "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Pattern[] for [value] is [valu[e]] +ModifyWords: split "value" into 1 words +VarMatch [value] [valu[e]] +Result of ${:Mvalu[e]} is "value" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Var_Parse: ${:UVAR} with VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:UVAR} is "VAR" (eflags = VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Global:delete VAR +Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK) +Result of ${:Uvariable} is "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +Applying ${:u...} to "variable" (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +make: Unknown modifier 'u' +Result of ${:unknown} is error (eflags = VARE_UNDEFERR|VARE_WANTRES, vflags = VAR_JUNK|VAR_KEEP) +make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown}) +Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES +make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED}) +Global:RELEVANT = no +exit status 1 Index: head/contrib/bmake/unit-tests/vardebug.mk =================================================================== --- head/contrib/bmake/unit-tests/vardebug.mk (nonexistent) +++ head/contrib/bmake/unit-tests/vardebug.mk (revision 365366) @@ -0,0 +1,59 @@ +# $NetBSD: vardebug.mk,v 1.3 2020/08/08 14:28:46 rillig Exp $ +# +# Demonstrates the debugging output for var.c. + +RELEVANT= yes + +VAR= added # VarAdd +VAR= overwritten # Var_Set +.undef VAR # Var_Delete (found) +.undef VAR # Var_Delete (not found) + +# The variable with the empty name cannot be set at all. +${:U}= empty name # Var_Set +${:U}+= empty name # Var_Append + +FROM_CMDLINE= overwritten # Var_Set (ignored) + +VAR= 1 +VAR+= 2 +VAR+= 3 + +.if ${VAR:M[2]} # VarMatch +.endif +.if ${VAR:N[2]} # VarNoMatch (no debug output) +.endif + +.if ${VAR:S,2,two,} # VarGetPattern +.endif + +.if ${VAR:Q} # VarQuote +.endif + +.if ${VAR:tu:tl:Q} # ApplyModifiers +.endif + +# ApplyModifiers, "Got ..." +.if ${:Uvalue:${:UM*e}:Mvalu[e]} +.endif + +.undef ${:UVAR} # Var_Delete + +# When ApplyModifiers results in an error, this appears in the debug log +# as "is error", without surrounding quotes. +.if ${:Uvariable:unknown} +.endif + +# XXX: The error message is "Malformed conditional", which is wrong. +# The condition is syntactically fine, it just contains an undefined variable. +# +# There is a specialized error message for "Undefined variable", but as of +# 2020-08-08, that is not covered by any unit tests. It might even be +# unreachable. +.if ${UNDEFINED} +.endif + +RELEVANT= no + +all: + @: Property changes on: head/contrib/bmake/unit-tests/vardebug.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varfind.exp =================================================================== --- head/contrib/bmake/unit-tests/varfind.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varfind.exp (revision 365366) @@ -0,0 +1,15 @@ +VarFind-aliases.to: long explicit-dependency VarFind-aliases.from +VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from +VarFind-aliases.to: long +VarFind-aliases.to: abbr +VarFind-aliases.to: long VarFind-aliases.from +VarFind-aliases.to: abbr VarFind-aliases.from +VarFind-aliases.to: long +VarFind-aliases.to: abbr +VarFind-aliases.to: long explicit-dependency VarFind-aliases.from +VarFind-aliases.to: abbr explicit-dependency VarFind-aliases.from +VarFind-aliases.to: long VarFind-aliases +VarFind-aliases.to: abbr VarFind-aliases +VarFind-aliases.to: long VarFind-aliases.to +VarFind-aliases.to: abbr VarFind-aliases.to +exit status 0 Index: head/contrib/bmake/unit-tests/varfind.mk =================================================================== --- head/contrib/bmake/unit-tests/varfind.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varfind.mk (revision 365366) @@ -0,0 +1,31 @@ +# $NetBSD: varfind.mk,v 1.1 2020/07/25 21:19:29 rillig Exp $ +# +# Demonstrates variable name aliases in VarFind. + +all: VarFind-aliases.to + +.SUFFIXES: .from .to + +VarFind-aliases.from: + @: do nothing + +VarFind-aliases.to: explicit-dependency + +explicit-dependency: + @: do nothing + +.from.to: + @echo $@: long ${.ALLSRC:Q} + @echo $@: abbr ${>:Q} + @echo $@: long ${.ARCHIVE:Q} + @echo $@: abbr ${!:Q} + @echo $@: long ${.IMPSRC:Q} + @echo $@: abbr ${<:Q} + @echo $@: long ${.MEMBER:Q} + @echo $@: abbr ${%:Q} + @echo $@: long ${.OODATE:Q} + @echo $@: abbr ${?:Q} + @echo $@: long ${.PREFIX:Q} + @echo $@: abbr ${*:Q} + @echo $@: long ${.TARGET:Q} + @echo $@: abbr ${@:Q} Property changes on: head/contrib/bmake/unit-tests/varfind.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmisc.exp =================================================================== --- head/contrib/bmake/unit-tests/varmisc.exp (revision 365365) +++ head/contrib/bmake/unit-tests/varmisc.exp (revision 365366) @@ -1,26 +1,74 @@ :D expanded when var set true TRUE :U expanded when var undef true TRUE :D skipped if var undef :U skipped when var set is set :? only lhs when value true true TRUE :? only rhs when value false false FALSE do not evaluate or expand :? if discarding is set year=2016 month=04 day=01 date=20160401 Version=123.456.789 == 123456789 Literal=3.4.5 == 3004005 We have target specific vars MAN= make.1 +save-dollars: 0 = $ +save-dollars: 1 = $$ +save-dollars: 2 = $$ +save-dollars: False = $ +save-dollars: True = $$ +save-dollars: false = $ +save-dollars: true = $$ +save-dollars: Yes = $$ +save-dollars: No = $ +save-dollars: yes = $$ +save-dollars: no = $ +save-dollars: On = $$ +save-dollars: Off = $ +save-dollars: ON = $$ +save-dollars: OFF = $ +save-dollars: on = $$ +save-dollars: off = $ +export-appended: env +export-appended: env +export-appended: env mk +parse-dynamic: parse-dynamic parse-dynamic before +parse-dynamic: parse-dynamic parse-dynamic after +parse-dynamic: parse-dynamic parse-dynamic after +varerror-unclosed:begin +make: Unclosed variable "" + +make: Unclosed variable "UNCLOSED" + +make: Unclosed variable "UNCLOSED" + +make: Unclosed variable "PATTERN" +make: Unclosed variable specification (expecting '}') for "UNCLOSED" (value "") modifier M + +make: Unclosed variable "param" +make: Unclosed variable "UNCLOSED." + + +make: Unclosed variable "UNCLOSED.1" + +make: Unclosed variable "UNCLOSED.2" + +make: Unclosed variable "UNCLOSED.3" + +make: Unclosed variable "UNCLOSED_ORIG" + +varerror-unclosed:end +target1-flags: we have: one two +target2-flags: we have: one two three four exit status 0 Index: head/contrib/bmake/unit-tests/varmisc.mk =================================================================== --- head/contrib/bmake/unit-tests/varmisc.mk (revision 365365) +++ head/contrib/bmake/unit-tests/varmisc.mk (revision 365366) @@ -1,84 +1,227 @@ -# $Id: varmisc.mk,v 1.11 2020/07/02 15:43:43 sjg Exp $ +# $Id: varmisc.mk,v 1.19 2020/08/31 16:28:10 sjg Exp $ # # Miscellaneous variable tests. all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \ strftime cmpv manok +all: save-dollars +all: export-appended +all: parse-dynamic +all: varerror-unclosed unmatched_var_paren: @echo ${foo::=foo-text} True = ${echo true >&2:L:sh}TRUE False= ${echo false >&2:L:sh}FALSE VSET= is set .undef UNDEF U_false: @echo :U skipped when var set @echo ${VSET:U${False}} D_false: @echo :D skipped if var undef @echo ${UNDEF:D${False}} U_true: @echo :U expanded when var undef @echo ${UNDEF:U${True}} D_true: @echo :D expanded when var set @echo ${VSET:D${True}} Q_lhs: @echo :? only lhs when value true @echo ${1:L:?${True}:${False}} Q_rhs: @echo :? only rhs when value false @echo ${0:L:?${True}:${False}} NQ_none: @echo do not evaluate or expand :? if discarding @echo ${VSET:U${1:L:?${True}:${False}}} April1= 1459494000 # slightly contorted syntax to use utc via variable strftime: @echo ${year=%Y month=%m day=%d:L:gmtime=1459494000} @echo date=${%Y%m%d:L:${gmtime=${April1}:L}} # big jumps to handle 3 digits per step M_cmpv.units = 1 1000 1000000 M_cmpv = S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh Version = 123.456.789 cmpv.only = target specific vars cmpv: @echo Version=${Version} == ${Version:${M_cmpv}} @echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}} @echo We have ${${.TARGET:T}.only} # catch misshandling of nested vars in .for loop MAN= MAN1= make.1 .for s in 1 2 .if defined(MAN$s) && !empty(MAN$s) MAN+= ${MAN$s} .endif .endfor manok: @echo MAN=${MAN} # This is an expanded variant of the above .for loop. -# Between 2020-08-28 and 2020-07-02 this paragraph generated a wrong +# Between 2020-06-28 and 2020-07-02 this paragraph generated a wrong # error message "Variable VARNAME is recursive". # When evaluating the !empty expression, the ${:U1} was not expanded and # thus resulted in the seeming definition VARNAME=${VARNAME}, which is # obviously recursive. VARNAME= ${VARNAME${:U1}} .if defined(VARNAME${:U2}) && !empty(VARNAME${:U2}) .endif + +# begin .MAKE.SAVE_DOLLARS; see Var_Set_with_flags and s2Boolean. +SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off +SD_4_DOLLARS= $$$$ + +.for val in ${SD_VALUES} +.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect. +SD.${val}:= ${SD_4_DOLLARS} +.endfor +.MAKE.SAVE_DOLLARS:= yes + +save-dollars: +.for val in ${SD_VALUES} + @printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q} +.endfor + +# Appending to an undefined variable does not add a space in front. +.undef APPENDED +APPENDED+= value +.if ${APPENDED} != "value" +.error "${APPENDED}" +.endif + +# Appending to an empty variable adds a space between the old value +# and the additional value. +APPENDED= # empty +APPENDED+= value +.if ${APPENDED} != " value" +.error "${APPENDED}" +.endif + +# Appending to parameterized variables works as well. +PARAM= param +VAR.${PARAM}= 1 +VAR.${PARAM}+= 2 +.if ${VAR.param} != "1 2" +.error "${VAR.param}" +.endif + +# The variable name can contain arbitrary characters. +# If the expanded variable name ends in a +, this still does not influence +# the parser. The assignment operator is still a simple assignment. +# Therefore, there is no need to add a space between the variable name +# and the assignment operator. +PARAM= + +VAR.${PARAM}= 1 +VAR.${PARAM}+= 2 +.if ${VAR.+} != "1 2" +.error "${VAR.+}" +.endif +.for param in + ! ? +VAR.${param}= ${param} +.endfor +.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?" +.error "${VAR.+}" "${VAR.!}" "${VAR.?}" +.endif + +# Appending to a variable from the environment creates a copy of that variable +# in the global context. +# The appended value is not exported automatically. +# When a variable is exported, the exported value is taken at the time of the +# .export directive. Later changes to the variable have no effect. +.export FROM_ENV_BEFORE +FROM_ENV+= mk +FROM_ENV_BEFORE+= mk +FROM_ENV_AFTER+= mk +.export FROM_ENV_AFTER + +export-appended: + @echo $@: "$$FROM_ENV" + @echo $@: "$$FROM_ENV_BEFORE" + @echo $@: "$$FROM_ENV_AFTER" + +# begin parse-dynamic +# +# Demonstrate that the target-specific variables are not evaluated in +# the global context. They are preserved until there is a local context +# in which resolving them makes sense. + +# There are different code paths for short names ... +${:U>}= before +GS_TARGET:= $@ +GS_MEMBER:= $% +GS_PREFIX:= $* +GS_ARCHIVE:= $! +GS_ALLSRC:= $> +${:U>}= after +# ... and for braced short names ... +GB_TARGET:= ${@} +GB_MEMBER:= ${%} +GB_PREFIX:= ${*} +GB_ARCHIVE:= ${!} +GB_ALLSRC:= ${>} +# ... and for long names. +GL_TARGET:= ${.TARGET} +GL_MEMBER:= ${.MEMBER} +GL_PREFIX:= ${.PREFIX} +GL_ARCHIVE:= ${.ARCHIVE} +GL_ALLSRC:= ${.ALLSRC} + +parse-dynamic: + @echo $@: ${GS_TARGET} ${GS_MEMBER} ${GS_PREFIX} ${GS_ARCHIVE} ${GS_ALLSRC} + @echo $@: ${GB_TARGET} ${GB_MEMBER} ${GB_PREFIX} ${GB_ARCHIVE} ${GB_ALLSRC} + @echo $@: ${GL_TARGET} ${GL_MEMBER} ${GL_PREFIX} ${GL_ARCHIVE} ${GL_ALLSRC} + +# Since 2020-07-28, make complains about unclosed variables. +# Before that, it had complained about unclosed variables only when +# parsing the modifiers, but not when parsing the variable name. + +UNCLOSED_INDIR_1= ${UNCLOSED_ORIG +UNCLOSED_INDIR_2= ${UNCLOSED_INDIR_1} + +FLAGS= one two +FLAGS+= ${FLAGS.${.ALLSRC:M*.c:T:u}} +FLAGS.target2.c = three four + +target1.c: +target2.c: + +all: target1-flags target2-flags +target1-flags: target1.c + @echo $@: we have: ${FLAGS} + +target2-flags: target2.c + @echo $@: we have: ${FLAGS} + +varerror-unclosed: + @echo $@:begin + @echo $( + @echo $(UNCLOSED + @echo ${UNCLOSED + @echo ${UNCLOSED:M${PATTERN + @echo ${UNCLOSED.${param + @echo $ +.for i in 1 2 3 + @echo ${UNCLOSED.${i} +.endfor + @echo ${UNCLOSED_INDIR_2} + @echo $@:end Index: head/contrib/bmake/unit-tests/varmod-assign.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-assign.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-assign.exp (revision 365366) @@ -0,0 +1,26 @@ +mod-assign: first=1. +mod-assign: last=3. +mod-assign: appended=1 2 3. +1 +2 +3 +mod-assign: ran:3. +mod-assign: global: 1, 3, 1 2 3, 3. +mod-assign-nested: then1t1 +mod-assign-nested: else2e2 +mod-assign-nested: then3t3 +mod-assign-nested: else4e4 +make: Bad modifier `:' for +value} +make: Bad modifier `:' for +mod-assign-empty: overwritten} +mod-assign-empty: VAR=overwritten +make: Unknown modifier ':' + +sysv:y +make: Unfinished modifier for ASSIGN ('}' missing) + +ok=word +make: " echo word; false " returned non-zero status +err=previous +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-assign.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-assign.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-assign.mk (revision 365366) @@ -0,0 +1,81 @@ +# $NetBSD: varmod-assign.mk,v 1.6 2020/08/25 21:16:53 rillig Exp $ +# +# Tests for the obscure ::= variable modifiers, which perform variable +# assignments during evaluation, just like the = operator in C. + +all: mod-assign +all: mod-assign-nested +all: mod-assign-empty +all: mod-assign-parse +all: mod-assign-shell-error + +mod-assign: + # The ::?= modifier applies the ?= assignment operator 3 times. + # The ?= operator only has an effect for the first time, therefore + # the variable FIRST ends up with the value 1. + @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}. + + # The ::= modifier applies the = assignment operator 3 times. + # The = operator overwrites the previous value, therefore the + # variable LAST ends up with the value 3. + @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}. + + # The ::+= modifier applies the += assignment operator 3 times. + # The += operator appends 3 times to the variable, therefore + # the variable APPENDED ends up with the value "1 2 3". + @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}. + + # The ::!= modifier applies the != assignment operator 3 times. + # The side effects of the shell commands are visible in the output. + # Just as with the ::= modifier, the last value is stored in the + # RAN variable. + @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}. + + # The assignments happen in the global scope and thus are + # preserved even after the shell command has been run. + @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}. + +mod-assign-nested: + # The condition "1" is true, therefore THEN1 gets assigned a value, + # and IT1 as well. Nothing surprising here. + @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1} + + # The condition "0" is false, therefore ELSE1 gets assigned a value, + # and IE1 as well. Nothing surprising here as well. + @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2} + + # The same effects happen when the variables are defined elsewhere. + @echo $@: ${SINK3:Q} + @echo $@: ${SINK4:Q} +SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3} +SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4} + +mod-assign-empty: + # Assigning to the empty variable would obviously not work since that variable + # is write-protected. Therefore it is rejected early as a "bad modifier". + @echo ${::=value} + @echo $@: ${:Uvalue::=overwritten} + + # The :L modifier sets the variable's value to its name. + # Since the name is still "VAR", assigning to that variable works. + @echo $@: ${VAR:L::=overwritten} VAR=${VAR} + +mod-assign-parse: + # The modifier for assignment operators starts with a ':'. + # An 'x' after that is an invalid modifier. + @echo ${ASSIGN::x} # 'x' is an unknown assignment operator + + # When parsing an assignment operator fails because the operator is + # incomplete, make falls back to the SysV modifier. + @echo ${SYSV::=sysv\:x}${SYSV::x=:y} + + @echo ${ASSIGN::=value # missing closing brace + +mod-assign-shell-error: + # If the command succeeds, the variable is assigned. + @${SH_OK::!= echo word; true } echo ok=${SH_OK} + + # If the command fails, the variable keeps its previous value. + # FIXME: the error message says: "previous" returned non-zero status + @${SH_ERR::=previous} + @${SH_ERR::!= echo word; false } echo err=${SH_ERR} Property changes on: head/contrib/bmake/unit-tests/varmod-assign.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-defined.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-defined.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-defined.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-defined.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-defined.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-defined.mk (revision 365366) @@ -0,0 +1,28 @@ +# $NetBSD: varmod-defined.mk,v 1.3 2020/08/25 21:58:08 rillig Exp $ +# +# Tests for the :D variable modifier, which returns the given string +# if the variable is defined. It is closely related to the :U modifier. + +DEF= defined +.undef UNDEF + +# Since DEF is defined, the value of the expression is "value", not +# "defined". +# +.if ${DEF:Dvalue} != "value" +.error +.endif + +# Since UNDEF is not defined, the "value" is ignored. Instead of leaving the +# expression undefined, it is set to "", exactly to allow the expression to +# be used in .if conditions. In this place, other undefined expressions +# would generate an error message. +# XXX: Ideally the error message would be "undefined variable", but as of +# 2020-08-25 it is "Malformed conditional". +# +.if ${UNDEF:Dvalue} != "" +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-defined.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-edge.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-edge.exp (revision 365365) +++ head/contrib/bmake/unit-tests/varmod-edge.exp (revision 365366) @@ -1,17 +1,22 @@ +make: "varmod-edge.mk" line omitted: ok M-paren +make: "varmod-edge.mk" line omitted: ok M-mixed +make: "varmod-edge.mk" line omitted: ok M-unescape 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 +make: "varmod-edge.mk" line omitted: ok M-nest-mix +make: "varmod-edge.mk" line omitted: ok M-nest-brk +make: "varmod-edge.mk" line omitted: ok M-pat-err +make: "varmod-edge.mk" line omitted: ok M-bsbs +make: "varmod-edge.mk" line omitted: ok M-bs1-par +make: "varmod-edge.mk" line omitted: ok M-bs2-par +make: "varmod-edge.mk" line omitted: ok M-128 +make: "varmod-edge.mk" line omitted: ok eq-ext +make: "varmod-edge.mk" line omitted: ok eq-q +make: "varmod-edge.mk" line omitted: ok eq-bs +make: Unfinished modifier for INP.eq-esc ('=' missing) +make: "varmod-edge.mk" line omitted: ok eq-esc +make: "varmod-edge.mk" line omitted: ok colon +make: Unknown modifier ':' +make: Unknown modifier ':' +make: "varmod-edge.mk" line omitted: ok colons +ok exit status 0 Index: head/contrib/bmake/unit-tests/varmod-edge.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-edge.mk (revision 365365) +++ head/contrib/bmake/unit-tests/varmod-edge.mk (revision 365366) @@ -1,162 +1,173 @@ -# $NetBSD: varmod-edge.mk,v 1.7 2020/04/27 14:07:22 christos Exp $ +# $NetBSD: varmod-edge.mk,v 1.12 2020/08/08 13:29:09 rillig 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. +# Having only an escaped '=' results in a parse error. +# The call to "pattern.lhs = ParseModifierPart" 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) +# make: Unfinished modifier for INP.eq-esc ('=' missing) -all: +TESTS+= colon +INP.colon= value +MOD.colon= ${INP.colon:} +EXP.colon= value + +TESTS+= colons +INP.colons= value +MOD.colons= ${INP.colons::::} +EXP.colons= # empty + .for test in ${TESTS} . if ${MOD.${test}} == ${EXP.${test}} - @printf 'ok %s\n' ${test:Q}'' +.info ok ${test} . else - @printf 'error in %s: expected %s, got %s\n' \ - ${test:Q}'' ${EXP.${test}:Q}'' ${MOD.${test}:Q}'' +.warning error in ${test}: expected "${EXP.${test}}", got "${MOD.${test}}" . endif .endfor + +all: + @echo ok Index: head/contrib/bmake/unit-tests/varmod-exclam-shell.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-exclam-shell.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-exclam-shell.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-exclam-shell.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-exclam-shell.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-exclam-shell.mk (revision 365366) @@ -0,0 +1,28 @@ +# $NetBSD: varmod-exclam-shell.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $ +# +# Tests for the :!cmd! variable modifier. + +.if ${:!echo hello | tr 'l' 'l'!} != "hello" +.warning unexpected +.endif + +# The output is truncated at the first null byte. +# Cmd_Exec returns only a string pointer without length information. +.if ${:!echo hello | tr 'l' '\0'!} != "he" +.warning unexpected +.endif + +.if ${:!echo!} != "" +.warning A newline at the end of the output must be stripped. +.endif + +.if ${:!echo;echo!} != " " +.warning Only a single newline at the end of the output is stripped. +.endif + +.if ${:!echo;echo;echo;echo!} != " " +.warning Other newlines in the output are converted to spaces. +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-exclam-shell.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-extension.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-extension.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-extension.exp (revision 365366) @@ -0,0 +1,10 @@ +extension of 'a/b/c' is '' +extension of 'def' is '' +extension of 'a.b.c' is 'c' +extension of 'a.b/c' is 'b/c' +extension of 'a' is '' +extension of 'a.a' is 'a' +extension of '.gitignore' is 'gitignore' +extension of 'a' is '' +extension of 'a.a' is 'a' +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-extension.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-extension.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-extension.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-extension.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :E variable modifier, which returns the filename extension +# of each word in the variable. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "extension of '"${path:Q}"' is '"${path:E:Q}"'" +.endfor Property changes on: head/contrib/bmake/unit-tests/varmod-extension.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-gmtime.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-gmtime.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-gmtime.exp (revision 365366) @@ -0,0 +1,9 @@ +mod-gmtime: +%Y +2020 +%Y +%Y +mod-gmtime-indirect: +make: Unknown modifier '1' + +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-gmtime.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-gmtime.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-gmtime.mk (revision 365366) @@ -0,0 +1,35 @@ +# $NetBSD: varmod-gmtime.mk,v 1.2 2020/08/16 12:48:55 rillig Exp $ +# +# Tests for the :gmtime variable modifier, which formats a timestamp +# using strftime(3). + +all: mod-gmtime +all: mod-gmtime-indirect + +mod-gmtime: + @echo $@: + @echo ${%Y:L:gmtim=1593536400} # modifier name too short + @echo ${%Y:L:gmtime=1593536400} # 2020-07-01T00:00:00Z + @echo ${%Y:L:gmtimer=1593536400} # modifier name too long + @echo ${%Y:L:gm=gm:M*} + +mod-gmtime-indirect: + @echo $@: + + # As of 2020-08-16, it is not possible to pass the seconds via a + # variable expression. This is because parsing of the :gmtime + # modifier stops at the '$' and returns to ApplyModifiers. + # + # There, a colon would be skipped but not a dollar. + # Parsing therefore continues at the '$' of the ${:U159...}, looking + # for an ordinary variable modifier. + # + # At this point, the ${:U} is expanded and interpreted as a variable + # modifier, which results in the error message "Unknown modifier '1'". + # + # If ApplyModifier_Gmtime were to pass its argument through + # ParseModifierPart, this would work. + @echo ${%Y:L:gmtime=${:U1593536400}} + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-gmtime.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-hash.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-hash.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-hash.exp (revision 365366) @@ -0,0 +1,9 @@ +make: Unknown modifier 'h' + +26bb0f5f +12345 +make: Unknown modifier 'h' + +make: Unknown modifier 'h' + +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-hash.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-hash.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-hash.mk (revision 365366) @@ -0,0 +1,10 @@ +# $NetBSD: varmod-hash.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :hash variable modifier. + +all: + @echo ${12345:L:has} # modifier name too short + @echo ${12345:L:hash} # ok + @echo ${12345:L:hash=SHA-256} # :hash does not accept '=' + @echo ${12345:L:hasX} # misspelled + @echo ${12345:L:hashed} # modifier name too long Property changes on: head/contrib/bmake/unit-tests/varmod-hash.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-head.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-head.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-head.exp (revision 365366) @@ -0,0 +1,10 @@ +head (dirname) of 'a/b/c' is 'a/b' +head (dirname) of 'def' is '.' +head (dirname) of 'a.b.c' is '.' +head (dirname) of 'a.b/c' is 'a.b' +head (dirname) of 'a' is '.' +head (dirname) of 'a.a' is '.' +head (dirname) of '.gitignore' is '.' +head (dirname) of 'a' is '.' +head (dirname) of 'a.a' is '.' +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-head.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-head.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-head.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-head.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :H variable modifier, which returns the dirname of +# each of the words in the variable value. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "head (dirname) of '"${path:Q}"' is '"${path:H:Q}"'" +.endfor Property changes on: head/contrib/bmake/unit-tests/varmod-head.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-ifelse.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-ifelse.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-ifelse.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-ifelse.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-ifelse.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-ifelse.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-ifelse.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the ${cond:?then:else} variable modifier, which evaluates either +# the then-expression or the else-expression, depending on the condition. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-ifelse.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-l-name-to-value.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-l-name-to-value.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-l-name-to-value.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-l-name-to-value.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-l-name-to-value.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-l-name-to-value.mk (revision 365366) @@ -0,0 +1,31 @@ +# $NetBSD: varmod-l-name-to-value.mk,v 1.3 2020/08/25 22:25:05 rillig Exp $ +# +# Tests for the :L modifier, which returns the variable name as the new value. + +# The empty variable name leads to an empty string. +.if ${:L} != "" +.error +.endif + +# The variable name is converted into an expression with the variable name +# "VARNAME" and the value "VARNAME". +.if ${VARNAME:L} != "VARNAME" +.error +.endif + +# The value of the expression can be modified afterwards. +.if ${VARNAME:L:S,VAR,,} != "NAME" +.error +.endif + +# The name of the expression is still the same as before. Using the :L +# modifier, it can be restored. +# +# Hmmm, this can be used as a double storage or a backup mechanism. +# Probably unintended, but maybe useful. +.if ${VARNAME:L:S,VAR,,:L} != "VARNAME" +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-l-name-to-value.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-localtime.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-localtime.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-localtime.exp (revision 365366) @@ -0,0 +1,4 @@ +%Y +2020 +%Y +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-localtime.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-localtime.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-localtime.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-localtime.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :localtime variable modifier, which returns the given time, +# formatted as a local timestamp. + +all: + @echo ${%Y:L:localtim=1593536400} # modifier name too short + @echo ${%Y:L:localtime=1593536400} # 2020-07-01T00:00:00Z + @echo ${%Y:L:localtimer=1593536400} # modifier name too long Property changes on: head/contrib/bmake/unit-tests/varmod-localtime.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-loop.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-loop.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-loop.exp (revision 365366) @@ -0,0 +1,16 @@ +:+one+ +two+ +three+: +:x1y x2y x3y: +:x1y x2y x3y: +:mod-loop-varname: :x1y x2y x3y: :: +:x1y x2y x3y: +mod-loop-resolve:w1d2d3w w2i3w w1i2d3 2i${RES3}w w1d2d3 2i${RES3} 1i${RES2}w: +mod-loop-varname-dollar:(1) (2) (3). +mod-loop-varname-dollar:() () (). +mod-loop-varname-dollar:() () (). +mod-loop-dollar:1: +mod-loop-dollar:${word}$: +mod-loop-dollar:$3$: +mod-loop-dollar:$${word}$$: +mod-loop-dollar:$$5$$: +mod-loop-dollar:$$${word}$$$: +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-loop.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-loop.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-loop.mk (revision 365366) @@ -0,0 +1,63 @@ +# $NetBSD: varmod-loop.mk,v 1.2 2020/08/16 12:30:45 rillig Exp $ +# +# Tests for the :@var@...${var}...@ variable modifier. + +all: mod-loop-varname +all: mod-loop-resolve +all: mod-loop-varname-dollar +all: mod-loop-dollar + +# In the :@ modifier, the name of the loop variable can even be generated +# dynamically. There's no practical use-case for this, and hopefully nobody +# will ever depend on this, but technically it's possible. +# Therefore, in -dL mode, this is forbidden, see lint.mk. +mod-loop-varname: + @echo :${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@:Q}: + # ":::" is a very creative variable name, unlikely in practice + # The expression ${\:\:\:} would not work since backslashes can only + # be escaped in the modifiers, but not in the variable name. + @echo :${:U1 2 3:@:::@x${${:U\:\:\:}}y@}: + # "@@" is another creative variable name. + @echo :${:U1 2 3:@\@\@@x${@@}y@}: + # Even "@" works as a variable name since the variable is installed + # in the "current" scope, which in this case is the one from the + # target. + @echo :$@: :${:U1 2 3:@\@@x${@}y@}: :$@: + # In extreme cases, even the backslash can be used as variable name. + # It needs to be doubled though. + @echo :${:U1 2 3:@\\@x${${:Ux:S,x,\\,}}y@}: + +# The :@ modifier resolves the variables a little more often than expected. +# In particular, it resolves _all_ variables from the context, and not only +# the loop variable (in this case v). +# +# The d means direct reference, the i means indirect reference. +RESOLVE= ${RES1} $${RES1} +RES1= 1d${RES2} 1i$${RES2} +RES2= 2d${RES3} 2i$${RES3} +RES3= 3 + +mod-loop-resolve: + @echo $@:${RESOLVE:@v@w${v}w@:Q}: + +# Until 2020-07-20, the variable name of the :@ modifier could end with one +# or two dollar signs, which were silently ignored. +# There's no point in allowing a dollar sign in that position. +mod-loop-varname-dollar: + @echo $@:${1 2 3:L:@v$@($v)@:Q}. + @echo $@:${1 2 3:L:@v$$@($v)@:Q}. + @echo $@:${1 2 3:L:@v$$$@($v)@:Q}. + +# Demonstrate that it is possible to generate dollar characters using the +# :@ modifier. +# +# These are edge cases that could have resulted in a parse error as well +# since the $@ at the end could have been interpreted as a variable, which +# would mean a missing closing @ delimiter. +mod-loop-dollar: + @echo $@:${:U1:@word@${word}$@:Q}: + @echo $@:${:U2:@word@$${word}$$@:Q}: + @echo $@:${:U3:@word@$$${word}$$$@:Q}: + @echo $@:${:U4:@word@$$$${word}$$$$@:Q}: + @echo $@:${:U5:@word@$$$$${word}$$$$$@:Q}: + @echo $@:${:U6:@word@$$$$$${word}$$$$$$@:Q}: Property changes on: head/contrib/bmake/unit-tests/varmod-loop.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-match-escape.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-match-escape.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-match-escape.exp (revision 365366) @@ -0,0 +1,3 @@ +Pattern[SPECIALS] for [\: : \\ * \*] is [\:] +Pattern[SPECIALS] for [\: : \\ * \*] is [:] +exit status 0 Property changes on: head/contrib/bmake/unit-tests/varmod-match-escape.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-match-escape.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-match-escape.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-match-escape.mk (revision 365366) @@ -0,0 +1,20 @@ +# $NetBSD: varmod-match-escape.mk,v 1.1 2020/08/16 20:03:53 rillig Exp $ +# +# As of 2020-08-01, the :M and :N modifiers interpret backslashes differently, +# depending on whether there was a variable expression somewhere before the +# first backslash or not. See ApplyModifier_Match, "copy = TRUE". +# +# Apart from the different and possibly confusing debug output, there is no +# difference in behavior. When parsing the modifier text, only \{, \} and \: +# are unescaped, and in the pattern matching these have the same meaning as +# their plain variants '{', '}' and ':'. In the pattern matching from +# Str_Match, only \*, \? or \[ would make a noticeable difference. +SPECIALS= \: : \\ * \* +RELEVANT= yes +.if ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} +.warning unexpected +.endif +RELEVANT= no + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-match-escape.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-match.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-match.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-match.exp (revision 365366) @@ -0,0 +1,5 @@ +match-char-class: + uppercase numbers: One Two Three Four + all the others: five six seven + starts with non-s, ends with [ex]: One Three five +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-match.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-match.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-match.mk (revision 365366) @@ -0,0 +1,22 @@ +# $NetBSD: varmod-match.mk,v 1.3 2020/08/16 20:03:53 rillig Exp $ +# +# Tests for the :M variable modifier, which filters words that match the +# given pattern. + +all: match-char-class +all: slow + + +NUMBERS= One Two Three Four five six seven + +match-char-class: + @echo '$@:' + @echo ' uppercase numbers: ${NUMBERS:M[A-Z]*}' + @echo ' all the others: ${NUMBERS:M[^A-Z]*}' + @echo ' starts with non-s, ends with [ex]: ${NUMBERS:M[^s]*[ex]}' + + +# Before 2020-06-13, this expression took quite a long time in Str_Match, +# calling itself 601080390 times for 16 asterisks. +slow: + @: ${:U****************:M****************b} Property changes on: head/contrib/bmake/unit-tests/varmod-match.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-no-match.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-no-match.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-no-match.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-no-match.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-no-match.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-no-match.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-no-match.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :N variable modifier, which filters words that do not match +# the given pattern. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-no-match.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-order-reverse.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-order-reverse.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order-reverse.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-order-reverse.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-order-reverse.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order-reverse.mk (revision 365366) @@ -0,0 +1,13 @@ +# $NetBSD: varmod-order-reverse.mk,v 1.3 2020/08/16 20:13:10 rillig Exp $ +# +# Tests for the :Or variable modifier, which returns the words, sorted in +# descending order. + +NUMBERS= one two three four five six seven eight nine ten + +.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight" +.error ${NUMBERS:Or} +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-order-reverse.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-order-shuffle.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-order-shuffle.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order-shuffle.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-order-shuffle.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-order-shuffle.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order-shuffle.mk (revision 365366) @@ -0,0 +1,39 @@ +# $NetBSD: varmod-order-shuffle.mk,v 1.3 2020/08/16 20:43:01 rillig Exp $ +# +# Tests for the :Ox variable modifier, which returns the words of the +# variable, shuffled. +# +# As of 2020-08-16, make uses random(3) seeded by the current time in seconds. +# This makes the random numbers completely predictable since there is no other +# part of make that uses random numbers. + +NUMBERS= one two three four five six seven eight nine ten + +# 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 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. + +# Create two shuffles using the := assignment operator. +shuffled1:= ${NUMBERS:Ox} +shuffled2:= ${NUMBERS:Ox} +.if ${shuffled1} == ${shuffled2} +.error ${shuffled1} == ${shuffled2} +.endif + +# Sorting the list before shuffling it has no effect. +shuffled1:= ${NUMBERS:O:Ox} +shuffled2:= ${NUMBERS:O:Ox} +.if ${shuffled1} == ${shuffled2} +.error ${shuffled1} == ${shuffled2} +.endif + +# Sorting after shuffling must produce the original numbers. +sorted:= ${NUMBERS:Ox:O} +.if ${sorted} != ${NUMBERS:O} +.error ${sorted} != ${NUMBERS:O} +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-order-shuffle.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-order.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-order.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order.exp (revision 365366) @@ -0,0 +1,7 @@ +make: Bad modifier `:OX' for NUMBERS +make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX" +make: Bad modifier `:OxXX' for NUMBERS +make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox" +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/varmod-order.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-order.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-order.mk (revision 365366) @@ -0,0 +1,19 @@ +# $NetBSD: varmod-order.mk,v 1.4 2020/08/16 20:43:01 rillig Exp $ +# +# Tests for the :O variable modifier, which returns the words, sorted in +# ascending order. + +NUMBERS= one two three four five six seven eight nine ten + +.if ${NUMBERS:O} != "eight five four nine one seven six ten three two" +.error ${NUMBERS:O} +.endif + +# Unknown modifier "OX" +_:= ${NUMBERS:OX} + +# Unknown modifier "OxXX" +_:= ${NUMBERS:OxXX} + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-order.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-path.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-path.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-path.exp (revision 365366) @@ -0,0 +1,4 @@ +varmod-path.subdir/varmod-path.phony +varmod-path.subdir/varmod-path.real +varmod-path.enoent +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-path.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-path.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-path.mk (revision 365366) @@ -0,0 +1,35 @@ +# $NetBSD: varmod-path.mk,v 1.3 2020/08/23 08:10:49 rillig Exp $ +# +# Tests for the :P variable modifier, which looks up the path for a given +# target. +# +# The phony target does not have a corresponding path, therefore ... oops, +# as of 2020-08-23 it is nevertheless resolved to a path. This is probably +# unintended. +# +# The real target is located in a subdirectory, and its full path is returned. +# If it had been in the current directory, the difference between its path and +# its name would not be visible. +# +# The enoent target does not exist, therefore the target name is returned. + +.MAIN: all + +_!= rm -rf varmod-path.subdir +_!= mkdir varmod-path.subdir +_!= > varmod-path.subdir/varmod-path.phony +_!= > varmod-path.subdir/varmod-path.real + +# To have an effect, this .PATH declaration must be after the directory is created. +.PATH: varmod-path.subdir + +varmod-path.phony: .PHONY +varmod-path.real: + +all: varmod-path.phony varmod-path.real + @echo ${varmod-path.phony:P} + @echo ${varmod-path.real:P} + @echo ${varmod-path.enoent:P} + +.END: + @rm -rf varmod-path.subdir Property changes on: head/contrib/bmake/unit-tests/varmod-path.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-quote-dollar.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-quote-dollar.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-quote-dollar.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-quote-dollar.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-quote-dollar.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-quote-dollar.mk (revision 365366) @@ -0,0 +1,10 @@ +# $NetBSD: varmod-quote-dollar.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :q variable modifier, which quotes the string for the shell +# and doubles dollar signs, to prevent them from being interpreted by a +# child process of make. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-quote-dollar.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-quote.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-quote.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-quote.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-quote.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-quote.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-quote.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-quote.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :Q variable modifier, which quotes the variable value +# to be used in a shell program. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-quote.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-range.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-range.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-range.exp (revision 365366) @@ -0,0 +1,8 @@ +make: Unknown modifier 'r' + +1 2 3 +make: Unknown modifier 'r' + +make: Unknown modifier 'r' + +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-range.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-range.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-range.mk (revision 365366) @@ -0,0 +1,10 @@ +# $NetBSD: varmod-range.mk,v 1.3 2020/08/23 15:13:21 rillig Exp $ +# +# Tests for the :range variable modifier, which generates sequences +# of integers from the given range. + +all: + @echo ${a b c:L:rang} # modifier name too short + @echo ${a b c:L:range} # ok + @echo ${a b c:L:rango} # misspelled + @echo ${a b c:L:ranger} # modifier name too long Property changes on: head/contrib/bmake/unit-tests/varmod-range.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-remember.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-remember.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-remember.exp (revision 365366) @@ -0,0 +1,3 @@ +1 2 3 1 2 3 1 2 3 +1 2 3, SAVED=3 +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-remember.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-remember.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-remember.mk (revision 365366) @@ -0,0 +1,12 @@ +# $NetBSD: varmod-remember.mk,v 1.3 2020/08/23 15:18:43 rillig Exp $ +# +# Tests for the :_ modifier, which saves the current variable value +# in the _ variable or another, to be used later again. + +# In the parameterized form, having the variable name on the right side of +# the = assignment operator is confusing. In almost all other situations +# the variable name is on the left-hand side of the = operator. Luckily +# this modifier is only rarely needed. +all: + @echo ${1 2 3:L:_:@var@${_}@} + @echo ${1 2 3:L:@var@${var:_=SAVED:}@}, SAVED=${SAVED} Property changes on: head/contrib/bmake/unit-tests/varmod-remember.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-root.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-root.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-root.exp (revision 365366) @@ -0,0 +1,10 @@ +root of 'a/b/c' is 'a/b/c' +root of 'def' is 'def' +root of 'a.b.c' is 'a.b' +root of 'a.b/c' is 'a' +root of 'a' is 'a' +root of 'a.a' is 'a' +root of '.gitignore' is '' +root of 'a' is 'a' +root of 'a.a' is 'a' +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-root.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-root.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-root.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-root.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :R variable modifier, which returns the filename root +# without the extension. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "root of '"${path:Q}"' is '"${path:R:Q}"'" +.endfor Property changes on: head/contrib/bmake/unit-tests/varmod-root.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-select-words.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-select-words.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-select-words.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-select-words.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-select-words.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-select-words.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :[...] variable modifier, which selects a single word +# or a range of words from a variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-select-words.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-shell.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-shell.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-shell.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-shell.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-shell.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-shell.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-shell.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :sh variable modifier, which runs the shell command +# given by the variable value and returns its output. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-shell.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-subst-regex.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-subst-regex.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-subst-regex.exp (revision 365366) @@ -0,0 +1,23 @@ +make: Regex compilation error: (details omitted) +mod-regex-compile-error: C,word,____,:Q}. +make: No subexpression \1 +make: No subexpression \1 +make: No subexpression \1 +make: No subexpression \1 +mod-regex-limits:11-missing:1 6 +mod-regex-limits:11-ok:1 22 446 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +mod-regex-limits:22-missing:1 6 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +make: No subexpression \2 +mod-regex-limits:22-missing:1 6 +mod-regex-limits:22-ok:1 33 556 +mod-regex-limits:capture:ihgfedcbaabcdefghijABCDEFGHIJa0a1a2rest +make: Regex compilation error: (details omitted) +mod-regex-errors: +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-subst-regex.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-subst-regex.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-subst-regex.mk (revision 365366) @@ -0,0 +1,87 @@ +# $NetBSD: varmod-subst-regex.mk,v 1.3 2020/08/28 17:15:04 rillig Exp $ +# +# Tests for the :C,from,to, variable modifier. + +all: mod-regex-compile-error +all: mod-regex-limits +all: mod-regex-errors + +# The variable expression expands to 4 words. Of these words, none matches +# the regular expression "a b" since these words don't contain any +# whitespace. +.if ${:Ua b b c:C,a b,,} != "a b b c" +.error +.endif + +# Using the '1' modifier does not change anything. The '1' modifier just +# means to apply at most 1 replacement in the whole variable expression. +.if ${:Ua b b c:C,a b,,1} != "a b b c" +.error +.endif + +# The 'W' modifier treats the whole variable value as a single big word, +# containing whitespace. This big word matches the regular expression, +# therefore it gets replaced. Whitespace is preserved after replacing. +.if ${:Ua b b c:C,a b,,W} != " b c" +.error +.endif + +# The 'g' modifier does not have any effect here since each of the words +# contains the character 'b' a single time. +.if ${:Ua b b c:C,b,,g} != "a c" +.error +.endif + +# The first :C modifier has the 'W' modifier, which makes the whole +# expression a single word. The 'g' modifier then replaces all occurrences +# of "1 2" with "___". The 'W' modifier only applies to this single :C +# modifier. This is demonstrated by the :C modifier that follows. If the +# 'W' modifier would be preserved, only a single underscore would have been +# replaced with an 'x'. +.if ${:U1 2 3 1 2 3:C,1 2,___,Wg:C,_,x,} != "x__ 3 x__ 3" +.error +.endif + +# The regular expression does not match in the first word. +# It matches once in the second word, and the \0\0 doubles that word. +# In the third word, the regular expression matches as early as possible, +# and since the matches must not overlap, the next possible match would +# start at the 6, but at that point, there is only one character left, +# and that cannot match the regular expression "..". Therefore only the +# "45" is doubled in the result. +.if ${:U1 23 456:C,..,\0\0,} != "1 2323 45456" +.error +.endif + +# The modifier '1' applies the replacement at most once, across the whole +# variable value, no matter whether it is a single big word or many small +# words. +# +# Up to 2020-08-28, the manual page said that the modifiers '1' and 'g' +# were orthogonal, which was wrong. +.if ${:U12345 12345:C,.,\0\0,1} != "112345 12345" +.error +.endif + +# Multiple asterisks form an invalid regular expression. This produces an +# error message and (as of 2020-08-28) stops parsing in the middle of the +# variable expression. The unparsed part of the expression is then copied +# verbatim to the output, which is unexpected and can lead to strange shell +# commands being run. +mod-regex-compile-error: + @echo $@: ${:Uword1 word2:C,****,____,g:C,word,____,:Q}. + +# These tests generate error messages but as of 2020-08-28 just continue +# parsing and execution as if nothing bad had happened. +mod-regex-limits: + @echo $@:11-missing:${:U1 23 456:C,..,\1\1,:Q} + @echo $@:11-ok:${:U1 23 456:C,(.).,\1\1,:Q} + @echo $@:22-missing:${:U1 23 456:C,..,\2\2,:Q} + @echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q} + @echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q} + # The :C modifier only handles single-digit capturing groups, + # which is more than enough for daily use. + @echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,} + +mod-regex-errors: + @echo $@: ${UNDEF:Uvalue:C,[,,} Property changes on: head/contrib/bmake/unit-tests/varmod-subst-regex.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-subst.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-subst.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-subst.exp (revision 365366) @@ -0,0 +1,51 @@ +mod-subst: +:a b b c: +:a b b c: +: b c: +:a c: +:x__ 3 x__ 3: +12345 +mod-subst-delimiter: +1 two 3 horizontal tabulator +1 two 3 space +1 two 3 exclamation mark +1 two 3 double quotes +1 two 3 hash +1 two 3 dollar +1 two 3 percent +1 two 3 apostrophe +1 two 3 opening parenthesis +1 two 3 closing parenthesis +1 two 3 digit +1 two 3 colon +1 two 3 less than sign +1 two 3 equal sign +1 two 3 greater than sign +1 two 3 question mark +1 two 3 at +1 two 3 letter +1 two 3 opening bracket +1 two 3 backslash +1 two 3 closing bracket +1 two 3 caret +1 two 3 opening brace +1 two 3 vertical line +1 two 3 closing brace +1 two 3 tilde +mod-subst-chain: +A B c. +make: Unknown modifier 'i' +. +mod-subst-dollar:$1: +mod-subst-dollar:$2: +mod-subst-dollar:$3: +mod-subst-dollar:$4: +mod-subst-dollar:$5: +mod-subst-dollar:$6: +mod-subst-dollar:$7: +mod-subst-dollar:$8: +mod-subst-dollar:$40: +mod-subst-dollar:U8: +mod-subst-dollar:$$$$: +mod-subst-dollar:$$$good3 +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-subst.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-subst.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-subst.mk (revision 365366) @@ -0,0 +1,153 @@ +# $NetBSD: varmod-subst.mk,v 1.3 2020/08/19 06:10:06 rillig Exp $ +# +# Tests for the :S,from,to, variable modifier. + +all: mod-subst +all: mod-subst-delimiter +all: mod-subst-chain +all: mod-subst-dollar + +WORDS= sequences of letters +.if ${WORDS:S,,,} != ${WORDS} +.warning The empty pattern matches something. +.endif +.if ${WORDS:S,e,*,1} != "s*quences of letters" +.warning The :S modifier flag '1' is not applied exactly once. +.endif +.if ${WORDS:S,f,*,1} != "sequences o* letters" +.warning The :S modifier flag '1' is only applied to the first word,\ + not to the first occurrence. +.endif +.if ${WORDS:S,e,*,} != "s*quences of l*tters" +.warning The :S modifier does not replace every first match per word. +.endif +.if ${WORDS:S,e,*,g} != "s*qu*nc*s of l*tt*rs" +.warning The :S modifier flag 'g' does not replace every occurrence. +.endif +.if ${WORDS:S,^sequ,occurr,} != "occurrences of letters" +.warning The :S modifier fails for a short match anchored at the start. +.endif +.if ${WORDS:S,^of,with,} != "sequences with letters" +.warning The :S modifier fails for an exact match anchored at the start. +.endif +.if ${WORDS:S,^office,does not match,} != ${WORDS} +.warning The :S modifier matches a too long pattern anchored at the start. +.endif +.if ${WORDS:S,f$,r,} != "sequences or letters" +.warning The :S modifier fails for a short match anchored at the end. +.endif +.if ${WORDS:S,s$,,} != "sequence of letter" +.warning The :S modifier fails to replace one occurrence per word. +.endif +.if ${WORDS:S,of$,,} != "sequences letters" +.warning The :S modifier fails for an exact match anchored at the end. +.endif +.if ${WORDS:S,eof$,,} != ${WORDS} +.warning The :S modifier matches a too long pattern anchored at the end. +.endif +.if ${WORDS:S,^of$,,} != "sequences letters" +.warning The :S modifier does not match a word anchored at both ends. +.endif +.if ${WORDS:S,^o$,,} != ${WORDS} +.warning The :S modifier matches a prefix anchored at both ends. +.endif +.if ${WORDS:S,^f$,,} != ${WORDS} +.warning The :S modifier matches a suffix anchored at both ends. +.endif +.if ${WORDS:S,^eof$,,} != ${WORDS} +.warning The :S modifier matches a too long prefix anchored at both ends. +.endif +.if ${WORDS:S,^office$,,} != ${WORDS} +.warning The :S modifier matches a too long suffix anchored at both ends. +.endif + +mod-subst: + @echo $@: + @echo :${:Ua b b c:S,a b,,:Q}: + @echo :${:Ua b b c:S,a b,,1:Q}: + @echo :${:Ua b b c:S,a b,,W:Q}: + @echo :${:Ua b b c:S,b,,g:Q}: + @echo :${:U1 2 3 1 2 3:S,1 2,___,Wg:S,_,x,:Q}: + @echo ${:U12345:S,,sep,g:Q} + +# The :S and :C modifiers accept an arbitrary character as the delimiter, +# including characters that are otherwise used as escape characters or +# interpreted in a special way. This can be used to confuse humans. +mod-subst-delimiter: + @echo $@: + @echo ${:U1 2 3:S 2 two :Q} horizontal tabulator + @echo ${:U1 2 3:S 2 two :Q} space + @echo ${:U1 2 3:S!2!two!:Q} exclamation mark + @echo ${:U1 2 3:S"2"two":Q} double quotes + # In shell command lines, the hash does not need to be escaped. + # It needs to be escaped in variable assignment lines though. + @echo ${:U1 2 3:S#2#two#:Q} hash + @echo ${:U1 2 3:S$2$two$:Q} dollar + @echo ${:U1 2 3:S%2%two%:Q} percent + @echo ${:U1 2 3:S'2'two':Q} apostrophe + @echo ${:U1 2 3:S(2(two(:Q} opening parenthesis + @echo ${:U1 2 3:S)2)two):Q} closing parenthesis + @echo ${:U1 2 3:S121two1:Q} digit + @echo ${:U1 2 3:S:2:two::Q} colon + @echo ${:U1 2 3:S<22>two>:Q} greater than sign + @echo ${:U1 2 3:S?2?two?:Q} question mark + @echo ${:U1 2 3:S@2@two@:Q} at + @echo ${:U1 2 3:Sa2atwoa:Q} letter + @echo ${:U1 2 3:S[2[two[:Q} opening bracket + @echo ${:U1 2 3:S\2\two\:Q} backslash + @echo ${:U1 2 3:S]2]two]:Q} closing bracket + @echo ${:U1 2 3:S^2^two^:Q} caret + @echo ${:U1 2 3:S{2{two{:Q} opening brace + @echo ${:U1 2 3:S|2|two|:Q} vertical line + @echo ${:U1 2 3:S}2}two}:Q} closing brace + @echo ${:U1 2 3:S~2~two~:Q} tilde + +# The :S and :C modifiers can be chained without a separating ':'. +# This is not documented in the manual page. +# It works because ApplyModifier_Subst scans for the known modifiers g1W +# and then just returns to ApplyModifiers. There, the colon is optionally +# skipped (see the *st.next == ':' at the end of the loop). +# +# Most other modifiers cannot be chained since their parsers skip until +# the next ':' or '}' or ')'. +mod-subst-chain: + @echo $@: + @echo ${:Ua b c:S,a,A,S,b,B,}. + # There is no 'i' modifier for the :S or :C modifiers. + # The error message is "make: Unknown modifier 'i'", which is + # kind of correct, although it is mixing the terms for variable + # modifiers with the matching modifiers. + @echo ${:Uvalue:S,a,x,i}. + +# No matter how many dollar characters there are, they all get merged +# into a single dollar by the :S modifier. +# +# As of 2020-08-09, this is because ParseModifierPart sees a '$' and +# calls Var_Parse to expand the variable. In all other places, the "$$" +# is handled outside of Var_Parse. Var_Parse therefore considers "$$" +# one of the "really stupid names", skips the first dollar, and parsing +# continues with the next character. This repeats for the other dollar +# signs, except the one before the delimiter. That one is handled by +# the code that optionally interprets the '$' as the end-anchor in the +# first part of the :S modifier. That code doesn't call Var_Parse but +# simply copies the dollar to the result. +mod-subst-dollar: + @echo $@:${:U1:S,^,$,:Q}: + @echo $@:${:U2:S,^,$$,:Q}: + @echo $@:${:U3:S,^,$$$,:Q}: + @echo $@:${:U4:S,^,$$$$,:Q}: + @echo $@:${:U5:S,^,$$$$$,:Q}: + @echo $@:${:U6:S,^,$$$$$$,:Q}: + @echo $@:${:U7:S,^,$$$$$$$,:Q}: + @echo $@:${:U8:S,^,$$$$$$$$,:Q}: + @echo $@:${:U40:S,^,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$,:Q}: +# This generates no dollar at all: + @echo $@:${:UU8:S,^,${:U$$$$$$$$},:Q}: +# Here is an alternative way to generate dollar characters. +# It's unexpectedly complicated though. + @echo $@:${:U:range=5:ts\x24:C,[0-9],,g:Q}: +# In modifiers, dollars are escaped using the backslash, not using another +# dollar sign. Therefore, creating a dollar sign is pretty simple: + @echo $@:${:Ugood3:S,^,\$\$\$,:Q} Property changes on: head/contrib/bmake/unit-tests/varmod-subst.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-sysv.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-sysv.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-sysv.exp (revision 365366) @@ -0,0 +1,8 @@ +ax:Q b c d eb +bcd.e +& +anchor-dollar: value +anchor-dollar: valux +mismatch: file.cpp file.h +mismatch: renamed.c other.c +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-sysv.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-sysv.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-sysv.mk (revision 365366) @@ -0,0 +1,61 @@ +# $NetBSD: varmod-sysv.mk,v 1.3 2020/08/23 14:52:06 rillig Exp $ +# +# Tests for the ${VAR:from=to} variable modifier, which replaces the suffix +# "from" with "to". It can also use '%' as a wildcard. +# +# This modifier is applied when the other modifiers don't match exactly. + +all: words ampersand anchor-dollar mismatch + +# The :Q looks like a modifier but isn't. +# It is part of the replacement string. +words: + @echo a${a b c d e:L:%a=x:Q}b + +# Before 2020-07-19, an ampersand could be used in the replacement part +# of a SysV substitution modifier. This was probably a copy-and-paste +# mistake since the SysV modifier code looked a lot like the code for the +# :S and :C modifiers. The ampersand is not mentioned in the manual page. +ampersand: + @echo ${:U${a.bcd.e:L:a.%=%}:Q} + @echo ${:U${a.bcd.e:L:a.%=&}:Q} + +# Before 2020-07-20, when a SysV modifier was parsed, a single dollar +# before the '=' was interpreted as an anchor, which doesn't make sense +# since the anchor was discarded immediately. +anchor-dollar: + @echo $@: ${:U${value:L:e$=x}:Q} + @echo $@: ${:U${value:L:e=x}:Q} + +# Words that don't match are copied unmodified. +# The % placeholder can be anywhere in the string. +mismatch: + @echo $@: ${:Ufile.c file.h:%.c=%.cpp} + @echo $@: ${:Ufile.c other.c:file.%=renamed.%} + +# Trying to cover all possible variants of the SysV modifier. +LIST= one two +EXPR.1= ${LIST:o=X} +EXP.1= one twX +EXPR.2= ${LIST:o=} +EXP.2= one tw +EXPR.3= ${LIST:o=%} +EXP.3= one tw% +EXPR.4= ${LIST:%o=X} +EXP.4= one X +EXPR.5= ${LIST:o%=X} +EXP.5= X two +EXPR.6= ${LIST:o%e=X} +EXP.6= X two +EXPR.7= ${LIST:o%%e=X} # Only the first '%' is the wildcard. +EXP.7= one two # None of the words contains a literal '%'. +EXPR.8= ${LIST:%=%%} +EXP.8= one% two% +EXPR.9= ${LIST:%nes=%xxx} # lhs is longer than the word "one" +EXP.9= one two + +.for i in ${:U:range=9} +.if ${EXPR.$i} != ${EXP.$i} +.warning test case $i expected "${EXP.$i}", got "${EXPR.$i} +.endif +.endfor Property changes on: head/contrib/bmake/unit-tests/varmod-sysv.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-tail.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-tail.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-tail.exp (revision 365366) @@ -0,0 +1,10 @@ +tail (basename) of 'a/b/c' is 'c' +tail (basename) of 'def' is 'def' +tail (basename) of 'a.b.c' is 'a.b.c' +tail (basename) of 'a.b/c' is 'c' +tail (basename) of 'a' is 'a' +tail (basename) of 'a.a' is 'a.a' +tail (basename) of '.gitignore' is '.gitignore' +tail (basename) of 'a' is 'a' +tail (basename) of 'a.a' is 'a.a' +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-tail.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-tail.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-tail.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-tail.mk,v 1.3 2020/08/23 15:09:15 rillig Exp $ +# +# Tests for the :T variable modifier, which returns the basename of each of +# the words in the variable value. + +all: +.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a + @echo "tail (basename) of '"${path:Q}"' is '"${path:T:Q}"'" +.endfor Property changes on: head/contrib/bmake/unit-tests/varmod-tail.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-abs.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-abs.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-abs.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-to-abs.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-abs.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-abs.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-abs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tA variable modifier, which returns the absolute path for +# each of the words in the variable value. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-to-abs.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-lower.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-lower.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-lower.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-to-lower.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-lower.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-lower.mk (revision 365366) @@ -0,0 +1,19 @@ +# $NetBSD: varmod-to-lower.mk,v 1.3 2020/08/28 17:21:02 rillig Exp $ +# +# Tests for the :tl variable modifier, which returns the words in the +# variable value, converted to lowercase. + +.if ${:UUPPER:tl} != "upper" +.error +.endif + +.if ${:Ulower:tl} != "lower" +.error +.endif + +.if ${:UMixeD case.:tl} != "mixed case." +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-to-lower.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-many-words.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-many-words.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-many-words.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-to-many-words.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-many-words.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-many-words.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-many-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tw modifier, which treats the variable as many words, +# to undo a previous :tW modifier. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-to-many-words.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-one-word.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-one-word.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-one-word.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-to-one-word.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-one-word.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-one-word.mk (revision 365366) @@ -0,0 +1,9 @@ +# $NetBSD: varmod-to-one-word.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the :tW variable modifier, which treats the variable value +# as a single word, for all following modifiers. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-to-one-word.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-separator.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-separator.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-separator.exp (revision 365366) @@ -0,0 +1,9 @@ +make: Bad modifier `:tx' for WORDS +make: "varmod-to-separator.mk" line 104: Malformed conditional (${WORDS:tx} != "anything") +make: "varmod-to-separator.mk" line 108: Parsing continues here. +make: Bad modifier `:t\X' for WORDS +make: "varmod-to-separator.mk" line 112: Malformed conditional (${WORDS:t\X} != "anything") +make: "varmod-to-separator.mk" line 115: Parsing continues here. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/varmod-to-separator.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-separator.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-separator.mk (revision 365366) @@ -0,0 +1,118 @@ +# $NetBSD: varmod-to-separator.mk,v 1.3 2020/08/31 19:58:21 rillig Exp $ +# +# Tests for the :ts variable modifier, which joins the words of the variable +# using an arbitrary character as word separator. + +WORDS= one two three four five six + +# The words are separated by a single space, just as usual. +.if ${WORDS:ts } != "one two three four five six" +. warning Space as separator does not work. +.endif + +# The separator can be an arbitrary character, for example a comma. +.if ${WORDS:ts,} != "one,two,three,four,five,six" +. warning Comma as separator does not work. +.endif + +# After the :ts modifier, other modifiers can follow. +.if ${WORDS:ts/:tu} != "ONE/TWO/THREE/FOUR/FIVE/SIX" +. warning Chaining modifiers does not work. +.endif + +# To use the ':' as the separator, just write it normally. +# The first colon is the separator, the second ends the modifier. +.if ${WORDS:ts::tu} != "ONE:TWO:THREE:FOUR:FIVE:SIX" +. warning Colon as separator does not work. +.endif + +# When there is just a colon but no other character, the words are +# "separated" by an empty string, that is, they are all squashed +# together. +.if ${WORDS:ts:tu} != "ONETWOTHREEFOURFIVESIX" +. warning Colon as separator does not work. +.endif + +# Applying the :tu modifier first and then the :ts modifier does not change +# anything since neither of these modifiers is related to how the string is +# split into words. Beware of separating the words using a single or double +# quote though, or other special characters like dollar or backslash. +# +# This example also demonstrates that the closing brace is not interpreted +# as a separator, but as the closing delimiter of the whole variable +# expression. +.if ${WORDS:tu:ts} != "ONETWOTHREEFOURFIVESIX" +. warning Colon as separator does not work. +.endif + +# The '}' plays the same role as the ':' in the preceding examples. +# Since there is a single character before it, that character is taken as +# the separator. +.if ${WORDS:tu:ts/} != "ONE/TWO/THREE/FOUR/FIVE/SIX" +. warning Colon as separator does not work. +.endif + +# Now it gets interesting and ambiguous: The separator could either be empty +# since it is followed by a colon. Or it could be the colon since that +# colon is followed by the closing brace. It's the latter case. +.if ${WORDS:ts:} != "one:two:three:four:five:six" +. warning Colon followed by closing brace does not work. +.endif + +# As in the ${WORDS:tu:ts} example above, the separator is empty. +.if ${WORDS:ts} != "onetwothreefourfivesix" +. warning Empty separator before closing brace does not work. +.endif + +# The :ts modifier can be followed by other modifiers. +.if ${WORDS:ts:S/two/2/} != "one2threefourfivesix" +. warning Separator followed by :S modifier does not work. +.endif + +# The :ts modifier can follow other modifiers. +.if ${WORDS:S/two/2/:ts} != "one2threefourfivesix" +. warning :S modifier followed by :ts modifier does not work. +.endif + +# The :ts modifier with an actual separator can be followed by other +# modifiers. +.if ${WORDS:ts/:S/two/2/} != "one/2/three/four/five/six" +. warning The :ts modifier followed by an :S modifier does not work. +.endif + +# The separator can be \n, which is a newline. +.if ${WORDS:[1..3]:ts\n} != "one${.newline}two${.newline}three" +. warning The separator \n does not produce a newline. +.endif + +# The separator can be \t, which is a tab. +.if ${WORDS:[1..3]:ts\t} != "one two three" +. warning The separator \t does not produce a tab. +.endif + +# The separator can be given as octal number. +.if ${WORDS:[1..3]:ts\012:tu} != "ONE${.newline}TWO${.newline}THREE" +. warning The separator \012 is not interpreted in octal ASCII. +.endif + +# The separator can be given as hexadecimal number. +.if ${WORDS:[1..3]:ts\xa:tu} != "ONE${.newline}TWO${.newline}THREE" +. warning The separator \xa is not interpreted in hexadecimal ASCII. +.endif + +# In the :t modifier, the :t must be followed by any of A, l, s, u. +.if ${WORDS:tx} != "anything" +. info This line is not reached because of the malformed condition. +. info If this line were reached, it would be visible in the -dcpv log. +.endif +.info Parsing continues here. + +# After the backslash, only n, t, an octal number, or x and a hexadecimal +# number are allowed. +.if ${WORDS:t\X} != "anything" +. info This line is not reached. +.endif +.info Parsing continues here. + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-to-separator.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-to-upper.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-upper.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-upper.exp (revision 365366) @@ -0,0 +1,2 @@ +mod-tu-space: A B +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-to-upper.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-to-upper.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-to-upper.mk (revision 365366) @@ -0,0 +1,21 @@ +# $NetBSD: varmod-to-upper.mk,v 1.4 2020/08/28 17:21:02 rillig Exp $ +# +# Tests for the :tu variable modifier, which returns the words in the +# variable value, converted to uppercase. + +.if ${:UUPPER:tu} != "UPPER" +.error +.endif + +.if ${:Ulower:tu} != "LOWER" +.error +.endif + +.if ${:UMixeD case.:tu} != "MIXED CASE." +.error +.endif + +# The :tu and :tl modifiers operate on the variable value as a single string, +# not as a list of words. Therefore, the adjacent spaces are preserved. +mod-tu-space: + @echo $@: ${a b:L:tu:Q} Property changes on: head/contrib/bmake/unit-tests/varmod-to-upper.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-undefined.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-undefined.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-undefined.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-undefined.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-undefined.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-undefined.mk (revision 365366) @@ -0,0 +1,55 @@ +# $NetBSD: varmod-undefined.mk,v 1.3 2020/08/23 20:49:33 rillig Exp $ +# +# Tests for the :U variable modifier, which returns the given string +# if the variable is undefined. +# +# The pattern ${:Uword} is heavily used when expanding .for loops. + +# This is how an expanded .for loop looks like. +# .for word in one +# . if ${word} != one +# . error ${word} +# . endif +# .endfor + +.if ${:Uone} != one +. error ${:Uone} +.endif + +# The variable expressions in the text of the :U modifier may be arbitrarily +# nested. + +.if ${:U${:Unested}${${${:Udeeply}}}} != nested +.error +.endif + +# The nested variable expressions may contain braces, and these braces don't +# need to match pairwise. In the following example, the :S modifier uses '{' +# as delimiter, which confuses both editors and humans because the opening +# and # closing braces don't match anymore. It's syntactically valid though. +# For more similar examples, see varmod-subst.mk, mod-subst-delimiter. + +.if ${:U${:Uvalue:S{a{X{}} != vXlue +.error +.endif + +# The escaping rules for the :U modifier (left-hand side) and condition +# string literals (right-hand side) are completely different. +# +# In the :U modifier, the backslash only escapes very few characters, all +# other backslashes are retained. +# +# In condition string literals, the backslash always escapes the following +# character, no matter whether it would be necessary or not. +# +# In both contexts, \n is an escaped letter n, not a newline; that's what +# the .newline variable is for. +# +# Whitespace at the edges is preserved, on both sides of the comparison. + +.if ${:U \: \} \$ \\ \a \b \n } != " : } \$ \\ \\a \\b \\n " +.error +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-undefined.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod-unique.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod-unique.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-unique.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod-unique.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod-unique.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod-unique.mk (revision 365366) @@ -0,0 +1,39 @@ +# $NetBSD: varmod-unique.mk,v 1.4 2020/08/31 17:41:38 rillig Exp $ +# +# Tests for the :u variable modifier, which discards adjacent duplicate +# words. + +.if ${:U1 2 1:u} != "1 2 1" +. warning The :u modifier only merges _adjacent_ duplicate words. +.endif + +.if ${:U1 2 2 3:u} != "1 2 3" +. warning The :u modifier must merge adjacent duplicate words. +.endif + +.if ${:U:u} != "" +. warning The :u modifier must do nothing with an empty word list. +.endif + +.if ${:U1:u} != "1" +. warning The :u modifier must do nothing with a single-element word list. +.endif + +.if ${:U1 1 1 1 1 1 1 1:u} != "1" +. warning The :u modifier must merge _all_ adjacent duplicate words. +.endif + +.if ${:U 1 2 1 1 :u} != "1 2 1" +. warning The :u modifier must normalize whitespace between the words. +.endif + +.if ${:U1 1 1 1 2:u} != "1 2" +. warning Duplicate words at the beginning must be merged. +.endif + +.if ${:U1 2 2 2 2:u} != "1 2" +. warning Duplicate words at the end must be merged. +.endif + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod-unique.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varmod.exp =================================================================== --- head/contrib/bmake/unit-tests/varmod.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varmod.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varmod.mk =================================================================== --- head/contrib/bmake/unit-tests/varmod.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varmod.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varmod.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varmod.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dollar.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dollar.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dollar.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "varname-dollar.mk" line 16: dollar is $. +make: "varname-dollar.mk" line 17: dollar in braces is . +make: "varname-dollar.mk" line 25: dollar is $. +make: "varname-dollar.mk" line 26: dollar in braces is dollar. +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dollar.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dollar.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dollar.mk (revision 365366) @@ -0,0 +1,29 @@ +# $NetBSD: varname-dollar.mk,v 1.3 2020/08/19 05:40:06 rillig Exp $ +# +# Tests for the expression "$$", which looks as if it referred to a variable, +# but simply expands to a single '$' sign. +# +# If there really were a special variable named '$', the expressions ${${DOLLAR}} +# and $$ would always expand to the same value. + +# Using the dollar sign in variable names is tricky and not recommended. +# To see that using this variable indeed affects the variable '$', run the +# test individually with the -dv option. +DOLLAR= $$ + +# At this point, the variable '$' is not defined. Therefore the second line +# returns an empty string. +.info dollar is $$. +.info dollar in braces is ${${DOLLAR}}. + +# Now overwrite the '$' variable to see whether '$$' really expands to that +# variable, or whether '$$' is handled by the parser. +${DOLLAR}= dollar + +# At this point, the variable '$' is defined, therefore its value is printed +# in the second .info directive. +.info dollar is $$. +.info dollar in braces is ${${DOLLAR}}. + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dollar.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-alltargets.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-alltargets.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-alltargets.exp (revision 365366) @@ -0,0 +1,4 @@ + +first second source +first second source all .END +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-alltargets.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-alltargets.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-alltargets.mk (revision 365366) @@ -0,0 +1,25 @@ +# $NetBSD: varname-dot-alltargets.mk,v 1.3 2020/08/25 22:51:54 rillig Exp $ +# +# Tests for the special .ALLTARGETS variable. + +.MAIN: all + +TARGETS_1:= ${.ALLTARGETS} + +first second: source + +TARGETS_2:= ${.ALLTARGETS} + +all: + # Since the tests are run with the -r option, no targets are + # defined at the beginning. + @echo ${TARGETS_1} + + # Only first and second are "real" targets. + # The .ALLTARGETS variable is not about targets though, but + # about all nodes, therefore source is also included. + @echo ${TARGETS_2} + + # Interestingly, the .END target is also implicitly defined at + # this point. + @echo ${.ALLTARGETS} Property changes on: head/contrib/bmake/unit-tests/varname-dot-alltargets.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-curdir.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-curdir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-curdir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-curdir.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-curdir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-curdir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-curdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .CURDIR variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-curdir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includedfromdir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-includedfromdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .INCLUDEDFROMDIR variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-includedfromdir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includedfromfile.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-includedfromfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .INCLUDEDFROMFILE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-includedfromfile.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-includes.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includes.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includes.exp (revision 365366) @@ -0,0 +1,2 @@ +.INCLUDES= -I. -I.. +exit status 0 Property changes on: head/contrib/bmake/unit-tests/varname-dot-includes.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-includes.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-includes.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-includes.mk (revision 365366) @@ -0,0 +1,20 @@ +# $NetBSD: varname-dot-includes.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $ +# +# Tests for the special .INCLUDES variable, which is not documented in the +# manual page. +# +# It is yet unclear in which situations this feature is useful. + +.SUFFIXES: .h + +.PATH.h: . .. + +.INCLUDES: .h + +# The .INCLUDES variable is not yet available. +.if defined(${.INCLUDES:Q}) +.error +.endif + +all: + @echo .INCLUDES=${.INCLUDES:Q} Property changes on: head/contrib/bmake/unit-tests/varname-dot-includes.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-libs.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-libs.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-libs.exp (revision 365366) @@ -0,0 +1,2 @@ +.LIBS= -L. -L.. +exit status 0 Property changes on: head/contrib/bmake/unit-tests/varname-dot-libs.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-libs.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-libs.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-libs.mk (revision 365366) @@ -0,0 +1,20 @@ +# $NetBSD: varname-dot-libs.mk,v 1.1 2020/08/28 03:51:06 rillig Exp $ +# +# Tests for the special .LIBS variable, which is not documented in the +# manual page. +# +# It is yet unclear in which situations this feature is useful. + +.SUFFIXES: .a + +.PATH.a: . .. + +.LIBS: .a + +# The .LIBS variable is not yet available. +.if defined(${.LIBS:Q}) +.error +.endif + +all: + @echo .LIBS=${.LIBS:Q} Property changes on: head/contrib/bmake/unit-tests/varname-dot-libs.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-dependfile.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-dependfile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.DEPENDFILE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-dependfile.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-expand_variables.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.EXPAND_VARIABLES variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-expand_variables.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-exported.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-exported.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-exported.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-exported.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-exported.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-exported.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-exported.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.EXPORTED variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-exported.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-jobs-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.JOBS.PREFIX variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-jobs-prefix.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-jobs.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-jobs.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-jobs.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-jobs.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-jobs.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-jobs.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-jobs.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.JOBS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-jobs.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-level.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-level.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-level.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-level.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-level.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-level.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-level.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.LEVEL variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-level.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-makefile_preference.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEFILE_PREFERENCE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-makefile_preference.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-makefiles.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-makefiles.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEFILES variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-makefiles.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-bailiwick.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.BAILIWICK variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-bailiwick.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-created.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-created.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.CREATED variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-created.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-files.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-files.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.FILES variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-files.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_filter.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_FILTER variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_filter.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_paths.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_PATHS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_paths.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-ignore_patterns.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.IGNORE_PATTERNS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-ignore_patterns.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-meta-prefix.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.META.PREFIX variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-meta-prefix.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-mode.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-mode.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-mode.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-mode.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-mode.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-mode.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-mode.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MODE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-mode.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-path_filemon.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PATH_FILEMON variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-path_filemon.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-pid.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-pid.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-pid.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-pid.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-pid.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-pid.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-pid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PID variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-pid.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-ppid.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-ppid.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-ppid.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-ppid.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-ppid.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-ppid.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-ppid.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PPID variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-ppid.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.SAVE_DOLLARS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-makeoverrides.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-makeoverrides.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.MAKEOVERRIDES variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-makeoverrides.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-newline.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-newline.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-newline.exp (revision 365366) @@ -0,0 +1,4 @@ +make: "varname-dot-newline.mk" line 16: The .newline variable can be overwritten. Just don't do that. +first +second +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-newline.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-newline.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-newline.mk (revision 365366) @@ -0,0 +1,23 @@ +# $NetBSD: varname-dot-newline.mk,v 1.3 2020/08/19 05:51:18 rillig Exp $ +# +# Tests for the special .newline variable. +# +# Contrary to the special variable named "" that is used in expressions like +# ${:Usome-value}, the variable ".newline" is not protected against +# modification. Nobody exploits that though. + +NEWLINE:= ${.newline} + +.newline= overwritten + +.if ${.newline} == ${NEWLINE} +.info The .newline variable cannot be overwritten. Good. +.else +.info The .newline variable can be overwritten. Just don't do that. +.endif + +# Restore the original value. +.newline= ${NEWLINE} + +all: + @echo 'first${.newline}second' Property changes on: head/contrib/bmake/unit-tests/varname-dot-newline.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-objdir.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-objdir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-objdir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-objdir.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-objdir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-objdir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-objdir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .OBJDIR variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-objdir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-parsedir.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-parsedir.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-parsedir.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-parsedir.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-parsedir.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-parsedir.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-parsedir.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PARSEDIR variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-parsedir.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-parsefile.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-parsefile.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-parsefile.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-parsefile.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-parsefile.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-parsefile.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-parsefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PARSEFILE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-parsefile.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-path.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-path.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-path.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-path.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-path.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-path.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-path.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .PATH variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-path.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-shell.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-shell.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-shell.exp (revision 365366) @@ -0,0 +1,19 @@ +ParseReadLine (8): 'ORIG_SHELL:= ${.SHELL}' +Var_Parse: ${.SHELL} with VARE_WANTRES|VARE_ASSIGN +Global:delete .SHELL (not found) +Command:.SHELL = (details omitted) +ParseReadLine (10): '.SHELL= overwritten' +Global:.SHELL = overwritten +Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES +ParseReadLine (18): '.undef .SHELL' +Global:delete .SHELL +ParseReadLine (19): '.SHELL= newly overwritten' +Global:.SHELL = newly overwritten +Var_Parse: ${.SHELL} != ${ORIG_SHELL} with VARE_UNDEFERR|VARE_WANTRES +ParseReadLine (24): 'all:' +ParseReadLine (25): ' @echo ${.SHELL:M*}' +Var_Parse: ${.SHELL:M*} with VARE_WANTRES +Applying ${.SHELL:M...} to "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY) +Pattern[.SHELL] for [(details omitted)] is [*] +Result of ${.SHELL:M*} is "(details omitted)" (eflags = VARE_WANTRES, vflags = VAR_READONLY) +exit status 0 Property changes on: head/contrib/bmake/unit-tests/varname-dot-shell.exp ___________________________________________________________________ Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-shell.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-shell.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-shell.mk (revision 365366) @@ -0,0 +1,25 @@ +# $NetBSD: varname-dot-shell.mk,v 1.2 2020/08/23 09:28:52 rillig Exp $ +# +# Tests for the special .SHELL variable, which contains the shell used for +# running the commands. +# +# This variable is read-only. + +ORIG_SHELL:= ${.SHELL} + +.SHELL= overwritten +.if ${.SHELL} != ${ORIG_SHELL} +.error +.endif + +# Trying to delete the variable. +# This has no effect since the variable is not defined in the global context, +# but in the command-line context. +.undef .SHELL +.SHELL= newly overwritten +.if ${.SHELL} != ${ORIG_SHELL} +.error +.endif + +all: + @echo ${.SHELL:M*} Property changes on: head/contrib/bmake/unit-tests/varname-dot-shell.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-dot-targets.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-targets.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-targets.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-dot-targets.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-dot-targets.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-dot-targets.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-dot-targets.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .TARGETS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-dot-targets.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-empty.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-empty.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-empty.exp (revision 365366) @@ -0,0 +1,11 @@ +Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored +Var_Set("", "cmline-plain", ...) name expands to empty string - ignored +Var_Set("", "default", ...) name expands to empty string - ignored +Var_Set("", "assigned", ...) name expands to empty string - ignored +Var_Set("", "appended", ...) name expands to empty string - ignored +Var_Set("", "", ...) name expands to empty string - ignored +Var_Set("", "subst", ...) name expands to empty string - ignored +Var_Set("", "shell-output", ...) name expands to empty string - ignored +out: fallback +out: 1 2 3 +exit status 0 Index: head/contrib/bmake/unit-tests/varname-empty.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-empty.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-empty.mk (revision 365366) @@ -0,0 +1,26 @@ +# $NetBSD: varname-empty.mk,v 1.5 2020/08/22 21:22:24 rillig Exp $ +# +# Tests for the special variable with the empty name. +# +# The variable "" is not supposed to be assigned any value. +# This is because it is heavily used in the .for loop expansion, +# as well as to generate arbitrary strings, as in ${:Ufallback}. + +# Until 2020-08-22 it was possible to assign a value to the variable with +# the empty name, leading to all kinds of unexpected effects. +?= default += assigned # undefined behavior until 2020-08-22 ++= appended +:= subst +!= echo 'shell-output' + +# The .for loop expands the expression ${i} to ${:U1}, ${:U2} and so on. +# This only works if the variable with the empty name is guaranteed to +# be undefined. +.for i in 1 2 3 +NUMBERS+= ${i} +.endfor + +all: + @echo out: ${:Ufallback} + @echo out: ${NUMBERS} Property changes on: head/contrib/bmake/unit-tests/varname-empty.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-make.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-make.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-make.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-make.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-make.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-make.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special MAKE variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-make.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-make_print_var_on_error.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-make_print_var_on_error.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special .MAKE.PRINT_VAR_ON_ERROR variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-make_print_var_on_error.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-makeflags.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-makeflags.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-makeflags.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-makeflags.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-makeflags.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-makeflags.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-makeflags.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special MAKEFLAGS variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-makeflags.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-pwd.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-pwd.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-pwd.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-pwd.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-pwd.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-pwd.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-pwd.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special PWD variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-pwd.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname-vpath.exp =================================================================== --- head/contrib/bmake/unit-tests/varname-vpath.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname-vpath.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname-vpath.mk =================================================================== --- head/contrib/bmake/unit-tests/varname-vpath.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname-vpath.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname-vpath.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for the special VPATH variable. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname-vpath.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varname.exp =================================================================== --- head/contrib/bmake/unit-tests/varname.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varname.exp (revision 365366) @@ -0,0 +1 @@ +exit status 0 Index: head/contrib/bmake/unit-tests/varname.mk =================================================================== --- head/contrib/bmake/unit-tests/varname.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varname.mk (revision 365366) @@ -0,0 +1,8 @@ +# $NetBSD: varname.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ +# +# Tests for special variables, such as .MAKE or .PARSEDIR. + +# TODO: Implementation + +all: + @:; Property changes on: head/contrib/bmake/unit-tests/varname.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/unit-tests/varparse-dynamic.exp =================================================================== --- head/contrib/bmake/unit-tests/varparse-dynamic.exp (nonexistent) +++ head/contrib/bmake/unit-tests/varparse-dynamic.exp (revision 365366) @@ -0,0 +1,5 @@ +make: "varparse-dynamic.mk" line 8: Malformed conditional (${.TARGEX}) +make: "varparse-dynamic.mk" line 10: Malformed conditional (${.TARGXX}) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: head/contrib/bmake/unit-tests/varparse-dynamic.mk =================================================================== --- head/contrib/bmake/unit-tests/varparse-dynamic.mk (nonexistent) +++ head/contrib/bmake/unit-tests/varparse-dynamic.mk (revision 365366) @@ -0,0 +1,14 @@ +# $NetBSD: varparse-dynamic.mk,v 1.1 2020/07/26 22:15:36 rillig Exp $ + +# Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped +# the last character in the variable name. +# To trigger the bug, the variable must not be defined. +.if ${.TARGET} # exact match, may be undefined +.endif +.if ${.TARGEX} # 1 character difference, must be defined +.endif +.if ${.TARGXX} # 2 characters difference, must be defined +.endif + +all: + @: Property changes on: head/contrib/bmake/unit-tests/varparse-dynamic.mk ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/contrib/bmake/util.c =================================================================== --- head/contrib/bmake/util.c (revision 365365) +++ head/contrib/bmake/util.c (revision 365366) @@ -1,596 +1,596 @@ -/* $NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $ */ +/* $NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $ */ /* * Missing stuff from OS's * - * $Id: util.c,v 1.35 2020/07/04 18:16:55 sjg Exp $ + * $Id: util.c,v 1.36 2020/08/01 23:08:14 sjg Exp $ */ #if defined(__MINT__) || defined(__linux__) #include #endif #include "make.h" #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $"; +static char rcsid[] = "$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $"; #else #ifndef lint -__RCSID("$NetBSD: util.c,v 1.57 2020/07/03 08:13:23 rillig Exp $"); +__RCSID("$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $"); #endif #endif #include #include #include #if !defined(HAVE_STRERROR) extern int errno, sys_nerr; extern char *sys_errlist[]; char * strerror(int e) { static char buf[100]; if (e < 0 || e >= sys_nerr) { snprintf(buf, sizeof(buf), "Unknown error %d", e); return buf; } else return sys_errlist[e]; } #endif #if !defined(HAVE_GETENV) || !defined(HAVE_SETENV) || !defined(HAVE_UNSETENV) extern char **environ; static char * findenv(const char *name, int *offset) { size_t i, len; char *p, *q; len = strlen(name); for (i = 0; (q = environ[i]); i++) { p = strchr(q, '='); if (p == NULL || p - q != len) continue; if (strncmp(name, q, len) == 0) { *offset = i; return q + len + 1; } } *offset = i; return NULL; } char * getenv(const char *name) { int offset; return findenv(name, &offset); } int unsetenv(const char *name) { char **p; int offset; if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { errno = EINVAL; return -1; } while (findenv(name, &offset)) { /* if set multiple times */ for (p = &environ[offset];; ++p) if (!(*p = *(p + 1))) break; } return 0; } int setenv(const char *name, const char *value, int rewrite) { char *c, **newenv; const char *cc; size_t l_value, size; int offset; if (name == NULL || value == NULL) { errno = EINVAL; return -1; } if (*value == '=') /* no `=' in value */ ++value; l_value = strlen(value); /* find if already exists */ if ((c = findenv(name, &offset))) { if (!rewrite) return 0; if (strlen(c) >= l_value) /* old larger; copy over */ goto copy; } else { /* create new slot */ size = sizeof(char *) * (offset + 2); if (savedEnv == environ) { /* just increase size */ if ((newenv = realloc(savedEnv, size)) == NULL) return -1; savedEnv = newenv; } else { /* get new space */ /* * We don't free here because we don't know if * the first allocation is valid on all OS's */ if ((savedEnv = malloc(size)) == NULL) return -1; (void)memcpy(savedEnv, environ, size - sizeof(char *)); } environ = savedEnv; environ[offset + 1] = NULL; } for (cc = name; *cc && *cc != '='; ++cc) /* no `=' in name */ continue; size = cc - name; /* name + `=' + value */ if ((environ[offset] = malloc(size + l_value + 2)) == NULL) return -1; c = environ[offset]; (void)memcpy(c, name, size); c += size; *c++ = '='; copy: (void)memcpy(c, value, l_value + 1); return 0; } #ifdef TEST int main(int argc, char *argv[]) { setenv(argv[1], argv[2], 0); printf("%s\n", getenv(argv[1])); unsetenv(argv[1]); printf("%s\n", getenv(argv[1])); return 0; } #endif #endif #if defined(__hpux__) || defined(__hpux) /* strrcpy(): * Like strcpy, going backwards and returning the new pointer */ static char * strrcpy(char *ptr, char *str) { int len = strlen(str); while (len) *--ptr = str[--len]; return ptr; } /* end strrcpy */ char *sys_siglist[] = { - "Signal 0", - "Hangup", /* SIGHUP */ - "Interrupt", /* SIGINT */ - "Quit", /* SIGQUIT */ - "Illegal instruction", /* SIGILL */ - "Trace/BPT trap", /* SIGTRAP */ - "IOT trap", /* SIGIOT */ - "EMT trap", /* SIGEMT */ - "Floating point exception", /* SIGFPE */ - "Killed", /* SIGKILL */ - "Bus error", /* SIGBUS */ - "Segmentation fault", /* SIGSEGV */ - "Bad system call", /* SIGSYS */ - "Broken pipe", /* SIGPIPE */ - "Alarm clock", /* SIGALRM */ - "Terminated", /* SIGTERM */ - "User defined signal 1", /* SIGUSR1 */ - "User defined signal 2", /* SIGUSR2 */ - "Child exited", /* SIGCLD */ - "Power-fail restart", /* SIGPWR */ - "Virtual timer expired", /* SIGVTALRM */ - "Profiling timer expired", /* SIGPROF */ - "I/O possible", /* SIGIO */ - "Window size changes", /* SIGWINDOW */ - "Stopped (signal)", /* SIGSTOP */ - "Stopped", /* SIGTSTP */ - "Continued", /* SIGCONT */ - "Stopped (tty input)", /* SIGTTIN */ - "Stopped (tty output)", /* SIGTTOU */ - "Urgent I/O condition", /* SIGURG */ - "Remote lock lost (NFS)", /* SIGLOST */ - "Signal 31", /* reserved */ - "DIL signal" /* SIGDIL */ + "Signal 0", + "Hangup", /* SIGHUP */ + "Interrupt", /* SIGINT */ + "Quit", /* SIGQUIT */ + "Illegal instruction", /* SIGILL */ + "Trace/BPT trap", /* SIGTRAP */ + "IOT trap", /* SIGIOT */ + "EMT trap", /* SIGEMT */ + "Floating point exception", /* SIGFPE */ + "Killed", /* SIGKILL */ + "Bus error", /* SIGBUS */ + "Segmentation fault", /* SIGSEGV */ + "Bad system call", /* SIGSYS */ + "Broken pipe", /* SIGPIPE */ + "Alarm clock", /* SIGALRM */ + "Terminated", /* SIGTERM */ + "User defined signal 1", /* SIGUSR1 */ + "User defined signal 2", /* SIGUSR2 */ + "Child exited", /* SIGCLD */ + "Power-fail restart", /* SIGPWR */ + "Virtual timer expired", /* SIGVTALRM */ + "Profiling timer expired", /* SIGPROF */ + "I/O possible", /* SIGIO */ + "Window size changes", /* SIGWINDOW */ + "Stopped (signal)", /* SIGSTOP */ + "Stopped", /* SIGTSTP */ + "Continued", /* SIGCONT */ + "Stopped (tty input)", /* SIGTTIN */ + "Stopped (tty output)", /* SIGTTOU */ + "Urgent I/O condition", /* SIGURG */ + "Remote lock lost (NFS)", /* SIGLOST */ + "Signal 31", /* reserved */ + "DIL signal" /* SIGDIL */ }; #endif /* __hpux__ || __hpux */ #if defined(__hpux__) || defined(__hpux) #include #include #include #include #include #include #include int killpg(int pid, int sig) { return kill(-pid, sig); } #if !defined(BSD) && !defined(d_fileno) # define d_fileno d_ino #endif #ifndef DEV_DEV_COMPARE # define DEV_DEV_COMPARE(a, b) ((a) == (b)) #endif #define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/'))) #define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1]))) char * getwd(char *pathname) { DIR *dp; struct dirent *d; extern int errno; struct stat st_root, st_cur, st_next, st_dotdot; char pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2]; char *pathptr, *nextpathptr, *cur_name_add; /* find the inode of root */ if (stat("/", &st_root) == -1) { (void)sprintf(pathname, "getwd: Cannot stat \"/\" (%s)", strerror(errno)); return NULL; } pathbuf[MAXPATHLEN - 1] = '\0'; pathptr = &pathbuf[MAXPATHLEN - 1]; nextpathbuf[MAXPATHLEN - 1] = '\0'; cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1]; /* find the inode of the current directory */ if (lstat(".", &st_cur) == -1) { (void)sprintf(pathname, "getwd: Cannot stat \".\" (%s)", strerror(errno)); return NULL; } nextpathptr = strrcpy(nextpathptr, "../"); /* Descend to root */ for (;;) { /* look if we found root yet */ if (st_cur.st_ino == st_root.st_ino && DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) { (void)strcpy(pathname, *pathptr != '/' ? "/" : pathptr); return pathname; } /* open the parent directory */ if (stat(nextpathptr, &st_dotdot) == -1) { (void)sprintf(pathname, "getwd: Cannot stat directory \"%s\" (%s)", nextpathptr, strerror(errno)); return NULL; } if ((dp = opendir(nextpathptr)) == NULL) { (void)sprintf(pathname, "getwd: Cannot open directory \"%s\" (%s)", nextpathptr, strerror(errno)); return NULL; } /* look in the parent for the entry with the same inode */ if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) { /* Parent has same device. No need to stat every member */ for (d = readdir(dp); d != NULL; d = readdir(dp)) if (d->d_fileno == st_cur.st_ino) break; } else { /* * Parent has a different device. This is a mount point so we * need to stat every member */ for (d = readdir(dp); d != NULL; d = readdir(dp)) { if (ISDOT(d->d_name) || ISDOTDOT(d->d_name)) continue; (void)strcpy(cur_name_add, d->d_name); if (lstat(nextpathptr, &st_next) == -1) { (void)sprintf(pathname, "getwd: Cannot stat \"%s\" (%s)", d->d_name, strerror(errno)); (void)closedir(dp); return NULL; } /* check if we found it yet */ if (st_next.st_ino == st_cur.st_ino && DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev)) break; } } if (d == NULL) { (void)sprintf(pathname, "getwd: Cannot find \".\" in \"..\""); (void)closedir(dp); return NULL; } st_cur = st_dotdot; pathptr = strrcpy(pathptr, d->d_name); pathptr = strrcpy(pathptr, "/"); nextpathptr = strrcpy(nextpathptr, "../"); (void)closedir(dp); *cur_name_add = '\0'; } } /* end getwd */ #endif /* __hpux */ #if !defined(HAVE_GETCWD) char * getcwd(path, sz) char *path; int sz; { return getwd(path); } #endif /* force posix signals */ void (* bmake_signal(int s, void (*a)(int)))(int) { struct sigaction sa, osa; sa.sa_handler = a; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(s, &sa, &osa) == -1) return SIG_ERR; else return osa.sa_handler; } #if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF) #include #endif #if !defined(HAVE_VSNPRINTF) #if !defined(__osf__) #ifdef _IOSTRG #define STRFLAG (_IOSTRG|_IOWRT) /* no _IOWRT: avoid stdio bug */ #else #if 0 #define STRFLAG (_IOREAD) /* XXX: Assume svr4 stdio */ #endif #endif /* _IOSTRG */ #endif /* __osf__ */ int vsnprintf(char *s, size_t n, const char *fmt, va_list args) { #ifdef STRFLAG FILE fakebuf; fakebuf._flag = STRFLAG; /* * Some os's are char * _ptr, others are unsigned char *_ptr... * We cast to void * to make everyone happy. */ fakebuf._ptr = (void *)s; fakebuf._cnt = n-1; fakebuf._file = -1; _doprnt(fmt, args, &fakebuf); fakebuf._cnt++; putc('\0', &fakebuf); if (fakebuf._cnt<0) fakebuf._cnt = 0; return n-fakebuf._cnt-1; #else #ifndef _PATH_DEVNULL # define _PATH_DEVNULL "/dev/null" #endif /* * Rats... we don't want to clobber anything... * do a printf to /dev/null to see how much space we need. */ static FILE *nullfp; int need = 0; /* XXX what's a useful error return? */ if (!nullfp) nullfp = fopen(_PATH_DEVNULL, "w"); if (nullfp) { need = vfprintf(nullfp, fmt, args); if (need < n) (void)vsprintf(s, fmt, args); } return need; #endif } #endif #if !defined(HAVE_SNPRINTF) int snprintf(char *s, size_t n, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = vsnprintf(s, n, fmt, ap); va_end(ap); return rv; } #endif #if !defined(HAVE_STRFTIME) size_t strftime(char *buf, size_t len, const char *fmt, const struct tm *tm) { static char months[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; size_t s; char *b = buf; while (*fmt) { if (len == 0) return buf - b; if (*fmt != '%') { *buf++ = *fmt++; len--; continue; } switch (*fmt++) { case '%': *buf++ = '%'; len--; if (len == 0) return buf - b; /*FALLTHROUGH*/ case '\0': *buf = '%'; s = 1; break; case 'k': s = snprintf(buf, len, "%d", tm->tm_hour); break; case 'M': s = snprintf(buf, len, "%02d", tm->tm_min); break; case 'S': s = snprintf(buf, len, "%02d", tm->tm_sec); break; case 'b': if (tm->tm_mon >= 12) return buf - b; s = snprintf(buf, len, "%s", months[tm->tm_mon]); break; case 'd': s = snprintf(buf, len, "%02d", tm->tm_mday); break; case 'Y': s = snprintf(buf, len, "%d", 1900 + tm->tm_year); break; default: s = snprintf(buf, len, "Unsupported format %c", fmt[-1]); break; } buf += s; len -= s; } } #endif #if !defined(HAVE_KILLPG) #if !defined(__hpux__) && !defined(__hpux) int killpg(int pid, int sig) { return kill(-pid, sig); } #endif #endif #if !defined(HAVE_WARNX) static void vwarnx(const char *fmt, va_list args) { fprintf(stderr, "%s: ", progname); if ((fmt)) { vfprintf(stderr, fmt, args); fprintf(stderr, ": "); } } #endif #if !defined(HAVE_WARN) static void vwarn(const char *fmt, va_list args) { vwarnx(fmt, args); fprintf(stderr, "%s\n", strerror(errno)); } #endif #if !defined(HAVE_ERR) static void verr(int eval, const char *fmt, va_list args) { vwarn(fmt, args); exit(eval); } #endif #if !defined(HAVE_ERRX) static void verrx(int eval, const char *fmt, va_list args) { vwarnx(fmt, args); exit(eval); } #endif #if !defined(HAVE_ERR) void err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(eval, fmt, ap); va_end(ap); } #endif #if !defined(HAVE_ERRX) void errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(eval, fmt, ap); va_end(ap); } #endif #if !defined(HAVE_WARN) void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } #endif #if !defined(HAVE_WARNX) void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } #endif Index: head/contrib/bmake/var.c =================================================================== --- head/contrib/bmake/var.c (revision 365365) +++ head/contrib/bmake/var.c (revision 365366) @@ -1,4130 +1,3771 @@ -/* $NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $ */ +/* $NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 MAKE_NATIVE -static char rcsid[] = "$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"; +static char rcsid[] = "$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $"; #else #include #ifndef lint #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: var.c,v 1.255 2020/07/04 17:41:04 rillig Exp $"); +__RCSID("$NetBSD: var.c,v 1.484 2020/09/02 06:25:48 rillig Exp $"); #endif #endif /* not lint */ #endif /*- * var.c -- * Variable-handling functions * * Interface: * Var_Set Set the value of a variable in the given * context. The variable is created if it doesn't - * yet exist. The value and variable name need not - * be preserved. + * yet exist. * * Var_Append Append more characters to an existing variable * in the given context. The variable needn't * exist already -- it will be created if it doesn't. * A space is placed between the old value and the * new one. * * Var_Exists See if a variable exists. * - * Var_Value Return the value of a variable in a context or - * NULL if the variable is undefined. + * Var_Value Return the unexpanded value of a variable in a + * context or NULL if the variable is undefined. * * Var_Subst Substitute either a single variable or all - * variables in a string, using the given context as - * the top-most one. + * variables in a string, using the given context. * * Var_Parse Parse a variable expansion from a string and * return the result and the number of characters * consumed. * * Var_Delete Delete a variable in a context. * * Var_Init Initialize this module. * * Debugging: * Var_Dump Print out all variables defined in the given * context. * * XXX: There's a lot of duplication in these functions. */ #include #include #ifndef NO_REGEX #include #endif -#include -#include -#include #include #include "make.h" -#ifdef HAVE_STDINT_H -#include +#ifdef HAVE_INTTYPES_H +#include +#elif defined(HAVE_STDINT_H) +#include #endif -#include "buf.h" +#include "enum.h" #include "dir.h" #include "job.h" #include "metachar.h" -extern int makelevel; +#define VAR_DEBUG_IF(cond, fmt, ...) \ + if (!(DEBUG(VAR) && (cond))) \ + (void) 0; \ + else \ + fprintf(debug_file, fmt, __VA_ARGS__) + +#define VAR_DEBUG(fmt, ...) VAR_DEBUG_IF(TRUE, fmt, __VA_ARGS__) + +ENUM_FLAGS_RTTI_3(VarEvalFlags, + VARE_UNDEFERR, VARE_WANTRES, VARE_ASSIGN); + /* * This lets us tell if we have replaced the original environ * (which we cannot free). */ char **savedEnv = NULL; /* * This is a harmless return value for Var_Parse that can be used by Var_Subst * to determine if there was an error in parsing -- easier than returning * a flag, as things outside this module don't give a hoot. */ char var_Error[] = ""; /* - * Similar to var_Error, but returned when the 'VARF_UNDEFERR' flag for - * Var_Parse is not set. Why not just use a constant? Well, gcc likes - * to condense identical string instances... + * Similar to var_Error, but returned when the 'VARE_UNDEFERR' flag for + * Var_Parse is not set. + * + * Why not just use a constant? Well, GCC likes to condense identical string + * instances... */ static char varNoError[] = ""; /* * Traditionally we consume $$ during := like any other expansion. * Other make's do not. * This knob allows controlling the behavior. - * FALSE for old behavior. - * TRUE for new compatible. + * FALSE to consume $$ during := assignment. + * TRUE to preserve $$ during := assignment. */ #define SAVE_DOLLARS ".MAKE.SAVE_DOLLARS" static Boolean save_dollars = FALSE; /* * Internally, variables are contained in four different contexts. - * 1) the environment. They may not be changed. If an environment - * variable is appended-to, the result is placed in the global + * 1) the environment. They cannot be changed. If an environment + * variable is appended to, the result is placed in the global * context. * 2) the global context. Variables set in the Makefile are located in - * the global context. It is the penultimate context searched when - * substituting. + * the global context. * 3) the command-line context. All variables set on the command line * are placed in this context. They are UNALTERABLE once placed here. * 4) the local context. Each target has associated with it a context * list. On this list are located the structures describing such * local variables as $(@) and $(*) * The four contexts are searched in the reverse order from which they are - * listed. + * listed (but see checkEnvFirst). */ GNode *VAR_INTERNAL; /* variables from make itself */ GNode *VAR_GLOBAL; /* variables from the makefile */ GNode *VAR_CMD; /* variables defined on the command-line */ -#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ -#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ -#define FIND_ENV 0x4 /* look in the environment also */ +typedef enum { + FIND_CMD = 0x01, /* look in VAR_CMD when searching */ + FIND_GLOBAL = 0x02, /* look in VAR_GLOBAL as well */ + FIND_ENV = 0x04 /* look in the environment also */ +} VarFindFlags; typedef enum { - VAR_IN_USE = 0x01, /* Variable's value is currently being used. - * Used to avoid endless recursion */ - VAR_FROM_ENV = 0x02, /* Variable comes from the environment */ - VAR_JUNK = 0x04, /* Variable is a junk variable that - * should be destroyed when done with - * it. Used by Var_Parse for undefined, - * modified variables */ - VAR_KEEP = 0x08, /* Variable is VAR_JUNK, but we found - * a use for it in some modifier and - * the value is therefore valid */ - VAR_EXPORTED = 0x10, /* Variable is exported */ - VAR_REEXPORT = 0x20, /* Indicate if var needs re-export. - * This would be true if it contains $'s */ - VAR_FROM_CMD = 0x40 /* Variable came from command line */ -} Var_Flags; + /* The variable's value is currently being used by Var_Parse or Var_Subst. + * This marker is used to avoid endless recursion. */ + VAR_IN_USE = 0x01, + /* The variable comes from the environment. + * These variables are not registered in any GNode, therefore they must + * be freed as soon as they are not used anymore. */ + VAR_FROM_ENV = 0x02, + /* The variable is a junk variable that should be destroyed when done with + * it. Used by Var_Parse for undefined, modified variables. */ + VAR_JUNK = 0x04, + /* Variable is VAR_JUNK, but we found a use for it in some modifier and + * the value is therefore valid. */ + VAR_KEEP = 0x08, + /* The variable is exported to the environment, to be used by child + * processes. */ + VAR_EXPORTED = 0x10, + /* At the point where this variable was exported, it contained an + * unresolved reference to another variable. Before any child process is + * started, it needs to be exported again, in the hope that the referenced + * variable can then be resolved. */ + VAR_REEXPORT = 0x20, + /* The variable came from command line. */ + VAR_FROM_CMD = 0x40, + VAR_READONLY = 0x80 +} VarFlags; +ENUM_FLAGS_RTTI_8(VarFlags, + VAR_IN_USE, VAR_FROM_ENV, VAR_JUNK, VAR_KEEP, + VAR_EXPORTED, VAR_REEXPORT, VAR_FROM_CMD, VAR_READONLY); + typedef struct Var { - char *name; /* the variable's name */ + char *name; /* the variable's name; it is allocated for + * environment variables and aliased to the + * Hash_Entry name for all other variables, + * and thus must not be modified */ Buffer val; /* its value */ - Var_Flags flags; /* miscellaneous status flags */ -} Var; + VarFlags flags; /* miscellaneous status flags */ +} Var; /* * Exporting vars is expensive so skip it if we can */ -#define VAR_EXPORTED_NONE 0 -#define VAR_EXPORTED_YES 1 -#define VAR_EXPORTED_ALL 2 -static int var_exportedVars = VAR_EXPORTED_NONE; -/* - * We pass this to Var_Export when doing the initial export - * or after updating an exported var. - */ -#define VAR_EXPORT_PARENT 1 -/* - * We pass this to Var_Export1 to tell it to leave the value alone. - */ -#define VAR_EXPORT_LITERAL 2 - typedef enum { - VAR_SUB_GLOBAL = 0x01, /* Apply substitution globally */ - VAR_SUB_ONE = 0x02, /* Apply substitution to one word */ - VAR_SUB_MATCHED = 0x04, /* There was a match */ - VAR_MATCH_START = 0x08, /* Match at start of word */ - VAR_MATCH_END = 0x10, /* Match at end of word */ - VAR_NOSUBST = 0x20 /* don't expand vars in VarGetPattern */ -} VarPattern_Flags; + VAR_EXPORTED_NONE, + VAR_EXPORTED_YES, + VAR_EXPORTED_ALL +} VarExportedMode; -typedef enum { - VAR_NO_EXPORT = 0x01 /* do not export */ -} VarSet_Flags; +static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE; -typedef struct { +typedef enum { /* - * The following fields are set by Var_Parse() when it - * encounters modifiers that need to keep state for use by - * subsequent modifiers within the same variable expansion. + * We pass this to Var_Export when doing the initial export + * or after updating an exported var. */ - Byte varSpace; /* Word separator in expansions */ - Boolean oneBigWord; /* TRUE if we will treat the variable as a - * single big word, even if it contains - * embedded spaces (as opposed to the - * usual behaviour of treating it as - * several space-separated words). */ -} Var_Parse_State; + VAR_EXPORT_PARENT = 0x01, + /* + * We pass this to Var_Export1 to tell it to leave the value alone. + */ + VAR_EXPORT_LITERAL = 0x02 +} VarExportFlags; -/* struct passed as 'void *' to VarSubstitute() for ":S/lhs/rhs/", - * to VarSYSVMatch() for ":lhs=rhs". */ -typedef struct { - const char *lhs; /* String to match */ - int leftLen; /* Length of string */ - const char *rhs; /* Replacement string (w/ &'s removed) */ - int rightLen; /* Length of replacement */ - VarPattern_Flags flags; -} VarPattern; +/* Flags for pattern matching in the :S and :C modifiers */ +typedef enum { + VARP_SUB_GLOBAL = 0x01, /* Apply substitution globally */ + VARP_SUB_ONE = 0x02, /* Apply substitution to one word */ + VARP_ANCHOR_START = 0x04, /* Match at start of word */ + VARP_ANCHOR_END = 0x08 /* Match at end of word */ +} VarPatternFlags; -/* struct passed as 'void *' to VarLoopExpand() for ":@tvar@str@" */ -typedef struct { - GNode *ctxt; /* variable context */ - char *tvar; /* name of temp var */ - int tvarLen; - char *str; /* string to expand */ - int strLen; - Varf_Flags flags; -} VarLoop; - -#ifndef NO_REGEX -/* struct passed as 'void *' to VarRESubstitute() for ":C///" */ -typedef struct { - regex_t re; - int nsub; - regmatch_t *matches; - char *replace; - int flags; -} VarREPattern; -#endif - -/* struct passed to VarSelectWords() for ":[start..end]" */ -typedef struct { - int start; /* first word to select */ - int end; /* last word to select */ -} VarSelectWords_t; - #define BROPEN '{' #define BRCLOSE '}' #define PROPEN '(' #define PRCLOSE ')' /*- *----------------------------------------------------------------------- * VarFind -- * Find the given variable in the given context and any other contexts * indicated. * * Input: * name name to find * ctxt context in which to find it - * flags FIND_GLOBAL set means to look in the - * VAR_GLOBAL context as well. FIND_CMD set means - * to look in the VAR_CMD context also. FIND_ENV - * set means to look in the environment + * flags FIND_GLOBAL look in VAR_GLOBAL as well + * FIND_CMD look in VAR_CMD as well + * FIND_ENV look in the environment as well * * Results: * A pointer to the structure describing the desired variable or * NULL if the variable does not exist. - * - * Side Effects: - * None *----------------------------------------------------------------------- */ static Var * -VarFind(const char *name, GNode *ctxt, int flags) +VarFind(const char *name, GNode *ctxt, VarFindFlags flags) { - Hash_Entry *var; - Var *v; + Hash_Entry *var; /* * If the variable name begins with a '.', it could very well be one of * the local ones. We check the name against all the local variables * and substitute the short version in for 'name' if it matches one of * them. */ - if (*name == '.' && isupper((unsigned char) name[1])) { + if (*name == '.' && isupper((unsigned char)name[1])) { switch (name[1]) { case 'A': - if (!strcmp(name, ".ALLSRC")) + if (strcmp(name, ".ALLSRC") == 0) name = ALLSRC; - if (!strcmp(name, ".ARCHIVE")) + if (strcmp(name, ".ARCHIVE") == 0) name = ARCHIVE; break; case 'I': - if (!strcmp(name, ".IMPSRC")) + if (strcmp(name, ".IMPSRC") == 0) name = IMPSRC; break; case 'M': - if (!strcmp(name, ".MEMBER")) + if (strcmp(name, ".MEMBER") == 0) name = MEMBER; break; case 'O': - if (!strcmp(name, ".OODATE")) + if (strcmp(name, ".OODATE") == 0) name = OODATE; break; case 'P': - if (!strcmp(name, ".PREFIX")) + if (strcmp(name, ".PREFIX") == 0) name = PREFIX; break; + case 'S': + if (strcmp(name, ".SHELL") == 0 ) { + if (!shellPath) + Shell_Init(); + } + break; case 'T': - if (!strcmp(name, ".TARGET")) + if (strcmp(name, ".TARGET") == 0) name = TARGET; break; } } #ifdef notyet /* for compatibility with gmake */ if (name[0] == '^' && name[1] == '\0') name = ALLSRC; #endif /* * First look for the variable in the given context. If it's not there, * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, * depending on the FIND_* flags in 'flags' */ var = Hash_FindEntry(&ctxt->context, name); - if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) { + if (var == NULL && (flags & FIND_CMD) && ctxt != VAR_CMD) var = Hash_FindEntry(&VAR_CMD->context, name); - } + if (!checkEnvFirst && var == NULL && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) { var = Hash_FindEntry(&VAR_GLOBAL->context, name); if (var == NULL && ctxt != VAR_INTERNAL) { /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ var = Hash_FindEntry(&VAR_INTERNAL->context, name); } } + if (var == NULL && (flags & FIND_ENV)) { char *env; if ((env = getenv(name)) != NULL) { - int len; - - v = bmake_malloc(sizeof(Var)); + Var *v = bmake_malloc(sizeof(Var)); + size_t len; v->name = bmake_strdup(name); len = strlen(env); - Buf_Init(&v->val, len + 1); - Buf_AddBytes(&v->val, len, env); + Buf_AddBytes(&v->val, env, len); v->flags = VAR_FROM_ENV; return v; - } else if (checkEnvFirst && (flags & FIND_GLOBAL) && - ctxt != VAR_GLOBAL) - { + } + + if (checkEnvFirst && (flags & FIND_GLOBAL) && ctxt != VAR_GLOBAL) { var = Hash_FindEntry(&VAR_GLOBAL->context, name); - if (var == NULL && ctxt != VAR_INTERNAL) { + if (var == NULL && ctxt != VAR_INTERNAL) var = Hash_FindEntry(&VAR_INTERNAL->context, name); - } - if (var == NULL) { + if (var == NULL) return NULL; - } else { + else return (Var *)Hash_GetValue(var); - } - } else { - return NULL; } - } else if (var == NULL) { + return NULL; - } else { - return (Var *)Hash_GetValue(var); } + + if (var == NULL) + return NULL; + else + return (Var *)Hash_GetValue(var); } /*- *----------------------------------------------------------------------- * VarFreeEnv -- * If the variable is an environment variable, free it * * Input: * v the variable * destroy true if the value buffer should be destroyed. * * Results: - * 1 if it is an environment variable 0 ow. - * - * Side Effects: - * The variable is free'ed if it is an environent variable. + * TRUE if it is an environment variable, FALSE otherwise. *----------------------------------------------------------------------- */ static Boolean VarFreeEnv(Var *v, Boolean destroy) { - if ((v->flags & VAR_FROM_ENV) == 0) + if (!(v->flags & VAR_FROM_ENV)) return FALSE; free(v->name); Buf_Destroy(&v->val, destroy); free(v); return TRUE; } -/*- - *----------------------------------------------------------------------- - * VarAdd -- - * Add a new variable of name name and value val to the given context - * - * Input: - * name name of variable to add - * val value to set it to - * ctxt context in which to set it - * - * Side Effects: - * The new variable is placed at the front of the given context - * The name and val arguments are duplicated so they may - * safely be freed. - *----------------------------------------------------------------------- - */ +/* Add a new variable of the given name and value to the given context. + * The name and val arguments are duplicated so they may safely be freed. */ static void -VarAdd(const char *name, const char *val, GNode *ctxt) +VarAdd(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) { - Var *v; - int len; - Hash_Entry *h; + Var *v = bmake_malloc(sizeof(Var)); + size_t len = strlen(val); + Hash_Entry *he; - v = bmake_malloc(sizeof(Var)); - - len = val ? strlen(val) : 0; Buf_Init(&v->val, len + 1); - Buf_AddBytes(&v->val, len, val); + Buf_AddBytes(&v->val, val, len); v->flags = 0; + if (flags & VAR_SET_READONLY) + v->flags |= VAR_READONLY; - h = Hash_CreateEntry(&ctxt->context, name, NULL); - Hash_SetValue(h, v); - v->name = h->name; - if (DEBUG(VAR) && (ctxt->flags & INTERNAL) == 0) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); - } + he = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(he, v); + v->name = he->name; + VAR_DEBUG_IF(!(ctxt->flags & INTERNAL), + "%s:%s = %s\n", ctxt->name, name, val); } -/*- - *----------------------------------------------------------------------- - * Var_Delete -- - * Remove a variable from a context. - * - * Side Effects: - * The Var structure is removed and freed. - * - *----------------------------------------------------------------------- - */ +/* Remove a variable from a context, freeing the Var structure as well. */ void Var_Delete(const char *name, GNode *ctxt) { - Hash_Entry *ln; - char *cp; + char *name_freeIt = NULL; + Hash_Entry *he; - if (strchr(name, '$')) { - cp = Var_Subst(NULL, name, VAR_GLOBAL, VARF_WANTRES); - } else { - cp = (char *)name; - } - ln = Hash_FindEntry(&ctxt->context, cp); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:delete %s%s\n", - ctxt->name, cp, ln ? "" : " (not found)"); - } - if (cp != name) { - free(cp); - } - if (ln != NULL) { - Var *v; + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, VAR_GLOBAL, VARE_WANTRES); + he = Hash_FindEntry(&ctxt->context, name); + VAR_DEBUG("%s:delete %s%s\n", + ctxt->name, name, he != NULL ? "" : " (not found)"); + free(name_freeIt); - v = (Var *)Hash_GetValue(ln); - if ((v->flags & VAR_EXPORTED)) { + if (he != NULL) { + Var *v = (Var *)Hash_GetValue(he); + if (v->flags & VAR_EXPORTED) unsetenv(v->name); - } - if (strcmp(MAKE_EXPORTED, v->name) == 0) { + if (strcmp(v->name, MAKE_EXPORTED) == 0) var_exportedVars = VAR_EXPORTED_NONE; - } - if (v->name != ln->name) + if (v->name != he->name) free(v->name); - Hash_DeleteEntry(&ctxt->context, ln); + Hash_DeleteEntry(&ctxt->context, he); Buf_Destroy(&v->val, TRUE); free(v); } } /* - * Export a var. - * We ignore make internal variables (those which start with '.') + * Export a single variable. + * We ignore make internal variables (those which start with '.'). * Also we jump through some hoops to avoid calling setenv * more than necessary since it can leak. * We only manipulate flags of vars if 'parent' is set. */ -static int -Var_Export1(const char *name, int flags) +static Boolean +Var_Export1(const char *name, VarExportFlags flags) { - char tmp[BUFSIZ]; + VarExportFlags parent = flags & VAR_EXPORT_PARENT; Var *v; - char *val = NULL; - int n; - int parent = (flags & VAR_EXPORT_PARENT); + char *val; - if (*name == '.') - return 0; /* skip internals */ - if (!name[1]) { + if (name[0] == '.') + return FALSE; /* skip internals */ + if (name[1] == '\0') { /* * A single char. * If it is one of the vars that should only appear in * local context, skip it, else we can get Var_Subst * into a loop. */ switch (name[0]) { case '@': case '%': case '*': case '!': - return 0; + return FALSE; } } + v = VarFind(name, VAR_GLOBAL, 0); - if (v == NULL) { - return 0; - } - if (!parent && - (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { - return 0; /* nothing to do */ - } + if (v == NULL) + return FALSE; + + if (!parent && (v->flags & VAR_EXPORTED) && !(v->flags & VAR_REEXPORT)) + return FALSE; /* nothing to do */ + val = Buf_GetAll(&v->val, NULL); - if ((flags & VAR_EXPORT_LITERAL) == 0 && strchr(val, '$')) { + if (!(flags & VAR_EXPORT_LITERAL) && strchr(val, '$') != NULL) { + char *expr; + if (parent) { /* * Flag this as something we need to re-export. * No point actually exporting it now though, * the child can do it at the last minute. */ - v->flags |= (VAR_EXPORTED|VAR_REEXPORT); - return 1; + v->flags |= VAR_EXPORTED | VAR_REEXPORT; + return TRUE; } if (v->flags & VAR_IN_USE) { /* * We recursed while exporting in a child. * This isn't going to end well, just skip it. */ - return 0; + return FALSE; } - n = snprintf(tmp, sizeof(tmp), "${%s}", name); - if (n < (int)sizeof(tmp)) { - val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - setenv(name, val, 1); - free(val); - } + + expr = str_concat3("${", name, "}"); + val = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + setenv(name, val, 1); + free(val); + free(expr); } else { - if (parent) { - v->flags &= ~VAR_REEXPORT; /* once will do */ - } - if (parent || !(v->flags & VAR_EXPORTED)) { + if (parent) + v->flags &= ~(unsigned)VAR_REEXPORT; /* once will do */ + if (parent || !(v->flags & VAR_EXPORTED)) setenv(name, val, 1); - } } /* * This is so Var_Set knows to call Var_Export again... */ if (parent) { v->flags |= VAR_EXPORTED; } - return 1; + return TRUE; } static void Var_ExportVars_callback(void *entry, void *unused MAKE_ATTR_UNUSED) { Var *var = entry; Var_Export1(var->name, 0); } /* * This gets called from our children. */ void Var_ExportVars(void) { - char tmp[BUFSIZ]; char *val; - int n; /* * Several make's support this sort of mechanism for tracking * recursion - but each uses a different name. * We allow the makefiles to update MAKELEVEL and ensure * children see a correctly incremented value. */ + char tmp[BUFSIZ]; snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); setenv(MAKE_LEVEL_ENV, tmp, 1); - if (VAR_EXPORTED_NONE == var_exportedVars) + if (var_exportedVars == VAR_EXPORTED_NONE) return; - if (VAR_EXPORTED_ALL == var_exportedVars) { + if (var_exportedVars == VAR_EXPORTED_ALL) { /* Ouch! This is crazy... */ Hash_ForEach(&VAR_GLOBAL->context, Var_ExportVars_callback, NULL); return; } - /* - * We have a number of exported vars, - */ - n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); - if (n < (int)sizeof(tmp)) { - char **av; - char *as; - int ac; - int i; - val = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - if (*val) { - av = brk_string(val, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - Var_Export1(av[i], 0); - } - free(as); - free(av); - } - free(val); + val = Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES); + if (*val) { + Words words = Str_Words(val, FALSE); + size_t i; + + for (i = 0; i < words.len; i++) + Var_Export1(words.words[i], 0); + Words_Free(words); } + free(val); } /* - * This is called when .export is seen or - * .MAKE.EXPORTED is modified. - * It is also called when any exported var is modified. + * This is called when .export is seen or .MAKE.EXPORTED is modified. + * + * It is also called when any exported variable is modified. + * XXX: Is it really? + * + * str has the format "[-env|-literal] varname...". */ void -Var_Export(char *str, int isExport) +Var_Export(const char *str, Boolean isExport) { - char *name; + VarExportFlags flags; char *val; - char **av; - char *as; - int flags; - int ac; - int i; - if (isExport && (!str || !str[0])) { + if (isExport && str[0] == '\0') { var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ return; } flags = 0; if (strncmp(str, "-env", 4) == 0) { str += 4; } else if (strncmp(str, "-literal", 8) == 0) { str += 8; flags |= VAR_EXPORT_LITERAL; } else { flags |= VAR_EXPORT_PARENT; } - val = Var_Subst(NULL, str, VAR_GLOBAL, VARF_WANTRES); - if (*val) { - av = brk_string(val, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - name = av[i]; - if (!name[1]) { - /* - * A single char. - * If it is one of the vars that should only appear in - * local context, skip it, else we can get Var_Subst - * into a loop. - */ - switch (name[0]) { - case '@': - case '%': - case '*': - case '!': - continue; - } - } + + val = Var_Subst(str, VAR_GLOBAL, VARE_WANTRES); + if (val[0] != '\0') { + Words words = Str_Words(val, FALSE); + + size_t i; + for (i = 0; i < words.len; i++) { + const char *name = words.words[i]; if (Var_Export1(name, flags)) { - if (VAR_EXPORTED_ALL != var_exportedVars) + if (var_exportedVars != VAR_EXPORTED_ALL) var_exportedVars = VAR_EXPORTED_YES; if (isExport && (flags & VAR_EXPORT_PARENT)) { Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); } } } - free(as); - free(av); + Words_Free(words); } free(val); } +extern char **environ; + /* * This is called when .unexport[-env] is seen. + * + * str must have the form "unexport[-env] varname...". */ -extern char **environ; - void -Var_UnExport(char *str) +Var_UnExport(const char *str) { - char tmp[BUFSIZ]; - char *vlist; - char *cp; + const char *varnames; + char *varnames_freeIt; Boolean unexport_env; - int n; - if (!str || !str[0]) { - return; /* assert? */ - } + varnames = NULL; + varnames_freeIt = NULL; - vlist = NULL; - - str += 8; - unexport_env = (strncmp(str, "-env", 4) == 0); + str += strlen("unexport"); + unexport_env = strncmp(str, "-env", 4) == 0; if (unexport_env) { + const char *cp; char **newenv; cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ if (environ == savedEnv) { /* we have been here before! */ newenv = bmake_realloc(environ, 2 * sizeof(char *)); } else { if (savedEnv) { free(savedEnv); savedEnv = NULL; } newenv = bmake_malloc(2 * sizeof(char *)); } - if (!newenv) - return; + /* Note: we cannot safely free() the original environ. */ environ = savedEnv = newenv; newenv[0] = NULL; newenv[1] = NULL; if (cp && *cp) setenv(MAKE_LEVEL_ENV, cp, 1); } else { - for (; *str != '\n' && isspace((unsigned char) *str); str++) + for (; isspace((unsigned char)*str); str++) continue; - if (str[0] && str[0] != '\n') { - vlist = str; - } + if (str[0] != '\0') + varnames = str; } - if (!vlist) { + if (varnames == NULL) { /* Using .MAKE.EXPORTED */ - n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); - if (n < (int)sizeof(tmp)) { - vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - } + varnames = varnames_freeIt = Var_Subst("${" MAKE_EXPORTED ":O:u}", + VAR_GLOBAL, VARE_WANTRES); } - if (vlist) { + + { Var *v; - char **av; - char *as; - int ac; - int i; + size_t i; - av = brk_string(vlist, &ac, FALSE, &as); - for (i = 0; i < ac; i++) { - v = VarFind(av[i], VAR_GLOBAL, 0); - if (!v) + Words words = Str_Words(varnames, FALSE); + for (i = 0; i < words.len; i++) { + const char *varname = words.words[i]; + v = VarFind(varname, VAR_GLOBAL, 0); + if (v == NULL) { + VAR_DEBUG("Not unexporting \"%s\" (not found)\n", varname); continue; - if (!unexport_env && - (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { - unsetenv(v->name); } - v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT); + + VAR_DEBUG("Unexporting \"%s\"\n", varname); + if (!unexport_env && (v->flags & VAR_EXPORTED) && + !(v->flags & VAR_REEXPORT)) + unsetenv(v->name); + v->flags &= ~(unsigned)(VAR_EXPORTED | VAR_REEXPORT); + /* * If we are unexporting a list, * remove each one from .MAKE.EXPORTED. * If we are removing them all, * just delete .MAKE.EXPORTED below. */ - if (vlist == str) { - n = snprintf(tmp, sizeof(tmp), - "${" MAKE_EXPORTED ":N%s}", v->name); - if (n < (int)sizeof(tmp)) { - cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); - Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); - free(cp); - } + if (varnames == str) { + char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name, "}"); + char *cp = Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES); + Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); + free(cp); + free(expr); } } - free(as); - free(av); - if (vlist != str) { + Words_Free(words); + if (varnames != str) { Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); - free(vlist); + free(varnames_freeIt); } } } -static void +/* See Var_Set for documentation. */ +void Var_Set_with_flags(const char *name, const char *val, GNode *ctxt, VarSet_Flags flags) { + const char *unexpanded_name = name; + char *name_freeIt = NULL; Var *v; - char *expanded_name = NULL; + assert(val != NULL); + /* * We only look for a variable in the given context since anything set * here will override anything in a lower context, so there's not much * point in searching them all just to save a bit of memory... */ - if (strchr(name, '$') != NULL) { - expanded_name = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - if (expanded_name[0] == 0) { - if (DEBUG(VAR)) { - fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " - "name expands to empty string - ignored\n", - name, val); - } - free(expanded_name); - return; - } - name = expanded_name; + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + + if (name[0] == '\0') { + VAR_DEBUG("Var_Set(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + unexpanded_name, val); + free(name_freeIt); + return; } + if (ctxt == VAR_GLOBAL) { v = VarFind(name, VAR_CMD, 0); if (v != NULL) { - if ((v->flags & VAR_FROM_CMD)) { - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); - } + if (v->flags & VAR_FROM_CMD) { + VAR_DEBUG("%s:%s = %s ignored!\n", ctxt->name, name, val); goto out; } VarFreeEnv(v, TRUE); } } + v = VarFind(name, ctxt, 0); if (v == NULL) { - if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT)) { /* * This var would normally prevent the same name being added * to VAR_GLOBAL, so delete it from there if needed. * Otherwise -V name may show the wrong value. */ Var_Delete(name, VAR_GLOBAL); } - VarAdd(name, val, ctxt); + VarAdd(name, val, ctxt, flags); } else { + if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { + VAR_DEBUG("%s:%s = %s ignored (read-only)\n", + ctxt->name, name, val); + goto out; + } Buf_Empty(&v->val); if (val) - Buf_AddBytes(&v->val, strlen(val), val); + Buf_AddStr(&v->val, val); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); - } - if ((v->flags & VAR_EXPORTED)) { + VAR_DEBUG("%s:%s = %s\n", ctxt->name, name, val); + if (v->flags & VAR_EXPORTED) { Var_Export1(name, VAR_EXPORT_PARENT); } } /* * Any variables given on the command line are automatically exported * to the environment (as per POSIX standard) + * Other than internals. */ - if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + if (ctxt == VAR_CMD && !(flags & VAR_NO_EXPORT) && name[0] != '.') { if (v == NULL) { /* we just added it */ v = VarFind(name, ctxt, 0); } if (v != NULL) v->flags |= VAR_FROM_CMD; /* * If requested, don't export these in the environment * individually. We still put them in MAKEOVERRIDES so * that the command-line settings continue to override * Makefile settings. */ - if (varNoExportEnv != TRUE) + if (!varNoExportEnv) setenv(name, val ? val : "", 1); Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); } - if (*name == '.') { - if (strcmp(name, SAVE_DOLLARS) == 0) - save_dollars = s2Boolean(val, save_dollars); - } + if (name[0] == '.' && strcmp(name, SAVE_DOLLARS) == 0) + save_dollars = s2Boolean(val, save_dollars); out: - free(expanded_name); + free(name_freeIt); if (v != NULL) VarFreeEnv(v, TRUE); } /*- *----------------------------------------------------------------------- * Var_Set -- * Set the variable name to the value val in the given context. * + * If the variable doesn't yet exist, it is created. + * Otherwise the new value overwrites and replaces the old value. + * * Input: * name name of variable to set * val value to give to the variable * ctxt context in which to set it * - * Side Effects: - * If the variable doesn't yet exist, a new record is created for it. - * Else the old value is freed and the new one stuck in its place - * * Notes: * The variable is searched for only in its context before being * created in that context. I.e. if the context is VAR_GLOBAL, * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only * VAR_CMD->context is searched. This is done to avoid the literally * thousands of unnecessary strcmp's that used to be done to * set, say, $(@) or $(<). * If the context is VAR_GLOBAL though, we check if the variable * was set in VAR_CMD from the command line and skip it if so. *----------------------------------------------------------------------- */ void Var_Set(const char *name, const char *val, GNode *ctxt) { Var_Set_with_flags(name, val, ctxt, 0); } /*- *----------------------------------------------------------------------- * Var_Append -- * The variable of the given name has the given value appended to it in * the given context. * + * If the variable doesn't exist, it is created. Otherwise the strings + * are concatenated, with a space in between. + * * Input: * name name of variable to modify - * val String to append to it - * ctxt Context in which this should occur + * val string to append to it + * ctxt context in which this should occur * - * Side Effects: - * If the variable doesn't exist, it is created. Else the strings - * are concatenated (with a space in between). - * * Notes: * Only if the variable is being sought in the global context is the * environment searched. * XXX: Knows its calling circumstances in that if called with ctxt * an actual target, it will only search that context since only * a local variable could be being appended to. This is actually * a big win and must be tolerated. *----------------------------------------------------------------------- */ void Var_Append(const char *name, const char *val, GNode *ctxt) { + char *name_freeIt = NULL; Var *v; - Hash_Entry *h; - char *expanded_name = NULL; + assert(val != NULL); + if (strchr(name, '$') != NULL) { - expanded_name = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - if (expanded_name[0] == 0) { - if (DEBUG(VAR)) { - fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " - "name expands to empty string - ignored\n", - name, val); - } - free(expanded_name); + const char *unexpanded_name = name; + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + if (name[0] == '\0') { + VAR_DEBUG("Var_Append(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + unexpanded_name, val); + free(name_freeIt); return; } - name = expanded_name; } - v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD|FIND_ENV) : 0); + v = VarFind(name, ctxt, ctxt == VAR_GLOBAL ? (FIND_CMD | FIND_ENV) : 0); if (v == NULL) { Var_Set(name, val, ctxt); } else if (ctxt == VAR_CMD || !(v->flags & VAR_FROM_CMD)) { Buf_AddByte(&v->val, ' '); - Buf_AddBytes(&v->val, strlen(val), val); + Buf_AddStr(&v->val, val); - if (DEBUG(VAR)) { - fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, - Buf_GetAll(&v->val, NULL)); - } + VAR_DEBUG("%s:%s = %s\n", ctxt->name, name, + Buf_GetAll(&v->val, NULL)); if (v->flags & VAR_FROM_ENV) { + Hash_Entry *h; + /* * If the original variable came from the environment, we * have to install it in the global context (we could place * it in the environment, but then we should provide a way to * export other variables...) */ - v->flags &= ~VAR_FROM_ENV; + v->flags &= ~(unsigned)VAR_FROM_ENV; h = Hash_CreateEntry(&ctxt->context, name, NULL); Hash_SetValue(h, v); } } - free(expanded_name); + free(name_freeIt); } -/*- - *----------------------------------------------------------------------- - * Var_Exists -- - * See if the given variable exists. +/* See if the given variable exists, in the given context or in other + * fallback contexts. * * Input: * name Variable to find * ctxt Context in which to start search - * - * Results: - * TRUE if it does, FALSE if it doesn't - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- */ Boolean Var_Exists(const char *name, GNode *ctxt) { - Var *v; - char *cp; + char *name_freeIt = NULL; + Var *v; - if ((cp = strchr(name, '$')) != NULL) { - cp = Var_Subst(NULL, name, ctxt, VARF_WANTRES); - } - v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); - free(cp); - if (v == NULL) { + if (strchr(name, '$') != NULL) + name = name_freeIt = Var_Subst(name, ctxt, VARE_WANTRES); + + v = VarFind(name, ctxt, FIND_CMD | FIND_GLOBAL | FIND_ENV); + free(name_freeIt); + if (v == NULL) return FALSE; - } (void)VarFreeEnv(v, TRUE); return TRUE; } /*- *----------------------------------------------------------------------- * Var_Value -- - * Return the value of the named variable in the given context + * Return the unexpanded value of the given variable in the given + * context, or the usual contexts. * * Input: * name name to find * ctxt context in which to search for it * * Results: - * The value if the variable exists, NULL if it doesn't - * - * Side Effects: - * None + * The value if the variable exists, NULL if it doesn't. + * If the returned value is not NULL, the caller must free *freeIt + * as soon as the returned value is no longer needed. *----------------------------------------------------------------------- */ -char * -Var_Value(const char *name, GNode *ctxt, char **frp) +const char * +Var_Value(const char *name, GNode *ctxt, char **freeIt) { - Var *v; + Var *v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + char *p; - v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); - *frp = NULL; + *freeIt = NULL; if (v == NULL) return NULL; - char *p = (Buf_GetAll(&v->val, NULL)); + p = Buf_GetAll(&v->val, NULL); if (VarFreeEnv(v, FALSE)) - *frp = p; + *freeIt = p; return p; } -/* This callback for VarModify gets a single word from an expression and +/* SepBuf is a string being built from "words", interleaved with separators. */ +typedef struct { + Buffer buf; + Boolean needSep; + char sep; /* usually ' ', but see the :ts modifier */ +} SepBuf; + +static void +SepBuf_Init(SepBuf *buf, char sep) +{ + Buf_Init(&buf->buf, 32 /* bytes */); + buf->needSep = FALSE; + buf->sep = sep; +} + +static void +SepBuf_Sep(SepBuf *buf) +{ + buf->needSep = TRUE; +} + +static void +SepBuf_AddBytes(SepBuf *buf, const char *mem, size_t mem_size) +{ + if (mem_size == 0) + return; + if (buf->needSep && buf->sep != '\0') { + Buf_AddByte(&buf->buf, buf->sep); + buf->needSep = FALSE; + } + Buf_AddBytes(&buf->buf, mem, mem_size); +} + +static void +SepBuf_AddBytesBetween(SepBuf *buf, const char *start, const char *end) +{ + SepBuf_AddBytes(buf, start, (size_t)(end - start)); +} + +static void +SepBuf_AddStr(SepBuf *buf, const char *str) +{ + SepBuf_AddBytes(buf, str, strlen(str)); +} + +static char * +SepBuf_Destroy(SepBuf *buf, Boolean free_buf) +{ + return Buf_Destroy(&buf->buf, free_buf); +} + + +/* This callback for ModifyWords gets a single word from an expression and * typically adds a modification of this word to the buffer. It may also do - * nothing or add several words. - * - * If addSpaces is TRUE, it must add a space before adding anything else to - * the buffer. - * - * It returns the addSpace value for the next call of this callback. Typical - * return values are the current addSpaces or TRUE. */ -typedef Boolean (*VarModifyCallback)(GNode *ctxt, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, void *data); + * nothing or add several words. */ +typedef void (*ModifyWordsCallback)(const char *word, SepBuf *buf, void *data); -/* Callback function for VarModify to implement the :H modifier. +/* Callback for ModifyWords to implement the :H modifier. * Add the dirname of the given word to the buffer. */ -static Boolean -VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Head(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); if (slash != NULL) - Buf_AddBytes(buf, slash - word, word); + SepBuf_AddBytesBetween(buf, word, slash); else - Buf_AddByte(buf, '.'); - - return TRUE; + SepBuf_AddStr(buf, "."); } -/* Callback function for VarModify to implement the :T modifier. +/* Callback for ModifyWords to implement the :T modifier. * Add the basename of the given word to the buffer. */ -static Boolean -VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Tail(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *slash = strrchr(word, '/'); const char *base = slash != NULL ? slash + 1 : word; - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(base), base); - return TRUE; + SepBuf_AddStr(buf, base); } -/* Callback function for VarModify to implement the :E modifier. +/* Callback for ModifyWords to implement the :E modifier. * Add the filename suffix of the given word to the buffer, if it exists. */ -static Boolean -VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +static void +ModifyWord_Suffix(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { const char *dot = strrchr(word, '.'); - if (dot == NULL) - return addSpace; - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(dot + 1), dot + 1); - return TRUE; + if (dot != NULL) + SepBuf_AddStr(buf, dot + 1); } -/* Callback function for VarModify to implement the :R modifier. - * Add the filename basename of the given word to the buffer. */ -static Boolean -VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *dummy MAKE_ATTR_UNUSED) +/* Callback for ModifyWords to implement the :R modifier. + * Add the basename of the given word to the buffer. */ +static void +ModifyWord_Root(const char *word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED) { - char *dot = strrchr(word, '.'); + const char *dot = strrchr(word, '.'); size_t len = dot != NULL ? (size_t)(dot - word) : strlen(word); - - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, len, word); - return TRUE; + SepBuf_AddBytes(buf, word, len); } -/* Callback function for VarModify to implement the :M modifier. +/* Callback for ModifyWords to implement the :M modifier. * Place the word in the buffer if it matches the given pattern. */ -static Boolean -VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_Match(const char *word, SepBuf *buf, void *data) { const char *pattern = data; - if (DEBUG(VAR)) - fprintf(debug_file, "VarMatch [%s] [%s]\n", word, pattern); + VAR_DEBUG("VarMatch [%s] [%s]\n", word, pattern); + if (Str_Match(word, pattern)) + SepBuf_AddStr(buf, word); +} + +/* Callback for ModifyWords to implement the :N modifier. + * Place the word in the buffer if it doesn't match the given pattern. */ +static void +ModifyWord_NoMatch(const char *word, SepBuf *buf, void *data) +{ + const char *pattern = data; if (!Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + SepBuf_AddStr(buf, word); } #ifdef SYSVVARSUB -/* Callback function for VarModify to implement the :%.from=%.to modifier. */ -static Boolean -VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +/*- + *----------------------------------------------------------------------- + * Str_SYSVMatch -- + * Check word against pattern for a match (% is wild), + * + * Input: + * word Word to examine + * pattern Pattern to examine against + * + * Results: + * Returns the start of the match, or NULL. + * *match_len returns the length of the match, if any. + * *hasPercent returns whether the pattern contains a percent. + *----------------------------------------------------------------------- + */ +static const char * +Str_SYSVMatch(const char *word, const char *pattern, size_t *match_len, + Boolean *hasPercent) { - size_t len; - char *ptr; - Boolean hasPercent; - VarPattern *pat = data; + const char *p = pattern; + const char *w = word; + const char *percent; + size_t w_len; + size_t p_len; + const char *w_tail; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); + *hasPercent = FALSE; + if (*p == '\0') { /* ${VAR:=suffix} */ + *match_len = strlen(w); /* Null pattern is the whole string */ + return w; + } - if ((ptr = Str_SYSVMatch(word, pat->lhs, &len, &hasPercent)) != NULL) { - char *varexp = Var_Subst(NULL, pat->rhs, ctx, VARF_WANTRES); - Str_SYSVSubst(buf, varexp, ptr, len, hasPercent); - free(varexp); - } else { - Buf_AddBytes(buf, strlen(word), word); + percent = strchr(p, '%'); + if (percent != NULL) { /* ${VAR:...%...=...} */ + *hasPercent = TRUE; + if (*w == '\0') + return NULL; /* empty word does not match pattern */ + + /* check that the prefix matches */ + for (; p != percent && *w != '\0' && *w == *p; w++, p++) + continue; + if (p != percent) + return NULL; /* No match */ + + p++; /* Skip the percent */ + if (*p == '\0') { + /* No more pattern, return the rest of the string */ + *match_len = strlen(w); + return w; + } } - return TRUE; + /* Test whether the tail matches */ + w_len = strlen(w); + p_len = strlen(p); + if (w_len < p_len) + return NULL; + + w_tail = w + w_len - p_len; + if (memcmp(p, w_tail, p_len) != 0) + return NULL; + + *match_len = (size_t)(w_tail - w); + return w; } -#endif -/* Callback function for VarModify to implement the :N modifier. - * Place the word in the buffer if it doesn't match the given pattern. */ -static Boolean -VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +typedef struct { + GNode *ctx; + const char *lhs; + const char *rhs; +} ModifyWord_SYSVSubstArgs; + +/* Callback for ModifyWords to implement the :%.from=%.to modifier. */ +static void +ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) { - const char *pattern = data; - if (Str_Match(word, pattern)) - return addSpace; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + const ModifyWord_SYSVSubstArgs *args = data; + char *rhs_expanded; + const char *rhs; + const char *percent; + + size_t match_len; + Boolean lhsPercent; + const char *match = Str_SYSVMatch(word, args->lhs, &match_len, &lhsPercent); + if (match == NULL) { + SepBuf_AddStr(buf, word); + return; + } + + /* Append rhs to the buffer, substituting the first '%' with the + * match, but only if the lhs had a '%' as well. */ + + rhs_expanded = Var_Subst(args->rhs, args->ctx, VARE_WANTRES); + + rhs = rhs_expanded; + percent = strchr(rhs, '%'); + + if (percent != NULL && lhsPercent) { + /* Copy the prefix of the replacement pattern */ + SepBuf_AddBytesBetween(buf, rhs, percent); + rhs = percent + 1; + } + if (percent != NULL || !lhsPercent) + SepBuf_AddBytes(buf, match, match_len); + + /* Append the suffix of the replacement pattern */ + SepBuf_AddStr(buf, rhs); + + free(rhs_expanded); } +#endif -/* Callback function for VarModify to implement the :S,from,to, modifier. + +typedef struct { + const char *lhs; + size_t lhsLen; + const char *rhs; + size_t rhsLen; + VarPatternFlags pflags; + Boolean matched; +} ModifyWord_SubstArgs; + +/* Callback for ModifyWords to implement the :S,from,to, modifier. * Perform a string substitution on the given word. */ -static Boolean -VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_Subst(const char *word, SepBuf *buf, void *data) { - int wordLen = strlen(word); - const char *cp; /* General pointer */ - VarPattern *pattern = data; + size_t wordLen = strlen(word); + ModifyWord_SubstArgs *args = data; + const char *match; - if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != - (VAR_SUB_ONE|VAR_SUB_MATCHED)) { - /* - * Still substituting -- break it down into simple anchored cases - * and if none of them fits, perform the general substitution case. - */ - if ((pattern->flags & VAR_MATCH_START) && - (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { - /* - * Anchored at start and beginning of word matches pattern - */ - if ((pattern->flags & VAR_MATCH_END) && - (wordLen == pattern->leftLen)) { - /* - * Also anchored at end and matches to the end (word - * is same length as pattern) add space and rhs only - * if rhs is non-null. - */ - if (pattern->rightLen != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - } - pattern->flags |= VAR_SUB_MATCHED; - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Doesn't match to end -- copy word wholesale - */ - goto nosub; - } else { - /* - * Matches at start but need to copy in trailing characters - */ - if ((pattern->rightLen + wordLen - pattern->leftLen) != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - Buf_AddBytes(buf, wordLen - pattern->leftLen, - (word + pattern->leftLen)); - pattern->flags |= VAR_SUB_MATCHED; - } - } else if (pattern->flags & VAR_MATCH_START) { - /* - * Had to match at start of word and didn't -- copy whole word. - */ + if ((args->pflags & VARP_SUB_ONE) && args->matched) + goto nosub; + + if (args->pflags & VARP_ANCHOR_START) { + if (wordLen < args->lhsLen || + memcmp(word, args->lhs, args->lhsLen) != 0) goto nosub; - } else if (pattern->flags & VAR_MATCH_END) { - /* - * Anchored at end, Find only place match could occur (leftLen - * characters from the end of the word) and see if it does. Note - * that because the $ will be left at the end of the lhs, we have - * to use strncmp. - */ - cp = word + (wordLen - pattern->leftLen); - if ((cp >= word) && - (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { - /* - * Match found. If we will place characters in the buffer, - * add a space before hand as indicated by addSpace, then - * stuff in the initial, unmatched part of the word followed - * by the right-hand-side. - */ - if (((cp - word) + pattern->rightLen) != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - addSpace = TRUE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - pattern->flags |= VAR_SUB_MATCHED; - } else { - /* - * Had to match at end and didn't. Copy entire word. - */ + + if (args->pflags & VARP_ANCHOR_END) { + if (wordLen != args->lhsLen) goto nosub; - } - } else { - /* - * Pattern is unanchored: search for the pattern in the word using - * String_FindSubstring, copying unmatched portions and the - * right-hand-side for each match found, handling non-global - * substitutions correctly, etc. When the loop is done, any - * remaining part of the word (word and wordLen are adjusted - * accordingly through the loop) is copied straight into the - * buffer. - * addSpace is set FALSE as soon as a space is added to the - * buffer. - */ - Boolean done; - int origSize; - done = FALSE; - origSize = Buf_Size(buf); - while (!done) { - cp = Str_FindSubstring(word, pattern->lhs); - if (cp != NULL) { - if (addSpace && (((cp - word) + pattern->rightLen) != 0)) { - Buf_AddByte(buf, vpstate->varSpace); - addSpace = FALSE; - } - Buf_AddBytes(buf, cp - word, word); - Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); - wordLen -= (cp - word) + pattern->leftLen; - word = cp + pattern->leftLen; - if (wordLen == 0) { - done = TRUE; - } - if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { - done = TRUE; - } - pattern->flags |= VAR_SUB_MATCHED; - } else { - done = TRUE; - } - } - if (wordLen != 0) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); - } - Buf_AddBytes(buf, wordLen, word); - } - /* - * If added characters to the buffer, need to add a space - * before we add any more. If we didn't add any, just return - * the previous value of addSpace. - */ - return (Buf_Size(buf) != origSize) || addSpace; + /* :S,^whole$,replacement, */ + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; + } else { + /* :S,^prefix,replacement, */ + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + SepBuf_AddBytes(buf, word + args->lhsLen, wordLen - args->lhsLen); + args->matched = TRUE; } - return addSpace; + return; } -nosub: - if (addSpace && vpstate->varSpace) { - Buf_AddByte(buf, vpstate->varSpace); + + if (args->pflags & VARP_ANCHOR_END) { + const char *start; + + if (wordLen < args->lhsLen) + goto nosub; + + start = word + (wordLen - args->lhsLen); + if (memcmp(start, args->lhs, args->lhsLen) != 0) + goto nosub; + + /* :S,suffix$,replacement, */ + SepBuf_AddBytesBetween(buf, word, start); + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; + return; } - Buf_AddBytes(buf, wordLen, word); - return TRUE; + + /* unanchored case, may match more than once */ + while ((match = Str_FindSubstring(word, args->lhs)) != NULL) { + SepBuf_AddBytesBetween(buf, word, match); + SepBuf_AddBytes(buf, args->rhs, args->rhsLen); + args->matched = TRUE; + wordLen -= (size_t)(match - word) + args->lhsLen; + word += (size_t)(match - word) + args->lhsLen; + if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) + break; + } +nosub: + SepBuf_AddBytes(buf, word, wordLen); } #ifndef NO_REGEX -/*- - *----------------------------------------------------------------------- - * VarREError -- - * Print the error caused by a regcomp or regexec call. - * - * Side Effects: - * An error gets printed. - * - *----------------------------------------------------------------------- - */ +/* Print the error caused by a regcomp or regexec call. */ static void VarREError(int reerr, regex_t *pat, const char *str) { - char *errbuf; - int errlen; - - errlen = regerror(reerr, pat, 0, 0); - errbuf = bmake_malloc(errlen); + size_t errlen = regerror(reerr, pat, 0, 0); + char *errbuf = bmake_malloc(errlen); regerror(reerr, pat, errbuf, errlen); Error("%s: %s", str, errbuf); free(errbuf); } -/* Callback function for VarModify to implement the :C/from/to/ modifier. +typedef struct { + regex_t re; + size_t nsub; + char *replace; + VarPatternFlags pflags; + Boolean matched; +} ModifyWord_SubstRegexArgs; + +/* Callback for ModifyWords to implement the :C/from/to/ modifier. * Perform a regex substitution on the given word. */ -static Boolean -VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, - Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +static void +ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) { - VarREPattern *pat = data; + ModifyWord_SubstRegexArgs *args = data; int xrv; const char *wp = word; char *rp; - int added = 0; int flags = 0; + regmatch_t m[10]; -#define MAYBE_ADD_SPACE() \ - if (addSpace && !added) \ - Buf_AddByte(buf, ' '); \ - added = 1 + if ((args->pflags & VARP_SUB_ONE) && args->matched) + goto nosub; - if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == - (VAR_SUB_ONE|VAR_SUB_MATCHED)) - xrv = REG_NOMATCH; - else { - tryagain: - xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); - } +tryagain: + xrv = regexec(&args->re, wp, args->nsub, m, flags); switch (xrv) { case 0: - pat->flags |= VAR_SUB_MATCHED; - if (pat->matches[0].rm_so > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, pat->matches[0].rm_so, wp); - } + args->matched = TRUE; + SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so); - for (rp = pat->replace; *rp; rp++) { - if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, rp[1]); + for (rp = args->replace; *rp; rp++) { + if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) { + SepBuf_AddBytes(buf, rp + 1, 1); rp++; - } else if ((*rp == '&') || - ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { - int n; - const char *subbuf; - int sublen; - char errstr[3]; + continue; + } - if (*rp == '&') { - n = 0; - errstr[0] = '&'; - errstr[1] = '\0'; - } else { - n = rp[1] - '0'; - errstr[0] = '\\'; - errstr[1] = rp[1]; - errstr[2] = '\0'; - rp++; - } + if (*rp == '&') { + SepBuf_AddBytesBetween(buf, wp + m[0].rm_so, wp + m[0].rm_eo); + continue; + } - if (n > pat->nsub) { - Error("No subexpression %s", &errstr[0]); - subbuf = ""; - sublen = 0; - } else if ((pat->matches[n].rm_so == -1) && - (pat->matches[n].rm_eo == -1)) { - Error("No match for subexpression %s", &errstr[0]); - subbuf = ""; - sublen = 0; + if (*rp != '\\' || !isdigit((unsigned char)rp[1])) { + SepBuf_AddBytes(buf, rp, 1); + continue; + } + + { /* \0 to \9 backreference */ + size_t n = (size_t)(rp[1] - '0'); + rp++; + + if (n >= args->nsub) { + Error("No subexpression \\%zu", n); + } else if (m[n].rm_so == -1 && m[n].rm_eo == -1) { + Error("No match for subexpression \\%zu", n); } else { - subbuf = wp + pat->matches[n].rm_so; - sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; + SepBuf_AddBytesBetween(buf, wp + m[n].rm_so, + wp + m[n].rm_eo); } - - if (sublen > 0) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, sublen, subbuf); - } - } else { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *rp); } } - wp += pat->matches[0].rm_eo; - if (pat->flags & VAR_SUB_GLOBAL) { + + wp += m[0].rm_eo; + if (args->pflags & VARP_SUB_GLOBAL) { flags |= REG_NOTBOL; - if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) { - MAYBE_ADD_SPACE(); - Buf_AddByte(buf, *wp); + if (m[0].rm_so == 0 && m[0].rm_eo == 0) { + SepBuf_AddBytes(buf, wp, 1); wp++; - } if (*wp) goto tryagain; } if (*wp) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, strlen(wp), wp); + SepBuf_AddStr(buf, wp); } break; default: - VarREError(xrv, &pat->re, "Unexpected regex error"); + VarREError(xrv, &args->re, "Unexpected regex error"); /* fall through */ case REG_NOMATCH: - if (*wp) { - MAYBE_ADD_SPACE(); - Buf_AddBytes(buf, strlen(wp), wp); - } + nosub: + SepBuf_AddStr(buf, wp); break; } - return addSpace || added; } #endif -/* Callback function for VarModify to implement the :@var@...@ modifier of - * ODE make. We set the temp variable named in pattern.lhs to word and - * expand pattern.rhs. */ -static Boolean -VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED, - Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - const char *word, Boolean addSpace, Buffer *buf, - void *data) +typedef struct { + GNode *ctx; + char *tvar; /* name of temporary variable */ + char *str; /* string to expand */ + VarEvalFlags eflags; +} ModifyWord_LoopArgs; + +/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */ +static void +ModifyWord_Loop(const char *word, SepBuf *buf, void *data) { - VarLoop *loop = data; + const ModifyWord_LoopArgs *args; char *s; - int slen; - if (*word) { - Var_Set_with_flags(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); - s = Var_Subst(NULL, loop->str, loop->ctxt, loop->flags); - if (DEBUG(VAR)) { - fprintf(debug_file, - "VarLoopExpand: in \"%s\", replace \"%s\" with \"%s\" " - "to \"%s\"\n", - word, loop->tvar, loop->str, s ? s : "(null)"); - } - if (s != NULL && *s != '\0') { - if (addSpace && *s != '\n') - Buf_AddByte(buf, ' '); - Buf_AddBytes(buf, (slen = strlen(s)), s); - addSpace = (slen > 0 && s[slen - 1] != '\n'); - } - free(s); - } - return addSpace; + if (word[0] == '\0') + return; + + args = data; + Var_Set_with_flags(args->tvar, word, args->ctx, VAR_NO_EXPORT); + s = Var_Subst(args->str, args->ctx, args->eflags); + + VAR_DEBUG("ModifyWord_Loop: in \"%s\", replace \"%s\" with \"%s\" " + "to \"%s\"\n", + word, args->tvar, args->str, s); + + if (s[0] == '\n' || (buf->buf.count > 0 && + buf->buf.buffer[buf->buf.count - 1] == '\n')) + buf->needSep = FALSE; + SepBuf_AddStr(buf, s); + free(s); } /*- - *----------------------------------------------------------------------- - * VarSelectWords -- - * Implements the :[start..end] modifier. - * This is a special case of VarModify since we want to be able - * to scan the list backwards if start > end. - * - * Input: - * str String whose words should be trimmed - * seldata words to select - * - * Results: - * A string of all the words selected. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- + * Implements the :[first..last] modifier. + * This is a special case of ModifyWords since we want to be able + * to scan the list backwards if first > last. */ static char * -VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *str, VarSelectWords_t *seldata) +VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, + int last) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed - * word */ - char **av; /* word list */ - char *as; /* word list memory */ - int ac, i; + Words words; int start, end, step; + int i; - Buf_Init(&buf, 0); - addSpace = FALSE; + SepBuf buf; + SepBuf_Init(&buf, sep); - if (vpstate->oneBigWord) { - /* fake what brk_string() would do if there were only one word */ - ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); - as = bmake_strdup(str); - av[0] = as; - av[1] = NULL; + if (oneBigWord) { + /* fake what Str_Words() would do if there were only one word */ + words.len = 1; + words.words = bmake_malloc((words.len + 1) * sizeof(char *)); + words.freeIt = bmake_strdup(str); + words.words[0] = words.freeIt; + words.words[1] = NULL; } else { - av = brk_string(str, &ac, FALSE, &as); + words = Str_Words(str, FALSE); } /* - * Now sanitize seldata. - * If seldata->start or seldata->end are negative, convert them to - * the positive equivalents (-1 gets converted to argc, -2 gets - * converted to (argc-1), etc.). + * Now sanitize the given range. + * If first or last are negative, convert them to the positive equivalents + * (-1 gets converted to ac, -2 gets converted to (ac - 1), etc.). */ - if (seldata->start < 0) - seldata->start = ac + seldata->start + 1; - if (seldata->end < 0) - seldata->end = ac + seldata->end + 1; + if (first < 0) + first += (int)words.len + 1; + if (last < 0) + last += (int)words.len + 1; /* * We avoid scanning more of the list than we need to. */ - if (seldata->start > seldata->end) { - start = MIN(ac, seldata->start) - 1; - end = MAX(0, seldata->end - 1); + if (first > last) { + start = MIN((int)words.len, first) - 1; + end = MAX(0, last - 1); step = -1; } else { - start = MAX(0, seldata->start - 1); - end = MIN(ac, seldata->end); + start = MAX(0, first - 1); + end = MIN((int)words.len, last); step = 1; } - for (i = start; - (step < 0 && i >= end) || (step > 0 && i < end); - i += step) { - if (av[i] && *av[i]) { - if (addSpace && vpstate->varSpace) { - Buf_AddByte(&buf, vpstate->varSpace); - } - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - addSpace = TRUE; - } + for (i = start; (step < 0) == (i >= end); i += step) { + SepBuf_AddStr(&buf, words.words[i]); + SepBuf_Sep(&buf); } - free(as); - free(av); + Words_Free(words); - return Buf_Destroy(&buf, FALSE); + return SepBuf_Destroy(&buf, FALSE); } -/* Callback function for VarModify to implement the :tA modifier. +/* Callback for ModifyWords to implement the :tA modifier. * Replace each word with the result of realpath() if successful. */ -static Boolean -VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, - const char *word, Boolean addSpace, Buffer *buf, - void *patternp MAKE_ATTR_UNUSED) +static void +ModifyWord_Realpath(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) { struct stat st; char rbuf[MAXPATHLEN]; - char *rp; - if (addSpace && vpstate->varSpace) - Buf_AddByte(buf, vpstate->varSpace); - rp = cached_realpath(word, rbuf); - if (rp && *rp == '/' && stat(rp, &st) == 0) + const char *rp = cached_realpath(word, rbuf); + if (rp != NULL && *rp == '/' && stat(rp, &st) == 0) word = rp; - Buf_AddBytes(buf, strlen(word), word); - return TRUE; + SepBuf_AddStr(buf, word); } /*- *----------------------------------------------------------------------- * Modify each of the words of the passed string using the given function. * * Input: - * str String whose words should be trimmed - * modProc Function to use to modify them - * data Custom data for the modProc + * str String whose words should be modified + * modifyWord Function that modifies a single word + * modifyWord_args Custom arguments for modifyWord * * Results: * A string of all the words modified appropriately. - * - * Side Effects: - * None. - * *----------------------------------------------------------------------- */ static char * -VarModify(GNode *ctx, Var_Parse_State *vpstate, - const char *str, VarModifyCallback modProc, void *datum) +ModifyWords(GNode *ctx, char sep, Boolean oneBigWord, const char *str, + ModifyWordsCallback modifyWord, void *modifyWord_args) { - Buffer buf; /* Buffer for the new string */ - Boolean addSpace; /* TRUE if need to add a space to the - * buffer before adding the trimmed word */ - char **av; /* word list */ - char *as; /* word list memory */ - int ac, i; + SepBuf result; + Words words; + size_t i; - Buf_Init(&buf, 0); - addSpace = FALSE; - - if (vpstate->oneBigWord) { - /* fake what brk_string() would do if there were only one word */ - ac = 1; - av = bmake_malloc((ac + 1) * sizeof(char *)); - as = bmake_strdup(str); - av[0] = as; - av[1] = NULL; - } else { - av = brk_string(str, &ac, FALSE, &as); + if (oneBigWord) { + SepBuf_Init(&result, sep); + modifyWord(str, &result, modifyWord_args); + return SepBuf_Destroy(&result, FALSE); } - if (DEBUG(VAR)) { - fprintf(debug_file, "VarModify: split \"%s\" into %d words\n", - str, ac); - } + SepBuf_Init(&result, sep); - for (i = 0; i < ac; i++) - addSpace = modProc(ctx, vpstate, av[i], addSpace, &buf, datum); + words = Str_Words(str, FALSE); - free(as); - free(av); + VAR_DEBUG("ModifyWords: split \"%s\" into %zu words\n", str, words.len); - return Buf_Destroy(&buf, FALSE); -} + for (i = 0; i < words.len; i++) { + modifyWord(words.words[i], &result, modifyWord_args); + if (result.buf.count > 0) + SepBuf_Sep(&result); + } + Words_Free(words); -static int -VarWordCompare(const void *a, const void *b) -{ - int r = strcmp(*(const char * const *)a, *(const char * const *)b); - return r; + return SepBuf_Destroy(&result, FALSE); } -static int -VarWordCompareReverse(const void *a, const void *b) -{ - int r = strcmp(*(const char * const *)b, *(const char * const *)a); - return r; -} -/*- - *----------------------------------------------------------------------- - * VarOrder -- - * Order the words in the string. - * - * Input: - * str String whose words should be sorted. - * otype How to order: s - sort, x - random. - * - * Results: - * A string containing the words ordered. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ static char * -VarOrder(const char *str, const char otype) +Words_JoinFree(Words words) { - Buffer buf; /* Buffer for the new string */ - char **av; /* word list [first word does not count] */ - char *as; /* word list memory */ - int ac, i; + Buffer buf; + size_t i; Buf_Init(&buf, 0); - av = brk_string(str, &ac, FALSE, &as); - - if (ac > 0) { - switch (otype) { - case 'r': /* reverse sort alphabetically */ - qsort(av, ac, sizeof(char *), VarWordCompareReverse); - break; - case 's': /* sort alphabetically */ - qsort(av, ac, sizeof(char *), VarWordCompare); - break; - case 'x': /* randomize */ - { - /* - * We will use [ac..2] range for mod factors. This will produce - * random numbers in [(ac-1)..0] interval, and minimal - * reasonable value for mod factor is 2 (the mod 1 will produce - * 0 with probability 1). - */ - for (i = ac - 1; i > 0; i--) { - int rndidx = random() % (i + 1); - char *t = av[i]; - av[i] = av[rndidx]; - av[rndidx] = t; - } - } - } + for (i = 0; i < words.len; i++) { + if (i != 0) + Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ + Buf_AddStr(&buf, words.words[i]); } - for (i = 0; i < ac; i++) { - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); - } + Words_Free(words); - free(as); - free(av); - return Buf_Destroy(&buf, FALSE); } - -/*- - *----------------------------------------------------------------------- - * VarUniq -- - * Remove adjacent duplicate words. - * - * Input: - * str String whose words should be sorted - * - * Results: - * A string containing the resulting words. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Remove adjacent duplicate words. */ static char * VarUniq(const char *str) { - Buffer buf; /* Buffer for new string */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ - int ac, i, j; + Words words = Str_Words(str, FALSE); - Buf_Init(&buf, 0); - av = brk_string(str, &ac, FALSE, &as); - - if (ac > 1) { - for (j = 0, i = 1; i < ac; i++) - if (strcmp(av[i], av[j]) != 0 && (++j != i)) - av[j] = av[i]; - ac = j + 1; + if (words.len > 1) { + size_t i, j; + for (j = 0, i = 1; i < words.len; i++) + if (strcmp(words.words[i], words.words[j]) != 0 && (++j != i)) + words.words[j] = words.words[i]; + words.len = j + 1; } - for (i = 0; i < ac; i++) { - Buf_AddBytes(&buf, strlen(av[i]), av[i]); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); - } - - free(as); - free(av); - - return Buf_Destroy(&buf, FALSE); + return Words_JoinFree(words); } -/*- - *----------------------------------------------------------------------- - * VarRange -- - * Return an integer sequence - * - * Input: - * str String whose words provide default range - * ac range length, if 0 use str words - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ -static char * -VarRange(const char *str, int ac) -{ - Buffer buf; /* Buffer for new string */ - char tmp[32]; /* each element */ - char **av; /* List of words to affect */ - char *as; /* Word list memory */ - int i, n; - Buf_Init(&buf, 0); - if (ac > 0) { - as = NULL; - av = NULL; - } else { - av = brk_string(str, &ac, FALSE, &as); - } - for (i = 0; i < ac; i++) { - n = snprintf(tmp, sizeof(tmp), "%d", 1 + i); - if (n >= (int)sizeof(tmp)) - break; - Buf_AddBytes(&buf, n, tmp); - if (i != ac - 1) - Buf_AddByte(&buf, ' '); - } - - free(as); - free(av); - - return Buf_Destroy(&buf, FALSE); -} - - /*- - *----------------------------------------------------------------------- - * VarGetPattern -- - * During the parsing of a part of a modifier such as :S or :@, - * pass through the tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution unless flags - * has VAR_NOSUBST set). - * Return the expanded string or NULL if the delimiter was missing - * If pattern is specified, handle escaped ampersands, and replace - * unescaped ampersands with the lhs of the pattern. + * Parse a part of a modifier such as the "from" and "to" in :S/from/to/ + * or the "var" or "replacement" in :@var@replacement+${var}@, up to and + * including the next unescaped delimiter. The delimiter, as well as the + * backslash or the dollar, can be escaped with a backslash. * - * Results: - * A string of all the words modified appropriately. - * If length is specified, return the string length of the buffer - * If flags is specified and the last character of the pattern is a - * $ set the VAR_MATCH_END bit of flags. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- + * Return the parsed (and possibly expanded) string, or NULL if no delimiter + * was found. On successful return, the parsing position pp points right + * after the delimiter. The delimiter is not included in the returned + * value though. */ static char * -VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, - VarPattern_Flags flags, const char **tstr, int delim, - VarPattern_Flags *vflags, int *length, VarPattern *pattern) -{ - const char *cp; - char *rstr; +ParseModifierPart( + const char **pp, /* The parsing position, updated upon return */ + int delim, /* Parsing stops at this delimiter */ + VarEvalFlags eflags, /* Flags for evaluating nested variables; + * if VARE_WANTRES is not set, the text is + * only parsed */ + GNode *ctxt, /* For looking up nested variables */ + size_t *out_length, /* Optionally stores the length of the returned + * string, just to save another strlen call. */ + VarPatternFlags *out_pflags,/* For the first part of the :S modifier, + * sets the VARP_ANCHOR_END flag if the last + * character of the pattern is a $. */ + ModifyWord_SubstArgs *subst /* For the second part of the :S modifier, + * allow ampersands to be escaped and replace + * unescaped ampersands with subst->lhs. */ +) { Buffer buf; - int junk; - int errnum = flags & VARF_UNDEFERR; + const char *p; + char *rstr; Buf_Init(&buf, 0); - if (length == NULL) - length = &junk; -#define IS_A_MATCH(cp, delim) \ - ((cp[0] == '\\') && ((cp[1] == delim) || \ - (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) - /* * Skim through until the matching delimiter is found; * pick up variable substitutions on the way. Also allow * backslashes to quote the delimiter, $, and \, but don't * touch other backslashes. */ - for (cp = *tstr; *cp && (*cp != delim); cp++) { - if (IS_A_MATCH(cp, delim)) { - Buf_AddByte(&buf, cp[1]); - cp++; - } else if (*cp == '$') { - if (cp[1] == delim) { - if (vflags == NULL) - Buf_AddByte(&buf, *cp); - else - /* - * Unescaped $ at end of pattern => anchor - * pattern at end. - */ - *vflags |= VAR_MATCH_END; - } else { - if (vflags == NULL || (*vflags & VAR_NOSUBST) == 0) { - char *cp2; - int len; - void *freeIt; + p = *pp; + while (*p != '\0' && *p != delim) { + const char *varstart; - /* - * If unescaped dollar sign not before the - * delimiter, assume it's a variable - * substitution and recurse. - */ - cp2 = Var_Parse(cp, ctxt, errnum | (flags & VARF_WANTRES), - &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); - free(freeIt); - cp += len - 1; - } else { - const char *cp2 = &cp[1]; + Boolean is_escaped = p[0] == '\\' && ( + p[1] == delim || p[1] == '\\' || p[1] == '$' || + (p[1] == '&' && subst != NULL)); + if (is_escaped) { + Buf_AddByte(&buf, p[1]); + p += 2; + continue; + } - if (*cp2 == PROPEN || *cp2 == BROPEN) { - /* - * Find the end of this variable reference - * and suck it in without further ado. - * It will be interpreted later. - */ - int have = *cp2; - int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; - int depth = 1; + if (*p != '$') { /* Unescaped, simple text */ + if (subst != NULL && *p == '&') + Buf_AddBytes(&buf, subst->lhs, subst->lhsLen); + else + Buf_AddByte(&buf, *p); + p++; + continue; + } - for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { - if (cp2[-1] != '\\') { - if (*cp2 == have) - ++depth; - if (*cp2 == want) - --depth; - } - } - Buf_AddBytes(&buf, cp2 - cp, cp); - cp = --cp2; - } else - Buf_AddByte(&buf, *cp); + if (p[1] == delim) { /* Unescaped $ at end of pattern */ + if (out_pflags != NULL) + *out_pflags |= VARP_ANCHOR_END; + else + Buf_AddByte(&buf, *p); + p++; + continue; + } + + if (eflags & VARE_WANTRES) { /* Nested variable, evaluated */ + const char *cp2; + int len; + void *freeIt; + VarEvalFlags nested_eflags = eflags & ~(unsigned)VARE_ASSIGN; + + cp2 = Var_Parse(p, ctxt, nested_eflags, &len, &freeIt); + Buf_AddStr(&buf, cp2); + free(freeIt); + p += len; + continue; + } + + /* XXX: This whole block is very similar to Var_Parse without + * VARE_WANTRES. There may be subtle edge cases though that are + * not yet covered in the unit tests and that are parsed differently, + * depending on whether they are evaluated or not. + * + * This subtle difference is not documented in the manual page, + * neither is the difference between parsing :D and :M documented. + * No code should ever depend on these details, but who knows. */ + + varstart = p; /* Nested variable, only parsed */ + if (p[1] == PROPEN || p[1] == BROPEN) { + /* + * Find the end of this variable reference + * and suck it in without further ado. + * It will be interpreted later. + */ + int have = p[1]; + int want = have == PROPEN ? PRCLOSE : BRCLOSE; + int depth = 1; + + for (p += 2; *p != '\0' && depth > 0; p++) { + if (p[-1] != '\\') { + if (*p == have) + depth++; + if (*p == want) + depth--; } } - } else if (pattern && *cp == '&') - Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs); - else - Buf_AddByte(&buf, *cp); + Buf_AddBytesBetween(&buf, varstart, p); + } else { + Buf_AddByte(&buf, *varstart); + p++; + } } - if (*cp != delim) { - *tstr = cp; - *length = 0; + if (*p != delim) { + *pp = p; return NULL; } - *tstr = ++cp; - *length = Buf_Size(&buf); + *pp = ++p; + if (out_length != NULL) + *out_length = Buf_Size(&buf); + rstr = Buf_Destroy(&buf, FALSE); - if (DEBUG(VAR)) - fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr); + VAR_DEBUG("Modifier part: \"%s\"\n", rstr); return rstr; } -/*- - *----------------------------------------------------------------------- - * VarQuote -- - * Quote shell meta-characters and space characters in the string - * if quoteDollar is set, also quote and double any '$' characters. - * - * Results: - * The quoted string - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Quote shell meta-characters and space characters in the string. + * If quoteDollar is set, also quote and double any '$' characters. */ static char * -VarQuote(char *str, Boolean quoteDollar) +VarQuote(const char *str, Boolean quoteDollar) { - - Buffer buf; - const char *newline; - size_t nlen; - - if ((newline = Shell_GetNewline()) == NULL) - newline = "\\\n"; - nlen = strlen(newline); - + char *res; + Buffer buf; Buf_Init(&buf, 0); for (; *str != '\0'; str++) { if (*str == '\n') { - Buf_AddBytes(&buf, nlen, newline); + const char *newline = Shell_GetNewline(); + if (newline == NULL) + newline = "\\\n"; + Buf_AddStr(&buf, newline); continue; } if (isspace((unsigned char)*str) || ismeta((unsigned char)*str)) Buf_AddByte(&buf, '\\'); Buf_AddByte(&buf, *str); if (quoteDollar && *str == '$') - Buf_AddBytes(&buf, 2, "\\$"); + Buf_AddStr(&buf, "\\$"); } - str = Buf_Destroy(&buf, FALSE); - if (DEBUG(VAR)) - fprintf(debug_file, "QuoteMeta: [%s]\n", str); - return str; + res = Buf_Destroy(&buf, FALSE); + VAR_DEBUG("QuoteMeta: [%s]\n", res); + return res; } -/*- - *----------------------------------------------------------------------- - * VarHash -- - * Hash the string using the MurmurHash3 algorithm. - * Output is computed using 32bit Little Endian arithmetic. - * - * Input: - * str String to modify - * - * Results: - * Hash value of str, encoded as 8 hex digits. - * - * Side Effects: - * None. - * - *----------------------------------------------------------------------- - */ +/* Compute the 32-bit hash of the given string, using the MurmurHash3 + * algorithm. Output is encoded as 8 hex digits, in Little Endian order. */ static char * VarHash(const char *str) { static const char hexdigits[16] = "0123456789abcdef"; - Buffer buf; - size_t len, len2; const unsigned char *ustr = (const unsigned char *)str; - uint32_t h, k, c1, c2; - h = 0x971e137bU; - c1 = 0x95543787U; - c2 = 0x2ad7eb25U; - len2 = strlen(str); + uint32_t h = 0x971e137bU; + uint32_t c1 = 0x95543787U; + uint32_t c2 = 0x2ad7eb25U; + size_t len2 = strlen(str); + char *buf; + size_t i; + + size_t len; for (len = len2; len; ) { - k = 0; + uint32_t k = 0; switch (len) { default: k = ((uint32_t)ustr[3] << 24) | ((uint32_t)ustr[2] << 16) | ((uint32_t)ustr[1] << 8) | (uint32_t)ustr[0]; len -= 4; ustr += 4; break; case 3: k |= (uint32_t)ustr[2] << 16; /* FALLTHROUGH */ case 2: k |= (uint32_t)ustr[1] << 8; /* FALLTHROUGH */ case 1: k |= (uint32_t)ustr[0]; len = 0; } c1 = c1 * 5 + 0x7b7d159cU; c2 = c2 * 5 + 0x6bce6396U; k *= c1; k = (k << 11) ^ (k >> 21); k *= c2; h = (h << 13) ^ (h >> 19); h = h * 5 + 0x52dce729U; h ^= k; } - h ^= len2; + h ^= (uint32_t)len2; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; - Buf_Init(&buf, 0); - for (len = 0; len < 8; ++len) { - Buf_AddByte(&buf, hexdigits[h & 15]); + buf = bmake_malloc(9); + for (i = 0; i < 8; i++) { + buf[i] = hexdigits[h & 0x0f]; h >>= 4; } - - return Buf_Destroy(&buf, FALSE); + buf[8] = '\0'; + return buf; } static char * -VarStrftime(const char *fmt, int zulu, time_t utc) +VarStrftime(const char *fmt, Boolean zulu, time_t tim) { char buf[BUFSIZ]; - if (!utc) - time(&utc); + if (!tim) + time(&tim); if (!*fmt) fmt = "%c"; - strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); + strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&tim) : localtime(&tim)); buf[sizeof(buf) - 1] = '\0'; return bmake_strdup(buf); } +/* The ApplyModifier functions all work in the same way. They get the + * current parsing position (pp) and parse the modifier from there. The + * modifier typically lasts until the next ':', or a closing '}' or ')' + * (taken from st->endc), or the end of the string (parse error). + * + * The high-level behavior of these functions is: + * + * 1. parse the modifier + * 2. evaluate the modifier + * 3. housekeeping + * + * Parsing the modifier + * + * If parsing succeeds, the parsing position *pp is updated to point to the + * first character following the modifier, which typically is either ':' or + * st->endc. + * + * If parsing fails because of a missing delimiter (as in the :S, :C or :@ + * modifiers), set st->missing_delim and return AMR_CLEANUP. + * + * If parsing fails because the modifier is unknown, return AMR_UNKNOWN to + * try the SysV modifier ${VAR:from=to} as fallback. This should only be + * done as long as there have been no side effects from evaluating nested + * variables, to avoid evaluating them more than once. In this case, the + * parsing position must not be updated. (XXX: Why not? The original parsing + * position is well-known in ApplyModifiers.) + * + * If parsing fails and the SysV modifier ${VAR:from=to} should not be used + * as a fallback, either issue an error message using Error or Parse_Error + * and then return AMR_CLEANUP, or return AMR_BAD for the default error + * message. Both of these return values will stop processing the variable + * expression. (XXX: As of 2020-08-23, evaluation of the whole string + * continues nevertheless after skipping a few bytes, which essentially is + * undefined behavior. Not in the sense of C, but still it's impossible to + * predict what happens in the parser.) + * + * Evaluating the modifier + * + * After parsing, the modifier is evaluated. The side effects from evaluating + * nested variable expressions in the modifier text often already happen + * during parsing though. + * + * Evaluating the modifier usually takes the current value of the variable + * expression from st->val, or the variable name from st->v->name and stores + * the result in st->newVal. + * + * If evaluating fails (as of 2020-08-23), an error message is printed using + * Error. This function has no side-effects, it really just prints the error + * message. Processing the expression continues as if everything were ok. + * XXX: This should be fixed by adding proper error handling to Var_Subst, + * Var_Parse, ApplyModifiers and ModifyWords. + * + * Housekeeping + * + * Some modifiers such as :D and :U turn undefined variables into useful + * variables (VAR_JUNK, VAR_KEEP). + * + * Some modifiers need to free some memory. + */ + typedef struct { - /* const parameters */ - int startc; - int endc; - Var *v; - GNode *ctxt; - int flags; - int *lengthPtr; - void **freePtr; + const char startc; /* '\0' or '{' or '(' */ + const char endc; /* '\0' or '}' or ')' */ + Var * const v; + GNode * const ctxt; + const VarEvalFlags eflags; - /* read-write */ - char *nstr; - const char *tstr; - const char *start; - const char *cp; /* Secondary pointer into str (place marker - * for tstr) */ - char termc; /* Character which terminated scan */ - int cnt; /* Used to count brace pairs when variable in - * in parens or braces */ - char delim; - int modifier; /* that we are processing */ - Var_Parse_State parsestate; /* Flags passed to helper functions */ + char *val; /* The old value of the expression, + * before applying the modifier, never NULL */ + char *newVal; /* The new value of the expression, + * after applying the modifier, never NULL */ + char missing_delim; /* For error reporting */ - /* result */ - char *newStr; /* New value to return */ + char sep; /* Word separator in expansions + * (see the :ts modifier) */ + Boolean oneBigWord; /* TRUE if some modifiers that otherwise split + * the variable value into words, like :S and + * :C, treat the variable value as a single big + * word, possibly containing spaces. */ } ApplyModifiersState; -/* we now have some modifiers with long names */ -#define STRMOD_MATCH(s, want, n) \ - (strncmp(s, want, n) == 0 && (s[n] == st->endc || s[n] == ':')) -#define STRMOD_MATCHX(s, want, n) \ - (strncmp(s, want, n) == 0 && \ - (s[n] == st->endc || s[n] == ':' || s[n] == '=')) -#define CHARMOD_MATCH(c) (c == st->endc || c == ':') +typedef enum { + AMR_OK, /* Continue parsing */ + AMR_UNKNOWN, /* Not a match, try other modifiers as well */ + AMR_BAD, /* Error out with "Bad modifier" message */ + AMR_CLEANUP /* Error out, with "Unfinished modifier" + * if st->missing_delim is set. */ +} ApplyModifierResult; -/* :@var@...${var}...@ */ +/* Test whether mod starts with modname, followed by a delimiter. */ static Boolean -ApplyModifier_At(ApplyModifiersState *st) { - VarLoop loop; - VarPattern_Flags vflags = VAR_NOSUBST; +ModMatch(const char *mod, const char *modname, char endc) +{ + size_t n = strlen(modname); + return strncmp(mod, modname, n) == 0 && + (mod[n] == endc || mod[n] == ':'); +} - st->cp = ++(st->tstr); - st->delim = '@'; - loop.tvar = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &loop.tvarLen, NULL); - if (loop.tvar == NULL) - return FALSE; +/* Test whether mod starts with modname, followed by a delimiter or '='. */ +static inline Boolean +ModMatchEq(const char *mod, const char *modname, char endc) +{ + size_t n = strlen(modname); + return strncmp(mod, modname, n) == 0 && + (mod[n] == endc || mod[n] == ':' || mod[n] == '='); +} - loop.str = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &loop.strLen, NULL); - if (loop.str == NULL) - return FALSE; +/* :@var@...${var}...@ */ +static ApplyModifierResult +ApplyModifier_Loop(const char **pp, ApplyModifiersState *st) +{ + ModifyWord_LoopArgs args; + char delim; + char prev_sep; + VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES; - st->termc = *st->cp; - st->delim = '\0'; + args.ctx = st->ctxt; - loop.flags = st->flags & (VARF_UNDEFERR | VARF_WANTRES); - loop.ctxt = st->ctxt; - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarLoopExpand, &loop); - Var_Delete(loop.tvar, st->ctxt); - free(loop.tvar); - free(loop.str); - return TRUE; + (*pp)++; /* Skip the first '@' */ + delim = '@'; + args.tvar = ParseModifierPart(pp, delim, eflags, + st->ctxt, NULL, NULL, NULL); + if (args.tvar == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + if (DEBUG(LINT) && strchr(args.tvar, '$') != NULL) { + Parse_Error(PARSE_FATAL, + "In the :@ modifier of \"%s\", the variable name \"%s\" " + "must not contain a dollar.", + st->v->name, args.tvar); + return AMR_CLEANUP; + } + + args.str = ParseModifierPart(pp, delim, eflags, + st->ctxt, NULL, NULL, NULL); + if (args.str == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + args.eflags = st->eflags & (VARE_UNDEFERR | VARE_WANTRES); + prev_sep = st->sep; + st->sep = ' '; /* XXX: should be st->sep for consistency */ + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Loop, &args); + st->sep = prev_sep; + Var_Delete(args.tvar, st->ctxt); + free(args.tvar); + free(args.str); + return AMR_OK; } /* :Ddefined or :Uundefined */ -static void -ApplyModifier_Defined(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Defined(const char **pp, ApplyModifiersState *st) { - Buffer buf; /* Buffer for patterns */ - int nflags; + Buffer buf; + const char *p; - if (st->flags & VARF_WANTRES) { - int wantres; - if (*st->tstr == 'U') - wantres = ((st->v->flags & VAR_JUNK) != 0); - else - wantres = ((st->v->flags & VAR_JUNK) == 0); - nflags = st->flags & ~VARF_WANTRES; - if (wantres) - nflags |= VARF_WANTRES; - } else - nflags = st->flags; + VarEvalFlags eflags = st->eflags & ~(unsigned)VARE_WANTRES; + if (st->eflags & VARE_WANTRES) { + if ((**pp == 'D') == !(st->v->flags & VAR_JUNK)) + eflags |= VARE_WANTRES; + } - /* - * Pass through tstr looking for 1) escaped delimiters, - * '$'s and backslashes (place the escaped character in - * uninterpreted) and 2) unescaped $'s that aren't before - * the delimiter (expand the variable substitution). - * The result is left in the Buffer buf. - */ Buf_Init(&buf, 0); - for (st->cp = st->tstr + 1; - *st->cp != st->endc && *st->cp != ':' && *st->cp != '\0'; - st->cp++) { - if (*st->cp == '\\' && - (st->cp[1] == ':' || st->cp[1] == '$' || st->cp[1] == st->endc || - st->cp[1] == '\\')) { - Buf_AddByte(&buf, st->cp[1]); - st->cp++; - } else if (*st->cp == '$') { - /* - * If unescaped dollar sign, assume it's a - * variable substitution and recurse. - */ - char *cp2; - int len; - void *freeIt; + p = *pp + 1; + while (*p != st->endc && *p != ':' && *p != '\0') { - cp2 = Var_Parse(st->cp, st->ctxt, nflags, &len, &freeIt); - Buf_AddBytes(&buf, strlen(cp2), cp2); + /* Escaped delimiter or other special character */ + if (*p == '\\') { + char c = p[1]; + if (c == st->endc || c == ':' || c == '$' || c == '\\') { + Buf_AddByte(&buf, c); + p += 2; + continue; + } + } + + /* Nested variable expression */ + if (*p == '$') { + const char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(p, st->ctxt, eflags, &len, &freeIt); + Buf_AddStr(&buf, cp2); free(freeIt); - st->cp += len - 1; - } else { - Buf_AddByte(&buf, *st->cp); + p += len; + continue; } + + /* Ordinary text */ + Buf_AddByte(&buf, *p); + p++; } + *pp = p; - st->termc = *st->cp; - - if ((st->v->flags & VAR_JUNK) != 0) + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - if (nflags & VARF_WANTRES) { - st->newStr = Buf_Destroy(&buf, FALSE); + if (eflags & VARE_WANTRES) { + st->newVal = Buf_Destroy(&buf, FALSE); } else { - st->newStr = st->nstr; + st->newVal = st->val; Buf_Destroy(&buf, TRUE); } + return AMR_OK; } /* :gmtime */ -static Boolean -ApplyModifier_Gmtime(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st) { time_t utc; - char *ep; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "gmtime", 6)) - return FALSE; - if (st->tstr[6] == '=') { - utc = strtoul(&st->tstr[7], &ep, 10); - st->cp = ep; + const char *mod = *pp; + if (!ModMatchEq(mod, "gmtime", st->endc)) + return AMR_UNKNOWN; + + if (mod[6] == '=') { + char *ep; + utc = (time_t)strtoul(mod + 7, &ep, 10); + *pp = ep; } else { utc = 0; - st->cp = st->tstr + 6; + *pp = mod + 6; } - st->newStr = VarStrftime(st->nstr, 1, utc); - st->termc = *st->cp; - return TRUE; + st->newVal = VarStrftime(st->val, TRUE, utc); + return AMR_OK; } /* :localtime */ static Boolean -ApplyModifier_Localtime(ApplyModifiersState *st) +ApplyModifier_Localtime(const char **pp, ApplyModifiersState *st) { time_t utc; - char *ep; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "localtime", 9)) - return FALSE; + const char *mod = *pp; + if (!ModMatchEq(mod, "localtime", st->endc)) + return AMR_UNKNOWN; - if (st->tstr[9] == '=') { - utc = strtoul(&st->tstr[10], &ep, 10); - st->cp = ep; + if (mod[9] == '=') { + char *ep; + utc = (time_t)strtoul(mod + 10, &ep, 10); + *pp = ep; } else { utc = 0; - st->cp = st->tstr + 9; + *pp = mod + 9; } - st->newStr = VarStrftime(st->nstr, 0, utc); - st->termc = *st->cp; - return TRUE; + st->newVal = VarStrftime(st->val, FALSE, utc); + return AMR_OK; } /* :hash */ -static Boolean -ApplyModifier_Hash(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Hash(const char **pp, ApplyModifiersState *st) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCH(st->tstr, "hash", 4)) - return FALSE; - st->newStr = VarHash(st->nstr); - st->cp = st->tstr + 4; - st->termc = *st->cp; - return TRUE; + if (!ModMatch(*pp, "hash", st->endc)) + return AMR_UNKNOWN; + + st->newVal = VarHash(st->val); + *pp += 4; + return AMR_OK; } /* :P */ -static void -ApplyModifier_Path(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Path(const char **pp, ApplyModifiersState *st) { GNode *gn; + char *path; - if ((st->v->flags & VAR_JUNK) != 0) + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; + gn = Targ_FindNode(st->v->name, TARG_NOCREATE); if (gn == NULL || gn->type & OP_NOPATH) { - st->newStr = NULL; + path = NULL; } else if (gn->path) { - st->newStr = bmake_strdup(gn->path); + path = bmake_strdup(gn->path); } else { - st->newStr = Dir_FindFile(st->v->name, Suff_FindPath(gn)); + Lst searchPath = Suff_FindPath(gn); + path = Dir_FindFile(st->v->name, searchPath); } - if (!st->newStr) - st->newStr = bmake_strdup(st->v->name); - st->cp = ++st->tstr; - st->termc = *st->tstr; + if (path == NULL) + path = bmake_strdup(st->v->name); + st->newVal = path; + + (*pp)++; + return AMR_OK; } /* :!cmd! */ -static Boolean -ApplyModifier_Exclam(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Exclam(const char **pp, ApplyModifiersState *st) { - const char *emsg; - VarPattern pattern; + char delim; + char *cmd; + const char *errfmt; - pattern.flags = 0; + (*pp)++; + delim = '!'; + cmd = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (cmd == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - st->delim = '!'; - emsg = NULL; - st->cp = ++st->tstr; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, NULL); - if (pattern.rhs == NULL) - return FALSE; - if (st->flags & VARF_WANTRES) - st->newStr = Cmd_Exec(pattern.rhs, &emsg); + errfmt = NULL; + if (st->eflags & VARE_WANTRES) + st->newVal = Cmd_Exec(cmd, &errfmt); else - st->newStr = varNoError; - free(UNCONST(pattern.rhs)); - if (emsg) - Error(emsg, st->nstr); - st->termc = *st->cp; - st->delim = '\0'; + st->newVal = varNoError; + free(cmd); + + if (errfmt != NULL) + Error(errfmt, st->val); /* XXX: why still return AMR_OK? */ + if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - return TRUE; + return AMR_OK; } -/* :range */ -static Boolean -ApplyModifier_Range(ApplyModifiersState *st) +/* The :range modifier generates an integer sequence as long as the words. + * The :range=7 modifier generates an integer sequence from 1 to 7. */ +static ApplyModifierResult +ApplyModifier_Range(const char **pp, ApplyModifiersState *st) { - int n; - char *ep; + size_t n; + Buffer buf; + size_t i; - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "range", 5)) - return FALSE; + const char *mod = *pp; + if (!ModMatchEq(mod, "range", st->endc)) + return AMR_UNKNOWN; - if (st->tstr[5] == '=') { - n = strtoul(&st->tstr[6], &ep, 10); - st->cp = ep; + if (mod[5] == '=') { + char *ep; + n = (size_t)strtoul(mod + 6, &ep, 10); + *pp = ep; } else { n = 0; - st->cp = st->tstr + 5; + *pp = mod + 5; } - st->newStr = VarRange(st->nstr, n); - st->termc = *st->cp; - return TRUE; + + if (n == 0) { + Words words = Str_Words(st->val, FALSE); + n = words.len; + Words_Free(words); + } + + Buf_Init(&buf, 0); + + for (i = 0; i < n; i++) { + if (i != 0) + Buf_AddByte(&buf, ' '); /* XXX: st->sep, for consistency */ + Buf_AddInt(&buf, 1 + (int)i); + } + + st->newVal = Buf_Destroy(&buf, FALSE); + return AMR_OK; } /* :Mpattern or :Npattern */ -static void -ApplyModifier_Match(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Match(const char **pp, ApplyModifiersState *st) { - char *pattern; - const char *endpat; /* points just after end of pattern */ - char *cp2; - Boolean copy; /* pattern should be, or has been, copied */ - Boolean needSubst; - int nest; + const char *mod = *pp; + Boolean copy = FALSE; /* pattern should be, or has been, copied */ + Boolean needSubst = FALSE; + const char *endpat; + char *pattern; + ModifyWordsCallback callback; - copy = FALSE; - needSubst = FALSE; - nest = 1; /* - * In the loop below, ignore ':' unless we are at - * (or back to) the original brace level. - * XXX This will likely not work right if $() and ${} - * are intermixed. + * In the loop below, ignore ':' unless we are at (or back to) the + * original brace level. + * XXX This will likely not work right if $() and ${} are intermixed. */ - for (st->cp = st->tstr + 1; - *st->cp != '\0' && !(*st->cp == ':' && nest == 1); - st->cp++) { - if (*st->cp == '\\' && - (st->cp[1] == ':' || st->cp[1] == st->endc || - st->cp[1] == st->startc)) { + int nest = 0; + const char *p; + for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) { + if (*p == '\\' && + (p[1] == ':' || p[1] == st->endc || p[1] == st->startc)) { if (!needSubst) copy = TRUE; - st->cp++; + p++; continue; } - if (*st->cp == '$') + if (*p == '$') needSubst = TRUE; - if (*st->cp == '(' || *st->cp == '{') - ++nest; - if (*st->cp == ')' || *st->cp == '}') { - --nest; - if (nest == 0) + if (*p == '(' || *p == '{') + nest++; + if (*p == ')' || *p == '}') { + nest--; + if (nest < 0) break; } } - st->termc = *st->cp; - endpat = st->cp; + *pp = p; + endpat = p; + if (copy) { - /* - * Need to compress the \:'s out of the pattern, so - * allocate enough room to hold the uncompressed - * pattern (note that st->cp started at st->tstr+1, so - * st->cp - st->tstr takes the null byte into account) and - * compress the pattern into the space. - */ - pattern = bmake_malloc(st->cp - st->tstr); - for (cp2 = pattern, st->cp = st->tstr + 1; - st->cp < endpat; - st->cp++, cp2++) { - if ((*st->cp == '\\') && (st->cp+1 < endpat) && - (st->cp[1] == ':' || st->cp[1] == st->endc)) - st->cp++; - *cp2 = *st->cp; + char *dst; + const char *src; + + /* Compress the \:'s out of the pattern. */ + pattern = bmake_malloc((size_t)(endpat - (mod + 1)) + 1); + dst = pattern; + src = mod + 1; + for (; src < endpat; src++, dst++) { + if (src[0] == '\\' && src + 1 < endpat && + /* XXX: st->startc is missing here; see above */ + (src[1] == ':' || src[1] == st->endc)) + src++; + *dst = *src; } - *cp2 = '\0'; - endpat = cp2; + *dst = '\0'; + endpat = dst; } else { - /* - * Either Var_Subst or VarModify will need a - * nul-terminated string soon, so construct one now. - */ - pattern = bmake_strndup(st->tstr+1, endpat - (st->tstr + 1)); + pattern = bmake_strsedup(mod + 1, endpat); } + if (needSubst) { /* pattern contains embedded '$', so use Var_Subst to expand it. */ - cp2 = pattern; - pattern = Var_Subst(NULL, cp2, st->ctxt, st->flags); - free(cp2); + char *old_pattern = pattern; + pattern = Var_Subst(pattern, st->ctxt, st->eflags); + free(old_pattern); } - if (DEBUG(VAR)) - fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", - st->v->name, st->nstr, pattern); - if (*st->tstr == 'M') { - st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarMatch, - pattern); - } else { - st->newStr = VarModify(st->ctxt, &st->parsestate, st->nstr, VarNoMatch, - pattern); - } + + VAR_DEBUG("Pattern[%s] for [%s] is [%s]\n", st->v->name, st->val, pattern); + + callback = mod[0] == 'M' ? ModifyWord_Match : ModifyWord_NoMatch; + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + callback, pattern); free(pattern); + return AMR_OK; } /* :S,from,to, */ -static Boolean -ApplyModifier_Subst(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Subst(const char **pp, ApplyModifiersState *st) { - VarPattern pattern; - Var_Parse_State tmpparsestate; + ModifyWord_SubstArgs args; + char *lhs, *rhs; + Boolean oneBigWord; - pattern.flags = 0; - tmpparsestate = st->parsestate; - st->delim = st->tstr[1]; - st->tstr += 2; + char delim = (*pp)[1]; + if (delim == '\0') { + Error("Missing delimiter for :S modifier"); + (*pp)++; + return AMR_CLEANUP; + } + *pp += 2; + + args.pflags = 0; + args.matched = FALSE; + /* * If pattern begins with '^', it is anchored to the * start of the word -- skip over it and flag pattern. */ - if (*st->tstr == '^') { - pattern.flags |= VAR_MATCH_START; - st->tstr += 1; + if (**pp == '^') { + args.pflags |= VARP_ANCHOR_START; + (*pp)++; } - st->cp = st->tstr; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &pattern.flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return FALSE; + lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + &args.lhsLen, &args.pflags, NULL); + if (lhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + args.lhs = lhs; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, &pattern); - if (pattern.rhs == NULL) - return FALSE; + rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + &args.rhsLen, NULL, &args); + if (rhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + args.rhs = rhs; - /* - * Check for global substitution. If 'g' after the final - * delimiter, substitution is global and is marked that - * way. - */ - for (;; st->cp++) { - switch (*st->cp) { + oneBigWord = st->oneBigWord; + for (;; (*pp)++) { + switch (**pp) { case 'g': - pattern.flags |= VAR_SUB_GLOBAL; + args.pflags |= VARP_SUB_GLOBAL; continue; case '1': - pattern.flags |= VAR_SUB_ONE; + args.pflags |= VARP_SUB_ONE; continue; case 'W': - tmpparsestate.oneBigWord = TRUE; + oneBigWord = TRUE; continue; } break; } - st->termc = *st->cp; - st->newStr = VarModify( - st->ctxt, &tmpparsestate, st->nstr, VarSubstitute, &pattern); + st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, + ModifyWord_Subst, &args); - /* Free the two strings. */ - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - st->delim = '\0'; - return TRUE; + free(lhs); + free(rhs); + return AMR_OK; } #ifndef NO_REGEX + /* :C,from,to, */ -static Boolean -ApplyModifier_Regex(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Regex(const char **pp, ApplyModifiersState *st) { - VarREPattern pattern; - char *re; - int error; - Var_Parse_State tmpparsestate; + char *re; + ModifyWord_SubstRegexArgs args; + Boolean oneBigWord; + int error; - pattern.flags = 0; - tmpparsestate = st->parsestate; - st->delim = st->tstr[1]; - st->tstr += 2; + char delim = (*pp)[1]; + if (delim == '\0') { + Error("Missing delimiter for :C modifier"); + (*pp)++; + return AMR_CLEANUP; + } - st->cp = st->tstr; + *pp += 2; - re = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (re == NULL) - return FALSE; + re = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (re == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - pattern.replace = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (pattern.replace == NULL) { + args.replace = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (args.replace == NULL) { free(re); - return FALSE; + st->missing_delim = delim; + return AMR_CLEANUP; } - for (;; st->cp++) { - switch (*st->cp) { + args.pflags = 0; + args.matched = FALSE; + oneBigWord = st->oneBigWord; + for (;; (*pp)++) { + switch (**pp) { case 'g': - pattern.flags |= VAR_SUB_GLOBAL; + args.pflags |= VARP_SUB_GLOBAL; continue; case '1': - pattern.flags |= VAR_SUB_ONE; + args.pflags |= VARP_SUB_ONE; continue; case 'W': - tmpparsestate.oneBigWord = TRUE; + oneBigWord = TRUE; continue; } break; } - st->termc = *st->cp; - - error = regcomp(&pattern.re, re, REG_EXTENDED); + error = regcomp(&args.re, re, REG_EXTENDED); free(re); if (error) { - *st->lengthPtr = st->cp - st->start + 1; - VarREError(error, &pattern.re, "RE substitution error"); - free(pattern.replace); - return FALSE; + VarREError(error, &args.re, "Regex compilation error"); + free(args.replace); + return AMR_CLEANUP; } - pattern.nsub = pattern.re.re_nsub + 1; - if (pattern.nsub < 1) - pattern.nsub = 1; - if (pattern.nsub > 10) - pattern.nsub = 10; - pattern.matches = bmake_malloc(pattern.nsub * sizeof(regmatch_t)); - st->newStr = VarModify( - st->ctxt, &tmpparsestate, st->nstr, VarRESubstitute, &pattern); - regfree(&pattern.re); - free(pattern.replace); - free(pattern.matches); - st->delim = '\0'; - return TRUE; + args.nsub = args.re.re_nsub + 1; + if (args.nsub > 10) + args.nsub = 10; + st->newVal = ModifyWords(st->ctxt, st->sep, oneBigWord, st->val, + ModifyWord_SubstRegex, &args); + regfree(&args.re); + free(args.replace); + return AMR_OK; } #endif -/* :tA, :tu, :tl, etc. */ -static Boolean -ApplyModifier_To(ApplyModifiersState *st) +static void +ModifyWord_Copy(const char *word, SepBuf *buf, void *data MAKE_ATTR_UNUSED) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (st->tstr[1] != st->endc && st->tstr[1] != ':') { - if (st->tstr[1] == 's') { - /* Use the char (if any) at st->tstr[2] as the word separator. */ - VarPattern pattern; + SepBuf_AddStr(buf, word); +} - if (st->tstr[2] != st->endc && - (st->tstr[3] == st->endc || st->tstr[3] == ':')) { - /* ":ts" or - * ":ts:" */ - st->parsestate.varSpace = st->tstr[2]; - st->cp = st->tstr + 3; - } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { - /* ":ts" or ":ts:" */ - st->parsestate.varSpace = 0; /* no separator */ - st->cp = st->tstr + 2; - } else if (st->tstr[2] == '\\') { - const char *xp = &st->tstr[3]; - int base = 8; /* assume octal */ +/* :ts */ +static ApplyModifierResult +ApplyModifier_ToSep(const char **pp, ApplyModifiersState *st) +{ + /* XXX: pp points to the 's', for historic reasons only. + * Changing this will influence the error messages. */ + const char *sep = *pp + 1; - switch (st->tstr[3]) { - case 'n': - st->parsestate.varSpace = '\n'; - st->cp = st->tstr + 4; - break; - case 't': - st->parsestate.varSpace = '\t'; - st->cp = st->tstr + 4; - break; - case 'x': - base = 16; - xp++; - goto get_numeric; - case '0': - base = 0; - goto get_numeric; - default: - if (isdigit((unsigned char)st->tstr[3])) { - char *ep; - get_numeric: - st->parsestate.varSpace = strtoul(xp, &ep, base); - if (*ep != ':' && *ep != st->endc) - return FALSE; - st->cp = ep; - } else { - /* ":ts". */ - return FALSE; - } - break; - } - } else { - /* Found ":ts". */ - return FALSE; - } + /* ":ts" or ":ts:" */ + if (sep[0] != st->endc && (sep[1] == st->endc || sep[1] == ':')) { + st->sep = sep[0]; + *pp = sep + 1; + goto ok; + } - st->termc = *st->cp; + /* ":ts" or ":ts:" */ + if (sep[0] == st->endc || sep[0] == ':') { + st->sep = '\0'; /* no separator */ + *pp = sep; + goto ok; + } - /* - * We cannot be certain that VarModify will be used - even if there - * is a subsequent modifier, so do a no-op VarSubstitute now to for - * str to be re-expanded without the spaces. - */ - pattern.flags = VAR_SUB_ONE; - pattern.lhs = pattern.rhs = "\032"; - pattern.leftLen = pattern.rightLen = 1; + /* ":ts". */ + if (sep[0] != '\\') + return AMR_BAD; - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarSubstitute, &pattern); - } else if (st->tstr[2] == st->endc || st->tstr[2] == ':') { - /* Check for two-character options: ":tu", ":tl" */ - if (st->tstr[1] == 'A') { /* absolute path */ - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarRealpath, NULL); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'u') { - char *dp = bmake_strdup(st->nstr); - for (st->newStr = dp; *dp; dp++) - *dp = toupper((unsigned char)*dp); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'l') { - char *dp = bmake_strdup(st->nstr); - for (st->newStr = dp; *dp; dp++) - *dp = tolower((unsigned char)*dp); - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else if (st->tstr[1] == 'W' || st->tstr[1] == 'w') { - st->parsestate.oneBigWord = (st->tstr[1] == 'W'); - st->newStr = st->nstr; - st->cp = st->tstr + 2; - st->termc = *st->cp; - } else { - /* Found ":t:" or ":t". */ - return FALSE; - } - } else { - /* Found ":t". */ - return FALSE; - } - } else { - /* Found ":t" or ":t:". */ - return FALSE; + /* ":ts\n" */ + if (sep[1] == 'n') { + st->sep = '\n'; + *pp = sep + 2; + goto ok; } - return TRUE; + + /* ":ts\t" */ + if (sep[1] == 't') { + st->sep = '\t'; + *pp = sep + 2; + goto ok; + } + + /* ":ts\x40" or ":ts\100" */ + { + const char *numStart = sep + 1; + int base = 8; /* assume octal */ + char *end; + + if (sep[1] == 'x') { + base = 16; + numStart++; + } else if (!isdigit((unsigned char)sep[1])) + return AMR_BAD; /* ":ts". */ + + st->sep = (char)strtoul(numStart, &end, base); + if (*end != ':' && *end != st->endc) + return AMR_BAD; + *pp = end; + } + +ok: + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Copy, NULL); + return AMR_OK; } +/* :tA, :tu, :tl, :ts, etc. */ +static ApplyModifierResult +ApplyModifier_To(const char **pp, ApplyModifiersState *st) +{ + const char *mod = *pp; + assert(mod[0] == 't'); + + *pp = mod + 1; /* make sure it is set */ + if (mod[1] == st->endc || mod[1] == ':' || mod[1] == '\0') + return AMR_BAD; /* Found ":t" or ":t:". */ + + if (mod[1] == 's') + return ApplyModifier_ToSep(pp, st); + + if (mod[2] != st->endc && mod[2] != ':') + return AMR_BAD; /* Found ":t". */ + + /* Check for two-character options: ":tu", ":tl" */ + if (mod[1] == 'A') { /* absolute path */ + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_Realpath, NULL); + *pp = mod + 2; + return AMR_OK; + } + + if (mod[1] == 'u') { + size_t i; + size_t len = strlen(st->val); + st->newVal = bmake_malloc(len + 1); + for (i = 0; i < len + 1; i++) + st->newVal[i] = (char)toupper((unsigned char)st->val[i]); + *pp = mod + 2; + return AMR_OK; + } + + if (mod[1] == 'l') { + size_t i; + size_t len = strlen(st->val); + st->newVal = bmake_malloc(len + 1); + for (i = 0; i < len + 1; i++) + st->newVal[i] = (char)tolower((unsigned char)st->val[i]); + *pp = mod + 2; + return AMR_OK; + } + + if (mod[1] == 'W' || mod[1] == 'w') { + st->oneBigWord = mod[1] == 'W'; + st->newVal = st->val; + *pp = mod + 2; + return AMR_OK; + } + + /* Found ":t:" or ":t". */ + return AMR_BAD; +} + /* :[#], :[1], etc. */ -static int -ApplyModifier_Words(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Words(const char **pp, ApplyModifiersState *st) { - /* - * Look for the closing ']', recursively - * expanding any embedded variables. - * - * estr is a pointer to the expanded result, - * which we must free(). - */ + char delim; char *estr; + char *ep; + int first, last; - st->cp = st->tstr + 1; /* point to char after '[' */ - st->delim = ']'; /* look for closing ']' */ - estr = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, NULL, NULL); - if (estr == NULL) - return 'c'; /* report missing ']' */ - /* now st->cp points just after the closing ']' */ - st->delim = '\0'; - if (st->cp[0] != ':' && st->cp[0] != st->endc) { - /* Found junk after ']' */ - free(estr); - return 'b'; + (*pp)++; /* skip the '[' */ + delim = ']'; /* look for closing ']' */ + estr = ParseModifierPart(pp, delim, st->eflags, st->ctxt, + NULL, NULL, NULL); + if (estr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; } - if (estr[0] == '\0') { - /* Found empty square brackets in ":[]". */ - free(estr); - return 'b'; - } else if (estr[0] == '#' && estr[1] == '\0') { - /* Found ":[#]" */ - /* - * We will need enough space for the decimal - * representation of an int. We calculate the - * space needed for the octal representation, - * and add enough slop to cope with a '-' sign - * (which should never be needed) and a '\0' - * string terminator. - */ - int newStrSize = (sizeof(int) * CHAR_BIT + 2) / 3 + 2; + /* now *pp points just after the closing ']' */ + if (**pp != ':' && **pp != st->endc) + goto bad_modifier; /* Found junk after ']' */ - st->newStr = bmake_malloc(newStrSize); - if (st->parsestate.oneBigWord) { - strncpy(st->newStr, "1", newStrSize); + if (estr[0] == '\0') + goto bad_modifier; /* empty square brackets in ":[]". */ + + if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */ + if (st->oneBigWord) { + st->newVal = bmake_strdup("1"); } else { - /* XXX: brk_string() is a rather expensive - * way of counting words. */ - char **av; - char *as; - int ac; + Buffer buf; - av = brk_string(st->nstr, &ac, FALSE, &as); - snprintf(st->newStr, newStrSize, "%d", ac); - free(as); - free(av); + Words words = Str_Words(st->val, FALSE); + size_t ac = words.len; + Words_Free(words); + + Buf_Init(&buf, 4); /* 3 digits + '\0' is usually enough */ + Buf_AddInt(&buf, (int)ac); + st->newVal = Buf_Destroy(&buf, FALSE); } - st->termc = *st->cp; - free(estr); - return 0; - } else if (estr[0] == '*' && estr[1] == '\0') { + goto ok; + } + + if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */ - st->parsestate.oneBigWord = TRUE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else if (estr[0] == '@' && estr[1] == '\0') { + st->oneBigWord = TRUE; + st->newVal = st->val; + goto ok; + } + + if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */ - st->parsestate.oneBigWord = FALSE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else { - char *ep; - /* - * We expect estr to contain a single - * integer for :[N], or two integers - * separated by ".." for :[start..end]. - */ - VarSelectWords_t seldata = { 0, 0 }; + st->oneBigWord = FALSE; + st->newVal = st->val; + goto ok; + } - seldata.start = strtol(estr, &ep, 0); - if (ep == estr) { - /* Found junk instead of a number */ - free(estr); - return 'b'; - } else if (ep[0] == '\0') { - /* Found only one integer in :[N] */ - seldata.end = seldata.start; - } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { - /* Expecting another integer after ".." */ - ep += 2; - seldata.end = strtol(ep, &ep, 0); - if (ep[0] != '\0') { - /* Found junk after ".." */ - free(estr); - return 'b'; - } - } else { - /* Found junk instead of ".." */ - free(estr); - return 'b'; - } - /* - * Now seldata is properly filled in, - * but we still have to check for 0 as - * a special case. - */ - if (seldata.start == 0 && seldata.end == 0) { - /* ":[0]" or perhaps ":[0..0]" */ - st->parsestate.oneBigWord = TRUE; - st->newStr = st->nstr; - st->termc = *st->cp; - free(estr); - return 0; - } else if (seldata.start == 0 || seldata.end == 0) { - /* ":[0..N]" or ":[N..0]" */ - free(estr); - return 'b'; - } - /* Normal case: select the words described by seldata. */ - st->newStr = VarSelectWords( - st->ctxt, &st->parsestate, st->nstr, &seldata); + /* + * We expect estr to contain a single integer for :[N], or two integers + * separated by ".." for :[start..end]. + */ + first = (int)strtol(estr, &ep, 0); + if (ep == estr) /* Found junk instead of a number */ + goto bad_modifier; - st->termc = *st->cp; - free(estr); - return 0; + if (ep[0] == '\0') { /* Found only one integer in :[N] */ + last = first; + } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { + /* Expecting another integer after ".." */ + ep += 2; + last = (int)strtol(ep, &ep, 0); + if (ep[0] != '\0') /* Found junk after ".." */ + goto bad_modifier; + } else + goto bad_modifier; /* Found junk instead of ".." */ + + /* + * Now seldata is properly filled in, but we still have to check for 0 as + * a special case. + */ + if (first == 0 && last == 0) { + /* ":[0]" or perhaps ":[0..0]" */ + st->oneBigWord = TRUE; + st->newVal = st->val; + goto ok; } + + /* ":[0..N]" or ":[N..0]" */ + if (first == 0 || last == 0) + goto bad_modifier; + + /* Normal case: select the words described by seldata. */ + st->newVal = VarSelectWords(st->sep, st->oneBigWord, st->val, first, last); + +ok: + free(estr); + return AMR_OK; + +bad_modifier: + free(estr); + return AMR_BAD; } -/* :O or :Ox */ -static Boolean -ApplyModifier_Order(ApplyModifiersState *st) +static int +str_cmp_asc(const void *a, const void *b) { - char otype; + return strcmp(*(const char * const *)a, *(const char * const *)b); +} - st->cp = st->tstr + 1; /* skip to the rest in any case */ - if (st->tstr[1] == st->endc || st->tstr[1] == ':') { - otype = 's'; - st->termc = *st->cp; - } else if ((st->tstr[1] == 'r' || st->tstr[1] == 'x') && - (st->tstr[2] == st->endc || st->tstr[2] == ':')) { - otype = st->tstr[1]; - st->cp = st->tstr + 2; - st->termc = *st->cp; +static int +str_cmp_desc(const void *a, const void *b) +{ + return strcmp(*(const char * const *)b, *(const char * const *)a); +} + +/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */ +static ApplyModifierResult +ApplyModifier_Order(const char **pp, ApplyModifiersState *st) +{ + const char *mod = (*pp)++; /* skip past the 'O' in any case */ + + Words words = Str_Words(st->val, FALSE); + + if (mod[1] == st->endc || mod[1] == ':') { + /* :O sorts ascending */ + qsort(words.words, words.len, sizeof(char *), str_cmp_asc); + + } else if ((mod[1] == 'r' || mod[1] == 'x') && + (mod[2] == st->endc || mod[2] == ':')) { + (*pp)++; + + if (mod[1] == 'r') { + /* :Or sorts descending */ + qsort(words.words, words.len, sizeof(char *), str_cmp_desc); + + } else { + /* :Ox shuffles + * + * We will use [ac..2] range for mod factors. This will produce + * random numbers in [(ac-1)..0] interval, and minimal + * reasonable value for mod factor is 2 (the mod 1 will produce + * 0 with probability 1). + */ + size_t i; + for (i = words.len - 1; i > 0; i--) { + size_t rndidx = (size_t)random() % (i + 1); + char *t = words.words[i]; + words.words[i] = words.words[rndidx]; + words.words[rndidx] = t; + } + } } else { - return FALSE; + Words_Free(words); + return AMR_BAD; } - st->newStr = VarOrder(st->nstr, otype); - return TRUE; + + st->newVal = Words_JoinFree(words); + return AMR_OK; } /* :? then : else */ -static Boolean -ApplyModifier_IfElse(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st) { - VarPattern pattern; - Boolean value; - int cond_rc; - VarPattern_Flags lhs_flags, rhs_flags; + char delim; + char *then_expr, *else_expr; - /* find ':', and then substitute accordingly */ - if (st->flags & VARF_WANTRES) { + Boolean value = FALSE; + VarEvalFlags then_eflags = st->eflags & ~(unsigned)VARE_WANTRES; + VarEvalFlags else_eflags = st->eflags & ~(unsigned)VARE_WANTRES; + + int cond_rc = COND_PARSE; /* anything other than COND_INVALID */ + if (st->eflags & VARE_WANTRES) { cond_rc = Cond_EvalExpression(NULL, st->v->name, &value, 0, FALSE); - if (cond_rc == COND_INVALID) { - lhs_flags = rhs_flags = VAR_NOSUBST; - } else if (value) { - lhs_flags = 0; - rhs_flags = VAR_NOSUBST; - } else { - lhs_flags = VAR_NOSUBST; - rhs_flags = 0; - } - } else { - /* we are just consuming and discarding */ - cond_rc = value = 0; - lhs_flags = rhs_flags = VAR_NOSUBST; + if (cond_rc != COND_INVALID && value) + then_eflags |= VARE_WANTRES; + if (cond_rc != COND_INVALID && !value) + else_eflags |= VARE_WANTRES; } - pattern.flags = 0; - st->cp = ++st->tstr; - st->delim = ':'; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &lhs_flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return FALSE; + (*pp)++; /* skip past the '?' */ + delim = ':'; + then_expr = ParseModifierPart(pp, delim, then_eflags, st->ctxt, + NULL, NULL, NULL); + if (then_expr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - /* BROPEN or PROPEN */ - st->delim = st->endc; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &rhs_flags, &pattern.rightLen, NULL); - if (pattern.rhs == NULL) - return FALSE; + delim = st->endc; /* BRCLOSE or PRCLOSE */ + else_expr = ParseModifierPart(pp, delim, else_eflags, st->ctxt, + NULL, NULL, NULL); + if (else_expr == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - st->termc = *--st->cp; - st->delim = '\0'; + (*pp)--; if (cond_rc == COND_INVALID) { Error("Bad conditional expression `%s' in %s?%s:%s", - st->v->name, st->v->name, pattern.lhs, pattern.rhs); - return FALSE; + st->v->name, st->v->name, then_expr, else_expr); + return AMR_CLEANUP; } if (value) { - st->newStr = UNCONST(pattern.lhs); - free(UNCONST(pattern.rhs)); + st->newVal = then_expr; + free(else_expr); } else { - st->newStr = UNCONST(pattern.rhs); - free(UNCONST(pattern.lhs)); + st->newVal = else_expr; + free(then_expr); } if (st->v->flags & VAR_JUNK) st->v->flags |= VAR_KEEP; - return TRUE; + return AMR_OK; } -/* "::=", "::!=", "::+=", or "::?=" */ -static int -ApplyModifier_Assign(ApplyModifiersState *st) +/* + * The ::= modifiers actually assign a value to the variable. + * Their main purpose is in supporting modifiers of .for loop + * iterators and other obscure uses. They always expand to + * nothing. In a target rule that would otherwise expand to an + * empty line they can be preceded with @: to keep make happy. + * Eg. + * + * foo: .USE + * .for i in ${.TARGET} ${.TARGET:R}.gz + * @: ${t::=$i} + * @echo blah ${t:T} + * .endfor + * + * ::= Assigns as the new value of variable. + * ::?= Assigns as value of variable if + * it was not already set. + * ::+= Appends to variable. + * ::!= Assigns output of as the new value of + * variable. + */ +static ApplyModifierResult +ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) { - if (st->tstr[1] == '=' || - (st->tstr[2] == '=' && - (st->tstr[1] == '!' || st->tstr[1] == '+' || st->tstr[1] == '?'))) { - GNode *v_ctxt; /* context where v belongs */ - const char *emsg; - char *sv_name; - VarPattern pattern; - int how; - VarPattern_Flags vflags; + GNode *v_ctxt; + char *sv_name; + char delim; + char *val; - if (st->v->name[0] == 0) - return 'b'; + const char *mod = *pp; + const char *op = mod + 1; + if (!(op[0] == '=' || + (op[1] == '=' && + (op[0] == '!' || op[0] == '+' || op[0] == '?')))) + return AMR_UNKNOWN; /* "::" */ - v_ctxt = st->ctxt; - sv_name = NULL; - ++st->tstr; - if (st->v->flags & VAR_JUNK) { - /* - * We need to bmake_strdup() it incase - * VarGetPattern() recurses. - */ - sv_name = st->v->name; - st->v->name = bmake_strdup(st->v->name); - } else if (st->ctxt != VAR_GLOBAL) { - Var *gv = VarFind(st->v->name, st->ctxt, 0); - if (gv == NULL) - v_ctxt = VAR_GLOBAL; - else - VarFreeEnv(gv, TRUE); - } - switch ((how = *st->tstr)) { + if (st->v->name[0] == 0) { + *pp = mod + 1; + return AMR_BAD; + } + + v_ctxt = st->ctxt; /* context where v belongs */ + sv_name = NULL; + if (st->v->flags & VAR_JUNK) { + /* + * We need to bmake_strdup() it in case ParseModifierPart() recurses. + */ + sv_name = st->v->name; + st->v->name = bmake_strdup(st->v->name); + } else if (st->ctxt != VAR_GLOBAL) { + Var *gv = VarFind(st->v->name, st->ctxt, 0); + if (gv == NULL) + v_ctxt = VAR_GLOBAL; + else + VarFreeEnv(gv, TRUE); + } + + switch (op[0]) { + case '+': + case '?': + case '!': + *pp = mod + 3; + break; + default: + *pp = mod + 2; + break; + } + + delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; + val = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (st->v->flags & VAR_JUNK) { + /* restore original name */ + free(st->v->name); + st->v->name = sv_name; + } + if (val == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } + + (*pp)--; + + if (st->eflags & VARE_WANTRES) { + switch (op[0]) { case '+': - case '?': - case '!': - st->cp = &st->tstr[2]; + Var_Append(st->v->name, val, v_ctxt); break; - default: - st->cp = ++st->tstr; + case '!': { + const char *errfmt; + char *cmd_output = Cmd_Exec(val, &errfmt); + if (errfmt) + Error(errfmt, val); + else + Var_Set(st->v->name, cmd_output, v_ctxt); + free(cmd_output); break; } - st->delim = st->startc == PROPEN ? PRCLOSE : BRCLOSE; - pattern.flags = 0; - - vflags = (st->flags & VARF_WANTRES) ? 0 : VAR_NOSUBST; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &vflags, &pattern.rightLen, NULL); - if (st->v->flags & VAR_JUNK) { - /* restore original name */ - free(st->v->name); - st->v->name = sv_name; - } - if (pattern.rhs == NULL) - return 'c'; - - st->termc = *--st->cp; - st->delim = '\0'; - - if (st->flags & VARF_WANTRES) { - switch (how) { - case '+': - Var_Append(st->v->name, pattern.rhs, v_ctxt); + case '?': + if (!(st->v->flags & VAR_JUNK)) break; - case '!': - st->newStr = Cmd_Exec(pattern.rhs, &emsg); - if (emsg) - Error(emsg, st->nstr); - else - Var_Set(st->v->name, st->newStr, v_ctxt); - free(st->newStr); - break; - case '?': - if ((st->v->flags & VAR_JUNK) == 0) - break; - /* FALLTHROUGH */ - default: - Var_Set(st->v->name, pattern.rhs, v_ctxt); - break; - } + /* FALLTHROUGH */ + default: + Var_Set(st->v->name, val, v_ctxt); + break; } - free(UNCONST(pattern.rhs)); - st->newStr = varNoError; - return 0; } - return 'd'; /* "::" */ + free(val); + st->newVal = varNoError; /* XXX: varNoError is kind of an error, + * the intention here is to just return + * an empty string. */ + return AMR_OK; } /* remember current value */ -static Boolean -ApplyModifier_Remember(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_Remember(const char **pp, ApplyModifiersState *st) { - st->cp = st->tstr + 1; /* make sure it is set */ - if (!STRMOD_MATCHX(st->tstr, "_", 1)) - return FALSE; + const char *mod = *pp; + if (!ModMatchEq(mod, "_", st->endc)) + return AMR_UNKNOWN; - if (st->tstr[1] == '=') { - char *np; - int n; - - st->cp++; - n = strcspn(st->cp, ":)}"); - np = bmake_strndup(st->cp, n + 1); - np[n] = '\0'; - st->cp = st->tstr + 2 + n; - Var_Set(np, st->nstr, st->ctxt); - free(np); + if (mod[1] == '=') { + size_t n = strcspn(mod + 2, ":)}"); + char *name = bmake_strldup(mod + 2, n); + Var_Set(name, st->val, st->ctxt); + free(name); + *pp = mod + 2 + n; } else { - Var_Set("_", st->nstr, st->ctxt); + Var_Set("_", st->val, st->ctxt); + *pp = mod + 1; } - st->newStr = st->nstr; - st->termc = *st->cp; - return TRUE; + st->newVal = st->val; + return AMR_OK; } +/* Apply the given function to each word of the variable value. */ +static ApplyModifierResult +ApplyModifier_WordFunc(const char **pp, ApplyModifiersState *st, + ModifyWordsCallback modifyWord) +{ + char delim = (*pp)[1]; + if (delim != st->endc && delim != ':') + return AMR_UNKNOWN; + + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, + st->val, modifyWord, NULL); + (*pp)++; + return AMR_OK; +} + #ifdef SYSVVARSUB /* :from=to */ -static int -ApplyModifier_SysV(ApplyModifiersState *st) +static ApplyModifierResult +ApplyModifier_SysV(const char **pp, ApplyModifiersState *st) { - /* - * This can either be a bogus modifier or a System-V - * substitution command. - */ - VarPattern pattern; - Boolean eqFound = FALSE; + char delim; + char *lhs, *rhs; - pattern.flags = 0; + const char *mod = *pp; + Boolean eqFound = FALSE; /* * First we make a pass through the string trying * to verify it is a SYSV-make-style translation: * it must be: =) */ - st->cp = st->tstr; - st->cnt = 1; - while (*st->cp != '\0' && st->cnt) { - if (*st->cp == '=') { + int nest = 1; + const char *next = mod; + while (*next != '\0' && nest > 0) { + if (*next == '=') { eqFound = TRUE; /* continue looking for st->endc */ - } else if (*st->cp == st->endc) - st->cnt--; - else if (*st->cp == st->startc) - st->cnt++; - if (st->cnt) - st->cp++; + } else if (*next == st->endc) + nest--; + else if (*next == st->startc) + nest++; + if (nest > 0) + next++; } - if (*st->cp != st->endc || !eqFound) - return 0; + if (*next != st->endc || !eqFound) + return AMR_UNKNOWN; - st->delim = '='; - st->cp = st->tstr; - pattern.lhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - &pattern.flags, &pattern.leftLen, NULL); - if (pattern.lhs == NULL) - return 'c'; + delim = '='; + *pp = mod; + lhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (lhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } - st->delim = st->endc; - pattern.rhs = VarGetPattern( - st->ctxt, &st->parsestate, st->flags, &st->cp, st->delim, - NULL, &pattern.rightLen, &pattern); - if (pattern.rhs == NULL) - return 'c'; + delim = st->endc; + rhs = ParseModifierPart(pp, delim, st->eflags, st->ctxt, NULL, NULL, NULL); + if (rhs == NULL) { + st->missing_delim = delim; + return AMR_CLEANUP; + } /* * SYSV modifications happen through the whole * string. Note the pattern is anchored at the end. */ - st->termc = *--st->cp; - st->delim = '\0'; - if (pattern.leftLen == 0 && *st->nstr == '\0') { - st->newStr = st->nstr; /* special case */ + (*pp)--; + if (lhs[0] == '\0' && *st->val == '\0') { + st->newVal = st->val; /* special case */ } else { - st->newStr = VarModify( - st->ctxt, &st->parsestate, st->nstr, VarSYSVMatch, &pattern); + ModifyWord_SYSVSubstArgs args = {st->ctxt, lhs, rhs}; + st->newVal = ModifyWords(st->ctxt, st->sep, st->oneBigWord, st->val, + ModifyWord_SYSVSubst, &args); } - free(UNCONST(pattern.lhs)); - free(UNCONST(pattern.rhs)); - return '='; + free(lhs); + free(rhs); + return AMR_OK; } #endif -/* - * Now we need to apply any modifiers the user wants applied. - * These are: - * :M words which match the given . - * is of the standard file - * wildcarding form. - * :N words which do not match the given . - * :S[1gW] - * Substitute for in the value - * :C[1gW] - * Substitute for regex in the value - * :H Substitute the head of each word - * :T Substitute the tail of each word - * :E Substitute the extension (minus '.') of - * each word - * :R Substitute the root of each word - * (pathname minus the suffix). - * :O ("Order") Alphabeticaly sort words in variable. - * :Ox ("intermiX") Randomize words in variable. - * :u ("uniq") Remove adjacent duplicate words. - * :tu Converts the variable contents to uppercase. - * :tl Converts the variable contents to lowercase. - * :ts[c] Sets varSpace - the char used to - * separate words to 'c'. If 'c' is - * omitted then no separation is used. - * :tW Treat the variable contents as a single - * word, even if it contains spaces. - * (Mnemonic: one big 'W'ord.) - * :tw Treat the variable contents as multiple - * space-separated words. - * (Mnemonic: many small 'w'ords.) - * :[index] Select a single word from the value. - * :[start..end] Select multiple words from the value. - * :[*] or :[0] Select the entire value, as a single - * word. Equivalent to :tW. - * :[@] Select the entire value, as multiple - * words. Undoes the effect of :[*]. - * Equivalent to :tw. - * :[#] Returns the number of words in the value. - * - * :?: - * If the variable evaluates to true, return - * true value, else return the second value. - * :lhs=rhs Like :S, but the rhs goes to the end of - * the invocation. - * :sh Treat the current value as a command - * to be run, new value is its output. - * The following added so we can handle ODE makefiles. - * :@@@ - * Assign a temporary local variable - * to the current value of each word in turn - * and replace each word with the result of - * evaluating - * :D Use as value if variable defined - * :U Use as value if variable undefined - * :L Use the name of the variable as the value. - * :P Use the path of the node that has the same - * name as the variable as the value. This - * basically includes an implied :L so that - * the common method of refering to the path - * of your dependent 'x' in a rule is to use - * the form '${x:P}'. - * :!! Run cmd much the same as :sh run's the - * current value of the variable. - * The ::= modifiers, actually assign a value to the variable. - * Their main purpose is in supporting modifiers of .for loop - * iterators and other obscure uses. They always expand to - * nothing. In a target rule that would otherwise expand to an - * empty line they can be preceded with @: to keep make happy. - * Eg. - * - * foo: .USE - * .for i in ${.TARGET} ${.TARGET:R}.gz - * @: ${t::=$i} - * @echo blah ${t:T} - * .endfor - * - * ::= Assigns as the new value of variable. - * ::?= Assigns as value of variable if - * it was not already set. - * ::+= Appends to variable. - * ::!= Assigns output of as the new value of - * variable. - */ +/* Apply any modifiers (such as :Mpattern or :@var@loop@ or :Q or ::=value). */ static char * -ApplyModifiers(char *nstr, const char *tstr, - int const startc, int const endc, - Var * const v, GNode * const ctxt, int const flags, - int * const lengthPtr, void ** const freePtr) -{ +ApplyModifiers( + const char **pp, /* the parsing position, updated upon return */ + char *val, /* the current value of the variable */ + char const startc, /* '(' or '{', or '\0' for indirect modifiers */ + char const endc, /* ')' or '}', or '\0' for indirect modifiers */ + Var * const v, /* the variable may have its flags changed */ + GNode * const ctxt, /* for looking up and modifying variables */ + VarEvalFlags const eflags, + void ** const freePtr /* free this after using the return value */ +) { ApplyModifiersState st = { - startc, endc, v, ctxt, flags, lengthPtr, freePtr, - nstr, tstr, tstr, tstr, - '\0', 0, '\0', 0, {' ', FALSE}, NULL + startc, endc, v, ctxt, eflags, val, + var_Error, /* .newVal */ + '\0', /* .missing_delim */ + ' ', /* .sep */ + FALSE /* .oneBigWord */ }; + const char *p; + const char *mod; + ApplyModifierResult res; - while (*st.tstr && *st.tstr != st.endc) { + assert(startc == '(' || startc == '{' || startc == '\0'); + assert(endc == ')' || endc == '}' || endc == '\0'); + assert(val != NULL); - if (*st.tstr == '$') { + p = *pp; + while (*p != '\0' && *p != endc) { + + if (*p == '$') { /* * We may have some complex modifiers in a variable. */ - void *freeIt; - char *rval; int rlen; - int c; + void *freeIt; + const char *rval = Var_Parse(p, st.ctxt, st.eflags, &rlen, &freeIt); - rval = Var_Parse(st.tstr, st.ctxt, st.flags, &rlen, &freeIt); - /* * If we have not parsed up to st.endc or ':', * we are not interested. */ - if (rval != NULL && *rval && - (c = st.tstr[rlen]) != '\0' && - c != ':' && - c != st.endc) { + int c; + if (rval[0] != '\0' && + (c = p[rlen]) != '\0' && c != ':' && c != st.endc) { free(freeIt); goto apply_mods; } - if (DEBUG(VAR)) { - fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", - rval, rlen, st.tstr, rlen, st.tstr + rlen); - } + VAR_DEBUG("Indirect modifier \"%s\" from \"%.*s\"\n", + rval, rlen, p); - st.tstr += rlen; + p += rlen; - if (rval != NULL && *rval) { - int used; - - st.nstr = ApplyModifiers(st.nstr, rval, 0, 0, st.v, - st.ctxt, st.flags, &used, st.freePtr); - if (st.nstr == var_Error - || (st.nstr == varNoError && (st.flags & VARF_UNDEFERR) == 0) - || strlen(rval) != (size_t) used) { + if (rval[0] != '\0') { + const char *rval_pp = rval; + st.val = ApplyModifiers(&rval_pp, st.val, '\0', '\0', v, + ctxt, eflags, freePtr); + if (st.val == var_Error + || (st.val == varNoError && !(st.eflags & VARE_UNDEFERR)) + || *rval_pp != '\0') { free(freeIt); goto out; /* error already reported */ } } free(freeIt); - if (*st.tstr == ':') - st.tstr++; - else if (!*st.tstr && st.endc) { + if (*p == ':') + p++; + else if (*p == '\0' && endc != '\0') { Error("Unclosed variable specification after complex " - "modifier (expecting '%c') for %s", st.endc, st.v->name); + "modifier (expecting '%c') for %s", st.endc, st.v->name); goto out; } continue; } apply_mods: + st.newVal = var_Error; /* default value, in case of errors */ + res = AMR_BAD; /* just a safe fallback */ + mod = p; + if (DEBUG(VAR)) { - fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", st.v->name, - *st.tstr, st.nstr); + char eflags_str[VarEvalFlags_ToStringSize]; + char vflags_str[VarFlags_ToStringSize]; + Boolean is_single_char = mod[0] != '\0' && + (mod[1] == endc || mod[1] == ':'); + + /* At this point, only the first character of the modifier can + * be used since the end of the modifier is not yet known. */ + VAR_DEBUG("Applying ${%s:%c%s} to \"%s\" " + "(eflags = %s, vflags = %s)\n", + st.v->name, mod[0], is_single_char ? "" : "...", st.val, + Enum_FlagsToString(eflags_str, sizeof eflags_str, + st.eflags, VarEvalFlags_ToStringSpecs), + Enum_FlagsToString(vflags_str, sizeof vflags_str, + st.v->flags, VarFlags_ToStringSpecs)); } - st.newStr = var_Error; - switch ((st.modifier = *st.tstr)) { + + switch (*mod) { case ':': - { - int res = ApplyModifier_Assign(&st); - if (res == 'b') - goto bad_modifier; - if (res == 'c') - goto cleanup; - if (res == 'd') - goto default_case; - break; - } + res = ApplyModifier_Assign(&p, &st); + break; case '@': - ApplyModifier_At(&st); + res = ApplyModifier_Loop(&p, &st); break; case '_': - if (!ApplyModifier_Remember(&st)) - goto default_case; + res = ApplyModifier_Remember(&p, &st); break; case 'D': case 'U': - ApplyModifier_Defined(&st); + res = ApplyModifier_Defined(&p, &st); break; case 'L': - { - if ((st.v->flags & VAR_JUNK) != 0) - st.v->flags |= VAR_KEEP; - st.newStr = bmake_strdup(st.v->name); - st.cp = ++st.tstr; - st.termc = *st.tstr; - break; - } + if (st.v->flags & VAR_JUNK) + st.v->flags |= VAR_KEEP; + st.newVal = bmake_strdup(st.v->name); + p++; + res = AMR_OK; + break; case 'P': - ApplyModifier_Path(&st); + res = ApplyModifier_Path(&p, &st); break; case '!': - if (!ApplyModifier_Exclam(&st)) - goto cleanup; + res = ApplyModifier_Exclam(&p, &st); break; case '[': - { - int res = ApplyModifier_Words(&st); - if (res == 'b') - goto bad_modifier; - if (res == 'c') - goto cleanup; - break; - } + res = ApplyModifier_Words(&p, &st); + break; case 'g': - if (!ApplyModifier_Gmtime(&st)) - goto default_case; + res = ApplyModifier_Gmtime(&p, &st); break; case 'h': - if (!ApplyModifier_Hash(&st)) - goto default_case; + res = ApplyModifier_Hash(&p, &st); break; case 'l': - if (!ApplyModifier_Localtime(&st)) - goto default_case; + res = ApplyModifier_Localtime(&p, &st); break; case 't': - if (!ApplyModifier_To(&st)) - goto bad_modifier; + res = ApplyModifier_To(&p, &st); break; case 'N': case 'M': - ApplyModifier_Match(&st); + res = ApplyModifier_Match(&p, &st); break; case 'S': - if (!ApplyModifier_Subst(&st)) - goto cleanup; + res = ApplyModifier_Subst(&p, &st); break; case '?': - if (!ApplyModifier_IfElse(&st)) - goto cleanup; + res = ApplyModifier_IfElse(&p, &st); break; #ifndef NO_REGEX case 'C': - if (!ApplyModifier_Regex(&st)) - goto cleanup; + res = ApplyModifier_Regex(&p, &st); break; #endif case 'q': case 'Q': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarQuote(st.nstr, st.modifier == 'q'); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + if (p[1] == st.endc || p[1] == ':') { + st.newVal = VarQuote(st.val, *mod == 'q'); + p++; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; case 'T': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarTail, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Tail); + break; case 'H': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarHead, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Head); + break; case 'E': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarSuffix, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Suffix); + break; case 'R': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarModify(st.ctxt, &st.parsestate, st.nstr, VarRoot, - NULL); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + res = ApplyModifier_WordFunc(&p, &st, ModifyWord_Root); + break; case 'r': - if (!ApplyModifier_Range(&st)) - goto default_case; + res = ApplyModifier_Range(&p, &st); break; case 'O': - if (!ApplyModifier_Order(&st)) - goto bad_modifier; + res = ApplyModifier_Order(&p, &st); break; case 'u': - if (st.tstr[1] == st.endc || st.tstr[1] == ':') { - st.newStr = VarUniq(st.nstr); - st.cp = st.tstr + 1; - st.termc = *st.cp; - break; - } - goto default_case; + if (p[1] == st.endc || p[1] == ':') { + st.newVal = VarUniq(st.val); + p++; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; #ifdef SUNSHCMD case 's': - if (st.tstr[1] == 'h' && (st.tstr[2] == st.endc || st.tstr[2] == ':')) { - const char *emsg; - if (st.flags & VARF_WANTRES) { - st.newStr = Cmd_Exec(st.nstr, &emsg); - if (emsg) - Error(emsg, st.nstr); + if (p[1] == 'h' && (p[2] == st.endc || p[2] == ':')) { + if (st.eflags & VARE_WANTRES) { + const char *errfmt; + st.newVal = Cmd_Exec(st.val, &errfmt); + if (errfmt) + Error(errfmt, st.val); } else - st.newStr = varNoError; - st.cp = st.tstr + 2; - st.termc = *st.cp; - break; - } - goto default_case; + st.newVal = varNoError; + p += 2; + res = AMR_OK; + } else + res = AMR_UNKNOWN; + break; #endif default: - default_case: - { + res = AMR_UNKNOWN; + } + #ifdef SYSVVARSUB - int res = ApplyModifier_SysV(&st); - if (res == 'c') - goto cleanup; - if (res != '=') + if (res == AMR_UNKNOWN) { + assert(p == mod); + res = ApplyModifier_SysV(&p, &st); + } #endif - { - Error("Unknown modifier '%c'", *st.tstr); - for (st.cp = st.tstr+1; - *st.cp != ':' && *st.cp != st.endc && *st.cp != '\0'; - st.cp++) - continue; - st.termc = *st.cp; - st.newStr = var_Error; - } - } + + if (res == AMR_UNKNOWN) { + Error("Unknown modifier '%c'", *mod); + for (p++; *p != ':' && *p != st.endc && *p != '\0'; p++) + continue; + st.newVal = var_Error; } + if (res == AMR_CLEANUP) + goto cleanup; + if (res == AMR_BAD) + goto bad_modifier; + if (DEBUG(VAR)) { - fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n", - st.v->name, st.modifier, st.newStr); + char eflags_str[VarEvalFlags_ToStringSize]; + char vflags_str[VarFlags_ToStringSize]; + const char *quot = st.newVal == var_Error ? "" : "\""; + const char *newVal = st.newVal == var_Error ? "error" : st.newVal; + + VAR_DEBUG("Result of ${%s:%.*s} is %s%s%s " + "(eflags = %s, vflags = %s)\n", + st.v->name, (int)(p - mod), mod, quot, newVal, quot, + Enum_FlagsToString(eflags_str, sizeof eflags_str, + st.eflags, VarEvalFlags_ToStringSpecs), + Enum_FlagsToString(vflags_str, sizeof vflags_str, + st.v->flags, VarFlags_ToStringSpecs)); } - if (st.newStr != st.nstr) { - if (*st.freePtr) { - free(st.nstr); - *st.freePtr = NULL; + if (st.newVal != st.val) { + if (*freePtr) { + free(st.val); + *freePtr = NULL; } - st.nstr = st.newStr; - if (st.nstr != var_Error && st.nstr != varNoError) { - *st.freePtr = st.nstr; + st.val = st.newVal; + if (st.val != var_Error && st.val != varNoError) { + *freePtr = st.val; } } - if (st.termc == '\0' && st.endc != '\0') { + if (*p == '\0' && st.endc != '\0') { Error("Unclosed variable specification (expecting '%c') " - "for \"%s\" (value \"%s\") modifier %c", - st.endc, st.v->name, st.nstr, st.modifier); - } else if (st.termc == ':') { - st.cp++; + "for \"%s\" (value \"%s\") modifier %c", + st.endc, st.v->name, st.val, *mod); + } else if (*p == ':') { + p++; } - st.tstr = st.cp; + mod = p; } out: - *st.lengthPtr = st.tstr - st.start; - return st.nstr; + *pp = p; + assert(st.val != NULL); /* Use var_Error or varNoError instead. */ + return st.val; bad_modifier: - /* "{(" */ - Error("Bad modifier `:%.*s' for %s", (int)strcspn(st.tstr, ":)}"), st.tstr, - st.v->name); + Error("Bad modifier `:%.*s' for %s", + (int)strcspn(mod, ":)}"), mod, st.v->name); cleanup: - *st.lengthPtr = st.cp - st.start; - if (st.delim != '\0') - Error("Unclosed substitution for %s (%c missing)", - st.v->name, st.delim); - free(*st.freePtr); - *st.freePtr = NULL; + *pp = p; + if (st.missing_delim != '\0') + Error("Unfinished modifier for %s ('%c' missing)", + st.v->name, st.missing_delim); + free(*freePtr); + *freePtr = NULL; return var_Error; } +static Boolean +VarIsDynamic(GNode *ctxt, const char *varname, size_t namelen) +{ + if ((namelen == 1 || + (namelen == 2 && (varname[1] == 'F' || varname[1] == 'D'))) && + (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) + { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (varname[0]) { + case '@': + case '%': + case '*': + case '!': + return TRUE; + } + return FALSE; + } + + if ((namelen == 7 || namelen == 8) && varname[0] == '.' && + isupper((unsigned char)varname[1]) && + (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) + { + return strcmp(varname, ".TARGET") == 0 || + strcmp(varname, ".ARCHIVE") == 0 || + strcmp(varname, ".PREFIX") == 0 || + strcmp(varname, ".MEMBER") == 0; + } + + return FALSE; +} + /*- *----------------------------------------------------------------------- * Var_Parse -- - * Given the start of a variable invocation, extract the variable - * name and find its value, then modify it according to the - * specification. + * Given the start of a variable invocation (such as $v, $(VAR), + * ${VAR:Mpattern}), extract the variable name, possibly some + * modifiers and find its value by applying the modifiers to the + * original value. * * Input: * str The string to parse * ctxt The context for the variable - * flags VARF_UNDEFERR if undefineds are an error - * VARF_WANTRES if we actually want the result - * VARF_ASSIGN if we are in a := assignment + * flags VARE_UNDEFERR if undefineds are an error + * VARE_WANTRES if we actually want the result + * VARE_ASSIGN if we are in a := assignment * lengthPtr OUT: The length of the specification * freePtr OUT: Non-NULL if caller should free *freePtr * * Results: - * The (possibly-modified) value of the variable or var_Error if the - * specification is invalid. The length of the specification is - * placed in *lengthPtr (for invalid specifications, this is just - * 2...?). - * If *freePtr is non-NULL then it's a pointer that the caller - * should pass to free() to free memory used by the result. + * Returns the value of the variable expression, never NULL. + * var_Error if there was a parse error and VARE_UNDEFERR was set. + * varNoError if there was a parse error and VARE_UNDEFERR was not set. * - * Side Effects: - * None. + * Parsing should continue at str + *lengthPtr. * + * After using the returned value, *freePtr must be freed, preferably + * using bmake_free since it is NULL in most cases. + * + * Side Effects: + * Any effects from the modifiers, such as :!cmd! or ::=value. *----------------------------------------------------------------------- */ /* coverity[+alloc : arg-*4] */ -char * -Var_Parse(const char *str, GNode *ctxt, Varf_Flags flags, +const char * +Var_Parse(const char * const str, GNode *ctxt, VarEvalFlags eflags, int *lengthPtr, void **freePtr) { const char *tstr; /* Pointer into str */ - Var *v; /* Variable in invocation */ Boolean haveModifier; /* TRUE if have modifiers for the variable */ - char endc; /* Ending character when variable in parens + char startc; /* Starting character if variable in parens * or braces */ - char startc; /* Starting character when variable in parens + char endc; /* Ending character if variable in parens * or braces */ - int vlen; /* Length of variable name */ - const char *start; /* Points to original start of str */ - char *nstr; /* New string, used during expansion */ Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The * result is just the invocation, unaltered */ - const char *extramodifiers; /* extra modifiers to apply first */ - char name[2]; + const char *extramodifiers; + Var *v; + char *nstr; + char eflags_str[VarEvalFlags_ToStringSize]; + VAR_DEBUG("%s: %s with %s\n", __func__, str, + Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags, + VarEvalFlags_ToStringSpecs)); + *freePtr = NULL; - extramodifiers = NULL; + extramodifiers = NULL; /* extra modifiers to apply first */ dynamic = FALSE; - start = str; +#ifdef USE_DOUBLE_BOOLEAN + /* Appease GCC 5.5.0, which thinks that the variable might not be + * initialized. */ + endc = '\0'; +#endif + startc = str[1]; if (startc != PROPEN && startc != BROPEN) { + char name[2]; + /* * If it's not bounded by braces of some sort, life is much simpler. * We just need to check for the first character and return the * value if it exists. */ /* Error out some really stupid names */ if (startc == '\0' || strchr(")}:$", startc)) { *lengthPtr = 1; return var_Error; } + name[0] = startc; name[1] = '\0'; - v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if (v == NULL) { *lengthPtr = 2; - if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { + if (ctxt == VAR_CMD || ctxt == VAR_GLOBAL) { /* * If substituting a local variable in a non-local context, * assume it's for dynamic source stuff. We have to handle * this specially and return the longhand for the variable * with the dollar sign escaped so it makes it back to the * caller. Only four of the local variables are treated * specially as they are the only four that will be set * when dynamic sources are expanded. */ switch (str[1]) { case '@': - return UNCONST("$(.TARGET)"); + return "$(.TARGET)"; case '%': - return UNCONST("$(.MEMBER)"); + return "$(.MEMBER)"; case '*': - return UNCONST("$(.PREFIX)"); + return "$(.PREFIX)"; case '!': - return UNCONST("$(.ARCHIVE)"); + return "$(.ARCHIVE)"; } } - return (flags & VARF_UNDEFERR) ? var_Error : varNoError; + return (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } else { haveModifier = FALSE; - tstr = &str[1]; - endc = str[1]; + tstr = str + 1; } } else { - Buffer buf; /* Holds the variable name */ - int depth = 1; + Buffer namebuf; /* Holds the variable name */ + int depth; + size_t namelen; + char *varname; endc = startc == PROPEN ? PRCLOSE : BRCLOSE; - Buf_Init(&buf, 0); + Buf_Init(&namebuf, 0); + /* * Skip to the end character or a colon, whichever comes first. */ + depth = 1; for (tstr = str + 2; *tstr != '\0'; tstr++) { /* Track depth so we can spot parse errors. */ if (*tstr == startc) depth++; if (*tstr == endc) { if (--depth == 0) break; } - if (depth == 1 && *tstr == ':') + if (*tstr == ':' && depth == 1) break; /* A variable inside a variable, expand. */ if (*tstr == '$') { int rlen; void *freeIt; - char *rval = Var_Parse(tstr, ctxt, flags, &rlen, &freeIt); - if (rval != NULL) - Buf_AddBytes(&buf, strlen(rval), rval); + const char *rval = Var_Parse(tstr, ctxt, eflags, &rlen, + &freeIt); + Buf_AddStr(&namebuf, rval); free(freeIt); tstr += rlen - 1; } else - Buf_AddByte(&buf, *tstr); + Buf_AddByte(&namebuf, *tstr); } if (*tstr == ':') { haveModifier = TRUE; } else if (*tstr == endc) { haveModifier = FALSE; } else { + Parse_Error(PARSE_FATAL, "Unclosed variable \"%s\"", + Buf_GetAll(&namebuf, NULL)); /* * If we never did find the end character, return NULL * right now, setting the length to be the distance to * the end of the string, since that's what make does. */ - *lengthPtr = tstr - str; - Buf_Destroy(&buf, TRUE); + *lengthPtr = (int)(size_t)(tstr - str); + Buf_Destroy(&namebuf, TRUE); return var_Error; } - str = Buf_GetAll(&buf, &vlen); + varname = Buf_GetAll(&namebuf, &namelen); + /* - * At this point, str points into newly allocated memory from - * buf, containing only the name of the variable. + * At this point, varname points into newly allocated memory from + * namebuf, containing only the name of the variable. * * start and tstr point into the const string that was pointed * to by the original value of the str parameter. start points * to the '$' at the beginning of the string, while tstr points * to the char just after the end of the variable name -- this * will be '\0', ':', PRCLOSE, or BRCLOSE. */ - v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + v = VarFind(varname, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); /* * Check also for bogus D and F forms of local variables since we're * in a local context and the name is the right length. */ - if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && - (vlen == 2) && (str[1] == 'F' || str[1] == 'D') && - strchr("@%?*!<>", str[0]) != NULL) { + if (v == NULL && ctxt != VAR_CMD && ctxt != VAR_GLOBAL && + namelen == 2 && (varname[1] == 'F' || varname[1] == 'D') && + strchr("@%?*!<>", varname[0]) != NULL) + { /* * Well, it's local -- go look for it. */ - name[0] = *str; - name[1] = '\0'; + char name[] = { varname[0], '\0' }; v = VarFind(name, ctxt, 0); if (v != NULL) { - if (str[1] == 'D') { + if (varname[1] == 'D') { extramodifiers = "H:"; } else { /* F */ extramodifiers = "T:"; } } } if (v == NULL) { - if (((vlen == 1) || - (((vlen == 2) && (str[1] == 'F' || str[1] == 'D')))) && - ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) - { - /* - * If substituting a local variable in a non-local context, - * assume it's for dynamic source stuff. We have to handle - * this specially and return the longhand for the variable - * with the dollar sign escaped so it makes it back to the - * caller. Only four of the local variables are treated - * specially as they are the only four that will be set - * when dynamic sources are expanded. - */ - switch (*str) { - case '@': - case '%': - case '*': - case '!': - dynamic = TRUE; - break; - } - } else if (vlen > 2 && *str == '.' && - isupper((unsigned char) str[1]) && - (ctxt == VAR_CMD || ctxt == VAR_GLOBAL)) - { - int len = vlen - 1; - if ((strncmp(str, ".TARGET", len) == 0) || - (strncmp(str, ".ARCHIVE", len) == 0) || - (strncmp(str, ".PREFIX", len) == 0) || - (strncmp(str, ".MEMBER", len) == 0)) - { - dynamic = TRUE; - } - } + dynamic = VarIsDynamic(ctxt, varname, namelen); if (!haveModifier) { /* * No modifiers -- have specification length so we can return * now. */ - *lengthPtr = tstr - start + 1; + *lengthPtr = (int)(size_t)(tstr - str) + 1; if (dynamic) { - char *pstr = bmake_strndup(start, *lengthPtr); + char *pstr = bmake_strldup(str, (size_t)*lengthPtr); *freePtr = pstr; - Buf_Destroy(&buf, TRUE); + Buf_Destroy(&namebuf, TRUE); return pstr; } else { - Buf_Destroy(&buf, TRUE); - return (flags & VARF_UNDEFERR) ? var_Error : varNoError; + Buf_Destroy(&namebuf, TRUE); + return (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } } else { /* * Still need to get to the end of the variable specification, * so kludge up a Var structure for the modifications */ v = bmake_malloc(sizeof(Var)); - v->name = UNCONST(str); + v->name = varname; Buf_Init(&v->val, 1); v->flags = VAR_JUNK; - Buf_Destroy(&buf, FALSE); + Buf_Destroy(&namebuf, FALSE); } } else - Buf_Destroy(&buf, TRUE); + Buf_Destroy(&namebuf, TRUE); } if (v->flags & VAR_IN_USE) { Fatal("Variable %s is recursive.", v->name); /*NOTREACHED*/ } else { v->flags |= VAR_IN_USE; } + /* * Before doing any modification, we have to make sure the value * has been fully expanded. If it looks like recursion might be * necessary (there's a dollar sign somewhere in the variable's value) * we just call Var_Subst to do any other substitutions that are * necessary. Note that the value returned by Var_Subst will have * been dynamically-allocated, so it will need freeing when we * return. */ nstr = Buf_GetAll(&v->val, NULL); - if (strchr(nstr, '$') != NULL && (flags & VARF_WANTRES) != 0) { - nstr = Var_Subst(NULL, nstr, ctxt, flags); + if (strchr(nstr, '$') != NULL && (eflags & VARE_WANTRES) != 0) { + nstr = Var_Subst(nstr, ctxt, eflags); *freePtr = nstr; } - v->flags &= ~VAR_IN_USE; + v->flags &= ~(unsigned)VAR_IN_USE; - if (nstr != NULL && (haveModifier || extramodifiers != NULL)) { + if (haveModifier || extramodifiers != NULL) { void *extraFree; - int used; extraFree = NULL; if (extramodifiers != NULL) { - nstr = ApplyModifiers(nstr, extramodifiers, '(', ')', - v, ctxt, flags, &used, &extraFree); + const char *em = extramodifiers; + nstr = ApplyModifiers(&em, nstr, '(', ')', + v, ctxt, eflags, &extraFree); } if (haveModifier) { /* Skip initial colon. */ tstr++; - nstr = ApplyModifiers(nstr, tstr, startc, endc, - v, ctxt, flags, &used, freePtr); - tstr += used; + nstr = ApplyModifiers(&tstr, nstr, startc, endc, + v, ctxt, eflags, freePtr); free(extraFree); } else { *freePtr = extraFree; } } - *lengthPtr = tstr - start + (*tstr ? 1 : 0); - if (v->flags & VAR_FROM_ENV) { - Boolean destroy = FALSE; + /* Skip past endc if possible. */ + *lengthPtr = (int)(size_t)(tstr + (*tstr ? 1 : 0) - str); - if (nstr != Buf_GetAll(&v->val, NULL)) { - destroy = TRUE; - } else { + if (v->flags & VAR_FROM_ENV) { + Boolean destroy = nstr != Buf_GetAll(&v->val, NULL); + if (!destroy) { /* * Returning the value unmodified, so tell the caller to free * the thing. */ *freePtr = nstr; } - VarFreeEnv(v, destroy); + (void)VarFreeEnv(v, destroy); } else if (v->flags & VAR_JUNK) { /* - * Perform any free'ing needed and set *freePtr to NULL so the caller + * Perform any freeing needed and set *freePtr to NULL so the caller * doesn't try to free a static pointer. - * If VAR_KEEP is also set then we want to keep str as is. + * If VAR_KEEP is also set then we want to keep str(?) as is. */ if (!(v->flags & VAR_KEEP)) { - if (*freePtr) { - free(nstr); + if (*freePtr != NULL) { + free(*freePtr); *freePtr = NULL; } if (dynamic) { - nstr = bmake_strndup(start, *lengthPtr); + nstr = bmake_strldup(str, (size_t)*lengthPtr); *freePtr = nstr; } else { - nstr = (flags & VARF_UNDEFERR) ? var_Error : varNoError; + nstr = (eflags & VARE_UNDEFERR) ? var_Error : varNoError; } } if (nstr != Buf_GetAll(&v->val, NULL)) Buf_Destroy(&v->val, TRUE); free(v->name); free(v); } return nstr; } -/*- - *----------------------------------------------------------------------- - * Var_Subst -- - * Substitute for all variables in the given string in the given context. - * If flags & VARF_UNDEFERR, Parse_Error will be called when an undefined - * variable is encountered. +/* Substitute for all variables in the given string in the given context. * + * If eflags & VARE_UNDEFERR, Parse_Error will be called when an undefined + * variable is encountered. + * + * If eflags & VARE_WANTRES, any effects from the modifiers, such as ::=, + * :sh or !cmd! take place. + * * Input: - * var Named variable || NULL for all * str the string which to substitute * ctxt the context wherein to find variables - * flags VARF_UNDEFERR if undefineds are an error - * VARF_WANTRES if we actually want the result - * VARF_ASSIGN if we are in a := assignment + * eflags VARE_UNDEFERR if undefineds are an error + * VARE_WANTRES if we actually want the result + * VARE_ASSIGN if we are in a := assignment * * Results: * The resulting string. - * - * Side Effects: - * None. - *----------------------------------------------------------------------- */ char * -Var_Subst(const char *var, const char *str, GNode *ctxt, Varf_Flags flags) +Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags) { - Buffer buf; /* Buffer for forming things */ - char *val; /* Value to substitute for a variable */ - int length; /* Length of the variable invocation */ - Boolean trailingBslash; /* variable ends in \ */ - void *freeIt = NULL; /* Set if it should be freed */ - static Boolean errorReported; /* Set true if an error has already - * been reported to prevent a plethora - * of messages when recursing */ + Buffer buf; /* Buffer for forming things */ + Boolean trailingBslash; + /* Set true if an error has already been reported, + * to prevent a plethora of messages when recursing */ + static Boolean errorReported; + Buf_Init(&buf, 0); errorReported = FALSE; - trailingBslash = FALSE; + trailingBslash = FALSE; /* variable ends in \ */ while (*str) { if (*str == '\n' && trailingBslash) Buf_AddByte(&buf, ' '); - if (var == NULL && (*str == '$') && (str[1] == '$')) { + + if (*str == '$' && str[1] == '$') { /* - * A dollar sign may be escaped either with another dollar sign. + * A dollar sign may be escaped with another dollar sign. * In such a case, we skip over the escape character and store the * dollar sign into the buffer directly. */ - if (save_dollars && (flags & VARF_ASSIGN)) - Buf_AddByte(&buf, *str); - str++; - Buf_AddByte(&buf, *str); - str++; + if (save_dollars && (eflags & VARE_ASSIGN)) + Buf_AddByte(&buf, '$'); + Buf_AddByte(&buf, '$'); + str += 2; } else if (*str != '$') { /* * Skip as many characters as possible -- either to the end of * the string or to the next dollar sign (variable invocation). */ const char *cp; for (cp = str++; *str != '$' && *str != '\0'; str++) continue; - Buf_AddBytes(&buf, str - cp, cp); + Buf_AddBytesBetween(&buf, cp, str); } else { - if (var != NULL) { - int expand; - for (;;) { - if (str[1] == '\0') { - /* A trailing $ is kind of a special case */ - Buf_AddByte(&buf, str[0]); - str++; - expand = FALSE; - } else if (str[1] != PROPEN && str[1] != BROPEN) { - if (str[1] != *var || strlen(var) > 1) { - Buf_AddBytes(&buf, 2, str); - str += 2; - expand = FALSE; - } else - expand = TRUE; - break; - } else { - const char *p; + int length; + void *freeIt; + const char *val = Var_Parse(str, ctxt, eflags, &length, &freeIt); - /* Scan up to the end of the variable name. */ - for (p = &str[2]; *p && - *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++) - if (*p == '$') - break; - /* - * A variable inside the variable. We cannot expand - * the external variable yet, so we try again with - * the nested one - */ - if (*p == '$') { - Buf_AddBytes(&buf, p - str, str); - str = p; - continue; - } - - if (strncmp(var, str + 2, p - str - 2) != 0 || - var[p - str - 2] != '\0') { - /* - * Not the variable we want to expand, scan - * until the next variable - */ - for (; *p != '$' && *p != '\0'; p++) - continue; - Buf_AddBytes(&buf, p - str, str); - str = p; - expand = FALSE; - } else - expand = TRUE; - break; - } - } - if (!expand) - continue; - } - - val = Var_Parse(str, ctxt, flags, &length, &freeIt); - - /* - * When we come down here, val should either point to the - * value of this variable, suitably modified, or be NULL. - * Length should be the total length of the potential - * variable invocation (from $ to end character...) - */ if (val == var_Error || val == varNoError) { /* * If performing old-time variable substitution, skip over * the variable and continue with the substitution. Otherwise, * store the dollar sign and advance str so we continue with * the string... */ if (oldVars) { str += length; - } else if ((flags & VARF_UNDEFERR) || val == var_Error) { + } else if ((eflags & VARE_UNDEFERR) || val == var_Error) { /* * If variable is undefined, complain and skip the * variable. The complaint will stop us from doing anything * when the file is parsed. */ if (!errorReported) { Parse_Error(PARSE_FATAL, "Undefined variable \"%.*s\"", length, str); } str += length; errorReported = TRUE; } else { Buf_AddByte(&buf, *str); str += 1; } } else { - /* - * We've now got a variable structure to store in. But first, - * advance the string pointer. - */ + size_t val_len; + str += length; - /* - * Copy all the characters from the variable value straight - * into the new string. - */ - length = strlen(val); - Buf_AddBytes(&buf, length, val); - trailingBslash = length > 0 && val[length - 1] == '\\'; + val_len = strlen(val); + Buf_AddBytes(&buf, val, val_len); + trailingBslash = val_len > 0 && val[val_len - 1] == '\\'; } free(freeIt); freeIt = NULL; } } return Buf_DestroyCompact(&buf); } /* Initialize the module. */ void Var_Init(void) { VAR_INTERNAL = Targ_NewGN("Internal"); VAR_GLOBAL = Targ_NewGN("Global"); VAR_CMD = Targ_NewGN("Command"); } void Var_End(void) { + Var_Stats(); } +void +Var_Stats(void) +{ + Hash_DebugStats(&VAR_GLOBAL->context, "VAR_GLOBAL"); +} + /****************** PRINT DEBUGGING INFO *****************/ static void VarPrintVar(void *vp, void *data MAKE_ATTR_UNUSED) { Var *v = (Var *)vp; fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL)); } -/*- - *----------------------------------------------------------------------- - * Var_Dump -- - * print all variables in a context - *----------------------------------------------------------------------- - */ +/* Print all variables in a context, unordered. */ void Var_Dump(GNode *ctxt) { Hash_ForEach(&ctxt->context, VarPrintVar, NULL); } Index: head/contrib/bmake =================================================================== --- head/contrib/bmake (revision 365365) +++ head/contrib/bmake (revision 365366) Property changes on: head/contrib/bmake ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/NetBSD/bmake/dist:r363117-365363 Index: head/usr.bin/bmake/Makefile =================================================================== --- head/usr.bin/bmake/Makefile (revision 365365) +++ head/usr.bin/bmake/Makefile (revision 365366) @@ -1,186 +1,160 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ SRCTOP?= ${.CURDIR:H:H} # look here first for config.h CFLAGS+= -I${.CURDIR} # for after-import CLEANDIRS+= FreeBSD CLEANFILES+= bootstrap -# $Id: Makefile,v 1.107 2020/06/07 21:18:46 sjg Exp $ +# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $ PROG?= ${.CURDIR:T} SRCS= \ arch.c \ buf.c \ compat.c \ cond.c \ dir.c \ + enum.c \ for.c \ hash.c \ job.c \ + lst.c \ main.c \ make.c \ make_malloc.c \ meta.c \ metachar.c \ parse.c \ str.c \ strlist.c \ suff.c \ targ.c \ trace.c \ util.c \ var.c -# from lst.lib/ -SRCS+= \ - lstAppend.c \ - lstAtEnd.c \ - lstAtFront.c \ - lstClose.c \ - lstConcat.c \ - lstDatum.c \ - lstDeQueue.c \ - lstDestroy.c \ - lstDupl.c \ - lstEnQueue.c \ - lstFind.c \ - lstFindFrom.c \ - lstFirst.c \ - lstForEach.c \ - lstForEachFrom.c \ - lstInit.c \ - lstInsert.c \ - lstIsAtEnd.c \ - lstIsEmpty.c \ - lstLast.c \ - lstMember.c \ - lstNext.c \ - lstOpen.c \ - lstPrev.c \ - lstRemove.c \ - lstReplace.c \ - lstSucc.c - .sinclude "Makefile.inc" # this file gets generated by configure .sinclude "Makefile.config" .if !empty(LIBOBJS) SRCS+= ${LIBOBJS:T:.o=.c} .endif # just in case prefix?= /usr srcdir?= ${.CURDIR} DEFAULT_SYS_PATH?= ${prefix}/share/mk CPPFLAGS+= -DUSE_META CFLAGS+= ${CPPFLAGS} CFLAGS+= -D_PATH_DEFSYSPATH=\"${DEFAULT_SYS_PATH}\" 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 # should be set by now USE_FILEMON ?= no .if ${USE_FILEMON:tl} != "no" .PATH: ${srcdir}/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.filemon_dev.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} .endif .endif # USE_FILEMON == dev .endif # USE_FILEMON .PATH: ${srcdir} -.PATH: ${srcdir}/lst.lib .if make(obj) || make(clean) SUBDIR+= unit-tests .endif MAN= ${PROG}.1 MAN1= ${MAN} .if (${PROG} != "make") CLEANFILES+= my.history .if make(${MAN}) || !exists(${srcdir}/${MAN}) my.history: @(echo ".Nm"; \ echo "is derived from NetBSD"; \ echo ".Xr make 1 ."; \ echo "It uses autoconf to facilitate portability to other platforms."; \ echo ".Pp") > $@ .NOPATH: ${MAN} ${MAN}: make.1 my.history @echo making $@ @sed \ -e '/^.Dt/s/MAKE/${PROG:tu}/' \ -e 's/^.Nx/NetBSD/' \ -e '/^.Nm/s/make/${PROG}/' \ -e '/^.Sh HISTORY/rmy.history' \ -e '/^.Sh HISTORY/,$$s,^.Nm,make,' ${srcdir}/make.1 > $@ all beforeinstall: ${MAN} _mfromdir=. .endif .endif MANTARGET?= cat MANDEST?= ${MANDIR}/${MANTARGET}1 .if ${MANTARGET} == "cat" _mfromdir=${srcdir} .endif .include CPPFLAGS+= -DMAKE_NATIVE -DHAVE_CONFIG_H COPTS.var.c += -Wno-cast-qual COPTS.job.c += -Wno-format-nonliteral COPTS.parse.c += -Wno-format-nonliteral COPTS.var.c += -Wno-format-nonliteral # Force these SHAREDIR= ${SHAREDIR.bmake:U${prefix}/share} BINDIR= ${BINDIR.bmake:U${prefix}/bin} MANDIR= ${MANDIR.bmake:U${SHAREDIR}/man} .if !exists(.depend) ${OBJS}: config.h .endif # A simple unit-test driver to help catch regressions +TEST_MAKE ?= ${.OBJDIR}/${PROG:T} accept test: - cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} + cd ${.CURDIR}/unit-tests && \ + MAKEFLAGS= ${TEST_MAKE} -r -m / ${.TARGET} ${TESTS:DTESTS=${TESTS:Q}} + # override some simple things BINDIR= /usr/bin MANDIR= /usr/share/man/man # make sure we get this CFLAGS+= ${COPTS.${.IMPSRC:T}} after-import: ${SRCTOP}/contrib/bmake/bsd.after-import.mk cd ${.CURDIR} && ${.MAKE} -f ${SRCTOP}/contrib/bmake/bsd.after-import.mk Index: head/usr.bin/bmake/Makefile.config =================================================================== --- head/usr.bin/bmake/Makefile.config (revision 365365) +++ head/usr.bin/bmake/Makefile.config (revision 365366) @@ -1,27 +1,27 @@ # This is a generated file, do NOT edit! # See contrib/bmake/bsd.after-import.mk # # $FreeBSD$ SRCTOP?= ${.CURDIR:H:H} # things set by configure -_MAKE_VERSION?=20200710 +_MAKE_VERSION?=20200902 prefix?= /usr srcdir= ${SRCTOP}/contrib/bmake CC?= cc DEFAULT_SYS_PATH?= .../share/mk:/usr/share/mk CPPFLAGS+= CFLAGS+= ${CPPFLAGS} -DHAVE_CONFIG_H LDFLAGS+= LIBOBJS+= ${LIBOBJDIR}stresep$U.o LDADD+= USE_META?= yes USE_FILEMON?= dev FILEMON_H?= /usr/include/dev/filemon/filemon.h BMAKE_PATH_MAX?= 1024 # used if MAXPATHLEN not defined CPPFLAGS+= -DBMAKE_PATH_MAX=${BMAKE_PATH_MAX} Index: head/usr.bin/bmake/unit-tests/Makefile =================================================================== --- head/usr.bin/bmake/unit-tests/Makefile (revision 365365) +++ head/usr.bin/bmake/unit-tests/Makefile (revision 365366) @@ -1,175 +1,484 @@ +# This is a generated file, do NOT edit! +# See contrib/bmake/bsd.after-import.mk +# # $FreeBSD$ -# $Id: Makefile,v 1.60 2020/07/10 00:48:32 sjg Exp $ +# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $ # -# $NetBSD: Makefile,v 1.63 2020/07/09 22:40:14 sjg Exp $ +# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $ # # Unit tests for make(1) # # The main targets are: # # all: # run all the tests # test: # run 'all', and compare to expected results # accept: # move generated output to expected results # # Settable variables # # TEST_MAKE # The make program to be tested. # # # Adding a test case # # Each feature should get its own set of tests in its own suitably # named makefile (*.mk), with its own set of expected results (*.exp), # and it should be added to the TESTS list. # -# Any added files must also be added to src/distrib/sets/lists/tests/mi. -# Makefiles that are not added to TESTS must be ignored in -# src/tests/usr.bin/make/t_make.sh (example: include-sub). +# A few *.mk files are helper files for other tests (such as include-sub.mk) +# and are thus not added to TESTS. Such files must be ignored in +# src/tests/usr.bin/make/t_make.sh. # # Each test is in a sub-makefile. # Keep the list sorted. +# Any test that is commented out must be ignored in +# src/tests/usr.bin/make/t_make.sh as well. +TESTS+= # archive # broken on FreeBSD, enabled in t_make.sh +TESTS+= archive-suffix +TESTS+= cmd-interrupt +TESTS+= cmdline TESTS+= comment +TESTS+= cond-cmp-numeric +TESTS+= cond-cmp-numeric-eq +TESTS+= cond-cmp-numeric-ge +TESTS+= cond-cmp-numeric-gt +TESTS+= cond-cmp-numeric-le +TESTS+= cond-cmp-numeric-lt +TESTS+= cond-cmp-numeric-ne +TESTS+= cond-cmp-string +TESTS+= cond-func +TESTS+= cond-func-commands +TESTS+= cond-func-defined +TESTS+= cond-func-empty +TESTS+= cond-func-exists +TESTS+= cond-func-make +TESTS+= cond-func-target TESTS+= cond-late +TESTS+= cond-op +TESTS+= cond-op-and +TESTS+= cond-op-not +TESTS+= cond-op-or +TESTS+= cond-op-parentheses TESTS+= cond-short +TESTS+= cond-token-number +TESTS+= cond-token-plain +TESTS+= cond-token-string +TESTS+= cond-token-var TESTS+= cond1 TESTS+= cond2 +TESTS+= counter +TESTS+= dep +TESTS+= dep-colon +TESTS+= dep-double-colon +TESTS+= dep-exclam +TESTS+= dep-none +TESTS+= dep-var +TESTS+= dep-wildcards +TESTS+= depsrc +TESTS+= depsrc-exec +TESTS+= depsrc-ignore +TESTS+= depsrc-made +TESTS+= depsrc-make +TESTS+= depsrc-meta +TESTS+= depsrc-nometa +TESTS+= depsrc-nometa_cmp +TESTS+= depsrc-nopath +TESTS+= depsrc-notmain +TESTS+= depsrc-optional +TESTS+= depsrc-phony +TESTS+= depsrc-precious +TESTS+= depsrc-recursive +TESTS+= depsrc-silent +TESTS+= depsrc-use +TESTS+= depsrc-usebefore +TESTS+= depsrc-usebefore-double-colon +TESTS+= depsrc-wait +TESTS+= deptgt +TESTS+= deptgt-begin +TESTS+= deptgt-default +TESTS+= deptgt-delete_on_error +TESTS+= deptgt-end +TESTS+= deptgt-error +TESTS+= deptgt-ignore +TESTS+= deptgt-interrupt +TESTS+= deptgt-main +TESTS+= deptgt-makeflags +TESTS+= deptgt-no_parallel +TESTS+= deptgt-nopath +TESTS+= deptgt-notparallel +TESTS+= deptgt-objdir +TESTS+= deptgt-order +TESTS+= deptgt-path +TESTS+= deptgt-path-suffix +TESTS+= deptgt-phony +TESTS+= deptgt-precious +TESTS+= deptgt-shell +TESTS+= deptgt-silent +TESTS+= deptgt-stale +TESTS+= deptgt-suffixes +TESTS+= dir +TESTS+= dir-expand-path +TESTS+= directive +TESTS+= directive-elif +TESTS+= directive-elifdef +TESTS+= directive-elifmake +TESTS+= directive-elifndef +TESTS+= directive-elifnmake +TESTS+= directive-else +TESTS+= directive-endif +TESTS+= directive-error +TESTS+= directive-export +TESTS+= directive-export-env +TESTS+= directive-export-literal +TESTS+= directive-for +TESTS+= directive-for-generating-endif +TESTS+= directive-if +TESTS+= directive-ifdef +TESTS+= directive-ifmake +TESTS+= directive-ifndef +TESTS+= directive-ifnmake +TESTS+= directive-info +TESTS+= directive-undef +TESTS+= directive-unexport +TESTS+= directive-unexport-env +TESTS+= directive-warning +TESTS+= directives TESTS+= dollar TESTS+= doterror TESTS+= dotwait +TESTS+= envfirst TESTS+= error TESTS+= # escape # broken by reverting POSIX changes TESTS+= export TESTS+= export-all TESTS+= export-env +TESTS+= export-variants TESTS+= forloop TESTS+= forsubst TESTS+= hash -TESTS+= # impsrc # broken by reverting POSIX changes +TESTS+= impsrc TESTS+= include-main +TESTS+= lint +TESTS+= make-exported TESTS+= misc TESTS+= moderrs TESTS+= modmatch TESTS+= modmisc -TESTS+= modorder TESTS+= modts TESTS+= modword +TESTS+= opt +TESTS+= opt-backwards +TESTS+= opt-chdir +TESTS+= opt-debug +TESTS+= opt-debug-g1 +TESTS+= opt-define +TESTS+= opt-env +TESTS+= opt-file +TESTS+= opt-ignore +TESTS+= opt-include-dir +TESTS+= opt-jobs +TESTS+= opt-jobs-internal +TESTS+= opt-keep-going +TESTS+= opt-m-include-dir +TESTS+= opt-no-action +TESTS+= opt-no-action-at-all +TESTS+= opt-query +TESTS+= opt-raw +TESTS+= opt-silent +TESTS+= opt-touch +TESTS+= opt-tracefile +TESTS+= opt-var-expanded +TESTS+= opt-var-literal +TESTS+= opt-warnings-as-errors +TESTS+= opt-where-am-i +TESTS+= opt-x-reduce-exported TESTS+= order -TESTS+= # phony-end # broken by reverting POSIX changes +TESTS+= phony-end TESTS+= posix TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= qequals -TESTS+= # suffixes # broken by reverting POSIX changes +TESTS+= recursive +TESTS+= sh +TESTS+= sh-dots +TESTS+= sh-jobs +TESTS+= sh-jobs-error +TESTS+= sh-leading-at +TESTS+= sh-leading-hyphen +TESTS+= sh-leading-plus +TESTS+= sh-meta-chars +TESTS+= sh-multi-line +TESTS+= sh-single-line +TESTS+= # suffixes # runs into an endless loop (try -dA) TESTS+= sunshcmd TESTS+= sysv TESTS+= ternary TESTS+= unexport TESTS+= unexport-env +TESTS+= use-inference +TESTS+= var-class +TESTS+= var-class-cmdline +TESTS+= var-class-env +TESTS+= var-class-global +TESTS+= var-class-local +TESTS+= var-class-local-legacy +TESTS+= var-op +TESTS+= var-op-append +TESTS+= var-op-assign +TESTS+= var-op-default +TESTS+= var-op-expand +TESTS+= var-op-shell TESTS+= varcmd +TESTS+= vardebug +TESTS+= varfind TESTS+= varmisc +TESTS+= varmod +TESTS+= varmod-assign +TESTS+= varmod-defined TESTS+= varmod-edge +TESTS+= varmod-exclam-shell +TESTS+= varmod-extension +TESTS+= varmod-gmtime +TESTS+= varmod-hash +TESTS+= varmod-head +TESTS+= varmod-ifelse +TESTS+= varmod-l-name-to-value +TESTS+= varmod-localtime +TESTS+= varmod-loop +TESTS+= varmod-match +TESTS+= varmod-match-escape +TESTS+= varmod-no-match +TESTS+= varmod-order +TESTS+= varmod-order-reverse +TESTS+= varmod-order-shuffle +TESTS+= varmod-path +TESTS+= varmod-quote +TESTS+= varmod-quote-dollar +TESTS+= varmod-range +TESTS+= varmod-remember +TESTS+= varmod-root +TESTS+= varmod-select-words +TESTS+= varmod-shell +TESTS+= varmod-subst +TESTS+= varmod-subst-regex +TESTS+= varmod-sysv +TESTS+= varmod-tail +TESTS+= varmod-to-abs +TESTS+= varmod-to-lower +TESTS+= varmod-to-many-words +TESTS+= varmod-to-one-word +TESTS+= varmod-to-separator +TESTS+= varmod-to-upper +TESTS+= varmod-undefined +TESTS+= varmod-unique +TESTS+= varname +TESTS+= varname-dollar +TESTS+= varname-dot-alltargets +TESTS+= varname-dot-curdir +TESTS+= varname-dot-includes +TESTS+= varname-dot-includedfromdir +TESTS+= varname-dot-includedfromfile +TESTS+= varname-dot-libs +TESTS+= varname-dot-make-dependfile +TESTS+= varname-dot-make-expand_variables +TESTS+= varname-dot-make-exported +TESTS+= varname-dot-make-jobs +TESTS+= varname-dot-make-jobs-prefix +TESTS+= varname-dot-make-level +TESTS+= varname-dot-make-makefile_preference +TESTS+= varname-dot-make-makefiles +TESTS+= varname-dot-make-meta-bailiwick +TESTS+= varname-dot-make-meta-created +TESTS+= varname-dot-make-meta-files +TESTS+= varname-dot-make-meta-ignore_filter +TESTS+= varname-dot-make-meta-ignore_paths +TESTS+= varname-dot-make-meta-ignore_patterns +TESTS+= varname-dot-make-meta-prefix +TESTS+= varname-dot-make-mode +TESTS+= varname-dot-make-path_filemon +TESTS+= varname-dot-make-pid +TESTS+= varname-dot-make-ppid +TESTS+= varname-dot-make-save_dollars +TESTS+= varname-dot-makeoverrides +TESTS+= varname-dot-newline +TESTS+= varname-dot-objdir +TESTS+= varname-dot-parsedir +TESTS+= varname-dot-parsefile +TESTS+= varname-dot-path +TESTS+= varname-dot-shell +TESTS+= varname-dot-targets +TESTS+= varname-empty +TESTS+= varname-make +TESTS+= varname-make_print_var_on_error +TESTS+= varname-makeflags +TESTS+= varname-pwd +TESTS+= varname-vpath +TESTS+= varparse-dynamic TESTS+= varquote TESTS+= varshell -# Override make flags for certain tests; default is -k. +# Additional environment variables for some of the tests. +# The base environment is -i PATH="$PATH". +ENV.envfirst= FROM_ENV=value-from-env +ENV.varmisc= FROM_ENV=env +ENV.varmisc+= FROM_ENV_BEFORE=env +ENV.varmisc+= FROM_ENV_AFTER=env + +# Override make flags for some of the tests; default is -k. +# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of +# settings FLAGS.test=-dv here, since that is closer to the test code. +FLAGS.archive= -dA +FLAGS.counter= -dv +FLAGS.directive-ifmake= first second FLAGS.doterror= # none +FLAGS.envfirst= -e +FLAGS.export= # none +FLAGS.lint= -dL -k +FLAGS.opt-debug-g1= -dg1 +FLAGS.opt-ignore= -i +FLAGS.opt-keep-going= -k +FLAGS.opt-no-action= -n +FLAGS.opt-query= -q +FLAGS.opt-var-expanded= -v VAR -v VALUE +FLAGS.opt-var-literal= -V VAR -V VALUE +FLAGS.opt-warnings-as-errors= -W FLAGS.order= -j1 +FLAGS.recursive= -dL +FLAGS.sh-leading-plus= -n +FLAGS.vardebug= -k -dv FROM_CMDLINE= +FLAGS.varmod-match-escape= -dv +FLAGS.varname-dot-shell= -dpv +FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain' # Some tests need extra post-processing. -SED_CMDS.modmisc+= -e 's,\(substitution error:\).*,\1 (details omitted),' -SED_CMDS.varshell+= -e 's,^[a-z]*sh: ,,' +SED_CMDS.opt-debug-g1= -e 's,${.CURDIR},CURDIR,' +SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d' +SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,,' +SED_CMDS.varmod-subst-regex+= \ + -e 's,\(Regex compilation error:\).*,\1 (details omitted),' +SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,' +SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,' SED_CMDS.varshell+= -e '/command/s,No such.*,not found,' +SED_CMDS.varname-dot-shell= -e 's, = /.*, = (details omitted),' +SED_CMDS.varname-dot-shell+= -e 's,"/[^"]*","(details omitted)",' +SED_CMDS.varname-dot-shell+= -e 's,\[/[^]]*\],[(details omitted)],' +# Some tests need an additional round of postprocessing. +POSTPROC.counter= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.deptgt-suffixes= \ + ${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p' +POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p' +POSTPROC.varmod-match-escape= ${TOOL_SED} -n -e '/^Pattern/p' +POSTPROC.varname-dot-shell= \ + awk '/\.SHELL/ || /^ParseReadLine/' +POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p' + +# Some tests reuse other tests, which makes them unnecessarily fragile. +export-all.rawout: export.mk +unexport.rawout: export.mk +unexport-env.rawout: export.mk + # End of the configuration section. .MAIN: all -.-include "Makefile.config" +.sinclude "Makefile.inc" +.sinclude "Makefile.config" -UNIT_TESTS:= ${srcdir} +UNIT_TESTS:= ${srcdir} .PATH: ${UNIT_TESTS} OUTFILES= ${TESTS:=.out} all: ${OUTFILES} -CLEANFILES+= *.rawout *.out *.status *.tmp *.core *.tmp +CLEANFILES= *.rawout *.out *.status *.tmp *.core *.tmp CLEANFILES+= obj*.[och] lib*.a # posix1.mk CLEANFILES+= issue* .[ab]* # suffixes.mk -CLEANRECURSIVE+= dir dummy # posix1.mk +CLEANDIRS= dir dummy # posix1.mk clean: rm -f ${CLEANFILES} -.if !empty(CLEANRECURSIVE) - rm -rf ${CLEANRECURSIVE} -.endif + rm -rf ${CLEANDIRS} TEST_MAKE?= ${.MAKE} TOOL_SED?= sed TOOL_TR?= tr TOOL_DIFF?= diff DIFF_FLAGS?= -u .if defined(.PARSEDIR) # ensure consistent results from sort(1) LC_ALL= C LANG= C .export LANG LC_ALL .endif +.if ${.MAKE.MODE:Unormal:Mmeta} != "" +# we don't need the noise +_MKMSG_TEST= : +.endif + # the tests are actually done with sub-makes. .SUFFIXES: .mk .rawout .out .mk.rawout: - @echo ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} - -@cd ${.OBJDIR} && \ - { ${TEST_MAKE} ${FLAGS.${.TARGET:R}:U-k} -f ${.IMPSRC} \ - 2>&1 ; echo $$? >${.TARGET:R}.status ; } > ${.TARGET}.tmp + @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} + @set -eu; \ + cd ${.OBJDIR}; \ + env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \ + ${TEST_MAKE} \ + -r -C ${.CURDIR} -f ${.IMPSRC} \ + ${FLAGS.${.TARGET:R}:U-k} \ + > ${.TARGET}.tmp 2>&1 \ + && status=$$? || status=$$?; \ + echo $$status > ${.TARGET:R}.status @mv ${.TARGET}.tmp ${.TARGET} # Post-process the test output so that the results can be compared. # # always pretend .MAKE was called 'make' _SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,' _SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' # strip ${.CURDIR}/ from the output _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' .rawout.out: - @echo postprocess ${.TARGET} @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \ - < ${.IMPSRC} > ${.TARGET}.tmp - @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp - @mv ${.TARGET}.tmp ${.TARGET} + < ${.IMPSRC} > ${.TARGET}.tmp1 + @${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2 + @rm ${.TARGET}.tmp1 + @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 + @mv ${.TARGET}.tmp2 ${.TARGET} # Compare all output files test: ${OUTFILES} .PHONY @failed= ; \ for test in ${TESTS}; do \ ${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \ || failed="$${failed}$${failed:+ }$${test}" ; \ done ; \ if [ -n "$${failed}" ]; then \ echo "Failed tests: $${failed}" ; false ; \ else \ echo "All tests passed" ; \ fi accept: @for test in ${TESTS}; do \ cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ || { echo "Replacing $${test}.exp" ; \ cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ done .if exists(${TEST_MAKE}) -${TESTS:=.rawout}: ${TEST_MAKE} +${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile .endif -.-include +.sinclude