1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-18 02:19:39 +00:00

Import bmake-20201101

Lots of new unit-tests increase code coverage.

Lots of refactoring, cleanup and simlpification to reduce
code size.

Fixes for Bug 223564 and 245807

Updates to dirdeps.mk and meta2deps.py
This commit is contained in:
Simon J. Gerraty 2020-11-07 19:39:21 +00:00
parent 6bbc783f48
commit 302da1a3d3
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/vendor/NetBSD/bmake/dist/; revision=367460
svn path=/vendor/NetBSD/bmake/20201101/; revision=367461; tag=vendor/NetBSD/bmake/20201101
387 changed files with 15784 additions and 12248 deletions

264
ChangeLog
View File

@ -1,3 +1,267 @@
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201101
Merge with NetBSD make, pick up
o negate NoExecute to GNode_ShouldExecute
o job.c: rename JobMatchShell to FindShellByName
extract EscapeShellDblQuot from JobPrintCommand
extract ParseRunOptions from JobPrintCommand
o var.c: extract ApplyModifiersIndirect from ApplyModifiers
treat malformed :range, :ts and :[...] as errors
add tests for the variable modifiers :[words] and :range
2020-10-31 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201031
Merge with NetBSD make, pick up
o format #include directives consistently
o do not look up local variables like .TARGET anywhere else
o main.c: Main_SetObjdir is first called for curdir which may be
readonly
reduce the scope where recursive expressions are detected
remove redundant :tl from getBoolean
clean up mkTempFile
o meta.c: simplify memory allocation in meta_create and meta_oodate
o parse.c: extract loadedfile_mmap from loadfile
o trace.c: document possible undefined behavior with .CURDIR
o var.c: make parsing of the :gmtime and :localtime modifiers stricter
rename ismeta to is_shell_metachar
remove debug logging for the :Q variable modifier
rename VarIsDynamic to VarnameIsDynamic
use consistent parameter order in varname parsing functions
extract ParseVarnameLong from Var_Parse
extract ParseVarnameShort from Var_Parse
fix type of ParseModifierPart parameter delim
extract IsEscapedModifierPart from ParseModifierPart
clean up ModifyWords
add test for combining the :@ and :? variable modifiers
2020-10-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201030
Merge with NetBSD make, pick up
o change char * to void * in Var_Value
o make iterating over HashTable simpler
o rename VAR_CMD to VAR_CMDLINE
o cond.c: clean up is_separator
fix parse error in string literal in conditional
o main.c: do not use objdir that is not writable
in lint mode, exit with error status on errors
o parse.c: clean up StrContainsWord
fix out-of-bounds pointer in ParseTrackInput
o var.c: rename Str_SYSVMatch and its parameters
remove unsatisfiable conditions in Var_Set_with_flags
document where the variable name is expanded
fix documentation for VARP_SUB_ONE
rename VAR_EXPORTED_YES to VAR_EXPORTED_SOME
document VAR_READONLY
prevent appending to read-only variables
extract MayExport from Var_Export1
remove redundant evaluations in VarFind
replace VarFindFlags with a simple Boolean
rename FIND_CMD to FIND_CMDLINE, to match VAR_CMDLINE
2020-10-28 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201028
Merge with NetBSD make, pick up
o rename defIncPath to defSysIncPath
o initialize all CmdOpts fields
o lst.c: inline Vector_Get
o main.c: refactor main extract
InitMaxJobs,InitObjdir,InitVarMake,InitRandom,
ReadMakefiles,CleanUp,InitVpath,ReadBuiltinRules,
InitDefIncPath,CmdOpts_Init,UnlimitFiles
o parse.c: merge curFile into includes
rename predecessor to order_pred
sort ParseSpecial alphabetically
remove unused, undocumented .NOEXPORT
rename ParseSpecial enum values consistently
rename some fields of struct IFile
2020-10-26 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201026
Merge with NetBSD make, pick up
o group the command line options and arguments into a struct
o rename GNode.cmgn to youngestChild
o rename hash functions to identify the type name
o negate OP_NOP and rename it to GNode_IsTarget
o add GNode_Path to access the path of a GNode
o remove macros MIN and MAX
o remove unused Lst_Find and Lst_FindFrom
o arch.c: and make Arch_FindLib simpler
clean up code layout
make Arch_ParseArchive simpler
o cond.c: inline CondFindStrMatch into FuncMake
o dir.c: replace Dir_CopyDir with Dir_CopyDirSearchPath
omit trailing space in debug output for expanding file patterns
refactor DirMatchFiles
document that the SearchPath of Dir_FindFile may be NULL
remove UNCONST from Dir_Expand
inline DirFindName
o for.c: clean up code for handling .for loops
o hash.c: print hash in debug log with fixed width
clean up hash table functions
reduce amount of string hashing
o job.c: refactor JobDeleteTarget
use proper enum constants for aborting
convert result of JobStart from macros to enum
convert abort reason macros to enum
rework Job_CheckCommands to reduce indentation
rename Shell fields
add field names in declaration of DEFSHELL_CUSTOM
convert JobState and JobFlags to enum types
move handling of the "..." command to JobPrintCommands
o lst.c: clean up
refactor LstNodeNew
remove Lst_Open, Lst_Next, Lst_Close
remove code for circular lists from Lst_Next
o main.c: do not attempt to read .MAKE.DEPENFILE if set to
/dev/null or anything starting with "no"
convert macros for debug flags into enum
o make.c: inline Lst_Copy in Make_ExpandUse
o meta.c: inline Lst_Find in meta_oodate
make Lst_RemoveIf simpler in meta_oodate
o parse.c: convert error level for Parse_Error to an enum
o suff.c: properly terminate debug output with newline
add more details to DEBUG_SRC log
replace Dir_CopyDir with Dir_CopyDirSearchPath
don't modify GNode name while rebuilding the suffix graph
o var.c: reduce duplicate code in VarFind
2020-10-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201022
Merge with NetBSD make, pick up
o more refactoring and simplification to reduce code size
o var.c: extract CanonicalVarname from VarFind
o make.c: extract UpdateImplicitParentsVars from Make_Update
o main.c: extract PrintVar from doPrintVars
extract HandlePWD from main
o lst.c: inline simple Lst getters
remove unused Lst_ForEach
o job.c: move struct Shell from job.h to job.c
o more unit tests
2020-10-19 Simon J Gerraty <sjg@beast.crufty.net>
* configure.in: remove inappropriate use of AC_INCLUDES_DEFAULT
2020-10-18 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201018
Merge with NetBSD make, pick up
o remove USE_IOVEC
o rename some Hash_* apis to Hash*
o replace execError with execDie
o rename Lst_Init to Lst_New
o add tags to enum types
o rename Stack to Vector
o parse.c: more refactoring
o unit-tests: make some tests use line buffered stdout
o unit-tests/Makefile: in meta mode do not make all tests depend on
Makefile, it isn't necessary.
2020-10-10 Simon J Gerraty <sjg@beast.crufty.net>
* main.c: check for CTL_HW being defined.
* unit-tests/Makefile: ensure export tests output are POSIX compliant
disable opt-debug-jobs test until it works on ubuntu
* VERSION (_MAKE_VERSION): 20201010
Merge with NetBSD make, pick up
o dir.c: remove pathname limit for Dir_FindHereOrAbove
o hash.c: replace strcpy with memcpy in Hash_CreateEntry
o main.c: extract init_machine and init_machine_arch from main
allow to disable debug logging options
o parse.c: enable format string truncation warnings
extract parsing of sources from ParseDoDependency
split ParseDoSrc into smaller functions
hide implementation details from Parse_DoVar
clean up parsing of variable assignments
split Parse_DoVar into manageable pieces
don't modify the given line during Parse_DoVar
fix out-of-bounds memory access in Parse_DoVar
fix parsing of the :sh assignment modifier
o var.c: rework memory allocation for the name of variables
extract ApplyModifier_Literal into separate function
in lint mode, reject modifiers without delimiter
do not export variable names starting with '-'
o fix double-free bug in -DCLEANUP mode
o more cleanup to enable higher warnings level
o more unit tests
2020-10-02 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201002
Merge with NetBSD make, pick up
o dir.c: use hash table for looking up open directories by name
o main.c: clean up option handling
o parse.c: add missing const for Parse_AddIncludeDir
o var.c: ApplyModifier_To, update pp in each branch
o remove redundant function prototypes
o more unit tests
2020-10-01 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201001
Merge with NetBSD make, pick up
o compat.c: comment about "..."
2020-09-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200930
Merge with NetBSD make, pick up
o job.c: split Job.jobPipe into 2 separate fields
replace Lst_Open with direct iteration
o lst.c: remove redundant assertions
o targ.c: replace Lst_Open with direct iteration
o var.c: fix bug in evaluation of indirect variable modifiers
extract ApplyModifier_Quote into separate function
o make debug logging simpler
2020-09-27 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200927
Merge with NetBSD make, pick up
o parse.c: ensure parse errors result in 'stopped in' message.
o compat.c: make parameter of Compat_RunCommand const
o main.c: extract InitVarTarget from main
o parse.c: rename ParseFinishLine to FinishDependencyGroup
refactor ParseDoDependency
o var.c: Var_Subst no longer returns string result
rename Var_ParsePP back to Var_Parse
in lint mode, improve error handling for undefined variables
extract ParseVarname from Var_Parse
o rename Lst_ForEach to Lst_ForEachUntil
o inline Lst_ForEachUntil in several cases
o clean up API for finding and creating GNodes
o fix assertion failure in -j mode with .END node
o inline and remove LstNode_Prev and LstNode_Next
o use fine-grained type names for lists and their nodes
o more unit tests
2020-09-11 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200911
Merge with NetBSD make, pick up
o cond.c: split EvalComparison into smaller functions
reorder parameters of condition parsing functions
reduce code size in CondParser_Eval
rename CondGetString to CondParser_String
add CondLexer_SkipWhitespace
group the condition parsing state into a struct
in CondGetString, replace repeated Buf_Add with Buf_AddStr
o migrate Var_Parse to Var_ParsePP
o add wrappers around ctype.h functions
o lst.c: use a stack instead of a list for the nested include path
o more unit tests
2020-09-04 Simon J Gerraty <sjg@beast.crufty.net>
* make-bootstrap.sh.in: adjust object list
2020-09-02 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20200902

158
FILES
View File

@ -65,18 +65,16 @@ sigcompat.c
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/archive.exp
unit-tests/archive.mk
unit-tests/cmd-interrupt.exp
unit-tests/cmd-interrupt.mk
unit-tests/cmdline.exp
@ -99,8 +97,8 @@ 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-cmp-unary.exp
unit-tests/cond-cmp-unary.mk
unit-tests/cond-func-commands.exp
unit-tests/cond-func-commands.mk
unit-tests/cond-func-defined.exp
@ -113,6 +111,8 @@ 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-func.exp
unit-tests/cond-func.mk
unit-tests/cond-late.exp
unit-tests/cond-late.mk
unit-tests/cond-op-and.exp
@ -135,26 +135,36 @@ 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/cond-undef-lint.exp
unit-tests/cond-undef-lint.mk
unit-tests/cond1.exp
unit-tests/cond1.mk
unit-tests/cond2.exp
unit-tests/cond2.mk
unit-tests/counter-append.exp
unit-tests/counter-append.mk
unit-tests/counter.exp
unit-tests/counter.mk
unit-tests/dep-colon-bug-cross-file.exp
unit-tests/dep-colon-bug-cross-file.mk
unit-tests/dep-colon.exp
unit-tests/dep-colon.mk
unit-tests/dep-double-colon-indep.exp
unit-tests/dep-double-colon-indep.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-percent.exp
unit-tests/dep-percent.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-end.exp
unit-tests/depsrc-end.mk
unit-tests/depsrc-exec.exp
unit-tests/depsrc-exec.mk
unit-tests/depsrc-ignore.exp
@ -185,10 +195,10 @@ 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-usebefore.exp
unit-tests/depsrc-usebefore.mk
unit-tests/depsrc-wait.exp
unit-tests/depsrc-wait.mk
unit-tests/depsrc.exp
@ -199,6 +209,8 @@ 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-jobs.exp
unit-tests/deptgt-end-jobs.mk
unit-tests/deptgt-end.exp
unit-tests/deptgt-end.mk
unit-tests/deptgt-error.exp
@ -239,10 +251,12 @@ 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/dir.exp
unit-tests/dir.mk
unit-tests/directive-dinclude.exp
unit-tests/directive-dinclude.mk
unit-tests/directive-elif.exp
unit-tests/directive-elif.mk
unit-tests/directive-elifdef.exp
@ -261,14 +275,18 @@ 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-gmake.exp
unit-tests/directive-export-gmake.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-for.exp
unit-tests/directive-for.mk
unit-tests/directive-hyphen-include.exp
unit-tests/directive-hyphen-include.mk
unit-tests/directive-if.exp
unit-tests/directive-if.mk
unit-tests/directive-ifdef.exp
@ -279,8 +297,14 @@ unit-tests/directive-ifndef.exp
unit-tests/directive-ifndef.mk
unit-tests/directive-ifnmake.exp
unit-tests/directive-ifnmake.mk
unit-tests/directive-include-fatal.exp
unit-tests/directive-include-fatal.mk
unit-tests/directive-include.exp
unit-tests/directive-include.mk
unit-tests/directive-info.exp
unit-tests/directive-info.mk
unit-tests/directive-sinclude.exp
unit-tests/directive-sinclude.mk
unit-tests/directive-undef.exp
unit-tests/directive-undef.mk
unit-tests/directive-unexport-env.exp
@ -317,20 +341,20 @@ 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/hanoi-include.exp
unit-tests/hanoi-include.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/job-output-long-lines.exp
unit-tests/job-output-long-lines.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
@ -345,10 +369,56 @@ unit-tests/opt-backwards.exp
unit-tests/opt-backwards.mk
unit-tests/opt-chdir.exp
unit-tests/opt-chdir.mk
unit-tests/opt-debug-all.exp
unit-tests/opt-debug-all.mk
unit-tests/opt-debug-archive.exp
unit-tests/opt-debug-archive.mk
unit-tests/opt-debug-cond.exp
unit-tests/opt-debug-cond.mk
unit-tests/opt-debug-curdir.exp
unit-tests/opt-debug-curdir.mk
unit-tests/opt-debug-dir.exp
unit-tests/opt-debug-dir.mk
unit-tests/opt-debug-errors.exp
unit-tests/opt-debug-errors.mk
unit-tests/opt-debug-file.exp
unit-tests/opt-debug-file.mk
unit-tests/opt-debug-for.exp
unit-tests/opt-debug-for.mk
unit-tests/opt-debug-graph1.exp
unit-tests/opt-debug-graph1.mk
unit-tests/opt-debug-graph2.exp
unit-tests/opt-debug-graph2.mk
unit-tests/opt-debug-graph3.exp
unit-tests/opt-debug-graph3.mk
unit-tests/opt-debug-hash.exp
unit-tests/opt-debug-hash.mk
unit-tests/opt-debug-jobs.exp
unit-tests/opt-debug-jobs.mk
unit-tests/opt-debug-lint.exp
unit-tests/opt-debug-lint.mk
unit-tests/opt-debug-loud.exp
unit-tests/opt-debug-loud.mk
unit-tests/opt-debug-making.exp
unit-tests/opt-debug-making.mk
unit-tests/opt-debug-meta.exp
unit-tests/opt-debug-meta.mk
unit-tests/opt-debug-no-rm.exp
unit-tests/opt-debug-no-rm.mk
unit-tests/opt-debug-parse.exp
unit-tests/opt-debug-parse.mk
unit-tests/opt-debug-suff.exp
unit-tests/opt-debug-suff.mk
unit-tests/opt-debug-targets.exp
unit-tests/opt-debug-targets.mk
unit-tests/opt-debug-var.exp
unit-tests/opt-debug-var.mk
unit-tests/opt-debug-varraw.exp
unit-tests/opt-debug-varraw.mk
unit-tests/opt-debug-x-trace.exp
unit-tests/opt-debug-x-trace.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
@ -395,6 +465,8 @@ unit-tests/opt.exp
unit-tests/opt.mk
unit-tests/order.exp
unit-tests/order.mk
unit-tests/parse-var.exp
unit-tests/parse-var.mk
unit-tests/phony-end.exp
unit-tests/phony-end.mk
unit-tests/posix.exp
@ -425,12 +497,34 @@ 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/shell-csh.exp
unit-tests/shell-csh.mk
unit-tests/shell-custom.exp
unit-tests/shell-custom.mk
unit-tests/shell-ksh.exp
unit-tests/shell-ksh.mk
unit-tests/shell-sh.exp
unit-tests/shell-sh.mk
unit-tests/suff-add-later.exp
unit-tests/suff-add-later.mk
unit-tests/suff-clear-regular.exp
unit-tests/suff-clear-regular.mk
unit-tests/suff-clear-single.exp
unit-tests/suff-clear-single.mk
unit-tests/suff-lookup.exp
unit-tests/suff-lookup.mk
unit-tests/suff-main.exp
unit-tests/suff-main.mk
unit-tests/suff-rebuild.exp
unit-tests/suff-rebuild.mk
unit-tests/suff-transform-endless.exp
unit-tests/suff-transform-endless.mk
unit-tests/suff-transform-expand.exp
unit-tests/suff-transform-expand.mk
unit-tests/suff-transform-select.exp
unit-tests/suff-transform-select.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
@ -461,8 +555,12 @@ 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-sunsh.exp
unit-tests/var-op-sunsh.mk
unit-tests/var-op.exp
unit-tests/var-op.mk
unit-tests/var-recursive.exp
unit-tests/var-recursive.mk
unit-tests/varcmd.exp
unit-tests/varcmd.mk
unit-tests/vardebug.exp
@ -555,12 +653,12 @@ 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-includes.exp
unit-tests/varname-dot-includes.mk
unit-tests/varname-dot-libs.exp
unit-tests/varname-dot-libs.mk
unit-tests/varname-dot-make-dependfile.exp
@ -623,8 +721,12 @@ 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-jobs.exp
unit-tests/varname-make_print_var_on_error-jobs.mk
unit-tests/varname-make_print_var_on_error.exp
unit-tests/varname-make_print_var_on_error.mk
unit-tests/varname-makefile.exp
unit-tests/varname-makefile.mk
unit-tests/varname-makeflags.exp
unit-tests/varname-makeflags.mk
unit-tests/varname-pwd.exp
@ -635,6 +737,10 @@ unit-tests/varname.exp
unit-tests/varname.mk
unit-tests/varparse-dynamic.exp
unit-tests/varparse-dynamic.mk
unit-tests/varparse-mod.exp
unit-tests/varparse-mod.mk
unit-tests/varparse-undef-partial.exp
unit-tests/varparse-undef-partial.mk
unit-tests/varquote.exp
unit-tests/varquote.mk
unit-tests/varshell.exp

View File

@ -1,4 +1,4 @@
# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $
# $Id: Makefile,v 1.113 2020/10/26 17:55:09 sjg Exp $
PROG= bmake
@ -20,7 +20,6 @@ SRCS= \
metachar.c \
parse.c \
str.c \
strlist.c \
suff.c \
targ.c \
trace.c \

View File

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

727
arch.c

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
.Dd August 28, 2020
.Dd November 1, 2020
.Dt BMAKE 1
.Os
.Sh NAME
@ -1872,7 +1872,7 @@ has been defined and has commands associated with it.
.Ar Expression
may also be an arithmetic or string comparison.
Variable expansion is
performed on both sides of the comparison, after which the integral
performed on both sides of the comparison, after which the numerical
values are compared.
A value is interpreted as hexadecimal if it is
preceded by 0x, otherwise it is decimal; octal numbers are not supported.
@ -1882,7 +1882,7 @@ variable expansion, either the left or right hand side of a
.Ql Ic ==
or
.Ql Ic "!="
operator is not an integral value, then
operator is not a numerical value, then
string comparison is performed between the expanded
variables.
If no relational operator is given, it is assumed that the expanded

View File

@ -1197,11 +1197,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
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
numerical 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
operator is not a numerical 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 of a string comparison.
@ -1568,4 +1568,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
There is no way of escaping a space character in a filename.
FreeBSD 11.3 August 28, 2020 FreeBSD 11.3
FreeBSD 11.3 November 1, 2020 FreeBSD 11.3

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -115,7 +115,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: boot-strap,v 1.51 2020/02/19 16:46:23 sjg Exp $
# $Id: boot-strap,v 1.53 2020/09/16 02:12:01 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
@ -458,17 +458,25 @@ op_all() {
else
op_test
MAKE_VERSION=`sed -n '/^_MAKE_VERSION/ { s,.*= *,,;p; }' $srcdir/Makefile`
echo You can install by running:
echo
echo $0 $cmd_args op=install
echo
echo "Use --install-prefix=/something to install somewhere other than $prefix"
echo "Use --install-destdir=/somewhere to set DESTDIR during install"
echo "Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin"
echo "Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION"
echo "Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink"
echo "Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk"
cat << EOM
You can install by running:
$0 $cmd_args op=install
Use --install-prefix=/something to install somewhere other than $prefix
Use --install-destdir=/somewhere to set DESTDIR during install
Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin
Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION
Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink
Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk
EOM
fi
cat << EOM
Note: bmake.cat1 contains ANSI escape sequences.
You may need the -r or -R option to more/less to view it correctly.
EOM
}
op_$op

120
buf.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */
/* $NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,115 +69,103 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93";
#else
__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $");
#endif
#endif /* not lint */
#endif
/* Functions for automatically-expanded null-terminated buffers. */
/* Automatically-expanding null-terminated buffers. */
#include <limits.h>
#include "make.h"
/* Extend the buffer for adding a single byte. */
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $");
/* Make space in the buffer for adding a single byte. */
void
Buf_Expand_1(Buffer *bp)
Buf_Expand_1(Buffer *buf)
{
bp->size += MAX(bp->size, 16);
bp->buffer = bmake_realloc(bp->buffer, bp->size);
buf->cap += buf->cap > 16 ? buf->cap : 16;
buf->data = bmake_realloc(buf->data, buf->cap);
}
/* Add the given bytes to the buffer. */
/* Add the bytes to the buffer. */
void
Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes)
Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
{
size_t count = bp->count;
char *ptr;
size_t old_len = buf->len;
char *end;
if (__predict_false(count + numBytes >= bp->size)) {
bp->size += MAX(bp->size, numBytes + 16);
bp->buffer = bmake_realloc(bp->buffer, bp->size);
if (__predict_false(old_len + bytes_len >= buf->cap)) {
buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16;
buf->data = bmake_realloc(buf->data, buf->cap);
}
ptr = bp->buffer + count;
bp->count = count + numBytes;
memcpy(ptr, bytesPtr, numBytes);
ptr[numBytes] = '\0';
end = buf->data + old_len;
buf->len = old_len + bytes_len;
memcpy(end, bytes, bytes_len);
end[bytes_len] = '\0';
}
/* Add the bytes between start and end to the buffer. */
void
Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end)
Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end)
{
Buf_AddBytes(bp, start, (size_t)(end - start));
Buf_AddBytes(buf, start, (size_t)(end - start));
}
/* Add the given string to the buffer. */
/* Add the string to the buffer. */
void
Buf_AddStr(Buffer *bp, const char *str)
Buf_AddStr(Buffer *buf, const char *str)
{
Buf_AddBytes(bp, str, strlen(str));
Buf_AddBytes(buf, str, strlen(str));
}
/* Add the given number to the buffer. */
/* Add the number to the buffer. */
void
Buf_AddInt(Buffer *bp, int n)
Buf_AddInt(Buffer *buf, 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
str_size = max_sign_chars + max_decimal_digits + 1
};
char buf[buf_size];
char str[str_size];
size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n);
Buf_AddBytes(bp, buf, len);
size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
Buf_AddBytes(buf, str, 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. */
* Returns the data and optionally the length of the data. */
char *
Buf_GetAll(Buffer *bp, size_t *numBytesPtr)
Buf_GetAll(Buffer *buf, size_t *out_len)
{
if (numBytesPtr != NULL)
*numBytesPtr = bp->count;
return bp->buffer;
if (out_len != NULL)
*out_len = buf->len;
return buf->data;
}
/* Mark the buffer as empty, so it can be filled with data again. */
void
Buf_Empty(Buffer *bp)
Buf_Empty(Buffer *buf)
{
bp->count = 0;
bp->buffer[0] = '\0';
buf->len = 0;
buf->data[0] = '\0';
}
/* Initialize a buffer.
* If the given initial size is 0, a reasonable default is used. */
* If the given initial capacity is 0, a reasonable default is used. */
void
Buf_Init(Buffer *bp, size_t size)
Buf_Init(Buffer *buf, size_t cap)
{
if (size <= 0) {
size = 256;
}
bp->size = size;
bp->count = 0;
bp->buffer = bmake_malloc(size);
bp->buffer[0] = '\0';
if (cap <= 0)
cap = 256;
buf->cap = cap;
buf->len = 0;
buf->data = bmake_malloc(cap);
buf->data[0] = '\0';
}
/* Reset the buffer.
@ -186,15 +174,15 @@ Buf_Init(Buffer *bp, size_t size)
char *
Buf_Destroy(Buffer *buf, Boolean freeData)
{
char *data = buf->buffer;
char *data = buf->data;
if (freeData) {
free(data);
data = NULL;
}
buf->size = 0;
buf->count = 0;
buf->buffer = NULL;
buf->cap = 0;
buf->len = 0;
buf->data = NULL;
return data;
}
@ -211,10 +199,10 @@ char *
Buf_DestroyCompact(Buffer *buf)
{
#if BUF_COMPACT_LIMIT > 0
if (buf->size - buf->count >= BUF_COMPACT_LIMIT) {
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
/* We trust realloc to be smart */
char *data = bmake_realloc(buf->buffer, buf->count + 1);
data[buf->count] = '\0';
char *data = bmake_realloc(buf->data, buf->len + 1);
data[buf->len] = '\0'; /* XXX: unnecessary */
Buf_Destroy(buf, FALSE);
return data;
}

38
buf.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */
/* $NetBSD: buf.h,v 1.34 2020/09/27 16:59:02 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -81,9 +81,9 @@
/* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer {
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) */
size_t cap; /* Allocated size of the buffer, including the null */
size_t len; /* Number of bytes in buffer, excluding the null */
char *data; /* The buffer itself (always null-terminated) */
} Buffer;
/* If we aren't on NetBSD, __predict_false() might not be defined. */
@ -94,22 +94,28 @@ typedef struct Buffer {
void Buf_Expand_1(Buffer *);
/* Buf_AddByte adds a single byte to a buffer. */
static inline void MAKE_ATTR_UNUSED
Buf_AddByte(Buffer *bp, char byte)
static inline MAKE_ATTR_UNUSED void
Buf_AddByte(Buffer *buf, 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;
size_t old_len = buf->len++;
char *end;
if (__predict_false(old_len + 1 >= buf->cap))
Buf_Expand_1(buf);
end = buf->data + old_len;
end[0] = byte;
end[1] = '\0';
}
static inline size_t MAKE_ATTR_UNUSED
Buf_Size(const Buffer *bp)
static inline MAKE_ATTR_UNUSED size_t
Buf_Len(const Buffer *buf)
{
return bp->count;
return buf->len;
}
static inline MAKE_ATTR_UNUSED Boolean
Buf_EndsWith(const Buffer *buf, char ch)
{
return buf->len > 0 && buf->data[buf->len - 1] == ch;
}
void Buf_AddBytes(Buffer *, const char *, size_t);

448
compat.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */
/* $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,19 +69,6 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
#else
__RCSID("$NetBSD: compat.c,v 1.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
@ -91,33 +78,30 @@ __RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
* - friendly variable substitution.
*
* Interface:
* Compat_Run Initialize things for this module and recreate
* thems as need creatin'
* Compat_Run Initialize things for this module and recreate
* thems as need creatin'
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include "wait.h"
#include <sys/types.h>
#include <sys/stat.h>
#include "wait.h"
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include "make.h"
#include "hash.h"
#include "dir.h"
#include "job.h"
#include "metachar.h"
#include "pathnames.h"
#include "make.h"
#include "dir.h"
#include "job.h"
#include "metachar.h"
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $");
static GNode *curTarg = NULL;
static GNode *ENDNode;
static void CompatInterrupt(int);
static GNode *curTarg = NULL;
static pid_t compatChild;
static int compatSigno;
@ -128,15 +112,12 @@ static int compatSigno;
static void
CompatDeleteTarget(GNode *gn)
{
if ((gn != NULL) && !Targ_Precious (gn)) {
char *p1;
const char *file = Var_Value(TARGET, gn, &p1);
if (gn != NULL && !Targ_Precious(gn)) {
const char *file = GNode_VarTarget(gn);
if (!noExecute && eunlink(file) != -1) {
if (!opts.noExecute && eunlink(file) != -1) {
Error("*** %s removed", file);
}
bmake_free(p1);
}
}
@ -155,22 +136,24 @@ CompatInterrupt(int signo)
CompatDeleteTarget(curTarg);
if ((curTarg != NULL) && !Targ_Precious (curTarg)) {
if (curTarg != NULL && !Targ_Precious(curTarg)) {
/*
* Run .INTERRUPT only if hit with interrupt signal
*/
if (signo == SIGINT) {
gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
gn = Targ_FindNode(".INTERRUPT");
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.
* If there is a child running, pass the signal on.
* We will exist after it has exited.
*/
compatSigno = signo;
if (compatChild > 0) {
@ -181,11 +164,8 @@ CompatInterrupt(int 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.
/* 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
@ -193,45 +173,38 @@ CompatInterrupt(int signo)
*
* 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)
Compat_RunCommand(const char *cmdp, GNode *gn)
{
char *cmdStart; /* Start of expanded command */
char *cp, *bp;
Boolean silent, /* Don't print command */
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 */
Boolean useShell; /* TRUE if command should be executed
char *cmdStart; /* Start of expanded command */
char *bp;
Boolean silent; /* Don't print command */
Boolean 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 */
StringListNode *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 */
Boolean useShell; /* TRUE if command should be executed
* using a shell */
char * volatile cmd = (char *)cmdp;
GNode *gn = (GNode *)gnp;
const char *volatile cmd = cmdp;
silent = (gn->type & OP_SILENT) != 0;
errCheck = !(gn->type & OP_IGNORE);
doIt = FALSE;
/* Luckily the commands don't end up in a string pool, otherwise
* this comparison could match too early, in a dependency using "..."
* for delayed commands, run in parallel mode, using the same shell
* command line more than once; see JobPrintCommand.
* TODO: write a unit-test to protect against this potential bug. */
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?).
*/
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
/* TODO: handle errors */
if (*cmdStart == '\0') {
free(cmdStart);
@ -240,17 +213,19 @@ CompatRunCommand(void *cmdp, void *gnp)
cmd = cmdStart;
LstNode_Set(cmdNode, cmdStart);
if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
assert(ENDNode != NULL);
Lst_Append(ENDNode->commands, cmdStart);
return 0;
if (gn->type & OP_SAVE_CMDS) {
GNode *endNode = Targ_GetEndNode();
if (gn != endNode) {
Lst_Append(endNode->commands, cmdStart);
return 0;
}
}
if (strcmp(cmdStart, "...") == 0) {
gn->type |= OP_SAVE_CMDS;
return 0;
}
while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
switch (*cmd) {
case '@':
silent = !DEBUG(LOUD);
@ -267,7 +242,7 @@ CompatRunCommand(void *cmdp, void *gnp)
cmd++;
}
while (isspace((unsigned char)*cmd))
while (ch_isspace(*cmd))
cmd++;
/*
@ -295,14 +270,14 @@ CompatRunCommand(void *cmdp, void *gnp)
* meta characters as documented in make(1).
*/
useShell = needshell(cmd, FALSE);
useShell = needshell(cmd);
#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)) {
if (!silent || !GNode_ShouldExecute(gn)) {
printf("%s\n", cmd);
fflush(stdout);
}
@ -311,11 +286,10 @@ CompatRunCommand(void *cmdp, void *gnp)
* If we're not supposed to execute any commands, this is as far as
* we go...
*/
if (!doIt && NoExecute(gn)) {
if (!doIt && !GNode_ShouldExecute(gn)) {
return 0;
}
if (DEBUG(JOB))
fprintf(debug_file, "Execute: '%s'\n", cmd);
DEBUG1(JOB, "Execute: '%s'\n", cmd);
if (useShell) {
/*
@ -374,8 +348,7 @@ CompatRunCommand(void *cmdp, void *gnp)
}
#endif
(void)execvp(av[0], (char *const *)UNCONST(av));
execError("exec", av[0]);
_exit(1);
execDie("exec", av[0]);
}
free(mav);
@ -394,85 +367,77 @@ CompatRunCommand(void *cmdp, void *gnp)
/*
* 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: ",
gn->name);
for (cp = cmd; *cp; ) {
if (isspace((unsigned char)*cp)) {
fprintf(debug_file, " ");
while (isspace((unsigned char)*cp))
cp++;
} else {
fprintf(debug_file, "%c", *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;
}
}
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
JobReapChild(retstat, reason, FALSE); /* not ours? */
if (retstat == -1 && errno != EINTR) {
break;
} else {
Fatal("error in wait: %d: %s", retstat, strerror(errno));
/*NOTREACHED*/
}
}
if (retstat < 0)
Fatal("error in wait: %d: %s", retstat, strerror(errno));
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)) {
const char *cp;
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
gn->name);
for (cp = cmd; *cp; ) {
if (ch_isspace(*cp)) {
debug_printf(" ");
while (ch_isspace(*cp))
cp++;
} else {
debug_printf("%c", *cp);
cp++;
}
}
debug_printf("\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 (opts.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;
}
}
free(cmdStart);
compatChild = 0;
if (compatSigno) {
@ -483,32 +448,41 @@ CompatRunCommand(void *cmdp, void *gnp)
return status;
}
/*-
*-----------------------------------------------------------------------
* Compat_Make --
* Make a target.
static void
RunCommands(GNode *gn)
{
StringListNode *ln;
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum;
if (Compat_RunCommand(cmd, gn) != 0)
break;
}
}
static void
MakeNodes(GNodeList *gnodes, GNode *pgn)
{
GNodeListNode *ln;
for (ln = gnodes->first; ln != NULL; ln = ln->next) {
GNode *cohort = ln->datum;
Compat_Make(cohort, pgn);
}
}
/* Make a target.
*
* If an error is detected and not being ignored, the process exits.
*
* 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.
*
*-----------------------------------------------------------------------
* gn The node to make
* pgn Parent to abort if necessary
*/
int
Compat_Make(void *gnp, void *pgnp)
void
Compat_Make(GNode *gn, GNode *pgn)
{
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)) {
if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
/*
* First mark ourselves to be made, then apply whatever transformations
* the suffix module thinks are necessary. Once that's done, we can
@ -519,45 +493,37 @@ Compat_Make(void *gnp, void *pgnp)
*/
gn->flags |= REMAKE;
gn->made = BEINGMADE;
if ((gn->type & OP_MADE) == 0)
if (!(gn->type & OP_MADE))
Suff_FindDeps(gn);
Lst_ForEach(gn->children, Compat_Make, gn);
if ((gn->flags & REMAKE) == 0) {
MakeNodes(gn->children, gn);
if (!(gn->flags & REMAKE)) {
gn->made = ABORTED;
pgn->flags &= ~(unsigned)REMAKE;
goto cohorts;
}
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
char *p1;
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
bmake_free(p1);
}
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
/*
* All the children were made ok. Now cmgn->mtime contains the
* All the children were made ok. Now youngestChild->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)) {
DEBUG1(MAKE, "Examining %s...", gn->name);
if (!Make_OODate(gn)) {
gn->made = UPTODATE;
if (DEBUG(MAKE)) {
fprintf(debug_file, "up-to-date.\n");
}
DEBUG0(MAKE, "up-to-date.\n");
goto cohorts;
} else if (DEBUG(MAKE)) {
fprintf(debug_file, "out-of-date.\n");
}
} else
DEBUG0(MAKE, "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) {
if (opts.queryFlag) {
exit(1);
}
@ -572,26 +538,24 @@ Compat_Make(void *gnp, void *pgnp)
* 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)) {
if (Targ_Ignore(gn))
gn->type |= OP_IGNORE;
}
if (Targ_Silent(gn)) {
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)) {
if (!opts.touchFlag || (gn->type & OP_MAKE)) {
curTarg = gn;
#ifdef USE_META
if (useMeta && !NoExecute(gn)) {
if (useMeta && GNode_ShouldExecute(gn)) {
meta_job_start(NULL, gn);
}
#endif
Lst_ForEach(gn->commands, CompatRunCommand, gn);
RunCommands(gn);
curTarg = NULL;
} else {
Job_Touch(gn, (gn->type & OP_SILENT) != 0);
@ -600,7 +564,7 @@ Compat_Make(void *gnp, void *pgnp)
gn->made = ERROR;
}
#ifdef USE_META
if (useMeta && !NoExecute(gn)) {
if (useMeta && GNode_ShouldExecute(gn)) {
if (meta_job_finish(NULL) != 0)
gn->made = ERROR;
}
@ -619,24 +583,19 @@ Compat_Make(void *gnp, void *pgnp)
pgn->flags |= CHILDMADE;
Make_TimeStamp(pgn, gn);
}
} else if (keepgoing) {
} else if (opts.keepgoing) {
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.
*/
/* Already had an error when making this. Tell the parent to abort. */
pgn->flags &= ~(unsigned)REMAKE;
} else {
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
char *p1;
const char *target = Var_Value(TARGET, gn, &p1);
const char *target = GNode_VarTarget(gn);
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
bmake_free(p1);
}
switch(gn->made) {
case BEINGMADE:
@ -661,8 +620,7 @@ Compat_Make(void *gnp, void *pgnp)
}
cohorts:
Lst_ForEach(gn->cohorts, Compat_Make, pgnp);
return 0;
MakeNodes(gn->cohorts, pgn);
}
/* Initialize this module and start making.
@ -671,35 +629,33 @@ Compat_Make(void *gnp, void *pgnp)
* targs The target nodes to re-create
*/
void
Compat_Run(Lst targs)
Compat_Run(GNodeList *targs)
{
GNode *gn = NULL;/* Current root target */
int errors; /* Number of targets not remade due to errors */
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) {
if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
bmake_signal(SIGINT, CompatInterrupt);
}
if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) {
if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
bmake_signal(SIGTERM, CompatInterrupt);
}
if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) {
if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
bmake_signal(SIGHUP, CompatInterrupt);
}
if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
bmake_signal(SIGQUIT, CompatInterrupt);
}
ENDNode = Targ_FindNode(".END", TARG_CREATE);
ENDNode->type = OP_SPECIAL;
/* Create the .END node now, to keep the (debug) output of the
* counter.mk test the same as before 2020-09-23. This implementation
* detail probably doesn't matter though. */
(void)Targ_GetEndNode();
/*
* If the user has defined a .BEGIN target, execute the commands attached
* to it.
*/
if (!queryFlag) {
gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
if (!opts.queryFlag) {
gn = Targ_FindNode(".BEGIN");
if (gn != NULL) {
Compat_Make(gn, gn);
if (gn->made == ERROR) {
@ -719,11 +675,11 @@ Compat_Run(Lst 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.
* 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)) {
@ -734,7 +690,7 @@ Compat_Run(Lst targs)
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;
errors++;
}
}
@ -742,7 +698,9 @@ Compat_Run(Lst targs)
* If the user has defined a .END target, run its commands.
*/
if (errors == 0) {
Compat_Make(ENDNode, ENDNode);
GNode *endNode = Targ_GetEndNode();
Compat_Make(endNode, endNode);
/* XXX: Did you mean endNode->made instead of gn->made? */
if (gn->made == ERROR) {
PrintOnError(gn, "\nStop.");
exit(1);

940
cond.c

File diff suppressed because it is too large Load Diff

27
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for bmake 20200710.
# Generated by GNU Autoconf 2.69 for bmake 20201018.
#
# Report bugs to <sjg@NetBSD.org>.
#
@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
PACKAGE_VERSION='20200710'
PACKAGE_STRING='bmake 20200710'
PACKAGE_VERSION='20201018'
PACKAGE_STRING='bmake 20201018'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
@ -1254,7 +1254,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures bmake 20200710 to adapt to many kinds of systems.
\`configure' configures bmake 20201018 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1315,7 +1315,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of bmake 20200710:";;
short | recursive ) echo "Configuration of bmake 20201018:";;
esac
cat <<\_ACEOF
@ -1323,16 +1323,16 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-pwd-override disable \$PWD overriding getcwd()
--disable-pwd-override disable $PWD overriding getcwd()
--disable-check-make-chdir disable make trying to guess
when it should automatically cd \${.CURDIR}
when it should automatically cd ${.CURDIR}
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions
--without-makefile dissable use of generated makefile
--without-meta dissable use of meta-mode
--without-makefile disable use of generated makefile
--without-meta disable use of meta-mode
--with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev
--with-machine=MACHINE explicitly set MACHINE
--with-force-machine=MACHINE set FORCE_MACHINE
@ -1421,7 +1421,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
bmake configure 20200710
bmake configure 20201018
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@ -2001,7 +2001,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by bmake $as_me 20200710, which was
It was created by bmake $as_me 20201018, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@ -4543,7 +4543,6 @@ if test $bmake_path_max -gt 1024; then
fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
$ac_includes_default
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
if ${ac_cv_header_sys_wait_h+:} false; then :
@ -6665,7 +6664,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by bmake $as_me 20200710, which was
This file was extended by bmake $as_me 20201018, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -6727,7 +6726,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
bmake config.status 20200710
bmake config.status 20201018
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"

View File

@ -1,11 +1,11 @@
dnl
dnl RCSid:
dnl $Id: configure.in,v 1.66 2020/07/10 16:34:38 sjg Exp $
dnl $Id: configure.in,v 1.67 2020/10/19 19:47:50 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ(2.50)
AC_INIT([bmake], [20200710], [sjg@NetBSD.org])
AC_INIT([bmake], [20201018], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@ -38,7 +38,7 @@ CYGWIN*|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
AC_ARG_WITH(makefile,
[ --without-makefile dissable use of generated makefile],
[ --without-makefile disable use of generated makefile],
[case "${withval}" in
yes|no) use_makefile=${withval};;
*) AC_MSG_ERROR(bad value ${withval} given for makefile) ;;
@ -46,7 +46,7 @@ esac])
dnl
use_meta=yes
AC_ARG_WITH(meta,
[ --without-meta dissable use of meta-mode],
[ --without-meta disable use of meta-mode],
[case "${withval}" in
yes|no) use_meta=${withval};;
*) AC_MSG_ERROR(bad value ${withval} given for meta) ;;
@ -128,7 +128,6 @@ dnl AC_C_CROSS
dnl
dnl Checks for header files.
AC_INCLUDES_DEFAULT
AC_HEADER_SYS_WAIT
AC_HEADER_DIRENT
dnl Keep this list sorted
@ -352,7 +351,7 @@ dnl
dnl And this can be handy to do with out.
dnl
AC_ARG_ENABLE(pwd-override,
[ --disable-pwd-override disable \$PWD overriding getcwd()],
[ --disable-pwd-override disable $PWD overriding getcwd()],
[case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;;
@ -363,7 +362,7 @@ dnl Just for grins
dnl
AC_ARG_ENABLE(check-make-chdir,
[ --disable-check-make-chdir disable make trying to guess
when it should automatically cd \${.CURDIR}],
when it should automatically cd ${.CURDIR}],
[case "${enableval}" in
yes) ;;
no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;;

1187
dir.c

File diff suppressed because it is too large Load Diff

55
dir.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */
/* $NetBSD: dir.h,v 1.32 2020/10/25 10:00:20 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -75,20 +75,21 @@
#ifndef MAKE_DIR_H
#define MAKE_DIR_H
/* 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
/* A cache for the filenames in a directory. */
typedef struct CachedDir {
char *name; /* Name of directory, either absolute or
* relative to the current directory.
* The name is not normalized in any way,
* that is, "." and "./." are different.
*
* Not sure what happens when .CURDIR is
* assigned a new value; see Parse_DoVar. */
int refCount; /* Number of SearchPaths with this directory */
int hits; /* The number of times a file in this
* directory has been found */
Hash_Table files; /* Hash set of files in directory */
} Path;
struct make_stat {
time_t mst_mtime;
mode_t mst_mode;
};
HashTable files; /* Hash set of files in directory;
* all values are NULL. */
} CachedDir;
void Dir_Init(void);
void Dir_InitDir(const char *);
@ -97,18 +98,24 @@ void Dir_InitDot(void);
void Dir_End(void);
void Dir_SetPATH(void);
Boolean Dir_HasWildcards(const char *);
void Dir_Expand(const char *, Lst, Lst);
char *Dir_FindFile(const char *, Lst);
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_Expand(const char *, SearchPath *, StringList *);
char *Dir_FindFile(const char *, SearchPath *);
char *Dir_FindHereOrAbove(const char *, const char *);
time_t Dir_MTime(GNode *, Boolean);
CachedDir *Dir_AddDir(SearchPath *, const char *);
char *Dir_MakeFlags(const char *, SearchPath *);
void Dir_ClearPath(SearchPath *);
void Dir_Concat(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void);
void Dir_PrintPath(Lst);
void Dir_PrintPath(SearchPath *);
void Dir_Destroy(void *);
void *Dir_CopyDir(void *);
SearchPath *Dir_CopyDirSearchPath(void);
/* Stripped-down variant of struct stat. */
struct make_stat {
time_t mst_mtime;
mode_t mst_mode;
};
int cached_lstat(const char *, struct make_stat *);
int cached_stat(const char *, struct make_stat *);

23
enum.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */
/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -27,20 +27,9 @@
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 <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $");
#endif
#endif
#include "make.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "enum.h"
MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $");
/* Convert a bitset into a string representation, showing the names of the
* individual bits.
@ -59,7 +48,7 @@ Enum_FlagsToString(char *buf, size_t buf_size,
size_t name_len;
if ((value & spec->es_value) != spec->es_value)
continue;
continue;
value &= ~spec->es_value;
assert(buf_size >= sep_len + 1);
@ -93,8 +82,8 @@ 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;
if (value == spec->es_value)
return spec->es_name;
}
abort(/* unknown enum value */);
}

44
enum.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */
/* $NetBSD: enum.h,v 1.12 2020/09/25 15:54:50 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -34,7 +34,7 @@
#include <stddef.h>
typedef struct {
typedef struct EnumToStringSpec {
int es_value;
const char *es_name;
} EnumToStringSpec;
@ -107,16 +107,38 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
enum { typnam ## _ ## ToStringSize = sizeof joined }
/* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 2 flags. */
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_1(v1), \
ENUM__SPEC_1(v2)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_1(v1), \
ENUM__JOIN_STR_1(v2)))
/* 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__SPEC_2(v1, v2), \
ENUM__SPEC_1(v3)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_1(v3)))
ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_1(v3)))
/* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 6 flags. */
#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_4(v1, v2, v3, v4), \
ENUM__SPEC_2(v5, v6)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_2(v5, v6)))
/* Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 8 flags. */
@ -156,8 +178,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
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, \
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), \
@ -179,8 +201,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
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, \
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)), \
@ -188,6 +210,6 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
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)))
v25, v26, v27, v28, v29, v30, v31, v32)))
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: filemon.h,v 1.2 2020/01/22 22:10:36 sjg Exp $ */
/* $NetBSD: filemon.h,v 1.3 2020/10/18 11:49:47 rillig Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@ -29,8 +29,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FILEMON_H_
#define FILEMON_H_
#ifndef MAKE_FILEMON_H
#define MAKE_FILEMON_H
#include <sys/types.h>
@ -50,4 +50,4 @@ int filemon_setpid_child(const struct filemon *, pid_t);
int filemon_readfd(const struct filemon *);
int filemon_process(struct filemon *);
#endif /* FILEMON_H_ */
#endif /* MAKE_FILEMON_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: filemon_ktrace.c,v 1.2 2020/01/19 20:22:57 riastradh Exp $ */
/* $NetBSD: filemon_ktrace.c,v 1.3 2020/10/18 11:54:43 rillig Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@ -478,7 +478,7 @@ filemon_dispatch(struct filemon *F)
*/
/* XXX What to do if syscall code doesn't match? */
if (S->i == S->npath && S->syscode == ret->ktr_code)
(*S->show)(F, S, ret);
S->show(F, S, ret);
/* Free the state now that it is no longer active. */
for (i = 0; i < S->i; i++)
@ -771,7 +771,7 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call)
{
const register_t *args = (const void *)&call[1];
int status = args[0];
int status = (int)args[0];
if (F->out) {
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
@ -806,7 +806,7 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
if (call->ktr_argsize < 2)
return NULL;
flags = args[1];
flags = (int)args[1];
if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, &show_open_readwrite);
@ -827,8 +827,8 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
if (call->ktr_argsize < 3)
return NULL;
fd = args[0];
flags = args[2];
fd = (int)args[0];
flags = (int)args[2];
if (fd == AT_CWD) {
if ((flags & O_RDWR) == O_RDWR)

548
for.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */
/* $NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@ -29,88 +29,136 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
#else
__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.
* Handling of .for/.endfor loops in a makefile.
*
* Interface:
* For_Eval Evaluate the loop in the passed line.
* For_Run Run accumulated loop
* For loops are of the form:
*
*/
#include "make.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 <variable> in <varlist>
* .for <varname...> in <value...>
* ...
* .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.
* When a .for line is parsed, all following lines are accumulated into a
* buffer, up to but excluding the corresponding .endfor line. To find the
* corresponding .endfor, the number of nested .for and .endfor directives
* are counted.
*
* 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.
* During parsing, any nested .for loops are just passed through; they get
* handled recursively in For_Eval when the enclosing .for loop is evaluated
* in For_Run.
*
* When the .for loop has been parsed completely, the variable expressions
* for the iteration variables are replaced with expressions of the form
* ${:Uvalue}, and then this modified body is "included" as a special file.
*
* Interface:
* For_Eval Evaluate the loop in the passed line.
*
* For_Run Run accumulated loop
*/
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $");
/* The .for loop substitutes the items as ${:U<value>...}, which means
* that characters that break this syntax must be backslash-escaped. */
typedef enum ForEscapes {
FOR_SUB_ESCAPE_CHAR = 0x0001,
FOR_SUB_ESCAPE_BRACE = 0x0002,
FOR_SUB_ESCAPE_PAREN = 0x0004
} ForEscapes;
static int forLevel = 0; /* Nesting level */
/* One of the variables to the left of the "in" in a .for loop. */
typedef struct ForVar {
char *name;
size_t len;
} ForVar;
/*
* State of a for loop.
*/
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;
typedef struct For {
Buffer body; /* Unexpanded body of the loop */
Vector /* of ForVar */ vars; /* Iteration variables */
Words items; /* Substitution items */
Buffer curBody; /* Expanded body of the current iteration */
/* Is any of the names 1 character long? If so, when the variable values
* are substituted, the parser must handle $V expressions as well, not
* only ${V} and $(V). */
Boolean short_var;
unsigned int sub_next; /* Where to continue iterating */
} For;
static For *accumFor; /* Loop being accumulated */
static void
For_Free(For *arg)
ForAddVar(For *f, const char *name, size_t len)
{
Buf_Destroy(&arg->buf, TRUE);
strlist_clean(&arg->vars);
strlist_clean(&arg->items);
free(arg->parse_buf);
free(arg);
ForVar *var = Vector_Push(&f->vars);
var->name = bmake_strldup(name, len);
var->len = len;
}
/*-
*-----------------------------------------------------------------------
* For_Eval --
* Evaluate the for loop in the passed line. The line
* looks like this:
* .for <variable> in <varlist>
static void
For_Free(For *f)
{
Buf_Destroy(&f->body, TRUE);
while (f->vars.len > 0) {
ForVar *var = Vector_Pop(&f->vars);
free(var->name);
}
Vector_Done(&f->vars);
Words_Free(f->items);
Buf_Destroy(&f->curBody, TRUE);
free(f);
}
static ForEscapes
GetEscapes(const char *word)
{
const char *p;
ForEscapes escapes = 0;
for (p = word; *p != '\0'; p++) {
switch (*p) {
case ':':
case '$':
case '\\':
escapes |= FOR_SUB_ESCAPE_CHAR;
break;
case ')':
escapes |= FOR_SUB_ESCAPE_PAREN;
break;
case '}':
escapes |= FOR_SUB_ESCAPE_BRACE;
break;
}
}
return escapes;
}
static Boolean
IsFor(const char *p)
{
return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
}
static Boolean
IsEndfor(const char *p)
{
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
(p[6] == '\0' || ch_isspace(p[6]));
}
/* Evaluate the for loop in the passed line. The line looks like this:
* .for <varname...> in <value...>
*
* Input:
* line Line to parse
@ -119,178 +167,130 @@ For_Free(For *arg)
* 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_Eval(const char *line)
{
For *new_for;
char *ptr = line, *sub;
size_t len;
int escapes;
unsigned char ch;
Words words;
For *f;
const char *p;
/* Skip the '.' and any following whitespace */
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
continue;
p = line + 1; /* skip the '.' */
cpp_skip_whitespace(&p);
/*
* 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) {
if (!IsFor(p)) {
if (IsEndfor(p)) {
Parse_Error(PARSE_FATAL, "for-less endfor");
return -1;
}
return 0;
}
ptr += 3;
p += 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);
f = bmake_malloc(sizeof *f);
Buf_Init(&f->body, 0);
Vector_Init(&f->vars, sizeof(ForVar));
f->items.words = NULL;
f->items.freeIt = NULL;
Buf_Init(&f->curBody, 0);
f->short_var = FALSE;
f->sub_next = 0;
/* Grab the variables. Terminate on "in". */
for (;; ptr += len) {
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
if (*ptr == '\0') {
for (;;) {
size_t len;
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
For_Free(new_for);
For_Free(f);
return -1;
}
for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
/* XXX: This allows arbitrary variable names; see directive-for.mk. */
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
continue;
if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
ptr += 2;
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
break;
}
if (len == 1)
new_for->short_var = 1;
strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len);
f->short_var = TRUE;
ForAddVar(f, p, len);
p += len;
}
if (strlist_num(&new_for->vars) == 0) {
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
For_Free(new_for);
For_Free(f);
return -1;
}
while (*ptr && isspace((unsigned char)*ptr))
ptr++;
/*
* Make a list with the remaining words
* The values are substituted as ${:U<value>...} 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(ptr, VAR_GLOBAL, VARE_WANTRES);
/*
* Split into words allowing for quoted strings.
*/
words = Str_Words(sub, FALSE);
free(sub);
cpp_skip_whitespace(&p);
{
size_t n;
char *items;
(void)Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items);
/* TODO: handle errors */
f->items = Str_Words(items, FALSE);
free(items);
for (n = 0; n < words.len; n++) {
ptr = words.words[n];
if (!*ptr)
continue;
escapes = 0;
while ((ch = *ptr++)) {
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.words[n]),
escapes);
}
if (f->items.len == 1 && f->items.words[0][0] == '\0')
f->items.len = 0; /* .for var in ${:U} */
}
Words_Free(words);
{
size_t nitems, nvars;
if ((len = strlist_num(&new_for->items)) > 0 &&
len % (n = strlist_num(&new_for->vars))) {
if ((nitems = f->items.len) > 0 && nitems % (nvars = f->vars.len)) {
Parse_Error(PARSE_FATAL,
"Wrong number of words (%zu) in .for substitution list"
" with %zu vars", len, n);
" with %zu variables", nitems, nvars);
/*
* 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);
f->items.len = 0;
}
}
Buf_Init(&new_for->buf, 0);
accumFor = new_for;
accumFor = f;
forLevel = 1;
return 1;
}
/*
* Add another line to a .for loop.
* Returns 0 when the matching .endfor is reached.
* Returns FALSE when the matching .endfor is reached.
*/
int
For_Accum(char *line)
Boolean
For_Accum(const char *line)
{
char *ptr = line;
const char *ptr = line;
if (*ptr == '.') {
ptr++;
cpp_skip_whitespace(&ptr);
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
continue;
if (strncmp(ptr, "endfor", 6) == 0 &&
(isspace((unsigned char)ptr[6]) || !ptr[6])) {
if (DEBUG(FOR))
(void)fprintf(debug_file, "For: end for %d\n", forLevel);
if (IsEndfor(ptr)) {
DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0)
return 0;
} else if (strncmp(ptr, "for", 3) == 0 &&
isspace((unsigned char)ptr[3])) {
return FALSE;
} else if (IsFor(ptr)) {
forLevel++;
if (DEBUG(FOR))
(void)fprintf(debug_file, "For: new loop %d\n", forLevel);
DEBUG1(FOR, "For: new loop %d\n", forLevel);
}
}
Buf_AddStr(&accumFor->buf, line);
Buf_AddByte(&accumFor->buf, '\n');
return 1;
Buf_AddStr(&accumFor->body, line);
Buf_AddByte(&accumFor->body, '\n');
return TRUE;
}
@ -326,23 +326,25 @@ for_var_len(const char *var)
return 0;
}
/* While expanding the body of a .for loop, write the item in the ${:U...}
* expression, escaping characters as needed. See ApplyModifier_Defined. */
static void
for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
{
ForEscapes escapes = GetEscapes(item);
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))) {
if (!(escapes & (ech == ')' ? ~(unsigned)FOR_SUB_ESCAPE_BRACE
: ~(unsigned)FOR_SUB_ESCAPE_PAREN))) {
Buf_AddStr(cmds, item);
return;
}
/* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
while ((ch = *item++) != 0) {
/* Escape ':', '$', '\\' and 'ech' - these will be removed later by
* :U processing, see ApplyModifier_Defined. */
while ((ch = *item++) != '\0') {
if (ch == '$') {
size_t len = for_var_len(item);
if (len != 0) {
@ -357,111 +359,141 @@ for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
}
}
static char *
For_Iterate(void *v_arg, size_t *ret_len)
/* While expanding the body of a .for loop, replace expressions like
* ${i}, ${i:...}, $(i) or $(i:...) with their ${:U...} expansion. */
static void
SubstVarLong(For *f, const char **pp, const char **inout_mark, char ech)
{
For *arg = v_arg;
int i;
char *var;
char *cp;
char *cmd_cp;
char *body_end;
char ch;
Buffer cmds;
size_t cmd_len;
size_t i;
const char *p = *pp;
if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
for (i = 0; i < f->vars.len; i++) {
ForVar *forVar = Vector_Get(&f->vars, i);
char *var = forVar->name;
size_t vlen = forVar->len;
/* XXX: undefined behavior for p if vlen is longer than p? */
if (memcmp(p, var, vlen) != 0)
continue;
/* XXX: why test for backslash here? */
if (p[vlen] != ':' && p[vlen] != ech && p[vlen] != '\\')
continue;
/* Found a variable match. Replace with :U<value> */
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
Buf_AddStr(&f->curBody, ":U");
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], ech);
p += vlen;
*inout_mark = p;
break;
}
*pp = p;
}
/* While expanding the body of a .for loop, replace single-character
* variable expressions like $i with their ${:U...} expansion. */
static void
SubstVarShort(For *f, char const ch, const char **pp, const char **inout_mark)
{
const char *p = *pp;
size_t i;
/* Probably a single character name, ignore $$ and stupid ones. */
if (!f->short_var || strchr("}):$", ch) != NULL) {
p++;
*pp = p;
return;
}
for (i = 0; i < f->vars.len; i++) {
ForVar *var = Vector_Get(&f->vars, i);
const char *varname = var->name;
if (varname[0] != ch || varname[1] != '\0')
continue;
/* Found a variable match. Replace with ${:U<value>} */
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
Buf_AddStr(&f->curBody, "{:U");
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
Buf_AddByte(&f->curBody, '}');
*inout_mark = ++p;
break;
}
*pp = p;
}
/*
* 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 substitutions 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.
*/
static char *
ForIterate(void *v_arg, size_t *out_len)
{
For *f = v_arg;
const char *p;
const char *mark; /* where the last replacement left off */
const char *body_end;
char *cmds_str;
if (f->sub_next + f->vars.len > f->items.len) {
/* No more iterations */
For_Free(arg);
For_Free(f);
return NULL;
}
free(arg->parse_buf);
arg->parse_buf = NULL;
Buf_Empty(&f->curBody);
/*
* 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, &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;
mark = Buf_GetAll(&f->body, NULL);
body_end = mark + Buf_Len(&f->body);
for (p = mark; (p = strchr(p, '$')) != NULL;) {
char ch, ech;
ch = *++p;
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) {
cp++;
p++;
/* Check variable name against the .for loop variables */
STRLIST_FOREACH(var, &arg->vars, i) {
size_t vlen = strlist_info(&arg->vars, i);
if (memcmp(cp, var, vlen) != 0)
continue;
if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\')
continue;
/* Found a variable match. Replace with :U<value> */
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;
}
SubstVarLong(f, &p, &mark, ech);
continue;
}
if (ch == 0)
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<value>} */
Buf_AddBytesBetween(&cmds, cmd_cp, cp);
Buf_AddStr(&cmds, "{:U");
cmd_cp = ++cp;
for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
Buf_AddByte(&cmds, '}');
break;
}
SubstVarShort(f, ch, &p, &mark);
}
Buf_AddBytesBetween(&cmds, cmd_cp, body_end);
Buf_AddBytesBetween(&f->curBody, mark, body_end);
cp = Buf_Destroy(&cmds, FALSE);
if (DEBUG(FOR))
(void)fprintf(debug_file, "For: loop body:\n%s", cp);
*out_len = Buf_Len(&f->curBody);
cmds_str = Buf_GetAll(&f->curBody, NULL);
DEBUG1(FOR, "For: loop body:\n%s", cmds_str);
arg->sub_next += strlist_num(&arg->vars);
f->sub_next += f->vars.len;
arg->parse_buf = cp;
*ret_len = strlen(cp);
return cp;
return cmds_str;
}
/* Run the for loop, imitating the actions of an include file. */
void
For_Run(int lineno)
{
For *arg;
arg = accumFor;
For *f = accumFor;
accumFor = NULL;
if (strlist_num(&arg->items) == 0) {
if (f->items.len == 0) {
/* Nothing to expand - possibly due to an earlier syntax error. */
For_Free(arg);
For_Free(f);
return;
}
Parse_SetInput(NULL, lineno, -1, For_Iterate, arg);
Parse_SetInput(NULL, lineno, -1, ForIterate, f);
}

468
hash.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */
/* $NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,336 +69,242 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
#else
__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $");
#endif
#endif /* not lint */
#endif
/* Hash tables with string keys. */
/* 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 "make.h"
/*
* Forward references to local procedures that are used before they're
* defined:
*/
static void RebuildTable(Hash_Table *);
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $");
/*
* The following defines the ratio of # entries to # buckets
* at which we rebuild the table to make it larger.
* 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.
*
* Input:
* 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.
*/
void
Hash_InitTable(Hash_Table *t, int numBuckets)
/* This hash function matches Gosling's emacs and java.lang.String. */
static unsigned int
hash(const char *key, size_t *out_keylen)
{
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->maxchain = 0;
t->bucketsSize = i;
t->bucketsMask = i - 1;
t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
while (--i >= 0)
*hp++ = NULL;
unsigned int h = 0;
const char *p = key;
while (*p != '\0')
h = (h << 5) - h + (unsigned char)*p++;
if (out_keylen != NULL)
*out_keylen = (size_t)(p - key);
return h;
}
/* 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)
unsigned int
Hash_Hash(const char *key)
{
struct Hash_Entry **hp, *h, *nexth = NULL;
int i;
return hash(key, NULL);
}
for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) {
for (h = *hp++; h != NULL; h = nexth) {
nexth = h->next;
free(h);
static HashEntry *
HashTable_Find(HashTable *t, unsigned int h, const char *key)
{
HashEntry *e;
unsigned int chainlen = 0;
#ifdef DEBUG_HASH_LOOKUP
DEBUG4(HASH, "%s: %p h=%08x key=%s\n", __func__, t, h, key);
#endif
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
chainlen++;
if (e->key_hash == h && strcmp(e->key, key) == 0)
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
return e;
}
/* Set up the hash table. */
void
HashTable_Init(HashTable *t)
{
unsigned int n = 16, i;
HashEntry **buckets = bmake_malloc(sizeof(*buckets) * n);
for (i = 0; i < n; i++)
buckets[i] = NULL;
t->buckets = buckets;
t->bucketsSize = n;
t->numEntries = 0;
t->bucketsMask = n - 1;
t->maxchain = 0;
}
/* Remove everything from the hash table and frees up the memory. */
void
HashTable_Done(HashTable *t)
{
HashEntry **buckets = t->buckets;
size_t i, n = t->bucketsSize;
for (i = 0; i < n; i++) {
HashEntry *he = buckets[i];
while (he != NULL) {
HashEntry *next = he->next;
free(he);
he = next;
}
}
free(t->buckets);
/*
* Set up the hash table to cause memory faults on any future access
* attempts until re-initialization.
*/
#ifdef CLEANUP
t->buckets = NULL;
#endif
}
/* Searches the hash table for an entry corresponding to the key.
*
* Input:
* t Hash table to search.
* key A hash key.
*
* Results:
* 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)
/* Find the entry corresponding to the key, or return NULL. */
HashEntry *
HashTable_FindEntry(HashTable *t, const char *key)
{
Hash_Entry *e;
unsigned h;
const char *p;
int chainlen;
if (t == NULL || t->buckets == NULL) {
return NULL;
}
HASH(h, key, p);
p = key;
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)
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
return e;
unsigned int h = hash(key, NULL);
return HashTable_Find(t, h, key);
}
/* Searches the hash table for an entry corresponding to the key.
* If no entry is found, then one is created.
*
* Input:
* t Hash table to search.
* key A hash key.
* newPtr Filled with TRUE if new entry created,
* FALSE otherwise.
*/
Hash_Entry *
Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
/* Find the value corresponding to the key, or return NULL. */
void *
HashTable_FindValue(HashTable *t, const char *key)
{
Hash_Entry *e;
unsigned h;
const char *p;
int keylen;
int chainlen;
struct Hash_Entry **hp;
HashEntry *he = HashTable_FindEntry(t, key);
return he != NULL ? he->value : NULL;
}
/*
* Hash the key. As a side effect, save the length (strlen) of the
* key in case we need to create the entry.
*/
HASH(h, key, p);
keylen = p - key;
p = key;
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;
break;
/* Find the value corresponding to the key and the precomputed hash,
* or return NULL. */
void *
HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
{
HashEntry *he = HashTable_Find(t, h, key);
return he != NULL ? he->value : NULL;
}
/* Make the hash table larger. Any bucket numbers from the old table become
* invalid; the hash codes stay valid though. */
static void
HashTable_Enlarge(HashTable *t)
{
unsigned int oldSize = t->bucketsSize;
HashEntry **oldBuckets = t->buckets;
unsigned int newSize = 2 * oldSize;
unsigned int newMask = newSize - 1;
HashEntry **newBuckets = bmake_malloc(sizeof(*newBuckets) * newSize);
size_t i;
for (i = 0; i < newSize; i++)
newBuckets[i] = NULL;
for (i = 0; i < oldSize; i++) {
HashEntry *he = oldBuckets[i];
while (he != NULL) {
HashEntry *next = he->next;
he->next = newBuckets[he->key_hash & newMask];
newBuckets[he->key_hash & newMask] = he;
he = next;
}
}
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->bucketsSize)
RebuildTable(t);
e = bmake_malloc(sizeof(*e) + keylen);
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++;
free(oldBuckets);
if (newPtr != NULL)
*newPtr = TRUE;
return e;
t->bucketsSize = newSize;
t->bucketsMask = newMask;
t->buckets = newBuckets;
DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
__func__, t, t->bucketsSize, t->numEntries, t->maxchain);
t->maxchain = 0;
}
/* Delete the given hash table entry and free memory associated with it. */
void
Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
/* Find or create an entry corresponding to the key.
* Return in out_isNew whether a new entry has been created. */
HashEntry *
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
{
Hash_Entry **hp, *p;
size_t keylen;
unsigned int h = hash(key, &keylen);
HashEntry *he = HashTable_Find(t, h, key);
if (e == NULL)
return;
for (hp = &t->buckets[e->namehash & t->bucketsMask];
(p = *hp) != NULL; hp = &p->next) {
if (p == e) {
*hp = p->next;
if (he != NULL) {
if (out_isNew != NULL)
*out_isNew = FALSE;
return he;
}
if (t->numEntries >= rebuildLimit * t->bucketsSize)
HashTable_Enlarge(t);
he = bmake_malloc(sizeof(*he) + keylen);
he->value = NULL;
he->key_hash = h;
memcpy(he->key, key, keylen + 1);
he->next = t->buckets[h & t->bucketsMask];
t->buckets[h & t->bucketsMask] = he;
t->numEntries++;
if (out_isNew != NULL)
*out_isNew = TRUE;
return he;
}
/* Delete the entry from the table and free the associated memory. */
void
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
{
HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
HashEntry *p;
for (; (p = *ref) != NULL; ref = &p->next) {
if (p == he) {
*ref = 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.
*
* 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.
*/
Hash_Entry *
Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr)
/* Set things up for iterating over all entries in the hash table. */
void
HashIter_Init(HashIter *hi, HashTable *t)
{
searchPtr->table = t;
searchPtr->nextBucket = 0;
searchPtr->entry = NULL;
return Hash_EnumNext(searchPtr);
hi->table = t;
hi->nextBucket = 0;
hi->entry = NULL;
}
/* Returns the next entry in the hash table, or NULL if the end of the table
* is reached.
*
* Input:
* searchPtr Area used to keep state about search.
*/
Hash_Entry *
Hash_EnumNext(Hash_Search *searchPtr)
/* Return the next entry in the hash table, or NULL if the end of the table
* is reached. */
HashEntry *
HashIter_Next(HashIter *hi)
{
Hash_Entry *e;
Hash_Table *t = searchPtr->table;
HashTable *t = hi->table;
HashEntry *he = hi->entry;
HashEntry **buckets = t->buckets;
unsigned int bucketsSize = t->bucketsSize;
/*
* 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->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->nextBucket >= t->bucketsSize)
if (he != NULL)
he = he->next; /* skip the most recently returned entry */
while (he == NULL) { /* find the next nonempty chain */
if (hi->nextBucket >= bucketsSize)
return NULL;
e = t->buckets[searchPtr->nextBucket++];
he = buckets[hi->nextBucket++];
}
searchPtr->entry = e;
return e;
}
/* 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;
int oldsize;
oldhp = t->buckets;
oldsize = i = t->bucketsSize;
i <<= 1;
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->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;
hi->entry = he;
return he;
}
void
Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
HashTable_DebugStats(HashTable *t, const char *name)
{
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);
DEBUG4(HASH, "HashTable %s: size=%u numEntries=%u maxchain=%u\n",
name, t->bucketsSize, t->numEntries, t->maxchain);
}

72
hash.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */
/* $NetBSD: hash.h,v 1.31 2020/10/25 19:19:07 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -78,54 +78,54 @@
#define MAKE_HASH_H
/* A single key-value entry in the hash table. */
typedef struct Hash_Entry {
struct Hash_Entry *next; /* Used to link together all the entries
typedef struct HashEntry {
struct HashEntry *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;
void *value;
unsigned int key_hash; /* hash value of the key */
char key[1]; /* key string, variable length */
} HashEntry;
/* The hash table containing the entries. */
typedef struct Hash_Table {
Hash_Entry **buckets; /* Pointers to Hash_Entry, one
typedef struct HashTable {
HashEntry **buckets; /* Pointers to HashEntry, one
* for each bucket in the table. */
int bucketsSize;
int numEntries; /* Number of entries in the table. */
int bucketsMask; /* Used to select the bucket for a hash. */
int maxchain; /* max length of chain detected */
} Hash_Table;
unsigned int bucketsSize;
unsigned int numEntries; /* Number of entries in the table. */
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
unsigned int maxchain; /* max length of chain detected */
} HashTable;
/*
* The following structure is used by the searching routines
* to record where we are in the search.
*/
typedef struct Hash_Search {
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;
/* State of an iteration over all entries in a table. */
typedef struct HashIter {
HashTable *table; /* Table being searched. */
unsigned int nextBucket; /* Next bucket to check (after current). */
HashEntry *entry; /* Next entry to check in current bucket. */
} HashIter;
static inline void * MAKE_ATTR_UNUSED
Hash_GetValue(Hash_Entry *h)
static inline MAKE_ATTR_UNUSED void *
HashEntry_Get(HashEntry *h)
{
return h->value;
}
static inline void MAKE_ATTR_UNUSED
Hash_SetValue(Hash_Entry *h, void *datum)
static inline MAKE_ATTR_UNUSED void
HashEntry_Set(HashEntry *h, void *datum)
{
h->value = datum;
}
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 *);
void HashTable_Init(HashTable *);
void HashTable_Done(HashTable *);
HashEntry *HashTable_FindEntry(HashTable *, const char *);
void *HashTable_FindValue(HashTable *, const char *);
unsigned int Hash_Hash(const char *);
void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
void HashTable_DeleteEntry(HashTable *, HashEntry *);
void HashTable_DebugStats(HashTable *, const char *);
void HashIter_Init(HashIter *, HashTable *);
HashEntry *HashIter_Next(HashIter *);
#endif /* MAKE_HASH_H */

1610
job.c

File diff suppressed because it is too large Load Diff

192
job.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */
/* $NetBSD: job.h,v 1.58 2020/10/26 21:34:10 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -73,10 +73,10 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
/*-
* job.h --
* Definitions pertaining to the running of jobs in parallel mode.
/*
* Running of jobs in parallel mode.
*/
#ifndef MAKE_JOB_H
#define MAKE_JOB_H
@ -110,28 +110,6 @@ emul_poll(struct pollfd *fd, int nfd, int timeout);
*/
#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;
@ -139,122 +117,90 @@ struct pollfd;
# 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' */
typedef enum JobState {
JOB_ST_FREE = 0, /* Job is available */
JOB_ST_SETUP = 1, /* Job is allocated but otherwise invalid */
JOB_ST_RUNNING = 3, /* Job is running, pid valid */
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
} JobState;
int jobPipe[2]; /* Pipe for reading output from job */
typedef enum JobFlags {
/* Ignore non-zero exits */
JOB_IGNERR = 0x001,
/* no output */
JOB_SILENT = 0x002,
/* Target is a special one. i.e. run it locally
* if we can't export it and maxLocal is 0 */
JOB_SPECIAL = 0x004,
/* Ignore "..." lines when processing commands */
JOB_IGNDOTS = 0x008,
/* we've sent 'set -x' */
JOB_TRACED = 0x400
} JobFlags;
/* A Job manages the shell commands that are run to create a single target.
* Each job is run in a separate subprocess by a shell. Several jobs can run
* in parallel.
*
* The shell commands for the target are written to a temporary file,
* then the shell is run with the temporary file as stdin, and the output
* of that shell is captured via a pipe.
*
* When a job is finished, Make_Update updates all parents of the node
* that was just remade, marking them as ready to be made next if all
* other dependencies are finished as well. */
typedef struct Job {
/* The process ID of the shell running the commands */
int pid;
/* The target the child is making */
GNode *node;
/* If one of the shell commands is "...", all following commands are
* delayed until the .END node is made. This list node points to the
* first of these commands, if any. */
StringListNode *tailCmds;
/* When creating the shell script, this is where the commands go.
* This is only used before the job is actually started. */
FILE *cmdFILE;
int exit_status; /* from wait4() in signal handler */
JobState job_state; /* status of the job entry */
char job_suspended;
JobFlags flags; /* Flags to control treatment of job */
int inPipe; /* Pipe for reading output from job */
int outPipe; /* Pipe for writing control commands */
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 */
#define JOB_BUFSIZE 1024
/* Buffer for storing the output of the job, line by line. */
char outBuf[JOB_BUFSIZE + 1];
size_t curPos; /* Current position in outBuf. */
#ifdef USE_META
struct BuildMon bm;
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 */
extern int jobTokensRunning; /* tokens currently "out" */
void Shell_Init(void);
const char *Shell_GetNewline(void);
void Job_Touch(GNode *, Boolean);
Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...));
Boolean Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
void Job_CatchChildren(void);
void Job_CatchOutput(void);
void Job_Make(GNode *);
void Job_Init(void);
Boolean Job_Empty(void);
Boolean Job_ParseShell(char *);
int Job_Finish(void);
void Job_End(void);

532
lst.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $ */
/* $NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -32,9 +32,9 @@
* SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "make.h"
MAKE_RCSID("$NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $");
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
@ -42,110 +42,34 @@
#include <stdint.h>
#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 <sys/cdefs.h>
#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)
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
{
LstNode node = bmake_malloc(sizeof *node);
node->useCount = 0;
node->deleted = FALSE;
ListNode *node = bmake_malloc(sizeof *node);
node->prev = prev;
node->next = next;
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)
List *
Lst_New(void)
{
Lst list = bmake_malloc(sizeof *list);
List *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. */
/* Free a list and all its nodes. The node data are not freed though. */
void
Lst_Free(Lst list)
Lst_Free(List *list)
{
LstNode node;
LstNode next;
assert(list != NULL);
ListNode *node;
ListNode *next;
for (node = list->first; node != NULL; node = next) {
next = node->next;
@ -158,13 +82,10 @@ Lst_Free(Lst 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)
Lst_Destroy(List *list, LstFreeProc freeProc)
{
LstNode node;
LstNode next;
assert(list != NULL);
assert(freeProc != NULL);
ListNode *node;
ListNode *next;
for (node = list->first; node != NULL; node = next) {
next = node->next;
@ -179,44 +100,33 @@ Lst_Destroy(Lst list, LstFreeProc freeProc)
* Functions to modify a list
*/
/* Insert a new node with the given piece of data before the given node in the
* given list. */
/* Insert a new node with the datum before the given node. */
void
Lst_InsertBefore(Lst list, LstNode node, void *datum)
Lst_InsertBefore(List *list, ListNode *node, void *datum)
{
LstNode newNode;
ListNode *newNode;
assert(list != NULL);
assert(!LstIsEmpty(list));
assert(node != NULL);
assert(datum != NULL);
newNode = LstNodeNew(datum);
newNode->prev = node->prev;
newNode->next = node;
newNode = LstNodeNew(node->prev, node, datum);
if (node->prev != NULL) {
if (node->prev != NULL)
node->prev->next = newNode;
}
node->prev = newNode;
if (node == list->first) {
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)
Lst_Prepend(List *list, void *datum)
{
LstNode node;
ListNode *node;
assert(list != NULL);
assert(datum != NULL);
node = LstNodeNew(datum);
node->prev = NULL;
node->next = list->first;
node = LstNodeNew(NULL, list->first, datum);
if (list->first == NULL) {
list->first = node;
@ -229,16 +139,13 @@ Lst_Prepend(Lst list, void *datum)
/* Add a piece of data at the end of the given list. */
void
Lst_Append(Lst list, void *datum)
Lst_Append(List *list, void *datum)
{
LstNode node;
ListNode *node;
assert(list != NULL);
assert(datum != NULL);
node = LstNodeNew(datum);
node->prev = list->last;
node->next = NULL;
node = LstNodeNew(list->last, NULL, datum);
if (list->last == NULL) {
list->first = node;
@ -252,265 +159,83 @@ Lst_Append(Lst list, void *datum)
/* 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)
Lst_Remove(List *list, ListNode *node)
{
assert(list != NULL);
assert(node != NULL);
/*
* unlink it from the list
*/
if (node->next != NULL) {
/* unlink it from its neighbors */
if (node->next != NULL)
node->next->prev = node->prev;
}
if (node->prev != NULL) {
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) {
/* unlink it from the list */
if (list->first == node)
list->first = node->next;
}
if (list->last == node) {
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)
LstNode_Set(ListNode *node, void *datum)
{
assert(node != NULL);
assert(datum != NULL);
node->datum = datum;
}
/* Replace the datum in the given node to NULL. */
/* Replace the datum in the given node to NULL.
* Having NULL values in a list is unusual though. */
void
LstNode_SetNull(LstNode node)
LstNode_SetNull(ListNode *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)
ListNode *
Lst_FindDatum(List *list, const void *datum)
{
LstNode node;
ListNode *node;
assert(list != NULL);
assert(datum != NULL);
for (node = list->first; node != NULL; node = node->next) {
if (node->datum == datum) {
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)
Lst_ForEachUntil(List *list, LstActionUntilProc 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);
ListNode *node;
int result = 0;
for (node = list->first; node != NULL; node = node->next) {
result = proc(node->datum, procData);
if (result != 0)
break;
}
return result;
}
/* Move all nodes from list2 to the end of list1.
* List2 is destroyed and freed. */
void
Lst_MoveAll(Lst list1, Lst list2)
Lst_MoveAll(List *list1, List *list2)
{
assert(list1 != NULL);
assert(list2 != NULL);
if (list2->first != NULL) {
list2->first->prev = list1->last;
if (list1->last != NULL) {
if (list1->last != NULL)
list1->last->next = list2->first;
} else {
else
list1->first = list2->first;
}
list1->last = list2->last;
}
free(list2);
@ -518,124 +243,77 @@ Lst_MoveAll(Lst list1, Lst list2)
/* Copy the element data from src to the start of dst. */
void
Lst_PrependAll(Lst dst, Lst src)
Lst_PrependAll(List *dst, List *src)
{
LstNode node;
ListNode *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)
Lst_AppendAll(List *dst, List *src)
{
LstNode node;
ListNode *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_Enqueue(List *list, void *datum)
{
Lst_Append(list, datum);
}
/* Remove and return the datum at the head of the given list. */
void *
Lst_Dequeue(Lst list)
Lst_Dequeue(List *list)
{
void *datum;
assert(list != NULL);
assert(!LstIsEmpty(list));
datum = list->first->datum;
void *datum = list->first->datum;
Lst_Remove(list, list->first);
assert(datum != NULL);
assert(datum != NULL); /* since NULL would mean end of the list */
return datum;
}
void
Vector_Init(Vector *v, size_t itemSize)
{
v->len = 0;
v->priv_cap = 10;
v->itemSize = itemSize;
v->items = bmake_malloc(v->priv_cap * v->itemSize);
}
/* Add space for a new item to the vector and return a pointer to that space.
* The returned data is valid until the next modifying operation. */
void *
Vector_Push(Vector *v)
{
if (v->len >= v->priv_cap) {
v->priv_cap *= 2;
v->items = bmake_realloc(v->items, v->priv_cap * v->itemSize);
}
v->len++;
return Vector_Get(v, v->len - 1);
}
/* Return the pointer to the last item in the vector.
* The returned data is valid until the next modifying operation. */
void *
Vector_Pop(Vector *v)
{
assert(v->len > 0);
v->len--;
return Vector_Get(v, v->len);
}
void
Vector_Done(Vector *v)
{
free(v->items);
}

128
lst.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */
/* $NetBSD: lst.h,v 1.84 2020/10/28 02:43:16 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -79,101 +79,109 @@
#define MAKE_LST_H
#include <sys/param.h>
#include <stdint.h>
#include <stdlib.h>
/* A doubly-linked list of pointers. */
typedef struct List *Lst;
typedef struct List List;
/* A single node in the doubly-linked list. */
typedef struct ListNode *LstNode;
typedef struct ListNode ListNode;
struct ListNode {
ListNode *prev; /* previous node in list, or NULL */
ListNode *next; /* next node in list, or NULL */
union {
void *datum; /* datum associated with this element */
const struct GNode *priv_gnode; /* alias, just for debugging */
const char *priv_str; /* alias, just for debugging */
};
};
struct List {
ListNode *first; /* first node in list */
ListNode *last; /* last node in list */
};
/* 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);
/* An action for Lst_ForEachUntil and Lst_ForEachUntilConcurrent. */
typedef int LstActionUntilProc(void *datum, void *args);
/* Create or destroy a list */
/* Create a new list. */
Lst Lst_Init(void);
/* Duplicate an existing list. */
Lst Lst_Copy(Lst, LstCopyProc);
List *Lst_New(void);
/* Free the list, leaving the node data unmodified. */
void Lst_Free(Lst);
void Lst_Free(List *);
/* Free the list, freeing the node data using the given function. */
void Lst_Destroy(Lst, LstFreeProc);
void Lst_Destroy(List *, LstFreeProc);
/* Get information about a list */
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 *);
static inline MAKE_ATTR_UNUSED Boolean
Lst_IsEmpty(List *list) { return list->first == NULL; }
/* Find the first node that contains the given datum, or NULL. */
LstNode Lst_FindDatum(Lst, const void *);
ListNode *Lst_FindDatum(List *, const void *);
/* Modify a list */
/* Insert a datum before the given node. */
void Lst_InsertBefore(Lst, LstNode, void *);
void Lst_InsertBefore(List *, ListNode *, void *);
/* Place a datum at the front of the list. */
void Lst_Prepend(Lst, void *);
void Lst_Prepend(List *, void *);
/* Place a datum at the end of the list. */
void Lst_Append(Lst, void *);
void Lst_Append(List *, 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);
void Lst_Remove(List *, ListNode *);
void Lst_PrependAll(List *, List *);
void Lst_AppendAll(List *, List *);
void Lst_MoveAll(List *, List *);
/* 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 *);
void LstNode_Set(ListNode *, void *);
/* Set the value of the node to NULL. Having NULL in a list is unusual. */
void LstNode_SetNull(LstNode);
void LstNode_SetNull(ListNode *);
/* 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);
/* Run the action for each datum of the list, until the action returns
* non-zero.
*
* During this iteration, the list must not be modified structurally. */
int Lst_ForEachUntil(List *, LstActionUntilProc, void *);
/* Using the list as a queue */
/* Add a datum at the tail of the queue. */
void Lst_Enqueue(Lst, void *);
void Lst_Enqueue(List *, void *);
/* Remove the head node of the queue and return its datum. */
void *Lst_Dequeue(Lst);
void *Lst_Dequeue(List *);
/* A vector is an ordered collection of items, allowing for fast indexed
* access. */
typedef struct Vector {
void *items; /* memory holding the items */
size_t itemSize; /* size of a single item in bytes */
size_t len; /* number of actually usable elements */
size_t priv_cap; /* capacity */
} Vector;
void Vector_Init(Vector *, size_t);
/* Return the pointer to the given item in the vector.
* The returned data is valid until the next modifying operation. */
static inline MAKE_ATTR_UNUSED void *
Vector_Get(Vector *v, size_t i)
{
unsigned char *items = v->items;
return items + i * v->itemSize;
}
void *Vector_Push(Vector *);
void *Vector_Pop(Vector *);
void Vector_Done(Vector *);
#endif /* MAKE_LST_H */

1855
main.c

File diff suppressed because it is too large Load Diff

View File

@ -58,22 +58,15 @@ do_link() {
${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}
for o in ${BASE_OBJECTS} ${LIB_OBJECTS}
do
do_compile "$o"
done

View File

@ -1,4 +1,4 @@
/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */
/* $NetBSD: config.h,v 1.25 2020/10/19 23:43:55 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -76,10 +76,11 @@
* DEFMAXJOBS
* DEFMAXLOCAL
* These control the default concurrency. On no occasion will more
* than DEFMAXJOBS targets be created at once (locally or remotely)
* 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...
* to 0, nothing will ever happen.
*/
#define DEFMAXJOBS 4
#define DEFMAXLOCAL 1
@ -88,10 +89,12 @@
* 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.
* .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS.
*
* See varname-dot-include.mk and varname-dot-libs.mk for more details.
*/
#define INCLUDES
#define LIBRARIES
@ -108,11 +111,13 @@
* 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 define this. Under
* NFS you probably should not, unless you aren't exporting jobs.
* re-made, causing later targets to appear up-to-date.
*
* On systems that don't have this problem, you should define this.
* Under NFS you probably should not, unless you aren't exporting jobs.
*/
#define RECHECK
@ -128,8 +133,10 @@
/*
* SYSVINCLUDE
* Recognize system V like include directives [include "filename"]
* (required by POSIX 2018)
* SYSVVARSUB
* Recognize system V like ${VAR:x=y} variable substitutions
* (required by POSIX 2018)
*/
#define SYSVINCLUDE
#define SYSVVARSUB
@ -149,14 +156,6 @@
*/
#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"

8
make.1
View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
.Dd August 28, 2020
.Dd November 1, 2020
.Dt MAKE 1
.Os
.Sh NAME
@ -1872,7 +1872,7 @@ has been defined and has commands associated with it.
.Ar Expression
may also be an arithmetic or string comparison.
Variable expansion is
performed on both sides of the comparison, after which the integral
performed on both sides of the comparison, after which the numerical
values are compared.
A value is interpreted as hexadecimal if it is
preceded by 0x, otherwise it is decimal; octal numbers are not supported.
@ -1882,7 +1882,7 @@ variable expansion, either the left or right hand side of a
.Ql Ic ==
or
.Ql Ic "!="
operator is not an integral value, then
operator is not a numerical value, then
string comparison is performed between the expanded
variables.
If no relational operator is given, it is assumed that the expanded

1000
make.c

File diff suppressed because it is too large Load Diff

459
make.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */
/* $NetBSD: make.h,v 1.179 2020/11/01 17:47:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -91,6 +91,7 @@
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
@ -142,23 +143,31 @@
#ifdef USE_DOUBLE_BOOLEAN
/* During development, to find type mismatches in function declarations. */
typedef double Boolean;
#define TRUE 1.0
#define FALSE 0.0
#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_CHAR_BOOLEAN)
/* During development, to find code that uses a boolean as array index, via
* -Wchar-subscripts. */
typedef char Boolean;
#define TRUE ((char)-1)
#define FALSE ((char)0x00)
#elif defined(USE_ENUM_BOOLEAN)
typedef enum { FALSE, TRUE} Boolean;
typedef enum Boolean { FALSE, TRUE } Boolean;
#else
typedef int Boolean;
#endif
#ifndef TRUE
#define TRUE 1
#endif /* TRUE */
#endif
#ifndef FALSE
#define FALSE 0
#endif /* FALSE */
#endif
#endif
#include "lst.h"
#include "enum.h"
@ -196,18 +205,20 @@ typedef enum {
* 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 (:) */
* Some of the OP_ constants can be combined, others cannot. */
typedef enum GNodeType {
/* The dependency operator ':' is the most common one. The commands of
* this node are executed if any child is out-of-date. */
OP_DEPENDS = 1 << 0,
/* Always execute commands (!) */
/* The dependency operator '!' always executes its commands, even if
* its children are up-to-date. */
OP_FORCE = 1 << 1,
/* Execution of commands depends on children per line (::) */
/* The dependency operator '::' behaves like ':', except that it allows
* multiple dependency groups to be defined. Each of these groups is
* executed on its own, independently from the others. */
OP_DOUBLEDEP = 1 << 2,
/* Matches the dependency operators ':', '!' and '::'. */
OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP,
/* Don't care if the target doesn't exist and can't be created */
@ -216,8 +227,8 @@ typedef enum {
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_EXEC = 1 << 5,
/* Ignore non-zero exit status from shell commands when creating the node */
OP_IGNORE = 1 << 6,
/* Don't remove the target when interrupted */
OP_PRECIOUS = 1 << 7,
@ -259,23 +270,32 @@ typedef enum {
/* The node is a transformation rule */
OP_TRANSFORM = 1 << 31,
/* Target is a member of an archive */
/* XXX: How does this differ from OP_ARCHV? */
OP_MEMBER = 1 << 30,
/* Target is a library */
/* The node is a library,
* its name has the form "-l<libname>" */
OP_LIB = 1 << 29,
/* Target is an archive construct */
/* The node is an archive member,
* its name has the form "archive(member)" */
/* XXX: How does this differ from OP_MEMBER? */
OP_ARCHV = 1 << 28,
/* Target has all the commands it should. Used when parsing to catch
* multiple commands for a target. */
* multiple command groups for a target. Only applies to the dependency
* operators ':' and '!', but not to '::'. */
OP_HAS_COMMANDS = 1 << 27,
/* Saving commands on .END (Compat) */
/* The special command "..." has been seen. All further commands from
* this node will be saved on the .END node instead, to be executed at
* the very end. */
OP_SAVE_CMDS = 1 << 26,
/* Already processed by Suff_FindDeps */
OP_DEPS_FOUND = 1 << 25,
/* Node found while expanding .ALLSRC */
OP_MARK = 1 << 24
OP_MARK = 1 << 24,
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
} GNodeType;
typedef enum {
typedef enum GNodeFlags {
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 */
@ -288,6 +308,14 @@ typedef enum {
INTERNAL = 0x4000 /* Internal use only */
} GNodeFlags;
typedef struct List StringList;
typedef struct ListNode StringListNode;
typedef struct List GNodeList;
typedef struct ListNode GNodeListNode;
typedef struct List /* of CachedDir */ SearchPath;
/* 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 {
@ -302,40 +330,41 @@ typedef struct GNode {
/* 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;
/* The state of processing on this node */
GNodeMade made;
int unmade; /* The number of unmade children */
time_t mtime; /* Its modification time */
struct GNode *cmgn; /* The youngest child */
/* The modification time; 0 means the node does not have a corresponding
* file; see Make_OODate. */
time_t mtime;
struct GNode *youngestChild;
/* 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;
GNodeList *implicitParents;
/* Other nodes of the same name for the :: operator. */
Lst cohorts;
/* Other nodes of the same name, for the '::' operator. */
GNodeList *cohorts;
/* The nodes that depend on this one, or in other words, the nodes for
* which this is a source. */
Lst parents;
GNodeList *parents;
/* The nodes on which this one depends. */
Lst children;
GNodeList *children;
/* .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;
GNodeList *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;
GNodeList *order_succ;
/* #n for this cohort */
/* The "#n" suffix for this cohort, or "" for other nodes */
char cohort_num[8];
/* The number of unmade instances on the cohorts list */
int unmade_cohorts;
@ -344,14 +373,17 @@ typedef struct GNode {
struct GNode *centurion;
/* Last time (sequence number) we tried to make this node */
unsigned int checked;
unsigned int checked_seqno;
/* The "local" variables that are specific to this target and this target
* only, such as $@, $<, $?. */
Hash_Table context;
* only, such as $@, $<, $?.
*
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
* VAR_INTERNAL, which contain variables with arbitrary names. */
HashTable /* of Var pointer */ context;
/* The commands to be given to a shell to create this target. */
Lst commands;
StringList *commands;
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
* but the Suff module) */
@ -363,40 +395,21 @@ typedef struct GNode {
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 */
/*
* 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.
* once the top-level makefile has been parsed. PARSE_WARNING and PARSE_INFO
* mean it can.
*/
#define PARSE_INFO 3
#define PARSE_WARNING 2
#define PARSE_FATAL 1
typedef enum ParseErrorLevel {
PARSE_FATAL = 1,
PARSE_WARNING,
PARSE_INFO
} ParseErrorLevel;
/*
* Values returned by Cond_Eval.
* Values returned by Cond_EvalLine and Cond_EvalCondition.
*/
typedef enum {
typedef enum CondEvalResult {
COND_PARSE, /* Parse the next lines */
COND_SKIP, /* Skip the next lines */
COND_INVALID /* Not a conditional statement */
@ -405,77 +418,52 @@ typedef enum {
/*
* 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 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 "<F" /* file part of IMPSRC */
#define DIMPSRC "<D" /* directory part of IMPSRC */
#define FPREFIX "*F" /* file part of PREFIX */
#define DPREFIX "*D" /* directory part of PREFIX */
#define FTARGET "@F" /* file part of TARGET */
#define DTARGET "@D" /* directory part of TARGET */
#define FIMPSRC "<F" /* file part of IMPSRC */
#define DIMPSRC "<D" /* directory part of IMPSRC */
#define FPREFIX "*F" /* file part of PREFIX */
#define DPREFIX "*D" /* directory part of PREFIX */
/*
* Global Variables
*/
extern Lst create; /* The list of target names specified on the
* command line. used to resolve #if
* make(...) statements */
extern Lst dirSearchPath; /* The list of directories to search when
extern SearchPath *dirSearchPath;
/* The list of directories to search when
* looking for targets */
extern Boolean compatMake; /* True if we are make compatible */
extern Boolean ignoreErrors; /* True if should ignore all errors */
extern Boolean beSilent; /* True if should print no commands */
extern Boolean noExecute; /* True if should execute nothing */
extern Boolean noRecursiveExecute; /* True if should execute nothing */
extern Boolean allPrecious; /* True if every target is precious */
extern Boolean allPrecious; /* True if every target is precious */
extern Boolean deleteOnError; /* True if failed targets should be deleted */
extern Boolean keepgoing; /* True if should continue on unaffected
* portions of the graph when have an error
* in one portion */
extern Boolean touchFlag; /* TRUE if targets should just be 'touched'
* if out of date. Set by the -t flag */
extern Boolean queryFlag; /* TRUE if we aren't supposed to really make
* anything, just see if the targets are out-
* of-date */
extern Boolean doing_depend; /* TRUE if processing .depend */
extern Boolean checkEnvFirst; /* TRUE if environment should be searched for
* variables before the global context */
extern Boolean parseWarnFatal; /* TRUE if makefile parsing warnings are
* treated as errors */
extern Boolean varNoExportEnv; /* TRUE if we should not export variables
* set on the command line to the env. */
extern GNode *DEFAULT; /* .DEFAULT rule */
extern GNode *DEFAULT; /* .DEFAULT rule */
extern GNode *VAR_INTERNAL; /* Variables defined internally by make
* which should not override those set by
* makefiles.
*/
extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
* in the Makefile itself */
extern GNode *VAR_CMD; /* Variables defined on the command line */
extern char var_Error[]; /* Value returned by Var_Parse when an error
extern GNode *VAR_CMDLINE; /* Variables defined on the command line */
extern char var_Error[]; /* Value returned by Var_Parse when an error
* is encountered. It actually points to
* an empty string, so naive callers needn't
* worry about it. */
extern time_t now; /* The time at the start of this whole
extern time_t now; /* The time at the start of this whole
* process */
extern Boolean oldVars; /* Do old-style variable substitution */
extern Boolean oldVars; /* Do old-style variable substitution */
extern Lst sysIncPath; /* The system include path. */
extern Lst defIncPath; /* The default include path. */
extern SearchPath *sysIncPath; /* The system include path. */
extern SearchPath *defSysIncPath; /* The default system include path. */
extern char curdir[]; /* Startup directory */
extern char *progname; /* The program name */
@ -495,7 +483,7 @@ extern pid_t myPid;
#define MAKEOVERRIDES ".MAKEOVERRIDES"
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all the makefiles we read */
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
#define MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
@ -504,61 +492,191 @@ extern pid_t myPid;
# define MAKE_LEVEL_ENV "MAKELEVEL"
#endif
/*
* debug control:
* There is one bit per module. It is up to the module what debug
* information to print.
*/
extern FILE *debug_file; /* Output is written here - default stderr */
extern int debug;
#define DEBUG_ARCH 0x00001
#define DEBUG_COND 0x00002
#define DEBUG_DIR 0x00004
#define DEBUG_GRAPH1 0x00008
#define DEBUG_GRAPH2 0x00010
#define DEBUG_JOB 0x00020
#define DEBUG_MAKE 0x00040
#define DEBUG_SUFF 0x00080
#define DEBUG_TARG 0x00100
#define DEBUG_VAR 0x00200
#define DEBUG_FOR 0x00400
#define DEBUG_SHELL 0x00800
#define DEBUG_ERROR 0x01000
#define DEBUG_LOUD 0x02000
#define DEBUG_META 0x04000
#define DEBUG_HASH 0x08000
typedef enum DebugFlags {
DEBUG_ARCH = 1 << 0,
DEBUG_COND = 1 << 1,
DEBUG_DIR = 1 << 2,
DEBUG_GRAPH1 = 1 << 3,
DEBUG_GRAPH2 = 1 << 4,
DEBUG_JOB = 1 << 5,
DEBUG_MAKE = 1 << 6,
DEBUG_SUFF = 1 << 7,
DEBUG_TARG = 1 << 8,
DEBUG_VAR = 1 << 9,
DEBUG_FOR = 1 << 10,
DEBUG_SHELL = 1 << 11,
DEBUG_ERROR = 1 << 12,
DEBUG_LOUD = 1 << 13,
DEBUG_META = 1 << 14,
DEBUG_HASH = 1 << 15,
#define DEBUG_GRAPH3 0x10000
#define DEBUG_SCRIPT 0x20000
#define DEBUG_PARSE 0x40000
#define DEBUG_CWD 0x80000
DEBUG_GRAPH3 = 1 << 16,
DEBUG_SCRIPT = 1 << 17,
DEBUG_PARSE = 1 << 18,
DEBUG_CWD = 1 << 19,
#define DEBUG_LINT 0x100000
DEBUG_LINT = 1 << 20
} DebugFlags;
#define CONCAT(a,b) a##b
#define DEBUG(module) (debug & CONCAT(DEBUG_,module))
#define DEBUG(module) (opts.debug & CONCAT(DEBUG_,module))
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
#define DEBUG0(module, text) \
if (!DEBUG(module)) (void)0; \
else debug_printf("%s", text)
#define DEBUG1(module, fmt, arg1) \
if (!DEBUG(module)) (void)0; \
else debug_printf(fmt, arg1)
#define DEBUG2(module, fmt, arg1, arg2) \
if (!DEBUG(module)) (void)0; \
else debug_printf(fmt, arg1, arg2)
#define DEBUG3(module, fmt, arg1, arg2, arg3) \
if (!DEBUG(module)) (void)0; \
else debug_printf(fmt, arg1, arg2, arg3)
#define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \
if (!DEBUG(module)) (void)0; \
else debug_printf(fmt, arg1, arg2, arg3, arg4)
#define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \
if (!DEBUG(module)) (void)0; \
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
typedef enum PrintVarsMode {
COMPAT_VARS = 1,
EXPAND_VARS
} PrintVarsMode;
/* Command line options */
typedef struct CmdOpts {
/* -B: whether we are make compatible */
Boolean compatMake;
/* -d: debug control: There is one bit per module. It is up to the
* module what debug information to print. */
DebugFlags debug;
/* -df: debug output is written here - default stderr */
FILE *debug_file;
/* -dV: for the -V option, print unexpanded variable values */
Boolean debugVflag;
/* -e: check environment variables before global variables */
Boolean checkEnvFirst;
/* -f: the makefiles to read */
StringList *makefiles;
/* -i: if true, ignore all errors from shell commands */
Boolean ignoreErrors;
/* -j: the maximum number of jobs that can run in parallel;
* this is coordinated with the submakes */
int maxJobs;
/* -k: if true, continue on unaffected portions of the graph when an
* error occurs in one portion */
Boolean keepgoing;
/* -N: execute no commands from the targets */
Boolean noRecursiveExecute;
/* -n: execute almost no commands from the targets */
Boolean noExecute;
/* -q: if true, we aren't supposed to really make anything, just see if
* the targets are out-of-date */
Boolean queryFlag;
/* -r: raw mode, without loading the builtin rules. */
Boolean noBuiltins;
/* -s: don't echo the shell commands before executing them */
Boolean beSilent;
/* -t: touch the targets if they are out-of-date, but don't actually
* make them */
Boolean touchFlag;
/* -[Vv]: print expanded or unexpanded selected variables */
PrintVarsMode printVars;
/* -[Vv]: the variables to print */
StringList *variables;
/* -W: if true, makefile parsing warnings are treated as errors */
Boolean parseWarnFatal;
/* -w: print Entering and Leaving for submakes */
Boolean enterFlag;
/* -X: if true, do not export variables set on the command line to the
* environment. */
Boolean varNoExportEnv;
/* The target names specified on the command line.
* Used to resolve .if make(...) statements. */
StringList *create;
} CmdOpts;
extern CmdOpts opts;
#include "nonints.h"
int Make_TimeStamp(GNode *, GNode *);
void Make_TimeStamp(GNode *, GNode *);
Boolean Make_OODate(GNode *);
void Make_ExpandUse(Lst);
void Make_ExpandUse(GNodeList *);
time_t Make_Recheck(GNode *);
void Make_HandleUse(GNode *, GNode *);
void Make_Update(GNode *);
void Make_DoAllVar(GNode *);
Boolean Make_Run(Lst);
Boolean Make_Run(GNodeList *);
int dieQuietly(GNode *, int);
void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(Boolean);
Boolean Main_SetObjdir(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
int mkTempFile(const char *, char **);
int str2Lst_Append(Lst, char *, const char *);
int str2Lst_Append(StringList *, char *, const char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
Boolean GNode_ShouldExecute(GNode *gn);
/* See if the node was seen on the left-hand side of a dependency operator. */
static MAKE_ATTR_UNUSED Boolean
GNode_IsTarget(const GNode *gn)
{
return (gn->type & OP_OPMASK) != 0;
}
static MAKE_ATTR_UNUSED const char *
GNode_Path(const GNode *gn)
{
return gn->path != NULL ? gn->path : gn->name;
}
static MAKE_ATTR_UNUSED const char *
GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarAllsrc(GNode *gn) { return Var_ValueDirect(ALLSRC, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarPrefix(GNode *gn) { return Var_ValueDirect(PREFIX, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); }
static MAKE_ATTR_UNUSED const char *
GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
#ifdef __GNUC__
#define UNCONST(ptr) ({ \
#define UNCONST(ptr) ({ \
union __unconst { \
const void *__cp; \
void *__p; \
@ -568,13 +686,6 @@ void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
#define UNCONST(ptr) (void *)(ptr)
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
/* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */
#ifdef HAVE_LIMITS_H
#include <limits.h>
@ -592,4 +703,42 @@ void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
#define KILLPG(pid, sig) killpg((pid), (sig))
#endif
static inline MAKE_ATTR_UNUSED Boolean ch_isalnum(char ch)
{ return isalnum((unsigned char)ch) != 0; }
static inline MAKE_ATTR_UNUSED Boolean ch_isalpha(char ch)
{ return isalpha((unsigned char)ch) != 0; }
static inline MAKE_ATTR_UNUSED Boolean ch_isdigit(char ch)
{ return isdigit((unsigned char)ch) != 0; }
static inline MAKE_ATTR_UNUSED Boolean ch_isspace(char ch)
{ return isspace((unsigned char)ch) != 0; }
static inline MAKE_ATTR_UNUSED Boolean ch_isupper(char ch)
{ return isupper((unsigned char)ch) != 0; }
static inline MAKE_ATTR_UNUSED char ch_tolower(char ch)
{ return (char)tolower((unsigned char)ch); }
static inline MAKE_ATTR_UNUSED char ch_toupper(char ch)
{ return (char)toupper((unsigned char)ch); }
static inline MAKE_ATTR_UNUSED void
cpp_skip_whitespace(const char **pp)
{
while (ch_isspace(**pp))
(*pp)++;
}
static inline MAKE_ATTR_UNUSED void
pp_skip_whitespace(char **pp)
{
while (ch_isspace(**pp))
(*pp)++;
}
#ifdef MAKE_NATIVE
# include <sys/cdefs.h>
# ifndef lint
# define MAKE_RCSID(id) __RCSID(id)
# endif
#else
# define MAKE_RCSID(id) static volatile char rcsid[] = id
#endif
#endif /* MAKE_MAKE_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $ */
/* $NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -26,20 +26,13 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef MAKE_NATIVE
#include <sys/cdefs.h>
__RCSID("$NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $");
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "make.h"
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $");
#ifndef USE_EMALLOC
static MAKE_ATTR_DEAD void enomem(void);
/* die when out of memory. */
static MAKE_ATTR_DEAD void

View File

@ -1,4 +1,4 @@
/* $NetBSD: make_malloc.h,v 1.10 2020/08/29 16:47:45 rillig Exp $ */
/* $NetBSD: make_malloc.h,v 1.12 2020/10/19 23:43:55 rillig Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -33,20 +33,20 @@ char *bmake_strdup(const char *);
char *bmake_strldup(const char *, size_t);
#else
#include <util.h>
#define bmake_malloc(x) emalloc(x)
#define bmake_realloc(x,y) erealloc(x,y)
#define bmake_strdup(x) estrdup(x)
#define bmake_strldup(x,y) estrndup(x,y)
#define bmake_malloc(n) emalloc(n)
#define bmake_realloc(p, n) erealloc(p, n)
#define bmake_strdup(s) estrdup(s)
#define bmake_strldup(s, n) estrndup(s, n)
#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.
* p is NULL, to save a few machine instructions.
*
* 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
static inline MAKE_ATTR_UNUSED void
bmake_free(void *p)
{
if (p != NULL)

341
meta.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.113 2020/09/02 04:08:54 rillig Exp $ */
/* $NetBSD: meta.c,v 1.136 2020/10/31 12:04:24 rillig Exp $ */
/*
* Implement 'meta' mode.
@ -55,9 +55,9 @@ char * dirname(char *);
#endif
static BuildMon Mybm; /* for compat */
static Lst metaBailiwick; /* our scope of control */
static StringList *metaBailiwick; /* our scope of control */
static char *metaBailiwickStr; /* string storage for the list */
static Lst metaIgnorePaths; /* paths we deliberately ignore */
static StringList *metaIgnorePaths; /* paths we deliberately ignore */
static char *metaIgnorePathsStr; /* string storage for the list */
#ifndef MAKE_META_IGNORE_PATHS
@ -165,7 +165,6 @@ 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.*/
@ -180,11 +179,13 @@ filemon_read(FILE *mfp, int fd)
warn("Could not rewind filemon");
fprintf(mfp, "\n");
} else {
ssize_t n;
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)
if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n)
error = EIO;
}
}
@ -230,7 +231,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
} while (cp > buf && *cp != '/');
}
if (*cp == '/') {
strlcpy(cp, cp2, bufsz - (cp - buf));
strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
} else {
return; /* can't happen? */
}
@ -264,7 +265,7 @@ meta_name(char *mname, size_t mnamelen,
rp++;
cp++;
if (strcmp(cp, rp) != 0)
strlcpy(rp, cp, sizeof(buf) - (rp - buf));
strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf));
}
tname = buf;
} else {
@ -320,7 +321,7 @@ static int
is_submake(void *cmdp, void *gnp)
{
static const char *p_make = NULL;
static int p_len;
static size_t p_len;
char *cmd = cmdp;
GNode *gn = gnp;
char *mp = NULL;
@ -329,12 +330,14 @@ is_submake(void *cmdp, void *gnp)
int rc = 0; /* keep looking */
if (!p_make) {
p_make = Var_Value(".MAKE", gn, &cp);
void *dontFreeIt;
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
p_len = strlen(p_make);
}
cp = strchr(cmd, '$');
if ((cp)) {
mp = Var_Subst(cmd, gn, VARE_WANTRES);
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
/* TODO: handle errors */
cmd = mp;
}
cp2 = strstr(cmd, p_make);
@ -368,29 +371,37 @@ typedef struct meta_file_s {
GNode *gn;
} meta_file_t;
static int
printCMD(void *cmdp, void *mfpp)
static void
printCMD(const char *cmd, meta_file_t *mfp)
{
meta_file_t *mfp = mfpp;
char *cmd = cmdp;
char *cmd_freeIt = NULL;
if (strchr(cmd, '$')) {
cmd = cmd_freeIt = Var_Subst(cmd, mfp->gn, VARE_WANTRES);
(void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt);
/* TODO: handle errors */
cmd = cmd_freeIt;
}
fprintf(mfp->fp, "CMD %s\n", cmd);
free(cmd_freeIt);
return 0;
}
static void
printCMDs(GNode *gn, meta_file_t *mf)
{
GNodeListNode *ln;
for (ln = gn->commands->first; ln != NULL; ln = ln->next)
printCMD(ln->datum, mf);
}
/*
* Certain node types never get a .meta file
*/
#define SKIP_META_TYPE(_type) do { \
if ((gn->type & __CONCAT(OP_, _type))) { \
if ((gn->type & __CONCAT(OP_, _type))) { \
if (verbose) { \
fprintf(debug_file, "Skipping meta for %s: .%s\n", \
gn->name, __STRING(_type)); \
debug_printf("Skipping meta for %s: .%s\n", \
gn->name, __STRING(_type)); \
} \
return FALSE; \
} \
@ -423,16 +434,13 @@ meta_needed(GNode *gn, const char *dname,
/* 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);
debug_printf("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);
if (Lst_ForEachUntil(gn->commands, is_submake, gn)) {
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
return FALSE;
}
}
@ -440,8 +448,7 @@ meta_needed(GNode *gn, const char *dname,
/* The object directory may not exist. Check it.. */
if (cached_stat(dname, &mst) != 0) {
if (verbose)
fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
gn->name);
debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
return FALSE;
}
@ -452,8 +459,8 @@ meta_needed(GNode *gn, const char *dname,
/* 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);
debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
gn->name);
return FALSE;
}
return TRUE;
@ -471,14 +478,12 @@ meta_create(BuildMon *pbm, GNode *gn)
const char *tname;
char *fname;
const char *cp;
char *p[5]; /* >= possible uses */
int i;
void *objdir_freeIt;
mf.fp = NULL;
i = 0;
dname = Var_Value(".OBJDIR", gn, &p[i++]);
tname = Var_Value(TARGET, gn, &p[i++]);
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
tname = GNode_VarTarget(gn);
/* if this succeeds objdir is realpath of dname */
if (!meta_needed(gn, dname, objdir, TRUE))
@ -489,7 +494,8 @@ meta_create(BuildMon *pbm, GNode *gn)
char *mp;
/* Describe the target we are building */
mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES);
(void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
/* TODO: handle errors */
if (*mp)
fprintf(stdout, "%s\n", mp);
free(mp);
@ -511,8 +517,7 @@ meta_create(BuildMon *pbm, GNode *gn)
dname, tname, objdir);
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_create: %s\n", fname);
DEBUG1(META, "meta_create: %s\n", fname);
#endif
if ((mf.fp = fopen(fname, "w")) == NULL)
@ -522,11 +527,11 @@ meta_create(BuildMon *pbm, GNode *gn)
mf.gn = gn;
Lst_ForEach(gn->commands, printCMD, &mf);
printCMDs(gn, &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++]);
cp = GNode_VarOodate(gn);
if (cp && *cp) {
fprintf(mf.fp, "OODATE %s\n", cp);
}
@ -546,9 +551,7 @@ meta_create(BuildMon *pbm, GNode *gn)
gn->type |= OP_SILENT;
}
out:
for (i--; i >= 0; i--) {
bmake_free(p[i]);
}
bmake_free(objdir_freeIt);
return mf.fp;
}
@ -592,6 +595,7 @@ meta_mode_init(const char *make_mode)
{
static int once = 0;
char *cp;
void *freeIt;
useMeta = TRUE;
useFilemon = TRUE;
@ -630,32 +634,34 @@ meta_mode_init(const char *make_mode)
/*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/
metaBailiwick = Lst_Init();
metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
VAR_GLOBAL, VARE_WANTRES);
metaBailiwick = Lst_New();
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
/* TODO: handle errors */
str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
metaIgnorePaths = Lst_Init();
metaIgnorePaths = Lst_New();
Var_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
VAR_GLOBAL, VARE_WANTRES);
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
/* TODO: handle errors */
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)) {
freeIt = NULL;
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) {
metaIgnorePatterns = TRUE;
bmake_free(cp);
bmake_free(freeIt);
}
cp = NULL;
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) {
freeIt = NULL;
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) {
metaIgnoreFilter = TRUE;
bmake_free(cp);
bmake_free(freeIt);
}
}
@ -790,7 +796,7 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
"(ignored)" : "");
}
if (gn) {
Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL);
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
}
getcwd(cwd, sizeof(cwd));
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL);
@ -813,15 +819,16 @@ meta_job_output(Job *job, char *cp, const char *nl)
if (pbm->mfp != NULL) {
if (metaVerbose) {
static char *meta_prefix = NULL;
static int meta_prefix_len;
static size_t meta_prefix_len;
if (!meta_prefix) {
char *cp2;
meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES);
(void)Var_Subst("${" MAKE_META_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
/* TODO: handle errors */
if ((cp2 = strchr(meta_prefix, '$')))
meta_prefix_len = cp2 - meta_prefix;
meta_prefix_len = (size_t)(cp2 - meta_prefix);
else
meta_prefix_len = strlen(meta_prefix);
}
@ -910,9 +917,9 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
struct stat fs;
int x;
if (fgets(&buf[o], bufsz - o, fp) != NULL) {
if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) {
check_newline:
x = o + strlen(&buf[o]);
x = o + (int)strlen(&buf[o]);
if (buf[x - 1] == '\n')
return x;
/*
@ -923,20 +930,18 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
size_t newsz;
char *p;
newsz = ROUNDUP((fs.st_size / 2), BUFSIZ);
newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ);
if (newsz <= bufsz)
newsz = ROUNDUP(fs.st_size, BUFSIZ);
newsz = ROUNDUP((size_t)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);
DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz);
p = bmake_realloc(buf, newsz);
if (p) {
*bufp = buf = p;
*szp = bufsz = newsz;
/* fetch the rest */
if (!fgets(&buf[x], bufsz - x, fp))
if (!fgets(&buf[x], (int)bufsz - x, fp))
return x; /* truncated! */
goto check_newline;
}
@ -945,7 +950,7 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
return 0;
}
/* Lst_ForEach wants 1 to stop search */
/* Lst_ForEachUntil wants 1 to stop search */
static int
prefix_match(void *p, void *q)
{
@ -958,24 +963,15 @@ prefix_match(void *p, void *q)
/* See if the path equals prefix or starts with "prefix/". */
static Boolean
path_match(const void *p, const void *q)
path_starts_with(const char *path, const char *prefix)
{
const char *path = p;
const char *prefix = q;
size_t n = strlen(prefix);
if (strncmp(path, prefix, n) != 0)
return FALSE;
return FALSE;
return path[n] == '\0' || path[n] == '/';
}
static Boolean
string_match(const void *p, const void *q)
{
return strcmp(p, q) == 0;
}
static int
meta_ignore(GNode *gn, const char *p)
{
@ -986,11 +982,9 @@ meta_ignore(GNode *gn, const char *p)
if (*p == '/') {
cached_realpath(p, fname); /* clean it up */
if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) {
if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_oodate: ignoring path: %s\n",
p);
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
#endif
return TRUE;
}
@ -1002,12 +996,11 @@ meta_ignore(GNode *gn, const char *p)
Var_Set(".p.", p, gn);
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
pm = Var_Subst(expr, gn, VARE_WANTRES);
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
/* TODO: handle errors */
if (*pm) {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n",
p);
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
#endif
free(pm);
return TRUE;
@ -1022,12 +1015,11 @@ meta_ignore(GNode *gn, const char *p)
snprintf(fname, sizeof(fname),
"${%s:L:${%s:ts:}}",
p, MAKE_META_IGNORE_FILTER);
fm = Var_Subst(fname, gn, VARE_WANTRES);
(void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
/* TODO: handle errors */
if (*fm == '\0') {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n",
p);
DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
#endif
free(fm);
return TRUE;
@ -1063,6 +1055,17 @@ meta_ignore(GNode *gn, const char *p)
*ep = '\0'; \
}
static void
append_if_new(StringList *list, const char *str)
{
StringListNode *ln;
for (ln = list->first; ln != NULL; ln = ln->next)
if (strcmp(ln->datum, str) == 0)
return;
Lst_Append(list, bmake_strdup(str));
}
Boolean
meta_oodate(GNode *gn, Boolean oodate)
{
@ -1086,25 +1089,22 @@ meta_oodate(GNode *gn, Boolean oodate)
static size_t tmplen = 0;
FILE *fp;
Boolean needOODATE = FALSE;
Lst missingFiles;
char *pa[4]; /* >= possible uses */
int i;
StringList *missingFiles;
int have_filemon = FALSE;
void *objdir_freeIt;
if (oodate)
return oodate; /* we're done */
i = 0;
dname = Var_Value(".OBJDIR", gn, &pa[i++]);
tname = Var_Value(TARGET, gn, &pa[i++]);
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
tname = GNode_VarTarget(gn);
/* if this succeeds fname3 is realpath of dname */
if (!meta_needed(gn, dname, fname3, FALSE))
goto oodate_out;
dname = fname3;
missingFiles = Lst_Init();
missingFiles = Lst_New();
/*
* We need to check if the target is out-of-date. This includes
@ -1117,8 +1117,7 @@ meta_oodate(GNode *gn, Boolean oodate)
meta_name(fname, sizeof(fname), dname, tname, dname);
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_oodate: %s\n", fname);
DEBUG1(META, "meta_oodate: %s\n", fname);
#endif
if ((fp = fopen(fname, "r")) != NULL) {
@ -1128,7 +1127,7 @@ meta_oodate(GNode *gn, Boolean oodate)
int lastpid = 0;
int pid;
int x;
LstNode ln;
StringListNode *cmdNode;
struct make_stat mst;
if (!buf) {
@ -1152,7 +1151,7 @@ meta_oodate(GNode *gn, Boolean oodate)
/* we want to track all the .meta we read */
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
ln = Lst_First(gn->commands);
cmdNode = gn->commands->first;
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
lineno++;
if (buf[x - 1] == '\n')
@ -1179,8 +1178,7 @@ meta_oodate(GNode *gn, Boolean oodate)
/* Delimit the record type. */
p = buf;
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
#endif
strsep(&p, " ");
if (have_filemon) {
@ -1223,7 +1221,7 @@ meta_oodate(GNode *gn, Boolean oodate)
pid = atoi(p);
if (pid > 0 && pid != lastpid) {
const char *ldir;
char *tp;
void *tp;
if (lastpid > 0) {
/* We need to remember these. */
@ -1249,9 +1247,9 @@ meta_oodate(GNode *gn, Boolean oodate)
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);
debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
fname, lineno,
pid, buf[0], cwd, lcwd, latestdir);
#endif
break;
}
@ -1279,9 +1277,10 @@ meta_oodate(GNode *gn, Boolean oodate)
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);
debug_printf(
"%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
fname, lineno,
child, cwd, lcwd, latestdir);
#endif
}
}
@ -1294,8 +1293,8 @@ meta_oodate(GNode *gn, Boolean oodate)
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);
DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
fname, lineno, cwd, lcwd);
#endif
break;
@ -1317,29 +1316,23 @@ meta_oodate(GNode *gn, Boolean oodate)
DEQUOTE(move_target);
/* FALLTHROUGH */
case 'D': /* unlink */
if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
if (*p == '/') {
/* remove any missingFiles entries that match p */
ln = Lst_Find(missingFiles, path_match, p);
if (ln != NULL) {
LstNode nln;
char *tp;
do {
nln = Lst_FindFrom(missingFiles,
LstNode_Next(ln),
path_match, p);
tp = LstNode_Datum(ln);
StringListNode *ln = missingFiles->first;
while (ln != NULL) {
StringListNode *next = ln->next;
if (path_starts_with(ln->datum, p)) {
free(ln->datum);
Lst_Remove(missingFiles, ln);
free(tp);
} while ((ln = nln) != NULL);
}
ln = next;
}
}
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);
DEBUG2(META, "meta_oodate: M %s -> %s\n",
p, move_target);
#endif
p = move_target;
goto check_write;
@ -1360,9 +1353,7 @@ meta_oodate(GNode *gn, Boolean oodate)
DEQUOTE(p);
DEQUOTE(link_src);
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "meta_oodate: L %s -> %s\n",
link_src, p);
DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p);
#endif
/* FALLTHROUGH */
case 'W': /* Write */
@ -1383,7 +1374,7 @@ meta_oodate(GNode *gn, Boolean oodate)
if (strncmp(p, cwd, cwdlen) == 0)
break;
if (!Lst_ForEach(metaBailiwick, prefix_match, p))
if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p))
break;
/* tmpdir might be within */
@ -1396,18 +1387,15 @@ meta_oodate(GNode *gn, Boolean oodate)
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, string_match, p) == NULL)
Lst_Append(missingFiles, bmake_strdup(p));
}
if (!meta_ignore(gn, p))
append_if_new(missingFiles, 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);
DEBUG1(META, "meta_oodate: L src %s\n", p);
#endif
/* FALLTHROUGH */
case 'R': /* Read */
@ -1455,8 +1443,8 @@ meta_oodate(GNode *gn, Boolean oodate)
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);
DEBUG3(META, "%s: %d: looking for: %s\n",
fname, lineno, *sdp);
#endif
if (cached_stat(*sdp, &mst) == 0) {
found = 1;
@ -1465,13 +1453,13 @@ meta_oodate(GNode *gn, Boolean oodate)
}
if (found) {
#ifdef DEBUG_META_MODE
if (DEBUG(META))
fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
DEBUG3(META, "%s: %d: found: %s\n",
fname, lineno, p);
#endif
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);
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
fname, lineno, p);
oodate = TRUE;
} else if (S_ISDIR(mst.mst_mode)) {
/* Update the latest directory. */
@ -1483,8 +1471,7 @@ meta_oodate(GNode *gn, Boolean oodate)
* A referenced file outside of CWD is missing.
* We cannot catch every eventuality here...
*/
if (Lst_Find(missingFiles, string_match, p) == NULL)
Lst_Append(missingFiles, bmake_strdup(p));
append_if_new(missingFiles, p);
}
}
if (buf[0] == 'E') {
@ -1502,12 +1489,12 @@ meta_oodate(GNode *gn, Boolean oodate)
* 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);
if (cmdNode == NULL) {
DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
fname, lineno);
oodate = TRUE;
} else {
char *cmd = LstNode_Datum(ln);
char *cmd = cmdNode->datum;
Boolean hasOODATE = FALSE;
if (strstr(cmd, "$?"))
@ -1519,10 +1506,11 @@ meta_oodate(GNode *gn, Boolean oodate)
}
if (hasOODATE) {
needOODATE = TRUE;
if (DEBUG(META))
fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
fname, lineno);
}
cmd = Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR);
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
/* TODO: handle errors */
if ((cp = strchr(cmd, '\n'))) {
int n;
@ -1553,28 +1541,28 @@ meta_oodate(GNode *gn, Boolean oodate)
!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);
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
fname, lineno, p, cmd);
if (!metaIgnoreCMDs)
oodate = TRUE;
}
free(cmd);
ln = LstNode_Next(ln);
cmdNode = cmdNode->next;
}
} 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);
if (!oodate && cmdNode != NULL) {
DEBUG2(META, "%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);
DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
fname, lineno, p, curdir);
oodate = TRUE;
}
}
@ -1582,14 +1570,12 @@ meta_oodate(GNode *gn, Boolean oodate)
fclose(fp);
if (!Lst_IsEmpty(missingFiles)) {
if (DEBUG(META))
fprintf(debug_file, "%s: missing files: %s...\n",
fname, (char *)LstNode_Datum(Lst_First(missingFiles)));
DEBUG2(META, "%s: missing files: %s...\n",
fname, (char *)missingFiles->first->datum);
oodate = TRUE;
}
if (!oodate && !have_filemon && filemonMissing) {
if (DEBUG(META))
fprintf(debug_file, "%s: missing filemon data\n", fname);
DEBUG1(META, "%s: missing filemon data\n", fname);
oodate = TRUE;
}
} else {
@ -1598,13 +1584,12 @@ meta_oodate(GNode *gn, Boolean oodate)
/* 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) {
if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
cp = NULL; /* not in .CURDIR */
}
}
if (!cp) {
if (DEBUG(META))
fprintf(debug_file, "%s: required but missing\n", fname);
DEBUG1(META, "%s: required but missing\n", fname);
oodate = TRUE;
needOODATE = TRUE; /* assume the worst */
}
@ -1620,14 +1605,11 @@ meta_oodate(GNode *gn, Boolean oodate)
* All we can sanely do is set it to .ALLSRC.
*/
Var_Delete(OODATE, gn);
Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn);
bmake_free(cp);
Var_Set(OODATE, GNode_VarAllsrc(gn), gn);
}
oodate_out:
for (i--; i >= 0; i--) {
bmake_free(pa[i]);
}
bmake_free(objdir_freeIt);
return oodate;
}
@ -1662,11 +1644,8 @@ void
meta_compat_child(void)
{
meta_job_child(NULL);
if (dup2(childPipe[1], 1) < 0 ||
dup2(1, 2) < 0) {
execError("dup2", "pipe");
_exit(1);
}
if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0)
execDie("dup2", "pipe");
}
void

7
meta.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.h,v 1.7 2020/07/03 08:13:23 rillig Exp $ */
/* $NetBSD: meta.h,v 1.8 2020/10/19 23:43:55 rillig Exp $ */
/*
* Things needed for 'meta' mode.
@ -38,9 +38,8 @@ typedef struct BuildMon {
FILE *mfp;
} BuildMon;
extern Boolean useMeta;
struct Job;
struct Job; /* not defined yet */
void meta_init(void);
void meta_finish(void);
void meta_mode_init(const char *);
@ -57,3 +56,5 @@ Boolean meta_oodate(GNode *, Boolean);
void meta_compat_start(void);
void meta_compat_child(void);
void meta_compat_parent(pid_t);
extern Boolean useMeta;

View File

@ -1,4 +1,4 @@
/* $NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $ */
/* $NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $ */
/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@ -37,18 +37,15 @@
#include <sys/cdefs.h>
#endif
#if defined(__RCSID) && !defined(lint)
__RCSID("$NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $");
#endif
#include "metachar.h"
MAKE_RCSID("$NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $");
/*
* 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] = {

View File

@ -1,4 +1,4 @@
/* $NetBSD: metachar.h,v 1.7 2020/08/25 17:37:09 rillig Exp $ */
/* $NetBSD: metachar.h,v 1.11 2020/10/31 18:20:00 rillig Exp $ */
/*-
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@ -35,17 +35,13 @@
extern unsigned char _metachar[];
#define ismeta(c) _metachar[(c) & 0x7f]
#define is_shell_metachar(c) _metachar[(c) & 0x7f]
static inline int MAKE_ATTR_UNUSED
needshell(const char *cmd, int white)
static inline MAKE_ATTR_UNUSED int
needshell(const char *cmd)
{
while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') {
if (white && isspace((unsigned char)*cmd))
break;
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
cmd++;
}
return *cmd != '\0';
}

View File

@ -1,3 +1,31 @@
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201101
* dirdeps.mk: most leaf makefiles are not suitable for building
dirdeps.cache so if RELDIR is not "." use dirdeps.mk
2020-10-28 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201028
* dirdeps.mk: if we don't have :range use equivalent of M_RANGE
when building dirdeps.cache for leaf directory use -f dirdeps.mk
* sys.vars.mk: add M_JOT and M_RANGE
2020-10-01 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201001
* meta2deps.{py,sh}: throw an error if we don't see filemon version
2020-09-09 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20200909
* dirdeps-cache-update.mk: use cache_update_dirdep as guard target
2020-08-26 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps.mk: ensure we cannot confuse a static cache for dynamic

View File

@ -1,4 +1,4 @@
# $Id: dirdeps-cache-update.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $
# $Id: dirdeps-cache-update.mk,v 1.22 2020/09/10 00:14:38 sjg Exp $
#
# @(#) Copyright (c) 2020, Simon J. Gerraty
#
@ -86,7 +86,7 @@ _debug_cache = 0
.endif
.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" && defined(STATIC_DIRDEPS_CACHE) && exists(${STATIC_DIRDEPS_CACHE})
.if !make(dirdeps)
.if !make(dirdeps) && !target(cache_update_dirdep)
# 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.
@ -99,12 +99,10 @@ _debug_cache = 0
cache_update_dirdep ?= $d.${TARGET_SPEC}
.endif
.endfor
.if !target(${cache_update_dirdep})
dirdeps: ${cache_update_dirdep}
dirdeps cache_update_dirdep: ${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_*

View File

@ -1,4 +1,4 @@
# $Id: dirdeps.mk,v 1.125 2020/08/26 21:49:45 sjg Exp $
# $Id: dirdeps.mk,v 1.130 2020/11/02 00:34:30 sjg Exp $
# Copyright (c) 2010-2020, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
@ -209,12 +209,9 @@ DEP_$v ?= ${$v}
# 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:[#]}]}
# do it the hard way
_tspec_x := ${TARGET_SPEC_VARS:[#]:@x@i=1;while [ $$i -le $x ]; do echo $$i; i=$$((i + 1)); done;@:sh}
.endif
# this handles unqualified entries
M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC};
@ -494,6 +491,11 @@ dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
@MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no
# leaf makefiles rarely work for building DIRDEPS_CACHE
.if ${RELDIR} != "."
BUILD_DIRDEPS_MAKEFILE ?= -f dirdeps.mk
.endif
# these should generally do
BUILD_DIRDEPS_MAKEFILE ?=
BUILD_DIRDEPS_TARGETS ?= ${.TARGETS}
@ -517,6 +519,7 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
${BUILD_DIRDEPS_MAKEFILE} \
${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \
.MAKE.DEPENDFILE=.none \
${"${DEBUG_DIRDEPS:Nno}":?DEBUG_DIRDEPS='${DEBUG_DIRDEPS}':} \
${.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;s,_{,$${,g' >> ${.TARGET}.new && \
@ -627,6 +630,7 @@ __qual_depdirs += ${__hostdpadd}
.endif
.if ${_debug_reldir}
.info DEP_DIRDEPS_FILTER=${DEP_DIRDEPS_FILTER:ts:}
.info depdirs=${__depdirs}
.info qualified=${__qual_depdirs}
.info unqualified=${__unqual_depdirs}

View File

@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
# $Id: install-mk,v 1.179 2020/08/26 21:49:45 sjg Exp $
# $Id: install-mk,v 1.183 2020/11/02 16:34:12 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@ -70,7 +70,7 @@
# sjg@crufty.net
#
MK_VERSION=20200826
MK_VERSION=20201101
OWNER=
GROUP=
MODE=444

View File

@ -37,7 +37,7 @@
"""
RCSid:
$Id: meta2deps.py,v 1.33 2020/08/19 17:51:53 sjg Exp $
$Id: meta2deps.py,v 1.34 2020/10/02 03:11:17 sjg Exp $
Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@ -510,6 +510,7 @@ def parse(self, name=None, file=None):
continue
self.parse_path(path, cwd, w[0], w)
assert(version > 0)
if not file:
f.close()

View File

@ -77,7 +77,7 @@
# RCSid:
# $Id: meta2deps.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $
# $Id: meta2deps.sh,v 1.14 2020/10/02 03:11:17 sjg Exp $
# Copyright (c) 2010-2013, Juniper Networks, Inc.
# All rights reserved.
@ -143,6 +143,11 @@ _excludes_f() {
egrep -v "$EXCLUDES"
}
error() {
echo "ERROR: $@" >&2
exit 1
}
meta2deps() {
DPDEPS=
SRCTOPS=$SRCTOP
@ -236,8 +241,8 @@ meta2deps() {
;;
*) cat /dev/null "$@";;
esac 2> /dev/null |
sed -e 's,^CWD,C C,;/^[CREFLM] /!d' -e "s,',,g" |
$_excludes |
sed -e 's,^CWD,C C,;/^[CREFLMV] /!d' -e "s,',,g" |
$_excludes | ( version=no
while read op pid path junk
do
: op=$op pid=$pid path=$path
@ -249,6 +254,12 @@ meta2deps() {
SB=`echo $CWD | sed 's,/obj.*,,'`
fi
SRCTOP=${SRCTOP:-$SB/src}
case "$verion" in
no) ;; # ignore
0) error "no filemon data";;
*) ;;
esac
version=0
continue
;;
$pid,$pid) ;;
@ -263,6 +274,7 @@ meta2deps() {
esac
case "$op,$path" in
V,*) version=$path; continue;;
W,*srcrel|*.dirdep) continue;;
C,*)
case "$path" in
@ -368,6 +380,9 @@ meta2deps() {
echo $dir;;
esac
done > $tf.dirdep
case "$version" in
0) error "no filemon data";;
esac ) || exit 1
_nl=echo
for f in $tf.dirdep $tf.qual $tf.srcdep
do

View File

@ -1,4 +1,4 @@
# $Id: sys.vars.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $
# $Id: sys.vars.mk,v 1.6 2020/10/28 20:50:04 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
#
@ -57,6 +57,20 @@ _type_sh = which
M_type = @x@(${_type_sh:Utype} $$x) 2> /dev/null; echo;@:sh:[0]:N* found*:[@]:C,[()],,g
M_whence = ${M_type}:M/*:[1]
# produce similar output to jot(1)
# eg. ${LIST:[#]:${M_JOT}}
# would be 1 2 3 4 5 if LIST has 5 words
# ${9:L:${M_JOT}}
# would be 1 2 3 4 5 6 7 8 9
M_JOT = @x@i=1;while [ $$$$i -le $$x ]; do echo $$$$i; i=$$$$((i + 1)); done;@:sh
# ${LIST:${M_RANGE}} is 1 2 3 4 5 if LIST has 5 words
.if ${MAKE_VERSION} >= 20170130
M_RANGE = range
.else
M_RANGE = [#]:${M_JOT}
.endif
# convert a path to a valid shell variable
M_P2V = tu:C,[./-],_,g

182
nonints.h
View File

@ -1,4 +1,4 @@
/* $NetBSD: nonints.h,v 1.102 2020/08/30 19:56:02 rillig Exp $ */
/* $NetBSD: nonints.h,v 1.149 2020/11/01 00:24:57 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@ -73,32 +73,32 @@
*/
/* arch.c */
Boolean Arch_ParseArchive(char **, Lst, GNode *);
void Arch_Init(void);
void Arch_End(void);
Boolean Arch_ParseArchive(char **, GNodeList *, GNode *);
void Arch_Touch(GNode *);
void Arch_TouchLib(GNode *);
time_t Arch_MTime(GNode *);
time_t Arch_MemMTime(GNode *);
void Arch_FindLib(GNode *, Lst);
void Arch_FindLib(GNode *, SearchPath *);
Boolean Arch_LibOODate(GNode *);
void Arch_Init(void);
void Arch_End(void);
Boolean Arch_IsLib(GNode *);
/* compat.c */
int CompatRunCommand(void *, void *);
void Compat_Run(Lst);
int Compat_Make(void *, void *);
int Compat_RunCommand(const char *, GNode *);
void Compat_Run(GNodeList *);
void Compat_Make(GNode *, GNode *);
/* cond.c */
struct If;
CondEvalResult Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean);
CondEvalResult Cond_Eval(char *);
CondEvalResult Cond_EvalCondition(const char *, Boolean *);
CondEvalResult Cond_EvalLine(const char *);
void Cond_restore_depth(unsigned int);
unsigned int Cond_save_depth(void);
/* for.c */
int For_Eval(char *);
int For_Accum(char *);
int For_Eval(const char *);
Boolean For_Accum(const char *);
void For_Run(int);
/* job.c */
@ -114,35 +114,52 @@ 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 *);
void execDie(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_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);
typedef enum VarAssignOp {
VAR_NORMAL, /* = */
VAR_SUBST, /* := */
VAR_SHELL, /* != or :sh= */
VAR_APPEND, /* += */
VAR_DEFAULT /* ?= */
} VarAssignOp;
typedef struct VarAssign {
char *varname; /* unexpanded */
VarAssignOp op;
const char *value; /* unexpanded */
} VarAssign;
typedef char *(*NextBufProc)(void *, size_t *);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
Boolean Parse_IsVar(const char *, VarAssign *out_var);
void Parse_DoVar(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_SetInput(const char *, int, int, NextBufProc, void *);
GNodeList *Parse_MainName(void);
int Parse_GetFatals(void);
/* str.c */
typedef struct {
typedef struct Words {
char **words;
size_t len;
void *freeIt;
} Words;
Words Str_Words(const char *, Boolean);
static inline void MAKE_ATTR_UNUSED
static inline MAKE_ATTR_UNUSED void
Words_Free(Words w) {
free(w.words);
free(w.freeIt);
@ -151,7 +168,6 @@ Words_Free(Words w) {
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 *);
#ifndef HAVE_STRLCPY
@ -160,69 +176,134 @@ 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(const char *, GNode **);
Lst Suff_GetPath(char *);
void Suff_DoPaths(void);
void Suff_AddInclude(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_ClearSuffixes(void);
Boolean Suff_IsTransform(const char *);
GNode *Suff_AddTransform(const char *);
void Suff_EndTransform(GNode *);
void Suff_AddSuffix(const char *, GNode **);
SearchPath *Suff_GetPath(const char *);
void Suff_DoPaths(void);
void Suff_AddInclude(const char *);
void Suff_AddLib(const char *);
void Suff_FindDeps(GNode *);
SearchPath *Suff_FindPath(GNode *);
void Suff_SetNull(const char *);
void Suff_PrintAll(void);
/* targ.c */
void Targ_Init(void);
void Targ_End(void);
void Targ_Stats(void);
Lst Targ_List(void);
GNodeList *Targ_List(void);
GNode *Targ_NewGN(const char *);
GNode *Targ_FindNode(const char *, int);
Lst Targ_FindList(Lst, int);
GNode *Targ_FindNode(const char *);
GNode *Targ_GetNode(const char *);
GNode *Targ_NewInternalNode(const char *);
GNode *Targ_GetEndNode(void);
GNodeList *Targ_FindList(StringList *);
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 *);
void Targ_PrintCmds(GNode *);
void Targ_PrintNode(GNode *, int);
void Targ_PrintNodes(GNodeList *, int);
char *Targ_FmtTime(time_t);
void Targ_PrintType(int);
void Targ_PrintGraph(int);
void Targ_Propagate(void);
/* var.c */
void Var_Init(void);
void Var_End(void);
typedef enum {
typedef enum VarEvalFlags {
VARE_NONE = 0,
/* Treat undefined variables as errors. */
VARE_UNDEFERR = 0x01,
/* Expand and evaluate variables during parsing. */
VARE_WANTRES = 0x02,
/* In an assignment using the ':=' operator, keep '$$' as '$$' instead
* of reducing it to a single '$'. */
VARE_ASSIGN = 0x04
} VarEvalFlags;
typedef enum {
typedef enum VarSet_Flags {
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;
/* The state of error handling returned by Var_Parse.
*
* As of 2020-09-13, this bitset looks quite bloated,
* with all the constants doubled.
*
* Its purpose is to first document the existing behavior,
* and then migrate away from the SILENT constants, step by step,
* as these are not suited for reliable, consistent error handling
* and reporting. */
typedef enum VarParseResult {
/* Both parsing and evaluation succeeded. */
VPR_OK = 0x0000,
/* See if a message has already been printed for this error. */
VPR_ANY_MSG = 0x0001,
/* Parsing failed.
* No error message has been printed yet.
* Deprecated, migrate to VPR_PARSE_MSG instead. */
VPR_PARSE_SILENT = 0x0002,
/* Parsing failed.
* An error message has already been printed. */
VPR_PARSE_MSG = VPR_PARSE_SILENT | VPR_ANY_MSG,
/* Parsing succeeded.
* During evaluation, VARE_UNDEFERR was set and there was an undefined
* variable.
* No error message has been printed yet.
* Deprecated, migrate to VPR_UNDEF_MSG instead. */
VPR_UNDEF_SILENT = 0x0004,
/* Parsing succeeded.
* During evaluation, VARE_UNDEFERR was set and there was an undefined
* variable.
* An error message has already been printed. */
VPR_UNDEF_MSG = VPR_UNDEF_SILENT | VPR_ANY_MSG,
/* Parsing succeeded.
* Evaluation failed.
* No error message has been printed yet.
* Deprecated, migrate to VPR_EVAL_MSG instead. */
VPR_EVAL_SILENT = 0x0006,
/* Parsing succeeded.
* Evaluation failed.
* An error message has already been printed. */
VPR_EVAL_MSG = VPR_EVAL_SILENT | VPR_ANY_MSG,
/* The exact error handling status is not known yet.
* Deprecated, migrate to VPR_OK or any VPE_*_MSG instead. */
VPR_UNKNOWN = 0x0008
} VarParseResult;
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 *);
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);
const char *Var_Value(const char *, GNode *, void **);
const char *Var_ValueDirect(const char *, GNode *);
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags,
const char **, void **);
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
void Var_Stats(void);
void Var_Dump(GNode *);
void Var_ExportVars(void);
@ -230,4 +311,5 @@ void Var_Export(const char *, Boolean);
void Var_UnExport(const char *);
/* util.c */
void (*bmake_signal(int, void (*)(int)))(int);
typedef void (*SignalProc)(int);
SignalProc bmake_signal(int, SignalProc);

3844
parse.c

File diff suppressed because it is too large Load Diff

62
str.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $ */
/* $NetBSD: str.c,v 1.70 2020/10/24 20:51:49 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@ -68,21 +68,11 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
#else
__RCSID("$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $");
#endif
#endif /* not lint */
#endif
#include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
MAKE_RCSID("$NetBSD: str.c,v 1.70 2020/10/24 20:51:49 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
str_concat2(const char *s1, const char *s2)
@ -157,7 +147,7 @@ Str_Words(const char *str, Boolean expand)
str_len = strlen(str);
words_buf = bmake_malloc(strlen(str) + 1);
words_cap = MAX((str_len / 5), 50);
words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
words = bmake_malloc((words_cap + 1) * sizeof(char *));
/*
@ -179,7 +169,7 @@ Str_Words(const char *str, Boolean expand)
else
break;
} else {
inquote = (char)ch;
inquote = ch;
/* Don't miss "" or '' */
if (word_start == NULL && str_p[1] == inquote) {
if (!expand) {
@ -278,46 +268,6 @@ Str_Words(const char *str, Boolean expand)
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]".
*

View File

@ -1,91 +0,0 @@
/* $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.6 2020/08/25 17:37:09 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $");
#endif /* not lint */
#endif
#include "make.h"
#include "strlist.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;
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 += n - 1;
items->si_str = str;
items->si_info = info;
items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */
}

View File

@ -1,62 +0,0 @@
/* $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 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 /* MAKE_STRLIST_H */

1591
suff.c

File diff suppressed because it is too large Load Diff

503
targ.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $ */
/* $NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@ -68,80 +68,67 @@
* SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94";
#else
__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 and a hash table.
*
* Interface:
* Targ_Init Initialization procedure.
* Targ_Init Initialization procedure.
*
* Targ_End Cleanup the module
* Targ_End Clean up the module
*
* Targ_List Return the list of all targets so far.
* 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_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_FindNode Find the node, or return NULL.
*
* 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_GetNode Find the node, or create it.
*
* Targ_Ignore Return TRUE if errors should be ignored when
* creating the given target.
* Targ_NewInternalNode
* Create an internal node.
*
* Targ_Silent Return TRUE if we should be silent when
* creating the given target.
* Targ_FindList Given a list of names, find nodes for all
* of them, creating them as necessary.
*
* Targ_Precious Return TRUE if the target is precious and
* should not be removed if we are interrupted.
* Targ_Ignore Return TRUE if errors should be ignored when
* creating the given target.
*
* Targ_Propagate Propagate information between related
* nodes. Should be called after the
* makefiles are parsed but before any
* action is taken.
* 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...
* Targ_PrintGraph
* Print out the entire graphm all variables and
* statistics for the directory cache. Should print
* something for suffixes, too, but...
*/
#include <stdio.h>
#include <time.h>
#include <time.h>
#include "make.h"
#include "dir.h"
#include "make.h"
#include "dir.h"
static Lst allTargets; /* the list of all targets found so far */
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $");
static GNodeList *allTargets; /* the list of all targets found so far */
#ifdef CLEANUP
static Lst allGNs; /* List of all the GNodes */
static GNodeList *allGNs; /* List of all the GNodes */
#endif
static Hash_Table targets; /* a hash table of same */
static HashTable targets; /* a hash table of same */
static int TargPrintOnlySrc(void *, void *);
static int TargPrintName(void *, void *);
#ifdef CLEANUP
static void TargFreeGN(void *);
#endif
@ -149,8 +136,8 @@ static void TargFreeGN(void *);
void
Targ_Init(void)
{
allTargets = Lst_Init();
Hash_InitTable(&targets, 191);
allTargets = Lst_New();
HashTable_Init(&targets);
}
void
@ -161,18 +148,18 @@ Targ_End(void)
Lst_Free(allTargets);
if (allGNs != NULL)
Lst_Destroy(allGNs, TargFreeGN);
Hash_DeleteTable(&targets);
HashTable_Done(&targets);
#endif
}
void
Targ_Stats(void)
{
Hash_DebugStats(&targets, "targets");
HashTable_DebugStats(&targets, "targets");
}
/* Return the list of all targets. */
Lst
GNodeList *
Targ_List(void)
{
return allTargets;
@ -182,7 +169,7 @@ Targ_List(void)
* all gnodes.
*
* Input:
* name the name of the node, such as "clean", "src.c"
* name the name of the node, such as "clean", "src.c", ".END"
*/
GNode *
Targ_NewGN(const char *name)
@ -194,30 +181,30 @@ Targ_NewGN(const char *name)
gn->uname = NULL;
gn->path = NULL;
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
gn->unmade = 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->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();
gn->suffix = NULL;
gn->fname = NULL;
gn->lineno = 0;
gn->cohort_num[0] = '\0';
gn->centurion = NULL;
gn->made = UNMADE;
gn->flags = 0;
gn->checked_seqno = 0;
gn->mtime = 0;
gn->youngestChild = NULL;
gn->implicitParents = Lst_New();
gn->cohorts = Lst_New();
gn->parents = Lst_New();
gn->children = Lst_New();
gn->order_pred = Lst_New();
gn->order_succ = Lst_New();
HashTable_Init(&gn->context);
gn->commands = Lst_New();
gn->suffix = NULL;
gn->fname = NULL;
gn->lineno = 0;
#ifdef CLEANUP
if (allGNs == NULL)
allGNs = Lst_Init();
allGNs = Lst_New();
Lst_Append(allGNs, gn);
#endif
@ -228,7 +215,7 @@ Targ_NewGN(const char *name)
static void
TargFreeGN(void *gnp)
{
GNode *gn = (GNode *)gnp;
GNode *gn = gnp;
free(gn->name);
free(gn->uname);
@ -240,53 +227,46 @@ TargFreeGN(void *gnp)
Lst_Free(gn->children);
Lst_Free(gn->order_succ);
Lst_Free(gn->order_pred);
Hash_DeleteTable(&gn->context);
HashTable_Done(&gn->context);
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
/* 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
*
* Results:
* 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
*/
/* Get the existing global node, or return NULL. */
GNode *
Targ_FindNode(const char *name, int flags)
Targ_FindNode(const char *name)
{
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 */
return HashTable_FindValue(&targets, name);
}
if (!(flags & (TARG_CREATE | TARG_NOHASH))) {
he = Hash_FindEntry(&targets, name);
if (he == NULL)
return NULL;
return (GNode *)Hash_GetValue(he);
/* Get the existing global node, or create it. */
GNode *
Targ_GetNode(const char *name)
{
Boolean isNew;
HashEntry *he = HashTable_CreateEntry(&targets, name, &isNew);
if (!isNew)
return HashEntry_Get(he);
{
GNode *gn = Targ_NewInternalNode(name);
HashEntry_Set(he, gn);
return gn;
}
}
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);
/* Create a node, register it in .ALLTARGETS but don't store it in the
* table of global nodes. This means it cannot be found by name.
*
* This is used for internal nodes, such as cohorts or .WAIT nodes. */
GNode *
Targ_NewInternalNode(const char *name)
{
GNode *gn = Targ_NewGN(name);
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
Lst_Append(allTargets, gn);
if (doing_depend)
@ -294,45 +274,30 @@ Targ_FindNode(const char *name, int flags)
return gn;
}
/* 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.
*/
Lst
Targ_FindList(Lst names, int flags)
/* Return the .END node, which contains the commands to be executed when
* everything else is done. */
GNode *Targ_GetEndNode(void)
{
Lst nodes; /* result list */
LstNode ln; /* name list element */
GNode *gn; /* node in tLn */
char *name;
nodes = Lst_Init();
Lst_Open(names);
while ((ln = Lst_Next(names)) != NULL) {
name = LstNode_Datum(ln);
gn = Targ_FindNode(name, flags);
if (gn != NULL) {
/*
* 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.
*/
Lst_Append(nodes, gn);
} else if (flags == TARG_NOCREATE) {
Error("\"%s\" -- target unknown.", name);
}
/* Save the node locally to avoid having to search for it all the time. */
static GNode *endNode = NULL;
if (endNode == NULL) {
endNode = Targ_GetNode(".END");
endNode->type = OP_SPECIAL;
}
return endNode;
}
/* Return the named nodes, creating them as necessary. */
GNodeList *
Targ_FindList(StringList *names)
{
StringListNode *ln;
GNodeList *nodes = Lst_New();
for (ln = names->first; ln != NULL; ln = ln->next) {
const char *name = ln->datum;
GNode *gn = Targ_GetNode(name);
Lst_Append(nodes, gn);
}
Lst_Close(names);
return nodes;
}
@ -340,14 +305,14 @@ Targ_FindList(Lst names, int flags)
Boolean
Targ_Ignore(GNode *gn)
{
return ignoreErrors || gn->type & OP_IGNORE;
return opts.ignoreErrors || gn->type & OP_IGNORE;
}
/* Return true if be silent when creating gn. */
Boolean
Targ_Silent(GNode *gn)
{
return beSilent || gn->type & OP_SILENT;
return opts.beSilent || gn->type & OP_SILENT;
}
/* See if the given target is precious. */
@ -369,22 +334,35 @@ Targ_SetMain(GNode *gn)
mainTarg = gn;
}
static int
TargPrintName(void *gnp, void *pflags MAKE_ATTR_UNUSED)
static void
PrintNodeNames(GNodeList *gnodes)
{
GNode *gn = (GNode *)gnp;
GNodeListNode *node;
fprintf(debug_file, "%s%s ", gn->name, gn->cohort_num);
return 0;
for (node = gnodes->first; node != NULL; node = node->next) {
GNode *gn = node->datum;
debug_printf(" %s%s", gn->name, gn->cohort_num);
}
}
int
Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED)
static void
PrintNodeNamesLine(const char *label, GNodeList *gnodes)
{
fprintf(debug_file, "\t%s\n", (char *)cmd);
return 0;
if (Lst_IsEmpty(gnodes))
return;
debug_printf("# %s:", label);
PrintNodeNames(gnodes);
debug_printf("\n");
}
void
Targ_PrintCmds(GNode *gn)
{
StringListNode *ln;
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum;
debug_printf("\t%s\n", cmd);
}
}
/* Format a modification time in some reasonable way and return it.
@ -392,8 +370,8 @@ Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED)
char *
Targ_FmtTime(time_t tm)
{
struct tm *parts;
static char buf[128];
struct tm *parts;
static char buf[128];
parts = localtime(&tm);
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
@ -406,8 +384,8 @@ 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
#define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break
type &= ~OP_OPMASK;
@ -416,22 +394,22 @@ Targ_PrintType(int type)
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);
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);
case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break;
PRINTDBIT(ARCHV);
PRINTDBIT(MADE);
PRINTDBIT(PHONY);
}
}
}
@ -452,102 +430,96 @@ made_name(GNodeMade made)
}
}
/* Print the contents of a node. */
int
Targ_PrintNode(void *gnp, void *passp)
static const char *
GNode_OpName(const GNode *gn)
{
GNode *gn = (GNode *)gnp;
int pass = passp ? *(int *)passp : 0;
switch (gn->type & OP_OPMASK) {
case OP_DEPENDS:
return ":";
case OP_FORCE:
return "!";
case OP_DOUBLEDEP:
return "::";
}
return "";
}
fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num);
GNode_FprintDetails(debug_file, ", ", gn, "\n");
/* Print the contents of a node. */
void
Targ_PrintNode(GNode *gn, int pass)
{
debug_printf("# %s%s", gn->name, gn->cohort_num);
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
if (gn->flags == 0)
return 0;
return;
if (!OP_NOP(gn->type)) {
fprintf(debug_file, "#\n");
if (GNode_IsTarget(gn)) {
debug_printf("#\n");
if (gn == mainTarg) {
fprintf(debug_file, "# *** MAIN TARGET ***\n");
debug_printf("# *** MAIN TARGET ***\n");
}
if (pass >= 2) {
if (gn->unmade) {
fprintf(debug_file, "# %d unmade children\n", gn->unmade);
debug_printf("# %d unmade children\n", gn->unmade);
} else {
fprintf(debug_file, "# No unmade children\n");
debug_printf("# 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));
debug_printf("# 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));
debug_printf("# non-existent (maybe): %s\n",
made_name(gn->made));
} else {
fprintf(debug_file, "# unmade\n");
debug_printf("# unmade\n");
}
}
if (!Lst_IsEmpty(gn->implicitParents)) {
fprintf(debug_file, "# implicit parents: ");
Lst_ForEach(gn->implicitParents, TargPrintName, NULL);
fprintf(debug_file, "\n");
}
PrintNodeNamesLine("implicit parents", gn->implicitParents);
} else {
if (gn->unmade)
fprintf(debug_file, "# %d unmade children\n", gn->unmade);
}
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)) {
fprintf(debug_file, "# order_pred: ");
Lst_ForEach(gn->order_pred, TargPrintName, NULL);
fprintf(debug_file, "\n");
}
if (!Lst_IsEmpty(gn->order_succ)) {
fprintf(debug_file, "# order_succ: ");
Lst_ForEach(gn->order_succ, TargPrintName, NULL);
fprintf(debug_file, "\n");
debug_printf("# %d unmade children\n", gn->unmade);
}
PrintNodeNamesLine("parents", gn->parents);
PrintNodeNamesLine("order_pred", gn->order_pred);
PrintNodeNamesLine("order_succ", gn->order_succ);
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;
}
debug_printf("%-16s%s", gn->name, GNode_OpName(gn));
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");
PrintNodeNames(gn->children);
debug_printf("\n");
Targ_PrintCmds(gn);
debug_printf("\n\n");
if (gn->type & OP_DOUBLEDEP) {
Lst_ForEach(gn->cohorts, Targ_PrintNode, &pass);
Targ_PrintNodes(gn->cohorts, pass);
}
}
return 0;
}
/* 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)
void
Targ_PrintNodes(GNodeList *gnodes, int pass)
{
GNode *gn = (GNode *)gnp;
if (!OP_NOP(gn->type))
return 0;
GNodeListNode *ln;
for (ln = gnodes->first; ln != NULL; ln = ln->next)
Targ_PrintNode(ln->datum, pass);
}
fprintf(debug_file, "#\t%s [%s] ",
gn->name, gn->path ? gn->path : gn->name);
Targ_PrintType(gn->type);
fprintf(debug_file, "\n");
/* Print only those targets that are just a source. */
static void
PrintOnlySources(void)
{
GNodeListNode *ln;
return 0;
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
if (GNode_IsTarget(gn))
continue;
debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn));
Targ_PrintType(gn->type);
debug_printf("\n");
}
}
/* Input:
@ -558,18 +530,18 @@ TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED)
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");
debug_printf("#*** Input graph:\n");
Targ_PrintNodes(allTargets, pass);
debug_printf("\n\n");
debug_printf("#\n# Files that are only sources:\n");
PrintOnlySources();
debug_printf("#*** Global Variables:\n");
Var_Dump(VAR_GLOBAL);
fprintf(debug_file, "#*** Command-line Variables:\n");
Var_Dump(VAR_CMD);
fprintf(debug_file, "\n");
debug_printf("#*** Command-line Variables:\n");
Var_Dump(VAR_CMDLINE);
debug_printf("\n");
Dir_PrintDirectories();
fprintf(debug_file, "\n");
debug_printf("\n");
Suff_PrintAll();
}
@ -581,18 +553,19 @@ Targ_PrintGraph(int pass)
void
Targ_Propagate(void)
{
LstNode pn, cn;
GNodeListNode *ln, *cln;
for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) {
GNode *pgn = LstNode_Datum(pn);
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
GNodeType type = gn->type;
if (!(pgn->type & OP_DOUBLEDEP))
if (!(type & OP_DOUBLEDEP))
continue;
for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) {
GNode *cgn = LstNode_Datum(cn);
for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) {
GNode *cohort = cln->datum;
cgn->type |= pgn->type & ~OP_OPMASK;
cohort->type |= type & ~OP_OPMASK;
}
}
}

21
trace.c
View File

@ -1,4 +1,4 @@
/* $NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $ */
/* $NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -29,16 +29,6 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef MAKE_NATIVE
static char rcsid[] = "$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
__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.
@ -54,13 +44,12 @@ __RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $");
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include "make.h"
#include "job.h"
#include "trace.h"
MAKE_RCSID("$NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $");
static FILE *trfile;
static pid_t trpid;
const char *trwd;
@ -78,8 +67,10 @@ void
Trace_Init(const char *pathname)
{
if (pathname != NULL) {
char *dontFreeIt;
void *dontFreeIt;
trpid = getpid();
/* XXX: This variable may get overwritten later, which
* would make trwd point to undefined behavior. */
trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt);
trfile = fopen(pathname, "a");

View File

@ -1,4 +1,4 @@
/* $NetBSD: trace.h,v 1.3 2008/04/28 20:24:14 martin Exp $ */
/* $NetBSD: trace.h,v 1.4 2020/10/18 17:19:54 rillig Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -34,7 +34,7 @@
* Definitions pertaining to the tracing of jobs in parallel mode.
*/
typedef enum {
typedef enum TrEvent {
MAKESTART,
MAKEEND,
MAKEERROR,

View File

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $
# $Id: Makefile,v 1.107 2020/11/02 00:40:25 sjg Exp $
#
# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $
# $NetBSD: Makefile,v 1.181 2020/11/01 19:02:22 rillig Exp $
#
# Unit tests for make(1)
#
@ -34,7 +34,7 @@
# 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
TESTS+= archive-suffix
TESTS+= cmd-interrupt
TESTS+= cmdline
@ -47,6 +47,7 @@ TESTS+= cond-cmp-numeric-le
TESTS+= cond-cmp-numeric-lt
TESTS+= cond-cmp-numeric-ne
TESTS+= cond-cmp-string
TESTS+= cond-cmp-unary
TESTS+= cond-func
TESTS+= cond-func-commands
TESTS+= cond-func-defined
@ -65,17 +66,22 @@ TESTS+= cond-token-number
TESTS+= cond-token-plain
TESTS+= cond-token-string
TESTS+= cond-token-var
TESTS+= cond-undef-lint
TESTS+= cond1
TESTS+= cond2
TESTS+= counter
TESTS+= counter-append
TESTS+= dep
TESTS+= dep-colon
TESTS+= dep-colon-bug-cross-file
TESTS+= dep-double-colon
TESTS+= dep-double-colon-indep
TESTS+= dep-exclam
TESTS+= dep-none
TESTS+= dep-percent
TESTS+= dep-var
TESTS+= dep-wildcards
TESTS+= depsrc
TESTS+= depsrc-end
TESTS+= depsrc-exec
TESTS+= depsrc-ignore
TESTS+= depsrc-made
@ -99,6 +105,7 @@ TESTS+= deptgt-begin
TESTS+= deptgt-default
TESTS+= deptgt-delete_on_error
TESTS+= deptgt-end
TESTS+= deptgt-end-jobs
TESTS+= deptgt-error
TESTS+= deptgt-ignore
TESTS+= deptgt-interrupt
@ -120,6 +127,7 @@ TESTS+= deptgt-suffixes
TESTS+= dir
TESTS+= dir-expand-path
TESTS+= directive
TESTS+= directive-dinclude
TESTS+= directive-elif
TESTS+= directive-elifdef
TESTS+= directive-elifmake
@ -130,15 +138,20 @@ TESTS+= directive-endif
TESTS+= directive-error
TESTS+= directive-export
TESTS+= directive-export-env
TESTS+= directive-export-gmake
TESTS+= directive-export-literal
TESTS+= directive-for
TESTS+= directive-for-generating-endif
TESTS+= directive-hyphen-include
TESTS+= directive-if
TESTS+= directive-ifdef
TESTS+= directive-ifmake
TESTS+= directive-ifndef
TESTS+= directive-ifnmake
TESTS+= directive-include
TESTS+= directive-include-fatal
TESTS+= directive-info
TESTS+= directive-sinclude
TESTS+= directive-undef
TESTS+= directive-unexport
TESTS+= directive-unexport-env
@ -156,12 +169,12 @@ TESTS+= export-env
TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst
TESTS+= hash
TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
#TESTS+= job-output-long-lines
TESTS+= lint
TESTS+= make-exported
TESTS+= misc
TESTS+= moderrs
TESTS+= modmatch
TESTS+= modmisc
@ -171,7 +184,30 @@ TESTS+= opt
TESTS+= opt-backwards
TESTS+= opt-chdir
TESTS+= opt-debug
TESTS+= opt-debug-g1
TESTS+= opt-debug-all
TESTS+= opt-debug-archive
TESTS+= opt-debug-curdir
TESTS+= opt-debug-cond
TESTS+= opt-debug-dir
TESTS+= opt-debug-errors
TESTS+= opt-debug-file
TESTS+= opt-debug-for
TESTS+= opt-debug-graph1
TESTS+= opt-debug-graph2
TESTS+= opt-debug-graph3
TESTS+= opt-debug-hash
#TESTS+= opt-debug-jobs
TESTS+= opt-debug-lint
TESTS+= opt-debug-loud
TESTS+= opt-debug-meta
TESTS+= opt-debug-making
TESTS+= opt-debug-no-rm
TESTS+= opt-debug-parse
TESTS+= opt-debug-suff
TESTS+= opt-debug-targets
TESTS+= opt-debug-varraw
TESTS+= opt-debug-var
TESTS+= opt-debug-x-trace
TESTS+= opt-define
TESTS+= opt-env
TESTS+= opt-file
@ -194,6 +230,7 @@ TESTS+= opt-warnings-as-errors
TESTS+= opt-where-am-i
TESTS+= opt-x-reduce-exported
TESTS+= order
TESTS+= parse-var
TESTS+= phony-end
TESTS+= posix
TESTS+= # posix1 # broken by reverting POSIX changes
@ -209,9 +246,20 @@ 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+= shell-csh
TESTS+= shell-custom
TESTS+= shell-ksh
TESTS+= shell-sh
TESTS+= suff-add-later
TESTS+= suff-clear-regular
TESTS+= suff-clear-single
TESTS+= suff-lookup
TESTS+= suff-main
TESTS+= suff-rebuild
TESTS+= suff-transform-endless
TESTS+= suff-transform-expand
TESTS+= suff-transform-select
TESTS+= sunshcmd
TESTS+= sysv
TESTS+= ternary
TESTS+= unexport
TESTS+= unexport-env
@ -228,6 +276,8 @@ TESTS+= var-op-assign
TESTS+= var-op-default
TESTS+= var-op-expand
TESTS+= var-op-shell
TESTS+= var-op-sunsh
TESTS+= var-recursive
TESTS+= varcmd
TESTS+= vardebug
TESTS+= varfind
@ -310,10 +360,14 @@ TESTS+= varname-dot-targets
TESTS+= varname-empty
TESTS+= varname-make
TESTS+= varname-make_print_var_on_error
TESTS+= varname-make_print_var_on_error-jobs
TESTS+= varname-makefile
TESTS+= varname-makeflags
TESTS+= varname-pwd
TESTS+= varname-vpath
TESTS+= varparse-dynamic
TESTS+= varparse-mod
TESTS+= varparse-undef-partial
TESTS+= varquote
TESTS+= varshell
@ -323,18 +377,16 @@ ENV.envfirst= FROM_ENV=value-from-env
ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=env
ENV.varmisc+= FROM_ENV_AFTER=env
ENV.varmod-localtime+= TZ=Europe/Berlin
# 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.cond-func-make= via-cmdline
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
@ -345,32 +397,52 @@ 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.opt-debug-g1= -e 's,${.CURDIR},CURDIR,'
SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d'
# Some tests need extra postprocessing.
SED_CMDS.export= \
-e '/^[^=_A-Za-z0-9]*=/d'
# these all share the same requirement
.for t in export-all export-env
SED_CMDS.$t= ${SED_CMDS.export}
.endfor
SED_CMDS.job-output-long-lines= \
${:D Job separators on their own line are ok. } \
-e '/^--- job-[ab] ---$$/d' \
${:D Plain output lines are ok as well. } \
${:D They may come in multiples of 1024 or as 10000. } \
-e '/^aa*$$/d' \
-e '/^bb*$$/d' \
${:D The following lines should rather not occur since the job } \
${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d'
SED_CMDS.opt-debug-graph1= \
-e 's,${.CURDIR},CURDIR,'
SED_CMDS.opt-debug-graph1+= \
-e '/Global Variables:/,/Suffixes:/d'
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
# The "-q" may be there or not, see jobs.c, variable shells.
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: sh\) -q,\1,'
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)],'
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
# 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= ${TOOL_SED} -n -e '/^MAGIC/p' -e '/^ORDER_/p'
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
# Some tests reuse other tests, which makes them unnecessarily fragile.
@ -388,7 +460,11 @@ unexport-env.rawout: export.mk
UNIT_TESTS:= ${.PARSEDIR}
.PATH: ${UNIT_TESTS}
.if ${USE_ABSOLUTE_TESTNAMES:Uno} == yes
OUTFILES= ${TESTS:@test@${.CURDIR:tA}/${test}.out@}
.else
OUTFILES= ${TESTS:=.out}
.endif
all: ${OUTFILES}
@ -419,22 +495,26 @@ LANG= C
_MKMSG_TEST= :
.endif
# the tests are actually done with sub-makes.
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
# Each test is run in a sub-make, to keep the tests for interfering with
# each other, and because they use different environment variables and
# command line options.
.SUFFIXES: .mk .rawout .out
.mk.rawout:
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
@set -eu; \
cd ${.OBJDIR}; \
env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
${TEST_MAKE} \
-r -C ${.CURDIR} -f ${.IMPSRC} \
${FLAGS.${.TARGET:R}:U-k} \
${FLAGS.${.PREFIX:T}: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.
# Postprocess 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:,'
@ -446,9 +526,9 @@ _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
.rawout.out:
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
< ${.IMPSRC} > ${.TARGET}.tmp1
@${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
@${POSTPROC.${.PREFIX:T}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
@rm ${.TARGET}.tmp1
@echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
@mv ${.TARGET}.tmp2 ${.TARGET}
@ -474,7 +554,12 @@ accept:
done
.if exists(${TEST_MAKE})
${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile
${TESTS:=.rawout}: ${TEST_MAKE}
# in meta mode, we *know* if a target script is impacted
# by a makefile change.
.if ${.MAKE.MODE:Unormal:Mmeta} == ""
${TESTS:=.rawout}: ${.PARSEDIR}/Makefile
.endif
.endif
.-include <obj.mk>

View File

@ -1,13 +1,28 @@
Making remove-archive
rm -f libprog.a
Making libprog.a out-of-date archive.mk modmisc.mk varmisc.mk
ar cru libprog.a archive.mk modmisc.mk varmisc.mk
ranlib libprog.a
Making create-archive out-of-date libprog.a
Making list-archive out-of-date libprog.a
ar t libprog.a
archive.mk
modmisc.mk
varmisc.mk
Making list-archive-wildcard out-of-date archive-suffix.mk archive.mk ternary.mk
list-archive-wildcard: archive-suffix.mk
list-archive-wildcard: archive.mk
list-archive-wildcard: ternary.mk
Making depend-on-existing-member out-of-date archive.mk
depend-on-existing-member
`depend-on-nonexistent-member' is up to date.
Making remove-archive
rm -f libprog.a
exit status 0

View File

@ -1,18 +1,25 @@
# $NetBSD: archive.mk,v 1.5 2020/08/23 17:51:24 rillig Exp $
# $NetBSD: archive.mk,v 1.10 2020/10/09 06:44:42 rillig Exp $
#
# Very basic demonstration of handling archives, based on the description
# in PSD.doc/tutorial.ms.
#
# This test aims at covering the code, not at being an introduction to
# archive handling. That's why it is more complicated and detailed than
# strictly necessary.
ARCHIVE= libprog.${EXT.a}
FILES= archive.${EXT.mk} modmisc.${EXT.mk} varmisc.mk
EXT.a= a
EXT.mk= mk
ARCHIVE= libprog.a
FILES= archive.mk modmisc.mk varmisc.mk
MAKE_CMD= ${.MAKE} -f ${MAKEFILE}
RUN?= @set -eu;
all:
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
@cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
.endif
# The following targets create and remove files. The filesystem cache in
# dir.c would probably not handle this correctly, therefore each of the
# targets is run in its separate sub-make.
${RUN} ${MAKE_CMD} remove-archive
${RUN} ${MAKE_CMD} create-archive
${RUN} ${MAKE_CMD} list-archive
@ -21,25 +28,35 @@ all:
${RUN} ${MAKE_CMD} depend-on-nonexistent-member
${RUN} ${MAKE_CMD} remove-archive
create-archive: ${ARCHIVE}
${ARCHIVE}: ${ARCHIVE}(${FILES})
ar cru ${.TARGET} ${.OODATE}
create-archive: ${ARCHIVE} pre post
# The indirect references with the $$ cover the code in Arch_ParseArchive
# that calls Var_Parse. It's an esoteric scenario since at the point where
# Arch_ParseArchive is called, the dependency line is already fully expanded.
#
${ARCHIVE}: $${:Ulibprog.a}(archive.mk modmisc.mk $${:Uvarmisc.mk}) pre post
ar cru ${.TARGET} ${.OODATE:O}
ranlib ${.TARGET}
list-archive: ${ARCHIVE}
list-archive: ${ARCHIVE} pre post
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)
list-archive-wildcard: ${ARCHIVE}([at]*.mk) pre post
${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
depend-on-existing-member: ${ARCHIVE}(archive.mk)
depend-on-existing-member: ${ARCHIVE}(archive.mk) pre post
${RUN} echo $@
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk)
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) pre post
${RUN} echo $@
remove-archive:
remove-archive: pre post
rm -f ${ARCHIVE}
pre: .USEBEFORE
@echo Making ${.TARGET} ${.OODATE:C,.+,out-of-date,W} ${.OODATE:O}
post: .USE
@echo

View File

@ -1,5 +1,6 @@
comment testing start
this is foo
This is how a comment looks: # comment
comment testing done
echo This is a shell comment: # comment
This is a shell comment:
echo This is not a shell comment: '# comment'
This is not a shell comment: # comment
A shell comment can#not start in the middle of a word.
exit status 0

View File

@ -1,31 +1,74 @@
# This is a comment
.if ${MACHINE_ARCH} == something
FOO=bar
.endif
# $NetBSD: comment.mk,v 1.2 2020/09/07 19:17:36 rillig Exp $
#
# Demonstrate how comments are written in makefiles.
# This is a comment.
#\
Multiline comment
This is a multiline comment.
BAR=# defined
FOOBAR= # defined
# This is an escaped comment \
that keeps going until the end of this line
# Another escaped comment \
# Another multiline comment \
that \
goes \
on
on and on.
# Comments can be indented, but that is rather unusual.
# Comments can be indented with a tab.
# These are not shell commands, they are just makefile comments.
.if 1 # There can be comments after conditions.
.endif # And after the closing directive.
VAR= # This comment makes the variable value empty.
.if ${VAR} != ""
. error
.endif
# The comment does not need to start at the beginning of a word (as in the
# shell), it can start anywhere.
VAR=# defined but empty
# The space before the comment is always trimmed.
VAR= value
.if ${VAR} != "value"
. error
.endif
# This is NOT an escaped comment due to the double backslashes \\
all: hi foo bar
@echo comment testing done
VAR= not part of the comment
.if ${VAR} != "not part of the comment"
. error
.endif
hi:
@echo comment testing start
# To escape a comment sign, precede it with a backslash.
VAR= \# # Both in the assignment.
.if ${VAR} != "\#" # And in the comparison.
. error
.endif
foo:
@echo this is $@
# Since 2012-03-24 the variable modifier :[#] does not need to be escaped.
# To keep the parsing code simple, any "[#" does not start a comment, even
# outside of a variable expression.
WORDS= ${VAR:[#]} [#
.if ${WORDS} != "1 [#"
. error
.endif
bar:
@echo This is how a comment looks: '# comment'
# An odd number of comment signs makes a line continuation, \\\
no matter if it is 3 or 5 \\\\\
or 9 backslashes. \\\\\\\\\
This is the last line of the comment.
VAR= no comment anymore
.if ${VAR} != "no comment anymore"
. error
.endif
all:
# In the commands associated with a target, the '#' does not start a makefile
# comment. The '#' is just passed to the shell, like any ordinary character.
echo This is a shell comment: # comment
# If the '#' were to start a makefile comment, the following shell command
# would have unbalanced quotes.
echo This is not a shell comment: '# comment'
@echo A shell comment can#not start in the middle of a word.

View File

@ -1 +1,6 @@
exit status 0
make: "cond-cmp-numeric-eq.mk" line 54: warning: Unknown operator
make: "cond-cmp-numeric-eq.mk" line 54: Malformed conditional (!(12345 = 12345))
make: "cond-cmp-numeric-eq.mk" line 61: Malformed conditional (!(12345 === 12345))
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,43 +1,43 @@
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.4 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 == 2
.error
. error
.endif
.if 2 == 1
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 == 2000e4
.else
.error
. error
.endif
.if 2000e4 == 2e7
.else
.error
. error
.endif
# Trailing zeroes after the decimal point are irrelevant for the numeric
# value.
.if 3.30000 == 3.3
.else
.error
. error
.endif
.if 3.3 == 3.30000
.else
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -46,7 +46,22 @@
# be equal.
.if 1.000000000000000001 == 1.000000000000000002
.else
.error
. error
.endif
# There is no = operator for numbers.
.if !(12345 = 12345)
. error
.else
. error
.endif
# There is no === operator for numbers either.
.if !(12345 === 12345)
. error
.else
. error
.endif
all:

View File

@ -1,65 +1,65 @@
# $NetBSD: cond-cmp-numeric-ge.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-ge.mk,v 1.2 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 >= 2
.error
. error
.endif
.if 2 >= 1
.else
.error
. 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
. error
.endif
.if 14 >= 5
.else
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 >= 1e8
.error
. error
.endif
.if 1e8 >= 2e7
.else
.error
. 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
. error
.endif
.if 111.222 >= 3.141
.else
.error
. 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
. error
.endif
.if 3.7 >= 3.30
.else
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -68,7 +68,7 @@
# be equal.
.if 1.000000000000000001 >= 1.000000000000000002
.else
.error
. error
.endif
all:

View File

@ -1,64 +1,64 @@
# $NetBSD: cond-cmp-numeric-gt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-gt.mk,v 1.2 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 > 2
.error
. error
.endif
.if 2 > 1
.else
.error
. 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
. error
.endif
.if 14 > 5
.else
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 > 1e8
.error
. error
.endif
.if 1e8 > 2e7
.else
.error
. 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
. error
.endif
.if 111.222 > 3.141
.else
.error
. 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
. error
.endif
.if 3.7 > 3.30
.else
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -66,7 +66,7 @@
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
.if 1.000000000000000001 > 1.000000000000000002
.error
. error
.endif
all:

View File

@ -1,53 +1,53 @@
# $NetBSD: cond-cmp-numeric-le.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-le.mk,v 1.2 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 <= 2
.else
.error
. error
.endif
.if 2 <= 1
.error
. 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
. error
.endif
.if 14 <= 5
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 <= 1e8
.else
.error
. error
.endif
.if 1e8 <= 2e7
.error
. 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
. error
.endif
.if 111.222 <= 3.141
.error
. error
.endif
# When parsed as a version number, 3.30 is greater than 3.7.
@ -55,11 +55,11 @@
# Numeric comparisons are not suited for comparing version number.
.if 3.30 <= 3.7
.else
.error
. error
.endif
.if 3.7 <= 3.30
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -68,7 +68,7 @@
# be equal.
.if 1.000000000000000001 <= 1.000000000000000002
.else
.error
. error
.endif
all:

View File

@ -1,52 +1,52 @@
# $NetBSD: cond-cmp-numeric-lt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-lt.mk,v 1.2 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 < 2
.else
.error
. error
.endif
.if 2 < 1
.error
. 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
. error
.endif
.if 14 < 5
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 < 1e8
.else
.error
. error
.endif
.if 1e8 < 2e7
.error
. 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
. error
.endif
.if 111.222 < 3.141
.error
. error
.endif
# When parsed as a version number, 3.30 is greater than 3.7.
@ -54,11 +54,11 @@
# Numeric comparisons are not suited for comparing version number.
.if 3.30 < 3.7
.else
.error
. error
.endif
.if 3.7 < 3.30
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -66,7 +66,7 @@
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
.if 1.000000000000000001 < 1.000000000000000002
.error
. error
.endif
all:

View File

@ -1,40 +1,40 @@
# $NetBSD: cond-cmp-numeric-ne.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
# $NetBSD: cond-cmp-numeric-ne.mk,v 1.2 2020/10/24 08:46:08 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
. error
.endif
# This comparison yields the same result, whether numeric or character-based.
.if 1 != 2
.else
.error
. error
.endif
.if 2 != 1
.else
.error
. error
.endif
# Scientific notation is supported, as per strtod.
.if 2e7 != 2000e4
.error
. error
.endif
.if 2000e4 != 2e7
.error
. error
.endif
# Trailing zeroes after the decimal point are irrelevant for the numeric
# value.
.if 3.30000 != 3.3
.error
. error
.endif
.if 3.3 != 3.30000
.error
. error
.endif
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
@ -42,7 +42,7 @@
# typically 16 or 17 significant digits, therefore these two numbers seem to
# be equal.
.if 1.000000000000000001 != 1.000000000000000002
.error
. error
.endif
all:

View File

@ -1 +1,11 @@
exit status 0
CondParser_Eval: !(${:UINF} > 1e100)
make: "cond-cmp-numeric.mk" line 11: warning: String comparison operator must be either == or !=
make: "cond-cmp-numeric.mk" line 11: Malformed conditional (!(${:UINF} > 1e100))
CondParser_Eval: ${:UNaN} > NaN
make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be either == or !=
make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN)
CondParser_Eval: !(${:UNaN} == NaN)
lhs = "NaN", rhs = "NaN", op = ==
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,8 +1,29 @@
# $NetBSD: cond-cmp-numeric.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: cond-cmp-numeric.mk,v 1.3 2020/09/12 18:01:51 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
# TODO: Implementation
.MAKEFLAGS: -dc
# The ${:U...} on the left-hand side is necessary for the parser.
# Even if strtod(3) parses "INF" as +Infinity, make does not accept this
# since it is not really a number; see TryParseNumber.
.if !(${:UINF} > 1e100)
. error
.endif
# Neither is NaN a number; see TryParseNumber.
.if ${:UNaN} > NaN
. error
.endif
# Since NaN is not parsed as a number, both operands are interpreted
# as strings and are therefore equal. If they were parsed as numbers,
# they would compare unequal, since NaN is unequal to any and everything,
# including itself.
.if !(${:UNaN} == NaN)
. error
.endif
all:
@:;

View File

@ -1,5 +1,8 @@
make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing")
make: "cond-cmp-string.mk" line 42: warning: String comparison operator must be either == or !=
make: "cond-cmp-string.mk" line 42: Malformed conditional (!("value" = "value"))
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" === "value"))
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,39 +1,90 @@
# $NetBSD: cond-cmp-string.mk,v 1.3 2020/08/20 18:43:19 rillig Exp $
# $NetBSD: cond-cmp-string.mk,v 1.11 2020/10/30 14:53:31 rillig Exp $
#
# Tests for string comparisons in .if conditions.
# This is a simple comparison of string literals.
# Nothing surprising here.
.if "str" != "str"
.error
. error
.endif
# The right-hand side of the comparison may be written without quotes.
.if "str" != str
.error
. 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
. 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
. 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
. error
.endif
# It is not possible to concatenate two string literals to form a single
# string.
.if "string" != "str""ing"
.error
. error
.endif
# There is no = operator for strings.
.if !("value" = "value")
. error
.else
. error
.endif
# There is no === operator for strings either.
.if !("value" === "value")
. error
.else
. error
.endif
# A variable expression can be enclosed in double quotes.
.if ${:Uword} != "${:Uword}"
. error
.endif
# Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the
# characters " \t!=><" directly after a variable expression resulted in a
# "Malformed conditional", even though the string was well-formed.
.if ${:Uword } != "${:Uword} "
. error
.endif
# Some other characters worked though, and some didn't.
# Those that are mentioned in is_separator didn't work.
.if ${:Uword0} != "${:Uword}0"
. error
.endif
.if ${:Uword&} != "${:Uword}&"
. error
.endif
.if ${:Uword!} != "${:Uword}!"
. error
.endif
.if ${:Uword<} != "${:Uword}<"
. error
.endif
# Adding another variable expression to the string literal works though.
.if ${:Uword} != "${:Uwo}${:Urd}"
. error
.endif
# Adding a space at the beginning of the quoted variable expression works
# though.
.if ${:U word } != " ${:Uword} "
. error
.endif

0
unit-tests/misc.exp → unit-tests/cond-cmp-unary.exp Normal file → Executable file
View File

43
unit-tests/cond-cmp-unary.mk Executable file
View File

@ -0,0 +1,43 @@
# $NetBSD: cond-cmp-unary.mk,v 1.1 2020/09/14 06:22:59 rillig Exp $
#
# Tests for unary comparisons in .if conditions, that is, comparisons with
# a single operand. If the operand is a number, it is compared to zero,
# if it is a string, it is tested for emptiness.
# The number 0 evaluates to false.
.if 0
. error
.endif
# Any other number evaluates to true.
.if !12345
. error
.endif
# The empty string evaluates to false.
.if ""
. error
.endif
# Any other string evaluates to true.
.if !"0"
. error
.endif
# The empty string may come from a variable expression.
.if ${:U}
. error
.endif
# A variable expression that is not surrounded by quotes is interpreted
# as a number if possible, otherwise as a string.
.if ${:U0}
. error
.endif
# A non-zero number from a variable expression evaluates to true.
.if !${:U12345}
. error
.endif
all: # nothing

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-commands.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
# $NetBSD: cond-func-commands.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the commands() function in .if conditions.
@ -6,14 +6,14 @@
# The target "target" does not exist yet, therefore it cannot have commands.
.if commands(target)
.error
. error
.endif
target:
# Now the target exists, but it still has no commands.
.if commands(target)
.error
. error
.endif
target:
@ -21,7 +21,7 @@ target:
# Even after the comment, the target still has no commands.
.if commands(target)
.error
. error
.endif
target:
@ -29,7 +29,7 @@ target:
# Finally the target has commands.
.if !commands(target)
.error
. error
.endif
all:

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-defined.mk,v 1.3 2020/08/20 17:23:43 rillig Exp $
# $NetBSD: cond-func-defined.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@ -6,27 +6,27 @@ DEF= defined
${:UA B}= variable name with spaces
.if !defined(DEF)
.error
. error
.endif
# Horizontal whitespace after the opening parenthesis is ignored.
# Horizontal whitespace (space tab) after the opening parenthesis is ignored.
.if !defined( DEF)
.error
. error
.endif
# Horizontal whitespace before the closing parenthesis is ignored.
# Horizontal whitespace (space tab) before the closing parenthesis is ignored.
.if !defined(DEF )
.error
. error
.endif
# The argument of a function must not directly contain whitespace.
.if !defined(A B)
.error
. error
.endif
# If necessary, the whitespace can be generated by a variable expression.
.if !defined(${:UA B})
.error
. error
.endif
all:

View File

@ -1,8 +1,150 @@
# $NetBSD: cond-func-empty.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: cond-func-empty.mk,v 1.8 2020/09/23 08:11:28 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
#
# Note that the argument in the parentheses is indeed a variable name,
# optionally followed by variable modifiers. This is like the defined()
# function.
#
# Tests for the empty() function in .if conditions.
# TODO: Implementation
.undef UNDEF
EMPTY= # empty
SPACE= ${:U }
WORD= word
# An undefined variable is empty.
.if !empty(UNDEF)
. error
.endif
# An undefined variable has the empty string as the value, and the :M
# variable modifier does not change that.
#
.if !empty(UNDEF:M*)
. error
.endif
# The :S modifier replaces the empty value with an actual word, and
# after that the expression is no longer empty. Because the variable
# was undefined in the first place, the expression has the flag VAR_JUNK
# but not VAR_KEEP, therefore it is still considered undefined.
# Only very few variable modifiers turn an undefined variable expression
# into a defined variable expression. The :U and :D modifiers belong to
# that group, but :S doesn't (see VAR_KEEP).
#
# XXX: This is hard to explain to someone who doesn't know these
# implementation details.
#
.if !empty(UNDEF:S,^$,value,W)
. error
.endif
# The :U modifier modifies expressions based on undefined variables
# (VAR_JUNK) by adding the VAR_KEEP flag, which marks the expression
# as "being interesting enough to be further processed".
#
.if empty(UNDEF:S,^$,value,W:Ufallback)
. error
.endif
# And now to the surprising part. Applying the following :S modifier to the
# undefined variable makes it non-empty, but the marker VAR_JUNK is preserved
# nevertheless. The :U modifier that follows only looks at VAR_JUNK to decide
# whether the variable is defined or not. This kind of makes sense since the
# :U modifier tests the _variable_, not the _expression_.
#
# But since the variable was undefined to begin with, the fallback value is
# used in this expression.
#
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
. error
.endif
# The variable EMPTY is completely empty (0 characters).
.if !empty(EMPTY)
. error
.endif
# The variable SPACE has a single space, which counts as being empty.
.if !empty(SPACE)
. error
.endif
# The variable .newline has a single newline, which counts as being empty.
.if !empty(.newline)
. error
.endif
# The empty variable named "" gets a fallback value of " ", which counts as
# empty.
#
# Contrary to the other functions in conditionals, the trailing space is not
# stripped off, as can be seen in the -dv debug log. If the space had been
# stripped, it wouldn't make a difference in this case.
#
.if !empty(:U )
. error
.endif
# Now the variable named " " gets a non-empty value, which demonstrates that
# neither leading nor trailing spaces are trimmed in the argument of the
# function. If the spaces were trimmed, the variable name would be "" and
# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse
# without VARE_UNDEFERR, the value of the undefined variable is returned as
# an empty string.
${:U }= space
.if empty( )
. error
.endif
# The value of the following expression is " word", which is not empty.
.if empty(:U word)
. error
.endif
# The :L modifier creates a variable expression that has the same value as
# its name, which both are "VAR" in this case. The value is therefore not
# empty.
.if empty(VAR:L)
. error
.endif
# The variable WORD has the value "word", which does not count as empty.
.if empty(WORD)
. error
.endif
# The expression ${} for a variable with the empty name always evaluates
# to an empty string (see Var_Parse, varUndefined).
.if !empty()
. error
.endif
# Ensure that variable expressions that appear as part of the argument are
# properly parsed. Typical use cases for this are .for loops, which are
# expanded to exactly these ${:U} expressions.
#
# If everything goes well, the argument expands to "WORD", and that variable
# is defined at the beginning of this file. The surrounding 'W' and 'D'
# ensure that the parser in ParseEmptyArg has the correct position, both
# before and after the call to Var_ParsePP.
.if empty(W${:UOR}D)
. error
.endif
# There may be spaces at the outside of the parentheses.
# Spaces inside the parentheses are interpreted as part of the variable name.
.if ! empty ( WORD )
. error
.endif
${:U WORD }= variable name with spaces
# Now there is a variable named " WORD ", and it is not empty.
.if empty ( WORD )
. error
.endif
all:
@:;

View File

@ -1,41 +1,41 @@
# $NetBSD: cond-func-exists.mk,v 1.4 2020/08/28 12:59:36 rillig Exp $
# $NetBSD: cond-func-exists.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the exists() function in .if conditions.
.if !exists(.)
.error
. error
.endif
# The argument to the function must not be enclosed in quotes.
# Neither double quotes nor single quotes are allowed.
.if exists(".")
.error
. error
.endif
.if exists('.')
.error
. 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
. error
.endif
.if !exists(${:U.})
.error
. 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
. error
.endif
# Whitespace is trimmed on both sides of the function argument.
.if !exists( . )
.error
. error
.endif
all:

View File

@ -1 +1,3 @@
: via-cmdline
: via-dot-makeflags
exit status 0

View File

@ -1,8 +1,24 @@
# $NetBSD: cond-func-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: cond-func-make.mk,v 1.3 2020/09/25 20:11:06 rillig Exp $
#
# Tests for the make() function in .if conditions.
# Tests for the make() function in .if conditions, which tests whether
# the argument has been passed as a target via the command line or later
# via the .MAKEFLAGS special dependency target.
# TODO: Implementation
.if !make(via-cmdline)
. error
.endif
.if make(via-dot-makeflags)
. error
.endif
all:
@:;
.MAKEFLAGS: via-dot-makeflags
.if !make(via-cmdline)
. error
.endif
.if !make(via-dot-makeflags)
. error
.endif
via-cmdline via-dot-makeflags:
: $@

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-target.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
# $NetBSD: cond-func-target.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the target() function in .if conditions.
@ -6,14 +6,14 @@
# The target "target" does not exist yet.
.if target(target)
.error
. error
.endif
target:
# The target exists, even though it does not have any commands.
.if !target(target)
.error
. error
.endif
target:
@ -22,7 +22,7 @@ target:
# Adding a comment to an existing target does not change whether the target
# is defined or not.
.if !target(target)
.error
. error
.endif
target:
@ -31,7 +31,7 @@ target:
# Adding a command to an existing target does not change whether the target
# is defined or not.
.if !target(target)
.error
. error
.endif
all:

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func.mk,v 1.1 2020/08/20 17:45:47 rillig Exp $
# $NetBSD: cond-func.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
#
# Tests for those parts of the functions in .if conditions that are common
# among several functions.
@ -12,27 +12,27 @@ ${:UVAR(value)}= variable name with parentheses
${:UVAR{value}}= variable name with braces
.if !defined(DEF)
.error
. error
.endif
# Horizontal whitespace after the opening parenthesis is ignored.
# Horizontal whitespace (space tab) after the opening parenthesis is ignored.
.if !defined( DEF)
.error
. error
.endif
# Horizontal whitespace before the closing parenthesis is ignored.
# Horizontal whitespace (space tab) before the closing parenthesis is ignored.
.if !defined(DEF )
.error
. error
.endif
# The argument of a function must not directly contain whitespace.
.if !defined(A B)
.error
. error
.endif
# If necessary, the whitespace can be generated by a variable expression.
.if !defined(${:UA B})
.error
. error
.endif
# Characters that could be mistaken for operators must not appear directly
@ -42,21 +42,29 @@ ${:UVAR{value}}= variable name with braces
# It's not entirely clear why these characters are forbidden.
# The most plausible reason seems to be typo detection.
.if !defined(A&B)
.error
. error
.endif
.if !defined(A|B)
.error
. error
.endif
# Even parentheses may appear in variable names.
# They must be balanced though.
.if !defined(VAR(value))
.error
. error
.endif
# Braces do not have any special meaning when parsing arguments.
.if !defined(VAR{value})
.error
. error
.endif
# There may be spaces around the operators and parentheses, and even
# inside the parentheses. The spaces inside the parentheses are not
# allowed for the empty() function (see cond-func-empty.mk), therefore
# they are typically omitted for the other functions as well.
.if ! defined ( DEF )
. error
.endif
all:

View File

@ -1 +1,4 @@
exit status 0
make: "cond-op-and.mk" line 43: Malformed conditional (0 &&& 0)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,21 +1,21 @@
# $NetBSD: cond-op-and.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
# $NetBSD: cond-op-and.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the && operator in .if conditions.
.if 0 && 0
.error
. error
.endif
.if 1 && 0
.error
. error
.endif
.if 0 && 1
.error
. error
.endif
.if !(1 && 1)
.error
. error
.endif
# The right-hand side is not evaluated since the left-hand side is already
@ -23,5 +23,26 @@
.if 0 && ${UNDEF}
.endif
# The && operator may be abbreviated as &. This is not widely known though
# and is also not documented in the manual page.
.if 0 & 0
. error
.endif
.if 1 & 0
. error
.endif
.if 0 & 1
. error
.endif
.if !(1 & 1)
. error
.endif
# There is no operator &&&.
.if 0 &&& 0
. error
.endif
all:
@:;

View File

@ -1,21 +1,21 @@
# $NetBSD: cond-op-not.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
# $NetBSD: cond-op-not.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the ! operator in .if conditions.
# The exclamation mark negates its operand.
.if !1
.error
. error
.endif
# Exclamation marks can be chained.
# This doesn't happen in practice though.
.if !!!1
.error
. error
.endif
# The ! binds more tightly than the &&.
.if !!0 && 1
.error
. error
.endif
all:

View File

@ -1 +1,4 @@
exit status 0
make: "cond-op-or.mk" line 43: Malformed conditional (0 ||| 0)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,21 +1,21 @@
# $NetBSD: cond-op-or.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
# $NetBSD: cond-op-or.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
#
# Tests for the || operator in .if conditions.
.if 0 || 0
.error
. error
.endif
.if !(1 || 0)
.error
. error
.endif
.if !(0 || 1)
.error
. error
.endif
.if !(1 || 1)
.error
. error
.endif
# The right-hand side is not evaluated since the left-hand side is already
@ -23,5 +23,26 @@
.if 1 || ${UNDEF}
.endif
# The || operator may be abbreviated as |. This is not widely known though
# and is also not documented in the manual page.
.if 0 | 0
. error
.endif
.if !(1 | 0)
. error
.endif
.if !(0 | 1)
. error
.endif
.if !(1 | 1)
. error
.endif
# There is no operator |||.
.if 0 ||| 0
. error
.endif
all:
@:;

View File

@ -1,5 +1,7 @@
make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word)
make: "cond-op.mk" line 57: Parsing continues until here.
make: "cond-op.mk" line 70: Malformed conditional (0 ${ERR::=evaluated})
make: "cond-op.mk" line 74: warning: After detecting a parse error, the rest is evaluated.
make: "cond-op.mk" line 78: Parsing continues until here.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-op.mk,v 1.4 2020/08/28 14:07:51 rillig Exp $
# $NetBSD: cond-op.mk,v 1.8 2020/10/24 08:46:08 rillig Exp $
#
# Tests for operators like &&, ||, ! in .if conditions.
#
@ -14,24 +14,24 @@
# If || were to bind more tightly than &&, the result would be different
# as well.
.if !(1 || 1 && 0)
.error
. error
.endif
# If make were to interpret the && and || operators like the shell, the
# implicit binding would be this:
.if (1 || 1) && 0
.error
. 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
. error
.endif
# This is how the above condition is actually interpreted.
.if !("word" == "word")
.error
. error
.endif
# TODO: Demonstrate that the precedence of the ! and == operators actually
@ -41,16 +41,37 @@
# 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.
# See CondParser_String.
.if "!word" == !word
.error
. 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.
# See CondParser_String, which only has '!' in the list of stop characters.
.if "a&&b||c" != a&&b||c
.error
. error
.endif
# As soon as the parser sees the '$', it knows that the condition will
# be malformed. Therefore there is no point in evaluating it.
#
# As of 2020-09-11, that part of the condition is evaluated nevertheless,
# since CondParser_Expr just requests the next token, without restricting
# the token to the expected tokens. If the parser were to restrict the
# valid follow tokens for the token "0" to those that can actually produce
# a correct condition (which in this case would be comparison operators,
# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have
# to be evaluated.
#
# This would add a good deal of complexity to the code though, for almost
# no benefit, especially since most expressions and conditions are side
# effect free.
.if 0 ${ERR::=evaluated}
. error
.endif
.if ${ERR:Uundefined} == evaluated
. warning After detecting a parse error, the rest is evaluated.
.endif
# Just in case that parsing should ever stop on the first error.

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-short.mk,v 1.9 2020/08/19 22:47:09 rillig Exp $
# $NetBSD: cond-short.mk,v 1.11 2020/10/24 08:50:17 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@ -76,7 +76,7 @@ VAR= # empty again, for the following tests
.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}
. warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
.endif
# The || operator.
@ -113,59 +113,59 @@ VAR= # empty again, for the following tests
# make sure these do not cause complaint
#.MAKEFLAGS: -dc
V42 = 42
iV1 = ${V42}
iV2 = ${V66}
V42= 42
iV1= ${V42}
iV2= ${V66}
.if defined(V42) && ${V42} > 0
x=Ok
x= Ok
.else
x=Fail
x= Fail
.endif
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
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
x= Fail
.else
x=Ok
x= Ok
.endif
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
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
x= Ok
.else
x=Fail
x= Fail
.endif
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
.if 1 || ${iV2:U2} < ${V42}
x=Ok
x= Ok
.else
x=Fail
x= Fail
.endif
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
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
x= Ok
.else
x=Fail
x= Fail
.endif
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
# ${iV2:U2} expands to 2
.if 0 || ${iV2:U2} < ${V42}
x=Ok
x= Ok
.else
x=Fail
x= Fail
.endif
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
all:
@:;:

View File

@ -1 +1,8 @@
exit status 0
make: "cond-token-number.mk" line 13: Malformed conditional (-0)
make: "cond-token-number.mk" line 21: Malformed conditional (+0)
make: "cond-token-number.mk" line 29: Malformed conditional (!-1)
make: "cond-token-number.mk" line 37: Malformed conditional (!+1)
make: "cond-token-number.mk" line 54: End of the tests.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,8 +1,56 @@
# $NetBSD: cond-token-number.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: cond-token-number.mk,v 1.3 2020/09/14 06:22:59 rillig Exp $
#
# Tests for number tokens in .if conditions.
# TODO: Implementation
.if 0
. error
.endif
all:
@:;
# Even though -0 is a number and would be accepted by strtod, it is not
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
.if -0
. error
.endif
# Even though +0 is a number and would be accepted by strtod, it is not
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
.if +0
. error
.endif
# Even though -1 is a number and would be accepted by strtod, it is not
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
.if !-1
. error
.endif
# Even though +1 is a number and would be accepted by strtod, it is not
# accepted by the condition parser.
#
# See the ch_isdigit call in CondParser_String.
.if !+1
. error
.endif
# When the number comes from a variable expression though, it may be signed.
# XXX: This is inconsistent.
.if ${:U+0}
. error
.endif
# When the number comes from a variable expression though, it may be signed.
# XXX: This is inconsistent.
.if !${:U+1}
. error
.endif
# Ensure that parsing continues until here.
.info End of the tests.
all: # nothing

View File

@ -1 +1,29 @@
CondParser_Eval: ${:Uvalue} != value
lhs = "value", rhs = "value", op = !=
CondParser_Eval: ${:U} != "
lhs = "", rhs = "", op = !=
CondParser_Eval: ${:U#hash} != "#hash"
lhs = "#hash", rhs = "#hash", op = !=
CondParser_Eval: ${:U\\} != "\\
lhs = "\", rhs = "\", op = !=
CondParser_Eval: ${:U#hash} != #hash
lhs = "#hash", rhs = "#hash", op = !=
CondParser_Eval: 0 # This is treated as a comment, but why?
CondParser_Eval: ${0 # comment :?yes:no} != no
CondParser_Eval: 0 # comment
lhs = "no", rhs = "no", op = !=
CondParser_Eval: ${1 # comment :?yes:no} != yes
CondParser_Eval: 1 # comment
lhs = "yes", rhs = "yes", op = !=
CondParser_Eval: ${UNDEF:Uundefined}!=undefined
lhs = "undefined", rhs = "undefined", op = !=
CondParser_Eval: ${UNDEF:U12345}>12345
lhs = 12345.000000, rhs = 12345.000000, op = >1
CondParser_Eval: ${UNDEF:U12345}<12345
lhs = 12345.000000, rhs = 12345.000000, op = <1
CondParser_Eval: (${UNDEF:U0})||0
CondParser_Eval: ${:Uvar}&&name != "var&&name"
lhs = "var&&name", rhs = "var&&name", op = !=
CondParser_Eval: ${:Uvar}||name != "var||name"
lhs = "var||name", rhs = "var||name", op = !=
exit status 0

View File

@ -1,9 +1,94 @@
# $NetBSD: cond-token-plain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
# $NetBSD: cond-token-plain.mk,v 1.4 2020/09/12 17:47:24 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions.
# TODO: Implementation
.MAKEFLAGS: -dc
.if ${:Uvalue} != value
. error
.endif
# Malformed condition since comment parsing is done in an early phase
# and removes the '#' and everything behind it long before the condition
# parser gets to see it.
#
# XXX: The error message is missing for this malformed condition.
# The right-hand side of the comparison is just a '"'.
.if ${:U} != "#hash"
. error
.endif
# To get a '#' into a condition, it has to be escaped using a backslash.
# This prevents the comment parser from removing it, and in turn, it becomes
# visible to CondParser_String.
.if ${:U\#hash} != "\#hash"
. error
.endif
# Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles
# the '#' specially, even though at this point, there should be no need for
# comment handling anymore. The comments are supposed to be stripped off
# in a very early parsing phase.
#
# XXX: Missing error message for the malformed condition. The right-hand
# side is double-quotes, backslash, backslash.
# XXX: It is unexpected that the right-hand side evaluates to a single
# backslash.
.if ${:U\\} != "\\#hash"
. error
.endif
# The right-hand side of a comparison is not parsed as a token, therefore
# the code from CondParser_Token does not apply to it.
.if ${:U\#hash} != \#hash
. error
.endif
# XXX: What is the purpose of treating an escaped '#' in the following
# condition as a comment? And why only at the beginning of a token,
# just as in the shell?
.if 0 \# This is treated as a comment, but why?
. error
.endif
# Ah, ok, this can be used to add an end-of-condition comment. But does
# anybody really use this? This is neither documented nor obvious since
# the '#' is escaped. It's much clearer to write a comment in the line
# above the condition.
.if ${0 \# comment :?yes:no} != no
. error
.endif
.if ${1 \# comment :?yes:no} != yes
. error
.endif
# Usually there is whitespace around the comparison operator, but this is
# not required.
.if ${UNDEF:Uundefined}!=undefined
. error
.endif
.if ${UNDEF:U12345}>12345
. error
.endif
.if ${UNDEF:U12345}<12345
. error
.endif
.if (${UNDEF:U0})||0
. error
.endif
# Only the comparison operator terminates the comparison operand, and it's
# a coincidence that the '!' is both used in the '!=' comparison operator
# as well as for negating a comparison result.
#
# The boolean operators '&' and '|' don't terminate a comparison operand.
.if ${:Uvar}&&name != "var&&name"
. error
.endif
.if ${:Uvar}||name != "var||name"
. error
.endif
all:
@:;

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-token-var.mk,v 1.3 2020/08/20 19:43:42 rillig Exp $
# $NetBSD: cond-token-var.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
#
# Tests for variables in .if conditions.
@ -6,19 +6,19 @@ DEF= defined
# A defined variable may appear on either side of the comparison.
.if ${DEF} == ${DEF}
.info ok
. info ok
.else
.error
. error
.endif
# A variable that appears on the left-hand side must be defined.
.if ${UNDEF} == ${DEF}
.error
. error
.endif
# A variable that appears on the right-hand side must be defined.
.if ${DEF} == ${UNDEF}
.error
. error
.endif
# A defined variable may appear as an expression of its own.

7
unit-tests/cond-undef-lint.exp Executable file
View File

@ -0,0 +1,7 @@
make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
make: "cond-undef-lint.mk" line 45: Variable "VAR.defined" is undefined
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

69
unit-tests/cond-undef-lint.mk Executable file
View File

@ -0,0 +1,69 @@
# $NetBSD: cond-undef-lint.mk,v 1.2 2020/09/14 07:13:29 rillig Exp $
#
# Tests for defined and undefined variables in .if conditions, in lint mode.
#
# As of 2020-09-14, lint mode contains experimental code for printing
# accurate error messages in case of undefined variables, instead of the
# wrong "Malformed condition".
#
# See also:
# opt-debug-lint.mk
.MAKEFLAGS: -dL
# DEF is defined, UNDEF is not.
DEF= defined
# An expression based on a defined variable is fine.
.if !${DEF}
. error
.endif
# Since the condition fails to evaluate, neither of the branches is taken.
.if ${UNDEF}
. error
.else
. error
.endif
# The variable name depends on the undefined variable, which is probably a
# mistake. The variable UNDEF, as used here, can be easily turned into
# an expression that is always defined, using the :U modifier.
#
# The outer expression does not generate an error message since there was
# already an error evaluating this variable's name.
#
# TODO: Suppress the error message "Variable VAR. is undefined". That part
# of the expression must not be evaluated at all.
.if ${VAR.${UNDEF}}
. error
.else
. error
.endif
# The variable VAR.defined is not defined and thus generates an error message.
.if ${VAR.${DEF}}
. error
.else
. error
.endif
# Variables that are referenced indirectly may be undefined in a condition.
#
# A practical example for this is CFLAGS, which consists of CWARNS, COPTS
# and a few others. Just because these nested variables are not defined,
# this does not make the condition invalid.
#
# The crucial point is that at the point where the variable appears in the
# condition, there is no way to influence the definedness of the nested
# variables. In particular, there is no modifier that would turn undefined
# nested variables into empty strings, as an equivalent to the :U modifier.
INDIRECT= ${NESTED_UNDEF} ${NESTED_DEF}
NESTED_DEF= nested-defined
# Since NESTED_UNDEF is not controllable at this point, it must not generate
# an error message, and it doesn't do so, since 2020-09-14.
.if !${INDIRECT}
. error
.endif

View File

@ -16,7 +16,7 @@ Passed:
4 is not prime
5 is prime
make: warning: String comparison operator should be either == or !=
make: warning: String comparison operator must be either == or !=
make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
OK

View File

@ -1,4 +1,4 @@
# $Id: cond1.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
# $NetBSD: cond1.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
# hard code these!
TEST_UNAME_S= NetBSD

View File

@ -1,7 +0,0 @@
make: Bad conditional expression ` == "empty"' in == "empty"?oops:ok
make: "cond2.mk" line 13: Malformed conditional ({TEST_TYPO} == "Ok")
TEST_NOT_SET is empty or not defined
make: "cond2.mk" line 20: Malformed conditional (${TEST_NOT_SET} == "empty")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,29 +0,0 @@
# $Id: cond2.mk,v 1.1.1.2 2015/12/02 00:34:27 sjg Exp $
TEST_UNAME_S= NetBSD
# this should be ok
X:= ${${TEST_UNAME_S} == "NetBSD":?Ok:fail}
.if $X == "Ok"
Y= good
.endif
# expect: Bad conditional expression ` == "empty"' in == "empty"?oops:ok
X:= ${${TEST_NOT_SET} == "empty":?oops:ok}
# expect: Malformed conditional ({TEST_TYPO} == "Ok")
.if {TEST_TYPO} == "Ok"
Y= oops
.endif
.if empty(TEST_NOT_SET)
Y!= echo TEST_NOT_SET is empty or not defined >&2; echo
.endif
# expect: Malformed conditional (${TEST_NOT_SET} == "empty")
.if ${TEST_NOT_SET} == "empty"
Y= oops
.endif
.if defined(.NDEF) && ${.NDEF} > 0
Z= yes
.endif
all:
@echo $@

Some files were not shown because too many files have changed in this diff Show More