Merge bmake-20210110

Quite a lot of churn on style, but lots of
good work refactoring complicated functions
and lots more unit-tests.
Thanks mostly to rillig at NetBSD

Some interesting entries from ChangeLog

o .MAKE.{UID,GID} represent uid and gid running make.

o allow env var MAKE_OBJDIR_CHECK_WRITABLE=no to skip writable
  checks in InitObjdir.  Explicit .OBJDIR target always allows
  read-only directory.

o add more unit tests for META MODE

Merge commit '8e11a9b4250be3c3379c45fa820bff78d99d5946' into main

Change-Id: I464fd4c013067f0915671c1ccc96d2d8090b2b9c
This commit is contained in:
Simon J. Gerraty 2021-01-13 22:21:37 -08:00
commit 06b9b3e0ad
243 changed files with 24314 additions and 14482 deletions

View File

@ -1,8 +1,247 @@
2021-01-10 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210110
Merge with NetBSD make, pick up
o fix lint warnings
o consistently use boolean expressions in conditions
2021-01-08 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210108
Merge with NetBSD make, pick up
o job.c: back to polling token pipe if we want a token
o main.c: always print 'stopped in' on first call
The execption is if we bail because of an abort token
in which case just exit 6.
2021-01-01 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210101
Merge with NetBSD make, pick up
o Happy New Year!
o rename CmdOpts.lint to strict
o exit 2 on technical errors
o replace pointers in controlling conditions with booleans
o replace global preserveUndefined with VARE_KEEP_UNDEF
o compat.c: re-export variables from the actual make process
if using vfork this is the effect anyway
o cond.c: clean up VarParseResult constants
o for.c: fix undefined behavior in SubstVarLong
make control flow in SubstVarLong of .for loops more obvious
clean up SubstVarShort in .for loops
extract ForSubstBody from ForReadMore
clean up ForReadMore
simplify termination condition for .for loop
add error handling for .for loop items
job.c: re-export variables from the actual make process
parse.c: remove mmap for loading files, only allow files < 1 GiB
fix edge case in := with undefined in variable name
skip variable expansion in ParseDependencyTargetWord
var.c: split ExportVar into separate functions
clean up code in extracted ExportVar functions
remove dead code from ApplyModifiersIndirect
split Var_Subst into easily understandable functions
clean up VarParseResult constants
2020-12-25 Simon J Gerraty <sjg@beast.crufty.net>
* main.c: use .MAKE.DEPENDFILE as set by makefiles
2020-12-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201222
Merge with NetBSD make, pick up
o make DEBUG macro return boolean
o parse.c: fix assertion failure for files without trailing newline
o var.c: allow .undef to undefine multiple variables at once
remove excess newline from parse errors
2020-12-21 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201221
Merge with NetBSD make, pick up
o some unit-test updates
2020-12-20 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201220
Merge with NetBSD make, pick up
o more unit tests
o return FStr from Var_Parse and Var_Value
o spell nonexistent consistently
o add str_basename to reduce duplicate code
o compat.c: fix .ERROR_TARGET in compat -k mode
extract InitSignals from Compat_Run
extract UseShell from Compat_RunCommand
o cond.c: error out if an '.endif' or '.else' contain extraneous text
o for.c: rename ForIterate to ForReadMore
o hash.c: clean up hash function for HashTable
o lst.c: rename Vector.priv_cap to cap
o main.c: remove constant parameter from MakeMode
o make.c: use symbolic time for 0 in Make_Recheck
extract MakeChildren from MakeStartJobs
o parse.c: clean up memory handling in VarAssign_EvalShell, Parse_DoVar
fix error message for .info/.warning/.error without argument
extract Var_Undef from ParseDirective
extract ParseSkippedBranches, ParseForLoop from ParseReadLine
rename mode constants for ParseGetLine to be more expressive
reduce debugging details in Parse_SetInput
fix line numbers in .for loops
split ParseGetLine into separate functions
fix garbled output for failed shell command
var.c: remove redundant assignment in ApplyModifier_SysV
error out on unknown variable modifiers at parse time
remove wrong error message for indirect modifier in lint mode
extract ApplySingleModifier from ApplyModifiers
use FStr for memory management in Var_SetWithFlags
extract SetVar from Var_SetWithFlags
use FStr in VarNew
extract string functions from ApplyModifier_To
error out if .undef has not exactly 1 argument
extract Var_DeleteVar from Var_Delete
extract Var_Undef from ParseDirective
clean up memory management for expanding variable expressions
2020-12-12 Simon J Gerraty <sjg@beast.crufty.net>
* avoid %zu
* lst.c: avoid anonymous union
* VERSION (_MAKE_VERSION): 20201212
Merge with NetBSD make, pick up
o more unit tests
o inline Targ_Ignore and Targ_Silent
o split JobFlags into separate fields
o remove const from function parameters (left overs from refactoring)
o eliminate boolean argument of Var_Export
o make API of Buf_Init simpler
o rename ParseRunOptions to ParseCommandFlags
o replace *line with line[0]
o compat.c: fix wrong exit status for multiple failed main targets
refactor Compat_Run to show the error condition more clearly
don't make .END if the main targets already failed (-k mode)
fix exit status in -k mode if a dependency fails
o for.c: clean up Buf_AddEscaped in .for loops
o job.c: extract ShellWriter_ErrOn from JobPrintCommand
make Job_Touch simpler
refactor JobFinish
rename Shell.exitFlag to errFlag
move Job.xtraced to ShellWriter
make printing of shell commands independent from the job
rename shell flags in struct Shell
extract JobOpenTmpFile from JobStart
rename RunFlags to CommandFlags
split various Job.* into separate fields
rename commandShell to shell
extract InitShellNameAndPath from Shell_Init
replace signal handling macros with local functions
replace macro MESSAGE with local function
parse.c: error out on null bytes in makefiles
error out on misspelled directives
rename IFile.nextbuf to readMore
fix undefined behavior in ParseEOF
str.c: remove redundant call to strlen in Str_Words
var.c: error out on misspelled .unexport-env
error out on misspelled .export directives
extract ExportVars from Var_Export
extract ExportVarsExpand from Var_Export
eliminate boolean argument of Var_Export
fix undefined behavior when exporting ${:U }
rename Var_ExportVars to Var_ReexportVars
rename Var_Export1 to ExportVar
2020-12-06 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201206
Merge with NetBSD make, pick up
o more unit tests
o inline macros for debug logging
o use consistent variable names for list nodes
o define constants for enum zero-values
o dir.c: use fixed format for debug output of the directory cache
remove Dir_InitDir
o lst.c: inline Lst_Enqueue, Vector_Done
o meta.c: remove unused parameter from meta_needed
o parse.c: rename parse functions
o suff.c: extract ExpandChildrenRegular from ExpandChildren
o targ.c: don't concatenate identifiers in Targ_PrintType
o var.c: remove comment decoration
extract UnexportVars from Var_UnExport
extract GetVarnamesToUnexport from Var_UnExport
extract UnexportEnv from Var_UnExport
extract UnexportVar from Var_UnExport
move CleanEnv to UnexportVars
replace pointer comparisons with enum
add FStr to var.c to make memory handling simpler
use FStr in Var_UnExport
move type definitions in var.c to the top
extract FreeEnvVar from Var_Parse
extract ShuffleStrings from ApplyModifier_Order
2020-11-30 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201130
Merge with NetBSD make, pick up
o add unit tests for META MODE
o reduce memory allocation for dirSearchPath, GNode.parents,
GNode.children, OpenDirs
o reduce pointer indirection for GNode.cohorts and
GNode.implicitParents
o remove pointer indirection from GNode.commands
o inline Lst_ForEachUntil in meta mode
o dir.c: fix memory leak for lstat cache in -DCLEANUP mode
clean up memory management for CachedDirs
fix the reference count of dotLast going negative
add debug logging for OpenDirs_Done
extract CacheNewDir from Dir_AddDir
add debug logging for reference counting of CachedDir
rename some Dir functions to SearchPath
o job.c: rename some global variables
o main.c: reduce memory allocation in ReadBuiltinRules
reduce memory allocation in CmdOpts.create, CmdOpts.variables,
CmdOpts.makefiles
Add .MAKE.UID and .MAKE.GID
o make.c: reduce memory allocation for/in toBeMade,
Make_ProcessWait, Make_ExpandUse
o meta.c: reduce memory allocation in meta_oodate
o parse.c: reduce memory allocations for parsing dependencies and
targets
o suff.c: reduce memory allocation in suffix handling
2020-11-24 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201124
Merge with NetBSD make, pick up
o .MAKE.{UID,GID} represent uid and gid running make.
o fix error handling for .BEGIN and .END dependency in -k mode
o fix missing "Stop." after failed .END node in -k mode
o use properly typed comparisons in boolean contexts
o replace a few HashTable_CreateEntry with HashTable_Set
o add HashSet type
o compat.c: split Compat_Make into smaller functions
extract DebugFailedTarget from Compat_RunCommand
o dir.c: refactor Dir_UpdateMTime
migrate CachedDir.files from HashTable to HashSet
o make.c: add high-level API for GNode.made
2020-11-22 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201122
Merge with NetBSD make, pick up
o rename GNode.context to vars
o suff.c: cleanup and refactor
rename some functions and vars to better reflect usage
add high-level API for CandidateSearcher
o targ.c: add more debug logging for suffix handling
o more unit tests
o add debug logging for setting and resetting the main target
2020-11-17 Simon J Gerraty <sjg@beast.crufty.net> 2020-11-17 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20201117 * VERSION (_MAKE_VERSION): 20201117
Merge with NetBSD make, pick up Merge with NetBSD make, pick up
o fix some unit-tests when dash is .SHELL o fix some unit-tests when .SHELL is dash
o rename Targ_NewGN to GNode_New o rename Targ_NewGN to GNode_New
o make some GNode functions const o make some GNode functions const
o main.c: call Targ_Init before Var_Init o main.c: call Targ_Init before Var_Init

View File

@ -75,6 +75,8 @@ unit-tests/archive-suffix.exp
unit-tests/archive-suffix.mk unit-tests/archive-suffix.mk
unit-tests/archive.exp unit-tests/archive.exp
unit-tests/archive.mk unit-tests/archive.mk
unit-tests/cmd-errors-jobs.exp
unit-tests/cmd-errors-jobs.mk
unit-tests/cmd-errors-lint.exp unit-tests/cmd-errors-lint.exp
unit-tests/cmd-errors-lint.mk unit-tests/cmd-errors-lint.mk
unit-tests/cmd-errors.exp unit-tests/cmd-errors.exp
@ -87,6 +89,8 @@ unit-tests/cmdline.exp
unit-tests/cmdline.mk unit-tests/cmdline.mk
unit-tests/comment.exp unit-tests/comment.exp
unit-tests/comment.mk unit-tests/comment.mk
unit-tests/compat-error.exp
unit-tests/compat-error.mk
unit-tests/cond-cmp-numeric-eq.exp unit-tests/cond-cmp-numeric-eq.exp
unit-tests/cond-cmp-numeric-eq.mk unit-tests/cond-cmp-numeric-eq.mk
unit-tests/cond-cmp-numeric-ge.exp unit-tests/cond-cmp-numeric-ge.exp
@ -105,6 +109,8 @@ unit-tests/cond-cmp-string.exp
unit-tests/cond-cmp-string.mk unit-tests/cond-cmp-string.mk
unit-tests/cond-cmp-unary.exp unit-tests/cond-cmp-unary.exp
unit-tests/cond-cmp-unary.mk unit-tests/cond-cmp-unary.mk
unit-tests/cond-eof.exp
unit-tests/cond-eof.mk
unit-tests/cond-func-commands.exp unit-tests/cond-func-commands.exp
unit-tests/cond-func-commands.mk unit-tests/cond-func-commands.mk
unit-tests/cond-func-defined.exp unit-tests/cond-func-defined.exp
@ -113,6 +119,8 @@ unit-tests/cond-func-empty.exp
unit-tests/cond-func-empty.mk unit-tests/cond-func-empty.mk
unit-tests/cond-func-exists.exp unit-tests/cond-func-exists.exp
unit-tests/cond-func-exists.mk unit-tests/cond-func-exists.mk
unit-tests/cond-func-make-main.exp
unit-tests/cond-func-make-main.mk
unit-tests/cond-func-make.exp unit-tests/cond-func-make.exp
unit-tests/cond-func-make.mk unit-tests/cond-func-make.mk
unit-tests/cond-func-target.exp unit-tests/cond-func-target.exp
@ -213,12 +221,22 @@ unit-tests/depsrc-wait.exp
unit-tests/depsrc-wait.mk unit-tests/depsrc-wait.mk
unit-tests/depsrc.exp unit-tests/depsrc.exp
unit-tests/depsrc.mk unit-tests/depsrc.mk
unit-tests/deptgt-begin-fail-indirect.exp
unit-tests/deptgt-begin-fail-indirect.mk
unit-tests/deptgt-begin-fail.exp
unit-tests/deptgt-begin-fail.mk
unit-tests/deptgt-begin.exp unit-tests/deptgt-begin.exp
unit-tests/deptgt-begin.mk unit-tests/deptgt-begin.mk
unit-tests/deptgt-default.exp unit-tests/deptgt-default.exp
unit-tests/deptgt-default.mk unit-tests/deptgt-default.mk
unit-tests/deptgt-delete_on_error.exp unit-tests/deptgt-delete_on_error.exp
unit-tests/deptgt-delete_on_error.mk unit-tests/deptgt-delete_on_error.mk
unit-tests/deptgt-end-fail-all.exp
unit-tests/deptgt-end-fail-all.mk
unit-tests/deptgt-end-fail-indirect.exp
unit-tests/deptgt-end-fail-indirect.mk
unit-tests/deptgt-end-fail.exp
unit-tests/deptgt-end-fail.mk
unit-tests/deptgt-end-jobs.exp unit-tests/deptgt-end-jobs.exp
unit-tests/deptgt-end-jobs.mk unit-tests/deptgt-end-jobs.mk
unit-tests/deptgt-end.exp unit-tests/deptgt-end.exp
@ -279,6 +297,8 @@ unit-tests/directive-elifnmake.exp
unit-tests/directive-elifnmake.mk unit-tests/directive-elifnmake.mk
unit-tests/directive-else.exp unit-tests/directive-else.exp
unit-tests/directive-else.mk unit-tests/directive-else.mk
unit-tests/directive-endfor.exp
unit-tests/directive-endfor.mk
unit-tests/directive-endif.exp unit-tests/directive-endif.exp
unit-tests/directive-endif.mk unit-tests/directive-endif.mk
unit-tests/directive-error.exp unit-tests/directive-error.exp
@ -287,12 +307,22 @@ unit-tests/directive-export-env.exp
unit-tests/directive-export-env.mk unit-tests/directive-export-env.mk
unit-tests/directive-export-gmake.exp unit-tests/directive-export-gmake.exp
unit-tests/directive-export-gmake.mk unit-tests/directive-export-gmake.mk
unit-tests/directive-export-impl.exp
unit-tests/directive-export-impl.mk
unit-tests/directive-export-literal.exp unit-tests/directive-export-literal.exp
unit-tests/directive-export-literal.mk unit-tests/directive-export-literal.mk
unit-tests/directive-export.exp unit-tests/directive-export.exp
unit-tests/directive-export.mk unit-tests/directive-export.mk
unit-tests/directive-for-errors.exp
unit-tests/directive-for-errors.mk
unit-tests/directive-for-escape.exp
unit-tests/directive-for-escape.mk
unit-tests/directive-for-generating-endif.exp unit-tests/directive-for-generating-endif.exp
unit-tests/directive-for-generating-endif.mk unit-tests/directive-for-generating-endif.mk
unit-tests/directive-for-lines.exp
unit-tests/directive-for-lines.mk
unit-tests/directive-for-null.exp
unit-tests/directive-for-null.mk
unit-tests/directive-for.exp unit-tests/directive-for.exp
unit-tests/directive-for.mk unit-tests/directive-for.mk
unit-tests/directive-hyphen-include.exp unit-tests/directive-hyphen-include.exp
@ -315,6 +345,8 @@ unit-tests/directive-include.exp
unit-tests/directive-include.mk unit-tests/directive-include.mk
unit-tests/directive-info.exp unit-tests/directive-info.exp
unit-tests/directive-info.mk unit-tests/directive-info.mk
unit-tests/directive-misspellings.exp
unit-tests/directive-misspellings.mk
unit-tests/directive-sinclude.exp unit-tests/directive-sinclude.exp
unit-tests/directive-sinclude.mk unit-tests/directive-sinclude.mk
unit-tests/directive-undef.exp unit-tests/directive-undef.exp
@ -365,10 +397,18 @@ unit-tests/job-flags.exp
unit-tests/job-flags.mk unit-tests/job-flags.mk
unit-tests/job-output-long-lines.exp unit-tests/job-output-long-lines.exp
unit-tests/job-output-long-lines.mk unit-tests/job-output-long-lines.mk
unit-tests/jobs-error-indirect.exp
unit-tests/jobs-error-indirect.mk
unit-tests/jobs-error-nested-make.exp
unit-tests/jobs-error-nested-make.mk
unit-tests/jobs-error-nested.exp
unit-tests/jobs-error-nested.mk
unit-tests/lint.exp unit-tests/lint.exp
unit-tests/lint.mk unit-tests/lint.mk
unit-tests/make-exported.exp unit-tests/make-exported.exp
unit-tests/make-exported.mk unit-tests/make-exported.mk
unit-tests/meta-cmd-cmp.exp
unit-tests/meta-cmd-cmp.mk
unit-tests/moderrs.exp unit-tests/moderrs.exp
unit-tests/moderrs.mk unit-tests/moderrs.mk
unit-tests/modmatch.exp unit-tests/modmatch.exp
@ -447,14 +487,20 @@ unit-tests/opt-include-dir.exp
unit-tests/opt-include-dir.mk unit-tests/opt-include-dir.mk
unit-tests/opt-jobs-internal.exp unit-tests/opt-jobs-internal.exp
unit-tests/opt-jobs-internal.mk unit-tests/opt-jobs-internal.mk
unit-tests/opt-jobs-no-action.exp
unit-tests/opt-jobs-no-action.mk
unit-tests/opt-jobs.exp unit-tests/opt-jobs.exp
unit-tests/opt-jobs.mk unit-tests/opt-jobs.mk
unit-tests/opt-keep-going-multiple.exp
unit-tests/opt-keep-going-multiple.mk
unit-tests/opt-keep-going.exp unit-tests/opt-keep-going.exp
unit-tests/opt-keep-going.mk unit-tests/opt-keep-going.mk
unit-tests/opt-m-include-dir.exp unit-tests/opt-m-include-dir.exp
unit-tests/opt-m-include-dir.mk unit-tests/opt-m-include-dir.mk
unit-tests/opt-no-action-at-all.exp unit-tests/opt-no-action-at-all.exp
unit-tests/opt-no-action-at-all.mk unit-tests/opt-no-action-at-all.mk
unit-tests/opt-no-action-runflags.exp
unit-tests/opt-no-action-runflags.mk
unit-tests/opt-no-action.exp unit-tests/opt-no-action.exp
unit-tests/opt-no-action.mk unit-tests/opt-no-action.mk
unit-tests/opt-query.exp unit-tests/opt-query.exp
@ -491,12 +537,14 @@ unit-tests/posix.exp
unit-tests/posix.mk unit-tests/posix.mk
unit-tests/posix1.exp unit-tests/posix1.exp
unit-tests/posix1.mk unit-tests/posix1.mk
unit-tests/qequals.exp
unit-tests/qequals.mk
unit-tests/recursive.exp unit-tests/recursive.exp
unit-tests/recursive.mk unit-tests/recursive.mk
unit-tests/sh-dots.exp unit-tests/sh-dots.exp
unit-tests/sh-dots.mk unit-tests/sh-dots.mk
unit-tests/sh-errctl.exp
unit-tests/sh-errctl.mk
unit-tests/sh-flags.exp
unit-tests/sh-flags.mk
unit-tests/sh-jobs-error.exp unit-tests/sh-jobs-error.exp
unit-tests/sh-jobs-error.mk unit-tests/sh-jobs-error.mk
unit-tests/sh-jobs.exp unit-tests/sh-jobs.exp
@ -529,14 +577,22 @@ unit-tests/suff-clear-regular.exp
unit-tests/suff-clear-regular.mk unit-tests/suff-clear-regular.mk
unit-tests/suff-clear-single.exp unit-tests/suff-clear-single.exp
unit-tests/suff-clear-single.mk unit-tests/suff-clear-single.mk
unit-tests/suff-incomplete.exp
unit-tests/suff-incomplete.mk
unit-tests/suff-lookup.exp unit-tests/suff-lookup.exp
unit-tests/suff-lookup.mk unit-tests/suff-lookup.mk
unit-tests/suff-main-several.exp
unit-tests/suff-main-several.mk
unit-tests/suff-main.exp unit-tests/suff-main.exp
unit-tests/suff-main.mk unit-tests/suff-main.mk
unit-tests/suff-phony.exp
unit-tests/suff-phony.mk
unit-tests/suff-rebuild.exp unit-tests/suff-rebuild.exp
unit-tests/suff-rebuild.mk unit-tests/suff-rebuild.mk
unit-tests/suff-self.exp unit-tests/suff-self.exp
unit-tests/suff-self.mk unit-tests/suff-self.mk
unit-tests/suff-transform-debug.exp
unit-tests/suff-transform-debug.mk
unit-tests/suff-transform-endless.exp unit-tests/suff-transform-endless.exp
unit-tests/suff-transform-endless.mk unit-tests/suff-transform-endless.mk
unit-tests/suff-transform-expand.exp unit-tests/suff-transform-expand.exp
@ -607,6 +663,8 @@ unit-tests/varmod-head.exp
unit-tests/varmod-head.mk unit-tests/varmod-head.mk
unit-tests/varmod-ifelse.exp unit-tests/varmod-ifelse.exp
unit-tests/varmod-ifelse.mk unit-tests/varmod-ifelse.mk
unit-tests/varmod-indirect.exp
unit-tests/varmod-indirect.mk
unit-tests/varmod-l-name-to-value.exp unit-tests/varmod-l-name-to-value.exp
unit-tests/varmod-l-name-to-value.mk unit-tests/varmod-l-name-to-value.mk
unit-tests/varmod-localtime.exp unit-tests/varmod-localtime.exp
@ -721,6 +779,8 @@ unit-tests/varname-dot-make-ppid.exp
unit-tests/varname-dot-make-ppid.mk unit-tests/varname-dot-make-ppid.mk
unit-tests/varname-dot-make-save_dollars.exp unit-tests/varname-dot-make-save_dollars.exp
unit-tests/varname-dot-make-save_dollars.mk unit-tests/varname-dot-make-save_dollars.mk
unit-tests/varname-dot-makeflags.exp
unit-tests/varname-dot-makeflags.mk
unit-tests/varname-dot-makeoverrides.exp unit-tests/varname-dot-makeoverrides.exp
unit-tests/varname-dot-makeoverrides.mk unit-tests/varname-dot-makeoverrides.mk
unit-tests/varname-dot-newline.exp unit-tests/varname-dot-newline.exp

View File

@ -2,11 +2,11 @@ The individual files in this distribution are copyright their
original contributors or assignees. original contributors or assignees.
Including: Including:
Copyright (c) 1993-2020, Simon J Gerraty Copyright (c) 1993-2021, Simon J Gerraty
Copyright (c) 2020, Roland Illig <rillig@NetBSD.org> Copyright (c) 2020, Roland Illig <rillig@NetBSD.org>
Copyright (c) 2009-2016, Juniper Networks, Inc. Copyright (c) 2009-2016, Juniper Networks, Inc.
Copyright (c) 2009, John Birrell. Copyright (c) 2009, John Birrell.
Copyright (c) 1997-2020 The NetBSD Foundation, Inc. Copyright (c) 1997-2021 The NetBSD Foundation, Inc.
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
Copyright (c) 1989 by Berkeley Softworks Copyright (c) 1989 by Berkeley Softworks
Copyright (c) 1988, 1989, 1990, 1992, 1993 Copyright (c) 1988, 1989, 1990, 1992, 1993

View File

@ -1,4 +1,4 @@
.\" $NetBSD: tutorial.ms,v 1.13 2017/03/01 13:05:11 kre Exp $ .\" $NetBSD: tutorial.ms,v 1.14 2020/12/18 15:47:34 rillig Exp $
.\" Copyright (c) 1988, 1989, 1993 .\" Copyright (c) 1988, 1989, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
.\" .\"
@ -1918,15 +1918,15 @@ Suff_FindDeps (jive.c)
applying .l -> .c to "jive.l" applying .l -> .c to "jive.l"
Suff_FindDeps (jive.l) Suff_FindDeps (jive.l)
Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
Examining jive.c...non-existent...out-of-date Examining jive.c...nonexistent...out-of-date
--- jive.c --- --- jive.c ---
lex jive.l lex jive.l
\&.\|.\|. meaningless lex output deleted .\|.\|. \&.\|.\|. meaningless lex output deleted .\|.\|.
mv lex.yy.c jive.c mv lex.yy.c jive.c
Examining jive.o...non-existent...out-of-date Examining jive.o...nonexistent...out-of-date
--- jive.o --- --- jive.o ---
cc -c jive.c cc -c jive.c
Examining jive.out...non-existent...out-of-date Examining jive.out...nonexistent...out-of-date
--- jive.out --- --- jive.out ---
cc -o jive.out jive.o cc -o jive.out jive.o
.DE .DE
@ -2871,7 +2871,7 @@ current directory. While people have suggested that PMake should read
the directories each time, my experience suggests that the caching seldom the directories each time, my experience suggests that the caching seldom
causes problems. In addition, not caching the directories slows things causes problems. In addition, not caching the directories slows things
down enormously because of PMake's attempts to apply transformation down enormously because of PMake's attempts to apply transformation
rules through non-existent files \*- the number of extra file-system rules through nonexistent files \*- the number of extra file-system
searches is truly staggering, especially if many files without searches is truly staggering, especially if many files without
suffixes are used and the null suffix isn't changed from suffixes are used and the null suffix isn't changed from
.CW .out . .CW .out .

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig Exp $ .\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\" .\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\" .\"
.Dd November 14, 2020 .Dd December 22, 2020
.Dt BMAKE 1 .Dt BMAKE 1
.Os .Os
.Sh NAME .Sh NAME
@ -1019,6 +1019,12 @@ If set to false,
becomes becomes
.Ql $ .Ql $
per normal evaluation rules. per normal evaluation rules.
.It Va .MAKE.UID
The user-id running
.Nm .
.It Va .MAKE.GID
The group-id running
.Nm .
.It Va MAKE_PRINT_VAR_ON_ERROR .It Va MAKE_PRINT_VAR_ON_ERROR
When When
.Nm .Nm
@ -1097,7 +1103,7 @@ to that directory before executing any targets.
.Pp .Pp
Except in the case of an explicit Except in the case of an explicit
.Ql Ic .OBJDIR .Ql Ic .OBJDIR
target, target,
.Nm .Nm
will check that the specified directory is writable and ignore it if not. will check that the specified directory is writable and ignore it if not.
This check can be skipped by setting the environment variable This check can be skipped by setting the environment variable
@ -1743,9 +1749,9 @@ The same as
except that variables in the value are not expanded. except that variables in the value are not expanded.
.It Ic .info Ar message .It Ic .info Ar message
The message is printed along with the name of the makefile and line number. The message is printed along with the name of the makefile and line number.
.It Ic .undef Ar variable .It Ic .undef Ar variable ...
Un-define the specified global variable. Un-define the specified global variables.
Only global variables may be un-defined. Only global variables can be un-defined.
.It Ic .unexport Ar variable ... .It Ic .unexport Ar variable ...
The opposite of The opposite of
.Ql .export . .Ql .export .

View File

@ -45,10 +45,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
`-' they are added to the MAKEFLAGS environment variable and will `-' they are added to the MAKEFLAGS environment variable and will
be processed by any child make processes. By default, debugging be processed by any child make processes. By default, debugging
information is printed to standard error, but this can be changed information is printed to standard error, but this can be changed
using the F debugging flag. The debugging output is always using the F debugging flag. The debugging output is always un-
unbuffered; in addition, if debugging is enabled but debugging buffered; in addition, if debugging is enabled but debugging out-
output is not directed to standard output, then the standard out- put is not directed to standard output, then the standard output
put is line buffered. Flags is one or more of the following: is line buffered. Flags is one or more of the following:
A Print all possible debugging information; equivalent to A Print all possible debugging information; equivalent to
specifying all of the debugging flags. specifying all of the debugging flags.
@ -178,8 +178,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
can be used multiple times to form a search path. This path will can be used multiple times to form a search path. This path will
override the default system include path: /usr/share/mk. Fur- override the default system include path: /usr/share/mk. Fur-
thermore the system include path will be appended to the search thermore the system include path will be appended to the search
path used for "file"-style include statements (see the -I path used for "file"-style include statements (see the -I op-
option). tion).
If a file or directory name in the -m argument (or the If a file or directory name in the -m argument (or the
MAKESYSPATH environment variable) starts with the string ".../" MAKESYSPATH environment variable) starts with the string ".../"
@ -232,9 +232,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.MAKE.EXPAND_VARIABLES is set to true and the -dV option has not .MAKE.EXPAND_VARIABLES is set to true and the -dV option has not
been used to override it. Note that loop-local and target-local been used to override it. Note that loop-local and target-local
variables, as well as values taken temporarily by global vari- variables, as well as values taken temporarily by global vari-
ables during makefile processing, are not accessible via this ables during makefile processing, are not accessible via this op-
option. The -dv debug mode can be used to see these at the cost tion. The -dv debug mode can be used to see these at the cost of
of generating substantial extraneous output. generating substantial extraneous output.
-v variable -v variable
Like -V but the variable is always expanded to its complete Like -V but the variable is always expanded to its complete
@ -247,8 +247,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
-X Don't export variables passed on the command line to the environ- -X Don't export variables passed on the command line to the environ-
ment individually. Variables passed on the command line are ment individually. Variables passed on the command line are
still exported via the MAKEFLAGS environment variable. This still exported via the MAKEFLAGS environment variable. This op-
option may be useful on systems which have a small limit on the tion may be useful on systems which have a small limit on the
size of command arguments. size of command arguments.
variable=value variable=value
@ -268,9 +268,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
FILE DEPENDENCY SPECIFICATIONS FILE DEPENDENCY SPECIFICATIONS
Dependency lines consist of one or more targets, an operator, and zero or Dependency lines consist of one or more targets, an operator, and zero or
more sources. This creates a relationship where the targets ``depend'' more sources. This creates a relationship where the targets "depend" on
on the sources and are customarily created from them. A target is con- the sources and are customarily created from them. A target is consid-
sidered out-of-date if it does not exist, or if its modification time is ered out-of-date if it does not exist, or if its modification time is
less than that of any of its sources. An out-of-date target will be re- less than that of any of its sources. An out-of-date target will be re-
created, but not until all sources have been examined and themselves re- created, but not until all sources have been examined and themselves re-
created as needed. Three operators may be used: created as needed. Three operators may be used:
@ -285,20 +285,20 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
out of date. out of date.
:: Any dependency line may have attached shell commands, but each one :: Any dependency line may have attached shell commands, but each one
is handled independently: its sources are considered and the is handled independently: its sources are considered and the at-
attached shell commands are run if the target is out of date with tached shell commands are run if the target is out of date with re-
respect to (only) those sources. Thus, different groups of the spect to (only) those sources. Thus, different groups of the at-
attached shell commands may be run depending on the circumstances. tached shell commands may be run depending on the circumstances.
Furthermore, unlike :, for dependency lines with no sources, the Furthermore, unlike :, for dependency lines with no sources, the
attached shell commands are always run. Also unlike :, the target attached shell commands are always run. Also unlike :, the target
will not be removed if bmake is interrupted. will not be removed if bmake is interrupted.
All dependency lines mentioning a particular target must use the same All dependency lines mentioning a particular target must use the same op-
operator. erator.
Targets and sources may contain the shell wildcard values `?', `*', `[]', Targets and sources may contain the shell wildcard values `?', `*', `[]',
and `{}'. The values `?', `*', and `[]' may only be used as part of the and `{}'. The values `?', `*', and `[]' may only be used as part of the
final component of the target or source, and must be used to describe final component of the target or source, and must be used to describe ex-
existing files. The value `{}' need not necessarily be used to describe isting files. The value `{}' need not necessarily be used to describe
existing files. Expansion is in directory order, not alphabetically as existing files. Expansion is in directory order, not alphabetically as
done in the shell. done in the shell.
@ -306,8 +306,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Each target may have associated with it one or more lines of shell com- Each target may have associated with it one or more lines of shell com-
mands, normally used to create the target. Each of the lines in this mands, normally used to create the target. Each of the lines in this
script must be preceded by a tab. (For historical reasons, spaces are script must be preceded by a tab. (For historical reasons, spaces are
not accepted.) While targets can appear in many dependency lines if not accepted.) While targets can appear in many dependency lines if de-
desired, by default only one of these rules may be followed by a creation sired, by default only one of these rules may be followed by a creation
script. If the `::' operator is used, however, all rules may include script. If the `::' operator is used, however, all rules may include
scripts and the scripts are executed in the order found. scripts and the scripts are executed in the order found.
@ -333,10 +333,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
failed. failed.
Makefiles should be written so that the mode of bmake operation does not Makefiles should be written so that the mode of bmake operation does not
change their behavior. For example, any command which needs to use change their behavior. For example, any command which needs to use "cd"
``cd'' or ``chdir'' without potentially changing the directory for subse- or "chdir" without potentially changing the directory for subsequent com-
quent commands should be put in parentheses so it executes in a subshell. mands should be put in parentheses so it executes in a subshell. To
To force the use of one shell, escape the line breaks so as to make the force the use of one shell, escape the line breaks so as to make the
whole script one command. For example: whole script one command. For example:
avoid-chdir-side-effects: avoid-chdir-side-effects:
@ -373,13 +373,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
not expanded. This can cause problems when variable modifiers not expanded. This can cause problems when variable modifiers
are used. are used.
!= Expand the value and pass it to the shell for execution and != Expand the value and pass it to the shell for execution and as-
assign the result to the variable. Any newlines in the result sign the result to the variable. Any newlines in the result are
are replaced with spaces. replaced with spaces.
Any white-space before the assigned value is removed; if the value is Any white-space before the assigned value is removed; if the value is be-
being appended, a single space is inserted between the previous contents ing appended, a single space is inserted between the previous contents of
of the variable and the appended value. the variable and the appended value.
Variables are expanded by surrounding the variable name with either curly Variables are expanded by surrounding the variable name with either curly
braces (`{}') or parentheses (`()') and preceding it with a dollar sign braces (`{}') or parentheses (`()') and preceding it with a dollar sign
@ -403,7 +403,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
2. Variables in shell commands are expanded when the shell command is 2. Variables in shell commands are expanded when the shell command is
executed. executed.
3. ``.for'' loop index variables are expanded on each loop iteration. 3. ".for" loop index variables are expanded on each loop iteration.
Note that other variables are not expanded inside loops so the fol- Note that other variables are not expanded inside loops so the fol-
lowing example code: lowing example code:
@ -423,9 +423,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
1 2 3 1 2 3
3 3 3 3 3 3
Because while ${a} contains ``1 2 3'' after the loop is executed, Because while ${a} contains "1 2 3" after the loop is executed, ${b}
${b} contains ``${j} ${j} ${j}'' which expands to ``3 3 3'' since contains "${j} ${j} ${j}" which expands to "3 3 3" since after the
after the loop completes ${j} contains ``3''. loop completes ${j} contains "3".
Variable classes Variable classes
The four different classes of variables (in order of increasing prece- The four different classes of variables (in order of increasing prece-
@ -454,8 +454,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.IMPSRC In suffix-transformation rules, the name/path of the .IMPSRC In suffix-transformation rules, the name/path of the
source from which the target is to be transformed (the source from which the target is to be transformed (the
``implied'' source); also known as `<'. It is not "implied" source); also known as `<'. It is not defined
defined in explicit rules. in explicit rules.
.MEMBER The name of the archive member; also known as `%'. .MEMBER The name of the archive member; also known as `%'.
@ -546,10 +546,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.MAKE.LEVEL The recursion depth of bmake. The initial instance of .MAKE.LEVEL The recursion depth of bmake. The initial instance of
bmake will be 0, and an incremented value is put into the bmake will be 0, and an incremented value is put into the
environment to be seen by the next generation. This environment to be seen by the next generation. This al-
allows tests like: .if ${.MAKE.LEVEL} == 0 to protect lows tests like: .if ${.MAKE.LEVEL} == 0 to protect
things which should only be evaluated in the initial things which should only be evaluated in the initial in-
instance of bmake. stance of bmake.
.MAKE.MAKEFILE_PREFERENCE .MAKE.MAKEFILE_PREFERENCE
The ordered list of makefile names (default `makefile', The ordered list of makefile names (default `makefile',
@ -647,9 +647,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Building ${.TARGET:H:tA}/${.TARGET:T} Building ${.TARGET:H:tA}/${.TARGET:T}
.MAKEOVERRIDES This variable is used to record the names of variables .MAKEOVERRIDES This variable is used to record the names of variables
assigned to on the command line, so that they may be assigned to on the command line, so that they may be ex-
exported as part of `MAKEFLAGS'. This behavior can be ported as part of `MAKEFLAGS'. This behavior can be dis-
disabled by assigning an empty value to `.MAKEOVERRIDES' abled by assigning an empty value to `.MAKEOVERRIDES'
within a makefile. Extra variables can be exported from within a makefile. Extra variables can be exported from
a makefile by appending their names to `.MAKEOVERRIDES'. a makefile by appending their names to `.MAKEOVERRIDES'.
`MAKEFLAGS' is re-exported whenever `.MAKEOVERRIDES' is `MAKEFLAGS' is re-exported whenever `.MAKEOVERRIDES' is
@ -668,8 +668,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
value should be a boolean that controls whether `$$' are value should be a boolean that controls whether `$$' are
preserved when doing `:=' assignments. The default is preserved when doing `:=' assignments. The default is
false, for backwards compatibility. Set to true for com- false, for backwards compatibility. Set to true for com-
patability with other makes. If set to false, `$$' patability with other makes. If set to false, `$$' be-
becomes `$' per normal evaluation rules. comes `$' per normal evaluation rules.
.MAKE.UID The user-id running bmake.
.MAKE.GID The group-id running bmake.
MAKE_PRINT_VAR_ON_ERROR MAKE_PRINT_VAR_ON_ERROR
When bmake stops due to an error, it sets `.ERROR_TARGET' When bmake stops due to an error, it sets `.ERROR_TARGET'
@ -733,8 +737,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.PARSEFILE The basename of the current `Makefile' being parsed. .PARSEFILE The basename of the current `Makefile' being parsed.
This variable and `.PARSEDIR' are both set only while the This variable and `.PARSEDIR' are both set only while the
`Makefiles' are being parsed. If you want to retain `Makefiles' are being parsed. If you want to retain
their current values, assign them to a variable using their current values, assign them to a variable using as-
assignment with expansion: (`:='). signment with expansion: (`:=').
.PATH A variable that represents the list of directories that .PATH A variable that represents the list of directories that
bmake will search for files. The search list should be bmake will search for files. The search list should be
@ -756,14 +760,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.TARGETS The list of targets explicitly specified on the command .TARGETS The list of targets explicitly specified on the command
line, if any. line, if any.
VPATH Colon-separated (``:'') lists of directories that bmake VPATH Colon-separated (":") lists of directories that bmake
will search for files. The variable is supported for will search for files. The variable is supported for
compatibility with old make programs only, use `.PATH' compatibility with old make programs only, use `.PATH'
instead. instead.
Variable modifiers Variable modifiers
Variable expansion may be modified to select or modify each word of the Variable expansion may be modified to select or modify each word of the
variable (where a ``word'' is white-space delimited sequence of charac- variable (where a "word" is white-space delimited sequence of charac-
ters). The general format of a variable expansion is as follows: ters). The general format of a variable expansion is as follows:
${variable[:modifier[:...]]} ${variable[:modifier[:...]]}
@ -831,8 +835,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
passed safely to the shell. passed safely to the shell.
:q Quotes every shell meta-character in the variable, and also doubles :q Quotes every shell meta-character in the variable, and also doubles
`$' characters so that it can be passed safely through recursive `$' characters so that it can be passed safely through recursive in-
invocations of bmake. This is equivalent to: `:S/\$/&&/g:Q'. vocations of bmake. This is equivalent to: `:S/\$/&&/g:Q'.
:R Replaces each word in the variable with everything but its suffix. :R Replaces each word in the variable with everything but its suffix.
@ -872,11 +876,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
:S/old_string/new_string/[1gW] :S/old_string/new_string/[1gW]
Modifies the first occurrence of old_string in each word of the Modifies the first occurrence of old_string in each word of the
variable's value, replacing it with new_string. If a `g' is variable's value, replacing it with new_string. If a `g' is ap-
appended to the last delimiter of the pattern, all occurrences in pended to the last delimiter of the pattern, all occurrences in each
each word are replaced. If a `1' is appended to the last delimiter word are replaced. If a `1' is appended to the last delimiter of
of the pattern, only the first occurrence is affected. If a `W' is the pattern, only the first occurrence is affected. If a `W' is ap-
appended to the last delimiter of the pattern, then the value is pended to the last delimiter of the pattern, then the value is
treated as a single word (possibly containing embedded white space). treated as a single word (possibly containing embedded white space).
If old_string begins with a caret (`^'), old_string is anchored at If old_string begins with a caret (`^'), old_string is anchored at
the beginning of each word. If old_string ends with a dollar sign the beginning of each word. If old_string ends with a dollar sign
@ -927,8 +931,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
This is the AT&T System V UNIX style variable substitution. It must This is the AT&T System V UNIX style variable substitution. It must
be the last modifier specified. If old_string or new_string do not be the last modifier specified. If old_string or new_string do not
contain the pattern matching character % then it is assumed that contain the pattern matching character % then it is assumed that
they are anchored at the end of each word, so only suffixes or they are anchored at the end of each word, so only suffixes or en-
entire words may be replaced. Otherwise % is the substring of tire words may be replaced. Otherwise % is the substring of
old_string to be replaced in new_string. If only old_string con- old_string to be replaced in new_string. If only old_string con-
tains the pattern matching character %, and old_string matches, then tains the pattern matching character %, and old_string matches, then
the result is the new_string. If only the new_string contains the the result is the new_string. If only the new_string contains the
@ -971,8 +975,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
:Unewval :Unewval
If the variable is undefined, newval is the value. If the variable If the variable is undefined, newval is the value. If the variable
is defined, the existing value is returned. This is another ODE is defined, the existing value is returned. This is another ODE
make feature. It is handy for setting per-target CFLAGS for make feature. It is handy for setting per-target CFLAGS for in-
instance: stance:
${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
If a value is only required if the variable is undefined, use: If a value is only required if the variable is undefined, use:
${VAR:D:Unewval} ${VAR:D:Unewval}
@ -1027,8 +1031,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
gers (where index 1 represents the first word), and backwards using gers (where index 1 represents the first word), and backwards using
negative integers (where index -1 represents the last word). negative integers (where index -1 represents the last word).
The range is subjected to variable expansion, and the expanded The range is subjected to variable expansion, and the expanded re-
result is then interpreted as follows: sult is then interpreted as follows:
index Selects a single word from the value. index Selects a single word from the value.
@ -1037,8 +1041,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
`:[2..-1]' selects all words from the second word to the last `:[2..-1]' selects all words from the second word to the last
word. If start is greater than end, then the words are out- word. If start is greater than end, then the words are out-
put in reverse order. For example, `:[-1..1]' selects all put in reverse order. For example, `:[-1..1]' selects all
the words from last to first. If the list is already the words from last to first. If the list is already or-
ordered, then this effectively reverses the list, but it is dered, then this effectively reverses the list, but it is
more efficient to use `:Or' instead of `:O:[-1..1]'. more efficient to use `:Or' instead of `:O:[-1..1]'.
* Causes subsequent modifiers to treat the value as a single * Causes subsequent modifiers to treat the value as a single
@ -1059,18 +1063,18 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
are identified by a line beginning with a single dot (`.') character. are identified by a line beginning with a single dot (`.') character.
Files are included with either .include <file> or .include "file". Vari- Files are included with either .include <file> or .include "file". Vari-
ables between the angle brackets or double quotes are expanded to form ables between the angle brackets or double quotes are expanded to form
the file name. If angle brackets are used, the included makefile is the file name. If angle brackets are used, the included makefile is ex-
expected to be in the system makefile directory. If double quotes are pected to be in the system makefile directory. If double quotes are
used, the including makefile's directory and any directories specified used, the including makefile's directory and any directories specified
using the -I option are searched before the system makefile directory. using the -I option are searched before the system makefile directory.
For compatibility with other versions of bmake `include file ...' is also For compatibility with other versions of bmake `include file ...' is also
accepted. accepted.
If the include statement is written as .-include or as .sinclude then If the include statement is written as .-include or as .sinclude then er-
errors locating and/or opening include files are ignored. rors locating and/or opening include files are ignored.
If the include statement is written as .dinclude not only are errors If the include statement is written as .dinclude not only are errors lo-
locating and/or opening include files ignored, but stale dependencies cating and/or opening include files ignored, but stale dependencies
within the included file will be ignored just like .MAKE.DEPENDFILE. within the included file will be ignored just like .MAKE.DEPENDFILE.
Conditional expressions are also preceded by a single dot as the first Conditional expressions are also preceded by a single dot as the first
@ -1087,8 +1091,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
flag, so should be used with caution. For compatibility with flag, so should be used with caution. For compatibility with
other bmake programs `export variable=value' is also accepted. other bmake programs `export variable=value' is also accepted.
Appending a variable name to .MAKE.EXPORTED is equivalent to Appending a variable name to .MAKE.EXPORTED is equivalent to ex-
exporting a variable. porting a variable.
.export-env variable ... .export-env variable ...
The same as `.export', except that the variable is not appended The same as `.export', except that the variable is not appended
@ -1103,9 +1107,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
The message is printed along with the name of the makefile and The message is printed along with the name of the makefile and
line number. line number.
.undef variable .undef variable ...
Un-define the specified global variable. Only global variables Un-define the specified global variables. Only global variables
may be un-defined. can be un-defined.
.unexport variable ... .unexport variable ...
The opposite of `.export'. The specified global variable will be The opposite of `.export'. The specified global variable will be
@ -1172,7 +1176,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|| Logical OR. || Logical OR.
&& Logical AND; of higher precedence than ``||''. && Logical AND; of higher precedence than "||".
As in C, bmake will only evaluate a conditional as far as is necessary to As in C, bmake will only evaluate a conditional as far as is necessary to
determine its value. Parentheses may be used to change the order of determine its value. Parentheses may be used to change the order of
@ -1185,9 +1189,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
the variable has been defined. the variable has been defined.
make Takes a target name as an argument and evaluates to true if the make Takes a target name as an argument and evaluates to true if the
target was specified as part of bmake's command line or was target was specified as part of bmake's command line or was de-
declared the default target (either implicitly or explicitly, clared the default target (either implicitly or explicitly, see
see .MAIN) before the line containing the conditional. .MAIN) before the line containing the conditional.
empty Takes a variable, with possible modifiers, and evaluates to true empty Takes a variable, with possible modifiers, and evaluates to true
if the expansion of the variable would result in an empty if the expansion of the variable would result in an empty
@ -1204,10 +1208,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Takes a target name as an argument and evaluates to true if the Takes a target name as an argument and evaluates to true if the
target has been defined and has commands associated with it. target has been defined and has commands associated with it.
Expression may also be an arithmetic or string comparison. Variable Expression may also be an arithmetic or string comparison. Variable ex-
expansion is performed on both sides of the comparison, after which the pansion is performed on both sides of the comparison, after which the nu-
numerical values are compared. A value is interpreted as hexadecimal if merical values are compared. A value is interpreted as hexadecimal if it
it is preceded by 0x, otherwise it is decimal; octal numbers are not sup- is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
ported. The standard C relational operators are all supported. If after ported. The standard C relational operators are all supported. If after
variable expansion, either the left or right hand side of a `==' or `!=' variable expansion, either the left or right hand side of a `==' or `!='
operator is not a numerical value, then string comparison is performed operator is not a numerical value, then string comparison is performed
@ -1215,12 +1219,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
is assumed that the expanded variable is being compared against 0, or an is assumed that the expanded variable is being compared against 0, or an
empty string in the case of a string comparison. empty string in the case of a string comparison.
When bmake is evaluating one of these conditional expressions, and it When bmake is evaluating one of these conditional expressions, and it en-
encounters a (white-space separated) word it doesn't recognize, either counters a (white-space separated) word it doesn't recognize, either the
the ``make'' or ``defined'' expression is applied to it, depending on the "make" or "defined" expression is applied to it, depending on the form of
form of the conditional. If the form is `.ifdef', `.ifndef', or `.if' the conditional. If the form is `.ifdef', `.ifndef', or `.if' the
the ``defined'' expression is applied. Similarly, if the form is "defined" expression is applied. Similarly, if the form is `.ifmake' or
`.ifmake' or `.ifnmake', the ``make'' expression is applied. `.ifnmake', the "make" expression is applied.
If the conditional evaluates to true the parsing of the makefile contin- If the conditional evaluates to true the parsing of the makefile contin-
ues as before. If it evaluates to false, the following lines are ues as before. If it evaluates to false, the following lines are
@ -1272,8 +1276,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
always changes. If the number of commands change, though, the always changes. If the number of commands change, though, the
target will still be out of date. The same effect applies to target will still be out of date. The same effect applies to
any command line that uses the variable .OODATE, which can be any command line that uses the variable .OODATE, which can be
used for that purpose even when not otherwise needed or used for that purpose even when not otherwise needed or de-
desired: sired:
skip-compare-for-some: skip-compare-for-some:
@ -1303,8 +1307,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.PRECIOUS .PRECIOUS
When bmake is interrupted, it normally removes any partially When bmake is interrupted, it normally removes any partially
made targets. This source prevents the target from being made targets. This source prevents the target from being re-
removed. moved.
.RECURSIVE .RECURSIVE
Synonym for .MAKE. Synonym for .MAKE.
@ -1324,10 +1328,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.WAIT If .WAIT appears in a dependency line, the sources that precede .WAIT If .WAIT appears in a dependency line, the sources that precede
it are made before the sources that succeed it in the line. it are made before the sources that succeed it in the line.
Since the dependents of files are not made until the file Since the dependents of files are not made until the file it-
itself could be made, this also stops the dependents being self could be made, this also stops the dependents being built
built unless they are needed for another branch of the depen- unless they are needed for another branch of the dependency
dency tree. So given: tree. So given:
x: a .WAIT b x: a .WAIT b
echo x echo x
@ -1361,14 +1365,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
make to delete targets whose commands fail. (By default, only make to delete targets whose commands fail. (By default, only
targets whose commands are interrupted during execution are targets whose commands are interrupted during execution are
deleted. This is the historical behavior.) This setting can be deleted. This is the historical behavior.) This setting can be
used to help prevent half-finished or malformed targets from used to help prevent half-finished or malformed targets from be-
being left around and corrupting future rebuilds. ing left around and corrupting future rebuilds.
.END Any command lines attached to this target are executed after .END Any command lines attached to this target are executed after ev-
everything else is done. erything else is done.
.ERROR Any command lines attached to this target are executed when .ERROR Any command lines attached to this target are executed when an-
another target fails. The .ERROR_TARGET variable is set to the other target fails. The .ERROR_TARGET variable is set to the
target that failed. See also MAKE_PRINT_VAR_ON_ERROR. target that failed. See also MAKE_PRINT_VAR_ON_ERROR.
.IGNORE Mark each of the sources with the .IGNORE attribute. If no .IGNORE Mark each of the sources with the .IGNORE attribute. If no
@ -1425,8 +1429,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.PRECIOUS .PRECIOUS
Apply the .PRECIOUS attribute to any specified sources. If no Apply the .PRECIOUS attribute to any specified sources. If no
sources are specified, the .PRECIOUS attribute is applied to sources are specified, the .PRECIOUS attribute is applied to ev-
every target in the file. ery target in the file.
.SHELL Sets the shell that bmake will use to execute commands. The .SHELL Sets the shell that bmake will use to execute commands. The
sources are a set of field=value pairs. sources are a set of field=value pairs.
@ -1469,8 +1473,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
sources are specified, the .SILENT attribute is applied to every sources are specified, the .SILENT attribute is applied to every
command in the file. command in the file.
.STALE This target gets run when a dependency file contains stale .STALE This target gets run when a dependency file contains stale en-
entries, having .ALLSRC set to the name of that dependency file. tries, having .ALLSRC set to the name of that dependency file.
.SUFFIXES .SUFFIXES
Each source specifies a suffix to bmake. If no sources are Each source specifies a suffix to bmake. If no sources are
@ -1509,8 +1513,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
The way that .for loop variables are substituted changed after NetBSD 5.0 The way that .for loop variables are substituted changed after NetBSD 5.0
so that they still appear to be variable expansions. In particular this so that they still appear to be variable expansions. In particular this
stops them being treated as syntax, and removes some obscure problems stops them being treated as syntax, and removes some obscure problems us-
using them in .if statements. ing them in .if statements.
The way that parallel makes are scheduled changed in NetBSD 4.0 so that The way that parallel makes are scheduled changed in NetBSD 4.0 so that
.ORDER and .WAIT apply recursively to the dependent nodes. The algo- .ORDER and .WAIT apply recursively to the dependent nodes. The algo-
@ -1518,8 +1522,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Other make dialects Other make dialects
Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup- Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup-
port most of the features of bmake as described in this manual. Most port most of the features of bmake as described in this manual. Most no-
notably: tably:
+o The .WAIT and .ORDER declarations and most functionality per- +o The .WAIT and .ORDER declarations and most functionality per-
taining to parallelization. (GNU make supports parallelization taining to parallelization. (GNU make supports parallelization
@ -1544,8 +1548,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
Some features are somewhat more portable, such as assignment with +=, ?=, Some features are somewhat more portable, such as assignment with +=, ?=,
and !=. The .PATH functionality is based on an older feature VPATH found and !=. The .PATH functionality is based on an older feature VPATH found
in GNU make and many versions of SVR4 make; however, historically its in GNU make and many versions of SVR4 make; however, historically its be-
behavior is too ill-defined (and too buggy) to rely upon. havior is too ill-defined (and too buggy) to rely upon.
The $@ and $< variables are more or less universally portable, as is the The $@ and $< variables are more or less universally portable, as is the
$(MAKE) variable. Basic use of suffix rules (for files only in the cur- $(MAKE) variable. Basic use of suffix rules (for files only in the cur-
@ -1562,11 +1566,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
A make command appeared in Version 7 AT&T UNIX. This make implementation A make command appeared in Version 7 AT&T UNIX. This make implementation
is based on Adam De Boor's pmake program which was written for Sprite at is based on Adam De Boor's pmake program which was written for Sprite at
Berkeley. It was designed to be a parallel distributed make running jobs Berkeley. It was designed to be a parallel distributed make running jobs
on different machines using a daemon called ``customs''. on different machines using a daemon called "customs".
Historically the target/dependency ``FRC'' has been used to FoRCe Historically the target/dependency "FRC" has been used to FoRCe rebuild-
rebuilding (since the target/dependency does not exist... unless someone ing (since the target/dependency does not exist... unless someone creates
creates an ``FRC'' file). an "FRC" file).
BUGS BUGS
The make syntax is difficult to parse without actually acting on the The make syntax is difficult to parse without actually acting on the
@ -1577,4 +1581,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
There is no way of escaping a space character in a filename. There is no way of escaping a space character in a filename.
FreeBSD 11.3 November 14, 2020 FreeBSD 11.3 FreeBSD 13.0 December 22, 2020 FreeBSD 13.0

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $ */ /* $NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -69,146 +69,153 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/* Automatically-expanding null-terminated buffers. */ /* Automatically-expanding null-terminated character buffers. */
#include <limits.h> #include <limits.h>
#include "make.h" #include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $"); MAKE_RCSID("$NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $");
/* Make space in the buffer for adding a single byte. */ /* Make space in the buffer for adding at least 16 more bytes. */
void void
Buf_Expand_1(Buffer *buf) Buf_Expand(Buffer *buf)
{ {
buf->cap += buf->cap > 16 ? buf->cap : 16; buf->cap += buf->cap > 16 ? buf->cap : 16;
buf->data = bmake_realloc(buf->data, buf->cap); buf->data = bmake_realloc(buf->data, buf->cap);
} }
/* Add the bytes to the buffer. */ /* Add the bytes to the buffer. */
void void
Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len) Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
{ {
size_t old_len = buf->len; size_t old_len = buf->len;
char *end; char *end;
if (__predict_false(old_len + bytes_len >= buf->cap)) { if (__predict_false(old_len + bytes_len >= buf->cap)) {
buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16; size_t minIncr = bytes_len + 16;
buf->data = bmake_realloc(buf->data, buf->cap); buf->cap += buf->cap > minIncr ? buf->cap : minIncr;
} buf->data = bmake_realloc(buf->data, buf->cap);
}
end = buf->data + old_len; end = buf->data + old_len;
buf->len = old_len + bytes_len; buf->len = old_len + bytes_len;
memcpy(end, bytes, bytes_len); memcpy(end, bytes, bytes_len);
end[bytes_len] = '\0'; end[bytes_len] = '\0';
} }
/* Add the bytes between start and end to the buffer. */ /* Add the bytes between start and end to the buffer. */
void void
Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end) Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end)
{ {
Buf_AddBytes(buf, start, (size_t)(end - start)); Buf_AddBytes(buf, start, (size_t)(end - start));
} }
/* Add the string to the buffer. */ /* Add the string to the buffer. */
void void
Buf_AddStr(Buffer *buf, const char *str) Buf_AddStr(Buffer *buf, const char *str)
{ {
Buf_AddBytes(buf, str, strlen(str)); Buf_AddBytes(buf, str, strlen(str));
} }
/* Add the number to the buffer. */ /* Add the number to the buffer. */
void void
Buf_AddInt(Buffer *buf, int n) Buf_AddInt(Buffer *buf, int n)
{ {
enum { enum {
bits = sizeof(int) * CHAR_BIT, bits = sizeof(int) * CHAR_BIT,
max_octal_digits = (bits + 2) / 3, max_octal_digits = (bits + 2) / 3,
max_decimal_digits = /* at most */ max_octal_digits, max_decimal_digits = /* at most */ max_octal_digits,
max_sign_chars = 1, max_sign_chars = 1,
str_size = max_sign_chars + max_decimal_digits + 1 str_size = max_sign_chars + max_decimal_digits + 1
}; };
char str[str_size]; char str[str_size];
size_t len = (size_t)snprintf(str, sizeof str, "%d", n); size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
Buf_AddBytes(buf, str, len); Buf_AddBytes(buf, str, len);
} }
/* Get the data (usually a string) from the buffer. /*
* Get the data (usually a string) from the buffer.
* The returned data is valid until the next modifying operation * The returned data is valid until the next modifying operation
* on the buffer. * on the buffer.
* *
* Returns the data and optionally the length of the data. */ * Returns the data and optionally the length of the data.
*/
char * char *
Buf_GetAll(Buffer *buf, size_t *out_len) Buf_GetAll(Buffer *buf, size_t *out_len)
{ {
if (out_len != NULL) if (out_len != NULL)
*out_len = buf->len; *out_len = buf->len;
return buf->data; return buf->data;
} }
/* Mark the buffer as empty, so it can be filled with data again. */ /* Mark the buffer as empty, so it can be filled with data again. */
void void
Buf_Empty(Buffer *buf) Buf_Empty(Buffer *buf)
{ {
buf->len = 0; buf->len = 0;
buf->data[0] = '\0'; buf->data[0] = '\0';
} }
/* Initialize a buffer. */ /* Initialize a buffer. */
void void
Buf_InitSize(Buffer *buf, size_t cap) Buf_InitSize(Buffer *buf, size_t cap)
{ {
buf->cap = cap; buf->cap = cap;
buf->len = 0; buf->len = 0;
buf->data = bmake_malloc(cap); buf->data = bmake_malloc(cap);
buf->data[0] = '\0'; buf->data[0] = '\0';
} }
void void
Buf_Init(Buffer *buf) Buf_Init(Buffer *buf)
{ {
Buf_InitSize(buf, 256); Buf_InitSize(buf, 256);
} }
/* Reset the buffer. /*
* Reset the buffer.
* If freeData is TRUE, the data from the buffer is freed as well. * If freeData is TRUE, the data from the buffer is freed as well.
* Otherwise it is kept and returned. */ * Otherwise it is kept and returned.
*/
char * char *
Buf_Destroy(Buffer *buf, Boolean freeData) Buf_Destroy(Buffer *buf, Boolean freeData)
{ {
char *data = buf->data; char *data = buf->data;
if (freeData) { if (freeData) {
free(data); free(data);
data = NULL; data = NULL;
} }
buf->cap = 0; buf->cap = 0;
buf->len = 0; buf->len = 0;
buf->data = NULL; buf->data = NULL;
return data; return data;
} }
#ifndef BUF_COMPACT_LIMIT #ifndef BUF_COMPACT_LIMIT
# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ # define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
#endif #endif
/* Reset the buffer and return its data. /*
* Reset the buffer and return its data.
* *
* If the buffer size is much greater than its content, * If the buffer size is much greater than its content,
* a new buffer will be allocated and the old one freed. */ * a new buffer will be allocated and the old one freed.
*/
char * char *
Buf_DestroyCompact(Buffer *buf) Buf_DestroyCompact(Buffer *buf)
{ {
#if BUF_COMPACT_LIMIT > 0 #if BUF_COMPACT_LIMIT > 0
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) { if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
/* We trust realloc to be smart */ /* We trust realloc to be smart */
char *data = bmake_realloc(buf->data, buf->len + 1); char *data = bmake_realloc(buf->data, buf->len + 1);
data[buf->len] = '\0'; /* XXX: unnecessary */ data[buf->len] = '\0'; /* XXX: unnecessary */
Buf_Destroy(buf, FALSE); Buf_Destroy(buf, FALSE);
return data; return data;
} }
#endif #endif
return Buf_Destroy(buf, FALSE); return Buf_Destroy(buf, FALSE);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: buf.h,v 1.36 2020/11/10 00:32:12 rillig Exp $ */ /* $NetBSD: buf.h,v 1.38 2020/12/28 15:42:53 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -81,9 +81,9 @@
/* An automatically growing null-terminated buffer of characters. */ /* An automatically growing null-terminated buffer of characters. */
typedef struct Buffer { typedef struct Buffer {
size_t cap; /* Allocated size of the buffer, including the null */ size_t cap; /* Allocated size of the buffer, including the null */
size_t len; /* Number of bytes in buffer, excluding the null */ size_t len; /* Number of bytes in buffer, excluding the null */
char *data; /* The buffer itself (always null-terminated) */ char *data; /* The buffer itself (always null-terminated) */
} Buffer; } Buffer;
/* If we aren't on NetBSD, __predict_false() might not be defined. */ /* If we aren't on NetBSD, __predict_false() might not be defined. */
@ -91,31 +91,31 @@ typedef struct Buffer {
#define __predict_false(x) (x) #define __predict_false(x) (x)
#endif #endif
void Buf_Expand_1(Buffer *); void Buf_Expand(Buffer *);
/* Buf_AddByte adds a single byte to a buffer. */ /* Buf_AddByte adds a single byte to a buffer. */
MAKE_INLINE void MAKE_INLINE void
Buf_AddByte(Buffer *buf, char byte) Buf_AddByte(Buffer *buf, char byte)
{ {
size_t old_len = buf->len++; size_t old_len = buf->len++;
char *end; char *end;
if (__predict_false(old_len + 1 >= buf->cap)) if (__predict_false(old_len + 1 >= buf->cap))
Buf_Expand_1(buf); Buf_Expand(buf);
end = buf->data + old_len; end = buf->data + old_len;
end[0] = byte; end[0] = byte;
end[1] = '\0'; end[1] = '\0';
} }
MAKE_INLINE size_t MAKE_INLINE size_t
Buf_Len(const Buffer *buf) Buf_Len(const Buffer *buf)
{ {
return buf->len; return buf->len;
} }
MAKE_INLINE Boolean MAKE_INLINE Boolean
Buf_EndsWith(const Buffer *buf, char ch) Buf_EndsWith(const Buffer *buf, char ch)
{ {
return buf->len > 0 && buf->data[buf->len - 1] == ch; return buf->len > 0 && buf->data[buf->len - 1] == ch;
} }
void Buf_AddBytes(Buffer *, const char *, size_t); void Buf_AddBytes(Buffer *, const char *, size_t);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: dir.h,v 1.34 2020/11/14 19:24:24 rillig Exp $ */ /* $NetBSD: dir.h,v 1.40 2020/12/01 19:28:32 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -72,27 +72,12 @@
* from: @(#)dir.h 8.1 (Berkeley) 6/6/93 * from: @(#)dir.h 8.1 (Berkeley) 6/6/93
*/ */
#ifndef MAKE_DIR_H #ifndef MAKE_DIR_H
#define MAKE_DIR_H #define MAKE_DIR_H
/* A cache for the filenames in a directory. */ typedef struct CachedDir CachedDir;
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 */
HashTable files; /* Hash set of files in directory;
* all values are NULL. */
} CachedDir;
void Dir_Init(void); void Dir_Init(void);
void Dir_InitDir(const char *);
void Dir_InitCur(const char *); void Dir_InitCur(const char *);
void Dir_InitDot(void); void Dir_InitDot(void);
void Dir_End(void); void Dir_End(void);
@ -103,18 +88,17 @@ char *Dir_FindFile(const char *, SearchPath *);
char *Dir_FindHereOrAbove(const char *, const char *); char *Dir_FindHereOrAbove(const char *, const char *);
void Dir_UpdateMTime(GNode *, Boolean); void Dir_UpdateMTime(GNode *, Boolean);
CachedDir *Dir_AddDir(SearchPath *, const char *); CachedDir *Dir_AddDir(SearchPath *, const char *);
char *Dir_MakeFlags(const char *, SearchPath *); char *SearchPath_ToFlags(const char *, SearchPath *);
void Dir_ClearPath(SearchPath *); void SearchPath_Clear(SearchPath *);
void Dir_Concat(SearchPath *, SearchPath *); void SearchPath_AddAll(SearchPath *, SearchPath *);
void Dir_PrintDirectories(void); void Dir_PrintDirectories(void);
void Dir_PrintPath(SearchPath *); void SearchPath_Print(SearchPath *);
void Dir_Destroy(void *);
SearchPath *Dir_CopyDirSearchPath(void); SearchPath *Dir_CopyDirSearchPath(void);
/* Stripped-down variant of struct stat. */ /* Stripped-down variant of struct stat. */
struct cached_stat { struct cached_stat {
time_t cst_mtime; time_t cst_mtime;
mode_t cst_mode; mode_t cst_mode;
}; };
int cached_lstat(const char *, struct cached_stat *); int cached_lstat(const char *, struct cached_stat *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */ /* $NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $ */
/* /*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -29,13 +29,15 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $"); MAKE_RCSID("$NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $");
/* Convert a bitset into a string representation, showing the names of the /*
* Convert a bitset into a string representation, showing the names of the
* individual bits. * individual bits.
* *
* Optionally, shortcuts for groups of bits can be added. To have an effect, * Optionally, shortcuts for groups of bits can be added. To have an effect,
* they need to be listed before their individual bits. */ * they need to be listed before their individual bits.
*/
const char * const char *
Enum_FlagsToString(char *buf, size_t buf_size, Enum_FlagsToString(char *buf, size_t buf_size,
int value, const EnumToStringSpec *spec) int value, const EnumToStringSpec *spec)
@ -86,4 +88,5 @@ Enum_ValueToString(int value, const EnumToStringSpec *spec)
return spec->es_name; return spec->es_name;
} }
abort(/* unknown enum value */); abort(/* unknown enum value */);
/*NOTREACHED*/
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: enum.h,v 1.12 2020/09/25 15:54:50 rillig Exp $ */ /* $NetBSD: enum.h,v 1.14 2020/12/30 10:03:16 rillig Exp $ */
/* /*
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org> Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
@ -45,8 +45,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
/* For Enum_FlagsToString, the separator between flags. */ /* For Enum_FlagsToString, the separator between flags. */
#define ENUM__SEP "|" #define ENUM__SEP "|"
/* Generate the string that joins all possible flags, to see how large the /*
* buffer must be. */ * Generate the string that joins all possible flags, to see how large the
* buffer must be.
*/
#define ENUM__JOIN_STR_1(v1) \ #define ENUM__JOIN_STR_1(v1) \
#v1 #v1
#define ENUM__JOIN_STR_2(v1, v2) \ #define ENUM__JOIN_STR_2(v1, v2) \
@ -107,8 +109,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
enum { typnam ## _ ## ToStringSize = sizeof joined } enum { typnam ## _ ## ToStringSize = sizeof joined }
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 2 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 2 flags.
*/
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \ #define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
@ -118,8 +122,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_1(v1), \ ENUM__JOIN_STR_1(v1), \
ENUM__JOIN_STR_1(v2))) ENUM__JOIN_STR_1(v2)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 3 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 3 flags.
*/
#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \ #define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
@ -129,8 +135,23 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_2(v1, v2), \ ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_1(v3))) ENUM__JOIN_STR_1(v3)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 6 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 4 flags.
*/
#define ENUM_FLAGS_RTTI_4(typnam, v1, v2, v3, v4) \
ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \
ENUM__SPEC_2(v1, v2), \
ENUM__SPEC_2(v3, v4)), \
ENUM__JOIN_2( \
ENUM__JOIN_STR_2(v1, v2), \
ENUM__JOIN_STR_2(v3, v4)))
/*
* 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) \ #define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
@ -140,8 +161,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_4(v1, v2, v3, v4), \ ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_2(v5, v6))) ENUM__JOIN_STR_2(v5, v6)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 8 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 8 flags.
*/
#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ #define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
@ -151,16 +174,20 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_4(v1, v2, v3, v4), \ ENUM__JOIN_STR_4(v1, v2, v3, v4), \
ENUM__JOIN_STR_4(v5, v6, v7, v8))) ENUM__JOIN_STR_4(v5, v6, v7, v8)))
/* Declare the necessary data structures for calling Enum_ValueToString /*
* for an enum with 8 constants. */ * Declare the necessary data structures for calling Enum_ValueToString
* for an enum with 8 constants.
*/
#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ #define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
ENUM__VALUE_RTTI(typnam, \ ENUM__VALUE_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
ENUM__SPEC_4(v1, v2, v3, v4), \ ENUM__SPEC_4(v1, v2, v3, v4), \
ENUM__SPEC_4(v5, v6, v7, v8))) ENUM__SPEC_4(v5, v6, v7, v8)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 10 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 10 flags.
*/
#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ #define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \
ENUM__FLAGS_RTTI(typnam, \ ENUM__FLAGS_RTTI(typnam, \
ENUM__SPECS_2( \ ENUM__SPECS_2( \
@ -170,8 +197,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \
ENUM__JOIN_STR_2(v9, v10))) ENUM__JOIN_STR_2(v9, v10)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 31 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 31 flags.
*/
#define ENUM_FLAGS_RTTI_31(typnam, \ #define ENUM_FLAGS_RTTI_31(typnam, \
v01, v02, v03, v04, v05, v06, v07, v08, \ v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16, \ v09, v10, v11, v12, v13, v14, v15, v16, \
@ -193,8 +222,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
ENUM__JOIN_STR_2(v29, v30), \ ENUM__JOIN_STR_2(v29, v30), \
ENUM__JOIN_STR_1(v31))) ENUM__JOIN_STR_1(v31)))
/* Declare the necessary data structures for calling Enum_FlagsToString /*
* for an enum with 32 flags. */ * Declare the necessary data structures for calling Enum_FlagsToString
* for an enum with 32 flags.
*/
#define ENUM_FLAGS_RTTI_32(typnam, \ #define ENUM_FLAGS_RTTI_32(typnam, \
v01, v02, v03, v04, v05, v06, v07, v08, \ v01, v02, v03, v04, v05, v06, v07, v08, \
v09, v10, v11, v12, v13, v14, v15, v16, \ v09, v10, v11, v12, v13, v14, v15, v16, \

View File

@ -1,4 +1,4 @@
/* $NetBSD: filemon.h,v 1.3 2020/10/18 11:49:47 rillig Exp $ */ /* $NetBSD: filemon.h,v 1.4 2020/11/29 09:27:40 rillig Exp $ */
/*- /*-
* Copyright (c) 2019 The NetBSD Foundation, Inc. * Copyright (c) 2019 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/ */
#ifndef MAKE_FILEMON_H #ifndef MAKE_FILEMON_H
#define MAKE_FILEMON_H #define MAKE_FILEMON_H
#include <sys/types.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_readfd(const struct filemon *);
int filemon_process(struct filemon *); int filemon_process(struct filemon *);
#endif /* MAKE_FILEMON_H */ #endif /* MAKE_FILEMON_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: filemon_dev.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */ /* $NetBSD: filemon_dev.c,v 1.6 2020/11/29 09:27:40 rillig Exp $ */
/*- /*-
* Copyright (c) 2020 The NetBSD Foundation, Inc. * Copyright (c) 2020 The NetBSD Foundation, Inc.
@ -43,7 +43,7 @@
#endif #endif
#ifndef _PATH_FILEMON #ifndef _PATH_FILEMON
#define _PATH_FILEMON "/dev/filemon" #define _PATH_FILEMON "/dev/filemon"
#endif #endif
struct filemon { struct filemon {
@ -127,7 +127,7 @@ filemon_close(struct filemon *F)
free(F); free(F);
/* Set errno and return -1 if anything went wrong. */ /* Set errno and return -1 if anything went wrong. */
if (error) { if (error != 0) {
errno = error; errno = error;
return -1; return -1;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: filemon_ktrace.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */ /* $NetBSD: filemon_ktrace.c,v 1.12 2021/01/10 23:59:53 rillig Exp $ */
/*- /*-
* Copyright (c) 2019 The NetBSD Foundation, Inc. * Copyright (c) 2019 The NetBSD Foundation, Inc.
@ -29,7 +29,7 @@
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#define _KERNTYPES /* register_t */ #define _KERNTYPES /* register_t */
#include "filemon.h" #include "filemon.h"
@ -90,9 +90,9 @@ static filemon_syscall_t *const filemon_syscalls[] = {
}; };
struct filemon { struct filemon {
int ktrfd; /* kernel writes ktrace events here */ int ktrfd; /* kernel writes ktrace events here */
FILE *in; /* we read ktrace events from here */ FILE *in; /* we read ktrace events from here */
FILE *out; /* we write filemon events to here */ FILE *out; /* we write filemon events to here */
rb_tree_t active; rb_tree_t active;
pid_t child; pid_t child;
@ -130,6 +130,7 @@ struct filemon_state {
char *path[/*npath*/]; char *path[/*npath*/];
}; };
/*ARGSUSED*/
static int static int
compare_filemon_states(void *cookie, const void *na, const void *nb) compare_filemon_states(void *cookie, const void *na, const void *nb)
{ {
@ -147,6 +148,7 @@ compare_filemon_states(void *cookie, const void *na, const void *nb)
return 0; return 0;
} }
/*ARGSUSED*/
static int static int
compare_filemon_key(void *cookie, const void *n, const void *k) compare_filemon_key(void *cookie, const void *n, const void *k)
{ {
@ -225,7 +227,6 @@ filemon_open(void)
/* Success! */ /* Success! */
return F; return F;
fail2: __unused
(void)fclose(F->in); (void)fclose(F->in);
fail1: (void)close(ktrpipe[0]); fail1: (void)close(ktrpipe[0]);
(void)close(ktrpipe[1]); (void)close(ktrpipe[1]);
@ -262,7 +263,7 @@ filemon_closefd(struct filemon *F)
F->out = NULL; F->out = NULL;
/* Set errno and return -1 if anything went wrong. */ /* Set errno and return -1 if anything went wrong. */
if (error) { if (error != 0) {
errno = error; errno = error;
return -1; return -1;
} }
@ -373,7 +374,7 @@ filemon_close(struct filemon *F)
free(F); free(F);
/* Set errno and return -1 if anything went wrong. */ /* Set errno and return -1 if anything went wrong. */
if (error) { if (error != 0) {
errno = error; errno = error;
return -1; return -1;
} }
@ -518,12 +519,12 @@ filemon_process(struct filemon *F)
return 0; return 0;
/* If we're waiting for input, read some. */ /* If we're waiting for input, read some. */
if (F->resid) { if (F->resid > 0) {
nread = fread(F->p, 1, F->resid, F->in); nread = fread(F->p, 1, F->resid, F->in);
if (nread == 0) { if (nread == 0) {
if (feof(F->in)) if (feof(F->in) != 0)
return 0; return 0;
assert(ferror(F->in)); assert(ferror(F->in) != 0);
/* /*
* If interrupted or would block, there may be * If interrupted or would block, there may be
* more events. Otherwise fail. * more events. Otherwise fail.
@ -538,7 +539,7 @@ filemon_process(struct filemon *F)
assert(nread <= F->resid); assert(nread <= F->resid);
F->p += nread; F->p += nread;
F->resid -= nread; F->resid -= nread;
if (F->resid) /* may be more events */ if (F->resid > 0) /* may be more events */
return 1; return 1;
} }
@ -582,7 +583,7 @@ filemon_process(struct filemon *F)
} }
static struct filemon_state * static struct filemon_state *
syscall_enter(struct filemon *F, syscall_enter(
const struct filemon_key *key, const struct ktr_syscall *call, const struct filemon_key *key, const struct ktr_syscall *call,
unsigned npath, unsigned npath,
void (*show)(struct filemon *, const struct filemon_state *, void (*show)(struct filemon *, const struct filemon_state *,
@ -618,7 +619,7 @@ show_paths(struct filemon *F, const struct filemon_state *S,
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
* we're not producing output. * we're not producing output.
*/ */
if (ret->ktr_error && ret->ktr_error != -2) if (ret->ktr_error != 0 && ret->ktr_error != -2)
return; return;
if (F->out == NULL) if (F->out == NULL)
return; return;
@ -644,7 +645,7 @@ show_retval(struct filemon *F, const struct filemon_state *S,
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if * Ignore it if it failed or yielded EJUSTRETURN (-2), or if
* we're not producing output. * we're not producing output.
*/ */
if (ret->ktr_error && ret->ktr_error != -2) if (ret->ktr_error != 0 && ret->ktr_error != -2)
return; return;
if (F->out == NULL) if (F->out == NULL)
return; return;
@ -664,7 +665,7 @@ static void
show_execve(struct filemon *F, const struct filemon_state *S, show_execve(struct filemon *F, const struct filemon_state *S,
const struct ktr_sysret *ret) const struct ktr_sysret *ret)
{ {
return show_paths(F, S, ret, "E"); show_paths(F, S, ret, "E");
} }
static void static void
@ -752,18 +753,20 @@ show_rename(struct filemon *F, const struct filemon_state *S,
show_paths(F, S, ret, "M"); show_paths(F, S, ret, "M");
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_chdir(struct filemon *F, const struct filemon_key *key, filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 1, &show_chdir); return syscall_enter(key, call, 1, &show_chdir);
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_execve(struct filemon *F, const struct filemon_key *key, filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 1, &show_execve); return syscall_enter(key, call, 1, &show_execve);
} }
static struct filemon_state * static struct filemon_state *
@ -773,7 +776,7 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
const register_t *args = (const void *)&call[1]; const register_t *args = (const void *)&call[1];
int status = (int)args[0]; int status = (int)args[0];
if (F->out) { if (F->out != NULL) {
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status); fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
if (key->pid == F->child) { if (key->pid == F->child) {
fprintf(F->out, "# Bye bye\n"); fprintf(F->out, "# Bye bye\n");
@ -783,20 +786,23 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
return NULL; return NULL;
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_fork(struct filemon *F, const struct filemon_key *key, filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 0, &show_fork); return syscall_enter(key, call, 0, &show_fork);
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_link(struct filemon *F, const struct filemon_key *key, filemon_sys_link(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 2, &show_link); return syscall_enter(key, call, 2, &show_link);
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_open(struct filemon *F, const struct filemon_key *key, filemon_sys_open(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
@ -809,15 +815,16 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
flags = (int)args[1]; flags = (int)args[1];
if ((flags & O_RDWR) == O_RDWR) if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, &show_open_readwrite); return syscall_enter(key, call, 1, &show_open_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY) else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1, &show_open_write); return syscall_enter(key, call, 1, &show_open_write);
else if ((flags & O_RDONLY) == O_RDONLY) else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1, &show_open_read); return syscall_enter(key, call, 1, &show_open_read);
else else
return NULL; /* XXX Do we care if no read or write? */ return NULL; /* XXX Do we care if no read or write? */
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_openat(struct filemon *F, const struct filemon_key *key, filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
@ -832,47 +839,47 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
if (fd == AT_CWD) { if (fd == AT_CWD) {
if ((flags & O_RDWR) == O_RDWR) if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, return syscall_enter(key, call, 1,
&show_open_readwrite); &show_open_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY) else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1, return syscall_enter(key, call, 1, &show_open_write);
&show_open_write);
else if ((flags & O_RDONLY) == O_RDONLY) else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1, &show_open_read); return syscall_enter(key, call, 1, &show_open_read);
else else
return NULL; return NULL;
} else { } else {
if ((flags & O_RDWR) == O_RDWR) if ((flags & O_RDWR) == O_RDWR)
return syscall_enter(F, key, call, 1, return syscall_enter(key, call, 1,
&show_openat_readwrite); &show_openat_readwrite);
else if ((flags & O_WRONLY) == O_WRONLY) else if ((flags & O_WRONLY) == O_WRONLY)
return syscall_enter(F, key, call, 1, return syscall_enter(key, call, 1, &show_openat_write);
&show_openat_write);
else if ((flags & O_RDONLY) == O_RDONLY) else if ((flags & O_RDONLY) == O_RDONLY)
return syscall_enter(F, key, call, 1, return syscall_enter(key, call, 1, &show_openat_read);
&show_openat_read);
else else
return NULL; return NULL;
} }
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key, filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 2, &show_symlink); return syscall_enter(key, call, 2, &show_symlink);
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_unlink(struct filemon *F, const struct filemon_key *key, filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 1, &show_unlink); return syscall_enter(key, call, 1, &show_unlink);
} }
/*ARGSUSED*/
static struct filemon_state * static struct filemon_state *
filemon_sys_rename(struct filemon *F, const struct filemon_key *key, filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
const struct ktr_syscall *call) const struct ktr_syscall *call)
{ {
return syscall_enter(F, key, call, 2, &show_rename); return syscall_enter(key, call, 2, &show_rename);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $ */ /* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */
/* /*
* Copyright (c) 1992, The Regents of the University of California. * Copyright (c) 1992, The Regents of the University of California.
@ -32,24 +32,22 @@
/*- /*-
* Handling of .for/.endfor loops in a makefile. * Handling of .for/.endfor loops in a makefile.
* *
* For loops are of the form: * For loops have the form:
* *
* .for <varname...> in <value...> * .for <varname...> in <value...>
* ... * # the body
* .endfor * .endfor
* *
* When a .for line is parsed, all following lines are accumulated into a * When a .for line is parsed, the following lines are copied to the body of
* buffer, up to but excluding the corresponding .endfor line. To find the * the .for loop, until the corresponding .endfor line is reached. In this
* corresponding .endfor, the number of nested .for and .endfor directives * phase, the body is not yet evaluated. This also applies to any nested
* are counted. * .for loops.
* *
* During parsing, any nested .for loops are just passed through; they get * After reaching the .endfor, the values from the .for line are grouped
* handled recursively in For_Eval when the enclosing .for loop is evaluated * according to the number of variables. For each such group, the unexpanded
* in For_Run. * body is scanned for variable expressions, and those that match the variable
* * names are replaced with expressions of the form ${:U...} or $(:U...).
* When the .for loop has been parsed completely, the variable expressions * After that, the body is treated like a file from an .include directive.
* for the iteration variables are replaced with expressions of the form
* ${:Uvalue}, and then this modified body is "included" as a special file.
* *
* Interface: * Interface:
* For_Eval Evaluate the loop in the passed line. * For_Eval Evaluate the loop in the passed line.
@ -60,29 +58,29 @@
#include "make.h" #include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $"); MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $");
static int forLevel = 0; /* Nesting level */ static int forLevel = 0; /* Nesting level */
/* One of the variables to the left of the "in" in a .for loop. */ /* One of the variables to the left of the "in" in a .for loop. */
typedef struct ForVar { typedef struct ForVar {
char *name; char *name;
size_t len; size_t nameLen;
} ForVar; } ForVar;
/* /*
* State of a for loop. * State of a for loop.
*/ */
typedef struct For { typedef struct For {
Buffer body; /* Unexpanded body of the loop */ Buffer body; /* Unexpanded body of the loop */
Vector /* of ForVar */ vars; /* Iteration variables */ Vector /* of ForVar */ vars; /* Iteration variables */
Words items; /* Substitution items */ Words items; /* Substitution items */
Buffer curBody; /* Expanded body of the current iteration */ Buffer curBody; /* Expanded body of the current iteration */
/* Is any of the names 1 character long? If so, when the variable values /* 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 * are substituted, the parser must handle $V expressions as well, not
* only ${V} and $(V). */ * only ${V} and $(V). */
Boolean short_var; Boolean short_var;
unsigned int sub_next; /* Where to continue iterating */ unsigned int sub_next; /* Where to continue iterating */
} For; } For;
static For *accumFor; /* Loop being accumulated */ static For *accumFor; /* Loop being accumulated */
@ -90,42 +88,43 @@ static For *accumFor; /* Loop being accumulated */
static void static void
ForAddVar(For *f, const char *name, size_t len) ForAddVar(For *f, const char *name, size_t len)
{ {
ForVar *var = Vector_Push(&f->vars); ForVar *var = Vector_Push(&f->vars);
var->name = bmake_strldup(name, len); var->name = bmake_strldup(name, len);
var->len = len; var->nameLen = len;
} }
static void static void
For_Free(For *f) For_Free(For *f)
{ {
Buf_Destroy(&f->body, TRUE); Buf_Destroy(&f->body, TRUE);
while (f->vars.len > 0) { while (f->vars.len > 0) {
ForVar *var = Vector_Pop(&f->vars); ForVar *var = Vector_Pop(&f->vars);
free(var->name); free(var->name);
} }
Vector_Done(&f->vars); Vector_Done(&f->vars);
Words_Free(f->items); Words_Free(f->items);
Buf_Destroy(&f->curBody, TRUE); Buf_Destroy(&f->curBody, TRUE);
free(f); free(f);
} }
static Boolean static Boolean
IsFor(const char *p) IsFor(const char *p)
{ {
return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]); return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
} }
static Boolean static Boolean
IsEndfor(const char *p) IsEndfor(const char *p)
{ {
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 && return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
(p[6] == '\0' || ch_isspace(p[6])); (p[6] == '\0' || ch_isspace(p[6]));
} }
/* Evaluate the for loop in the passed line. The line looks like this: /*
* Evaluate the for loop in the passed line. The line looks like this:
* .for <varname...> in <value...> * .for <varname...> in <value...>
* *
* Input: * Input:
@ -139,98 +138,108 @@ IsEndfor(const char *p)
int int
For_Eval(const char *line) For_Eval(const char *line)
{ {
For *f; For *f;
const char *p; const char *p;
p = line + 1; /* skip the '.' */ p = line + 1; /* skip the '.' */
cpp_skip_whitespace(&p); cpp_skip_whitespace(&p);
if (!IsFor(p)) { if (!IsFor(p)) {
if (IsEndfor(p)) { if (IsEndfor(p)) {
Parse_Error(PARSE_FATAL, "for-less endfor"); Parse_Error(PARSE_FATAL, "for-less endfor");
return -1; return -1;
}
return 0;
} }
return 0; p += 3;
}
p += 3;
/* /*
* we found a for loop, and now we are going to parse it. * we found a for loop, and now we are going to parse it.
*/ */
f = bmake_malloc(sizeof *f); f = bmake_malloc(sizeof *f);
Buf_Init(&f->body); Buf_Init(&f->body);
Vector_Init(&f->vars, sizeof(ForVar)); Vector_Init(&f->vars, sizeof(ForVar));
f->items.words = NULL; f->items.words = NULL;
f->items.freeIt = NULL; f->items.freeIt = NULL;
Buf_Init(&f->curBody); Buf_Init(&f->curBody);
f->short_var = FALSE; f->short_var = FALSE;
f->sub_next = 0; f->sub_next = 0;
/* Grab the variables. Terminate on "in". */ /* Grab the variables. Terminate on "in". */
for (;;) { for (;;) {
size_t len; size_t len;
cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
For_Free(f);
return -1;
}
/*
* 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 && p[0] == 'i' && p[1] == 'n') {
p += 2;
break;
}
if (len == 1)
f->short_var = TRUE;
ForAddVar(f, p, len);
p += len;
}
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
For_Free(f);
return -1;
}
cpp_skip_whitespace(&p); cpp_skip_whitespace(&p);
if (*p == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for"); {
For_Free(f); char *items;
return -1; if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
Parse_Error(PARSE_FATAL, "Error in .for loop items");
f->items.len = 0;
goto done;
}
f->items = Str_Words(items, FALSE);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
f->items.len = 0; /* .for var in ${:U} */
} }
/* XXX: This allows arbitrary variable names; see directive-for.mk. */ {
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) size_t nitems, nvars;
continue;
if (len == 2 && p[0] == 'i' && p[1] == 'n') { if ((nitems = f->items.len) > 0 &&
p += 2; nitems % (nvars = f->vars.len) != 0) {
break; Parse_Error(PARSE_FATAL,
"Wrong number of words (%u) in .for "
"substitution list with %u variables",
(unsigned)nitems, (unsigned)nvars);
/*
* Return 'success' so that the body of the .for loop
* is accumulated.
* Remove all items so that the loop doesn't iterate.
*/
f->items.len = 0;
}
} }
if (len == 1)
f->short_var = TRUE;
ForAddVar(f, p, len); done:
p += len; accumFor = f;
} forLevel = 1;
return 1;
if (f->vars.len == 0) {
Parse_Error(PARSE_FATAL, "no iteration variables in for");
For_Free(f);
return -1;
}
cpp_skip_whitespace(&p);
{
char *items;
(void)Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items);
/* TODO: handle errors */
f->items = Str_Words(items, FALSE);
free(items);
if (f->items.len == 1 && f->items.words[0][0] == '\0')
f->items.len = 0; /* .for var in ${:U} */
}
{
size_t nitems, nvars;
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 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.
*/
f->items.len = 0;
}
}
accumFor = f;
forLevel = 1;
return 1;
} }
/* /*
@ -240,240 +249,256 @@ For_Eval(const char *line)
Boolean Boolean
For_Accum(const char *line) For_Accum(const char *line)
{ {
const char *ptr = line; const char *ptr = line;
if (*ptr == '.') { if (*ptr == '.') {
ptr++; ptr++;
cpp_skip_whitespace(&ptr); cpp_skip_whitespace(&ptr);
if (IsEndfor(ptr)) { if (IsEndfor(ptr)) {
DEBUG1(FOR, "For: end for %d\n", forLevel); DEBUG1(FOR, "For: end for %d\n", forLevel);
if (--forLevel <= 0) if (--forLevel <= 0)
return FALSE; return FALSE;
} else if (IsFor(ptr)) { } else if (IsFor(ptr)) {
forLevel++; forLevel++;
DEBUG1(FOR, "For: new loop %d\n", forLevel); DEBUG1(FOR, "For: new loop %d\n", forLevel);
}
} }
}
Buf_AddStr(&accumFor->body, line); Buf_AddStr(&accumFor->body, line);
Buf_AddByte(&accumFor->body, '\n'); Buf_AddByte(&accumFor->body, '\n');
return TRUE; return TRUE;
} }
static size_t static size_t
for_var_len(const char *var) for_var_len(const char *var)
{ {
char ch, var_start, var_end; char ch, var_start, var_end;
int depth; int depth;
size_t len; size_t len;
var_start = *var; var_start = *var;
if (var_start == '\0') if (var_start == '\0')
/* just escape the $ */ /* just escape the $ */
return 0;
if (var_start == '(')
var_end = ')';
else if (var_start == '{')
var_end = '}';
else
return 1; /* Single char variable */
depth = 1;
for (len = 1; (ch = var[len++]) != '\0';) {
if (ch == var_start)
depth++;
else if (ch == var_end && --depth == 0)
return len;
}
/* Variable end not found, escape the $ */
return 0; return 0;
if (var_start == '(')
var_end = ')';
else if (var_start == '{')
var_end = '}';
else
/* Single char variable */
return 1;
depth = 1;
for (len = 1; (ch = var[len++]) != '\0';) {
if (ch == var_start)
depth++;
else if (ch == var_end && --depth == 0)
return len;
}
/* Variable end not found, escape the $ */
return 0;
}
/* The .for loop substitutes the items as ${:U<value>...}, which means
* that characters that break this syntax must be backslash-escaped. */
static Boolean
NeedsEscapes(const char *word, char endc)
{
const char *p;
for (p = word; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
return TRUE;
}
return FALSE;
}
/* While expanding the body of a .for loop, write the item in the ${:U...}
* expression, escaping characters as needed.
*
* The result is later unescaped by ApplyModifier_Defined. */
static void
Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
{
char ch;
if (!NeedsEscapes(item, ech)) {
Buf_AddStr(cmds, item);
return;
}
/* 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) {
Buf_AddBytes(cmds, item - 1, len + 1);
item += len;
continue;
}
Buf_AddByte(cmds, '\\');
} else if (ch == ':' || ch == '\\' || ch == ech)
Buf_AddByte(cmds, '\\');
Buf_AddByte(cmds, ch);
}
}
/* 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)
{
size_t i;
const char *p = *pp;
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 * The .for loop substitutes the items as ${:U<value>...}, which means
* with variable references that expand to the required text. * that characters that break this syntax must be backslash-escaped.
*/
static Boolean
NeedsEscapes(const char *word, char endc)
{
const char *p;
for (p = word; *p != '\0'; p++) {
if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
return TRUE;
}
return FALSE;
}
/*
* While expanding the body of a .for loop, write the item in the ${:U...}
* expression, escaping characters as needed.
* *
* Using variable expansions ensures that the .for loop can't generate * The result is later unescaped by ApplyModifier_Defined.
*/
static void
Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
{
char ch;
if (!NeedsEscapes(item, endc)) {
Buf_AddStr(cmds, item);
return;
}
/* Escape ':', '$', '\\' and 'endc' - 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) {
Buf_AddBytes(cmds, item - 1, len + 1);
item += len;
continue;
}
Buf_AddByte(cmds, '\\');
} else if (ch == ':' || ch == '\\' || ch == endc)
Buf_AddByte(cmds, '\\');
Buf_AddByte(cmds, ch);
}
}
/*
* While expanding the body of a .for loop, replace the variable name of an
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
*/
static void
SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc,
const char **inout_mark)
{
size_t i;
const char *p = *pp;
for (i = 0; i < f->vars.len; i++) {
ForVar *forVar = Vector_Get(&f->vars, i);
char *varname = forVar->name;
size_t varnameLen = forVar->nameLen;
if (varnameLen >= (size_t)(bodyEnd - p))
continue;
if (memcmp(p, varname, varnameLen) != 0)
continue;
/* XXX: why test for backslash here? */
if (p[varnameLen] != ':' && p[varnameLen] != endc &&
p[varnameLen] != '\\')
continue;
/*
* Found a variable match. Skip over the variable name and
* instead add ':U<value>' to the current body.
*/
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
Buf_AddStr(&f->curBody, ":U");
Buf_AddEscaped(&f->curBody,
f->items.words[f->sub_next + i], endc);
p += varnameLen;
*inout_mark = p;
*pp = p;
return;
}
}
/*
* While expanding the body of a .for loop, replace single-character
* variable expressions like $i with their ${:U...} expansion.
*/
static void
SubstVarShort(For *f, const char *p, const char **inout_mark)
{
const char ch = *p;
ForVar *vars;
size_t i;
/* Skip $$ and stupid ones. */
if (!f->short_var || strchr("}):$", ch) != NULL)
return;
vars = Vector_Get(&f->vars, 0);
for (i = 0; i < f->vars.len; i++) {
const char *varname = vars[i].name;
if (varname[0] == ch && varname[1] == '\0')
goto found;
}
return;
found:
/* Replace $<ch> with ${:U<value>} */
Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1;
Buf_AddStr(&f->curBody, "{:U");
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
Buf_AddByte(&f->curBody, '}');
}
/*
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*
* Using variable expressions ensures that the .for loop can't generate
* syntax, and that the later parsing will still see a variable. * syntax, and that the later parsing will still see a variable.
* We assume that the null variable will never be defined. * This code assumes that the variable with the empty name will never be
* defined, see unit-tests/varname-empty.mk for more details.
* *
* The detection of substitutions of the loop control variable is naive. * The detection of substitutions of the loop control variable is naive.
* Many of the modifiers use \ to escape $ (not $) so it is possible * Many of the modifiers use \ to escape $ (not $) so it is possible
* to contrive a makefile where an unwanted substitution happens. * to contrive a makefile where an unwanted substitution happens.
*/ */
static char * static void
ForIterate(void *v_arg, size_t *out_len) ForSubstBody(For *f)
{ {
For *f = v_arg; const char *p, *bodyEnd;
const char *p; const char *mark; /* where the last replacement left off */
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) { Buf_Empty(&f->curBody);
/* No more iterations */
For_Free(f);
return NULL;
}
Buf_Empty(&f->curBody); mark = f->body.data;
bodyEnd = f->body.data + f->body.len;
mark = Buf_GetAll(&f->body, NULL); for (p = mark; (p = strchr(p, '$')) != NULL;) {
body_end = mark + Buf_Len(&f->body); if (p[1] == '{' || p[1] == '(') {
for (p = mark; (p = strchr(p, '$')) != NULL;) { p += 2;
char ch, ech; SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')',
ch = *++p; &mark);
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) { } else if (p[1] != '\0') {
p++; SubstVarShort(f, p + 1, &mark);
/* Check variable name against the .for loop variables */ p += 2;
SubstVarLong(f, &p, &mark, ech); } else
continue; break;
} }
if (ch == '\0')
break;
SubstVarShort(f, ch, &p, &mark); Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
}
Buf_AddBytesBetween(&f->curBody, mark, body_end);
*out_len = Buf_Len(&f->curBody);
cmds_str = Buf_GetAll(&f->curBody, NULL);
DEBUG1(FOR, "For: loop body:\n%s", cmds_str);
f->sub_next += f->vars.len;
return cmds_str;
} }
/* Run the for loop, imitating the actions of an include file. */ /*
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*/
static char *
ForReadMore(void *v_arg, size_t *out_len)
{
For *f = v_arg;
if (f->sub_next == f->items.len) {
/* No more iterations */
For_Free(f);
return NULL;
}
ForSubstBody(f);
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
f->sub_next += (unsigned int)f->vars.len;
*out_len = f->curBody.len;
return f->curBody.data;
}
/* Run the .for loop, imitating the actions of an include file. */
void void
For_Run(int lineno) For_Run(int lineno)
{ {
For *f = accumFor; For *f = accumFor;
accumFor = NULL; accumFor = NULL;
if (f->items.len == 0) { if (f->items.len == 0) {
/* Nothing to expand - possibly due to an earlier syntax error. */ /*
For_Free(f); * Nothing to expand - possibly due to an earlier syntax
return; * error.
} */
For_Free(f);
return;
}
Parse_SetInput(NULL, lineno, -1, ForIterate, f); Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $ */ /* $NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -74,7 +74,7 @@
#include "make.h" #include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */ /* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $"); MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $");
/* /*
* The ratio of # entries to # buckets at which we rebuild the table to * The ratio of # entries to # buckets at which we rebuild the table to
@ -86,10 +86,13 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $");
static unsigned int static unsigned int
hash(const char *key, size_t *out_keylen) hash(const char *key, size_t *out_keylen)
{ {
unsigned int h = 0; unsigned int h;
const char *p = key; const char *p;
while (*p != '\0')
h = (h << 5) - h + (unsigned char)*p++; h = 0;
for (p = key; *p != '\0'; p++)
h = 31 * h + (unsigned char)*p;
if (out_keylen != NULL) if (out_keylen != NULL)
*out_keylen = (size_t)(p - key); *out_keylen = (size_t)(p - key);
return h; return h;
@ -98,7 +101,7 @@ hash(const char *key, size_t *out_keylen)
unsigned int unsigned int
Hash_Hash(const char *key) Hash_Hash(const char *key)
{ {
return hash(key, NULL); return hash(key, NULL);
} }
static HashEntry * static HashEntry *
@ -177,8 +180,10 @@ HashTable_FindValue(HashTable *t, const char *key)
return he != NULL ? he->value : NULL; return he != NULL ? he->value : NULL;
} }
/* Find the value corresponding to the key and the precomputed hash, /*
* or return NULL. */ * Find the value corresponding to the key and the precomputed hash,
* or return NULL.
*/
void * void *
HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h) HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
{ {
@ -186,8 +191,10 @@ HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
return he != NULL ? he->value : NULL; 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. */ * Make the hash table larger. Any bucket numbers from the old table become
* invalid; the hash codes stay valid though.
*/
static void static void
HashTable_Enlarge(HashTable *t) HashTable_Enlarge(HashTable *t)
{ {
@ -221,8 +228,10 @@ HashTable_Enlarge(HashTable *t)
t->maxchain = 0; t->maxchain = 0;
} }
/* Find or create an entry corresponding to the key. /*
* Return in out_isNew whether a new entry has been created. */ * Find or create an entry corresponding to the key.
* Return in out_isNew whether a new entry has been created.
*/
HashEntry * HashEntry *
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew) HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
{ {
@ -288,8 +297,10 @@ HashIter_Init(HashIter *hi, HashTable *t)
hi->entry = NULL; hi->entry = NULL;
} }
/* Return the next entry in the hash table, or NULL if the end of the table /*
* is reached. */ * Return the next entry in the hash table, or NULL if the end of the table
* is reached.
*/
HashEntry * HashEntry *
HashIter_Next(HashIter *hi) HashIter_Next(HashIter *hi)
{ {

View File

@ -1,4 +1,4 @@
/* $NetBSD: hash.h,v 1.33 2020/11/14 21:29:44 rillig Exp $ */ /* $NetBSD: hash.h,v 1.38 2020/12/15 01:23:55 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -74,45 +74,50 @@
/* Hash tables with strings as keys and arbitrary pointers as values. */ /* Hash tables with strings as keys and arbitrary pointers as values. */
#ifndef MAKE_HASH_H #ifndef MAKE_HASH_H
#define MAKE_HASH_H #define MAKE_HASH_H
/* A single key-value entry in the hash table. */ /* A single key-value entry in the hash table. */
typedef struct HashEntry { typedef struct HashEntry {
struct HashEntry *next; /* Used to link together all the entries struct HashEntry *next; /* Used to link together all the entries
* associated with the same bucket. */ * associated with the same bucket. */
void *value; void *value;
unsigned int key_hash; /* hash value of the key */ unsigned int key_hash; /* hash value of the key */
char key[1]; /* key string, variable length */ char key[1]; /* key string, variable length */
} HashEntry; } HashEntry;
/* The hash table containing the entries. */ /* The hash table containing the entries. */
typedef struct HashTable { typedef struct HashTable {
HashEntry **buckets; /* Pointers to HashEntry, one HashEntry **buckets; /* Pointers to HashEntry, one
* for each bucket in the table. */ * for each bucket in the table. */
unsigned int bucketsSize; unsigned int bucketsSize;
unsigned int numEntries; /* Number of entries in the table. */ unsigned int numEntries; /* Number of entries in the table. */
unsigned int bucketsMask; /* Used to select the bucket for a hash. */ unsigned int bucketsMask; /* Used to select the bucket for a hash. */
unsigned int maxchain; /* max length of chain detected */ unsigned int maxchain; /* max length of chain detected */
} HashTable; } HashTable;
/* State of an iteration over all entries in a table. */ /* State of an iteration over all entries in a table. */
typedef struct HashIter { typedef struct HashIter {
HashTable *table; /* Table being searched. */ HashTable *table; /* Table being searched. */
unsigned int nextBucket; /* Next bucket to check (after current). */ unsigned int nextBucket; /* Next bucket to check (after current). */
HashEntry *entry; /* Next entry to check in current bucket. */ HashEntry *entry; /* Next entry to check in current bucket. */
} HashIter; } HashIter;
/* A set of strings. */
typedef struct HashSet {
HashTable tbl;
} HashSet;
MAKE_INLINE void * MAKE_INLINE void *
HashEntry_Get(HashEntry *h) HashEntry_Get(HashEntry *h)
{ {
return h->value; return h->value;
} }
MAKE_INLINE void MAKE_INLINE void
HashEntry_Set(HashEntry *h, void *datum) HashEntry_Set(HashEntry *h, void *datum)
{ {
h->value = datum; h->value = datum;
} }
void HashTable_Init(HashTable *); void HashTable_Init(HashTable *);
@ -129,4 +134,37 @@ void HashTable_DebugStats(HashTable *, const char *);
void HashIter_Init(HashIter *, HashTable *); void HashIter_Init(HashIter *, HashTable *);
HashEntry *HashIter_Next(HashIter *); HashEntry *HashIter_Next(HashIter *);
MAKE_INLINE void
HashSet_Init(HashSet *set)
{
HashTable_Init(&set->tbl);
}
MAKE_INLINE void
HashSet_Done(HashSet *set)
{
HashTable_Done(&set->tbl);
}
MAKE_INLINE Boolean
HashSet_Add(HashSet *set, const char *key)
{
Boolean isNew;
(void)HashTable_CreateEntry(&set->tbl, key, &isNew);
return isNew;
}
MAKE_INLINE Boolean
HashSet_Contains(HashSet *set, const char *key)
{
return HashTable_FindEntry(&set->tbl, key) != NULL;
}
MAKE_INLINE void
HashIter_InitSet(HashIter *hi, HashSet *set)
{
HashIter_Init(hi, &set->tbl);
}
#endif /* MAKE_HASH_H */ #endif /* MAKE_HASH_H */

87
contrib/bmake/import.sh Executable file
View File

@ -0,0 +1,87 @@
#!/bin/sh
# Import bmake
ECHO=
GIT=${GIT:-git}
# For consistency...
Error() {
echo ERROR: ${1+"$@"} >&2
exit 1
}
Cd() {
[ $# -eq 1 ] || Error "Cd() takes a single parameter."
cd $1 || Error "cannot \"cd $1\" from $PWD"
}
# Call this function and then follow it by any specific import script additions
option_parsing() {
local _shift=$#
# Parse command line options
while :
do
case "$1" in
*=*) eval "$1"; shift;;
--) shift; break;;
-a) TARBALL=$2; shift 2;;
-n) ECHO=echo; shift;;
-P) PR=$2; shift 2;;
-r) REVIEWER=$2; shift 2;;
-u) url=$2; shift 2;;
-h) echo "Usage:";
echo " "$0 '[-ahnPr] [TARBALL=] [PR=] [REVIEWER=]'
echo " "$0 '-a <filename> # (a)rchive'
echo " "$0 '-h # print usage'
echo " "$0 '-n # do not import, check only.'
echo " "$0 '-P <PR Number> # Use PR'
echo " "$0 '-r <reviewer(s) list> # (r)eviewed by'
echo " "$0 'PR=<PR Number>'
echo " "$0 'REVIEWER=<reviewer(s) list>'
exit 1;;
*) break;;
esac
done
return $(($_shift - $#))
}
###
option_parsing "$@"
shift $?
TF=/tmp/.$USER.$$
Cd `dirname $0`
test -s ${TARBALL:-/dev/null} || Error need TARBALL
here=`pwd`
# thing should match what the TARBALL contains
thing=`basename $here`
case "$thing" in
bmake) (cd .. && tar zxf $TARBALL);;
*) Error "we should be in bmake";;
esac
VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'`
rm -f *~
mkdir -p ../tmp
if [ -z "$ECHO" ]; then
# new files are handled automatically
# but we need to rm if needed
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
test -s $TF.rm && xargs rm -f < $TF.rm
$GIT add -A
$GIT diff --staged | tee ../tmp/bmake-import.diff
echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh
echo "After you commit, run $here/../tmp/bmake-post.sh"
else
# FILES is kept sorted so we can determine what was added and deleted
$GIT diff FILES | sed -n '/^+[^+]/s,^+,,p' > $TF.add
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
test -s $TF.rm && { echo Removing:; cat $TF.rm; }
test -s $TF.add && { echo Adding:; cat $TF.add; }
$GIT diff
fi

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: job.h,v 1.63 2020/11/14 13:27:01 rillig Exp $ */ /* $NetBSD: job.h,v 1.71 2020/12/30 10:03:16 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -97,8 +97,8 @@ struct emul_pollfd {
short revents; short revents;
}; };
#define POLLIN 0x0001 #define POLLIN 0x0001
#define POLLOUT 0x0004 #define POLLOUT 0x0004
int int
emul_poll(struct pollfd *fd, int nfd, int timeout); emul_poll(struct pollfd *fd, int nfd, int timeout);
@ -118,27 +118,15 @@ struct pollfd;
#endif #endif
typedef enum JobStatus { typedef enum JobStatus {
JOB_ST_FREE = 0, /* Job is available */ JOB_ST_FREE = 0, /* Job is available */
JOB_ST_SET_UP = 1, /* Job is allocated but otherwise invalid */ JOB_ST_SET_UP = 1, /* Job is allocated but otherwise invalid */
/* XXX: What about the 2? */ /* XXX: What about the 2? */
JOB_ST_RUNNING = 3, /* Job is running, pid valid */ JOB_ST_RUNNING = 3, /* Job is running, pid valid */
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */ JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
} JobStatus; } JobStatus;
typedef enum JobFlags { /*
JOB_NONE = 0, * A Job manages the shell commands that are run to create a single target.
/* Ignore non-zero exits */
JOB_IGNERR = 1 << 0,
/* no output */
JOB_SILENT = 1 << 1,
/* Target is a special one. i.e. run it locally
* if we can't export it and maxLocal is 0 */
JOB_SPECIAL = 1 << 2,
/* we've sent 'set -x' */
JOB_TRACED = 1 << 10
} 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 * Each job is run in a separate subprocess by a shell. Several jobs can run
* in parallel. * in parallel.
* *
@ -148,42 +136,47 @@ typedef enum JobFlags {
* *
* When a job is finished, Make_Update updates all parents of the node * 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 * that was just remade, marking them as ready to be made next if all
* other dependencies are finished as well. */ * other dependencies are finished as well.
*/
typedef struct Job { typedef struct Job {
/* The process ID of the shell running the commands */ /* The process ID of the shell running the commands */
int pid; int pid;
/* The target the child is making */ /* The target the child is making */
GNode *node; GNode *node;
/* If one of the shell commands is "...", all following commands are /* If one of the shell commands is "...", all following commands are
* delayed until the .END node is made. This list node points to the * delayed until the .END node is made. This list node points to the
* first of these commands, if any. */ * first of these commands, if any. */
StringListNode *tailCmds; StringListNode *tailCmds;
/* When creating the shell script, this is where the commands go. /* This is where the shell commands go. */
* This is only used before the job is actually started. */ FILE *cmdFILE;
FILE *cmdFILE;
int exit_status; /* from wait4() in signal handler */ int exit_status; /* from wait4() in signal handler */
JobStatus status; JobStatus status;
Boolean suspended; Boolean suspended;
JobFlags flags; /* Flags to control treatment of job */ /* Ignore non-zero exits */
Boolean ignerr;
/* Output the command before or instead of running it. */
Boolean echo;
/* Target is a special one. */
Boolean special;
int inPipe; /* Pipe for reading output from job */ int inPipe; /* Pipe for reading output from job */
int outPipe; /* Pipe for writing control commands */ int outPipe; /* Pipe for writing control commands */
struct pollfd *inPollfd; /* pollfd associated with inPipe */ struct pollfd *inPollfd; /* pollfd associated with inPipe */
#define JOB_BUFSIZE 1024 #define JOB_BUFSIZE 1024
/* Buffer for storing the output of the job, line by line. */ /* Buffer for storing the output of the job, line by line. */
char outBuf[JOB_BUFSIZE + 1]; char outBuf[JOB_BUFSIZE + 1];
size_t curPos; /* Current position in outBuf. */ size_t curPos; /* Current position in outBuf. */
#ifdef USE_META #ifdef USE_META
struct BuildMon bm; struct BuildMon bm;
#endif #endif
} Job; } Job;
@ -211,5 +204,6 @@ Boolean Job_TokenWithdraw(void);
void Job_ServerStart(int, int, int); void Job_ServerStart(int, int, int);
void Job_SetPrefix(void); void Job_SetPrefix(void);
Boolean Job_RunTarget(const char *, const char *); Boolean Job_RunTarget(const char *, const char *);
void Job_FlagsToString(const Job *, char *, size_t);
#endif /* MAKE_JOB_H */ #endif /* MAKE_JOB_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $ */ /* $NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -34,278 +34,266 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $"); MAKE_RCSID("$NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $");
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
#endif
static ListNode * static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum) LstNodeNew(ListNode *prev, ListNode *next, void *datum)
{ {
ListNode *ln = bmake_malloc(sizeof *ln); ListNode *ln = bmake_malloc(sizeof *ln);
ln->prev = prev;
ln->next = next; ln->prev = prev;
ln->datum = datum; ln->next = next;
return ln; ln->datum = datum;
return ln;
} }
/* Create and initialize a new, empty list. */ /* Create and initialize a new, empty list. */
List * List *
Lst_New(void) Lst_New(void)
{ {
List *list = bmake_malloc(sizeof *list); List *list = bmake_malloc(sizeof *list);
Lst_Init(list);
return list;
}
list->first = NULL; void
list->last = NULL; Lst_Done(List *list)
{
ListNode *ln, *next;
return list; for (ln = list->first; ln != NULL; ln = next) {
next = ln->next;
free(ln);
}
}
void
Lst_DoneCall(List *list, LstFreeProc freeProc)
{
ListNode *ln, *next;
for (ln = list->first; ln != NULL; ln = next) {
next = ln->next;
freeProc(ln->datum);
free(ln);
}
} }
/* Free a list and all its nodes. The node data are not freed though. */ /* Free a list and all its nodes. The node data are not freed though. */
void void
Lst_Free(List *list) Lst_Free(List *list)
{ {
ListNode *ln, *next;
for (ln = list->first; ln != NULL; ln = next) { Lst_Done(list);
next = ln->next; free(list);
free(ln);
}
free(list);
} }
/* Destroy a list and free all its resources. The freeProc is called with the /*
* datum from each node in turn before the node is freed. */ * 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 void
Lst_Destroy(List *list, LstFreeProc freeProc) Lst_Destroy(List *list, LstFreeProc freeProc)
{ {
ListNode *ln, *next; Lst_DoneCall(list, freeProc);
free(list);
for (ln = list->first; ln != NULL; ln = next) {
next = ln->next;
freeProc(ln->datum);
free(ln);
}
free(list);
} }
/* Insert a new node with the datum before the given node. */ /* Insert a new node with the datum before the given node. */
void void
Lst_InsertBefore(List *list, ListNode *ln, void *datum) Lst_InsertBefore(List *list, ListNode *ln, void *datum)
{ {
ListNode *newNode; ListNode *newNode;
assert(datum != NULL); assert(datum != NULL);
newNode = LstNodeNew(ln->prev, ln, datum); newNode = LstNodeNew(ln->prev, ln, datum);
if (ln->prev != NULL) if (ln->prev != NULL)
ln->prev->next = newNode; ln->prev->next = newNode;
ln->prev = newNode; ln->prev = newNode;
if (ln == list->first) if (ln == list->first)
list->first = newNode; list->first = newNode;
} }
/* Add a piece of data at the start of the given list. */ /* Add a piece of data at the start of the given list. */
void void
Lst_Prepend(List *list, void *datum) Lst_Prepend(List *list, void *datum)
{ {
ListNode *ln; ListNode *ln;
assert(datum != NULL); assert(datum != NULL);
ln = LstNodeNew(NULL, list->first, datum); ln = LstNodeNew(NULL, list->first, datum);
if (list->first == NULL) { if (list->first == NULL) {
list->first = ln; list->first = ln;
list->last = ln; list->last = ln;
} else { } else {
list->first->prev = ln; list->first->prev = ln;
list->first = ln; list->first = ln;
} }
} }
/* Add a piece of data at the end of the given list. */ /* Add a piece of data at the end of the given list. */
void void
Lst_Append(List *list, void *datum) Lst_Append(List *list, void *datum)
{ {
ListNode *ln; ListNode *ln;
assert(datum != NULL); assert(datum != NULL);
ln = LstNodeNew(list->last, NULL, datum); ln = LstNodeNew(list->last, NULL, datum);
if (list->last == NULL) { if (list->last == NULL) {
list->first = ln; list->first = ln;
list->last = ln; list->last = ln;
} else { } else {
list->last->next = ln; list->last->next = ln;
list->last = ln; list->last = ln;
} }
} }
/* Remove the given node from the given list. /*
* The datum stored in the node must be freed by the caller, if necessary. */ * Remove the given node from the given list.
* The datum stored in the node must be freed by the caller, if necessary.
*/
void void
Lst_Remove(List *list, ListNode *ln) Lst_Remove(List *list, ListNode *ln)
{ {
/* unlink it from its neighbors */ /* unlink it from its neighbors */
if (ln->next != NULL) if (ln->next != NULL)
ln->next->prev = ln->prev; ln->next->prev = ln->prev;
if (ln->prev != NULL) if (ln->prev != NULL)
ln->prev->next = ln->next; ln->prev->next = ln->next;
/* unlink it from the list */ /* unlink it from the list */
if (list->first == ln) if (list->first == ln)
list->first = ln->next; list->first = ln->next;
if (list->last == ln) if (list->last == ln)
list->last = ln->prev; list->last = ln->prev;
} }
/* Replace the datum in the given node with the new datum. */ /* Replace the datum in the given node with the new datum. */
void void
LstNode_Set(ListNode *ln, void *datum) LstNode_Set(ListNode *ln, void *datum)
{ {
assert(datum != NULL); assert(datum != NULL);
ln->datum = datum; ln->datum = datum;
} }
/* Replace the datum in the given node with NULL. /*
* Having NULL values in a list is unusual though. */ * Replace the datum in the given node with NULL.
* Having NULL values in a list is unusual though.
*/
void void
LstNode_SetNull(ListNode *ln) LstNode_SetNull(ListNode *ln)
{ {
ln->datum = NULL; ln->datum = NULL;
} }
/* Return the first node that contains the given datum, or NULL. /*
* Return the first node that contains the given datum, or NULL.
* *
* Time complexity: O(length(list)) */ * Time complexity: O(length(list))
*/
ListNode * ListNode *
Lst_FindDatum(List *list, const void *datum) Lst_FindDatum(List *list, const void *datum)
{ {
ListNode *ln; ListNode *ln;
assert(datum != NULL); assert(datum != NULL);
for (ln = list->first; ln != NULL; ln = ln->next) for (ln = list->first; ln != NULL; ln = ln->next)
if (ln->datum == datum) if (ln->datum == datum)
return ln; return ln;
return NULL; return NULL;
} }
int /*
Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData) * Move all nodes from src to the end of dst.
{ * The source list becomes empty but is not freed.
ListNode *ln; */
int result = 0;
for (ln = list->first; ln != NULL; ln = ln->next) {
result = proc(ln->datum, procData);
if (result != 0)
break;
}
return result;
}
/* Move all nodes from src to the end of dst.
* The source list is destroyed and freed. */
void void
Lst_MoveAll(List *dst, List *src) Lst_MoveAll(List *dst, List *src)
{ {
if (src->first != NULL) { if (src->first != NULL) {
src->first->prev = dst->last; src->first->prev = dst->last;
if (dst->last != NULL) if (dst->last != NULL)
dst->last->next = src->first; dst->last->next = src->first;
else else
dst->first = src->first; dst->first = src->first;
dst->last = src->last; dst->last = src->last;
} }
free(src);
} }
/* Copy the element data from src to the start of dst. */ /* Copy the element data from src to the start of dst. */
void void
Lst_PrependAll(List *dst, List *src) Lst_PrependAll(List *dst, List *src)
{ {
ListNode *node; ListNode *ln;
for (node = src->last; node != NULL; node = node->prev)
Lst_Prepend(dst, node->datum); for (ln = src->last; ln != NULL; ln = ln->prev)
Lst_Prepend(dst, ln->datum);
} }
/* Copy the element data from src to the end of dst. */ /* Copy the element data from src to the end of dst. */
void void
Lst_AppendAll(List *dst, List *src) Lst_AppendAll(List *dst, List *src)
{ {
ListNode *node; ListNode *ln;
for (node = src->first; node != NULL; node = node->next)
Lst_Append(dst, node->datum);
}
/* for (ln = src->first; ln != NULL; ln = ln->next)
* for using the list as a queue Lst_Append(dst, ln->datum);
*/
/* Add the datum to the tail of the given list. */
void
Lst_Enqueue(List *list, void *datum)
{
Lst_Append(list, datum);
} }
/* Remove and return the datum at the head of the given list. */ /* Remove and return the datum at the head of the given list. */
void * void *
Lst_Dequeue(List *list) Lst_Dequeue(List *list)
{ {
void *datum = list->first->datum; void *datum = list->first->datum;
Lst_Remove(list, list->first); Lst_Remove(list, list->first);
assert(datum != NULL); /* since NULL would mean end of the list */ assert(datum != NULL); /* since NULL would mean end of the list */
return datum; return datum;
} }
void void
Vector_Init(Vector *v, size_t itemSize) Vector_Init(Vector *v, size_t itemSize)
{ {
v->len = 0; v->len = 0;
v->priv_cap = 10; v->cap = 10;
v->itemSize = itemSize; v->itemSize = itemSize;
v->items = bmake_malloc(v->priv_cap * v->itemSize); v->items = bmake_malloc(v->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. */ * 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 * void *
Vector_Push(Vector *v) Vector_Push(Vector *v)
{ {
if (v->len >= v->priv_cap) { if (v->len >= v->cap) {
v->priv_cap *= 2; v->cap *= 2;
v->items = bmake_realloc(v->items, v->priv_cap * v->itemSize); v->items = bmake_realloc(v->items, v->cap * v->itemSize);
} }
v->len++; v->len++;
return Vector_Get(v, v->len - 1); 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. */ * Return the pointer to the last item in the vector.
* The returned data is valid until the next modifying operation.
*/
void * void *
Vector_Pop(Vector *v) Vector_Pop(Vector *v)
{ {
assert(v->len > 0); assert(v->len > 0);
v->len--; v->len--;
return Vector_Get(v, v->len); return Vector_Get(v, v->len);
}
void
Vector_Done(Vector *v)
{
free(v->items);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: lst.h,v 1.85 2020/11/10 00:32:12 rillig Exp $ */ /* $NetBSD: lst.h,v 1.95 2021/01/03 21:12:03 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -78,48 +78,62 @@
#ifndef MAKE_LST_H #ifndef MAKE_LST_H
#define MAKE_LST_H #define MAKE_LST_H
#include <sys/param.h> #ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#elif defined(HAVE_STDINT_H)
#include <stdint.h> #include <stdint.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h> #include <stdlib.h>
#endif
/* A doubly-linked list of pointers. */ /* A doubly-linked list of pointers. */
typedef struct List List; typedef struct List List;
/* A single node in the doubly-linked list. */ /* A single node in the doubly-linked list. */
typedef struct ListNode ListNode; typedef struct ListNode ListNode;
struct ListNode { struct ListNode {
ListNode *prev; /* previous node in list, or NULL */ ListNode *prev; /* previous node in list, or NULL */
ListNode *next; /* next node in list, or NULL */ ListNode *next; /* next node in list, or NULL */
union {
void *datum; /* datum associated with this element */ 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 { struct List {
ListNode *first; /* first node in list */ ListNode *first;
ListNode *last; /* last node in list */ ListNode *last;
}; };
/* Free the datum of a node, called before freeing the node itself. */ /* Free the datum of a node, called before freeing the node itself. */
typedef void LstFreeProc(void *); typedef void LstFreeProc(void *);
/* An action for Lst_ForEachUntil and Lst_ForEachUntilConcurrent. */
typedef int LstActionUntilProc(void *datum, void *args);
/* Create or destroy a list */ /* Create or destroy a list */
/* Create a new list. */ /* Create a new list. */
List *Lst_New(void); List *Lst_New(void);
/* Free the list nodes, but not the list itself. */
void Lst_Done(List *);
/* Free the list nodes, freeing the node data using the given function. */
void Lst_DoneCall(List *, LstFreeProc);
/* Free the list, leaving the node data unmodified. */ /* Free the list, leaving the node data unmodified. */
void Lst_Free(List *); void Lst_Free(List *);
/* Free the list, freeing the node data using the given function. */ /* Free the list, freeing the node data using the given function. */
void Lst_Destroy(List *, LstFreeProc); void Lst_Destroy(List *, LstFreeProc);
#define LST_INIT { NULL, NULL }
/* Initialize a list, without memory allocation. */
MAKE_INLINE void
Lst_Init(List *list)
{
list->first = NULL;
list->last = NULL;
}
/* Get information about a list */ /* Get information about a list */
MAKE_INLINE Boolean MAKE_INLINE Boolean
Lst_IsEmpty(List *list) { return list->first == NULL; } Lst_IsEmpty(List *list)
{ return list->first == NULL; }
/* Find the first node that contains the given datum, or NULL. */ /* Find the first node that contains the given datum, or NULL. */
ListNode *Lst_FindDatum(List *, const void *); ListNode *Lst_FindDatum(List *, const void *);
@ -145,43 +159,47 @@ void LstNode_Set(ListNode *, void *);
/* Set the value of the node to NULL. Having NULL in a list is unusual. */ /* Set the value of the node to NULL. Having NULL in a list is unusual. */
void LstNode_SetNull(ListNode *); void LstNode_SetNull(ListNode *);
/* Iterating over a list, using a callback function */
/* 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 */ /* Using the list as a queue */
/* Add a datum at the tail of the queue. */ /* Add a datum at the tail of the queue. */
void Lst_Enqueue(List *, void *); MAKE_INLINE void
Lst_Enqueue(List *list, void *datum) {
Lst_Append(list, datum);
}
/* Remove the head node of the queue and return its datum. */ /* Remove the head node of the queue and return its datum. */
void *Lst_Dequeue(List *); void *Lst_Dequeue(List *);
/* A vector is an ordered collection of items, allowing for fast indexed /*
* access. */ * A vector is an ordered collection of items, allowing for fast indexed
* access.
*/
typedef struct Vector { typedef struct Vector {
void *items; /* memory holding the items */ void *items; /* memory holding the items */
size_t itemSize; /* size of a single item in bytes */ size_t itemSize; /* size of a single item */
size_t len; /* number of actually usable elements */ size_t len; /* number of actually usable elements */
size_t priv_cap; /* capacity */ size_t cap; /* capacity */
} Vector; } Vector;
void Vector_Init(Vector *, size_t); 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. */ * Return the pointer to the given item in the vector.
* The returned data is valid until the next modifying operation.
*/
MAKE_INLINE void * MAKE_INLINE void *
Vector_Get(Vector *v, size_t i) Vector_Get(Vector *v, size_t i)
{ {
unsigned char *items = v->items; unsigned char *items = v->items;
return items + i * v->itemSize; return items + i * v->itemSize;
} }
void *Vector_Push(Vector *); void *Vector_Push(Vector *);
void *Vector_Pop(Vector *); void *Vector_Pop(Vector *);
void Vector_Done(Vector *);
MAKE_INLINE void
Vector_Done(Vector *v) {
free(v->items);
}
#endif /* MAKE_LST_H */ #endif /* MAKE_LST_H */

View File

@ -1,4 +1,4 @@
/* $NetBSD: main.c,v 1.476 2020/11/16 22:08:20 rillig Exp $ */ /* $NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -68,7 +68,8 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
/* The main file for this entire program. Exit routines etc. reside here. /*
* The main file for this entire program. Exit routines etc. reside here.
* *
* Utility functions defined in this file: * Utility functions defined in this file:
* *
@ -109,17 +110,13 @@
#include "trace.h" #include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: main.c,v 1.476 2020/11/16 22:08:20 rillig Exp $"); MAKE_RCSID("$NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint) #if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. " "The Regents of the University of California. "
"All rights reserved."); "All rights reserved.");
#endif #endif
#ifndef DEFMAXLOCAL
#define DEFMAXLOCAL DEFMAXJOBS
#endif
#ifndef __arraycount #ifndef __arraycount
# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
#endif #endif
@ -133,7 +130,6 @@ Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */ static int maxJobTokens; /* -j argument */
Boolean enterFlagObj; /* -w and objdir != srcdir */ Boolean enterFlagObj; /* -w and objdir != srcdir */
Boolean preserveUndefined;
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
Boolean doing_depend; /* Set while reading .depend */ Boolean doing_depend; /* Set while reading .depend */
static Boolean jobsRunning; /* TRUE if the jobs might be running */ static Boolean jobsRunning; /* TRUE if the jobs might be running */
@ -144,13 +140,13 @@ static void purge_relative_cached_realpaths(void);
static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ static Boolean ignorePWD; /* if we use -C, PWD is meaningless */
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
char curdir[MAXPATHLEN + 1]; /* Startup directory */ char curdir[MAXPATHLEN + 1]; /* Startup directory */
char *progname; /* the program name */ const char *progname;
char *makeDependfile; char *makeDependfile;
pid_t myPid; pid_t myPid;
int makelevel; int makelevel;
Boolean forceJobs = FALSE; Boolean forceJobs = FALSE;
static int errors = 0; static int main_errors = 0;
static HashTable cached_realpaths; static HashTable cached_realpaths;
/* /*
@ -167,16 +163,16 @@ explode(const char *flags)
if (flags == NULL) if (flags == NULL)
return NULL; return NULL;
for (f = flags; *f; f++) for (f = flags; *f != '\0'; f++)
if (!ch_isalpha(*f)) if (!ch_isalpha(*f))
break; break;
if (*f) if (*f != '\0')
return bmake_strdup(flags); return bmake_strdup(flags);
len = strlen(flags); len = strlen(flags);
st = nf = bmake_malloc(len * 3 + 1); st = nf = bmake_malloc(len * 3 + 1);
while (*flags) { while (*flags != '\0') {
*nf++ = '-'; *nf++ = '-';
*nf++ = *flags++; *nf++ = *flags++;
*nf++ = ' '; *nf++ = ' ';
@ -251,7 +247,7 @@ parse_debug_options(const char *argvalue)
const char *modules; const char *modules;
DebugFlags debug = opts.debug; DebugFlags debug = opts.debug;
for (modules = argvalue; *modules; ++modules) { for (modules = argvalue; *modules != '\0'; ++modules) {
switch (*modules) { switch (*modules) {
case '0': /* undocumented, only intended for tests */ case '0': /* undocumented, only intended for tests */
debug = DEBUG_NONE; debug = DEBUG_NONE;
@ -296,7 +292,7 @@ parse_debug_options(const char *argvalue)
debug |= DEBUG_JOB; debug |= DEBUG_JOB;
break; break;
case 'L': case 'L':
opts.lint = TRUE; opts.strict = TRUE;
break; break;
case 'l': case 'l':
debug |= DEBUG_LOUD; debug |= DEBUG_LOUD;
@ -381,7 +377,7 @@ MainParseArgChdir(const char *argvalue)
if (chdir(argvalue) == -1) { if (chdir(argvalue) == -1) {
(void)fprintf(stderr, "%s: chdir %s: %s\n", (void)fprintf(stderr, "%s: chdir %s: %s\n",
progname, argvalue, strerror(errno)); progname, argvalue, strerror(errno));
exit(1); exit(2); /* Not 1 so -q can distinguish error */
} }
if (getcwd(curdir, MAXPATHLEN) == NULL) { if (getcwd(curdir, MAXPATHLEN) == NULL) {
(void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
@ -434,7 +430,7 @@ MainParseArgJobs(const char *argvalue)
(void)fprintf(stderr, (void)fprintf(stderr,
"%s: illegal argument to -j -- must be positive integer!\n", "%s: illegal argument to -j -- must be positive integer!\n",
progname); progname);
exit(1); /* XXX: why not 2? */ exit(2); /* Not 1 so -q can distinguish error */
} }
Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
@ -504,7 +500,7 @@ MainParseArg(char c, const char *argvalue)
case 'V': case 'V':
case 'v': case 'v':
opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED;
Lst_Append(opts.variables, bmake_strdup(argvalue)); Lst_Append(&opts.variables, bmake_strdup(argvalue));
/* XXX: Why always -V? */ /* XXX: Why always -V? */
Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
@ -532,7 +528,7 @@ MainParseArg(char c, const char *argvalue)
Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
break; break;
case 'f': case 'f':
Lst_Append(opts.makefiles, bmake_strdup(argvalue)); Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
break; break;
case 'i': case 'i':
opts.ignoreErrors = TRUE; opts.ignoreErrors = TRUE;
@ -581,13 +577,15 @@ MainParseArg(char c, const char *argvalue)
return TRUE; return TRUE;
} }
/* Parse the given arguments. Called from main() and from /*
* Parse the given arguments. Called from main() and from
* Main_ParseArgLine() when the .MAKEFLAGS target is used. * Main_ParseArgLine() when the .MAKEFLAGS target is used.
* *
* The arguments must be treated as read-only and will be freed after the * The arguments must be treated as read-only and will be freed after the
* call. * call.
* *
* XXX: Deal with command line overriding .MAKEFLAGS in makefile */ * XXX: Deal with command line overriding .MAKEFLAGS in makefile
*/
static void static void
MainParseArgs(int argc, char **argv) MainParseArgs(int argc, char **argv)
{ {
@ -668,7 +666,7 @@ MainParseArgs(int argc, char **argv)
Punt("illegal (null) argument."); Punt("illegal (null) argument.");
if (argv[1][0] == '-' && !dashDash) if (argv[1][0] == '-' && !dashDash)
goto rearg; goto rearg;
Lst_Append(opts.create, bmake_strdup(argv[1])); Lst_Append(&opts.create, bmake_strdup(argv[1]));
} }
} }
@ -679,10 +677,12 @@ MainParseArgs(int argc, char **argv)
usage(); usage();
} }
/* Break a line of arguments into words and parse them. /*
* Break a line of arguments into words and parse them.
* *
* Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and
* by main() when reading the MAKEFLAGS environment variable. */ * by main() when reading the MAKEFLAGS environment variable.
*/
void void
Main_ParseArgLine(const char *line) Main_ParseArgLine(const char *line)
{ {
@ -691,6 +691,7 @@ Main_ParseArgLine(const char *line)
if (line == NULL) if (line == NULL)
return; return;
/* XXX: don't use line as an iterator variable */
for (; *line == ' '; ++line) for (; *line == ' '; ++line)
continue; continue;
if (line[0] == '\0') if (line[0] == '\0')
@ -711,10 +712,9 @@ Main_ParseArgLine(const char *line)
} }
#endif #endif
{ {
void *freeIt; FStr argv0 = Var_Value(".MAKE", VAR_GLOBAL);
const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &freeIt); buf = str_concat3(argv0.str, " ", line);
buf = str_concat3(argv0, " ", line); FStr_Done(&argv0);
free(freeIt);
} }
words = Str_Words(buf, TRUE); words = Str_Words(buf, TRUE);
@ -772,34 +772,34 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
static Boolean static Boolean
SetVarObjdir(Boolean writable, const char *var, const char *suffix) SetVarObjdir(Boolean writable, const char *var, const char *suffix)
{ {
void *path_freeIt; FStr path = Var_Value(var, VAR_CMDLINE);
const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt); FStr xpath;
const char *xpath;
char *xpath_freeIt;
if (path == NULL || path[0] == '\0') { if (path.str == NULL || path.str[0] == '\0') {
bmake_free(path_freeIt); FStr_Done(&path);
return FALSE; return FALSE;
} }
/* expand variable substitutions */ /* expand variable substitutions */
xpath = path; xpath = FStr_InitRefer(path.str);
xpath_freeIt = NULL; if (strchr(path.str, '$') != 0) {
if (strchr(path, '$') != 0) { char *expanded;
(void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt); (void)Var_Subst(path.str, VAR_GLOBAL, VARE_WANTRES, &expanded);
/* TODO: handle errors */ /* TODO: handle errors */
xpath = xpath_freeIt; xpath = FStr_InitOwn(expanded);
} }
(void)Main_SetObjdir(writable, "%s%s", xpath, suffix); (void)Main_SetObjdir(writable, "%s%s", xpath.str, suffix);
bmake_free(xpath_freeIt); FStr_Done(&xpath);
bmake_free(path_freeIt); FStr_Done(&path);
return TRUE; return TRUE;
} }
/* Splits str into words, adding them to the list. /*
* The string must be kept alive as long as the list. */ * Splits str into words, adding them to the list.
* The string must be kept alive as long as the list.
*/
int int
str2Lst_Append(StringList *lp, char *str) str2Lst_Append(StringList *lp, char *str)
{ {
@ -808,7 +808,7 @@ str2Lst_Append(StringList *lp, char *str)
const char *sep = " \t"; const char *sep = " \t";
for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) {
Lst_Append(lp, cp); Lst_Append(lp, cp);
n++; n++;
} }
@ -831,39 +831,38 @@ siginfo(int signo MAKE_ATTR_UNUSED)
} }
#endif #endif
/* /* Allow makefiles some control over the mode we run in. */
* Allow makefiles some control over the mode we run in. static void
*/ MakeMode(void)
void
MakeMode(const char *mode)
{ {
char *mode_freeIt = NULL; FStr mode = FStr_InitRefer(NULL);
if (mode == NULL) { if (mode.str == NULL) {
char *expanded;
(void)Var_Subst("${" MAKE_MODE ":tl}", (void)Var_Subst("${" MAKE_MODE ":tl}",
VAR_GLOBAL, VARE_WANTRES, &mode_freeIt); VAR_GLOBAL, VARE_WANTRES, &expanded);
/* TODO: handle errors */ /* TODO: handle errors */
mode = mode_freeIt; mode = FStr_InitOwn(expanded);
} }
if (mode[0] != '\0') { if (mode.str[0] != '\0') {
if (strstr(mode, "compat")) { if (strstr(mode.str, "compat") != NULL) {
opts.compatMake = TRUE; opts.compatMake = TRUE;
forceJobs = FALSE; forceJobs = FALSE;
} }
#if USE_META #if USE_META
if (strstr(mode, "meta")) if (strstr(mode.str, "meta") != NULL)
meta_mode_init(mode); meta_mode_init(mode.str);
#endif #endif
} }
free(mode_freeIt); FStr_Done(&mode);
} }
static void static void
PrintVar(const char *varname, Boolean expandVars) PrintVar(const char *varname, Boolean expandVars)
{ {
if (strchr(varname, '$')) { if (strchr(varname, '$') != NULL) {
char *evalue; char *evalue;
(void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue); (void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue);
/* TODO: handle errors */ /* TODO: handle errors */
@ -880,10 +879,9 @@ PrintVar(const char *varname, Boolean expandVars)
bmake_free(evalue); bmake_free(evalue);
} else { } else {
void *freeIt; FStr value = Var_Value(varname, VAR_GLOBAL);
const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt); printf("%s\n", value.str != NULL ? value.str : "");
printf("%s\n", value ? value : ""); FStr_Done(&value);
bmake_free(freeIt);
} }
} }
@ -922,7 +920,7 @@ doPrintVars(void)
else else
expandVars = GetBooleanVar(".MAKE.EXPAND_VARIABLES", FALSE); expandVars = GetBooleanVar(".MAKE.EXPAND_VARIABLES", FALSE);
for (ln = opts.variables->first; ln != NULL; ln = ln->next) { for (ln = opts.variables.first; ln != NULL; ln = ln->next) {
const char *varname = ln->datum; const char *varname = ln->datum;
PrintVar(varname, expandVars); PrintVar(varname, expandVars);
} }
@ -931,7 +929,7 @@ doPrintVars(void)
static Boolean static Boolean
runTargets(void) runTargets(void)
{ {
GNodeList *targs; /* target nodes to create */ GNodeList targs = LST_INIT; /* target nodes to create */
Boolean outOfDate; /* FALSE if all targets up to date */ Boolean outOfDate; /* FALSE if all targets up to date */
/* /*
@ -940,10 +938,10 @@ runTargets(void)
* we consult the parsing module to find the main target(s) * we consult the parsing module to find the main target(s)
* to create. * to create.
*/ */
if (Lst_IsEmpty(opts.create)) if (Lst_IsEmpty(&opts.create))
targs = Parse_MainName(); Parse_MainName(&targs);
else else
targs = Targ_FindList(opts.create); Targ_FindList(&targs, &opts.create);
if (!opts.compatMake) { if (!opts.compatMake) {
/* /*
@ -959,16 +957,16 @@ runTargets(void)
} }
/* Traverse the graph, checking on all the targets */ /* Traverse the graph, checking on all the targets */
outOfDate = Make_Run(targs); outOfDate = Make_Run(&targs);
} else { } else {
/* /*
* Compat_Init will take care of creating all the * Compat_Init will take care of creating all the
* targets as well as initializing the module. * targets as well as initializing the module.
*/ */
Compat_Run(targs); Compat_Run(&targs);
outOfDate = FALSE; outOfDate = FALSE;
} }
Lst_Free(targs); Lst_Done(&targs); /* Don't free the nodes. */
return outOfDate; return outOfDate;
} }
@ -982,12 +980,12 @@ InitVarTargets(void)
{ {
StringListNode *ln; StringListNode *ln;
if (Lst_IsEmpty(opts.create)) { if (Lst_IsEmpty(&opts.create)) {
Var_Set(".TARGETS", "", VAR_GLOBAL); Var_Set(".TARGETS", "", VAR_GLOBAL);
return; return;
} }
for (ln = opts.create->first; ln != NULL; ln = ln->next) { for (ln = opts.create.first; ln != NULL; ln = ln->next) {
char *name = ln->datum; char *name = ln->datum;
Var_Append(".TARGETS", name, VAR_GLOBAL); Var_Append(".TARGETS", name, VAR_GLOBAL);
} }
@ -1040,11 +1038,11 @@ InitVarMachineArch(void)
const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
size_t len = sizeof machine_arch_buf; size_t len = sizeof machine_arch_buf;
if (sysctl(mib, __arraycount(mib), machine_arch_buf, if (sysctl(mib, (unsigned int)__arraycount(mib),
&len, NULL, 0) < 0) { machine_arch_buf, &len, NULL, 0) < 0) {
(void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, (void)fprintf(stderr, "%s: sysctl failed (%s).\n",
strerror(errno)); progname, strerror(errno));
exit(2); exit(2);
} }
return machine_arch_buf; return machine_arch_buf;
@ -1077,21 +1075,20 @@ static void
HandlePWD(const struct stat *curdir_st) HandlePWD(const struct stat *curdir_st)
{ {
char *pwd; char *pwd;
void *prefix_freeIt, *makeobjdir_freeIt; FStr prefix, makeobjdir;
const char *makeobjdir;
struct stat pwd_st; struct stat pwd_st;
if (ignorePWD || (pwd = getenv("PWD")) == NULL) if (ignorePWD || (pwd = getenv("PWD")) == NULL)
return; return;
if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) != prefix = Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE);
NULL) { if (prefix.str != NULL) {
bmake_free(prefix_freeIt); FStr_Done(&prefix);
return; return;
} }
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt); makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE);
if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL) if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL)
goto ignore_pwd; goto ignore_pwd;
if (stat(pwd, &pwd_st) == 0 && if (stat(pwd, &pwd_st) == 0 &&
@ -1100,7 +1097,7 @@ HandlePWD(const struct stat *curdir_st)
(void)strncpy(curdir, pwd, MAXPATHLEN); (void)strncpy(curdir, pwd, MAXPATHLEN);
ignore_pwd: ignore_pwd:
bmake_free(makeobjdir_freeIt); FStr_Done(&makeobjdir);
} }
#endif #endif
@ -1118,7 +1115,7 @@ InitObjdir(const char *machine, const char *machine_arch)
{ {
Boolean writable; Boolean writable;
Dir_InitDir(curdir); Dir_InitCur(curdir);
writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE); writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE);
(void)Main_SetObjdir(FALSE, "%s", curdir); (void)Main_SetObjdir(FALSE, "%s", curdir);
@ -1147,35 +1144,37 @@ UnlimitFiles(void)
static void static void
CmdOpts_Init(void) CmdOpts_Init(void)
{ {
opts.compatMake = FALSE; /* No compat mode */ opts.compatMake = FALSE;
opts.debug = 0; /* No debug verbosity, please. */ opts.debug = DEBUG_NONE;
/* opts.debug_file has been initialized earlier */ /* opts.debug_file has been initialized earlier */
opts.lint = FALSE; opts.strict = FALSE;
opts.debugVflag = FALSE; opts.debugVflag = FALSE;
opts.checkEnvFirst = FALSE; opts.checkEnvFirst = FALSE;
opts.makefiles = Lst_New(); Lst_Init(&opts.makefiles);
opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */ opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */
opts.maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ opts.maxJobs = 1;
opts.keepgoing = FALSE; /* Stop on error */ opts.keepgoing = FALSE; /* Stop on error */
opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
opts.noExecute = FALSE; /* Execute all commands */ opts.noExecute = FALSE; /* Execute all commands */
opts.queryFlag = FALSE; /* This is not just a check-run */ opts.queryFlag = FALSE;
opts.noBuiltins = FALSE; /* Read the built-in rules */ opts.noBuiltins = FALSE; /* Read the built-in rules */
opts.beSilent = FALSE; /* Print commands as executed */ opts.beSilent = FALSE; /* Print commands as executed */
opts.touchFlag = FALSE; /* Actually update targets */ opts.touchFlag = FALSE;
opts.printVars = PVM_NONE; opts.printVars = PVM_NONE;
opts.variables = Lst_New(); Lst_Init(&opts.variables);
opts.parseWarnFatal = FALSE; opts.parseWarnFatal = FALSE;
opts.enterFlag = FALSE; opts.enterFlag = FALSE;
opts.varNoExportEnv = FALSE; opts.varNoExportEnv = FALSE;
opts.create = Lst_New(); Lst_Init(&opts.create);
} }
/* Initialize MAKE and .MAKE to the path of the executable, so that it can be /*
* Initialize MAKE and .MAKE to the path of the executable, so that it can be
* found by execvp(3) and the shells, even after a chdir. * found by execvp(3) and the shells, even after a chdir.
* *
* If it's a relative path and contains a '/', resolve it to an absolute path. * If it's a relative path and contains a '/', resolve it to an absolute path.
* Otherwise keep it as is, assuming it will be found in the PATH. */ * Otherwise keep it as is, assuming it will be found in the PATH.
*/
static void static void
InitVarMake(const char *argv0) InitVarMake(const char *argv0)
{ {
@ -1183,18 +1182,21 @@ InitVarMake(const char *argv0)
if (argv0[0] != '/' && strchr(argv0, '/') != NULL) { if (argv0[0] != '/' && strchr(argv0, '/') != NULL) {
char pathbuf[MAXPATHLEN]; char pathbuf[MAXPATHLEN];
const char *abs = cached_realpath(argv0, pathbuf); const char *abspath = cached_realpath(argv0, pathbuf);
struct stat st; struct stat st;
if (abs != NULL && abs[0] == '/' && stat(make, &st) == 0) if (abspath != NULL && abspath[0] == '/' &&
make = abs; stat(make, &st) == 0)
make = abspath;
} }
Var_Set("MAKE", make, VAR_GLOBAL); Var_Set("MAKE", make, VAR_GLOBAL);
Var_Set(".MAKE", make, VAR_GLOBAL); Var_Set(".MAKE", make, VAR_GLOBAL);
} }
/* Add the directories from the colon-separated syspath to defSysIncPath. /*
* After returning, the contents of syspath is unspecified. */ * Add the directories from the colon-separated syspath to defSysIncPath.
* After returning, the contents of syspath is unspecified.
*/
static void static void
InitDefSysIncPath(char *syspath) InitDefSysIncPath(char *syspath)
{ {
@ -1237,25 +1239,25 @@ static void
ReadBuiltinRules(void) ReadBuiltinRules(void)
{ {
StringListNode *ln; StringListNode *ln;
StringList *sysMkPath = Lst_New(); StringList sysMkPath = LST_INIT;
Dir_Expand(_PATH_DEFSYSMK, Dir_Expand(_PATH_DEFSYSMK,
Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath, Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath,
sysMkPath); &sysMkPath);
if (Lst_IsEmpty(sysMkPath)) if (Lst_IsEmpty(&sysMkPath))
Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK);
for (ln = sysMkPath->first; ln != NULL; ln = ln->next) for (ln = sysMkPath.first; ln != NULL; ln = ln->next)
if (ReadMakefile(ln->datum) == 0) if (ReadMakefile(ln->datum) == 0)
break; break;
if (ln == NULL) if (ln == NULL)
Fatal("%s: cannot open %s.", Fatal("%s: cannot open %s.",
progname, (const char *)sysMkPath->first->datum); progname, (const char *)sysMkPath.first->datum);
/* Free the list but not the actual filenames since these may still /* Free the list but not the actual filenames since these may still
* be used in GNodes. */ * be used in GNodes. */
Lst_Free(sysMkPath); Lst_Done(&sysMkPath);
} }
static void static void
@ -1276,7 +1278,7 @@ InitMaxJobs(void)
"%s: illegal value for .MAKE.JOBS " "%s: illegal value for .MAKE.JOBS "
"-- must be positive integer!\n", "-- must be positive integer!\n",
progname); progname);
exit(1); exit(2); /* Not 1 so -q can distinguish error */
} }
if (n != opts.maxJobs) { if (n != opts.maxJobs) {
@ -1315,7 +1317,7 @@ InitVpath(void)
savec = *cp; savec = *cp;
*cp = '\0'; *cp = '\0';
/* Add directory to search path */ /* Add directory to search path */
(void)Dir_AddDir(dirSearchPath, path); (void)Dir_AddDir(&dirSearchPath, path);
*cp = savec; *cp = savec;
path = cp + 1; path = cp + 1;
} while (savec == ':'); } while (savec == ':');
@ -1348,18 +1350,20 @@ ReadFirstDefaultMakefile(void)
* since these makefiles do not come from the command line. They * since these makefiles do not come from the command line. They
* also have different semantics in that only the first file that * also have different semantics in that only the first file that
* is found is processed. See ReadAllMakefiles. */ * is found is processed. See ReadAllMakefiles. */
(void)str2Lst_Append(opts.makefiles, prefs); (void)str2Lst_Append(&opts.makefiles, prefs);
for (ln = opts.makefiles->first; ln != NULL; ln = ln->next) for (ln = opts.makefiles.first; ln != NULL; ln = ln->next)
if (ReadMakefile(ln->datum) == 0) if (ReadMakefile(ln->datum) == 0)
break; break;
free(prefs); free(prefs);
} }
/* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. /*
* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS.
* Initialize a few modules. * Initialize a few modules.
* Parse the arguments from MAKEFLAGS and the command line. */ * Parse the arguments from MAKEFLAGS and the command line.
*/
static void static void
main_Init(int argc, char **argv) main_Init(int argc, char **argv)
{ {
@ -1380,10 +1384,7 @@ main_Init(int argc, char **argv)
InitRandom(); InitRandom();
if ((progname = strrchr(argv[0], '/')) != NULL) progname = str_basename(argv[0]);
progname++;
else
progname = argv[0];
UnlimitFiles(); UnlimitFiles();
@ -1469,6 +1470,10 @@ main_Init(int argc, char **argv)
Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); Var_Set(".MAKE.PID", tmp, VAR_GLOBAL);
snprintf(tmp, sizeof tmp, "%u", getppid()); snprintf(tmp, sizeof tmp, "%u", getppid());
Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL);
snprintf(tmp, sizeof tmp, "%u", getuid());
Var_Set(".MAKE.UID", tmp, VAR_GLOBAL);
snprintf(tmp, sizeof tmp, "%u", getgid());
Var_Set(".MAKE.GID", tmp, VAR_GLOBAL);
} }
if (makelevel > 0) { if (makelevel > 0) {
char pn[1024]; char pn[1024];
@ -1545,8 +1550,10 @@ main_Init(int argc, char **argv)
InitDefSysIncPath(syspath); InitDefSysIncPath(syspath);
} }
/* Read the system makefile followed by either makefile, Makefile or the /*
* files given by the -f option. Exit on parse errors. */ * Read the system makefile followed by either makefile, Makefile or the
* files given by the -f option. Exit on parse errors.
*/
static void static void
main_ReadFiles(void) main_ReadFiles(void)
{ {
@ -1554,8 +1561,8 @@ main_ReadFiles(void)
if (!opts.noBuiltins) if (!opts.noBuiltins)
ReadBuiltinRules(); ReadBuiltinRules();
if (!Lst_IsEmpty(opts.makefiles)) if (!Lst_IsEmpty(&opts.makefiles))
ReadAllMakefiles(opts.makefiles); ReadAllMakefiles(&opts.makefiles);
else else
ReadFirstDefaultMakefile(); ReadFirstDefaultMakefile();
} }
@ -1566,8 +1573,7 @@ main_PrepareMaking(void)
{ {
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
if (!opts.noBuiltins || opts.printVars == PVM_NONE) { if (!opts.noBuiltins || opts.printVars == PVM_NONE) {
/* ignore /dev/null and anything starting with "no" */ (void)Var_Subst("${.MAKE.DEPENDFILE}",
(void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}",
VAR_CMDLINE, VARE_WANTRES, &makeDependfile); VAR_CMDLINE, VARE_WANTRES, &makeDependfile);
if (makeDependfile[0] != '\0') { if (makeDependfile[0] != '\0') {
/* TODO: handle errors */ /* TODO: handle errors */
@ -1580,13 +1586,12 @@ main_PrepareMaking(void)
if (enterFlagObj) if (enterFlagObj)
printf("%s: Entering directory `%s'\n", progname, objdir); printf("%s: Entering directory `%s'\n", progname, objdir);
MakeMode(NULL); MakeMode();
{ {
void *freeIt; FStr makeflags = Var_Value(MAKEFLAGS, VAR_GLOBAL);
Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt), Var_Append("MFLAGS", makeflags.str, VAR_GLOBAL);
VAR_GLOBAL); FStr_Done(&makeflags);
bmake_free(freeIt);
} }
InitMaxJobs(); InitMaxJobs();
@ -1624,9 +1629,11 @@ main_PrepareMaking(void)
Targ_PrintGraph(1); Targ_PrintGraph(1);
} }
/* Make the targets. /*
* Make the targets.
* If the -v or -V options are given, print variables instead. * If the -v or -V options are given, print variables instead.
* Return whether any of the targets is out-of-date. */ * Return whether any of the targets is out-of-date.
*/
static Boolean static Boolean
main_Run(void) main_Run(void)
{ {
@ -1644,9 +1651,13 @@ static void
main_CleanUp(void) main_CleanUp(void)
{ {
#ifdef CLEANUP #ifdef CLEANUP
Lst_Destroy(opts.variables, free); Lst_DoneCall(&opts.variables, free);
Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */ /*
Lst_Destroy(opts.create, free); * Don't free the actual strings from opts.makefiles, they may be
* used in GNodes.
*/
Lst_Done(&opts.makefiles);
Lst_DoneCall(&opts.create, free);
#endif #endif
/* print the graph now it's been processed if the user requested it */ /* print the graph now it's been processed if the user requested it */
@ -1677,7 +1688,7 @@ main_CleanUp(void)
static int static int
main_Exit(Boolean outOfDate) main_Exit(Boolean outOfDate)
{ {
if (opts.lint && (errors > 0 || Parse_GetFatals() > 0)) if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0))
return 2; /* Not 1 so -q can distinguish error */ return 2; /* Not 1 so -q can distinguish error */
return outOfDate ? 1 : 0; return outOfDate ? 1 : 0;
} }
@ -1695,7 +1706,8 @@ main(int argc, char **argv)
return main_Exit(outOfDate); return main_Exit(outOfDate);
} }
/* Open and parse the given makefile, with all its side effects. /*
* Open and parse the given makefile, with all its side effects.
* *
* Results: * Results:
* 0 if ok. -1 if couldn't open file. * 0 if ok. -1 if couldn't open file.
@ -1776,7 +1788,7 @@ char *
Cmd_Exec(const char *cmd, const char **errfmt) Cmd_Exec(const char *cmd, const char **errfmt)
{ {
const char *args[4]; /* Args for invoking the shell */ const char *args[4]; /* Args for invoking the shell */
int fds[2]; /* Pipe streams */ int pipefds[2];
int cpid; /* Child PID */ int cpid; /* Child PID */
int pid; /* PID from wait() */ int pid; /* PID from wait() */
int status; /* command exit status */ int status; /* command exit status */
@ -1789,7 +1801,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
*errfmt = NULL; *errfmt = NULL;
if (!shellName) if (shellName == NULL)
Shell_Init(); Shell_Init();
/* /*
* Set up arguments for shell * Set up arguments for shell
@ -1802,27 +1814,27 @@ Cmd_Exec(const char *cmd, const char **errfmt)
/* /*
* Open a pipe for fetching its output * Open a pipe for fetching its output
*/ */
if (pipe(fds) == -1) { if (pipe(pipefds) == -1) {
*errfmt = "Couldn't create pipe for \"%s\""; *errfmt = "Couldn't create pipe for \"%s\"";
goto bad; goto bad;
} }
Var_ReexportVars();
/* /*
* Fork * Fork
*/ */
switch (cpid = vFork()) { switch (cpid = vFork()) {
case 0: case 0:
(void)close(fds[0]); /* Close input side of pipe */ (void)close(pipefds[0]); /* Close input side of pipe */
/* /*
* Duplicate the output stream to the shell's output, then * Duplicate the output stream to the shell's output, then
* shut the extra thing down. Note we don't fetch the error * shut the extra thing down. Note we don't fetch the error
* stream...why not? Why? * stream...why not? Why?
*/ */
(void)dup2(fds[1], 1); (void)dup2(pipefds[1], 1);
(void)close(fds[1]); (void)close(pipefds[1]);
Var_ExportVars();
(void)execv(shellPath, UNCONST(args)); (void)execv(shellPath, UNCONST(args));
_exit(1); _exit(1);
@ -1833,14 +1845,14 @@ Cmd_Exec(const char *cmd, const char **errfmt)
goto bad; goto bad;
default: default:
(void)close(fds[1]); /* No need for the writing half */ (void)close(pipefds[1]); /* No need for the writing half */
savederr = 0; savederr = 0;
Buf_Init(&buf); Buf_Init(&buf);
do { do {
char result[BUFSIZ]; char result[BUFSIZ];
bytes_read = read(fds[0], result, sizeof result); bytes_read = read(pipefds[0], result, sizeof result);
if (bytes_read > 0) if (bytes_read > 0)
Buf_AddBytes(&buf, result, (size_t)bytes_read); Buf_AddBytes(&buf, result, (size_t)bytes_read);
} while (bytes_read > 0 || } while (bytes_read > 0 ||
@ -1848,8 +1860,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
if (bytes_read == -1) if (bytes_read == -1)
savederr = errno; savederr = errno;
(void)close( (void)close(pipefds[0]); /* Close the input side of the pipe. */
fds[0]); /* Close the input side of the pipe. */
/* Wait for the process to exit. */ /* Wait for the process to exit. */
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
@ -1879,10 +1890,12 @@ Cmd_Exec(const char *cmd, const char **errfmt)
return bmake_strdup(""); return bmake_strdup("");
} }
/* Print a printf-style error message. /*
* Print a printf-style error message.
* *
* In default mode, this error message has no consequences, in particular it * In default mode, this error message has no consequences, in particular it
* does not affect the exit status. Only in lint mode (-dL) it does. */ * does not affect the exit status. Only in lint mode (-dL) it does.
*/
void void
Error(const char *fmt, ...) Error(const char *fmt, ...)
{ {
@ -1904,14 +1917,16 @@ Error(const char *fmt, ...)
break; break;
err_file = stderr; err_file = stderr;
} }
errors++; main_errors++;
} }
/* Wait for any running jobs to finish, then produce an error message, /*
* Wait for any running jobs to finish, then produce an error message,
* finally exit immediately. * finally exit immediately.
* *
* Exiting immediately differs from Parse_Error, which exits only after the * Exiting immediately differs from Parse_Error, which exits only after the
* current top-level makefile has been parsed completely. */ * current top-level makefile has been parsed completely.
*/
void void
Fatal(const char *fmt, ...) Fatal(const char *fmt, ...)
{ {
@ -1935,8 +1950,10 @@ Fatal(const char *fmt, ...)
exit(2); /* Not 1 so -q can distinguish error */ exit(2); /* Not 1 so -q can distinguish error */
} }
/* Major exception once jobs are being created. /*
* Kills all jobs, prints a message and exits. */ * Major exception once jobs are being created.
* Kills all jobs, prints a message and exits.
*/
void void
Punt(const char *fmt, ...) Punt(const char *fmt, ...)
{ {
@ -1964,12 +1981,14 @@ DieHorribly(void)
if (DEBUG(GRAPH2)) if (DEBUG(GRAPH2))
Targ_PrintGraph(2); Targ_PrintGraph(2);
Trace_Log(MAKEERROR, NULL); Trace_Log(MAKEERROR, NULL);
exit(2); /* Not 1, so -q can distinguish error */ exit(2); /* Not 1 so -q can distinguish error */
} }
/* Called when aborting due to errors in child shell to signal abnormal exit. /*
* Called when aborting due to errors in child shell to signal abnormal exit.
* The program exits. * The program exits.
* Errors is the number of errors encountered in Make_Make. */ * Errors is the number of errors encountered in Make_Make.
*/
void void
Finish(int errs) Finish(int errs)
{ {
@ -2101,7 +2120,7 @@ shouldDieQuietly(GNode *gn, int bf)
else if (bf >= 0) else if (bf >= 0)
quietly = bf; quietly = bf;
else else
quietly = gn != NULL && (gn->type & OP_MAKE); quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0;
} }
return quietly; return quietly;
} }
@ -2117,7 +2136,7 @@ SetErrorVars(GNode *gn)
Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL);
Var_Delete(".ERROR_CMD", VAR_GLOBAL); Var_Delete(".ERROR_CMD", VAR_GLOBAL);
for (ln = gn->commands->first; ln != NULL; ln = ln->next) { for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum; const char *cmd = ln->datum;
if (cmd == NULL) if (cmd == NULL)
@ -2126,8 +2145,10 @@ SetErrorVars(GNode *gn)
} }
} }
/* Print some helpful information in case of an error. /*
* The caller should exit soon after calling this function. */ * Print some helpful information in case of an error.
* The caller should exit soon after calling this function.
*/
void void
PrintOnError(GNode *gn, const char *msg) PrintOnError(GNode *gn, const char *msg)
{ {
@ -2138,16 +2159,16 @@ PrintOnError(GNode *gn, const char *msg)
Var_Stats(); Var_Stats();
} }
/* we generally want to keep quiet if a sub-make died */ if (errorNode != NULL)
if (shouldDieQuietly(gn, -1)) return; /* we've been here! */
return;
if (msg != NULL) if (msg != NULL)
printf("%s", msg); printf("%s", msg);
printf("\n%s: stopped in %s\n", progname, curdir); printf("\n%s: stopped in %s\n", progname, curdir);
if (errorNode != NULL) /* we generally want to keep quiet if a sub-make died */
return; /* we've been here! */ if (shouldDieQuietly(gn, -1))
return;
if (gn != NULL) if (gn != NULL)
SetErrorVars(gn); SetErrorVars(gn);
@ -2241,11 +2262,10 @@ mkTempFile(const char *pattern, char **out_fname)
if ((fd = mkstemp(tfile)) < 0) if ((fd = mkstemp(tfile)) < 0)
Punt("Could not create temporary file %s: %s", tfile, Punt("Could not create temporary file %s: %s", tfile,
strerror(errno)); strerror(errno));
if (out_fname) { if (out_fname != NULL) {
*out_fname = bmake_strdup(tfile); *out_fname = bmake_strdup(tfile);
} else { } else {
unlink( unlink(tfile); /* we just want the descriptor */
tfile); /* we just want the descriptor */
} }
return fd; return fd;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: config.h,v 1.25 2020/10/19 23:43:55 rillig Exp $ */ /* $NetBSD: config.h,v 1.28 2020/12/11 22:53:08 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@ -72,19 +72,6 @@
* from: @(#)config.h 8.1 (Berkeley) 6/6/93 * from: @(#)config.h 8.1 (Berkeley) 6/6/93
*/ */
/*
* DEFMAXJOBS
* DEFMAXLOCAL
* These control the default concurrency. On no occasion will more
* than DEFMAXJOBS targets be created at once (locally or remotely).
*
* DEFMAXLOCAL is the highest number of targets which will be
* created on the local machine at once. Note that if you set this
* to 0, nothing will ever happen.
*/
#define DEFMAXJOBS 4
#define DEFMAXLOCAL 1
/* /*
* INCLUDES * INCLUDES
* LIBRARIES * LIBRARIES
@ -104,7 +91,7 @@
* Is the suffix used to denote libraries and is used by the Suff module * Is the suffix used to denote libraries and is used by the Suff module
* to find the search path on which to seek any -l<xx> targets. * to find the search path on which to seek any -l<xx> targets.
*/ */
#define LIBSUFF ".a" #define LIBSUFF ".a"
/* /*
* RECHECK * RECHECK
@ -119,14 +106,13 @@
* On systems that don't have this problem, you should define this. * On systems that don't have this problem, you should define this.
* Under NFS you probably should not, unless you aren't exporting jobs. * Under NFS you probably should not, unless you aren't exporting jobs.
*/ */
#define RECHECK #define RECHECK
/* /*
* POSIX * POSIX
* Adhere to the POSIX 1003.2 draft for the make(1) program. * Adhere to the POSIX 1003.2 draft for the make(1) program.
* - Use MAKEFLAGS instead of MAKE to pick arguments from the * - Use MAKEFLAGS instead of MAKE to pick arguments from the
* environment. * environment.
* - Allow empty command lines if starting with tab.
*/ */
#define POSIX #define POSIX

View File

@ -1,4 +1,4 @@
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig Exp $ .\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $
.\" .\"
.\" Copyright (c) 1990, 1993 .\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -29,7 +29,7 @@
.\" .\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\" .\"
.Dd November 14, 2020 .Dd December 22, 2020
.Dt MAKE 1 .Dt MAKE 1
.Os .Os
.Sh NAME .Sh NAME
@ -1030,6 +1030,12 @@ If set to false,
becomes becomes
.Ql $ .Ql $
per normal evaluation rules. per normal evaluation rules.
.It Va .MAKE.UID
The user-id running
.Nm .
.It Va .MAKE.GID
The group-id running
.Nm .
.It Va MAKE_PRINT_VAR_ON_ERROR .It Va MAKE_PRINT_VAR_ON_ERROR
When When
.Nm .Nm
@ -1108,7 +1114,7 @@ to that directory before executing any targets.
.Pp .Pp
Except in the case of an explicit Except in the case of an explicit
.Ql Ic .OBJDIR .Ql Ic .OBJDIR
target, target,
.Nm .Nm
will check that the specified directory is writable and ignore it if not. will check that the specified directory is writable and ignore it if not.
This check can be skipped by setting the environment variable This check can be skipped by setting the environment variable
@ -1754,9 +1760,9 @@ The same as
except that variables in the value are not expanded. except that variables in the value are not expanded.
.It Ic .info Ar message .It Ic .info Ar message
The message is printed along with the name of the makefile and line number. The message is printed along with the name of the makefile and line number.
.It Ic .undef Ar variable .It Ic .undef Ar variable ...
Un-define the specified global variable. Un-define the specified global variables.
Only global variables may be un-defined. Only global variables can be un-defined.
.It Ic .unexport Ar variable ... .It Ic .unexport Ar variable ...
The opposite of The opposite of
.Ql .export . .Ql .export .

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: make.h,v 1.210 2020/11/16 21:53:10 rillig Exp $ */ /* $NetBSD: make.h,v 1.242 2021/01/10 21:20:46 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -107,25 +107,25 @@
#endif #endif
#if defined(__GNUC__) #if defined(__GNUC__)
#define MAKE_GNUC_PREREQ(x, y) \ #define MAKE_GNUC_PREREQ(x, y) \
((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
(__GNUC__ > (x))) (__GNUC__ > (x)))
#else /* defined(__GNUC__) */ #else /* defined(__GNUC__) */
#define MAKE_GNUC_PREREQ(x, y) 0 #define MAKE_GNUC_PREREQ(x, y) 0
#endif /* defined(__GNUC__) */ #endif /* defined(__GNUC__) */
#if MAKE_GNUC_PREREQ(2, 7) #if MAKE_GNUC_PREREQ(2, 7)
#define MAKE_ATTR_UNUSED __attribute__((__unused__)) #define MAKE_ATTR_UNUSED __attribute__((__unused__))
#else #else
#define MAKE_ATTR_UNUSED /* delete */ #define MAKE_ATTR_UNUSED /* delete */
#endif #endif
#if MAKE_GNUC_PREREQ(2, 5) #if MAKE_GNUC_PREREQ(2, 5)
#define MAKE_ATTR_DEAD __attribute__((__noreturn__)) #define MAKE_ATTR_DEAD __attribute__((__noreturn__))
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define MAKE_ATTR_DEAD __volatile #define MAKE_ATTR_DEAD __volatile
#else #else
#define MAKE_ATTR_DEAD /* delete */ #define MAKE_ATTR_DEAD /* delete */
#endif #endif
#if MAKE_GNUC_PREREQ(2, 7) #if MAKE_GNUC_PREREQ(2, 7)
@ -141,21 +141,29 @@
* A boolean type is defined as an integer, not an enum, for historic reasons. * A boolean type is defined as an integer, not an enum, for historic reasons.
* The only allowed values are the constants TRUE and FALSE (1 and 0). * The only allowed values are the constants TRUE and FALSE (1 and 0).
*/ */
#if defined(lint) || defined(USE_C99_BOOLEAN)
#ifdef USE_DOUBLE_BOOLEAN #include <stdbool.h>
typedef bool Boolean;
#define FALSE false
#define TRUE true
#elif defined(USE_DOUBLE_BOOLEAN)
/* During development, to find type mismatches in function declarations. */ /* During development, to find type mismatches in function declarations. */
typedef double Boolean; typedef double Boolean;
#define TRUE 1.0 #define TRUE 1.0
#define FALSE 0.0 #define FALSE 0.0
#elif defined(USE_UCHAR_BOOLEAN) #elif defined(USE_UCHAR_BOOLEAN)
/* During development, to find code that depends on the exact value of TRUE or /*
* that stores other values in Boolean variables. */ * 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; typedef unsigned char Boolean;
#define TRUE ((unsigned char)0xFF) #define TRUE ((unsigned char)0xFF)
#define FALSE ((unsigned char)0x00) #define FALSE ((unsigned char)0x00)
#elif defined(USE_CHAR_BOOLEAN) #elif defined(USE_CHAR_BOOLEAN)
/* During development, to find code that uses a boolean as array index, via /*
* -Wchar-subscripts. */ * During development, to find code that uses a boolean as array index, via
* -Wchar-subscripts.
*/
typedef char Boolean; typedef char Boolean;
#define TRUE ((char)-1) #define TRUE ((char)-1)
#define FALSE ((char)0x00) #define FALSE ((char)0x00)
@ -186,134 +194,181 @@ typedef int Boolean;
#endif #endif
#if defined(sun) && (defined(__svr4__) || defined(__SVR4)) #if defined(sun) && (defined(__svr4__) || defined(__SVR4))
#define POSIX_SIGNALS # define POSIX_SIGNALS
#endif #endif
/*
* The typical flow of states is:
*
* The direct successful path:
* UNMADE -> BEINGMADE -> MADE.
*
* The direct error path:
* UNMADE -> BEINGMADE -> ERROR.
*
* The successful path when dependencies need to be made first:
* UNMADE -> DEFERRED -> REQUESTED -> BEINGMADE -> MADE.
*
* A node that has dependencies, and one of the dependencies cannot be made:
* UNMADE -> DEFERRED -> ABORTED.
*
* A node that turns out to be up-to-date:
* UNMADE -> BEINGMADE -> UPTODATE.
*/
typedef enum GNodeMade { typedef enum GNodeMade {
UNMADE, /* Not examined yet */ /* Not examined yet. */
DEFERRED, /* Examined once (building child) */ UNMADE,
REQUESTED, /* on toBeMade list */ /* The node has been examined but is not yet ready since its
BEINGMADE, /* Target is already being made. * dependencies have to be made first. */
* Indicates a cycle in the graph. */ DEFERRED,
MADE, /* Was out-of-date and has been made */
UPTODATE, /* Was already up-to-date */ /* The node is on the toBeMade list. */
ERROR, /* An error occurred while it was being REQUESTED,
* made (used only in compat mode) */
ABORTED /* The target was aborted due to an error /* The node is already being made. Trying to build a node in this
* making an inferior (compat). */ * state indicates a cycle in the graph. */
BEINGMADE,
/* Was out-of-date and has been made. */
MADE,
/* Was already up-to-date, does not need to be made. */
UPTODATE,
/* An error occurred while it was being made.
* Used only in compat mode. */
ERROR,
/* The target was aborted due to an error making a dependency.
* Used only in compat mode. */
ABORTED
} GNodeMade; } GNodeMade;
/* The OP_ constants are used when parsing a dependency line as a way of /*
* The OP_ constants are used when parsing a dependency line as a way of
* communicating to other parts of the program the way in which a target * communicating to other parts of the program the way in which a target
* should be made. * should be made.
* *
* Some of the OP_ constants can be combined, others cannot. */ * Some of the OP_ constants can be combined, others cannot.
*/
typedef enum GNodeType { typedef enum GNodeType {
OP_NONE = 0, OP_NONE = 0,
/* The dependency operator ':' is the most common one. The commands of /* The dependency operator ':' is the most common one. The commands
* this node are executed if any child is out-of-date. */ * of this node are executed if any child is out-of-date. */
OP_DEPENDS = 1 << 0, OP_DEPENDS = 1 << 0,
/* The dependency operator '!' always executes its commands, even if /* The dependency operator '!' always executes its commands, even if
* its children are up-to-date. */ * its children are up-to-date. */
OP_FORCE = 1 << 1, OP_FORCE = 1 << 1,
/* The dependency operator '::' behaves like ':', except that it allows /* The dependency operator '::' behaves like ':', except that it
* multiple dependency groups to be defined. Each of these groups is * allows multiple dependency groups to be defined. Each of these
* executed on its own, independently from the others. Each individual * groups is executed on its own, independently from the others.
* dependency group is called a cohort. */ * Each individual dependency group is called a cohort. */
OP_DOUBLEDEP = 1 << 2, OP_DOUBLEDEP = 1 << 2,
/* Matches the dependency operators ':', '!' and '::'. */ /* Matches the dependency operators ':', '!' and '::'. */
OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP, OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP,
/* Don't care if the target doesn't exist and can't be created */ /* Don't care if the target doesn't exist and can't be created. */
OP_OPTIONAL = 1 << 3, OP_OPTIONAL = 1 << 3,
/* Use associated commands for parents */ /* Use associated commands for parents. */
OP_USE = 1 << 4, OP_USE = 1 << 4,
/* Target is never out of date, but always execute commands anyway. /* Target is never out of date, but always execute commands anyway.
* Its time doesn't matter, so it has none...sort of */ * Its time doesn't matter, so it has none...sort of. */
OP_EXEC = 1 << 5, OP_EXEC = 1 << 5,
/* Ignore non-zero exit status from shell commands when creating the node */ /* Ignore non-zero exit status from shell commands when creating the
OP_IGNORE = 1 << 6, * node. */
/* Don't remove the target when interrupted */ OP_IGNORE = 1 << 6,
OP_PRECIOUS = 1 << 7, /* Don't remove the target when interrupted. */
/* Don't echo commands when executed */ OP_PRECIOUS = 1 << 7,
OP_SILENT = 1 << 8, /* Don't echo commands when executed. */
/* Target is a recursive make so its commands should always be executed OP_SILENT = 1 << 8,
* when it is out of date, regardless of the state of the -n or -t flags */ /* Target is a recursive make so its commands should always be
OP_MAKE = 1 << 9, * executed when it is out of date, regardless of the state of the
/* Target is out-of-date only if any of its children was out-of-date */ * -n or -t flags. */
OP_JOIN = 1 << 10, OP_MAKE = 1 << 9,
/* Assume the children of the node have been already made */ /* Target is out-of-date only if any of its children was out-of-date. */
OP_MADE = 1 << 11, OP_JOIN = 1 << 10,
/* Special .BEGIN, .END, .INTERRUPT */ /* Assume the children of the node have been already made. */
OP_SPECIAL = 1 << 12, OP_MADE = 1 << 11,
/* Like .USE, only prepend commands */ /* Special .BEGIN, .END or .INTERRUPT. */
OP_USEBEFORE = 1 << 13, OP_SPECIAL = 1 << 12,
/* The node is invisible to its parents. I.e. it doesn't show up in the /* Like .USE, only prepend commands. */
* parents' local variables (.IMPSRC, .ALLSRC). */ OP_USEBEFORE = 1 << 13,
OP_INVISIBLE = 1 << 14, /* The node is invisible to its parents. I.e. it doesn't show up in
/* The node is exempt from normal 'main target' processing in parse.c */ * the parents' local variables (.IMPSRC, .ALLSRC). */
OP_NOTMAIN = 1 << 15, OP_INVISIBLE = 1 << 14,
/* Not a file target; run always */ /* The node does not become the main target, even if it is the first
OP_PHONY = 1 << 16, * target in the first makefile. */
/* Don't search for file in the path */ OP_NOTMAIN = 1 << 15,
OP_NOPATH = 1 << 17, /* Not a file target; run always. */
/* In a dependency line "target: source1 .WAIT source2", source1 is made OP_PHONY = 1 << 16,
* first, including its children. Once that is finished, source2 is made, /* Don't search for the file in the path. */
* including its children. The .WAIT keyword may appear more than once in OP_NOPATH = 1 << 17,
* a single dependency declaration. */ /* In a dependency line "target: source1 .WAIT source2", source1 is
OP_WAIT = 1 << 18, * made first, including its children. Once that is finished,
/* .NOMETA do not create a .meta file */ * source2 is made, including its children. The .WAIT keyword may
OP_NOMETA = 1 << 19, * appear more than once in a single dependency declaration. */
/* .META we _do_ want a .meta file */ OP_WAIT = 1 << 18,
OP_META = 1 << 20, /* .NOMETA do not create a .meta file */
/* Do not compare commands in .meta file */ OP_NOMETA = 1 << 19,
OP_NOMETA_CMP = 1 << 21, /* .META we _do_ want a .meta file */
/* Possibly a submake node */ OP_META = 1 << 20,
OP_SUBMAKE = 1 << 22, /* Do not compare commands in .meta file */
OP_NOMETA_CMP = 1 << 21,
/* Possibly a submake node */
OP_SUBMAKE = 1 << 22,
/* Attributes applied by PMake */ /* Attributes applied by PMake */
/* The node is a transformation rule, such as ".c.o". */ /* The node is a transformation rule, such as ".c.o". */
OP_TRANSFORM = 1 << 31, OP_TRANSFORM = 1 << 30,
/* Target is a member of an archive */ /* Target is a member of an archive */
/* XXX: How does this differ from OP_ARCHV? */ /* XXX: How does this differ from OP_ARCHV? */
OP_MEMBER = 1 << 30, OP_MEMBER = 1 << 29,
/* The node is a library, /* The node is a library,
* its name has the form "-l<libname>" */ * its name has the form "-l<libname>" */
OP_LIB = 1 << 29, OP_LIB = 1 << 28,
/* The node is an archive member, /* The node is an archive member,
* its name has the form "archive(member)" */ * its name has the form "archive(member)" */
/* XXX: How does this differ from OP_MEMBER? */ /* XXX: How does this differ from OP_MEMBER? */
OP_ARCHV = 1 << 28, OP_ARCHV = 1 << 27,
/* Target has all the commands it should. Used when parsing to catch /* Target has all the commands it should. Used when parsing to catch
* multiple command groups for a target. Only applies to the dependency * multiple command groups for a target. Only applies to the
* operators ':' and '!', but not to '::'. */ * dependency operators ':' and '!', but not to '::'. */
OP_HAS_COMMANDS = 1 << 27, OP_HAS_COMMANDS = 1 << 26,
/* The special command "..." has been seen. All further commands from /* The special command "..." has been seen. All further commands from
* this node will be saved on the .END node instead, to be executed at * this node will be saved on the .END node instead, to be executed at
* the very end. */ * the very end. */
OP_SAVE_CMDS = 1 << 26, OP_SAVE_CMDS = 1 << 25,
/* Already processed by Suff_FindDeps */ /* Already processed by Suff_FindDeps, to find dependencies from
OP_DEPS_FOUND = 1 << 25, * suffix transformation rules. */
/* Node found while expanding .ALLSRC */ OP_DEPS_FOUND = 1 << 24,
OP_MARK = 1 << 24, /* Node found while expanding .ALLSRC */
OP_MARK = 1 << 23,
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
} GNodeType; } GNodeType;
typedef enum GNodeFlags { typedef enum GNodeFlags {
REMAKE = 0x0001, /* this target needs to be (re)made */ GNF_NONE = 0,
CHILDMADE = 0x0002, /* children of this target were made */ /* this target needs to be (re)made */
FORCE = 0x0004, /* children don't exist, and we pretend made */ REMAKE = 0x0001,
DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */ /* children of this target were made */
DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */ CHILDMADE = 0x0002,
FROM_DEPEND = 0x0020, /* Node created from .depend */ /* children don't exist, and we pretend made */
DONE_ALLSRC = 0x0040, /* We do it once only */ FORCE = 0x0004,
CYCLE = 0x1000, /* Used by MakePrintStatus */ /* Set by Make_ProcessWait() */
DONECYCLE = 0x2000, /* Used by MakePrintStatus */ DONE_WAIT = 0x0008,
INTERNAL = 0x4000 /* Internal use only */ /* Build requested by .ORDER processing */
DONE_ORDER = 0x0010,
/* Node created from .depend */
FROM_DEPEND = 0x0020,
/* We do it once only */
DONE_ALLSRC = 0x0040,
/* Used by MakePrintStatus */
CYCLE = 0x1000,
/* Used by MakePrintStatus */
DONECYCLE = 0x2000,
/* Internal use only */
INTERNAL = 0x4000
} GNodeFlags; } GNodeFlags;
typedef struct List StringList; typedef struct List StringList;
@ -324,112 +379,118 @@ typedef struct ListNode GNodeListNode;
typedef struct List /* of CachedDir */ SearchPath; 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. */ * 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 { typedef struct GNode {
/* The target's name, such as "clean" or "make.c" */ /* The target's name, such as "clean" or "make.c" */
char *name; char *name;
/* The unexpanded name of a .USE node */ /* The unexpanded name of a .USE node */
char *uname; char *uname;
/* The full pathname of the file belonging to the target. /* The full pathname of the file belonging to the target.
* XXX: What about .PHONY targets? These don't have an associated path. */ * XXX: What about .PHONY targets? These don't have an associated
char *path; * path. */
char *path;
/* The type of operator used to define the sources (see the OP flags below). /* The type of operator used to define the sources (see the OP flags
* XXX: This looks like a wild mixture of type and flags. */ * below).
GNodeType type; * XXX: This looks like a wild mixture of type and flags. */
GNodeFlags flags; GNodeType type;
GNodeFlags flags;
/* The state of processing on this node */ /* The state of processing on this node */
GNodeMade made; GNodeMade made;
int unmade; /* The number of unmade children */ /* The number of unmade children */
int unmade;
/* The modification time; 0 means the node does not have a corresponding /* The modification time; 0 means the node does not have a
* file; see GNode_IsOODate. */ * corresponding file; see GNode_IsOODate. */
time_t mtime; time_t mtime;
struct GNode *youngestChild; struct GNode *youngestChild;
/* The GNodes for which this node is an implied source. May be empty. /* 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 * 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. */ * file.c has the node for file.o in this list. */
GNodeList *implicitParents; GNodeList implicitParents;
/* The nodes that depend on this one, or in other words, the nodes for /* The nodes that depend on this one, or in other words, the nodes for
* which this is a source. */ * which this is a source. */
GNodeList *parents; GNodeList parents;
/* The nodes on which this one depends. */ /* The nodes on which this one depends. */
GNodeList *children; GNodeList children;
/* .ORDER nodes we need made. The nodes that must be made (if they're /* .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 * made) before this node can be made, but that do not enter into the
* datedness of this node. */ * datedness of this node. */
GNodeList *order_pred; GNodeList order_pred;
/* .ORDER nodes who need us. The nodes that must be made (if they're made /* .ORDER nodes who need us. The nodes that must be made (if they're
* at all) after this node is made, but that do not depend on this node, * made at all) after this node is made, but that do not depend on
* in the normal sense. */ * this node, in the normal sense. */
GNodeList *order_succ; GNodeList order_succ;
/* Other nodes of the same name, for the '::' dependency operator. */ /* Other nodes of the same name, for the '::' dependency operator. */
GNodeList *cohorts; GNodeList cohorts;
/* The "#n" suffix for this cohort, or "" for other nodes */ /* The "#n" suffix for this cohort, or "" for other nodes */
char cohort_num[8]; char cohort_num[8];
/* The number of unmade instances on the cohorts list */ /* The number of unmade instances on the cohorts list */
int unmade_cohorts; int unmade_cohorts;
/* Pointer to the first instance of a '::' node; only set when on a /* Pointer to the first instance of a '::' node; only set when on a
* cohorts list */ * cohorts list */
struct GNode *centurion; struct GNode *centurion;
/* Last time (sequence number) we tried to make this node */ /* Last time (sequence number) we tried to make this node */
unsigned int checked_seqno; unsigned int checked_seqno;
/* The "local" variables that are specific to this target and this target /* The "local" variables that are specific to this target and this
* only, such as $@, $<, $?. * target only, such as $@, $<, $?.
* *
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE, * Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
* VAR_INTERNAL, which contain variables with arbitrary names. */ * VAR_INTERNAL, which contain variables with arbitrary names. */
HashTable /* of Var pointer */ context; HashTable /* of Var pointer */ vars;
/* The commands to be given to a shell to create this target. */ /* The commands to be given to a shell to create this target. */
StringList *commands; StringList commands;
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone /* Suffix for the node (determined by Suff_FindDeps and opaque to
* but the Suff module) */ * everyone but the Suff module) */
struct Suff *suffix; struct Suffix *suffix;
/* Filename where the GNode got defined */ /* Filename where the GNode got defined */
/* XXX: What is the lifetime of this string? */ /* XXX: What is the lifetime of this string? */
const char *fname; const char *fname;
/* Line number where the GNode got defined */ /* Line number where the GNode got defined */
int lineno; int lineno;
} GNode; } GNode;
/* Error levels for diagnostics during parsing. */ /* Error levels for diagnostics during parsing. */
typedef enum ParseErrorLevel { typedef enum ParseErrorLevel {
/* Exit when the current top-level makefile has been parsed completely. */ /* Exit when the current top-level makefile has been parsed
PARSE_FATAL = 1, * completely. */
/* Print "warning"; may be upgraded to fatal by the -w option. */ PARSE_FATAL = 1,
PARSE_WARNING, /* Print "warning"; may be upgraded to fatal by the -w option. */
/* Informational, mainly used during development of makefiles. */ PARSE_WARNING,
PARSE_INFO /* Informational, mainly used during development of makefiles. */
PARSE_INFO
} ParseErrorLevel; } ParseErrorLevel;
/* /*
* Values returned by Cond_EvalLine and Cond_EvalCondition. * Values returned by Cond_EvalLine and Cond_EvalCondition.
*/ */
typedef enum CondEvalResult { typedef enum CondEvalResult {
COND_PARSE, /* Parse the next lines */ COND_PARSE, /* Parse the next lines */
COND_SKIP, /* Skip the next lines */ COND_SKIP, /* Skip the next lines */
COND_INVALID /* Not a conditional statement */ COND_INVALID /* Not a conditional statement */
} CondEvalResult; } CondEvalResult;
/* Names of the variables that are "local" to a specific target. */ /* Names of the variables that are "local" to a specific target. */
#define TARGET "@" /* Target of dependency */ #define TARGET "@" /* Target of dependency */
#define OODATE "?" /* All out-of-date sources */ #define OODATE "?" /* All out-of-date sources */
#define ALLSRC ">" /* All sources */ #define ALLSRC ">" /* All sources */
#define IMPSRC "<" /* Source implied by transformation */ #define IMPSRC "<" /* Source implied by transformation */
#define PREFIX "*" /* Common prefix */ #define PREFIX "*" /* Common prefix */
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */ #define ARCHIVE "!" /* Archive in "archive(member)" syntax */
#define MEMBER "%" /* Member in "archive(member)" syntax */ #define MEMBER "%" /* Member in "archive(member)" syntax */
/* /*
* Global Variables * Global Variables
@ -444,43 +505,36 @@ extern Boolean doing_depend;
/* .DEFAULT rule */ /* .DEFAULT rule */
extern GNode *defaultNode; extern GNode *defaultNode;
/* Variables defined internally by make which should not override those set /*
* by makefiles. */ * Variables defined internally by make which should not override those set
* by makefiles.
*/
extern GNode *VAR_INTERNAL; extern GNode *VAR_INTERNAL;
/* Variables defined in a global context, e.g in the Makefile itself. */ /* Variables defined in a global context, e.g in the Makefile itself. */
extern GNode *VAR_GLOBAL; extern GNode *VAR_GLOBAL;
/* Variables defined on the command line. */ /* Variables defined on the command line. */
extern GNode *VAR_CMDLINE; extern GNode *VAR_CMDLINE;
/* 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. */ * 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 char var_Error[]; extern char var_Error[];
/* The time at the start of this whole process */ /* The time at the start of this whole process */
extern time_t now; extern time_t now;
/* /*
* If FALSE (the default behavior), undefined subexpressions in a variable * The list of directories to search when looking for targets (set by the
* expression are discarded. If TRUE (only during variable assignments using * special target .PATH).
* the ':=' assignment operator, no matter how deeply nested), they are
* preserved and possibly expanded later when the variable from the
* subexpression has been defined.
*
* Example for a ':=' assignment:
* CFLAGS = $(.INCLUDES)
* CFLAGS := -I.. $(CFLAGS)
* # If .INCLUDES (an undocumented special variable, by the way) is
* # still undefined, the updated CFLAGS becomes "-I.. $(.INCLUDES)".
*/ */
extern Boolean preserveUndefined; extern SearchPath dirSearchPath;
/* The list of directories to search when looking for targets (set by the
* special target .PATH). */
extern SearchPath *dirSearchPath;
/* Used for .include "...". */ /* Used for .include "...". */
extern SearchPath *parseIncPath; extern SearchPath *parseIncPath;
/* Used for .include <...>, for the built-in sys.mk and makefiles from the /*
* command line arguments. */ * Used for .include <...>, for the built-in sys.mk and makefiles from the
* command line arguments.
*/
extern SearchPath *sysIncPath; extern SearchPath *sysIncPath;
/* The default for sysIncPath. */ /* The default for sysIncPath. */
extern SearchPath *defSysIncPath; extern SearchPath *defSysIncPath;
@ -488,7 +542,7 @@ extern SearchPath *defSysIncPath;
/* Startup directory */ /* Startup directory */
extern char curdir[]; extern char curdir[];
/* The basename of the program name, suffixed with [n] for sub-makes. */ /* The basename of the program name, suffixed with [n] for sub-makes. */
extern char *progname; extern const char *progname;
/* Name of the .depend makefile */ /* Name of the .depend makefile */
extern char *makeDependfile; extern char *makeDependfile;
/* If we replaced environ, this will be non-NULL. */ /* If we replaced environ, this will be non-NULL. */
@ -503,156 +557,152 @@ extern int makelevel;
#define vFork() ((getpid() == myPid) ? vfork() : fork()) #define vFork() ((getpid() == myPid) ? vfork() : fork())
extern pid_t myPid; extern pid_t myPid;
#define MAKEFLAGS ".MAKEFLAGS" #define MAKEFLAGS ".MAKEFLAGS"
#define MAKEOVERRIDES ".MAKEOVERRIDES" #define MAKEOVERRIDES ".MAKEOVERRIDES"
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */ /* prefix when printing the target of a job */
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */ #define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX"
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */ #define MAKE_EXPORTED ".MAKE.EXPORTED" /* exported variables */
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */ #define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all loaded makefiles */
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
#define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE" #define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */ #define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
#define MAKE_MODE ".MAKE.MODE" #define MAKE_MODE ".MAKE.MODE"
#ifndef MAKE_LEVEL_ENV #ifndef MAKE_LEVEL_ENV
# define MAKE_LEVEL_ENV "MAKELEVEL" # define MAKE_LEVEL_ENV "MAKELEVEL"
#endif #endif
typedef enum DebugFlags { typedef enum DebugFlags {
DEBUG_NONE = 0, DEBUG_NONE = 0,
DEBUG_ARCH = 1 << 0, DEBUG_ARCH = 1 << 0,
DEBUG_COND = 1 << 1, DEBUG_COND = 1 << 1,
DEBUG_CWD = 1 << 2, DEBUG_CWD = 1 << 2,
DEBUG_DIR = 1 << 3, DEBUG_DIR = 1 << 3,
DEBUG_ERROR = 1 << 4, DEBUG_ERROR = 1 << 4,
DEBUG_FOR = 1 << 5, DEBUG_FOR = 1 << 5,
DEBUG_GRAPH1 = 1 << 6, DEBUG_GRAPH1 = 1 << 6,
DEBUG_GRAPH2 = 1 << 7, DEBUG_GRAPH2 = 1 << 7,
DEBUG_GRAPH3 = 1 << 8, DEBUG_GRAPH3 = 1 << 8,
DEBUG_HASH = 1 << 9, DEBUG_HASH = 1 << 9,
DEBUG_JOB = 1 << 10, DEBUG_JOB = 1 << 10,
DEBUG_LOUD = 1 << 11, DEBUG_LOUD = 1 << 11,
DEBUG_MAKE = 1 << 12, DEBUG_MAKE = 1 << 12,
DEBUG_META = 1 << 13, DEBUG_META = 1 << 13,
DEBUG_PARSE = 1 << 14, DEBUG_PARSE = 1 << 14,
DEBUG_SCRIPT = 1 << 15, DEBUG_SCRIPT = 1 << 15,
DEBUG_SHELL = 1 << 16, DEBUG_SHELL = 1 << 16,
DEBUG_SUFF = 1 << 17, DEBUG_SUFF = 1 << 17,
DEBUG_TARG = 1 << 18, DEBUG_TARG = 1 << 18,
DEBUG_VAR = 1 << 19, DEBUG_VAR = 1 << 19,
DEBUG_ALL = (1 << 20) - 1 DEBUG_ALL = (1 << 20) - 1
} DebugFlags; } DebugFlags;
#define CONCAT(a,b) a##b #define CONCAT(a, b) a##b
#define DEBUG(module) (opts.debug & CONCAT(DEBUG_,module)) #define DEBUG(module) ((opts.debug & CONCAT(DEBUG_, module)) != 0)
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
#define DEBUG_IMPL(module, args) \
do { \
if (DEBUG(module)) \
debug_printf args; \
} while (/*CONSTCOND*/ 0)
#define DEBUG0(module, text) \ #define DEBUG0(module, text) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, ("%s", text))
else debug_printf("%s", text)
#define DEBUG1(module, fmt, arg1) \ #define DEBUG1(module, fmt, arg1) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, (fmt, arg1))
else debug_printf(fmt, arg1)
#define DEBUG2(module, fmt, arg1, arg2) \ #define DEBUG2(module, fmt, arg1, arg2) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, (fmt, arg1, arg2))
else debug_printf(fmt, arg1, arg2)
#define DEBUG3(module, fmt, arg1, arg2, arg3) \ #define DEBUG3(module, fmt, arg1, arg2, arg3) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3))
else debug_printf(fmt, arg1, arg2, arg3)
#define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \ #define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4))
else debug_printf(fmt, arg1, arg2, arg3, arg4)
#define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \ #define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \
if (!DEBUG(module)) (void)0; \ DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5))
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
typedef enum PrintVarsMode { typedef enum PrintVarsMode {
PVM_NONE, PVM_NONE,
PVM_UNEXPANDED, PVM_UNEXPANDED,
PVM_EXPANDED PVM_EXPANDED
} PrintVarsMode; } PrintVarsMode;
/* Command line options */ /* Command line options */
typedef struct CmdOpts { typedef struct CmdOpts {
/* -B: whether we are make compatible */ /* -B: whether we are make compatible */
Boolean compatMake; Boolean compatMake;
/* -d: debug control: There is one bit per module. It is up to the /* -d: debug control: There is one bit per module. It is up to the
* module what debug information to print. */ * module what debug information to print. */
DebugFlags debug; DebugFlags debug;
/* -df: debug output is written here - default stderr */ /* -df: debug output is written here - default stderr */
FILE *debug_file; FILE *debug_file;
/* -dL: lint mode /* -dL: lint mode
* *
* Runs make in strict mode, with additional checks and better error * Runs make in strict mode, with additional checks and better error
* handling. */ * handling. */
Boolean lint; Boolean strict;
/* -dV: for the -V option, print unexpanded variable values */ /* -dV: for the -V option, print unexpanded variable values */
Boolean debugVflag; Boolean debugVflag;
/* -e: check environment variables before global variables */ /* -e: check environment variables before global variables */
Boolean checkEnvFirst; Boolean checkEnvFirst;
/* -f: the makefiles to read */ /* -f: the makefiles to read */
StringList *makefiles; StringList makefiles;
/* -i: if true, ignore all errors from shell commands */ /* -i: if true, ignore all errors from shell commands */
Boolean ignoreErrors; Boolean ignoreErrors;
/* -j: the maximum number of jobs that can run in parallel; /* -j: the maximum number of jobs that can run in parallel;
* this is coordinated with the submakes */ * this is coordinated with the submakes */
int maxJobs; int maxJobs;
/* -k: if true, continue on unaffected portions of the graph when an /* -k: if true and an error occurs while making a node, continue
* error occurs in one portion */ * making nodes that do not depend on the erroneous node */
Boolean keepgoing; Boolean keepgoing;
/* -N: execute no commands from the targets */ /* -N: execute no commands from the targets */
Boolean noRecursiveExecute; Boolean noRecursiveExecute;
/* -n: execute almost no commands from the targets */ /* -n: execute almost no commands from the targets */
Boolean noExecute; Boolean noExecute;
/* -q: if true, we aren't supposed to really make anything, just see if /* -q: if true, we aren't supposed to really make anything, just see
* the targets are out-of-date */ * if the targets are out-of-date */
Boolean queryFlag; Boolean queryFlag;
/* -r: raw mode, without loading the builtin rules. */ /* -r: raw mode, without loading the builtin rules. */
Boolean noBuiltins; Boolean noBuiltins;
/* -s: don't echo the shell commands before executing them */ /* -s: don't echo the shell commands before executing them */
Boolean beSilent; Boolean beSilent;
/* -t: touch the targets if they are out-of-date, but don't actually /* -t: touch the targets if they are out-of-date, but don't actually
* make them */ * make them */
Boolean touchFlag; Boolean touchFlag;
/* -[Vv]: print expanded or unexpanded selected variables */ /* -[Vv]: print expanded or unexpanded selected variables */
PrintVarsMode printVars; PrintVarsMode printVars;
/* -[Vv]: the variables to print */ /* -[Vv]: the variables to print */
StringList *variables; StringList variables;
/* -W: if true, makefile parsing warnings are treated as errors */ /* -W: if true, makefile parsing warnings are treated as errors */
Boolean parseWarnFatal; Boolean parseWarnFatal;
/* -w: print Entering and Leaving for submakes */ /* -w: print Entering and Leaving for submakes */
Boolean enterFlag; Boolean enterFlag;
/* -X: if true, do not export variables set on the command line to the /* -X: if true, do not export variables set on the command line to the
* environment. */ * environment. */
Boolean varNoExportEnv; Boolean varNoExportEnv;
/* The target names specified on the command line. /* The target names specified on the command line.
* Used to resolve .if make(...) statements. */ * Used to resolve .if make(...) statements. */
StringList *create; StringList create;
} CmdOpts; } CmdOpts;
@ -681,13 +731,37 @@ Boolean GNode_ShouldExecute(GNode *gn);
MAKE_INLINE Boolean MAKE_INLINE Boolean
GNode_IsTarget(const GNode *gn) GNode_IsTarget(const GNode *gn)
{ {
return (gn->type & OP_OPMASK) != 0; return (gn->type & OP_OPMASK) != 0;
} }
MAKE_INLINE const char * MAKE_INLINE const char *
GNode_Path(const GNode *gn) GNode_Path(const GNode *gn)
{ {
return gn->path != NULL ? gn->path : gn->name; return gn->path != NULL ? gn->path : gn->name;
}
MAKE_INLINE Boolean
GNode_IsWaitingFor(const GNode *gn)
{
return (gn->flags & REMAKE) && gn->made <= REQUESTED;
}
MAKE_INLINE Boolean
GNode_IsReady(const GNode *gn)
{
return gn->made > DEFERRED;
}
MAKE_INLINE Boolean
GNode_IsDone(const GNode *gn)
{
return gn->made >= MADE;
}
MAKE_INLINE Boolean
GNode_IsError(const GNode *gn)
{
return gn->made == ERROR || gn->made == ABORTED;
} }
MAKE_INLINE const char * MAKE_INLINE const char *
@ -728,9 +802,9 @@ GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
#endif #endif
#if defined(SYSV) #if defined(SYSV)
#define KILLPG(pid, sig) kill(-(pid), (sig)) #define KILLPG(pid, sig) kill(-(pid), (sig))
#else #else
#define KILLPG(pid, sig) killpg((pid), (sig)) #define KILLPG(pid, sig) killpg((pid), (sig))
#endif #endif
MAKE_INLINE Boolean MAKE_INLINE Boolean
@ -751,36 +825,42 @@ ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
MAKE_INLINE void MAKE_INLINE void
cpp_skip_whitespace(const char **pp) cpp_skip_whitespace(const char **pp)
{ {
while (ch_isspace(**pp)) while (ch_isspace(**pp))
(*pp)++; (*pp)++;
} }
MAKE_INLINE void MAKE_INLINE void
cpp_skip_hspace(const char **pp) cpp_skip_hspace(const char **pp)
{ {
while (**pp == ' ' || **pp == '\t') while (**pp == ' ' || **pp == '\t')
(*pp)++; (*pp)++;
} }
MAKE_INLINE void MAKE_INLINE void
pp_skip_whitespace(char **pp) pp_skip_whitespace(char **pp)
{ {
while (ch_isspace(**pp)) while (ch_isspace(**pp))
(*pp)++; (*pp)++;
} }
MAKE_INLINE void MAKE_INLINE void
pp_skip_hspace(char **pp) pp_skip_hspace(char **pp)
{ {
while (**pp == ' ' || **pp == '\t') while (**pp == ' ' || **pp == '\t')
(*pp)++; (*pp)++;
} }
#ifdef MAKE_NATIVE #if defined(lint)
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
#elif defined(MAKE_NATIVE)
# include <sys/cdefs.h> # include <sys/cdefs.h>
# ifndef lint # define MAKE_RCSID(id) __RCSID(id)
# define MAKE_RCSID(id) __RCSID(id) #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
# endif # define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
# define MAKE_RCSID(id) static volatile char \
MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id
#elif defined(MAKE_ALL_IN_ONE)
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
#else #else
# define MAKE_RCSID(id) static volatile char rcsid[] = id # define MAKE_RCSID(id) static volatile char rcsid[] = id
#endif #endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $ */ /* $NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $ */
/*- /*-
* Copyright (c) 2009 The NetBSD Foundation, Inc. * Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
#include "make.h" #include "make.h"
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $"); MAKE_RCSID("$NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $");
#ifndef USE_EMALLOC #ifndef USE_EMALLOC
@ -61,8 +61,7 @@ bmake_strdup(const char *str)
char *p; char *p;
len = strlen(str) + 1; len = strlen(str) + 1;
if ((p = malloc(len)) == NULL) p = bmake_malloc(len);
enomem();
return memcpy(p, str, len); return memcpy(p, str, len);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: make_malloc.h,v 1.13 2020/11/10 00:32:12 rillig Exp $ */ /* $NetBSD: make_malloc.h,v 1.15 2020/12/30 10:03:16 rillig Exp $ */
/*- /*-
* Copyright (c) 2009 The NetBSD Foundation, Inc. * Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -41,14 +41,16 @@ char *bmake_strldup(const char *, size_t);
char *bmake_strsedup(const char *, const char *); char *bmake_strsedup(const char *, const char *);
/* Thin wrapper around free(3) to avoid the extra function call in case /*
* Thin wrapper around free(3) to avoid the extra function call in case
* p is NULL, to save a few machine instructions. * p is NULL, to save a few machine instructions.
* *
* The case of a NULL pointer happens especially often after Var_Value, * The case of a NULL pointer happens especially often after Var_Value,
* since only environment variables need to be freed, but not others. */ * since only environment variables need to be freed, but not others.
*/
MAKE_INLINE void MAKE_INLINE void
bmake_free(void *p) bmake_free(void *p)
{ {
if (p != NULL) if (p != NULL)
free(p); free(p);
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.c,v 1.144 2020/11/15 12:02:44 rillig Exp $ */ /* $NetBSD: meta.c,v 1.168 2021/01/10 21:20:46 rillig Exp $ */
/* /*
* Implement 'meta' mode. * Implement 'meta' mode.
@ -55,9 +55,9 @@ char * dirname(char *);
#endif #endif
static BuildMon Mybm; /* for compat */ static BuildMon Mybm; /* for compat */
static StringList *metaBailiwick; /* our scope of control */ static StringList metaBailiwick = LST_INIT; /* our scope of control */
static char *metaBailiwickStr; /* string storage for the list */ static char *metaBailiwickStr; /* string storage for the list */
static StringList *metaIgnorePaths; /* paths we deliberately ignore */ static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */
static char *metaIgnorePathsStr; /* string storage for the list */ static char *metaIgnorePathsStr; /* string storage for the list */
#ifndef MAKE_META_IGNORE_PATHS #ifndef MAKE_META_IGNORE_PATHS
@ -79,14 +79,14 @@ static Boolean metaEnv = FALSE; /* don't save env unless asked */
static Boolean metaVerbose = FALSE; static Boolean metaVerbose = FALSE;
static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */ static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */ static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
extern Boolean forceJobs; extern Boolean forceJobs;
extern char **environ; extern char **environ;
#define MAKE_META_PREFIX ".MAKE.META.PREFIX" #define MAKE_META_PREFIX ".MAKE.META.PREFIX"
#ifndef N2U #ifndef N2U
# define N2U(n, u) (((n) + ((u) - 1)) / (u)) # define N2U(n, u) (((n) + ((u) - 1)) / (u))
@ -128,7 +128,7 @@ meta_open_filemon(BuildMon *pbm)
pbm->mon_fd = -1; pbm->mon_fd = -1;
pbm->filemon = NULL; pbm->filemon = NULL;
if (!useFilemon || !pbm->mfp) if (!useFilemon || pbm->mfp == NULL)
return; return;
pbm->filemon = filemon_open(); pbm->filemon = filemon_open();
@ -222,7 +222,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
do { do {
cp = strstr(buf, eat); cp = strstr(buf, eat);
if (cp) { if (cp != NULL) {
cp2 = cp + eatlen; cp2 = cp + eatlen;
if (dots == 2 && cp > buf) { if (dots == 2 && cp > buf) {
do { do {
@ -235,7 +235,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
return; /* can't happen? */ return; /* can't happen? */
} }
} }
} while (cp); } while (cp != NULL);
} }
static char * static char *
@ -258,9 +258,9 @@ meta_name(char *mname, size_t mnamelen,
* So we use realpath() just to get the dirname, and leave the * So we use realpath() just to get the dirname, and leave the
* basename as given to us. * basename as given to us.
*/ */
if ((cp = strrchr(tname, '/'))) { if ((cp = strrchr(tname, '/')) != NULL) {
if (cached_realpath(tname, buf)) { if (cached_realpath(tname, buf) != NULL) {
if ((rp = strrchr(buf, '/'))) { if ((rp = strrchr(buf, '/')) != NULL) {
rp++; rp++;
cp++; cp++;
if (strcmp(cp, rp) != 0) if (strcmp(cp, rp) != 0)
@ -316,25 +316,22 @@ meta_name(char *mname, size_t mnamelen,
* Return true if running ${.MAKE} * Return true if running ${.MAKE}
* Bypassed if target is flagged .MAKE * Bypassed if target is flagged .MAKE
*/ */
static int static Boolean
is_submake(void *cmdp, void *gnp) is_submake(const char *cmd, GNode *gn)
{ {
static const char *p_make = NULL; static const char *p_make = NULL;
static size_t p_len; static size_t p_len;
char *cmd = cmdp;
GNode *gn = gnp;
char *mp = NULL; char *mp = NULL;
char *cp; char *cp;
char *cp2; char *cp2;
int rc = 0; /* keep looking */ Boolean rc = FALSE;
if (p_make == NULL) { if (p_make == NULL) {
void *dontFreeIt; p_make = Var_Value(".MAKE", gn).str;
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
p_len = strlen(p_make); p_len = strlen(p_make);
} }
cp = strchr(cmd, '$'); cp = strchr(cmd, '$');
if ((cp)) { if (cp != NULL) {
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp); (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
/* TODO: handle errors */ /* TODO: handle errors */
cmd = mp; cmd = mp;
@ -346,17 +343,17 @@ is_submake(void *cmdp, void *gnp)
case ' ': case ' ':
case '\t': case '\t':
case '\n': case '\n':
rc = 1; rc = TRUE;
break; break;
} }
if (cp2 > cmd && rc > 0) { if (cp2 > cmd && rc) {
switch (cp2[-1]) { switch (cp2[-1]) {
case ' ': case ' ':
case '\t': case '\t':
case '\n': case '\n':
break; break;
default: default:
rc = 0; /* no match */ rc = FALSE; /* no match */
break; break;
} }
} }
@ -365,32 +362,38 @@ is_submake(void *cmdp, void *gnp)
return rc; return rc;
} }
typedef struct meta_file_s { static Boolean
FILE *fp; any_is_submake(GNode *gn)
GNode *gn; {
} meta_file_t; StringListNode *ln;
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
if (is_submake(ln->datum, gn))
return TRUE;
return FALSE;
}
static void static void
printCMD(const char *cmd, meta_file_t *mfp) printCMD(const char *cmd, FILE *fp, GNode *gn)
{ {
char *cmd_freeIt = NULL; char *cmd_freeIt = NULL;
if (strchr(cmd, '$')) { if (strchr(cmd, '$') != NULL) {
(void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt); (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmd_freeIt);
/* TODO: handle errors */ /* TODO: handle errors */
cmd = cmd_freeIt; cmd = cmd_freeIt;
} }
fprintf(mfp->fp, "CMD %s\n", cmd); fprintf(fp, "CMD %s\n", cmd);
free(cmd_freeIt); free(cmd_freeIt);
} }
static void static void
printCMDs(GNode *gn, meta_file_t *mf) printCMDs(GNode *gn, FILE *fp)
{ {
GNodeListNode *ln; StringListNode *ln;
for (ln = gn->commands->first; ln != NULL; ln = ln->next) for (ln = gn->commands.first; ln != NULL; ln = ln->next)
printCMD(ln->datum, mf); printCMD(ln->datum, fp, gn);
} }
/* /*
@ -404,7 +407,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
} \ } \
return FALSE; \ return FALSE; \
} \ } \
} while (0) } while (/*CONSTCOND*/0)
/* /*
@ -412,7 +415,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
*/ */
static Boolean static Boolean
meta_needed(GNode *gn, const char *dname, meta_needed(GNode *gn, const char *dname,
char *objdir, int verbose) char *objdir_realpath, Boolean verbose)
{ {
struct cached_stat cst; struct cached_stat cst;
@ -431,14 +434,14 @@ meta_needed(GNode *gn, const char *dname,
} }
/* Check if there are no commands to execute. */ /* Check if there are no commands to execute. */
if (Lst_IsEmpty(gn->commands)) { if (Lst_IsEmpty(&gn->commands)) {
if (verbose) if (verbose)
debug_printf("Skipping meta for %s: no commands\n", gn->name); debug_printf("Skipping meta for %s: no commands\n", gn->name);
return FALSE; return FALSE;
} }
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) { if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
/* OP_SUBMAKE is a bit too aggressive */ /* OP_SUBMAKE is a bit too aggressive */
if (Lst_ForEachUntil(gn->commands, is_submake, gn)) { if (any_is_submake(gn)) {
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name); DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
return FALSE; return FALSE;
} }
@ -452,8 +455,8 @@ meta_needed(GNode *gn, const char *dname,
} }
/* make sure these are canonical */ /* make sure these are canonical */
if (cached_realpath(dname, objdir)) if (cached_realpath(dname, objdir_realpath) != NULL)
dname = objdir; dname = objdir_realpath;
/* If we aren't in the object directory, don't create a meta file. */ /* If we aren't in the object directory, don't create a meta file. */
if (!metaCurdirOk && strcmp(curdir, dname) == 0) { if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
@ -469,25 +472,24 @@ meta_needed(GNode *gn, const char *dname,
static FILE * static FILE *
meta_create(BuildMon *pbm, GNode *gn) meta_create(BuildMon *pbm, GNode *gn)
{ {
meta_file_t mf; FILE *fp;
char buf[MAXPATHLEN]; char buf[MAXPATHLEN];
char objdir[MAXPATHLEN]; char objdir_realpath[MAXPATHLEN];
char **ptr; char **ptr;
const char *dname; FStr dname;
const char *tname; const char *tname;
char *fname; char *fname;
const char *cp; const char *cp;
void *objdir_freeIt;
mf.fp = NULL; fp = NULL;
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt); dname = Var_Value(".OBJDIR", gn);
tname = GNode_VarTarget(gn); tname = GNode_VarTarget(gn);
/* if this succeeds objdir is realpath of dname */ /* if this succeeds objdir_realpath is realpath of dname */
if (!meta_needed(gn, dname, objdir, TRUE)) if (!meta_needed(gn, dname.str, objdir_realpath, TRUE))
goto out; goto out;
dname = objdir; dname.str = objdir_realpath;
if (metaVerbose) { if (metaVerbose) {
char *mp; char *mp;
@ -495,16 +497,12 @@ meta_create(BuildMon *pbm, GNode *gn)
/* Describe the target we are building */ /* Describe the target we are building */
(void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp); (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
/* TODO: handle errors */ /* TODO: handle errors */
if (*mp) if (mp[0] != '\0')
fprintf(stdout, "%s\n", mp); fprintf(stdout, "%s\n", mp);
free(mp); free(mp);
} }
/* Get the basename of the target */ /* Get the basename of the target */
if ((cp = strrchr(tname, '/')) == NULL) { cp = str_basename(tname);
cp = tname;
} else {
cp++;
}
fflush(stdout); fflush(stdout);
@ -513,34 +511,32 @@ meta_create(BuildMon *pbm, GNode *gn)
goto out; goto out;
fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname, fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname,
dname, tname, objdir); dname.str, tname, objdir_realpath);
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG1(META, "meta_create: %s\n", fname); DEBUG1(META, "meta_create: %s\n", fname);
#endif #endif
if ((mf.fp = fopen(fname, "w")) == NULL) if ((fp = fopen(fname, "w")) == NULL)
err(1, "Could not open meta file '%s'", fname); err(1, "Could not open meta file '%s'", fname);
fprintf(mf.fp, "# Meta data file %s\n", fname); fprintf(fp, "# Meta data file %s\n", fname);
mf.gn = gn; printCMDs(gn, fp);
printCMDs(gn, &mf); fprintf(fp, "CWD %s\n", getcwd(buf, sizeof buf));
fprintf(fp, "TARGET %s\n", tname);
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof buf));
fprintf(mf.fp, "TARGET %s\n", tname);
cp = GNode_VarOodate(gn); cp = GNode_VarOodate(gn);
if (cp && *cp) { if (cp != NULL && *cp != '\0') {
fprintf(mf.fp, "OODATE %s\n", cp); fprintf(fp, "OODATE %s\n", cp);
} }
if (metaEnv) { if (metaEnv) {
for (ptr = environ; *ptr != NULL; ptr++) for (ptr = environ; *ptr != NULL; ptr++)
fprintf(mf.fp, "ENV %s\n", *ptr); fprintf(fp, "ENV %s\n", *ptr);
} }
fprintf(mf.fp, "-- command output --\n"); fprintf(fp, "-- command output --\n");
fflush(mf.fp); fflush(fp);
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
@ -550,9 +546,9 @@ meta_create(BuildMon *pbm, GNode *gn)
gn->type |= OP_SILENT; gn->type |= OP_SILENT;
} }
out: out:
bmake_free(objdir_freeIt); FStr_Done(&dname);
return mf.fp; return fp;
} }
static Boolean static Boolean
@ -583,7 +579,7 @@ meta_init(void)
#define get_mode_bf(bf, token) \ #define get_mode_bf(bf, token) \
if ((cp = strstr(make_mode, token))) \ if ((cp = strstr(make_mode, token)) != NULL) \
bf = boolValue(cp + sizeof (token) - 1) bf = boolValue(cp + sizeof (token) - 1)
/* /*
@ -592,24 +588,24 @@ meta_init(void)
void void
meta_mode_init(const char *make_mode) meta_mode_init(const char *make_mode)
{ {
static int once = 0; static Boolean once = FALSE;
char *cp; char *cp;
void *freeIt; FStr value;
useMeta = TRUE; useMeta = TRUE;
useFilemon = TRUE; useFilemon = TRUE;
writeMeta = TRUE; writeMeta = TRUE;
if (make_mode) { if (make_mode != NULL) {
if (strstr(make_mode, "env")) if (strstr(make_mode, "env") != NULL)
metaEnv = TRUE; metaEnv = TRUE;
if (strstr(make_mode, "verb")) if (strstr(make_mode, "verb") != NULL)
metaVerbose = TRUE; metaVerbose = TRUE;
if (strstr(make_mode, "read")) if (strstr(make_mode, "read") != NULL)
writeMeta = FALSE; writeMeta = FALSE;
if (strstr(make_mode, "nofilemon")) if (strstr(make_mode, "nofilemon") != NULL)
useFilemon = FALSE; useFilemon = FALSE;
if (strstr(make_mode, "ignore-cmd")) if (strstr(make_mode, "ignore-cmd") != NULL)
metaIgnoreCMDs = TRUE; metaIgnoreCMDs = TRUE;
if (useFilemon) if (useFilemon)
get_mode_bf(filemonMissing, "missing-filemon="); get_mode_bf(filemonMissing, "missing-filemon=");
@ -628,39 +624,37 @@ meta_mode_init(const char *make_mode)
} }
if (once) if (once)
return; return;
once = 1; once = TRUE;
memset(&Mybm, 0, sizeof Mybm); memset(&Mybm, 0, sizeof Mybm);
/* /*
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK} * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
*/ */
metaBailiwick = Lst_New();
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr); VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
/* TODO: handle errors */ /* TODO: handle errors */
str2Lst_Append(metaBailiwick, metaBailiwickStr); str2Lst_Append(&metaBailiwick, metaBailiwickStr);
/* /*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/ */
metaIgnorePaths = Lst_New();
Var_Append(MAKE_META_IGNORE_PATHS, Var_Append(MAKE_META_IGNORE_PATHS,
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
/* TODO: handle errors */ /* TODO: handle errors */
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr); str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
/* /*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
*/ */
freeIt = NULL; value = Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL);
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) { if (value.str != NULL) {
metaIgnorePatterns = TRUE; metaIgnorePatterns = TRUE;
bmake_free(freeIt); FStr_Done(&value);
} }
freeIt = NULL; value = Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL);
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) { if (value.str != NULL) {
metaIgnoreFilter = TRUE; metaIgnoreFilter = TRUE;
bmake_free(freeIt); FStr_Done(&value);
} }
} }
@ -710,7 +704,7 @@ meta_job_child(Job *job)
} }
if (pbm->mfp != NULL) { if (pbm->mfp != NULL) {
close(fileno(pbm->mfp)); close(fileno(pbm->mfp));
if (useFilemon && pbm->filemon) { if (useFilemon && pbm->filemon != NULL) {
pid_t pid; pid_t pid;
pid = getpid(); pid = getpid();
@ -733,7 +727,7 @@ meta_job_parent(Job *job, pid_t pid)
} else { } else {
pbm = &Mybm; pbm = &Mybm;
} }
if (useFilemon && pbm->filemon) { if (useFilemon && pbm->filemon != NULL) {
filemon_setpid_parent(pbm->filemon, pid); filemon_setpid_parent(pbm->filemon, pid);
} }
#endif #endif
@ -750,7 +744,7 @@ meta_job_fd(Job *job)
} else { } else {
pbm = &Mybm; pbm = &Mybm;
} }
if (useFilemon && pbm->filemon) { if (useFilemon && pbm->filemon != NULL) {
return filemon_readfd(pbm->filemon); return filemon_readfd(pbm->filemon);
} }
#endif #endif
@ -768,7 +762,7 @@ meta_job_event(Job *job)
} else { } else {
pbm = &Mybm; pbm = &Mybm;
} }
if (useFilemon && pbm->filemon) { if (useFilemon && pbm->filemon != NULL) {
return filemon_process(pbm->filemon); return filemon_process(pbm->filemon);
} }
#endif #endif
@ -776,7 +770,7 @@ meta_job_event(Job *job)
} }
void void
meta_job_error(Job *job, GNode *gn, int flags, int status) meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status)
{ {
char cwd[MAXPATHLEN]; char cwd[MAXPATHLEN];
BuildMon *pbm; BuildMon *pbm;
@ -790,11 +784,9 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
} }
if (pbm->mfp != NULL) { if (pbm->mfp != NULL) {
fprintf(pbm->mfp, "\n*** Error code %d%s\n", fprintf(pbm->mfp, "\n*** Error code %d%s\n",
status, status, ignerr ? "(ignored)" : "");
(flags & JOB_IGNERR) ?
"(ignored)" : "");
} }
if (gn) { if (gn != NULL) {
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL); Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
} }
getcwd(cwd, sizeof cwd); getcwd(cwd, sizeof cwd);
@ -826,15 +818,16 @@ meta_job_output(Job *job, char *cp, const char *nl)
(void)Var_Subst("${" MAKE_META_PREFIX "}", (void)Var_Subst("${" MAKE_META_PREFIX "}",
VAR_GLOBAL, VARE_WANTRES, &meta_prefix); VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
/* TODO: handle errors */ /* TODO: handle errors */
if ((cp2 = strchr(meta_prefix, '$'))) if ((cp2 = strchr(meta_prefix, '$')) != NULL)
meta_prefix_len = (size_t)(cp2 - meta_prefix); meta_prefix_len = (size_t)(cp2 - meta_prefix);
else else
meta_prefix_len = strlen(meta_prefix); meta_prefix_len = strlen(meta_prefix);
} }
if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
cp = strchr(cp+1, '\n'); cp = strchr(cp + 1, '\n');
if (!cp++) if (cp == NULL)
return; return;
cp++;
} }
} }
fprintf(pbm->mfp, "%s%s", cp, nl); fprintf(pbm->mfp, "%s%s", cp, nl);
@ -854,7 +847,7 @@ meta_cmd_finish(void *pbmp)
pbm = &Mybm; pbm = &Mybm;
#ifdef USE_FILEMON #ifdef USE_FILEMON
if (pbm->filemon) { if (pbm->filemon != NULL) {
while (filemon_process(pbm->filemon) > 0) while (filemon_process(pbm->filemon) > 0)
continue; continue;
if (filemon_close(pbm->filemon) == -1) if (filemon_close(pbm->filemon) == -1)
@ -898,11 +891,9 @@ meta_job_finish(Job *job)
void void
meta_finish(void) meta_finish(void)
{ {
if (metaBailiwick != NULL) Lst_Done(&metaBailiwick);
Lst_Free(metaBailiwick);
free(metaBailiwickStr); free(metaBailiwickStr);
if (metaIgnorePaths != NULL) Lst_Done(&metaIgnorePaths);
Lst_Free(metaIgnorePaths);
free(metaIgnorePathsStr); free(metaIgnorePathsStr);
} }
@ -936,32 +927,39 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ); newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
if (newsz <= bufsz) if (newsz <= bufsz)
return x; /* truncated */ return x; /* truncated */
DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz); DEBUG2(META, "growing buffer %u -> %u\n",
(unsigned)bufsz, (unsigned)newsz);
p = bmake_realloc(buf, newsz); p = bmake_realloc(buf, newsz);
if (p) { *bufp = buf = p;
*bufp = buf = p; *szp = bufsz = newsz;
*szp = bufsz = newsz; /* fetch the rest */
/* fetch the rest */ if (fgets(&buf[x], (int)bufsz - x, fp) == NULL)
if (fgets(&buf[x], (int)bufsz - x, fp) == NULL) return x; /* truncated! */
return x; /* truncated! */ goto check_newline;
goto check_newline;
}
} }
} }
return 0; return 0;
} }
/* Lst_ForEachUntil wants 1 to stop search */ static Boolean
static int prefix_match(const char *prefix, const char *path)
prefix_match(void *p, void *q)
{ {
const char *prefix = p;
const char *path = q;
size_t n = strlen(prefix); size_t n = strlen(prefix);
return strncmp(path, prefix, n) == 0; return strncmp(path, prefix, n) == 0;
} }
static Boolean
has_any_prefix(const char *path, StringList *prefixes)
{
StringListNode *ln;
for (ln = prefixes->first; ln != NULL; ln = ln->next)
if (prefix_match(ln->datum, path))
return TRUE;
return FALSE;
}
/* See if the path equals prefix or starts with "prefix/". */ /* See if the path equals prefix or starts with "prefix/". */
static Boolean static Boolean
path_starts_with(const char *path, const char *prefix) path_starts_with(const char *path, const char *prefix)
@ -973,7 +971,7 @@ path_starts_with(const char *path, const char *prefix)
return path[n] == '\0' || path[n] == '/'; return path[n] == '\0' || path[n] == '/';
} }
static int static Boolean
meta_ignore(GNode *gn, const char *p) meta_ignore(GNode *gn, const char *p)
{ {
char fname[MAXPATHLEN]; char fname[MAXPATHLEN];
@ -983,7 +981,7 @@ meta_ignore(GNode *gn, const char *p)
if (*p == '/') { if (*p == '/') {
cached_realpath(p, fname); /* clean it up */ cached_realpath(p, fname); /* clean it up */
if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) { if (has_any_prefix(fname, &metaIgnorePaths)) {
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p); DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
#endif #endif
@ -999,7 +997,7 @@ meta_ignore(GNode *gn, const char *p)
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm); (void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
/* TODO: handle errors */ /* TODO: handle errors */
if (*pm) { if (pm[0] != '\0') {
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p); DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
#endif #endif
@ -1043,7 +1041,7 @@ meta_ignore(GNode *gn, const char *p)
* if we detect this we want to reproduce it. * if we detect this we want to reproduce it.
* Setting oodate TRUE will have that effect. * Setting oodate TRUE will have that effect.
*/ */
#define CHECK_VALID_META(p) if (!(p && *p)) { \ #define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
warnx("%s: %d: malformed", fname, lineno); \ warnx("%s: %d: malformed", fname, lineno); \
oodate = TRUE; \ oodate = TRUE; \
continue; \ continue; \
@ -1052,7 +1050,7 @@ meta_ignore(GNode *gn, const char *p)
#define DEQUOTE(p) if (*p == '\'') { \ #define DEQUOTE(p) if (*p == '\'') { \
char *ep; \ char *ep; \
p++; \ p++; \
if ((ep = strchr(p, '\''))) \ if ((ep = strchr(p, '\'')) != NULL) \
*ep = '\0'; \ *ep = '\0'; \
} }
@ -1080,32 +1078,30 @@ meta_oodate(GNode *gn, Boolean oodate)
char fname1[MAXPATHLEN]; char fname1[MAXPATHLEN];
char fname2[MAXPATHLEN]; char fname2[MAXPATHLEN];
char fname3[MAXPATHLEN]; char fname3[MAXPATHLEN];
const char *dname; FStr dname;
const char *tname; const char *tname;
char *p; char *p;
char *cp;
char *link_src; char *link_src;
char *move_target; char *move_target;
static size_t cwdlen = 0; static size_t cwdlen = 0;
static size_t tmplen = 0; static size_t tmplen = 0;
FILE *fp; FILE *fp;
Boolean needOODATE = FALSE; Boolean needOODATE = FALSE;
StringList *missingFiles; StringList missingFiles;
Boolean have_filemon = FALSE; Boolean have_filemon = FALSE;
void *objdir_freeIt;
if (oodate) if (oodate)
return oodate; /* we're done */ return oodate; /* we're done */
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt); dname = Var_Value(".OBJDIR", gn);
tname = GNode_VarTarget(gn); tname = GNode_VarTarget(gn);
/* if this succeeds fname3 is realpath of dname */ /* if this succeeds fname3 is realpath of dname */
if (!meta_needed(gn, dname, fname3, FALSE)) if (!meta_needed(gn, dname.str, fname3, FALSE))
goto oodate_out; goto oodate_out;
dname = fname3; dname.str = fname3;
missingFiles = Lst_New(); Lst_Init(&missingFiles);
/* /*
* We need to check if the target is out-of-date. This includes * We need to check if the target is out-of-date. This includes
@ -1115,7 +1111,7 @@ meta_oodate(GNode *gn, Boolean oodate)
*/ */
Make_DoAllVar(gn); Make_DoAllVar(gn);
meta_name(fname, sizeof fname, dname, tname, dname); meta_name(fname, sizeof fname, dname.str, tname, dname.str);
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG1(META, "meta_oodate: %s\n", fname); DEBUG1(META, "meta_oodate: %s\n", fname);
@ -1152,7 +1148,7 @@ meta_oodate(GNode *gn, Boolean oodate)
/* we want to track all the .meta we read */ /* we want to track all the .meta we read */
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
cmdNode = gn->commands->first; cmdNode = gn->commands.first;
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
lineno++; lineno++;
if (buf[x - 1] == '\n') if (buf[x - 1] == '\n')
@ -1221,8 +1217,7 @@ meta_oodate(GNode *gn, Boolean oodate)
CHECK_VALID_META(p); CHECK_VALID_META(p);
pid = atoi(p); pid = atoi(p);
if (pid > 0 && pid != lastpid) { if (pid > 0 && pid != lastpid) {
const char *ldir; FStr ldir;
void *tp;
if (lastpid > 0) { if (lastpid > 0) {
/* We need to remember these. */ /* We need to remember these. */
@ -1232,15 +1227,15 @@ meta_oodate(GNode *gn, Boolean oodate)
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid); snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid); snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
lastpid = pid; lastpid = pid;
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); ldir = Var_Value(ldir_vname, VAR_GLOBAL);
if (ldir) { if (ldir.str != NULL) {
strlcpy(latestdir, ldir, sizeof latestdir); strlcpy(latestdir, ldir.str, sizeof latestdir);
bmake_free(tp); FStr_Done(&ldir);
} }
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); ldir = Var_Value(lcwd_vname, VAR_GLOBAL);
if (ldir) { if (ldir.str != NULL) {
strlcpy(lcwd, ldir, sizeof lcwd); strlcpy(lcwd, ldir.str, sizeof lcwd);
bmake_free(tp); FStr_Done(&ldir);
} }
} }
/* Skip past the pid. */ /* Skip past the pid. */
@ -1305,13 +1300,15 @@ meta_oodate(GNode *gn, Boolean oodate)
* the src as for 'R'ead * the src as for 'R'ead
* and the target as for 'W'rite. * and the target as for 'W'rite.
*/ */
cp = p; /* save this for a second */ {
/* now get target */ char *cp = p; /* save this for a second */
if (strsep(&p, " ") == NULL) /* now get target */
continue; if (strsep(&p, " ") == NULL)
CHECK_VALID_META(p); continue;
move_target = p; CHECK_VALID_META(p);
p = cp; move_target = p;
p = cp;
}
/* 'L' and 'M' put single quotes around the args */ /* 'L' and 'M' put single quotes around the args */
DEQUOTE(p); DEQUOTE(p);
DEQUOTE(move_target); DEQUOTE(move_target);
@ -1319,12 +1316,12 @@ meta_oodate(GNode *gn, Boolean oodate)
case 'D': /* unlink */ case 'D': /* unlink */
if (*p == '/') { if (*p == '/') {
/* remove any missingFiles entries that match p */ /* remove any missingFiles entries that match p */
StringListNode *ln = missingFiles->first; StringListNode *ln = missingFiles.first;
while (ln != NULL) { while (ln != NULL) {
StringListNode *next = ln->next; StringListNode *next = ln->next;
if (path_starts_with(ln->datum, p)) { if (path_starts_with(ln->datum, p)) {
free(ln->datum); free(ln->datum);
Lst_Remove(missingFiles, ln); Lst_Remove(&missingFiles, ln);
} }
ln = next; ln = next;
} }
@ -1368,14 +1365,14 @@ meta_oodate(GNode *gn, Boolean oodate)
if (*p != '/') if (*p != '/')
break; break;
if (Lst_IsEmpty(metaBailiwick)) if (Lst_IsEmpty(&metaBailiwick))
break; break;
/* ignore cwd - normal dependencies handle those */ /* ignore cwd - normal dependencies handle those */
if (strncmp(p, cwd, cwdlen) == 0) if (strncmp(p, cwd, cwdlen) == 0)
break; break;
if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p)) if (!has_any_prefix(p, &metaBailiwick))
break; break;
/* tmpdir might be within */ /* tmpdir might be within */
@ -1384,13 +1381,13 @@ meta_oodate(GNode *gn, Boolean oodate)
/* ignore anything containing the string "tmp" */ /* ignore anything containing the string "tmp" */
/* XXX: The arguments to strstr must be swapped. */ /* XXX: The arguments to strstr must be swapped. */
if ((strstr("tmp", p))) if (strstr("tmp", p) != NULL)
break; break;
if ((link_src != NULL && cached_lstat(p, &cst) < 0) || if ((link_src != NULL && cached_lstat(p, &cst) < 0) ||
(link_src == NULL && cached_stat(p, &cst) < 0)) { (link_src == NULL && cached_stat(p, &cst) < 0)) {
if (!meta_ignore(gn, p)) if (!meta_ignore(gn, p))
append_if_new(missingFiles, p); append_if_new(&missingFiles, p);
} }
break; break;
check_link_src: check_link_src:
@ -1418,13 +1415,13 @@ meta_oodate(GNode *gn, Boolean oodate)
char *sdirs[4]; char *sdirs[4];
char **sdp; char **sdp;
int sdx = 0; int sdx = 0;
int found = 0; Boolean found = FALSE;
if (*p == '/') { if (*p == '/') {
sdirs[sdx++] = p; /* done */ sdirs[sdx++] = p; /* done */
} else { } else {
if (strcmp(".", p) == 0) if (strcmp(".", p) == 0)
continue; /* no point */ continue; /* no point */
/* Check vs latestdir */ /* Check vs latestdir */
snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p); snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p);
@ -1443,13 +1440,13 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
sdirs[sdx++] = NULL; sdirs[sdx++] = NULL;
for (sdp = sdirs; *sdp && !found; sdp++) { for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
#ifdef DEBUG_META_MODE #ifdef DEBUG_META_MODE
DEBUG3(META, "%s: %d: looking for: %s\n", DEBUG3(META, "%s: %d: looking for: %s\n",
fname, lineno, *sdp); fname, lineno, *sdp);
#endif #endif
if (cached_stat(*sdp, &cst) == 0) { if (cached_stat(*sdp, &cst) == 0) {
found = 1; found = TRUE;
p = *sdp; p = *sdp;
} }
} }
@ -1473,7 +1470,7 @@ meta_oodate(GNode *gn, Boolean oodate)
* A referenced file outside of CWD is missing. * A referenced file outside of CWD is missing.
* We cannot catch every eventuality here... * We cannot catch every eventuality here...
*/ */
append_if_new(missingFiles, p); append_if_new(&missingFiles, p);
} }
} }
if (buf[0] == 'E') { if (buf[0] == 'E') {
@ -1496,12 +1493,13 @@ meta_oodate(GNode *gn, Boolean oodate)
fname, lineno); fname, lineno);
oodate = TRUE; oodate = TRUE;
} else { } else {
const char *cp;
char *cmd = cmdNode->datum; char *cmd = cmdNode->datum;
Boolean hasOODATE = FALSE; Boolean hasOODATE = FALSE;
if (strstr(cmd, "$?")) if (strstr(cmd, "$?") != NULL)
hasOODATE = TRUE; hasOODATE = TRUE;
else if ((cp = strstr(cmd, ".OODATE"))) { else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
/* check for $[{(].OODATE[:)}] */ /* check for $[{(].OODATE[:)}] */
if (cp > cmd + 2 && cp[-2] == '$') if (cp > cmd + 2 && cp[-2] == '$')
hasOODATE = TRUE; hasOODATE = TRUE;
@ -1514,7 +1512,7 @@ meta_oodate(GNode *gn, Boolean oodate)
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd); (void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
/* TODO: handle errors */ /* TODO: handle errors */
if ((cp = strchr(cmd, '\n'))) { if ((cp = strchr(cmd, '\n')) != NULL) {
int n; int n;
/* /*
@ -1534,8 +1532,8 @@ meta_oodate(GNode *gn, Boolean oodate)
warnx("%s: %d: line truncated at %u", fname, lineno, x); warnx("%s: %d: line truncated at %u", fname, lineno, x);
break; break;
} }
cp = strchr(++cp, '\n'); cp = strchr(cp + 1, '\n');
} while (cp); } while (cp != NULL);
if (buf[x - 1] == '\n') if (buf[x - 1] == '\n')
buf[x - 1] = '\0'; buf[x - 1] = '\0';
} }
@ -1571,9 +1569,9 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
fclose(fp); fclose(fp);
if (!Lst_IsEmpty(missingFiles)) { if (!Lst_IsEmpty(&missingFiles)) {
DEBUG2(META, "%s: missing files: %s...\n", DEBUG2(META, "%s: missing files: %s...\n",
fname, (char *)missingFiles->first->datum); fname, (char *)missingFiles.first->datum);
oodate = TRUE; oodate = TRUE;
} }
if (!oodate && !have_filemon && filemonMissing) { if (!oodate && !have_filemon && filemonMissing) {
@ -1582,10 +1580,11 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
} else { } else {
if (writeMeta && (metaMissing || (gn->type & OP_META))) { if (writeMeta && (metaMissing || (gn->type & OP_META))) {
cp = NULL; const char *cp = NULL;
/* if target is in .CURDIR we do not need a meta file */ /* if target is in .CURDIR we do not need a meta file */
if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) { if (gn->path != NULL && (cp = strrchr(gn->path, '/')) != NULL &&
(cp > gn->path)) {
if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) { if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
cp = NULL; /* not in .CURDIR */ cp = NULL; /* not in .CURDIR */
} }
@ -1598,7 +1597,7 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
} }
Lst_Destroy(missingFiles, free); Lst_DoneCall(&missingFiles, free);
if (oodate && needOODATE) { if (oodate && needOODATE) {
/* /*
@ -1611,7 +1610,7 @@ meta_oodate(GNode *gn, Boolean oodate)
} }
oodate_out: oodate_out:
bmake_free(objdir_freeIt); FStr_Done(&dname);
return oodate; return oodate;
} }
@ -1661,7 +1660,7 @@ meta_compat_parent(pid_t child)
close(childPipe[1]); /* child side */ close(childPipe[1]); /* child side */
outfd = childPipe[0]; outfd = childPipe[0];
#ifdef USE_FILEMON #ifdef USE_FILEMON
metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1; metafd = Mybm.filemon != NULL ? filemon_readfd(Mybm.filemon) : -1;
#else #else
metafd = -1; metafd = -1;
#endif #endif
@ -1686,7 +1685,7 @@ meta_compat_parent(pid_t child)
err(1, "select"); err(1, "select");
} }
if (outfd != -1 && FD_ISSET(outfd, &readfds)) do { if (outfd != -1 && FD_ISSET(outfd, &readfds) != 0) do {
/* XXX this is not line-buffered */ /* XXX this is not line-buffered */
ssize_t nread = read(outfd, buf, sizeof buf - 1); ssize_t nread = read(outfd, buf, sizeof buf - 1);
if (nread == -1) if (nread == -1)
@ -1700,12 +1699,12 @@ meta_compat_parent(pid_t child)
fflush(stdout); fflush(stdout);
buf[nread] = '\0'; buf[nread] = '\0';
meta_job_output(NULL, buf, ""); meta_job_output(NULL, buf, "");
} while (0); } while (/*CONSTCOND*/0);
if (metafd != -1 && FD_ISSET(metafd, &readfds)) { if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0) if (meta_job_event(NULL) <= 0)
metafd = -1; metafd = -1;
} }
} }
} }
#endif /* USE_META */ #endif /* USE_META */

View File

@ -1,4 +1,4 @@
/* $NetBSD: meta.h,v 1.8 2020/10/19 23:43:55 rillig Exp $ */ /* $NetBSD: meta.h,v 1.9 2020/12/10 20:49:11 rillig Exp $ */
/* /*
* Things needed for 'meta' mode. * Things needed for 'meta' mode.
@ -48,7 +48,7 @@ void meta_job_child(struct Job *);
void meta_job_parent(struct Job *, pid_t); void meta_job_parent(struct Job *, pid_t);
int meta_job_fd(struct Job *); int meta_job_fd(struct Job *);
int meta_job_event(struct Job *); int meta_job_event(struct Job *);
void meta_job_error(struct Job *, GNode *, int, int); void meta_job_error(struct Job *, GNode *, Boolean, int);
void meta_job_output(struct Job *, char *, const char *); void meta_job_output(struct Job *, char *, const char *);
int meta_cmd_finish(void *); int meta_cmd_finish(void *);
int meta_job_finish(struct Job *); int meta_job_finish(struct Job *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: metachar.h,v 1.12 2020/11/10 00:32:12 rillig Exp $ */ /* $NetBSD: metachar.h,v 1.13 2021/01/10 21:20:46 rillig Exp $ */
/*- /*-
* Copyright (c) 2015 The NetBSD Foundation, Inc. * Copyright (c) 2015 The NetBSD Foundation, Inc.
@ -35,7 +35,7 @@
extern unsigned char _metachar[]; extern unsigned char _metachar[];
#define is_shell_metachar(c) _metachar[(c) & 0x7f] #define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
MAKE_INLINE int MAKE_INLINE int
needshell(const char *cmd) needshell(const char *cmd)

View File

@ -1,3 +1,42 @@
2021-01-06 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210101
* dirdeps.mk: first time we are read, just use TARGET_SPEC for
_DEP_TARGET_SPEC
2020-12-22 Simon J Gerraty <sjg@beast.crufty.net>
* sys.mk (MAKE_SHELL): use ${.SHELL:Ush}
and use := when setting SHELL
2020-12-21 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201221
* dirdeps-options.mk: latest bmake allows only one arg to .undef
2020-12-11 Simon J Gerraty <sjg@beast.crufty.net>
* dirdeps-targets.mk: allow for "." in DIRDEPS_TARGETS_DIRS
so that any directory can be treated as a target.
2020-11-26 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201126
* own.mk: use .MAKE.{UID,GID} if available.
* init.mk: suppress _SKIP_BUILD warning if doing -V
2020-11-20 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201120
* init.mk: rename LEVEL0_TARGETS to DIRDEPS_BUILD_LEVEL0_TARGETS
* dirdeps-targets.mk: fix typo in comment
2020-11-06 Simon J Gerraty <sjg@beast.crufty.net> 2020-11-06 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20201106 * install-mk (MK_VERSION): 20201106

View File

@ -1,4 +1,4 @@
# $Id: dirdeps-options.mk,v 1.17 2020/08/07 01:57:38 sjg Exp $ # $Id: dirdeps-options.mk,v 1.18 2020/12/22 18:10:34 sjg Exp $
# #
# @(#) Copyright (c) 2018-2020, Simon J. Gerraty # @(#) Copyright (c) 2018-2020, Simon J. Gerraty
# #
@ -59,7 +59,8 @@ DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
# note that we need to include $o in the variable _o$o # note that we need to include $o in the variable _o$o
# to ensure correct evaluation. # to ensure correct evaluation.
.for o in ${DIRDEPS_OPTIONS} .for o in ${DIRDEPS_OPTIONS}
.undef _o$o _v$o .undef _o$o
.undef _v$o
.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST} .for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
.if defined(MK_$o.$x) .if defined(MK_$o.$x)
_o$o ?= MK_$o.$x _o$o ?= MK_$o.$x

View File

@ -1,5 +1,5 @@
# RCSid: # RCSid:
# $Id: dirdeps-targets.mk,v 1.22 2020/08/15 18:00:11 sjg Exp $ # $Id: dirdeps-targets.mk,v 1.24 2020/12/11 18:15:43 sjg Exp $
# #
# @(#) Copyright (c) 2019-2020 Simon J. Gerraty # @(#) Copyright (c) 2019-2020 Simon J. Gerraty
# #
@ -41,6 +41,7 @@
.-include <local.dirdeps-targets.mk> .-include <local.dirdeps-targets.mk>
# for DIRDEPS_BUILD this is how we prime the pump # for DIRDEPS_BUILD this is how we prime the pump
# include . to allow any directory to work as a target
DIRDEPS_TARGETS_DIRS ?= targets targets/pseudo DIRDEPS_TARGETS_DIRS ?= targets targets/pseudo
# these prefixes can modify how we behave # these prefixes can modify how we behave
# they need to be stripped when looking for target dirs # they need to be stripped when looking for target dirs
@ -76,7 +77,7 @@ DIRDEPS_TARGETS_MACHINE_LIST += \
DIRDEPS_TARGETS_MACHINE_LIST := ${DIRDEPS_TARGETS_MACHINE_LIST:O:u} DIRDEPS_TARGETS_MACHINE_LIST := ${DIRDEPS_TARGETS_MACHINE_LIST:O:u}
# raw Makefile.depend* list # raw Makefile.depend* list
tdeps != 'cd' ${SRCTOP} && 'ls' -1 ${tdirs:O:u:@d@$d/${.MAKE.DEPENDFILE_PREFIX}*@} 2> /dev/null; echo tdeps != 'cd' ${SRCTOP} && 'ls' -1 ${tdirs:O:u:@d@$d/${.MAKE.DEPENDFILE_PREFIX}*@:S,^./,,} 2> /dev/null; echo
.if ${DEBUG_DIRDEPS_TARGETS:U:Mdep*} != "" .if ${DEBUG_DIRDEPS_TARGETS:U:Mdep*} != ""
.info tdeps=${tdeps} .info tdeps=${tdeps}
.endif .endif
@ -135,7 +136,7 @@ DIRDEPS := ${DIRDEPS:O:u}
# if we got DIRDEPS get to work # if we got DIRDEPS get to work
.if !empty(DIRDEPS) .if !empty(DIRDEPS)
DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@} DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@}
# some targets what to tweak options we might want to process now # some targets want to tweak options we might want to process now
.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,} .for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,}
.-include <$m> .-include <$m>
.endfor .endfor

View File

@ -1,6 +1,6 @@
# $Id: dirdeps.mk,v 1.130 2020/11/02 00:34:30 sjg Exp $ # $Id: dirdeps.mk,v 1.131 2021/01/07 00:57:51 sjg Exp $
# Copyright (c) 2010-2020, Simon J. Gerraty # Copyright (c) 2010-2021, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc. # Copyright (c) 2010-2018, Juniper Networks, Inc.
# All rights reserved. # All rights reserved.
# #
@ -265,24 +265,9 @@ N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
# if we were included recursively _DEP_TARGET_SPEC should be valid. # if we were included recursively _DEP_TARGET_SPEC should be valid.
.if empty(_DEP_TARGET_SPEC) .if empty(_DEP_TARGET_SPEC)
# we may or may not have included a dependfile yet # if not, just use TARGET_SPEC
.if defined(.INCLUDEDFROMFILE) _DEP_TARGET_SPEC := ${TARGET_SPEC}
_last_dependfile := ${.INCLUDEDFROMFILE:M${.MAKE.DEPENDFILE_PREFIX}*} .if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != ""
.else
_last_dependfile := ${.MAKE.MAKEFILES:M*/${.MAKE.DEPENDFILE_PREFIX}*:[-1]}
.endif
.if ${_debug_reldir:U0}
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _last_dependfile='${_last_dependfile}'
.endif
.if empty(_last_dependfile) || ${_last_dependfile:E:${N_notmachine}} == ""
# this is all we have to work with
DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}}
_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC}
.else
_DEP_TARGET_SPEC = ${_last_dependfile:${M_dep_qual_fixes:ts:}:E}
.endif
.if !empty(_last_dependfile)
# record that we've read dependfile for this # record that we've read dependfile for this
_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: _dirdeps_checked.${_CURDIR}.${TARGET_SPEC}:
.endif .endif

View File

@ -1,4 +1,4 @@
# $Id: init.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $ # $Id: init.mk,v 1.25 2020/11/27 17:59:46 sjg Exp $
# #
# @(#) Copyright (c) 2002, Simon J. Gerraty # @(#) Copyright (c) 2002, Simon J. Gerraty
# #
@ -65,14 +65,15 @@ CC_PIC?= -DPIC
CXX_PIC?= ${CC_PIC} CXX_PIC?= ${CC_PIC}
PROFFLAGS?= -DGPROF -DPROF PROFFLAGS?= -DGPROF -DPROF
# targets that are ok at level 0 .if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
LEVEL0_TARGETS += clean* destory* # targets that are ok at level 0
M_ListToSkip= O:u:S,^,N,:ts: DIRDEPS_BUILD_LEVEL0_TARGETS += clean* destroy*
M_ListToSkip?= O:u:S,^,N,:ts:
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != "" .if ${.TARGETS:Uall:${DIRDEPS_BUILD_LEVEL0_TARGETS:${M_ListToSkip}}} != ""
# this tells lib.mk and prog.mk to not actually build anything # this tells lib.mk and prog.mk to not actually build anything
_SKIP_BUILD = not building at level 0 _SKIP_BUILD = not building at level 0
.endif .endif
.endif
.if !defined(.PARSEDIR) .if !defined(.PARSEDIR)
# no-op is the best we can do if not bmake. # no-op is the best we can do if not bmake.
@ -80,13 +81,15 @@ _SKIP_BUILD = not building at level 0
.endif .endif
# define this once for consistency # define this once for consistency
.if empty(_SKIP_BUILD) .if !defined(_SKIP_BUILD)
# beforebuild is a hook for things that must be done early # beforebuild is a hook for things that must be done early
all: beforebuild .WAIT realbuild all: beforebuild .WAIT realbuild
.else .else
all: .PHONY all: .PHONY
.if !empty(_SKIP_BUILD) && ${.MAKEFLAGS:M-V} == ""
.warning ${_SKIP_BUILD} .warning ${_SKIP_BUILD}
.endif .endif
.endif
beforebuild: beforebuild:
realbuild: realbuild:

4
contrib/bmake/mk/install-mk Normal file → Executable file
View File

@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net> # Simon J. Gerraty <sjg@crufty.net>
# RCSid: # RCSid:
# $Id: install-mk,v 1.184 2020/11/08 05:47:56 sjg Exp $ # $Id: install-mk,v 1.190 2021/01/07 00:58:42 sjg Exp $
# #
# @(#) Copyright (c) 1994 Simon J. Gerraty # @(#) Copyright (c) 1994 Simon J. Gerraty
# #
@ -70,7 +70,7 @@
# sjg@crufty.net # sjg@crufty.net
# #
MK_VERSION=20201106 MK_VERSION=20210101
OWNER= OWNER=
GROUP= GROUP=
MODE=444 MODE=444

View File

@ -1,4 +1,4 @@
# $Id: meta.subdir.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $ # $Id: meta.subdir.mk,v 1.13 2021/01/05 22:24:37 sjg Exp $
# #
# @(#) Copyright (c) 2010, Simon J. Gerraty # @(#) Copyright (c) 2010, Simon J. Gerraty
@ -17,7 +17,7 @@
.if !defined(NO_SUBDIR) && !empty(SUBDIR) .if !defined(NO_SUBDIR) && !empty(SUBDIR)
.if make(destroy*) || make(clean*) .if make(destroy*) || make(clean*)
.MAKE.MODE = compat .MAKE.MODE = compat
.if !commands(destroy) .if !commands(obj)
.-include <bsd.obj.mk> .-include <bsd.obj.mk>
.endif .endif
.elif ${.MAKE.LEVEL} == 0 .elif ${.MAKE.LEVEL} == 0

0
contrib/bmake/mk/mkopt.sh Executable file → Normal file
View File

View File

@ -1,4 +1,4 @@
# $Id: own.mk,v 1.41 2020/08/19 17:51:53 sjg Exp $ # $Id: own.mk,v 1.42 2020/11/27 18:00:08 sjg Exp $
.if !target(__${.PARSEFILE}__) .if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: __${.PARSEFILE}__:
@ -125,10 +125,10 @@ OPTIONS_DEFAULT_DEPENDENT+= \
.if ${MK_INSTALL_AS_USER} == "yes" .if ${MK_INSTALL_AS_USER} == "yes"
# We ignore this if user is root. # We ignore this if user is root.
_uid!= id -u _uid:= ${.MAKE.UID:U${id -u:L:sh}}
.if ${_uid} != 0 .if ${_uid} != 0
.if !defined(USERGRP) .if !defined(USERGRP)
USERGRP!= id -g USERGRP:= ${.MAKE.GID:U${id -g:L:sh}}
.export USERGRP .export USERGRP
.endif .endif
.for x in BIN CONF DOC INC INFO FILES KMOD LIB MAN NLS PROG SHARE .for x in BIN CONF DOC INC INFO FILES KMOD LIB MAN NLS PROG SHARE

View File

@ -1,4 +1,4 @@
# $Id: sys.mk,v 1.51 2020/08/19 17:51:53 sjg Exp $ # $Id: sys.mk,v 1.52 2020/12/22 20:44:24 sjg Exp $
# #
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty # @(#) Copyright (c) 2003-2009, Simon J. Gerraty
# #
@ -118,8 +118,8 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
unix ?= We run ${_HOST_OSNAME}. unix ?= We run ${_HOST_OSNAME}.
# We need a Bourne/POSIX shell # We need a Bourne/POSIX shell
MAKE_SHELL ?= sh MAKE_SHELL ?= ${.SHELL:Ush}
SHELL ?= ${MAKE_SHELL} SHELL := ${MAKE_SHELL}
# A race condition in mkdir, means that it can bail if another # A race condition in mkdir, means that it can bail if another
# process made a dir that mkdir expected to. # process made a dir that mkdir expected to.

View File

@ -1,4 +1,4 @@
/* $NetBSD: nonints.h,v 1.162 2020/11/16 21:48:18 rillig Exp $ */ /* $NetBSD: nonints.h,v 1.186 2020/12/28 00:46:24 rillig Exp $ */
/*- /*-
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -86,7 +86,7 @@ Boolean Arch_LibOODate(GNode *);
Boolean Arch_IsLib(GNode *); Boolean Arch_IsLib(GNode *);
/* compat.c */ /* compat.c */
int Compat_RunCommand(const char *, GNode *); int Compat_RunCommand(const char *, GNode *, StringListNode *);
void Compat_Run(GNodeList *); void Compat_Run(GNodeList *);
void Compat_Make(GNode *, GNode *); void Compat_Make(GNode *, GNode *);
@ -96,6 +96,21 @@ CondEvalResult Cond_EvalLine(const char *);
void Cond_restore_depth(unsigned int); void Cond_restore_depth(unsigned int);
unsigned int Cond_save_depth(void); unsigned int Cond_save_depth(void);
/* dir.c; see also dir.h */
MAKE_INLINE const char *
str_basename(const char *pathname)
{
const char *lastSlash = strrchr(pathname, '/');
return lastSlash != NULL ? lastSlash + 1 : pathname;
}
MAKE_INLINE SearchPath *
SearchPath_New(void)
{ return Lst_New(); }
void SearchPath_Free(SearchPath *);
/* for.c */ /* for.c */
int For_Eval(const char *); int For_Eval(const char *);
Boolean For_Accum(const char *); Boolean For_Accum(const char *);
@ -109,7 +124,6 @@ void JobReapChild(pid_t, WAIT_T, Boolean);
/* main.c */ /* main.c */
Boolean GetBooleanVar(const char *, Boolean); Boolean GetBooleanVar(const char *, Boolean);
void Main_ParseArgLine(const char *); void Main_ParseArgLine(const char *);
void MakeMode(const char *);
char *Cmd_Exec(const char *, const char **); char *Cmd_Exec(const char *, const char **);
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
@ -127,42 +141,96 @@ void Parse_Init(void);
void Parse_End(void); void Parse_End(void);
typedef enum VarAssignOp { typedef enum VarAssignOp {
VAR_NORMAL, /* = */ VAR_NORMAL, /* = */
VAR_SUBST, /* := */ VAR_SUBST, /* := */
VAR_SHELL, /* != or :sh= */ VAR_SHELL, /* != or :sh= */
VAR_APPEND, /* += */ VAR_APPEND, /* += */
VAR_DEFAULT /* ?= */ VAR_DEFAULT /* ?= */
} VarAssignOp; } VarAssignOp;
typedef struct VarAssign { typedef struct VarAssign {
char *varname; /* unexpanded */ char *varname; /* unexpanded */
VarAssignOp op; VarAssignOp op;
const char *value; /* unexpanded */ const char *value; /* unexpanded */
} VarAssign; } VarAssign;
typedef char *(*NextBufProc)(void *, size_t *); typedef char *(*ReadMoreProc)(void *, size_t *);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
Boolean Parse_IsVar(const char *, VarAssign *out_var); Boolean Parse_IsVar(const char *, VarAssign *out_var);
void Parse_DoVar(VarAssign *, GNode *); void Parse_DoVar(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *); void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int); void Parse_File(const char *, int);
void Parse_SetInput(const char *, int, int, NextBufProc, void *); void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
GNodeList *Parse_MainName(void); void Parse_MainName(GNodeList *);
int Parse_GetFatals(void); int Parse_GetFatals(void);
/* str.c */ /* str.c */
/* A read-only string that may need to be freed after use. */
typedef struct FStr {
const char *str;
void *freeIt;
} FStr;
/* A modifiable string that may need to be freed after use. */
typedef struct MFStr {
char *str;
void *freeIt;
} MFStr;
typedef struct Words { typedef struct Words {
char **words; char **words;
size_t len; size_t len;
void *freeIt; void *freeIt;
} Words; } Words;
/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
FStr_InitOwn(char *str)
{
return (FStr){ str, str };
}
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
return (FStr){ str, NULL };
}
MAKE_INLINE void
FStr_Done(FStr *fstr)
{
free(fstr->freeIt);
}
/* Return a string that is the sole owner of str. */
MAKE_INLINE MFStr
MFStr_InitOwn(char *str)
{
return (MFStr){ str, str };
}
/* Return a string that refers to the shared str. */
MAKE_INLINE MFStr
MFStr_InitRefer(char *str)
{
return (MFStr){ str, NULL };
}
MAKE_INLINE void
MFStr_Done(MFStr *mfstr)
{
free(mfstr->freeIt);
}
Words Str_Words(const char *, Boolean); Words Str_Words(const char *, Boolean);
MAKE_INLINE void MAKE_INLINE void
Words_Free(Words w) { Words_Free(Words w)
free(w.words); {
free(w.freeIt); free(w.words);
free(w.freeIt);
} }
char *str_concat2(const char *, const char *); char *str_concat2(const char *, const char *);
@ -204,15 +272,13 @@ GNode *Targ_FindNode(const char *);
GNode *Targ_GetNode(const char *); GNode *Targ_GetNode(const char *);
GNode *Targ_NewInternalNode(const char *); GNode *Targ_NewInternalNode(const char *);
GNode *Targ_GetEndNode(void); GNode *Targ_GetEndNode(void);
GNodeList *Targ_FindList(StringList *); void Targ_FindList(GNodeList *, StringList *);
Boolean Targ_Ignore(const GNode *);
Boolean Targ_Silent(const GNode *);
Boolean Targ_Precious(const GNode *); Boolean Targ_Precious(const GNode *);
void Targ_SetMain(GNode *); void Targ_SetMain(GNode *);
void Targ_PrintCmds(GNode *); void Targ_PrintCmds(GNode *);
void Targ_PrintNode(GNode *, int); void Targ_PrintNode(GNode *, int);
void Targ_PrintNodes(GNodeList *, int); void Targ_PrintNodes(GNodeList *, int);
char *Targ_FmtTime(time_t); const char *Targ_FmtTime(time_t);
void Targ_PrintType(int); void Targ_PrintType(int);
void Targ_PrintGraph(int); void Targ_PrintGraph(int);
void Targ_Propagate(void); void Targ_Propagate(void);
@ -222,112 +288,104 @@ void Var_Init(void);
void Var_End(void); void Var_End(void);
typedef enum VarEvalFlags { typedef enum VarEvalFlags {
VARE_NONE = 0, VARE_NONE = 0,
/* Expand and evaluate variables during parsing. /* Expand and evaluate variables during parsing.
* *
* TODO: Document what Var_Parse and Var_Subst return when this flag * TODO: Document what Var_Parse and Var_Subst return when this flag
* is not set. */ * is not set. */
VARE_WANTRES = 1 << 0, VARE_WANTRES = 1 << 0,
/* Treat undefined variables as errors. /* Treat undefined variables as errors.
* Must only be used in combination with VARE_WANTRES. */ * Must only be used in combination with VARE_WANTRES. */
VARE_UNDEFERR = 1 << 1, VARE_UNDEFERR = 1 << 1,
/* Keep '$$' as '$$' instead of reducing it to a single '$'. /* Keep '$$' as '$$' instead of reducing it to a single '$'.
* *
* Used in variable assignments using the ':=' operator. It allows * Used in variable assignments using the ':=' operator. It allows
* multiple such assignments to be chained without accidentally expanding * multiple such assignments to be chained without accidentally
* '$$file' to '$file' in the first assignment and interpreting it as * expanding '$$file' to '$file' in the first assignment and
* '${f}' followed by 'ile' in the next assignment. * interpreting it as '${f}' followed by 'ile' in the next assignment.
* *
* See also preserveUndefined, which preserves subexpressions that are * See also preserveUndefined, which preserves subexpressions that are
* based on undefined variables; maybe that can be converted to a flag * based on undefined variables; maybe that can be converted to a flag
* as well. */ * as well. */
VARE_KEEP_DOLLAR = 1 << 2 VARE_KEEP_DOLLAR = 1 << 2,
/*
* Keep undefined variables as-is instead of expanding them to an
* empty string.
*
* Example for a ':=' assignment:
* CFLAGS = $(.INCLUDES)
* CFLAGS := -I.. $(CFLAGS)
* # If .INCLUDES (an undocumented special variable, by the
* # way) is still undefined, the updated CFLAGS becomes
* # "-I.. $(.INCLUDES)".
*/
VARE_KEEP_UNDEF = 1 << 3
} VarEvalFlags; } VarEvalFlags;
typedef enum VarSetFlags { typedef enum VarSetFlags {
VAR_SET_NONE = 0, VAR_SET_NONE = 0,
/* do not export */ /* do not export */
VAR_SET_NO_EXPORT = 1 << 0, VAR_SET_NO_EXPORT = 1 << 0,
/* Make the variable read-only. No further modification is possible, /* Make the variable read-only. No further modification is possible,
* except for another call to Var_Set with the same flag. */ * except for another call to Var_Set with the same flag. */
VAR_SET_READONLY = 1 << 1 VAR_SET_READONLY = 1 << 1
} VarSetFlags; } VarSetFlags;
/* The state of error handling returned by Var_Parse. /* 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 { typedef enum VarParseResult {
/* Both parsing and evaluation succeeded. */ /* Both parsing and evaluation succeeded. */
VPR_OK = 0x0000, VPR_OK,
/* See if a message has already been printed for this error. */ /* Parsing or evaluating failed, with an error message. */
VPR_ANY_MSG = 0x0001, VPR_ERR,
/* Parsing failed. /*
* No error message has been printed yet. * Parsing succeeded, undefined expressions are allowed and the
* Deprecated, migrate to VPR_PARSE_MSG instead. */ * expression was still undefined after applying all modifiers.
VPR_PARSE_SILENT = 0x0002, * No error message is printed in this case.
*
* Some callers handle this case differently, so return this
* information to them, for now.
*
* TODO: Replace this with a new flag VARE_KEEP_UNDEFINED.
*/
VPR_UNDEF
/* 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; } VarParseResult;
typedef enum VarExportMode {
/* .export-env */
VEM_ENV,
/* .export: Initial export or update an already exported variable. */
VEM_PLAIN,
/* .export-literal: Do not expand the variable value. */
VEM_LITERAL
} VarExportMode;
void Var_DeleteVar(const char *, GNode *);
void Var_Delete(const char *, GNode *); void Var_Delete(const char *, GNode *);
void Var_Undef(const char *);
void Var_Set(const char *, const char *, GNode *); void Var_Set(const char *, const char *, GNode *);
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags); void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags);
void Var_Append(const char *, const char *, GNode *); void Var_Append(const char *, const char *, GNode *);
Boolean Var_Exists(const char *, GNode *); Boolean Var_Exists(const char *, GNode *);
const char *Var_Value(const char *, GNode *, void **); FStr Var_Value(const char *, GNode *);
const char *Var_ValueDirect(const char *, GNode *); const char *Var_ValueDirect(const char *, GNode *);
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *);
const char **, void **);
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **); VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
void Var_Stats(void); void Var_Stats(void);
void Var_Dump(GNode *); void Var_Dump(GNode *);
void Var_ExportVars(void); void Var_ReexportVars(void);
void Var_Export(const char *, Boolean); void Var_Export(VarExportMode, const char *);
void Var_UnExport(const char *); void Var_ExportVars(const char *);
void Var_UnExport(Boolean, const char *);
/* util.c */ /* util.c */
typedef void (*SignalProc)(int); typedef void (*SignalProc)(int);

0
contrib/bmake/os.sh Executable file → Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: pathnames.h,v 1.17 2009/04/11 09:41:18 apb Exp $ */ /* $NetBSD: pathnames.h,v 1.18 2020/11/29 09:27:40 rillig Exp $ */
/* /*
* Copyright (c) 1990, 1993 * Copyright (c) 1990, 1993
@ -29,7 +29,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90 * from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90
* $Id: pathnames.h,v 1.13 2009/08/26 23:43:42 sjg Exp $ * $Id: pathnames.h,v 1.14 2020/11/30 19:27:41 sjg Exp $
*/ */
#if HAVE_CONFIG_H #if HAVE_CONFIG_H
@ -43,12 +43,13 @@
#ifdef HAVE_PATHS_H #ifdef HAVE_PATHS_H
#include <paths.h> #include <paths.h>
#endif #endif
#define _PATH_OBJDIR "obj"
#define _PATH_OBJDIRPREFIX "/usr/obj" #define _PATH_OBJDIR "obj"
#define _PATH_OBJDIRPREFIX "/usr/obj"
#ifndef _PATH_DEFSHELLDIR #ifndef _PATH_DEFSHELLDIR
#define _PATH_DEFSHELLDIR "/bin" #define _PATH_DEFSHELLDIR "/bin"
#endif #endif
#define _PATH_DEFSYSMK "sys.mk" #define _PATH_DEFSYSMK "sys.mk"
#define _path_defsyspath "/usr/share/mk:/usr/local/share/mk:/opt/share/mk" #define _path_defsyspath "/usr/share/mk:/usr/local/share/mk:/opt/share/mk"
#ifndef _PATH_DEFSYSPATH #ifndef _PATH_DEFSYSPATH
# ifdef _PATH_PREFIX_SYSPATH # ifdef _PATH_PREFIX_SYSPATH
@ -58,5 +59,5 @@
# endif # endif
#endif #endif
#ifndef _PATH_TMP #ifndef _PATH_TMP
#define _PATH_TMP "/tmp/" /* with trailing slash */ #define _PATH_TMP "/tmp/" /* with trailing slash */
#endif #endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: str.c,v 1.74 2020/11/16 18:28:27 rillig Exp $ */ /* $NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $ */
/*- /*-
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -71,7 +71,7 @@
#include "make.h" #include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */ /* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
MAKE_RCSID("$NetBSD: str.c,v 1.74 2020/11/16 18:28:27 rillig Exp $"); MAKE_RCSID("$NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */ /* Return the concatenation of s1 and s2, freshly allocated. */
char * char *
@ -115,7 +115,8 @@ str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
return result; return result;
} }
/* Fracture a string into an array of words (as delineated by tabs or spaces) /*
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account. * taking quotation marks into account.
* *
* If expand is TRUE, quotes are removed and escape sequences such as \r, \t, * If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
@ -142,7 +143,7 @@ Str_Words(const char *str, Boolean expand)
/* words_buf holds the words, separated by '\0'. */ /* words_buf holds the words, separated by '\0'. */
str_len = strlen(str); str_len = strlen(str);
words_buf = bmake_malloc(strlen(str) + 1); words_buf = bmake_malloc(str_len + 1);
words_cap = str_len / 5 > 50 ? str_len / 5 : 50; words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
words = bmake_malloc((words_cap + 1) * sizeof(char *)); words = bmake_malloc((words_cap + 1) * sizeof(char *));
@ -160,7 +161,7 @@ Str_Words(const char *str, Boolean expand)
switch (ch) { switch (ch) {
case '"': case '"':
case '\'': case '\'':
if (inquote) { if (inquote != '\0') {
if (inquote == ch) if (inquote == ch)
inquote = '\0'; inquote = '\0';
else else
@ -188,7 +189,7 @@ Str_Words(const char *str, Boolean expand)
case ' ': case ' ':
case '\t': case '\t':
case '\n': case '\n':
if (inquote) if (inquote != '\0')
break; break;
if (word_start == NULL) if (word_start == NULL)
continue; continue;
@ -211,7 +212,7 @@ Str_Words(const char *str, Boolean expand)
words[words_len++] = word_start; words[words_len++] = word_start;
word_start = NULL; word_start = NULL;
if (ch == '\n' || ch == '\0') { if (ch == '\n' || ch == '\0') {
if (expand && inquote) { if (expand && inquote != '\0') {
free(words); free(words);
free(words_buf); free(words_buf);
return (Words){ NULL, 0, NULL }; return (Words){ NULL, 0, NULL };

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* $NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $ */ /* $NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990, 1993 * Copyright (c) 1988, 1989, 1990, 1993
@ -93,12 +93,6 @@
* Targ_FindList Given a list of names, find nodes for all * Targ_FindList Given a list of names, find nodes for all
* of them, creating them as necessary. * of them, creating them as necessary.
* *
* Targ_Ignore Return TRUE if errors should be ignored when
* creating the given target.
*
* Targ_Silent Return TRUE if we should be silent when
* creating the given target.
*
* Targ_Precious Return TRUE if the target is precious and * Targ_Precious Return TRUE if the target is precious and
* should not be removed if we are interrupted. * should not be removed if we are interrupted.
* *
@ -108,7 +102,7 @@
* *
* Debugging: * Debugging:
* Targ_PrintGraph * Targ_PrintGraph
* Print out the entire graphm all variables and * Print out the entire graph, all variables and
* statistics for the directory cache. Should print * statistics for the directory cache. Should print
* something for suffixes, too, but... * something for suffixes, too, but...
*/ */
@ -119,14 +113,17 @@
#include "dir.h" #include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $"); MAKE_RCSID("$NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $");
/* All target nodes found so far, but not the source nodes. */ /*
static GNodeList *allTargets; * All target nodes that appeared on the left-hand side of one of the
* dependency operators ':', '::', '!'.
*/
static GNodeList allTargets = LST_INIT;
static HashTable allTargetsByName; static HashTable allTargetsByName;
#ifdef CLEANUP #ifdef CLEANUP
static GNodeList *allNodes; static GNodeList allNodes = LST_INIT;
static void GNode_Free(void *); static void GNode_Free(void *);
#endif #endif
@ -134,28 +131,24 @@ static void GNode_Free(void *);
void void
Targ_Init(void) Targ_Init(void)
{ {
allTargets = Lst_New(); HashTable_Init(&allTargetsByName);
HashTable_Init(&allTargetsByName);
#ifdef CLEANUP
allNodes = Lst_New();
#endif
} }
void void
Targ_End(void) Targ_End(void)
{ {
Targ_Stats(); Targ_Stats();
#ifdef CLEANUP #ifdef CLEANUP
Lst_Free(allTargets); Lst_Done(&allTargets);
HashTable_Done(&allTargetsByName); HashTable_Done(&allTargetsByName);
Lst_Destroy(allNodes, GNode_Free); Lst_DoneCall(&allNodes, GNode_Free);
#endif #endif
} }
void void
Targ_Stats(void) Targ_Stats(void)
{ {
HashTable_DebugStats(&allTargetsByName, "targets"); HashTable_DebugStats(&allTargetsByName, "targets");
} }
/* /*
@ -166,10 +159,11 @@ Targ_Stats(void)
GNodeList * GNodeList *
Targ_List(void) Targ_List(void)
{ {
return allTargets; return &allTargets;
} }
/* Create a new graph node, but don't register it anywhere. /*
* Create a new graph node, but don't register it anywhere.
* *
* Graph nodes that appear on the left-hand side of a dependency line such * Graph nodes that appear on the left-hand side of a dependency line such
* as "target: source" are called targets. XXX: In some cases (like the * as "target: source" are called targets. XXX: In some cases (like the
@ -186,68 +180,95 @@ Targ_List(void)
GNode * GNode *
GNode_New(const char *name) GNode_New(const char *name)
{ {
GNode *gn; GNode *gn;
gn = bmake_malloc(sizeof *gn); gn = bmake_malloc(sizeof *gn);
gn->name = bmake_strdup(name); gn->name = bmake_strdup(name);
gn->uname = NULL; gn->uname = NULL;
gn->path = NULL; gn->path = NULL;
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0; gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE;
gn->flags = 0; gn->flags = GNF_NONE;
gn->made = UNMADE; gn->made = UNMADE;
gn->unmade = 0; gn->unmade = 0;
gn->mtime = 0; gn->mtime = 0;
gn->youngestChild = NULL; gn->youngestChild = NULL;
gn->implicitParents = Lst_New(); Lst_Init(&gn->implicitParents);
gn->parents = Lst_New(); Lst_Init(&gn->parents);
gn->children = Lst_New(); Lst_Init(&gn->children);
gn->order_pred = Lst_New(); Lst_Init(&gn->order_pred);
gn->order_succ = Lst_New(); Lst_Init(&gn->order_succ);
gn->cohorts = Lst_New(); Lst_Init(&gn->cohorts);
gn->cohort_num[0] = '\0'; gn->cohort_num[0] = '\0';
gn->unmade_cohorts = 0; gn->unmade_cohorts = 0;
gn->centurion = NULL; gn->centurion = NULL;
gn->checked_seqno = 0; gn->checked_seqno = 0;
HashTable_Init(&gn->context); HashTable_Init(&gn->vars);
gn->commands = Lst_New(); Lst_Init(&gn->commands);
gn->suffix = NULL; gn->suffix = NULL;
gn->fname = NULL; gn->fname = NULL;
gn->lineno = 0; gn->lineno = 0;
#ifdef CLEANUP #ifdef CLEANUP
Lst_Append(allNodes, gn); Lst_Append(&allNodes, gn);
#endif #endif
return gn; return gn;
} }
#ifdef CLEANUP #ifdef CLEANUP
static void static void
GNode_Free(void *gnp) GNode_Free(void *gnp)
{ {
GNode *gn = gnp; GNode *gn = gnp;
free(gn->name); free(gn->name);
free(gn->uname); free(gn->uname);
free(gn->path); free(gn->path);
/* gn->youngestChild is not owned by this node. */
Lst_Free(gn->implicitParents); /* ... but not the nodes themselves, */
Lst_Free(gn->parents); /* as they are not owned by this node. */
Lst_Free(gn->children); /* likewise */
Lst_Free(gn->order_pred); /* likewise */
Lst_Free(gn->order_succ); /* likewise */
Lst_Free(gn->cohorts); /* likewise */
HashTable_Done(&gn->context); /* ... but not the variables themselves,
* even though they are owned by this node.
* XXX: they should probably be freed. */
Lst_Free(gn->commands); /* ... but not the commands themselves,
* as they may be shared with other nodes. */
/* gn->suffix is not owned by this node. */
/* XXX: gn->suffix should be unreferenced here. This requires a thorough
* check that the reference counting is done correctly in all places,
* otherwise a suffix might be freed too early. */
free(gn); /* Don't free gn->youngestChild since it is not owned by this node. */
/*
* In the following lists, only free the list nodes, but not the
* GNodes in them since these are not owned by this node.
*/
Lst_Done(&gn->implicitParents);
Lst_Done(&gn->parents);
Lst_Done(&gn->children);
Lst_Done(&gn->order_pred);
Lst_Done(&gn->order_succ);
Lst_Done(&gn->cohorts);
/*
* Do not free the variables themselves, even though they are owned
* by this node.
*
* XXX: For the nodes that represent targets or sources (and not
* VAR_GLOBAL), it should be safe to free the variables as well,
* since each node manages the memory for all its variables itself.
*
* XXX: The GNodes that are only used as variable contexts (VAR_CMD,
* VAR_GLOBAL, VAR_INTERNAL) are not freed at all (see Var_End, where
* they are not mentioned). These might be freed at all, if their
* variable values are indeed not used anywhere else (see Trace_Init
* for the only suspicious use).
*/
HashTable_Done(&gn->vars);
/*
* Do not free the commands themselves, as they may be shared with
* other nodes.
*/
Lst_Done(&gn->commands);
/*
* gn->suffix is not owned by this node.
*
* XXX: gn->suffix should be unreferenced here. This requires a
* thorough check that the reference counting is done correctly in
* all places, otherwise a suffix might be freed too early.
*/
free(gn);
} }
#endif #endif
@ -255,23 +276,23 @@ GNode_Free(void *gnp)
GNode * GNode *
Targ_FindNode(const char *name) Targ_FindNode(const char *name)
{ {
return HashTable_FindValue(&allTargetsByName, name); return HashTable_FindValue(&allTargetsByName, name);
} }
/* Get the existing global node, or create it. */ /* Get the existing global node, or create it. */
GNode * GNode *
Targ_GetNode(const char *name) Targ_GetNode(const char *name)
{ {
Boolean isNew; Boolean isNew;
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew); HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
if (!isNew) if (!isNew)
return HashEntry_Get(he); return HashEntry_Get(he);
{ {
GNode *gn = Targ_NewInternalNode(name); GNode *gn = Targ_NewInternalNode(name);
HashEntry_Set(he, gn); HashEntry_Set(he, gn);
return gn; return gn;
} }
} }
/* /*
@ -283,63 +304,54 @@ Targ_GetNode(const char *name)
GNode * GNode *
Targ_NewInternalNode(const char *name) Targ_NewInternalNode(const char *name)
{ {
GNode *gn = GNode_New(name); GNode *gn = GNode_New(name);
Var_Append(".ALLTARGETS", name, VAR_GLOBAL); Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
Lst_Append(allTargets, gn); Lst_Append(&allTargets, gn);
if (doing_depend) DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
gn->flags |= FROM_DEPEND; if (doing_depend)
return gn; gn->flags |= FROM_DEPEND;
return gn;
} }
/* /*
* Return the .END node, which contains the commands to be run when * Return the .END node, which contains the commands to be run when
* everything else has been made. * everything else has been made.
*/ */
GNode *Targ_GetEndNode(void) GNode *
Targ_GetEndNode(void)
{ {
/* Save the node locally to avoid having to search for it all the time. */ /*
static GNode *endNode = NULL; * Save the node locally to avoid having to search for it all
if (endNode == NULL) { * the time.
endNode = Targ_GetNode(".END"); */
endNode->type = OP_SPECIAL; static GNode *endNode = NULL;
}
return endNode; if (endNode == NULL) {
endNode = Targ_GetNode(".END");
endNode->type = OP_SPECIAL;
}
return endNode;
} }
/* Return the named nodes, creating them as necessary. */ /* Add the named nodes to the list, creating them as necessary. */
GNodeList * void
Targ_FindList(StringList *names) Targ_FindList(GNodeList *gns, StringList *names)
{ {
StringListNode *ln; 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);
}
return nodes;
}
/* Return true if should ignore errors when creating gn. */ for (ln = names->first; ln != NULL; ln = ln->next) {
Boolean const char *name = ln->datum;
Targ_Ignore(const GNode *gn) GNode *gn = Targ_GetNode(name);
{ Lst_Append(gns, gn);
return opts.ignoreErrors || gn->type & OP_IGNORE; }
}
/* Return true if be silent when creating gn. */
Boolean
Targ_Silent(const GNode *gn)
{
return opts.beSilent || gn->type & OP_SILENT;
} }
/* See if the given target is precious. */ /* See if the given target is precious. */
Boolean Boolean
Targ_Precious(const GNode *gn) Targ_Precious(const GNode *gn)
{ {
/* XXX: Why are '::' targets precious? */ /* XXX: Why are '::' targets precious? */
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP); return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
} }
/* /*
@ -352,198 +364,202 @@ static GNode *mainTarg;
void void
Targ_SetMain(GNode *gn) Targ_SetMain(GNode *gn)
{ {
mainTarg = gn; mainTarg = gn;
} }
static void static void
PrintNodeNames(GNodeList *gnodes) PrintNodeNames(GNodeList *gnodes)
{ {
GNodeListNode *node; GNodeListNode *ln;
for (node = gnodes->first; node != NULL; node = node->next) { for (ln = gnodes->first; ln != NULL; ln = ln->next) {
GNode *gn = node->datum; GNode *gn = ln->datum;
debug_printf(" %s%s", gn->name, gn->cohort_num); debug_printf(" %s%s", gn->name, gn->cohort_num);
} }
} }
static void static void
PrintNodeNamesLine(const char *label, GNodeList *gnodes) PrintNodeNamesLine(const char *label, GNodeList *gnodes)
{ {
if (Lst_IsEmpty(gnodes)) if (Lst_IsEmpty(gnodes))
return; return;
debug_printf("# %s:", label); debug_printf("# %s:", label);
PrintNodeNames(gnodes); PrintNodeNames(gnodes);
debug_printf("\n"); debug_printf("\n");
} }
void void
Targ_PrintCmds(GNode *gn) Targ_PrintCmds(GNode *gn)
{ {
StringListNode *ln; StringListNode *ln;
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum; for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
debug_printf("\t%s\n", cmd); const char *cmd = ln->datum;
} debug_printf("\t%s\n", cmd);
}
} }
/* Format a modification time in some reasonable way and return it. /*
* The time is placed in a static area, so it is overwritten with each call. */ * Format a modification time in some reasonable way and return it.
char * * The formatted time is placed in a static area, so it is overwritten
* with each call.
*/
const char *
Targ_FmtTime(time_t tm) Targ_FmtTime(time_t tm)
{ {
struct tm *parts; static char buf[128];
static char buf[128];
parts = localtime(&tm); struct tm *parts = localtime(&tm);
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
return buf; return buf;
} }
/* Print out a type field giving only those attributes the user can set. */ /* Print out a type field giving only those attributes the user can set. */
void void
Targ_PrintType(int type) Targ_PrintType(int type)
{ {
int tbit; int tbit;
#define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break type &= ~OP_OPMASK;
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break
type &= ~OP_OPMASK; while (type != 0) {
tbit = 1 << (ffs(type) - 1);
type &= ~tbit;
while (type) { switch (tbit) {
tbit = 1 << (ffs(type) - 1); #define PRINTBIT(bit, attr) case bit: debug_printf(" " attr); break
type &= ~tbit; #define PRINTDBIT(bit, attr) case bit: DEBUG0(TARG, " " attr); break
PRINTBIT(OP_OPTIONAL, ".OPTIONAL");
switch(tbit) { PRINTBIT(OP_USE, ".USE");
PRINTBIT(OPTIONAL); PRINTBIT(OP_EXEC, ".EXEC");
PRINTBIT(USE); PRINTBIT(OP_IGNORE, ".IGNORE");
PRINTBIT(EXEC); PRINTBIT(OP_PRECIOUS, ".PRECIOUS");
PRINTBIT(IGNORE); PRINTBIT(OP_SILENT, ".SILENT");
PRINTBIT(PRECIOUS); PRINTBIT(OP_MAKE, ".MAKE");
PRINTBIT(SILENT); PRINTBIT(OP_JOIN, ".JOIN");
PRINTBIT(MAKE); PRINTBIT(OP_INVISIBLE, ".INVISIBLE");
PRINTBIT(JOIN); PRINTBIT(OP_NOTMAIN, ".NOTMAIN");
PRINTBIT(INVISIBLE); PRINTDBIT(OP_LIB, ".LIB");
PRINTBIT(NOTMAIN); PRINTDBIT(OP_MEMBER, ".MEMBER");
PRINTDBIT(LIB); PRINTDBIT(OP_ARCHV, ".ARCHV");
/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ PRINTDBIT(OP_MADE, ".MADE");
case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break; PRINTDBIT(OP_PHONY, ".PHONY");
PRINTDBIT(ARCHV); #undef PRINTBIT
PRINTDBIT(MADE); #undef PRINTDBIT
PRINTDBIT(PHONY); }
} }
}
} }
static const char * static const char *
made_name(GNodeMade made) made_name(GNodeMade made)
{ {
switch (made) { switch (made) {
case UNMADE: return "unmade"; case UNMADE: return "unmade";
case DEFERRED: return "deferred"; case DEFERRED: return "deferred";
case REQUESTED: return "requested"; case REQUESTED: return "requested";
case BEINGMADE: return "being made"; case BEINGMADE: return "being made";
case MADE: return "made"; case MADE: return "made";
case UPTODATE: return "up-to-date"; case UPTODATE: return "up-to-date";
case ERROR: return "error when made"; case ERROR: return "error when made";
case ABORTED: return "aborted"; case ABORTED: return "aborted";
default: return "unknown enum_made value"; default: return "unknown enum_made value";
} }
} }
static const char * static const char *
GNode_OpName(const GNode *gn) GNode_OpName(const GNode *gn)
{ {
switch (gn->type & OP_OPMASK) { switch (gn->type & OP_OPMASK) {
case OP_DEPENDS: case OP_DEPENDS:
return ":"; return ":";
case OP_FORCE: case OP_FORCE:
return "!"; return "!";
case OP_DOUBLEDEP: case OP_DOUBLEDEP:
return "::"; return "::";
} }
return ""; return "";
} }
/* Print the contents of a node. */ /* Print the contents of a node. */
void void
Targ_PrintNode(GNode *gn, int pass) Targ_PrintNode(GNode *gn, int pass)
{ {
debug_printf("# %s%s", gn->name, gn->cohort_num); debug_printf("# %s%s", gn->name, gn->cohort_num);
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n"); GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
if (gn->flags == 0) if (gn->flags == 0)
return; return;
if (!GNode_IsTarget(gn))
return;
if (GNode_IsTarget(gn)) {
debug_printf("#\n"); debug_printf("#\n");
if (gn == mainTarg) { if (gn == mainTarg)
debug_printf("# *** MAIN TARGET ***\n"); debug_printf("# *** MAIN TARGET ***\n");
}
if (pass >= 2) { if (pass >= 2) {
if (gn->unmade > 0) { if (gn->unmade > 0)
debug_printf("# %d unmade children\n", gn->unmade); debug_printf("# %d unmade children\n", gn->unmade);
} else { else
debug_printf("# No unmade children\n"); debug_printf("# No unmade children\n");
} if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) {
if (!(gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { if (gn->mtime != 0) {
if (gn->mtime != 0) { debug_printf("# last modified %s: %s\n",
debug_printf("# last modified %s: %s\n", Targ_FmtTime(gn->mtime),
Targ_FmtTime(gn->mtime), made_name(gn->made));
made_name(gn->made)); } else if (gn->made != UNMADE) {
} else if (gn->made != UNMADE) { debug_printf("# nonexistent (maybe): %s\n",
debug_printf("# non-existent (maybe): %s\n", made_name(gn->made));
made_name(gn->made)); } else
} else { debug_printf("# unmade\n");
debug_printf("# unmade\n");
} }
} PrintNodeNamesLine("implicit parents", &gn->implicitParents);
PrintNodeNamesLine("implicit parents", gn->implicitParents);
} else { } else {
if (gn->unmade) if (gn->unmade != 0)
debug_printf("# %d unmade children\n", gn->unmade); debug_printf("# %d unmade children\n", gn->unmade);
} }
PrintNodeNamesLine("parents", gn->parents);
PrintNodeNamesLine("order_pred", gn->order_pred); PrintNodeNamesLine("parents", &gn->parents);
PrintNodeNamesLine("order_succ", gn->order_succ); PrintNodeNamesLine("order_pred", &gn->order_pred);
PrintNodeNamesLine("order_succ", &gn->order_succ);
debug_printf("%-16s%s", gn->name, GNode_OpName(gn)); debug_printf("%-16s%s", gn->name, GNode_OpName(gn));
Targ_PrintType(gn->type); Targ_PrintType(gn->type);
PrintNodeNames(gn->children); PrintNodeNames(&gn->children);
debug_printf("\n"); debug_printf("\n");
Targ_PrintCmds(gn); Targ_PrintCmds(gn);
debug_printf("\n\n"); debug_printf("\n\n");
if (gn->type & OP_DOUBLEDEP) { if (gn->type & OP_DOUBLEDEP)
Targ_PrintNodes(gn->cohorts, pass); Targ_PrintNodes(&gn->cohorts, pass);
}
}
} }
void void
Targ_PrintNodes(GNodeList *gnodes, int pass) Targ_PrintNodes(GNodeList *gnodes, int pass)
{ {
GNodeListNode *ln; GNodeListNode *ln;
for (ln = gnodes->first; ln != NULL; ln = ln->next)
Targ_PrintNode(ln->datum, pass); for (ln = gnodes->first; ln != NULL; ln = ln->next)
Targ_PrintNode(ln->datum, pass);
} }
/* Print only those targets that are just a source. */ /* Print only those targets that are just a source. */
static void static void
PrintOnlySources(void) PrintOnlySources(void)
{ {
GNodeListNode *ln; GNodeListNode *ln;
for (ln = allTargets->first; ln != NULL; ln = ln->next) { for (ln = allTargets.first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum; GNode *gn = ln->datum;
if (GNode_IsTarget(gn)) if (GNode_IsTarget(gn))
continue; continue;
debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn)); debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn));
Targ_PrintType(gn->type); Targ_PrintType(gn->type);
debug_printf("\n"); debug_printf("\n");
} }
} }
/* Input: /*
* Input:
* pass 1 => before processing * pass 1 => before processing
* 2 => after processing * 2 => after processing
* 3 => after processing, an error occurred * 3 => after processing, an error occurred
@ -551,49 +567,51 @@ PrintOnlySources(void)
void void
Targ_PrintGraph(int pass) Targ_PrintGraph(int pass)
{ {
debug_printf("#*** Input graph:\n"); debug_printf("#*** Input graph:\n");
Targ_PrintNodes(allTargets, pass); Targ_PrintNodes(&allTargets, pass);
debug_printf("\n"); debug_printf("\n");
debug_printf("\n"); debug_printf("\n");
debug_printf("#\n"); debug_printf("#\n");
debug_printf("# Files that are only sources:\n"); debug_printf("# Files that are only sources:\n");
PrintOnlySources(); PrintOnlySources();
debug_printf("#*** Global Variables:\n"); debug_printf("#*** Global Variables:\n");
Var_Dump(VAR_GLOBAL); Var_Dump(VAR_GLOBAL);
debug_printf("#*** Command-line Variables:\n"); debug_printf("#*** Command-line Variables:\n");
Var_Dump(VAR_CMDLINE); Var_Dump(VAR_CMDLINE);
debug_printf("\n"); debug_printf("\n");
Dir_PrintDirectories(); Dir_PrintDirectories();
debug_printf("\n"); debug_printf("\n");
Suff_PrintAll(); Suff_PrintAll();
} }
/* Propagate some type information to cohort nodes (those from the '::' /*
* Propagate some type information to cohort nodes (those from the '::'
* dependency operator). * dependency operator).
* *
* Should be called after the makefiles are parsed but before any action is * Should be called after the makefiles are parsed but before any action is
* taken. */ * taken.
*/
void void
Targ_Propagate(void) Targ_Propagate(void)
{ {
GNodeListNode *ln, *cln; GNodeListNode *ln, *cln;
for (ln = allTargets->first; ln != NULL; ln = ln->next) { for (ln = allTargets.first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum; GNode *gn = ln->datum;
GNodeType type = gn->type; GNodeType type = gn->type;
if (!(type & OP_DOUBLEDEP)) if (!(type & OP_DOUBLEDEP))
continue; continue;
for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) { for (cln = gn->cohorts.first; cln != NULL; cln = cln->next) {
GNode *cohort = cln->datum; GNode *cohort = cln->datum;
cohort->type |= type & ~OP_OPMASK; cohort->type |= type & ~OP_OPMASK;
}
} }
}
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $ */ /* $NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $ */
/*- /*-
* Copyright (c) 2000 The NetBSD Foundation, Inc. * Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -48,7 +48,7 @@
#include "job.h" #include "job.h"
#include "trace.h" #include "trace.h"
MAKE_RCSID("$NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $"); MAKE_RCSID("$NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $");
static FILE *trfile; static FILE *trfile;
static pid_t trpid; static pid_t trpid;
@ -67,11 +67,12 @@ void
Trace_Init(const char *pathname) Trace_Init(const char *pathname)
{ {
if (pathname != NULL) { if (pathname != NULL) {
void *dontFreeIt; FStr curDir;
trpid = getpid(); trpid = getpid();
/* XXX: This variable may get overwritten later, which /* XXX: This variable may get overwritten later, which
* would make trwd point to undefined behavior. */ * would make trwd point to undefined behavior. */
trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt); curDir = Var_Value(".CURDIR", VAR_GLOBAL);
trwd = curDir.str;
trfile = fopen(pathname, "a"); trfile = fopen(pathname, "a");
} }
@ -92,8 +93,11 @@ Trace_Log(TrEvent event, Job *job)
jobTokensRunning, jobTokensRunning,
evname[event], trpid, trwd); evname[event], trpid, trwd);
if (job != NULL) { if (job != NULL) {
fprintf(trfile, " %s %d %x %x", job->node->name, char flags[4];
job->pid, job->flags, job->node->type);
Job_FlagsToString(job, flags, sizeof flags);
fprintf(trfile, " %s %d %s %x", job->node->name,
job->pid, flags, job->node->type);
} }
fputc('\n', trfile); fputc('\n', trfile);
fflush(trfile); fflush(trfile);

View File

@ -1,4 +1,4 @@
/* $NetBSD: trace.h,v 1.4 2020/10/18 17:19:54 rillig Exp $ */ /* $NetBSD: trace.h,v 1.5 2020/11/28 08:41:53 rillig Exp $ */
/*- /*-
* Copyright (c) 2000 The NetBSD Foundation, Inc. * Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -34,6 +34,9 @@
* Definitions pertaining to the tracing of jobs in parallel mode. * Definitions pertaining to the tracing of jobs in parallel mode.
*/ */
#ifndef MAKE_TRACE_H
#define MAKE_TRACE_H
typedef enum TrEvent { typedef enum TrEvent {
MAKESTART, MAKESTART,
MAKEEND, MAKEEND,
@ -47,3 +50,4 @@ void Trace_Init(const char *);
void Trace_Log(TrEvent, Job *); void Trace_Log(TrEvent, Job *);
void Trace_End(void); void Trace_End(void);
#endif

View File

@ -1,6 +1,6 @@
# $Id: Makefile,v 1.115 2020/11/18 04:01:07 sjg Exp $ # $Id: Makefile,v 1.138 2021/01/01 22:55:09 sjg Exp $
# #
# $NetBSD: Makefile,v 1.206 2020/11/18 01:12:00 sjg Exp $ # $NetBSD: Makefile,v 1.260 2020/12/31 03:05:12 rillig Exp $
# #
# Unit tests for make(1) # Unit tests for make(1)
# #
@ -30,18 +30,24 @@
# src/tests/usr.bin/make/t_make.sh. # src/tests/usr.bin/make/t_make.sh.
# #
# we use these below but we might be an older make
.MAKE.OS?= ${uname -s:L:sh}
.MAKE.UID?= ${id -u:L:sh}
# Each test is in a sub-makefile. # Each test is in a sub-makefile.
# Keep the list sorted. # Keep the list sorted.
# Any test that is commented out must be ignored in # Any test that is commented out must be ignored in
# src/tests/usr.bin/make/t_make.sh as well. # src/tests/usr.bin/make/t_make.sh as well.
#TESTS+= archive #TESTS+= archive
TESTS+= archive-suffix #TESTS+= archive-suffix
TESTS+= cmd-errors TESTS+= cmd-errors
TESTS+= cmd-errors-jobs
TESTS+= cmd-errors-lint TESTS+= cmd-errors-lint
TESTS+= cmd-interrupt TESTS+= cmd-interrupt
TESTS+= cmdline TESTS+= cmdline
TESTS+= cmdline-undefined TESTS+= cmdline-undefined
TESTS+= comment TESTS+= comment
TESTS+= compat-error
TESTS+= cond-cmp-numeric TESTS+= cond-cmp-numeric
TESTS+= cond-cmp-numeric-eq TESTS+= cond-cmp-numeric-eq
TESTS+= cond-cmp-numeric-ge TESTS+= cond-cmp-numeric-ge
@ -51,12 +57,14 @@ TESTS+= cond-cmp-numeric-lt
TESTS+= cond-cmp-numeric-ne TESTS+= cond-cmp-numeric-ne
TESTS+= cond-cmp-string TESTS+= cond-cmp-string
TESTS+= cond-cmp-unary TESTS+= cond-cmp-unary
TESTS+= cond-eof
TESTS+= cond-func TESTS+= cond-func
TESTS+= cond-func-commands TESTS+= cond-func-commands
TESTS+= cond-func-defined TESTS+= cond-func-defined
TESTS+= cond-func-empty TESTS+= cond-func-empty
TESTS+= cond-func-exists TESTS+= cond-func-exists
TESTS+= cond-func-make TESTS+= cond-func-make
TESTS+= cond-func-make-main
TESTS+= cond-func-target TESTS+= cond-func-target
TESTS+= cond-late TESTS+= cond-late
TESTS+= cond-op TESTS+= cond-op
@ -107,9 +115,14 @@ TESTS+= depsrc-usebefore-double-colon
TESTS+= depsrc-wait TESTS+= depsrc-wait
TESTS+= deptgt TESTS+= deptgt
TESTS+= deptgt-begin TESTS+= deptgt-begin
TESTS+= deptgt-begin-fail
TESTS+= deptgt-begin-fail-indirect
TESTS+= deptgt-default TESTS+= deptgt-default
TESTS+= deptgt-delete_on_error TESTS+= deptgt-delete_on_error
TESTS+= deptgt-end TESTS+= deptgt-end
TESTS+= deptgt-end-fail
TESTS+= deptgt-end-fail-all
TESTS+= deptgt-end-fail-indirect
TESTS+= deptgt-end-jobs TESTS+= deptgt-end-jobs
TESTS+= deptgt-error TESTS+= deptgt-error
TESTS+= deptgt-ignore TESTS+= deptgt-ignore
@ -139,14 +152,20 @@ TESTS+= directive-elifmake
TESTS+= directive-elifndef TESTS+= directive-elifndef
TESTS+= directive-elifnmake TESTS+= directive-elifnmake
TESTS+= directive-else TESTS+= directive-else
TESTS+= directive-endfor
TESTS+= directive-endif TESTS+= directive-endif
TESTS+= directive-error TESTS+= directive-error
TESTS+= directive-export TESTS+= directive-export
TESTS+= directive-export-env TESTS+= directive-export-env
TESTS+= directive-export-impl
TESTS+= directive-export-gmake TESTS+= directive-export-gmake
TESTS+= directive-export-literal TESTS+= directive-export-literal
TESTS+= directive-for TESTS+= directive-for
TESTS+= directive-for-errors
TESTS+= directive-for-escape
TESTS+= directive-for-generating-endif TESTS+= directive-for-generating-endif
TESTS+= directive-for-lines
TESTS+= directive-for-null
TESTS+= directive-hyphen-include TESTS+= directive-hyphen-include
TESTS+= directive-if TESTS+= directive-if
TESTS+= directive-if-nested TESTS+= directive-if-nested
@ -157,6 +176,7 @@ TESTS+= directive-ifnmake
TESTS+= directive-include TESTS+= directive-include
TESTS+= directive-include-fatal TESTS+= directive-include-fatal
TESTS+= directive-info TESTS+= directive-info
TESTS+= directive-misspellings
TESTS+= directive-sinclude TESTS+= directive-sinclude
TESTS+= directive-undef TESTS+= directive-undef
TESTS+= directive-unexport TESTS+= directive-unexport
@ -180,14 +200,20 @@ TESTS+= impsrc
TESTS+= include-main TESTS+= include-main
TESTS+= job-flags TESTS+= job-flags
#TESTS+= job-output-long-lines #TESTS+= job-output-long-lines
TESTS+= jobs-error-indirect
TESTS+= jobs-error-nested
TESTS+= jobs-error-nested-make
TESTS+= lint TESTS+= lint
TESTS+= make-exported TESTS+= make-exported
TESTS+= meta-cmd-cmp
TESTS+= moderrs TESTS+= moderrs
TESTS+= modmatch TESTS+= modmatch
TESTS+= modmisc TESTS+= modmisc
TESTS+= modts TESTS+= modts
TESTS+= modword TESTS+= modword
.if ${.MAKE.UID} > 0
TESTS+= objdir-writable TESTS+= objdir-writable
.endif
TESTS+= opt TESTS+= opt
TESTS+= opt-backwards TESTS+= opt-backwards
TESTS+= opt-chdir TESTS+= opt-chdir
@ -223,10 +249,13 @@ TESTS+= opt-ignore
TESTS+= opt-include-dir TESTS+= opt-include-dir
TESTS+= opt-jobs TESTS+= opt-jobs
TESTS+= opt-jobs-internal TESTS+= opt-jobs-internal
TESTS+= opt-jobs-no-action
TESTS+= opt-keep-going TESTS+= opt-keep-going
TESTS+= opt-keep-going-multiple
TESTS+= opt-m-include-dir TESTS+= opt-m-include-dir
TESTS+= opt-no-action TESTS+= opt-no-action
TESTS+= opt-no-action-at-all TESTS+= opt-no-action-at-all
TESTS+= opt-no-action-runflags
TESTS+= opt-query TESTS+= opt-query
TESTS+= opt-raw TESTS+= opt-raw
TESTS+= opt-silent TESTS+= opt-silent
@ -243,10 +272,11 @@ TESTS+= parse-var
TESTS+= phony-end TESTS+= phony-end
TESTS+= posix TESTS+= posix
TESTS+= # posix1 # broken by reverting POSIX changes TESTS+= # posix1 # broken by reverting POSIX changes
TESTS+= qequals
TESTS+= recursive TESTS+= recursive
TESTS+= sh TESTS+= sh
TESTS+= sh-dots TESTS+= sh-dots
TESTS+= sh-errctl
TESTS+= sh-flags
TESTS+= sh-jobs TESTS+= sh-jobs
TESTS+= sh-jobs-error TESTS+= sh-jobs-error
TESTS+= sh-leading-at TESTS+= sh-leading-at
@ -264,10 +294,14 @@ TESTS+= shell-sh
TESTS+= suff-add-later TESTS+= suff-add-later
TESTS+= suff-clear-regular TESTS+= suff-clear-regular
TESTS+= suff-clear-single TESTS+= suff-clear-single
TESTS+= suff-incomplete
TESTS+= suff-lookup TESTS+= suff-lookup
TESTS+= suff-main TESTS+= suff-main
TESTS+= suff-main-several
TESTS+= suff-phony
TESTS+= suff-rebuild TESTS+= suff-rebuild
TESTS+= suff-self TESTS+= suff-self
TESTS+= suff-transform-debug
TESTS+= suff-transform-endless TESTS+= suff-transform-endless
TESTS+= suff-transform-expand TESTS+= suff-transform-expand
TESTS+= suff-transform-select TESTS+= suff-transform-select
@ -304,6 +338,7 @@ TESTS+= varmod-gmtime
TESTS+= varmod-hash TESTS+= varmod-hash
TESTS+= varmod-head TESTS+= varmod-head
TESTS+= varmod-ifelse TESTS+= varmod-ifelse
TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime TESTS+= varmod-localtime
TESTS+= varmod-loop TESTS+= varmod-loop
@ -361,6 +396,7 @@ TESTS+= varname-dot-make-path_filemon
TESTS+= varname-dot-make-pid TESTS+= varname-dot-make-pid
TESTS+= varname-dot-make-ppid TESTS+= varname-dot-make-ppid
TESTS+= varname-dot-make-save_dollars TESTS+= varname-dot-make-save_dollars
TESTS+= varname-dot-makeflags
TESTS+= varname-dot-makeoverrides TESTS+= varname-dot-makeoverrides
TESTS+= varname-dot-newline TESTS+= varname-dot-newline
TESTS+= varname-dot-objdir TESTS+= varname-dot-objdir
@ -422,21 +458,22 @@ ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
# Override make flags for some of the tests; default is -k. # Override make flags for some of the tests; default is -k.
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of # 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. # settings FLAGS.test=-dv here, since that is closer to the test code.
FLAGS.cond-func-make= via-cmdline FLAGS.cond-func-make= via-cmdline
FLAGS.directive-ifmake= first second FLAGS.directive-ifmake= first second
FLAGS.doterror= # none, especially not -k FLAGS.doterror= # none, especially not -k
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain' FLAGS.jobs-error-indirect= # none, especially not -k
FLAGS.jobs-error-nested= # none, especially not -k
FLAGS.jobs-error-nested-make= # none, especially not -k
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
# Some tests need extra postprocessing. # Some tests need extra postprocessing.
SED_CMDS.export= \ SED_CMDS.dir= ${:D remove output from -DCLEANUP mode }
-e '/^[^=_A-Za-z0-9]*=/d' SED_CMDS.dir+= -e '/^OpenDirs_Done:/d'
# these all share the same requirement SED_CMDS.dir+= -e '/^CachedDir /d'
.for t in export-all export-env SED_CMDS.export= -e '/^[^=_A-Za-z0-9]*=/d'
SED_CMDS.$t= ${SED_CMDS.export} SED_CMDS.export-all= ${SED_CMDS.export}
.endfor SED_CMDS.export-env= ${SED_CMDS.export}
SED_CMDS.directive-export-gmake= \ SED_CMDS.cmdline= -e 's,uid${.MAKE.UID}/,,'
${:D dash is a pain } \
-e /non-zero/d
SED_CMDS.job-output-long-lines= \ SED_CMDS.job-output-long-lines= \
${:D Job separators on their own line are ok. } \ ${:D Job separators on their own line are ok. } \
-e '/^--- job-[ab] ---$$/d' \ -e '/^--- job-[ab] ---$$/d' \
@ -448,26 +485,34 @@ SED_CMDS.job-output-long-lines= \
${:D marker should always be at the beginning of the line. } \ ${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \ -e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d' -e '/^bb*--- job-a ---$$/d'
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g' SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
SED_CMDS.opt-debug-graph1= \ SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
-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,([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,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,Process [0-9][0-9]*,Process <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,' SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
# The "-q" may be there or not, see jobs.c, variable shells. # 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.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
SED_CMDS.var-op-shell+= -e 's,^${.SHELL:T}: ,,' SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.var-op-shell+= -e '/command/{ s,^[1-9]: ,,;s,No such.*,not found,; }' SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.vardebug= \ # For Compat_RunCommand, useShell == FALSE.
${:D canonicalize .SHELL } \ SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
-e 's,${.SHELL},</path/to/shell>,' # For Compat_RunCommand, useShell == TRUE.
SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= \
-e 's,^${.SHELL:T}: [ 0-9:]*,,' \
-e 's,^${.SHELL:T}: ,,' \
-e '/command/s,No such.*,not found,'
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= \ SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),' -e 's,\(Regex compilation error:\).*,\1 (details omitted),'
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,' 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-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'
@ -475,10 +520,9 @@ 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. # Some tests need an additional round of postprocessing.
POSTPROC.deptgt-suffixes= \ POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
# Some tests reuse other tests, which makes them unnecessarily fragile. # Some tests reuse other tests, which makes them unnecessarily fragile.
export-all.rawout: export.mk export-all.rawout: export.mk
@ -487,6 +531,35 @@ unexport-env.rawout: export.mk
# End of the configuration section. # End of the configuration section.
# Some standard sed commands, to be used in the SED_CMDS above.
# Omit details such as process IDs from the output of the -dg1 option.
STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,<curdir>,'
STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d'
STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d'
STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
# Omit details such as process IDs from the output of the -dj option.
STD_SED_CMDS.dj= \
-e '/Process/d;/JobFinish:/d' \
-e 's,^\(Job_TokenWithdraw\)([0-9]*),\1(<pid>),' \
-e 's,^([0-9][0-9]*) \(withdrew token\),(<pid>) \1,' \
-e 's, \(pid\) [0-9][0-9]*, \1 <pid>,' \
-e 's,^\( Command:\) .*,\1 <shell>,'
# Reduce the noise for tests running with the -n option, since there is no
# other way to suppress the echoing of the commands.
STD_SED_CMDS.hide-from-output= \
-e '/^echo hide-from-output/d' \
-e 's,hide-from-output ,,' \
-e 's,hide-from-output,,'
# End of the configuration helpers section.
.MAIN: all .MAIN: all
.-include "Makefile.inc" .-include "Makefile.inc"
@ -532,6 +605,11 @@ _MKMSG_TEST= :
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
.if ${.MAKE.OS} == "NetBSD"
LIMIT_RESOURCES?= ulimit -v 200000
.endif
LIMIT_RESOURCES?= :
# Each test is run in a sub-make, to keep the tests for interfering with # 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 # each other, and because they use different environment variables and
# command line options. # command line options.
@ -539,6 +617,7 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
.mk.rawout: .mk.rawout:
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX} @${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
@set -eu; \ @set -eu; \
${LIMIT_RESOURCES}; \
cd ${.OBJDIR}; \ cd ${.OBJDIR}; \
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \ env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
${TEST_MAKE} \ ${TEST_MAKE} \
@ -561,6 +640,10 @@ _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
# strip ${.CURDIR}/ from the output # strip ${.CURDIR}/ from the output
_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
# on AT&T derrived systems; false exits 255 not 1
.if ${.MAKE.OS:N*BSD} != ""
_SED_CMDS+= -e 's,\(Error code\) 255,\1 1,'
.endif
.rawout.out: .rawout.out:
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \ @${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
@ -570,10 +653,18 @@ _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
@echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2 @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
@mv ${.TARGET}.tmp2 ${.TARGET} @mv ${.TARGET}.tmp2 ${.TARGET}
.if empty(DIFF_FLAGS)
DIFF_ECHO= echo
.else
DIFF_ECHO= :
.endif
# Compare all output files # Compare all output files
test: ${OUTFILES} .PHONY test: ${OUTFILES} .PHONY
@failed= ; \ @failed= ; \
for test in ${TESTS}; do \ for test in ${TESTS}; do \
cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out && continue || \
${DIFF_ECHO} diff ${UNIT_TESTS}/$${test}.exp $${test}.out; \
${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \ ${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \
|| failed="$${failed}$${failed:+ }$${test}" ; \ || failed="$${failed}$${failed:+ }$${test}" ; \
done ; \ done ; \

View File

@ -0,0 +1,9 @@
: undefined eol
make: Unclosed variable "UNCLOSED"
: unclosed-variable
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier 'Z'
: unknown-modifier eol
: end eol
exit status 0

View File

@ -0,0 +1,32 @@
# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $
#
# Demonstrate how errors in variable expansions affect whether the commands
# are actually executed in jobs mode.
.MAKEFLAGS: -j1
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
# Undefined variables are not an error. They expand to empty strings.
undefined:
: $@ ${UNDEFINED} eol
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-variable:
: $@ ${UNCLOSED
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unclosed-modifier:
: $@ ${UNCLOSED:
# XXX: As of 2020-11-01, this command is executed even though it contains
# parse errors.
unknown-modifier:
: $@ ${UNKNOWN:Z} eol
end:
: $@ eol
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.

View File

@ -1,7 +1,7 @@
# $NetBSD: cmd-errors.mk,v 1.3 2020/11/09 23:36:34 rillig Exp $ # $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $
# #
# Demonstrate how errors in variable expansions affect whether the commands # Demonstrate how errors in variable expansions affect whether the commands
# are actually executed. # are actually executed in compat mode.
all: undefined unclosed-variable unclosed-modifier unknown-modifier end all: undefined unclosed-variable unclosed-modifier unknown-modifier end

View File

@ -2,7 +2,7 @@
# #
# Tests for command line parsing and related special variables. # Tests for command line parsing and related special variables.
TMPBASE?= /tmp TMPBASE?= /tmp/uid${.MAKE.UID}
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r

View File

@ -0,0 +1,15 @@
: Making success1 out of nothing.
: Making fail1 out of nothing.
false 'fail1' '${.TARGET}' '$${.TARGET}'
*** Error code 1 (continuing)
: Making success2 out of nothing.
: Making fail2 out of nothing.
false 'fail2' '${.TARGET}' '$${.TARGET}'
*** Error code 1 (continuing)
: Making success3 out of nothing.
Stop.
make: stopped in unit-tests
.ERROR target: <fail1>
.ERROR command: <>
exit status 1

View File

@ -0,0 +1,37 @@
# $NetBSD: compat-error.mk,v 1.3 2020/12/13 19:33:53 rillig Exp $
#
# Test detailed error handling in compat mode.
#
# Until 2020-12-13, .ERROR_TARGET was success3, which was wrong.
# Since compat.c 1.215 from 2020-12-13, it is 'fail1', which is the first
# failed top-level target. XXX: Even better would be if .ERROR_TARGET were
# the smallest target that caused the build to fail, even if it were a
# sub-sub-sub-dependency of a top-level target.
#
# XXX: As of 2020-12-13, .ERROR_CMD is empty, which is wrong.
#
# See also:
# Compat_Run
#
# The commit that added the NULL command to gn->commands:
# CVS: 1994.06.06.22.45.??
# Git: 26a8972fd7f982502c5fbfdabd34578b99d77ca5
# 1994: Lst_Replace (cmdNode, (ClientData) NULL);
# 2020: LstNode_SetNull(cmdNode);
#
# The commit that skipped NULL commands for .ERROR_CMD:
# CVS: 2016.08.11.19.53.??
# Git: 58b23478b7353d46457089e726b07a49197388e4
.MAKEFLAGS: success1 fail1 success2 fail2 success3
success1 success2 success3:
: Making ${.TARGET} out of nothing.
fail1 fail2:
: Making ${.TARGET} out of nothing.
false '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
.ERROR:
@echo ${.TARGET} target: '<'${.ERROR_TARGET:Q}'>'
@echo ${.TARGET} command: '<'${.ERROR_CMD:Q}'>'

View File

@ -0,0 +1,9 @@
side effect
make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
side effect
make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
side effect
make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,20 @@
# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $
#
# Tests for parsing conditions, especially the end of such conditions, which
# are represented as the token TOK_EOF.
SIDE_EFFECT= ${:!echo 'side effect' 1>&2!}
SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
# In the following conditions, ${SIDE_EFFECT} is the position of the first
# parse error. It is always fully evaluated, even if it were not necessary
# to expand the variable expression. This is because these syntax errors are
# an edge case that does not occur during normal operation, therefore there
# is no need to optimize for this case, and it would slow down the common
# case as well.
.if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
.if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
.if (0) ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-empty.mk,v 1.10 2020/11/15 14:07:53 rillig Exp $ # $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
# #
# Tests for the empty() function in .if conditions, which tests a variable # Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness. # expression for emptiness.
@ -155,5 +155,30 @@ ${:U WORD }= variable name with spaces
. error . error
.endif .endif
# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated
# a wrong error message "Variable VARNAME is recursive".
#
# The bug was that the !empty() condition was evaluated, even though this was
# not necessary since the defined() condition already evaluated to false.
#
# When evaluating the !empty condition, the variable name was parsed as
# "VARNAME${:U2}", but without expanding any nested variable expression, in
# this case the ${:U2}. Therefore, the variable name came out as simply
# "VARNAME". Since this variable name should have been discarded quickly after
# parsing it, this unrealistic variable name should have done no harm.
#
# The variable expression was expanded though, and this was wrong. The
# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
# then) though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be
# expanded.
#
# This was fixed by expanding nested variable expressions in the variable name
# only if the flag VARE_WANTRES is given.
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
.endif
all: all:
@:; @:;

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-func-exists.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $ # $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
# #
# Tests for the exists() function in .if conditions. # Tests for the exists() function in .if conditions.
@ -38,5 +38,14 @@
. error . error
.endif .endif
# The exists function does not really look up the file in the file system,
# instead it uses a cache that is preloaded very early, before parsing the
# first makefile. At that time, the file did not exist yet.
_!= > cond-func-exists.just-created
.if exists(cond-func-exists.just-created)
. error
.endif
_!= rm cond-func-exists.just-created
all: all:
@:; @:;

View File

@ -0,0 +1,3 @@
: Making dot-main-target-1a.
: Making dot-main-target-1b.
exit status 0

View File

@ -0,0 +1,62 @@
# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
#
# Test how accurately the make() function in .if conditions reflects
# what is actually made.
#
# There are several ways to specify what is being made:
#
# 1. The default main target is the first target in the given makefiles that
# is not one of the special targets. For example, .PHONY is special when
# it appears on the left-hand side of the ':'. It is not special on the
# right-hand side though.
#
# 2. Command line arguments that are neither options (-ds or -k) nor variable
# assignments (VAR=value) are interpreted as targets to be made. These
# override the default main target from above.
#
# 3. All sources of the first '.MAIN: sources' line. Any further .MAIN line
# is treated as if .MAIN were a regular name.
#
# This test only covers items 1 and 3. For item 2, see cond-func-make.mk.
first-main-target:
: Making ${.TARGET}.
# Even though the main-target would actually be made at this point, it is
# ignored by the make() function.
.if make(first-main-target)
. error
.endif
# Declaring a target via the .MAIN dependency adds it to the targets to be
# created (opts.create), but only that list was empty at the beginning of
# the line. This implies that several main targets can be set at the name
# time, but they have to be in the same dependency group.
#
# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
.MAIN: dot-main-target-1a dot-main-target-1b
.if !make(dot-main-target-1a)
. error
.endif
.if !make(dot-main-target-1b)
. error
.endif
dot-main-target-{1,2}{a,b}:
: Making ${.TARGET}.
# At this point, the list of targets to be made (opts.create) is not empty
# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
# it were an ordinary target. Since .MAIN is not listed as a dependency
# anywhere, it is not made.
.if target(.MAIN)
. error
.endif
.MAIN: dot-main-target-2a dot-main-target-2b
.if !target(.MAIN)
. error
.endif
.if make(dot-main-target-2a)
. error
.endif

View File

@ -7,10 +7,10 @@ expected M pattern
expected or expected or
expected or exists expected or exists
expected or empty expected or empty
defined(V42) && 42 > 0: Ok defined(V42) && ${V42} > 0: Ok
defined(V66) && ( "" < 42 ): Ok defined(V66) && ( "${iV2}" < ${V42} ): Ok
1 || 42 < 42: Ok 1 || ${iV1} < ${V42}: Ok
1 || < 42: Ok 1 || ${iV2:U2} < ${V42}: Ok
0 || 42 <= 42: Ok 0 || ${iV1} <= ${V42}: Ok
0 || < 42: Ok 0 || ${iV2:U2} < ${V42}: Ok
exit status 0 exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: cond-short.mk,v 1.12 2020/11/15 14:58:14 rillig Exp $ # $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
# #
# Demonstrates that in conditions, the right-hand side of an && or || # Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result. # is only evaluated if it can actually influence the result.
@ -6,9 +6,13 @@
# mode in most programming languages. A notable exception is Ada, which # mode in most programming languages. A notable exception is Ada, which
# distinguishes between the operators 'And', 'And Then', 'Or', 'Or Else'. # distinguishes between the operators 'And', 'And Then', 'Or', 'Or Else'.
# #
# Between 2015-10-11 and 2020-06-28, the right-hand side of an && or || # Before 2020-06-28, the right-hand side of an && or || operator was always
# operator was always evaluated, which was wrong. # evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
# TODO: Had the evaluation been correct at some time before 2015-11-12? # Var_Parse got a new parameter named 'wantit'. Since then it would have been
# possible to skip evaluation of irrelevant variable expressions and only
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
# expressions, undefined variables were allowed.
# The && operator. # The && operator.
@ -128,33 +132,56 @@ x= Ok
.else .else
x= Fail x= Fail
.endif .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 # With cond.c 1.76 from 2020-07-03, the following condition triggered a
# Malformed conditional with cond.c 1.78 # warning: "String comparison operator should be either == or !=".
# indirect iV2 would expand to "" and treated as 0 # This was because the variable expression ${iV2} was defined, but the
# contained variable V66 was undefined. The left-hand side of the comparison
# therefore evaluated to the string "${V66}", which is obviously not a number.
#
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
# comparisons. Instead, they are only parsed and then discarded.
#
# At that time, there was not enough debug logging to see the details in the
# -dA log. To actually see it, add debug logging at the beginning and end of
# Var_Parse.
.if defined(V66) && ( ${iV2} < ${V42} ) .if defined(V66) && ( ${iV2} < ${V42} )
x= Fail x= Fail
.else .else
x= Ok x= Ok
.endif .endif
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo # XXX: This condition doesn't match the one above. The quotes are missing
# above. This is a crucial detail since without quotes, the variable
# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to ""
# since undefined variables are allowed and expand to an empty string.
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} .if 1 || ${iV1} < ${V42}
x= Ok x= Ok
.else .else
x= Fail x= Fail
.endif .endif
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo
# With cond.c 1.76 from 2020-07-03, the following condition triggered a
# warning: "String comparison operator should be either == or !=".
# This was because the variable expression ${iV2} was defined, but the
# contained variable V66 was undefined. The left-hand side of the comparison
# therefore evaluated to the string "${V66}", which is obviously not a number.
#
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
# comparisons. Instead, they are only parsed and then discarded.
#
# At that time, there was not enough debug logging to see the details in the
# -dA log. To actually see it, add debug logging at the beginning and end of
# Var_Parse.
.if 1 || ${iV2:U2} < ${V42} .if 1 || ${iV2:U2} < ${V42}
x= Ok x= Ok
.else .else
x= Fail x= Fail
.endif .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 # the same expressions are fine when the lhs is expanded
# ${iV1} expands to 42 # ${iV1} expands to 42
@ -163,7 +190,7 @@ x= Ok
.else .else
x= Fail x= Fail
.endif .endif
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
# ${iV2:U2} expands to 2 # ${iV2:U2} expands to 2
.if 0 || ${iV2:U2} < ${V42} .if 0 || ${iV2:U2} < ${V42}
@ -171,11 +198,12 @@ x= Ok
.else .else
x= Fail x= Fail
.endif .endif
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
# TODO: Has this always worked? There may have been a time, maybe around # The right-hand side of the '&&' is irrelevant since the left-hand side
# 2000, when make would complain about the "Malformed conditional" because # already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
# UNDEF is not defined. # expanded nevertheless, although with a small modification: undefined
# variables may be used in these expressions without generating an error.
.if defined(UNDEF) && ${UNDEF} != "undefined" .if defined(UNDEF) && ${UNDEF} != "undefined"
. error . error
.endif .endif

View File

@ -1,4 +1,4 @@
make: Unknown modifier 'Z' make: "cond-token-string.mk" line 9: Unknown modifier 'Z'
make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}") make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}")
make: "cond-token-string.mk" line 18: xvalue is not defined. make: "cond-token-string.mk" line 18: xvalue is not defined.
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "") make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")

View File

@ -1,3 +1,6 @@
make: don't know how to make dep-percent.o (continuing) make: don't know how to make dep-percent.o (continuing)
`all' not remade because of errors. `all' not remade because of errors.
exit status 0
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -1 +1,5 @@
Skipping meta for actual-test: no commands
Skipping meta for .END: .SPECIAL
Targets from meta mode:
| TARGET depsrc-meta-target
exit status 0 exit status 0

View File

@ -1,8 +1,31 @@
# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $ # $NetBSD: depsrc-meta.mk,v 1.4 2020/11/27 08:39:07 rillig Exp $
# #
# Tests for the special source .META in dependency declarations. # Tests for the special source .META in dependency declarations.
# TODO: Implementation # TODO: Implementation
# TODO: Explanation
.if make(actual-test)
.MAKEFLAGS: -dM
.MAKE.MODE= meta curDirOk=true
actual-test: depsrc-meta-target
depsrc-meta-target: .META
@> ${.TARGET}-file
@rm -f ${.TARGET}-file
.elif make(check-results)
check-results:
@echo 'Targets from meta mode:'
@awk '/^TARGET/ { print "| " $$0 }' depsrc-meta-target.meta
@rm depsrc-meta-target.meta
.else
all: all:
@:; @${MAKE} -f ${MAKEFILE} actual-test
@${MAKE} -f ${MAKEFILE} check-results
.endif

View File

@ -5,16 +5,16 @@ ExamineLater: need to examine "optional"
ExamineLater: need to examine "optional-cohort" ExamineLater: need to examine "optional-cohort"
Make_ExpandUse: examine optional Make_ExpandUse: examine optional
Make_ExpandUse: examine optional-cohort Make_ExpandUse: examine optional-cohort
Examining optional...non-existent...up-to-date. Examining optional...nonexistent...up-to-date.
Examining optional-cohort...non-existent...:: operator and no sources...out-of-date. Examining optional-cohort...nonexistent...:: operator and no sources...out-of-date.
: A leaf node using '::' is considered out-of-date. : A leaf node using '::' is considered out-of-date.
recheck(optional-cohort): update time from 0:00:00 Jan 01, 1970 to now recheck(optional-cohort): update time from nonexistent to now
Examining important...non-existent...modified before source "optional-cohort"...out-of-date. Examining important...nonexistent...modified before source "optional-cohort"...out-of-date.
: important is made. : important is made.
recheck(important): update time from 0:00:00 Jan 01, 1970 to now recheck(important): update time from nonexistent to now
Examining all...non-existent...modified before source "important"...out-of-date. Examining all...nonexistent...modified before source "important"...out-of-date.
: all is made. : all is made.
recheck(all): update time from 0:00:00 Jan 01, 1970 to now recheck(all): update time from nonexistent to now
Examining .END...non-existent...non-existent and no sources...out-of-date. Examining .END...nonexistent...nonexistent and no sources...out-of-date.
recheck(.END): update time from 0:00:00 Jan 01, 1970 to now recheck(.END): update time from nonexistent to now
exit status 0 exit status 0

View File

@ -1 +1,4 @@
: 'Undefined variables are expanded directly in the dependency'
: 'declaration. They are not preserved and maybe expanded later.'
: 'This is in contrast to local variables such as ${.TARGET}.'
exit status 0 exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: depsrc.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $ # $NetBSD: depsrc.mk,v 1.4 2020/12/22 19:38:44 rillig Exp $
# #
# Tests for special sources (those starting with a dot, followed by # Tests for special sources (those starting with a dot, followed by
# uppercase letters) in dependency declarations, such as .PHONY. # uppercase letters) in dependency declarations, such as .PHONY.
@ -7,5 +7,20 @@
# TODO: Test 'target: ${:U.SILENT}' # TODO: Test 'target: ${:U.SILENT}'
# Demonstrate when exactly undefined variables are expanded in a dependency
# declaration.
target: .PHONY source-${DEFINED_LATER}
#
DEFINED_LATER= later
#
source-: .PHONY
: 'Undefined variables are expanded directly in the dependency'
: 'declaration. They are not preserved and maybe expanded later.'
: 'This is in contrast to local variables such as $${.TARGET}.'
source-later: .PHONY
: 'Undefined variables are tried to be expanded in a dependency'
: 'declaration. If that fails because the variable is undefined,'
: 'the expression is preserved and tried to be expanded later.'
all: all:
@:; @:;

View File

@ -0,0 +1,6 @@
false
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,16 @@
# $NetBSD: deptgt-begin-fail-indirect.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
#
# Test for a .BEGIN target whose dependency results in an error.
# This stops make immediately and does not build the main targets.
#
# Between 2005-05-08 and 2020-11-24, a failing dependency of the .BEGIN node
# would not stop make from running the main targets. In the end, the exit
# status was even 0.
.BEGIN: failing
failing: .PHONY .NOTMAIN
false
all:
: This is not made.

View File

@ -0,0 +1,6 @@
false
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,10 @@
# $NetBSD: deptgt-begin-fail.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
#
# Test for a .BEGIN target whose command results in an error.
# This stops make immediately and does not build the main targets.
.BEGIN:
false
all:
: This is not made.

View File

@ -0,0 +1,7 @@
: Making all out of nothing.
false
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,19 @@
# $NetBSD: deptgt-end-fail-all.mk,v 1.2 2020/12/07 01:04:07 rillig Exp $
#
# Test whether the commands from the .END target are run even if there is
# an error before. The manual page says "after everything else is done",
# which leaves room for interpretation.
#
# Until 2020-12-07, the .END node was made even if the main nodes had failed.
# This was not intended since the .END node had already been skipped if a
# dependency of the main nodes had failed, just not if one of the main nodes
# themselves had failed. This inconsistency was not worth keeping. To run
# some commands on error, use the .ERROR target instead, see deptgt-error.mk.
all: .PHONY
: Making ${.TARGET} out of nothing.
false
.END:
: Making ${.TARGET} out of nothing.
false

View File

@ -0,0 +1,7 @@
: all
false
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,16 @@
# $NetBSD: deptgt-end-fail-indirect.mk,v 1.2 2020/12/06 21:22:04 rillig Exp $
#
# Tests for an error in a dependency of the .END node.
#
# Before 2020-11-25, an error in the .END target did not print the "Stop."
# and exited with status 0. The cause for this was a missing condition in
# Compat_Run in the handling of the .END node.
all:
: $@
.END: failing
: Making ${.TARGET} from ${.ALLSRC}.
failing: .PHONY
false

View File

@ -0,0 +1,163 @@
Test case all=ok all-dep=ok end=ok end-dep=ok.
: Making all-dep out of nothing.
: Making all from all-dep.
: Making end-dep out of nothing.
: Making .END from end-dep.
exit status 0
Test case all=ok all-dep=ok end=ok end-dep=ERR.
: Making all-dep out of nothing.
: Making all from all-dep.
: Making end-dep out of nothing.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ok end=ERR end-dep=ok.
: Making all-dep out of nothing.
: Making all from all-dep.
: Making end-dep out of nothing.
: Making .END from end-dep.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ok end=ERR end-dep=ERR.
: Making all-dep out of nothing.
: Making all from all-dep.
: Making end-dep out of nothing.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ERR end=ok end-dep=ok.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ERR end=ok end-dep=ERR.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ERR end=ERR end-dep=ok.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ok all-dep=ERR end=ERR end-dep=ERR.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ok end=ok end-dep=ok.
: Making all-dep out of nothing.
: Making all from all-dep.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ok end=ok end-dep=ERR.
: Making all-dep out of nothing.
: Making all from all-dep.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ok end=ERR end-dep=ok.
: Making all-dep out of nothing.
: Making all from all-dep.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ok end=ERR end-dep=ERR.
: Making all-dep out of nothing.
: Making all from all-dep.
*** Error code 1 (continuing)
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ERR end=ok end-dep=ok.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ERR end=ok end-dep=ERR.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ERR end=ERR end-dep=ok.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
Test case all=ERR all-dep=ERR end=ERR end-dep=ERR.
: Making all-dep out of nothing.
*** Error code 1 (continuing)
`all' not remade because of errors.
Stop.
make: stopped in unit-tests
exit status 1
exit status 0

View File

@ -0,0 +1,69 @@
# $NetBSD: deptgt-end-fail.mk,v 1.6 2020/12/07 01:04:07 rillig Exp $
#
# Tests for an errors in the main target, its dependencies,
# the .END node and its dependencies.
#
# Before 2020-11-25, an error in the .END target did not print the "Stop.",
# even though this was intended. The cause for this was a missing condition
# in Compat_Run, in the code handling the .END node.
test: .PHONY
# The default stop-on-error mode is not as interesting to test since it
# stops right after the first error.
.MAKEFLAGS: -k
.for all in ok ERR
. for all-dep in ok ERR
. for end in ok ERR
. for end-dep in ok ERR
. for target in ${all}-${all-dep}-${end}-${end-dep}
test: ${target}
${target}: .PHONY .SILENT
echo Test case all=${all} all-dep=${all-dep} end=${end} end-dep=${end-dep}.
${MAKE} -r -f ${MAKEFILE} \
all=${all} all-dep=${all-dep} \
end=${end} end-dep=${end-dep} \
all; \
echo "exit status $$?"
echo
echo
. endfor
. endfor
. endfor
. endfor
.endfor
.if make(all)
all all-dep end-dep: .PHONY
CMD.ok= true
CMD.ERR= false
all: all-dep
: Making ${.TARGET} from ${.ALLSRC}.
@${CMD.${all}}
all-dep:
: Making ${.TARGET} out of nothing.
@${CMD.${all-dep}}
.END: end-dep
: Making ${.TARGET} from ${.ALLSRC}.
@${CMD.${end}}
end-dep:
: Making ${.TARGET} out of nothing.
@${CMD.${end-dep}}
.endif
# Until 2020-12-07, several of the test cases printed "`all' not remade
# because of errors.", followed by "exit status 0", which contradicted
# each other.
# Until 2020-12-07, '.END' was even made if 'all' failed, but if a dependency
# of 'all' failed, it was skipped. This inconsistency was not needed for
# anything and thus has been dropped. To run some commands on error, use the
# .ERROR target instead, see deptgt-error.mk.

View File

@ -3,5 +3,31 @@
# To: # To:
# From: # From:
# Search Path: . .. # Search Path: . ..
# ".src-left" (num 2, ref 2)
# To: .tgt-right
# From:
# Search Path:
# ".tgt-right" (num 3, ref 2)
# To:
# From: .src-left
# Search Path:
# ".tgt-left" (num 4, ref 2)
# To:
# From: .src-right
# Search Path:
# ".src-right" (num 5, ref 2)
# To: .tgt-left
# From:
# Search Path:
#*** Transformations: #*** Transformations:
.src-left.tgt-right:
: Making ${.TARGET} from ${.IMPSRC}.
.src-right.tgt-left:
: Making ${.TARGET} from ${.IMPSRC}.
: Making deptgt-suffixes.src-left out of nothing.
: Making deptgt-suffixes.tgt-right from deptgt-suffixes.src-left.
: Making deptgt-suffixes.src-right out of nothing.
: Making deptgt-suffixes.tgt-left from deptgt-suffixes.src-right.
exit status 0 exit status 0

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $ # $NetBSD: deptgt-suffixes.mk,v 1.4 2020/11/21 21:54:42 rillig Exp $
# #
# Tests for the special target .SUFFIXES in dependency declarations. # Tests for the special target .SUFFIXES in dependency declarations.
# #
@ -8,11 +8,28 @@
.MAKEFLAGS: -dg1 .MAKEFLAGS: -dg1
.MAIN: all
.SUFFIXES: .custom-null .SUFFIXES: .custom-null
# TODO: What is the effect of this? How is it useful? # TODO: What is the effect of this? How is it useful?
.NULL: .custom-null .NULL: .custom-null
.PATH.custom-null: . .. .PATH.custom-null: . ..
all: # The order in which the suffixes are listed doesn't matter.
@:; # Here, they are listed from source to target, just like in the transformation
# rule below it.
.SUFFIXES: .src-left .tgt-right
deptgt-suffixes.src-left:
: Making ${.TARGET} out of nothing.
.src-left.tgt-right:
: Making ${.TARGET} from ${.IMPSRC}.
all: deptgt-suffixes.tgt-right
# Here, the target is listed earlier than the source.
.SUFFIXES: .tgt-left .src-right
deptgt-suffixes.src-right:
: Making ${.TARGET} out of nothing.
.src-right.tgt-left:
: Making ${.TARGET} from ${.IMPSRC}.
all: deptgt-suffixes.tgt-left

View File

@ -8,6 +8,7 @@ ParseDoDependency(: empty-source)
ParseReadLine (37): ' : command for empty targets list' ParseReadLine (37): ' : command for empty targets list'
ParseReadLine (38): '.MAKEFLAGS: -d0' ParseReadLine (38): '.MAKEFLAGS: -d0'
ParseDoDependency(.MAKEFLAGS: -d0) ParseDoDependency(.MAKEFLAGS: -d0)
make: "deptgt.mk" line 46: Unknown modifier 'Z'
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,4 +1,4 @@
# $NetBSD: deptgt.mk,v 1.9 2020/11/15 11:57:00 rillig Exp $ # $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
# #
# Tests for special targets like .BEGIN or .SUFFIXES in dependency # Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations. # declarations.
@ -37,5 +37,13 @@ ${:U}: empty-source
: command for empty targets list : command for empty targets list
.MAKEFLAGS: -d0 .MAKEFLAGS: -d0
# Just to show that a malformed expression is only expanded once in
# ParseDependencyTargetWord. The only way to produce an expression that
# is well-formed on the first expansion and ill-formed on the second
# expansion would be to use the variable modifier '::=' to modify the
# targets. This in turn would be such an extreme and unreliable edge case
# that nobody uses it.
$$$$$$$${:U:Z}:
all: all:
@:; @:;

View File

@ -1,17 +1,21 @@
make: "directive-elif.mk" line 7: begin .elif misspellings tests, part 1 make: "directive-elif.mk" line 47: Unknown directive "elsif"
make: "directive-elif.mk" line 9: 1-then make: "directive-elif.mk" line 52: This branch is taken.
make: "directive-elif.mk" line 18: begin .elif misspellings tests, part 2 make: "directive-elif.mk" line 60: Unknown directive "elsif"
make: "directive-elif.mk" line 29: begin .elif misspellings tests, part 3 make: "directive-elif.mk" line 63: This branch is taken.
make: "directive-elif.mk" line 41: which branch is taken on misspelling after false? make: "directive-elif.mk" line 69: This branch is taken.
make: "directive-elif.mk" line 49: else make: "directive-elif.mk" line 89: Unknown directive "elsif"
make: "directive-elif.mk" line 52: which branch is taken on misspelling after true? make: "directive-elif.mk" line 90: This misspelling is detected.
make: "directive-elif.mk" line 54: 1-then make: "directive-elif.mk" line 91: This branch is taken because of the .else.
make: "directive-elif.mk" line 55: Unknown directive "elsif" make: "directive-elif.mk" line 109: What happens on misspelling in a skipped branch?
make: "directive-elif.mk" line 56: 1-elsif make: "directive-elif.mk" line 119: else
make: "directive-elif.mk" line 57: Unknown directive "elsif" make: "directive-elif.mk" line 122: What happens on misspelling in a taken branch?
make: "directive-elif.mk" line 58: 2-elsif make: "directive-elif.mk" line 124: 1-then
make: "directive-elif.mk" line 64: if-less elif make: "directive-elif.mk" line 125: Unknown directive "elsif"
make: "directive-elif.mk" line 69: warning: extra elif make: "directive-elif.mk" line 126: 1-elsif
make: "directive-elif.mk" line 127: Unknown directive "elsif"
make: "directive-elif.mk" line 128: 2-elsif
make: "directive-elif.mk" line 134: if-less elif
make: "directive-elif.mk" line 139: warning: extra elif
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,55 +1,125 @@
# $NetBSD: directive-elif.mk,v 1.6 2020/11/12 19:46:36 rillig Exp $ # $NetBSD: directive-elif.mk,v 1.7 2020/12/19 19:49:01 rillig Exp $
# #
# Tests for the .elif directive. # Tests for the .elif directive.
#
# Misspellings of the .elif directive are not always detected. They are only
# detected if the conditional branch directly above it is taken. In all other
# cases, make skips over the skipped branch as fast as possible, looking only
# at the initial '.' of the line and whether the directive is one of the known
# conditional directives. All other directives are silently ignored, as they
# could be variable assignments or dependency declarations as well, and
# deciding this would cost time.
# TODO: Implementation # TODO: Implementation
.info begin .elif misspellings tests, part 1
# Misspelling '.elsif' below an .if branch that is not taken.
.if 0
. info This branch is not taken.
# As of 2020-12-19, the misspelling is not recognized as a conditional
# directive and is thus silently skipped.
#
# Since the .if condition evaluated to false, this whole branch is not taken.
.elsif 0
. info XXX: This misspelling is not detected.
. info This branch is not taken.
# Even if the misspelling were detected, the branch would not be taken
# since the condition of the '.elsif' evaluates to false as well.
.endif
# Misspelling '.elsif' below an .if branch that is not taken.
.if 0
. info This branch is not taken.
# As of 2020-12-19, the misspelling is not recognized as a conditional
# directive and is thus silently skipped. Since the .if condition evaluated
# to false, this whole branch is not taken.
.elsif 1
. info XXX: This misspelling is not detected.
# If the misspelling were detected, this branch would be taken.
.endif
# Misspelling '.elsif' below an .if branch that is taken.
.if 1 .if 1
. info 1-then # This misspelling is in an active branch and is therefore detected.
.elif 1 # ok .elsif 0
. info 1-elif # The only thing that make detects here is a misspelled directive, make
.elsif 1 # oops: misspelled # doesn't recognize that it was meant to be a conditional directive.
. info 1-elsif # Therefore the branch continues here, even though the '.elsif' condition
.elseif 1 # oops: misspelled # evaluates to false.
. info 1-elseif . info This branch is taken.
.endif .endif
.info begin .elif misspellings tests, part 2
# Misspelling '.elsif' below an .if branch that is taken.
.if 1
# As of 2020-12-19, the misspelling is in an active branch and is therefore
# detected.
.elsif 1
# Since both conditions evaluate to true, this branch is taken no matter
# whether make detects a misspelling or not.
. info This branch is taken.
.endif
# Misspelling '.elsif' in a skipped branch below a branch that was taken.
.if 1
. info This branch is taken.
.elif 0
. info This branch is not taken.
.elsif 1
. info XXX: This misspelling is not detected.
.endif
# Misspelling '.elsif' in an .else branch that is not taken.
.if 1
.else
. info This branch is not taken.
.elsif 1
. info XXX: This misspelling is not detected.
.endif
# Misspelling '.elsif' in an .else branch that is taken.
.if 0 .if 0
. info 0-then .else
.elsif 1
. info This misspelling is detected.
. info This branch is taken because of the .else.
.endif
# Misspellings for .elif in a .elif branch that is not taken.
.if 0
. info This branch is not taken.
.elif 0 # ok .elif 0 # ok
. info 0-elif . info This branch is not taken.
.elsif 0 # oops: misspelled .elsif 0
. info 0-elsif . info XXX: This misspelling is not detected.
.elseif 0 # oops: misspelled . info This branch is not taken.
. info 0-elseif .elseif 0
. info XXX: This misspelling is not detected.
. info This branch is not taken.
.endif .endif
.info begin .elif misspellings tests, part 3
.if 0
. info 0-then
.elsif 0 # oops: misspelled
. info 0-elsif
.endif
.if 0
. info 0-then
.elseif 0 # oops: misspelled
. info 0-elseif
.endif
.info which branch is taken on misspelling after false? .info What happens on misspelling in a skipped branch?
.if 0 .if 0
. info 0-then . info 0-then
.elsif 1 .elsif 1
. info XXX: This misspelling is not detected.
. info 1-elsif . info 1-elsif
.elsif 2 .elsif 2
. info XXX: This misspelling is not detected.
. info 2-elsif . info 2-elsif
.else .else
. info else . info else
.endif .endif
.info which branch is taken on misspelling after true? .info What happens on misspelling in a taken branch?
.if 1 .if 1
. info 1-then . info 1-then
.elsif 1 .elsif 1
@ -65,7 +135,7 @@
.if 1 .if 1
.else .else
# Expect: "warning: if-less elif" # Expect: "warning: extra elif"
.elif .elif
.endif .endif

View File

@ -1,11 +1,11 @@
make: "directive-else.mk" line 11: The .else directive does not take arguments. make: "directive-else.mk" line 14: The .else directive does not take arguments.
make: "directive-else.mk" line 12: ok make: "directive-else.mk" line 15: ok
make: "directive-else.mk" line 16: ok make: "directive-else.mk" line 19: ok
make: "directive-else.mk" line 17: The .else directive does not take arguments. make: "directive-else.mk" line 21: The .else directive does not take arguments.
make: "directive-else.mk" line 22: if-less else make: "directive-else.mk" line 26: if-less else
make: "directive-else.mk" line 28: ok make: "directive-else.mk" line 32: ok
make: "directive-else.mk" line 29: warning: extra else make: "directive-else.mk" line 33: warning: extra else
make: "directive-else.mk" line 42: The .else directive does not take arguments. make: "directive-else.mk" line 45: The .else directive does not take arguments.
make: Fatal errors encountered -- cannot continue make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests make: stopped in unit-tests
exit status 1 exit status 1

View File

@ -1,19 +1,23 @@
# $NetBSD: directive-else.mk,v 1.6 2020/11/13 09:01:59 rillig Exp $ # $NetBSD: directive-else.mk,v 1.7 2020/12/14 22:17:11 rillig Exp $
# #
# Tests for the .else directive. # Tests for the .else directive.
#
# Since 2020-11-13, an '.else' followed by extraneous text generates a parse
# error in -dL (lint) mode.
#
# Since 2020-12-15, an '.else' followed by extraneous text always generates
# a parse error.
.MAKEFLAGS: -dL # To enable the check for ".else <cond>"
# The .else directive does not take any arguments.
# As of 2020-08-29, make doesn't warn about this.
.if 0 .if 0
. warning must not be reached . warning must not be reached
# The .else directive does not take any arguments.
.else 123 .else 123
. info ok . info ok
.endif .endif
.if 1 .if 1
. info ok . info ok
# The .else directive does not take any arguments.
.else 123 .else 123
. warning must not be reached . warning must not be reached
.endif .endif
@ -37,7 +41,6 @@
.endif .endif
# A variable expression does count as an argument, even if it is empty. # A variable expression does count as an argument, even if it is empty.
# XXX: This should be a parse error.
.if 0 .if 0
.else ${:U} .else ${:U}
.endif .endif

View File

@ -0,0 +1,4 @@
make: "directive-endfor.mk" line 9: for-less endfor
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -0,0 +1,9 @@
# $NetBSD: directive-endfor.mk,v 1.1 2020/12/30 14:50:08 rillig Exp $
#
# Test for the directive .endfor, which ends a .for loop.
#
# See also:
# directive-for.mk
# An .endfor without a corresponding .for is a parse error.
.endfor

View File

@ -1 +1,8 @@
exit status 0 make: "directive-endif.mk" line 18: The .endif directive does not take arguments.
make: "directive-endif.mk" line 23: The .endif directive does not take arguments.
make: "directive-endif.mk" line 33: The .endif directive does not take arguments.
make: "directive-endif.mk" line 39: The .endif directive does not take arguments.
make: "directive-endif.mk" line 45: Unknown directive "endifx"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1

View File

@ -1,7 +1,10 @@
# $NetBSD: directive-endif.mk,v 1.3 2020/11/12 22:40:11 rillig Exp $ # $NetBSD: directive-endif.mk,v 1.5 2020/12/14 21:56:17 rillig Exp $
# #
# Tests for the .endif directive. # Tests for the .endif directive.
# #
# Since 2020-12-15, the .endif directive no longer accepts arguments.
# The manual page had never allowed that, but the code didn't check it.
#
# See also: # See also:
# Cond_EvalLine # Cond_EvalLine
@ -10,18 +13,37 @@
.MAKEFLAGS: -dL .MAKEFLAGS: -dL
# Error: .endif does not take arguments # Error: .endif does not take arguments
# XXX: Missing error message
.if 0 .if 0
# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif 0 .endif 0
# Error: .endif does not take arguments # Error: .endif does not take arguments
# XXX: Missing error message
.if 1 .if 1
# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif 1 .endif 1
# Comments are allowed after an '.endif'. # Comments are allowed after an '.endif'.
.if 2 .if 2
.endif # comment .endif # comment
# Only whitespace and comments are allowed after an '.endif', but nothing
# else.
.if 1
# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif0
# Only whitespace and comments are allowed after an '.endif', but nothing
# else.
.if 1
# Since 2020-12-15, complain about the extra text after the 'endif'.
.endif/
# After an '.endif', no other letter must occur. This 'endifx' is not
# parsed as an 'endif', therefore another '.endif' must follow to balance
# the directives.
.if 1
.endifx
.endif # to close the preceding '.if'
all: all:
@:; @:;

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