Import 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 Change-Id: I4d3bcf08b4c864d98b343f602efe5a75dbfa7a94
This commit is contained in:
parent
1b65f0bd2b
commit
8e11a9b425
241
ChangeLog
241
ChangeLog
@ -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>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201117
|
||||
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 make some GNode functions const
|
||||
o main.c: call Targ_Init before Var_Init
|
||||
|
64
FILES
64
FILES
@ -75,6 +75,8 @@ unit-tests/archive-suffix.exp
|
||||
unit-tests/archive-suffix.mk
|
||||
unit-tests/archive.exp
|
||||
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.mk
|
||||
unit-tests/cmd-errors.exp
|
||||
@ -87,6 +89,8 @@ unit-tests/cmdline.exp
|
||||
unit-tests/cmdline.mk
|
||||
unit-tests/comment.exp
|
||||
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.mk
|
||||
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-unary.exp
|
||||
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.mk
|
||||
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-exists.exp
|
||||
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.mk
|
||||
unit-tests/cond-func-target.exp
|
||||
@ -213,12 +221,22 @@ unit-tests/depsrc-wait.exp
|
||||
unit-tests/depsrc-wait.mk
|
||||
unit-tests/depsrc.exp
|
||||
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.mk
|
||||
unit-tests/deptgt-default.exp
|
||||
unit-tests/deptgt-default.mk
|
||||
unit-tests/deptgt-delete_on_error.exp
|
||||
unit-tests/deptgt-delete_on_error.mk
|
||||
unit-tests/deptgt-end-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.mk
|
||||
unit-tests/deptgt-end.exp
|
||||
@ -279,6 +297,8 @@ unit-tests/directive-elifnmake.exp
|
||||
unit-tests/directive-elifnmake.mk
|
||||
unit-tests/directive-else.exp
|
||||
unit-tests/directive-else.mk
|
||||
unit-tests/directive-endfor.exp
|
||||
unit-tests/directive-endfor.mk
|
||||
unit-tests/directive-endif.exp
|
||||
unit-tests/directive-endif.mk
|
||||
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-gmake.exp
|
||||
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.mk
|
||||
unit-tests/directive-export.exp
|
||||
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.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.mk
|
||||
unit-tests/directive-hyphen-include.exp
|
||||
@ -315,6 +345,8 @@ unit-tests/directive-include.exp
|
||||
unit-tests/directive-include.mk
|
||||
unit-tests/directive-info.exp
|
||||
unit-tests/directive-info.mk
|
||||
unit-tests/directive-misspellings.exp
|
||||
unit-tests/directive-misspellings.mk
|
||||
unit-tests/directive-sinclude.exp
|
||||
unit-tests/directive-sinclude.mk
|
||||
unit-tests/directive-undef.exp
|
||||
@ -365,10 +397,18 @@ unit-tests/job-flags.exp
|
||||
unit-tests/job-flags.mk
|
||||
unit-tests/job-output-long-lines.exp
|
||||
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.mk
|
||||
unit-tests/make-exported.exp
|
||||
unit-tests/make-exported.mk
|
||||
unit-tests/meta-cmd-cmp.exp
|
||||
unit-tests/meta-cmd-cmp.mk
|
||||
unit-tests/moderrs.exp
|
||||
unit-tests/moderrs.mk
|
||||
unit-tests/modmatch.exp
|
||||
@ -447,14 +487,20 @@ unit-tests/opt-include-dir.exp
|
||||
unit-tests/opt-include-dir.mk
|
||||
unit-tests/opt-jobs-internal.exp
|
||||
unit-tests/opt-jobs-internal.mk
|
||||
unit-tests/opt-jobs-no-action.exp
|
||||
unit-tests/opt-jobs-no-action.mk
|
||||
unit-tests/opt-jobs.exp
|
||||
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.mk
|
||||
unit-tests/opt-m-include-dir.exp
|
||||
unit-tests/opt-m-include-dir.mk
|
||||
unit-tests/opt-no-action-at-all.exp
|
||||
unit-tests/opt-no-action-at-all.mk
|
||||
unit-tests/opt-no-action-runflags.exp
|
||||
unit-tests/opt-no-action-runflags.mk
|
||||
unit-tests/opt-no-action.exp
|
||||
unit-tests/opt-no-action.mk
|
||||
unit-tests/opt-query.exp
|
||||
@ -491,12 +537,14 @@ unit-tests/posix.exp
|
||||
unit-tests/posix.mk
|
||||
unit-tests/posix1.exp
|
||||
unit-tests/posix1.mk
|
||||
unit-tests/qequals.exp
|
||||
unit-tests/qequals.mk
|
||||
unit-tests/recursive.exp
|
||||
unit-tests/recursive.mk
|
||||
unit-tests/sh-dots.exp
|
||||
unit-tests/sh-dots.mk
|
||||
unit-tests/sh-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.mk
|
||||
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-single.exp
|
||||
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.mk
|
||||
unit-tests/suff-main-several.exp
|
||||
unit-tests/suff-main-several.mk
|
||||
unit-tests/suff-main.exp
|
||||
unit-tests/suff-main.mk
|
||||
unit-tests/suff-phony.exp
|
||||
unit-tests/suff-phony.mk
|
||||
unit-tests/suff-rebuild.exp
|
||||
unit-tests/suff-rebuild.mk
|
||||
unit-tests/suff-self.exp
|
||||
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.mk
|
||||
unit-tests/suff-transform-expand.exp
|
||||
@ -607,6 +663,8 @@ unit-tests/varmod-head.exp
|
||||
unit-tests/varmod-head.mk
|
||||
unit-tests/varmod-ifelse.exp
|
||||
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.mk
|
||||
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-save_dollars.exp
|
||||
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.mk
|
||||
unit-tests/varname-dot-newline.exp
|
||||
|
4
LICENSE
4
LICENSE
@ -2,11 +2,11 @@ The individual files in this distribution are copyright their
|
||||
original contributors or assignees.
|
||||
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) 2009-2016, Juniper Networks, Inc.
|
||||
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) 1989 by Berkeley Softworks
|
||||
Copyright (c) 1988, 1989, 1990, 1992, 1993
|
||||
|
@ -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
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
.\"
|
||||
@ -1918,15 +1918,15 @@ Suff_FindDeps (jive.c)
|
||||
applying .l -> .c to "jive.l"
|
||||
Suff_FindDeps (jive.l)
|
||||
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 ---
|
||||
lex jive.l
|
||||
\&.\|.\|. meaningless lex output deleted .\|.\|.
|
||||
mv lex.yy.c jive.c
|
||||
Examining jive.o...non-existent...out-of-date
|
||||
Examining jive.o...nonexistent...out-of-date
|
||||
--- jive.o ---
|
||||
cc -c jive.c
|
||||
Examining jive.out...non-existent...out-of-date
|
||||
Examining jive.out...nonexistent...out-of-date
|
||||
--- jive.out ---
|
||||
cc -o jive.out jive.o
|
||||
.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
|
||||
causes problems. In addition, not caching the directories slows things
|
||||
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
|
||||
suffixes are used and the null suffix isn't changed from
|
||||
.CW .out .
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20201117
|
||||
_MAKE_VERSION=20210110
|
||||
|
18
bmake.1
18
bmake.1
@ -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
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd November 14, 2020
|
||||
.Dd December 22, 2020
|
||||
.Dt BMAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1019,6 +1019,12 @@ If set to false,
|
||||
becomes
|
||||
.Ql $
|
||||
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
|
||||
When
|
||||
.Nm
|
||||
@ -1097,7 +1103,7 @@ to that directory before executing any targets.
|
||||
.Pp
|
||||
Except in the case of an explicit
|
||||
.Ql Ic .OBJDIR
|
||||
target,
|
||||
target,
|
||||
.Nm
|
||||
will check that the specified directory is writable and ignore it if not.
|
||||
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.
|
||||
.It Ic .info Ar message
|
||||
The message is printed along with the name of the makefile and line number.
|
||||
.It Ic .undef Ar variable
|
||||
Un-define the specified global variable.
|
||||
Only global variables may be un-defined.
|
||||
.It Ic .undef Ar variable ...
|
||||
Un-define the specified global variables.
|
||||
Only global variables can be un-defined.
|
||||
.It Ic .unexport Ar variable ...
|
||||
The opposite of
|
||||
.Ql .export .
|
||||
|
248
bmake.cat1
248
bmake.cat1
@ -45,10 +45,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
`-' they are added to the [4mMAKEFLAGS[24m environment variable and will
|
||||
be processed by any child make processes. By default, debugging
|
||||
information is printed to standard error, but this can be changed
|
||||
using the [4mF[24m debugging flag. The debugging output is always
|
||||
unbuffered; in addition, if debugging is enabled but debugging
|
||||
output is not directed to standard output, then the standard out-
|
||||
put is line buffered. [4mFlags[24m is one or more of the following:
|
||||
using the [4mF[24m debugging flag. The debugging output is always un-
|
||||
buffered; in addition, if debugging is enabled but debugging out-
|
||||
put is not directed to standard output, then the standard output
|
||||
is line buffered. [4mFlags[24m is one or more of the following:
|
||||
|
||||
[4mA[24m Print all possible debugging information; equivalent to
|
||||
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
|
||||
override the default system include path: /usr/share/mk. Fur-
|
||||
thermore the system include path will be appended to the search
|
||||
path used for "[4mfile[24m"-style include statements (see the [1m-I[0m
|
||||
option).
|
||||
path used for "[4mfile[24m"-style include statements (see the [1m-I [22mop-
|
||||
tion).
|
||||
|
||||
If a file or directory name in the [1m-m [22margument (or the
|
||||
MAKESYSPATH environment variable) starts with the string ".../"
|
||||
@ -232,9 +232,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4m.MAKE.EXPAND_VARIABLES[24m is set to true and the [1m-dV [22moption has not
|
||||
been used to override it. Note that loop-local and target-local
|
||||
variables, as well as values taken temporarily by global vari-
|
||||
ables during makefile processing, are not accessible via this
|
||||
option. The [1m-dv [22mdebug mode can be used to see these at the cost
|
||||
of generating substantial extraneous output.
|
||||
ables during makefile processing, are not accessible via this op-
|
||||
tion. The [1m-dv [22mdebug mode can be used to see these at the cost of
|
||||
generating substantial extraneous output.
|
||||
|
||||
[1m-v [4m[22mvariable[0m
|
||||
Like [1m-V [22mbut the variable is always expanded to its complete
|
||||
@ -247,8 +247,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m-X [22mDon't export variables passed on the command line to the environ-
|
||||
ment individually. Variables passed on the command line are
|
||||
still exported via the [4mMAKEFLAGS[24m environment variable. This
|
||||
option may be useful on systems which have a small limit on the
|
||||
still exported via the [4mMAKEFLAGS[24m environment variable. This op-
|
||||
tion may be useful on systems which have a small limit on the
|
||||
size of command arguments.
|
||||
|
||||
[4mvariable=value[0m
|
||||
@ -268,9 +268,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1mFILE DEPENDENCY SPECIFICATIONS[0m
|
||||
Dependency lines consist of one or more targets, an operator, and zero or
|
||||
more sources. This creates a relationship where the targets ``depend''
|
||||
on the sources and are customarily created from them. A target is con-
|
||||
sidered out-of-date if it does not exist, or if its modification time is
|
||||
more sources. This creates a relationship where the targets "depend" on
|
||||
the sources and are customarily created from them. A target is consid-
|
||||
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-
|
||||
created, but not until all sources have been examined and themselves re-
|
||||
created as needed. Three operators may be used:
|
||||
@ -285,20 +285,20 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
out of date.
|
||||
|
||||
[1m:: [22mAny dependency line may have attached shell commands, but each one
|
||||
is handled independently: its sources are considered and the
|
||||
attached shell commands are run if the target is out of date with
|
||||
respect to (only) those sources. Thus, different groups of the
|
||||
attached shell commands may be run depending on the circumstances.
|
||||
is handled independently: its sources are considered and the at-
|
||||
tached shell commands are run if the target is out of date with re-
|
||||
spect to (only) those sources. Thus, different groups of the at-
|
||||
tached shell commands may be run depending on the circumstances.
|
||||
Furthermore, unlike [1m:, [22mfor dependency lines with no sources, the
|
||||
attached shell commands are always run. Also unlike [1m:, [22mthe target
|
||||
will not be removed if [1mbmake [22mis interrupted.
|
||||
All dependency lines mentioning a particular target must use the same
|
||||
operator.
|
||||
All dependency lines mentioning a particular target must use the same op-
|
||||
erator.
|
||||
|
||||
Targets and sources may contain the shell wildcard values `?', `*', `[]',
|
||||
and `{}'. The values `?', `*', and `[]' may only be used as part of the
|
||||
final component of the target or source, and must be used to describe
|
||||
existing files. The value `{}' need not necessarily be used to describe
|
||||
final component of the target or source, and must be used to describe ex-
|
||||
isting files. The value `{}' need not necessarily be used to describe
|
||||
existing files. Expansion is in directory order, not alphabetically as
|
||||
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-
|
||||
mands, normally used to create the target. Each of the lines in this
|
||||
script [4mmust[24m be preceded by a tab. (For historical reasons, spaces are
|
||||
not accepted.) While targets can appear in many dependency lines if
|
||||
desired, by default only one of these rules may be followed by a creation
|
||||
not accepted.) While targets can appear in many dependency lines if de-
|
||||
sired, by default only one of these rules may be followed by a creation
|
||||
script. If the `[1m::[22m' operator is used, however, all rules may include
|
||||
scripts and the scripts are executed in the order found.
|
||||
|
||||
@ -333,10 +333,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
failed.
|
||||
|
||||
Makefiles should be written so that the mode of [1mbmake [22moperation does not
|
||||
change their behavior. For example, any command which needs to use
|
||||
``cd'' or ``chdir'' without potentially changing the directory for subse-
|
||||
quent commands should be put in parentheses so it executes in a subshell.
|
||||
To force the use of one shell, escape the line breaks so as to make the
|
||||
change their behavior. For example, any command which needs to use "cd"
|
||||
or "chdir" without potentially changing the directory for subsequent com-
|
||||
mands should be put in parentheses so it executes in a subshell. To
|
||||
force the use of one shell, escape the line breaks so as to make the
|
||||
whole script one command. For example:
|
||||
|
||||
avoid-chdir-side-effects:
|
||||
@ -373,13 +373,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4mnot[24m expanded. This can cause problems when variable modifiers
|
||||
are used.
|
||||
|
||||
[1m!= [22mExpand the value and pass it to the shell for execution and
|
||||
assign the result to the variable. Any newlines in the result
|
||||
are replaced with spaces.
|
||||
[1m!= [22mExpand the value and pass it to the shell for execution and as-
|
||||
sign the result to the variable. Any newlines in the result are
|
||||
replaced with spaces.
|
||||
|
||||
Any white-space before the assigned [4mvalue[24m is removed; if the value is
|
||||
being appended, a single space is inserted between the previous contents
|
||||
of the variable and the appended value.
|
||||
Any white-space before the assigned [4mvalue[24m is removed; if the value is be-
|
||||
ing appended, a single space is inserted between the previous contents of
|
||||
the variable and the appended value.
|
||||
|
||||
Variables are expanded by surrounding the variable name with either curly
|
||||
braces (`{}') or parentheses (`()') and preceding it with a dollar sign
|
||||
@ -403,7 +403,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
2. Variables in shell commands are expanded when the shell command is
|
||||
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-
|
||||
lowing example code:
|
||||
|
||||
@ -423,9 +423,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
1 2 3
|
||||
3 3 3
|
||||
|
||||
Because while ${a} contains ``1 2 3'' after the loop is executed,
|
||||
${b} contains ``${j} ${j} ${j}'' which expands to ``3 3 3'' since
|
||||
after the loop completes ${j} contains ``3''.
|
||||
Because while ${a} contains "1 2 3" after the loop is executed, ${b}
|
||||
contains "${j} ${j} ${j}" which expands to "3 3 3" since after the
|
||||
loop completes ${j} contains "3".
|
||||
|
||||
[1mVariable classes[0m
|
||||
The four different classes of variables (in order of increasing prece-
|
||||
@ -454,8 +454,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[4m.IMPSRC[24m In suffix-transformation rules, the name/path of the
|
||||
source from which the target is to be transformed (the
|
||||
``implied'' source); also known as `[4m<[24m'. It is not
|
||||
defined in explicit rules.
|
||||
"implied" source); also known as `[4m<[24m'. It is not defined
|
||||
in explicit rules.
|
||||
|
||||
[4m.MEMBER[24m The name of the archive member; also known as `[4m%[24m'.
|
||||
|
||||
@ -546,10 +546,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[4m.MAKE.LEVEL[24m The recursion depth of [1mbmake[22m. The initial instance of
|
||||
[1mbmake [22mwill be 0, and an incremented value is put into the
|
||||
environment to be seen by the next generation. This
|
||||
allows tests like: .if ${.MAKE.LEVEL} == 0 to protect
|
||||
things which should only be evaluated in the initial
|
||||
instance of [1mbmake[22m.
|
||||
environment to be seen by the next generation. This al-
|
||||
lows tests like: .if ${.MAKE.LEVEL} == 0 to protect
|
||||
things which should only be evaluated in the initial in-
|
||||
stance of [1mbmake[22m.
|
||||
|
||||
[4m.MAKE.MAKEFILE_PREFERENCE[0m
|
||||
The ordered list of makefile names (default `[4mmakefile[24m',
|
||||
@ -647,9 +647,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
Building ${.TARGET:H:tA}/${.TARGET:T}
|
||||
|
||||
[4m.MAKEOVERRIDES[24m This variable is used to record the names of variables
|
||||
assigned to on the command line, so that they may be
|
||||
exported as part of `MAKEFLAGS'. This behavior can be
|
||||
disabled by assigning an empty value to `[4m.MAKEOVERRIDES[24m'
|
||||
assigned to on the command line, so that they may be ex-
|
||||
ported as part of `MAKEFLAGS'. This behavior can be dis-
|
||||
abled by assigning an empty value to `[4m.MAKEOVERRIDES[24m'
|
||||
within a makefile. Extra variables can be exported from
|
||||
a makefile by appending their names to `[4m.MAKEOVERRIDES[24m'.
|
||||
`MAKEFLAGS' is re-exported whenever `[4m.MAKEOVERRIDES[24m' is
|
||||
@ -668,8 +668,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
value should be a boolean that controls whether `$$' are
|
||||
preserved when doing `:=' assignments. The default is
|
||||
false, for backwards compatibility. Set to true for com-
|
||||
patability with other makes. If set to false, `$$'
|
||||
becomes `$' per normal evaluation rules.
|
||||
patability with other makes. If set to false, `$$' be-
|
||||
comes `$' per normal evaluation rules.
|
||||
|
||||
[4m.MAKE.UID[24m The user-id running [1mbmake[22m.
|
||||
|
||||
[4m.MAKE.GID[24m The group-id running [1mbmake[22m.
|
||||
|
||||
[4mMAKE_PRINT_VAR_ON_ERROR[0m
|
||||
When [1mbmake [22mstops due to an error, it sets `[4m.ERROR_TARGET[24m'
|
||||
@ -733,8 +737,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4m.PARSEFILE[24m The basename of the current `[4mMakefile[24m' being parsed.
|
||||
This variable and `[4m.PARSEDIR[24m' are both set only while the
|
||||
`[4mMakefiles[24m' are being parsed. If you want to retain
|
||||
their current values, assign them to a variable using
|
||||
assignment with expansion: (`[1m:=[22m').
|
||||
their current values, assign them to a variable using as-
|
||||
signment with expansion: (`[1m:=[22m').
|
||||
|
||||
[4m.PATH[24m A variable that represents the list of directories that
|
||||
[1mbmake [22mwill 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
|
||||
line, if any.
|
||||
|
||||
VPATH Colon-separated (``:'') lists of directories that [1mbmake[0m
|
||||
VPATH Colon-separated (":") lists of directories that [1mbmake[0m
|
||||
will search for files. The variable is supported for
|
||||
compatibility with old make programs only, use `[4m.PATH[24m'
|
||||
instead.
|
||||
|
||||
[1mVariable modifiers[0m
|
||||
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:
|
||||
|
||||
${variable[:modifier[:...]]}
|
||||
@ -831,8 +835,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
passed safely to the shell.
|
||||
|
||||
[1m:q [22mQuotes every shell meta-character in the variable, and also doubles
|
||||
`$' characters so that it can be passed safely through recursive
|
||||
invocations of [1mbmake[22m. This is equivalent to: `:S/\$/&&/g:Q'.
|
||||
`$' characters so that it can be passed safely through recursive in-
|
||||
vocations of [1mbmake[22m. This is equivalent to: `:S/\$/&&/g:Q'.
|
||||
|
||||
[1m:R [22mReplaces each word in the variable with everything but its suffix.
|
||||
|
||||
@ -872,11 +876,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m:S[22m/[4mold_string[24m/[4mnew_string[24m/[[1m1gW[22m]
|
||||
Modifies the first occurrence of [4mold_string[24m in each word of the
|
||||
variable's value, replacing it with [4mnew_string[24m. If a `g' is
|
||||
appended to the last delimiter of the pattern, all occurrences in
|
||||
each word are replaced. If a `1' is appended to the last delimiter
|
||||
of the pattern, only the first occurrence is affected. If a `W' is
|
||||
appended to the last delimiter of the pattern, then the value is
|
||||
variable's value, replacing it with [4mnew_string[24m. If a `g' is ap-
|
||||
pended to the last delimiter of the pattern, all occurrences in each
|
||||
word are replaced. If a `1' is appended to the last delimiter of
|
||||
the pattern, only the first occurrence is affected. If a `W' is ap-
|
||||
pended to the last delimiter of the pattern, then the value is
|
||||
treated as a single word (possibly containing embedded white space).
|
||||
If [4mold_string[24m begins with a caret (`^'), [4mold_string[24m is anchored at
|
||||
the beginning of each word. If [4mold_string[24m 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
|
||||
be the last modifier specified. If [4mold_string[24m or [4mnew_string[24m do not
|
||||
contain the pattern matching character [4m%[24m then it is assumed that
|
||||
they are anchored at the end of each word, so only suffixes or
|
||||
entire words may be replaced. Otherwise [4m%[24m is the substring of
|
||||
they are anchored at the end of each word, so only suffixes or en-
|
||||
tire words may be replaced. Otherwise [4m%[24m is the substring of
|
||||
[4mold_string[24m to be replaced in [4mnew_string[24m. If only [4mold_string[24m con-
|
||||
tains the pattern matching character [4m%[24m, and [4mold_string[24m matches, then
|
||||
the result is the [4mnew_string[24m. If only the [4mnew_string[24m contains the
|
||||
@ -971,8 +975,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[1m:U[4m[22mnewval[0m
|
||||
If the variable is undefined, [4mnewval[24m is the value. If the variable
|
||||
is defined, the existing value is returned. This is another ODE
|
||||
make feature. It is handy for setting per-target CFLAGS for
|
||||
instance:
|
||||
make feature. It is handy for setting per-target CFLAGS for in-
|
||||
stance:
|
||||
${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
|
||||
If a value is only required if the variable is undefined, use:
|
||||
${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
|
||||
negative integers (where index -1 represents the last word).
|
||||
|
||||
The [4mrange[24m is subjected to variable expansion, and the expanded
|
||||
result is then interpreted as follows:
|
||||
The [4mrange[24m is subjected to variable expansion, and the expanded re-
|
||||
sult is then interpreted as follows:
|
||||
|
||||
[4mindex[24m Selects a single word from the value.
|
||||
|
||||
@ -1037,8 +1041,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
`[1m:[2..-1][22m' selects all words from the second word to the last
|
||||
word. If [4mstart[24m is greater than [4mend[24m, then the words are out-
|
||||
put in reverse order. For example, `[1m:[-1..1][22m' selects all
|
||||
the words from last to first. If the list is already
|
||||
ordered, then this effectively reverses the list, but it is
|
||||
the words from last to first. If the list is already or-
|
||||
dered, then this effectively reverses the list, but it is
|
||||
more efficient to use `[1m:Or[22m' instead of `[1m:O:[-1..1][22m'.
|
||||
|
||||
[1m* [22mCauses 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.
|
||||
Files are included with either [1m.include <[4m[22mfile[24m[1m> [22mor [1m.include "[4m[22mfile[24m[1m"[22m. Vari-
|
||||
ables between the angle brackets or double quotes are expanded to form
|
||||
the file name. If angle brackets are used, the included makefile is
|
||||
expected to be in the system makefile directory. If double quotes are
|
||||
the file name. If angle brackets are used, the included makefile is ex-
|
||||
pected to be in the system makefile directory. If double quotes are
|
||||
used, the including makefile's directory and any directories specified
|
||||
using the [1m-I [22moption are searched before the system makefile directory.
|
||||
For compatibility with other versions of [1mbmake [22m`include file ...' is also
|
||||
accepted.
|
||||
|
||||
If the include statement is written as [1m.-include [22mor as [1m.sinclude [22mthen
|
||||
errors locating and/or opening include files are ignored.
|
||||
If the include statement is written as [1m.-include [22mor as [1m.sinclude [22mthen er-
|
||||
rors locating and/or opening include files are ignored.
|
||||
|
||||
If the include statement is written as [1m.dinclude [22mnot only are errors
|
||||
locating and/or opening include files ignored, but stale dependencies
|
||||
If the include statement is written as [1m.dinclude [22mnot only are errors lo-
|
||||
cating and/or opening include files ignored, but stale dependencies
|
||||
within the included file will be ignored just like [4m.MAKE.DEPENDFILE[24m.
|
||||
|
||||
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
|
||||
other [1mbmake [22mprograms `export variable=value' is also accepted.
|
||||
|
||||
Appending a variable name to [4m.MAKE.EXPORTED[24m is equivalent to
|
||||
exporting a variable.
|
||||
Appending a variable name to [4m.MAKE.EXPORTED[24m is equivalent to ex-
|
||||
porting a variable.
|
||||
|
||||
[1m.export-env [4m[22mvariable[24m [4m...[0m
|
||||
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
|
||||
line number.
|
||||
|
||||
[1m.undef [4m[22mvariable[0m
|
||||
Un-define the specified global variable. Only global variables
|
||||
may be un-defined.
|
||||
[1m.undef [4m[22mvariable[24m [4m...[0m
|
||||
Un-define the specified global variables. Only global variables
|
||||
can be un-defined.
|
||||
|
||||
[1m.unexport [4m[22mvariable[24m [4m...[0m
|
||||
The opposite of `.export'. The specified global [4mvariable[24m will be
|
||||
@ -1172,7 +1176,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m|| [22mLogical OR.
|
||||
|
||||
[1m&& [22mLogical AND; of higher precedence than ``||''.
|
||||
[1m&& [22mLogical AND; of higher precedence than "||".
|
||||
|
||||
As in C, [1mbmake [22mwill only evaluate a conditional as far as is necessary to
|
||||
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.
|
||||
|
||||
[1mmake [22mTakes a target name as an argument and evaluates to true if the
|
||||
target was specified as part of [1mbmake[22m's command line or was
|
||||
declared the default target (either implicitly or explicitly,
|
||||
see [4m.MAIN[24m) before the line containing the conditional.
|
||||
target was specified as part of [1mbmake[22m's command line or was de-
|
||||
clared the default target (either implicitly or explicitly, see
|
||||
[4m.MAIN[24m) before the line containing the conditional.
|
||||
|
||||
[1mempty [22mTakes a variable, with possible modifiers, and evaluates to true
|
||||
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
|
||||
target has been defined and has commands associated with it.
|
||||
|
||||
[4mExpression[24m may also be an arithmetic or string comparison. Variable
|
||||
expansion is performed on both sides of the comparison, after which the
|
||||
numerical values are compared. A value is interpreted as hexadecimal if
|
||||
it is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
|
||||
[4mExpression[24m may also be an arithmetic or string comparison. Variable ex-
|
||||
pansion is performed on both sides of the comparison, after which the nu-
|
||||
merical values are compared. A value is interpreted as hexadecimal if it
|
||||
is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
|
||||
ported. The standard C relational operators are all supported. If after
|
||||
variable expansion, either the left or right hand side of a `[1m==[22m' or `[1m!=[22m'
|
||||
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
|
||||
empty string in the case of a string comparison.
|
||||
|
||||
When [1mbmake [22mis evaluating one of these conditional expressions, and it
|
||||
encounters a (white-space separated) word it doesn't recognize, either
|
||||
the ``make'' or ``defined'' expression is applied to it, depending on the
|
||||
form of the conditional. If the form is `[1m.ifdef[22m', `[1m.ifndef[22m', or `[1m.if[22m'
|
||||
the ``defined'' expression is applied. Similarly, if the form is
|
||||
`[1m.ifmake[22m' or `[1m.ifnmake[22m', the ``make'' expression is applied.
|
||||
When [1mbmake [22mis evaluating one of these conditional expressions, and it en-
|
||||
counters a (white-space separated) word it doesn't recognize, either the
|
||||
"make" or "defined" expression is applied to it, depending on the form of
|
||||
the conditional. If the form is `[1m.ifdef[22m', `[1m.ifndef[22m', or `[1m.if[22m' the
|
||||
"defined" expression is applied. Similarly, if the form is `[1m.ifmake[22m' or
|
||||
`[1m.ifnmake[22m', the "make" expression is applied.
|
||||
|
||||
If the conditional evaluates to true the parsing of the makefile contin-
|
||||
ues as before. If it evaluates to false, the following lines are
|
||||
@ -1272,8 +1276,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
always changes. If the number of commands change, though, the
|
||||
target will still be out of date. The same effect applies to
|
||||
any command line that uses the variable [4m.OODATE[24m, which can be
|
||||
used for that purpose even when not otherwise needed or
|
||||
desired:
|
||||
used for that purpose even when not otherwise needed or de-
|
||||
sired:
|
||||
|
||||
|
||||
skip-compare-for-some:
|
||||
@ -1303,8 +1307,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.PRECIOUS[0m
|
||||
When [1mbmake [22mis interrupted, it normally removes any partially
|
||||
made targets. This source prevents the target from being
|
||||
removed.
|
||||
made targets. This source prevents the target from being re-
|
||||
moved.
|
||||
|
||||
[1m.RECURSIVE[0m
|
||||
Synonym for [1m.MAKE[22m.
|
||||
@ -1324,10 +1328,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.WAIT [22mIf [1m.WAIT [22mappears in a dependency line, the sources that precede
|
||||
it are made before the sources that succeed it in the line.
|
||||
Since the dependents of files are not made until the file
|
||||
itself could be made, this also stops the dependents being
|
||||
built unless they are needed for another branch of the depen-
|
||||
dency tree. So given:
|
||||
Since the dependents of files are not made until the file it-
|
||||
self could be made, this also stops the dependents being built
|
||||
unless they are needed for another branch of the dependency
|
||||
tree. So given:
|
||||
|
||||
x: a .WAIT b
|
||||
echo x
|
||||
@ -1361,14 +1365,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
make to delete targets whose commands fail. (By default, only
|
||||
targets whose commands are interrupted during execution are
|
||||
deleted. This is the historical behavior.) This setting can be
|
||||
used to help prevent half-finished or malformed targets from
|
||||
being left around and corrupting future rebuilds.
|
||||
used to help prevent half-finished or malformed targets from be-
|
||||
ing left around and corrupting future rebuilds.
|
||||
|
||||
[1m.END [22mAny command lines attached to this target are executed after
|
||||
everything else is done.
|
||||
[1m.END [22mAny command lines attached to this target are executed after ev-
|
||||
erything else is done.
|
||||
|
||||
[1m.ERROR [22mAny command lines attached to this target are executed when
|
||||
another target fails. The [1m.ERROR_TARGET [22mvariable is set to the
|
||||
[1m.ERROR [22mAny command lines attached to this target are executed when an-
|
||||
other target fails. The [1m.ERROR_TARGET [22mvariable is set to the
|
||||
target that failed. See also [1mMAKE_PRINT_VAR_ON_ERROR[22m.
|
||||
|
||||
[1m.IGNORE [22mMark each of the sources with the [1m.IGNORE [22mattribute. If no
|
||||
@ -1425,8 +1429,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.PRECIOUS[0m
|
||||
Apply the [1m.PRECIOUS [22mattribute to any specified sources. If no
|
||||
sources are specified, the [1m.PRECIOUS [22mattribute is applied to
|
||||
every target in the file.
|
||||
sources are specified, the [1m.PRECIOUS [22mattribute is applied to ev-
|
||||
ery target in the file.
|
||||
|
||||
[1m.SHELL [22mSets the shell that [1mbmake [22mwill use to execute commands. The
|
||||
sources are a set of [4mfield=value[24m pairs.
|
||||
@ -1469,8 +1473,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
sources are specified, the [1m.SILENT [22mattribute is applied to every
|
||||
command in the file.
|
||||
|
||||
[1m.STALE [22mThis target gets run when a dependency file contains stale
|
||||
entries, having [4m.ALLSRC[24m set to the name of that dependency file.
|
||||
[1m.STALE [22mThis target gets run when a dependency file contains stale en-
|
||||
tries, having [4m.ALLSRC[24m set to the name of that dependency file.
|
||||
|
||||
[1m.SUFFIXES[0m
|
||||
Each source specifies a suffix to [1mbmake[22m. 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
|
||||
so that they still appear to be variable expansions. In particular this
|
||||
stops them being treated as syntax, and removes some obscure problems
|
||||
using them in .if statements.
|
||||
stops them being treated as syntax, and removes some obscure problems us-
|
||||
ing them in .if statements.
|
||||
|
||||
The way that parallel makes are scheduled changed in NetBSD 4.0 so that
|
||||
.ORDER and .WAIT apply recursively to the dependent nodes. The algo-
|
||||
@ -1518,8 +1522,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1mOther make dialects[0m
|
||||
Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup-
|
||||
port most of the features of [1mbmake [22mas described in this manual. Most
|
||||
notably:
|
||||
port most of the features of [1mbmake [22mas described in this manual. Most no-
|
||||
tably:
|
||||
|
||||
[1m+o [22mThe [1m.WAIT [22mand [1m.ORDER [22mdeclarations and most functionality per-
|
||||
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 [1m+=[22m, [1m?=[22m,
|
||||
and [1m!=[22m. The [1m.PATH [22mfunctionality is based on an older feature [1mVPATH [22mfound
|
||||
in GNU make and many versions of SVR4 make; however, historically its
|
||||
behavior is too ill-defined (and too buggy) to rely upon.
|
||||
in GNU make and many versions of SVR4 make; however, historically its be-
|
||||
havior is too ill-defined (and too buggy) to rely upon.
|
||||
|
||||
The [1m$@ [22mand [1m$< [22mvariables are more or less universally portable, as is the
|
||||
[1m$(MAKE) [22mvariable. 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
|
||||
is based on Adam De Boor's pmake program which was written for Sprite at
|
||||
Berkeley. It was designed to be a parallel distributed make running jobs
|
||||
on different machines using a daemon called ``customs''.
|
||||
on different machines using a daemon called "customs".
|
||||
|
||||
Historically the target/dependency ``FRC'' has been used to FoRCe
|
||||
rebuilding (since the target/dependency does not exist... unless someone
|
||||
creates an ``FRC'' file).
|
||||
Historically the target/dependency "FRC" has been used to FoRCe rebuild-
|
||||
ing (since the target/dependency does not exist... unless someone creates
|
||||
an "FRC" file).
|
||||
|
||||
[1mBUGS[0m
|
||||
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.
|
||||
|
||||
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 |
133
buf.c
133
buf.c
@ -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.
|
||||
@ -69,146 +69,153 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Automatically-expanding null-terminated buffers. */
|
||||
/* Automatically-expanding null-terminated character buffers. */
|
||||
|
||||
#include <limits.h>
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)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
|
||||
Buf_Expand_1(Buffer *buf)
|
||||
Buf_Expand(Buffer *buf)
|
||||
{
|
||||
buf->cap += buf->cap > 16 ? buf->cap : 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
buf->cap += buf->cap > 16 ? buf->cap : 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
|
||||
/* Add the bytes to the buffer. */
|
||||
void
|
||||
Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
|
||||
{
|
||||
size_t old_len = buf->len;
|
||||
char *end;
|
||||
size_t old_len = buf->len;
|
||||
char *end;
|
||||
|
||||
if (__predict_false(old_len + bytes_len >= buf->cap)) {
|
||||
buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
if (__predict_false(old_len + bytes_len >= buf->cap)) {
|
||||
size_t minIncr = bytes_len + 16;
|
||||
buf->cap += buf->cap > minIncr ? buf->cap : minIncr;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
|
||||
end = buf->data + old_len;
|
||||
buf->len = old_len + bytes_len;
|
||||
memcpy(end, bytes, bytes_len);
|
||||
end[bytes_len] = '\0';
|
||||
end = buf->data + old_len;
|
||||
buf->len = old_len + bytes_len;
|
||||
memcpy(end, bytes, bytes_len);
|
||||
end[bytes_len] = '\0';
|
||||
}
|
||||
|
||||
/* Add the bytes between start and end to the buffer. */
|
||||
void
|
||||
Buf_AddBytesBetween(Buffer *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. */
|
||||
void
|
||||
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. */
|
||||
void
|
||||
Buf_AddInt(Buffer *buf, int n)
|
||||
{
|
||||
enum {
|
||||
bits = sizeof(int) * CHAR_BIT,
|
||||
max_octal_digits = (bits + 2) / 3,
|
||||
max_decimal_digits = /* at most */ max_octal_digits,
|
||||
max_sign_chars = 1,
|
||||
str_size = max_sign_chars + max_decimal_digits + 1
|
||||
};
|
||||
char str[str_size];
|
||||
enum {
|
||||
bits = sizeof(int) * CHAR_BIT,
|
||||
max_octal_digits = (bits + 2) / 3,
|
||||
max_decimal_digits = /* at most */ max_octal_digits,
|
||||
max_sign_chars = 1,
|
||||
str_size = max_sign_chars + max_decimal_digits + 1
|
||||
};
|
||||
char str[str_size];
|
||||
|
||||
size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
|
||||
Buf_AddBytes(buf, str, len);
|
||||
size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
|
||||
Buf_AddBytes(buf, str, len);
|
||||
}
|
||||
|
||||
/* Get the data (usually a string) from the buffer.
|
||||
/*
|
||||
* Get the data (usually a string) from the buffer.
|
||||
* The returned data is valid until the next modifying operation
|
||||
* on the buffer.
|
||||
*
|
||||
* Returns the data and optionally the length of the data. */
|
||||
* Returns the data and optionally the length of the data.
|
||||
*/
|
||||
char *
|
||||
Buf_GetAll(Buffer *buf, size_t *out_len)
|
||||
{
|
||||
if (out_len != NULL)
|
||||
*out_len = buf->len;
|
||||
return buf->data;
|
||||
if (out_len != NULL)
|
||||
*out_len = buf->len;
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
/* Mark the buffer as empty, so it can be filled with data again. */
|
||||
void
|
||||
Buf_Empty(Buffer *buf)
|
||||
{
|
||||
buf->len = 0;
|
||||
buf->data[0] = '\0';
|
||||
buf->len = 0;
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Initialize a buffer. */
|
||||
void
|
||||
Buf_InitSize(Buffer *buf, size_t cap)
|
||||
{
|
||||
buf->cap = cap;
|
||||
buf->len = 0;
|
||||
buf->data = bmake_malloc(cap);
|
||||
buf->data[0] = '\0';
|
||||
buf->cap = cap;
|
||||
buf->len = 0;
|
||||
buf->data = bmake_malloc(cap);
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
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.
|
||||
* Otherwise it is kept and returned. */
|
||||
* Otherwise it is kept and returned.
|
||||
*/
|
||||
char *
|
||||
Buf_Destroy(Buffer *buf, Boolean freeData)
|
||||
{
|
||||
char *data = buf->data;
|
||||
if (freeData) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
char *data = buf->data;
|
||||
if (freeData) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
buf->cap = 0;
|
||||
buf->len = 0;
|
||||
buf->data = NULL;
|
||||
buf->cap = 0;
|
||||
buf->len = 0;
|
||||
buf->data = NULL;
|
||||
|
||||
return data;
|
||||
return data;
|
||||
}
|
||||
|
||||
#ifndef BUF_COMPACT_LIMIT
|
||||
# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
|
||||
# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
|
||||
#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,
|
||||
* a new buffer will be allocated and the old one freed. */
|
||||
* a new buffer will be allocated and the old one freed.
|
||||
*/
|
||||
char *
|
||||
Buf_DestroyCompact(Buffer *buf)
|
||||
{
|
||||
#if BUF_COMPACT_LIMIT > 0
|
||||
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
|
||||
/* We trust realloc to be smart */
|
||||
char *data = bmake_realloc(buf->data, buf->len + 1);
|
||||
data[buf->len] = '\0'; /* XXX: unnecessary */
|
||||
Buf_Destroy(buf, FALSE);
|
||||
return data;
|
||||
}
|
||||
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
|
||||
/* We trust realloc to be smart */
|
||||
char *data = bmake_realloc(buf->data, buf->len + 1);
|
||||
data[buf->len] = '\0'; /* XXX: unnecessary */
|
||||
Buf_Destroy(buf, FALSE);
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
return Buf_Destroy(buf, FALSE);
|
||||
return Buf_Destroy(buf, FALSE);
|
||||
}
|
||||
|
28
buf.h
28
buf.h
@ -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.
|
||||
@ -81,9 +81,9 @@
|
||||
|
||||
/* An automatically growing null-terminated buffer of characters. */
|
||||
typedef struct Buffer {
|
||||
size_t cap; /* Allocated size of the buffer, including the null */
|
||||
size_t len; /* Number of bytes in buffer, excluding the null */
|
||||
char *data; /* The buffer itself (always null-terminated) */
|
||||
size_t cap; /* Allocated size of the buffer, including the null */
|
||||
size_t len; /* Number of bytes in buffer, excluding the null */
|
||||
char *data; /* The buffer itself (always null-terminated) */
|
||||
} Buffer;
|
||||
|
||||
/* If we aren't on NetBSD, __predict_false() might not be defined. */
|
||||
@ -91,31 +91,31 @@ typedef struct Buffer {
|
||||
#define __predict_false(x) (x)
|
||||
#endif
|
||||
|
||||
void Buf_Expand_1(Buffer *);
|
||||
void Buf_Expand(Buffer *);
|
||||
|
||||
/* Buf_AddByte adds a single byte to a buffer. */
|
||||
MAKE_INLINE void
|
||||
Buf_AddByte(Buffer *buf, char byte)
|
||||
{
|
||||
size_t old_len = buf->len++;
|
||||
char *end;
|
||||
if (__predict_false(old_len + 1 >= buf->cap))
|
||||
Buf_Expand_1(buf);
|
||||
end = buf->data + old_len;
|
||||
end[0] = byte;
|
||||
end[1] = '\0';
|
||||
size_t old_len = buf->len++;
|
||||
char *end;
|
||||
if (__predict_false(old_len + 1 >= buf->cap))
|
||||
Buf_Expand(buf);
|
||||
end = buf->data + old_len;
|
||||
end[0] = byte;
|
||||
end[1] = '\0';
|
||||
}
|
||||
|
||||
MAKE_INLINE size_t
|
||||
Buf_Len(const Buffer *buf)
|
||||
{
|
||||
return buf->len;
|
||||
return buf->len;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
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);
|
||||
|
36
dir.h
36
dir.h
@ -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.
|
||||
@ -72,27 +72,12 @@
|
||||
* from: @(#)dir.h 8.1 (Berkeley) 6/6/93
|
||||
*/
|
||||
|
||||
#ifndef MAKE_DIR_H
|
||||
#define MAKE_DIR_H
|
||||
#ifndef MAKE_DIR_H
|
||||
#define MAKE_DIR_H
|
||||
|
||||
/* A cache for the filenames in a directory. */
|
||||
typedef struct CachedDir {
|
||||
char *name; /* Name of directory, either absolute or
|
||||
* relative to the current directory.
|
||||
* The name is not normalized in any way,
|
||||
* that is, "." and "./." are different.
|
||||
*
|
||||
* Not sure what happens when .CURDIR is
|
||||
* assigned a new value; see Parse_DoVar. */
|
||||
int refCount; /* Number of SearchPaths with this directory */
|
||||
int hits; /* The number of times a file in this
|
||||
* directory has been found */
|
||||
HashTable files; /* Hash set of files in directory;
|
||||
* all values are NULL. */
|
||||
} CachedDir;
|
||||
typedef struct CachedDir CachedDir;
|
||||
|
||||
void Dir_Init(void);
|
||||
void Dir_InitDir(const char *);
|
||||
void Dir_InitCur(const char *);
|
||||
void Dir_InitDot(void);
|
||||
void Dir_End(void);
|
||||
@ -103,18 +88,17 @@ char *Dir_FindFile(const char *, SearchPath *);
|
||||
char *Dir_FindHereOrAbove(const char *, const char *);
|
||||
void Dir_UpdateMTime(GNode *, Boolean);
|
||||
CachedDir *Dir_AddDir(SearchPath *, const char *);
|
||||
char *Dir_MakeFlags(const char *, SearchPath *);
|
||||
void Dir_ClearPath(SearchPath *);
|
||||
void Dir_Concat(SearchPath *, SearchPath *);
|
||||
char *SearchPath_ToFlags(const char *, SearchPath *);
|
||||
void SearchPath_Clear(SearchPath *);
|
||||
void SearchPath_AddAll(SearchPath *, SearchPath *);
|
||||
void Dir_PrintDirectories(void);
|
||||
void Dir_PrintPath(SearchPath *);
|
||||
void Dir_Destroy(void *);
|
||||
void SearchPath_Print(SearchPath *);
|
||||
SearchPath *Dir_CopyDirSearchPath(void);
|
||||
|
||||
/* Stripped-down variant of struct stat. */
|
||||
struct cached_stat {
|
||||
time_t cst_mtime;
|
||||
mode_t cst_mode;
|
||||
time_t cst_mtime;
|
||||
mode_t cst_mode;
|
||||
};
|
||||
|
||||
int cached_lstat(const char *, struct cached_stat *);
|
||||
|
11
enum.c
11
enum.c
@ -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>
|
||||
@ -29,13 +29,15 @@
|
||||
|
||||
#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.
|
||||
*
|
||||
* 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 *
|
||||
Enum_FlagsToString(char *buf, size_t buf_size,
|
||||
int value, const EnumToStringSpec *spec)
|
||||
@ -86,4 +88,5 @@ Enum_ValueToString(int value, const EnumToStringSpec *spec)
|
||||
return spec->es_name;
|
||||
}
|
||||
abort(/* unknown enum value */);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
69
enum.h
69
enum.h
@ -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>
|
||||
@ -45,8 +45,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
/* For Enum_FlagsToString, the separator between flags. */
|
||||
#define ENUM__SEP "|"
|
||||
|
||||
/* Generate the string that joins all possible flags, to see how large the
|
||||
* buffer must be. */
|
||||
/*
|
||||
* Generate the string that joins all possible flags, to see how large the
|
||||
* buffer must be.
|
||||
*/
|
||||
#define ENUM__JOIN_STR_1(v1) \
|
||||
#v1
|
||||
#define ENUM__JOIN_STR_2(v1, v2) \
|
||||
@ -107,8 +109,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
|
||||
enum { typnam ## _ ## ToStringSize = sizeof joined }
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 2 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 2 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -118,8 +122,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_1(v1), \
|
||||
ENUM__JOIN_STR_1(v2)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 3 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 3 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -129,8 +135,23 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_2(v1, v2), \
|
||||
ENUM__JOIN_STR_1(v3)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 6 flags. */
|
||||
/*
|
||||
* 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) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
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_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) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
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(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) \
|
||||
ENUM__VALUE_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_4(v1, v2, v3, v4), \
|
||||
ENUM__SPEC_4(v5, v6, v7, v8)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 10 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 10 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -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_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, \
|
||||
v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
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_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, \
|
||||
v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16, \
|
||||
|
@ -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.
|
||||
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#ifndef MAKE_FILEMON_H
|
||||
#define MAKE_FILEMON_H
|
||||
#define MAKE_FILEMON_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -50,4 +50,4 @@ int filemon_setpid_child(const struct filemon *, pid_t);
|
||||
int filemon_readfd(const struct filemon *);
|
||||
int filemon_process(struct filemon *);
|
||||
|
||||
#endif /* MAKE_FILEMON_H */
|
||||
#endif /* MAKE_FILEMON_H */
|
||||
|
@ -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.
|
||||
@ -43,7 +43,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef _PATH_FILEMON
|
||||
#define _PATH_FILEMON "/dev/filemon"
|
||||
#define _PATH_FILEMON "/dev/filemon"
|
||||
#endif
|
||||
|
||||
struct filemon {
|
||||
@ -127,7 +127,7 @@ filemon_close(struct filemon *F)
|
||||
free(F);
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
@ -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.
|
||||
@ -29,7 +29,7 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _KERNTYPES /* register_t */
|
||||
#define _KERNTYPES /* register_t */
|
||||
|
||||
#include "filemon.h"
|
||||
|
||||
@ -90,9 +90,9 @@ static filemon_syscall_t *const filemon_syscalls[] = {
|
||||
};
|
||||
|
||||
struct filemon {
|
||||
int ktrfd; /* kernel writes ktrace events here */
|
||||
FILE *in; /* we read ktrace events from here */
|
||||
FILE *out; /* we write filemon events to here */
|
||||
int ktrfd; /* kernel writes ktrace events here */
|
||||
FILE *in; /* we read ktrace events from here */
|
||||
FILE *out; /* we write filemon events to here */
|
||||
rb_tree_t active;
|
||||
pid_t child;
|
||||
|
||||
@ -130,6 +130,7 @@ struct filemon_state {
|
||||
char *path[/*npath*/];
|
||||
};
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
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;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
compare_filemon_key(void *cookie, const void *n, const void *k)
|
||||
{
|
||||
@ -225,7 +227,6 @@ filemon_open(void)
|
||||
/* Success! */
|
||||
return F;
|
||||
|
||||
fail2: __unused
|
||||
(void)fclose(F->in);
|
||||
fail1: (void)close(ktrpipe[0]);
|
||||
(void)close(ktrpipe[1]);
|
||||
@ -262,7 +263,7 @@ filemon_closefd(struct filemon *F)
|
||||
F->out = NULL;
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
@ -373,7 +374,7 @@ filemon_close(struct filemon *F)
|
||||
free(F);
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
@ -518,12 +519,12 @@ filemon_process(struct filemon *F)
|
||||
return 0;
|
||||
|
||||
/* If we're waiting for input, read some. */
|
||||
if (F->resid) {
|
||||
if (F->resid > 0) {
|
||||
nread = fread(F->p, 1, F->resid, F->in);
|
||||
if (nread == 0) {
|
||||
if (feof(F->in))
|
||||
if (feof(F->in) != 0)
|
||||
return 0;
|
||||
assert(ferror(F->in));
|
||||
assert(ferror(F->in) != 0);
|
||||
/*
|
||||
* If interrupted or would block, there may be
|
||||
* more events. Otherwise fail.
|
||||
@ -538,7 +539,7 @@ filemon_process(struct filemon *F)
|
||||
assert(nread <= F->resid);
|
||||
F->p += nread;
|
||||
F->resid -= nread;
|
||||
if (F->resid) /* may be more events */
|
||||
if (F->resid > 0) /* may be more events */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -582,7 +583,7 @@ filemon_process(struct filemon *F)
|
||||
}
|
||||
|
||||
static struct filemon_state *
|
||||
syscall_enter(struct filemon *F,
|
||||
syscall_enter(
|
||||
const struct filemon_key *key, const struct ktr_syscall *call,
|
||||
unsigned npath,
|
||||
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
|
||||
* we're not producing output.
|
||||
*/
|
||||
if (ret->ktr_error && ret->ktr_error != -2)
|
||||
if (ret->ktr_error != 0 && ret->ktr_error != -2)
|
||||
return;
|
||||
if (F->out == NULL)
|
||||
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
|
||||
* we're not producing output.
|
||||
*/
|
||||
if (ret->ktr_error && ret->ktr_error != -2)
|
||||
if (ret->ktr_error != 0 && ret->ktr_error != -2)
|
||||
return;
|
||||
if (F->out == NULL)
|
||||
return;
|
||||
@ -664,7 +665,7 @@ static void
|
||||
show_execve(struct filemon *F, const struct filemon_state *S,
|
||||
const struct ktr_sysret *ret)
|
||||
{
|
||||
return show_paths(F, S, ret, "E");
|
||||
show_paths(F, S, ret, "E");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -752,18 +753,20 @@ show_rename(struct filemon *F, const struct filemon_state *S,
|
||||
show_paths(F, S, ret, "M");
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_chdir);
|
||||
return syscall_enter(key, call, 1, &show_chdir);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_execve);
|
||||
return syscall_enter(key, call, 1, &show_execve);
|
||||
}
|
||||
|
||||
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];
|
||||
int status = (int)args[0];
|
||||
|
||||
if (F->out) {
|
||||
if (F->out != NULL) {
|
||||
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
|
||||
if (key->pid == F->child) {
|
||||
fprintf(F->out, "# Bye bye\n");
|
||||
@ -783,20 +786,23 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 0, &show_fork);
|
||||
return syscall_enter(key, call, 0, &show_fork);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_link(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_link);
|
||||
return syscall_enter(key, call, 2, &show_link);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_open(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
@ -809,15 +815,16 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
|
||||
flags = (int)args[1];
|
||||
|
||||
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)
|
||||
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)
|
||||
return syscall_enter(F, key, call, 1, &show_open_read);
|
||||
return syscall_enter(key, call, 1, &show_open_read);
|
||||
else
|
||||
return NULL; /* XXX Do we care if no read or write? */
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
|
||||
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 ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
return syscall_enter(key, call, 1,
|
||||
&show_open_readwrite);
|
||||
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)
|
||||
return syscall_enter(F, key, call, 1, &show_open_read);
|
||||
return syscall_enter(key, call, 1, &show_open_read);
|
||||
else
|
||||
return NULL;
|
||||
} else {
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
return syscall_enter(key, call, 1,
|
||||
&show_openat_readwrite);
|
||||
else if ((flags & O_WRONLY) == O_WRONLY)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
&show_openat_write);
|
||||
return syscall_enter(key, call, 1, &show_openat_write);
|
||||
else if ((flags & O_RDONLY) == O_RDONLY)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
&show_openat_read);
|
||||
return syscall_enter(key, call, 1, &show_openat_read);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_symlink);
|
||||
return syscall_enter(key, call, 2, &show_symlink);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_unlink);
|
||||
return syscall_enter(key, call, 1, &show_unlink);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_rename);
|
||||
return syscall_enter(key, call, 2, &show_rename);
|
||||
}
|
||||
|
675
for.c
675
for.c
@ -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.
|
||||
@ -32,24 +32,22 @@
|
||||
/*-
|
||||
* Handling of .for/.endfor loops in a makefile.
|
||||
*
|
||||
* For loops are of the form:
|
||||
* For loops have the form:
|
||||
*
|
||||
* .for <varname...> in <value...>
|
||||
* ...
|
||||
* .endfor
|
||||
* .for <varname...> in <value...>
|
||||
* # the body
|
||||
* .endfor
|
||||
*
|
||||
* When a .for line is parsed, all following lines are accumulated into a
|
||||
* buffer, up to but excluding the corresponding .endfor line. To find the
|
||||
* corresponding .endfor, the number of nested .for and .endfor directives
|
||||
* are counted.
|
||||
* When a .for line is parsed, the following lines are copied to the body of
|
||||
* the .for loop, until the corresponding .endfor line is reached. In this
|
||||
* phase, the body is not yet evaluated. This also applies to any nested
|
||||
* .for loops.
|
||||
*
|
||||
* During parsing, any nested .for loops are just passed through; they get
|
||||
* handled recursively in For_Eval when the enclosing .for loop is evaluated
|
||||
* in For_Run.
|
||||
*
|
||||
* When the .for loop has been parsed completely, the variable expressions
|
||||
* for the iteration variables are replaced with expressions of the form
|
||||
* ${:Uvalue}, and then this modified body is "included" as a special file.
|
||||
* After reaching the .endfor, the values from the .for line are grouped
|
||||
* according to the number of variables. For each such group, the unexpanded
|
||||
* body is scanned for variable expressions, and those that match the variable
|
||||
* names are replaced with expressions of the form ${:U...} or $(:U...).
|
||||
* After that, the body is treated like a file from an .include directive.
|
||||
*
|
||||
* Interface:
|
||||
* For_Eval Evaluate the loop in the passed line.
|
||||
@ -60,29 +58,29 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)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 */
|
||||
|
||||
/* One of the variables to the left of the "in" in a .for loop. */
|
||||
typedef struct ForVar {
|
||||
char *name;
|
||||
size_t len;
|
||||
char *name;
|
||||
size_t nameLen;
|
||||
} ForVar;
|
||||
|
||||
/*
|
||||
* State of a for loop.
|
||||
*/
|
||||
typedef struct For {
|
||||
Buffer body; /* Unexpanded body of the loop */
|
||||
Vector /* of ForVar */ vars; /* Iteration variables */
|
||||
Words items; /* Substitution items */
|
||||
Buffer curBody; /* Expanded body of the current iteration */
|
||||
/* Is any of the names 1 character long? If so, when the variable values
|
||||
* are substituted, the parser must handle $V expressions as well, not
|
||||
* only ${V} and $(V). */
|
||||
Boolean short_var;
|
||||
unsigned int sub_next; /* Where to continue iterating */
|
||||
Buffer body; /* Unexpanded body of the loop */
|
||||
Vector /* of ForVar */ vars; /* Iteration variables */
|
||||
Words items; /* Substitution items */
|
||||
Buffer curBody; /* Expanded body of the current iteration */
|
||||
/* Is any of the names 1 character long? If so, when the variable values
|
||||
* are substituted, the parser must handle $V expressions as well, not
|
||||
* only ${V} and $(V). */
|
||||
Boolean short_var;
|
||||
unsigned int sub_next; /* Where to continue iterating */
|
||||
} For;
|
||||
|
||||
static For *accumFor; /* Loop being accumulated */
|
||||
@ -90,42 +88,43 @@ static For *accumFor; /* Loop being accumulated */
|
||||
static void
|
||||
ForAddVar(For *f, const char *name, size_t len)
|
||||
{
|
||||
ForVar *var = Vector_Push(&f->vars);
|
||||
var->name = bmake_strldup(name, len);
|
||||
var->len = len;
|
||||
ForVar *var = Vector_Push(&f->vars);
|
||||
var->name = bmake_strldup(name, len);
|
||||
var->nameLen = len;
|
||||
}
|
||||
|
||||
static void
|
||||
For_Free(For *f)
|
||||
{
|
||||
Buf_Destroy(&f->body, TRUE);
|
||||
Buf_Destroy(&f->body, TRUE);
|
||||
|
||||
while (f->vars.len > 0) {
|
||||
ForVar *var = Vector_Pop(&f->vars);
|
||||
free(var->name);
|
||||
}
|
||||
Vector_Done(&f->vars);
|
||||
while (f->vars.len > 0) {
|
||||
ForVar *var = Vector_Pop(&f->vars);
|
||||
free(var->name);
|
||||
}
|
||||
Vector_Done(&f->vars);
|
||||
|
||||
Words_Free(f->items);
|
||||
Buf_Destroy(&f->curBody, TRUE);
|
||||
Words_Free(f->items);
|
||||
Buf_Destroy(&f->curBody, TRUE);
|
||||
|
||||
free(f);
|
||||
free(f);
|
||||
}
|
||||
|
||||
static Boolean
|
||||
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
|
||||
IsEndfor(const char *p)
|
||||
{
|
||||
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
|
||||
(p[6] == '\0' || ch_isspace(p[6]));
|
||||
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
|
||||
(p[6] == '\0' || ch_isspace(p[6]));
|
||||
}
|
||||
|
||||
/* Evaluate the for loop in the passed line. The line looks like this:
|
||||
/*
|
||||
* Evaluate the for loop in the passed line. The line looks like this:
|
||||
* .for <varname...> in <value...>
|
||||
*
|
||||
* Input:
|
||||
@ -139,98 +138,108 @@ IsEndfor(const char *p)
|
||||
int
|
||||
For_Eval(const char *line)
|
||||
{
|
||||
For *f;
|
||||
const char *p;
|
||||
For *f;
|
||||
const char *p;
|
||||
|
||||
p = line + 1; /* skip the '.' */
|
||||
cpp_skip_whitespace(&p);
|
||||
p = line + 1; /* skip the '.' */
|
||||
cpp_skip_whitespace(&p);
|
||||
|
||||
if (!IsFor(p)) {
|
||||
if (IsEndfor(p)) {
|
||||
Parse_Error(PARSE_FATAL, "for-less endfor");
|
||||
return -1;
|
||||
if (!IsFor(p)) {
|
||||
if (IsEndfor(p)) {
|
||||
Parse_Error(PARSE_FATAL, "for-less endfor");
|
||||
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);
|
||||
Buf_Init(&f->body);
|
||||
Vector_Init(&f->vars, sizeof(ForVar));
|
||||
f->items.words = NULL;
|
||||
f->items.freeIt = NULL;
|
||||
Buf_Init(&f->curBody);
|
||||
f->short_var = FALSE;
|
||||
f->sub_next = 0;
|
||||
f = bmake_malloc(sizeof *f);
|
||||
Buf_Init(&f->body);
|
||||
Vector_Init(&f->vars, sizeof(ForVar));
|
||||
f->items.words = NULL;
|
||||
f->items.freeIt = NULL;
|
||||
Buf_Init(&f->curBody);
|
||||
f->short_var = FALSE;
|
||||
f->sub_next = 0;
|
||||
|
||||
/* Grab the variables. Terminate on "in". */
|
||||
for (;;) {
|
||||
size_t len;
|
||||
/* Grab the variables. Terminate on "in". */
|
||||
for (;;) {
|
||||
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);
|
||||
if (*p == '\0') {
|
||||
Parse_Error(PARSE_FATAL, "missing `in' in for");
|
||||
For_Free(f);
|
||||
return -1;
|
||||
|
||||
{
|
||||
char *items;
|
||||
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++)
|
||||
continue;
|
||||
{
|
||||
size_t nitems, nvars;
|
||||
|
||||
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
|
||||
p += 2;
|
||||
break;
|
||||
if ((nitems = f->items.len) > 0 &&
|
||||
nitems % (nvars = f->vars.len) != 0) {
|
||||
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);
|
||||
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);
|
||||
|
||||
{
|
||||
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;
|
||||
done:
|
||||
accumFor = f;
|
||||
forLevel = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -240,240 +249,256 @@ For_Eval(const char *line)
|
||||
Boolean
|
||||
For_Accum(const char *line)
|
||||
{
|
||||
const char *ptr = line;
|
||||
const char *ptr = line;
|
||||
|
||||
if (*ptr == '.') {
|
||||
ptr++;
|
||||
cpp_skip_whitespace(&ptr);
|
||||
if (*ptr == '.') {
|
||||
ptr++;
|
||||
cpp_skip_whitespace(&ptr);
|
||||
|
||||
if (IsEndfor(ptr)) {
|
||||
DEBUG1(FOR, "For: end for %d\n", forLevel);
|
||||
if (--forLevel <= 0)
|
||||
return FALSE;
|
||||
} else if (IsFor(ptr)) {
|
||||
forLevel++;
|
||||
DEBUG1(FOR, "For: new loop %d\n", forLevel);
|
||||
if (IsEndfor(ptr)) {
|
||||
DEBUG1(FOR, "For: end for %d\n", forLevel);
|
||||
if (--forLevel <= 0)
|
||||
return FALSE;
|
||||
} else if (IsFor(ptr)) {
|
||||
forLevel++;
|
||||
DEBUG1(FOR, "For: new loop %d\n", forLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Buf_AddStr(&accumFor->body, line);
|
||||
Buf_AddByte(&accumFor->body, '\n');
|
||||
return TRUE;
|
||||
Buf_AddStr(&accumFor->body, line);
|
||||
Buf_AddByte(&accumFor->body, '\n');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
for_var_len(const char *var)
|
||||
{
|
||||
char ch, var_start, var_end;
|
||||
int depth;
|
||||
size_t len;
|
||||
char ch, var_start, var_end;
|
||||
int depth;
|
||||
size_t len;
|
||||
|
||||
var_start = *var;
|
||||
if (var_start == '\0')
|
||||
/* just escape the $ */
|
||||
var_start = *var;
|
||||
if (var_start == '\0')
|
||||
/* 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;
|
||||
|
||||
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
|
||||
* with variable references that expand to the required text.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
* Many of the modifiers use \ to escape $ (not $) so it is possible
|
||||
* to contrive a makefile where an unwanted substitution happens.
|
||||
*/
|
||||
static char *
|
||||
ForIterate(void *v_arg, size_t *out_len)
|
||||
static void
|
||||
ForSubstBody(For *f)
|
||||
{
|
||||
For *f = v_arg;
|
||||
const char *p;
|
||||
const char *mark; /* where the last replacement left off */
|
||||
const char *body_end;
|
||||
char *cmds_str;
|
||||
const char *p, *bodyEnd;
|
||||
const char *mark; /* where the last replacement left off */
|
||||
|
||||
if (f->sub_next + f->vars.len > f->items.len) {
|
||||
/* No more iterations */
|
||||
For_Free(f);
|
||||
return NULL;
|
||||
}
|
||||
Buf_Empty(&f->curBody);
|
||||
|
||||
Buf_Empty(&f->curBody);
|
||||
|
||||
mark = Buf_GetAll(&f->body, NULL);
|
||||
body_end = mark + Buf_Len(&f->body);
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
char ch, ech;
|
||||
ch = *++p;
|
||||
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) {
|
||||
p++;
|
||||
/* Check variable name against the .for loop variables */
|
||||
SubstVarLong(f, &p, &mark, ech);
|
||||
continue;
|
||||
mark = f->body.data;
|
||||
bodyEnd = f->body.data + f->body.len;
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
if (p[1] == '{' || p[1] == '(') {
|
||||
p += 2;
|
||||
SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')',
|
||||
&mark);
|
||||
} else if (p[1] != '\0') {
|
||||
SubstVarShort(f, p + 1, &mark);
|
||||
p += 2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
SubstVarShort(f, ch, &p, &mark);
|
||||
}
|
||||
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;
|
||||
Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
|
||||
}
|
||||
|
||||
/* 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
|
||||
For_Run(int lineno)
|
||||
{
|
||||
For *f = accumFor;
|
||||
accumFor = NULL;
|
||||
For *f = accumFor;
|
||||
accumFor = NULL;
|
||||
|
||||
if (f->items.len == 0) {
|
||||
/* Nothing to expand - possibly due to an earlier syntax error. */
|
||||
For_Free(f);
|
||||
return;
|
||||
}
|
||||
if (f->items.len == 0) {
|
||||
/*
|
||||
* Nothing to expand - possibly due to an earlier syntax
|
||||
* error.
|
||||
*/
|
||||
For_Free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
Parse_SetInput(NULL, lineno, -1, ForIterate, f);
|
||||
Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
|
||||
}
|
||||
|
41
hash.c
41
hash.c
@ -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.
|
||||
@ -74,7 +74,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)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
|
||||
@ -86,10 +86,13 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $");
|
||||
static unsigned int
|
||||
hash(const char *key, size_t *out_keylen)
|
||||
{
|
||||
unsigned int h = 0;
|
||||
const char *p = key;
|
||||
while (*p != '\0')
|
||||
h = (h << 5) - h + (unsigned char)*p++;
|
||||
unsigned int h;
|
||||
const char *p;
|
||||
|
||||
h = 0;
|
||||
for (p = key; *p != '\0'; p++)
|
||||
h = 31 * h + (unsigned char)*p;
|
||||
|
||||
if (out_keylen != NULL)
|
||||
*out_keylen = (size_t)(p - key);
|
||||
return h;
|
||||
@ -98,7 +101,7 @@ hash(const char *key, size_t *out_keylen)
|
||||
unsigned int
|
||||
Hash_Hash(const char *key)
|
||||
{
|
||||
return hash(key, NULL);
|
||||
return hash(key, NULL);
|
||||
}
|
||||
|
||||
static HashEntry *
|
||||
@ -177,8 +180,10 @@ HashTable_FindValue(HashTable *t, const char *key)
|
||||
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 *
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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
|
||||
HashTable_Enlarge(HashTable *t)
|
||||
{
|
||||
@ -221,8 +228,10 @@ HashTable_Enlarge(HashTable *t)
|
||||
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 *
|
||||
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
|
||||
{
|
||||
@ -288,8 +297,10 @@ HashIter_Init(HashIter *hi, HashTable *t)
|
||||
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 *
|
||||
HashIter_Next(HashIter *hi)
|
||||
{
|
||||
|
72
hash.h
72
hash.h
@ -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.
|
||||
@ -74,45 +74,50 @@
|
||||
|
||||
/* Hash tables with strings as keys and arbitrary pointers as values. */
|
||||
|
||||
#ifndef MAKE_HASH_H
|
||||
#define MAKE_HASH_H
|
||||
#ifndef MAKE_HASH_H
|
||||
#define MAKE_HASH_H
|
||||
|
||||
/* A single key-value entry in the hash table. */
|
||||
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. */
|
||||
void *value;
|
||||
unsigned int key_hash; /* hash value of the key */
|
||||
char key[1]; /* key string, variable length */
|
||||
void *value;
|
||||
unsigned int key_hash; /* hash value of the key */
|
||||
char key[1]; /* key string, variable length */
|
||||
} HashEntry;
|
||||
|
||||
/* The hash table containing the entries. */
|
||||
typedef struct HashTable {
|
||||
HashEntry **buckets; /* Pointers to HashEntry, one
|
||||
HashEntry **buckets; /* Pointers to HashEntry, one
|
||||
* for each bucket in the table. */
|
||||
unsigned int bucketsSize;
|
||||
unsigned int numEntries; /* Number of entries in the table. */
|
||||
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
|
||||
unsigned int maxchain; /* max length of chain detected */
|
||||
unsigned int bucketsSize;
|
||||
unsigned int numEntries; /* Number of entries in the table. */
|
||||
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
|
||||
unsigned int maxchain; /* max length of chain detected */
|
||||
} HashTable;
|
||||
|
||||
/* State of an iteration over all entries in a table. */
|
||||
typedef struct HashIter {
|
||||
HashTable *table; /* Table being searched. */
|
||||
unsigned int nextBucket; /* Next bucket to check (after current). */
|
||||
HashEntry *entry; /* Next entry to check in current bucket. */
|
||||
HashTable *table; /* Table being searched. */
|
||||
unsigned int nextBucket; /* Next bucket to check (after current). */
|
||||
HashEntry *entry; /* Next entry to check in current bucket. */
|
||||
} HashIter;
|
||||
|
||||
/* A set of strings. */
|
||||
typedef struct HashSet {
|
||||
HashTable tbl;
|
||||
} HashSet;
|
||||
|
||||
MAKE_INLINE void *
|
||||
HashEntry_Get(HashEntry *h)
|
||||
{
|
||||
return h->value;
|
||||
return h->value;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
HashEntry_Set(HashEntry *h, void *datum)
|
||||
{
|
||||
h->value = datum;
|
||||
h->value = datum;
|
||||
}
|
||||
|
||||
void HashTable_Init(HashTable *);
|
||||
@ -129,4 +134,37 @@ void HashTable_DebugStats(HashTable *, const char *);
|
||||
void HashIter_Init(HashIter *, HashTable *);
|
||||
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 */
|
||||
|
87
import.sh
Executable file
87
import.sh
Executable 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
|
84
job.h
84
job.h
@ -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.
|
||||
@ -97,8 +97,8 @@ struct emul_pollfd {
|
||||
short revents;
|
||||
};
|
||||
|
||||
#define POLLIN 0x0001
|
||||
#define POLLOUT 0x0004
|
||||
#define POLLIN 0x0001
|
||||
#define POLLOUT 0x0004
|
||||
|
||||
int
|
||||
emul_poll(struct pollfd *fd, int nfd, int timeout);
|
||||
@ -118,27 +118,15 @@ struct pollfd;
|
||||
#endif
|
||||
|
||||
typedef enum JobStatus {
|
||||
JOB_ST_FREE = 0, /* Job is available */
|
||||
JOB_ST_SET_UP = 1, /* Job is allocated but otherwise invalid */
|
||||
/* XXX: What about the 2? */
|
||||
JOB_ST_RUNNING = 3, /* Job is running, pid valid */
|
||||
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
|
||||
JOB_ST_FREE = 0, /* Job is available */
|
||||
JOB_ST_SET_UP = 1, /* Job is allocated but otherwise invalid */
|
||||
/* XXX: What about the 2? */
|
||||
JOB_ST_RUNNING = 3, /* Job is running, pid valid */
|
||||
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
|
||||
} JobStatus;
|
||||
|
||||
typedef enum JobFlags {
|
||||
JOB_NONE = 0,
|
||||
/* 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.
|
||||
/*
|
||||
* A Job manages the shell commands that are run to create a single target.
|
||||
* Each job is run in a separate subprocess by a shell. Several jobs can run
|
||||
* in parallel.
|
||||
*
|
||||
@ -148,42 +136,47 @@ typedef enum JobFlags {
|
||||
*
|
||||
* When a job is finished, Make_Update updates all parents of the node
|
||||
* that was just remade, marking them as ready to be made next if all
|
||||
* other dependencies are finished as well. */
|
||||
* other dependencies are finished as well.
|
||||
*/
|
||||
typedef struct Job {
|
||||
/* The process ID of the shell running the commands */
|
||||
int pid;
|
||||
/* The process ID of the shell running the commands */
|
||||
int pid;
|
||||
|
||||
/* The target the child is making */
|
||||
GNode *node;
|
||||
/* The target the child is making */
|
||||
GNode *node;
|
||||
|
||||
/* If one of the shell commands is "...", all following commands are
|
||||
* delayed until the .END node is made. This list node points to the
|
||||
* first of these commands, if any. */
|
||||
StringListNode *tailCmds;
|
||||
/* If one of the shell commands is "...", all following commands are
|
||||
* delayed until the .END node is made. This list node points to the
|
||||
* first of these commands, if any. */
|
||||
StringListNode *tailCmds;
|
||||
|
||||
/* When creating the shell script, this is where the commands go.
|
||||
* This is only used before the job is actually started. */
|
||||
FILE *cmdFILE;
|
||||
/* This is where the shell commands go. */
|
||||
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 outPipe; /* Pipe for writing control commands */
|
||||
struct pollfd *inPollfd; /* pollfd associated with inPipe */
|
||||
int inPipe; /* Pipe for reading output from job */
|
||||
int outPipe; /* Pipe for writing control commands */
|
||||
struct pollfd *inPollfd; /* pollfd associated with inPipe */
|
||||
|
||||
#define JOB_BUFSIZE 1024
|
||||
/* Buffer for storing the output of the job, line by line. */
|
||||
char outBuf[JOB_BUFSIZE + 1];
|
||||
size_t curPos; /* Current position in outBuf. */
|
||||
/* Buffer for storing the output of the job, line by line. */
|
||||
char outBuf[JOB_BUFSIZE + 1];
|
||||
size_t curPos; /* Current position in outBuf. */
|
||||
|
||||
#ifdef USE_META
|
||||
struct BuildMon bm;
|
||||
struct BuildMon bm;
|
||||
#endif
|
||||
} Job;
|
||||
|
||||
@ -211,5 +204,6 @@ Boolean Job_TokenWithdraw(void);
|
||||
void Job_ServerStart(int, int, int);
|
||||
void Job_SetPrefix(void);
|
||||
Boolean Job_RunTarget(const char *, const char *);
|
||||
void Job_FlagsToString(const Job *, char *, size_t);
|
||||
|
||||
#endif /* MAKE_JOB_H */
|
||||
|
300
lst.c
300
lst.c
@ -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
|
||||
@ -34,278 +34,266 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $");
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $");
|
||||
|
||||
static ListNode *
|
||||
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
|
||||
{
|
||||
ListNode *ln = bmake_malloc(sizeof *ln);
|
||||
ln->prev = prev;
|
||||
ln->next = next;
|
||||
ln->datum = datum;
|
||||
return ln;
|
||||
ListNode *ln = bmake_malloc(sizeof *ln);
|
||||
|
||||
ln->prev = prev;
|
||||
ln->next = next;
|
||||
ln->datum = datum;
|
||||
|
||||
return ln;
|
||||
}
|
||||
|
||||
/* Create and initialize a new, empty list. */
|
||||
List *
|
||||
Lst_New(void)
|
||||
{
|
||||
List *list = bmake_malloc(sizeof *list);
|
||||
List *list = bmake_malloc(sizeof *list);
|
||||
Lst_Init(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
list->first = NULL;
|
||||
list->last = NULL;
|
||||
void
|
||||
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. */
|
||||
void
|
||||
Lst_Free(List *list)
|
||||
{
|
||||
ListNode *ln, *next;
|
||||
|
||||
for (ln = list->first; ln != NULL; ln = next) {
|
||||
next = ln->next;
|
||||
free(ln);
|
||||
}
|
||||
|
||||
free(list);
|
||||
Lst_Done(list);
|
||||
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
|
||||
Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
{
|
||||
ListNode *ln, *next;
|
||||
|
||||
for (ln = list->first; ln != NULL; ln = next) {
|
||||
next = ln->next;
|
||||
freeProc(ln->datum);
|
||||
free(ln);
|
||||
}
|
||||
|
||||
free(list);
|
||||
Lst_DoneCall(list, freeProc);
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* Insert a new node with the datum before the given node. */
|
||||
void
|
||||
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)
|
||||
ln->prev->next = newNode;
|
||||
ln->prev = newNode;
|
||||
if (ln->prev != NULL)
|
||||
ln->prev->next = newNode;
|
||||
ln->prev = newNode;
|
||||
|
||||
if (ln == list->first)
|
||||
list->first = newNode;
|
||||
if (ln == list->first)
|
||||
list->first = newNode;
|
||||
}
|
||||
|
||||
/* Add a piece of data at the start of the given list. */
|
||||
void
|
||||
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) {
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->first->prev = ln;
|
||||
list->first = ln;
|
||||
}
|
||||
if (list->first == NULL) {
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->first->prev = ln;
|
||||
list->first = ln;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a piece of data at the end of the given list. */
|
||||
void
|
||||
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) {
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->last->next = ln;
|
||||
list->last = ln;
|
||||
}
|
||||
if (list->last == NULL) {
|
||||
list->first = ln;
|
||||
list->last = ln;
|
||||
} else {
|
||||
list->last->next = 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
|
||||
Lst_Remove(List *list, ListNode *ln)
|
||||
{
|
||||
/* unlink it from its neighbors */
|
||||
if (ln->next != NULL)
|
||||
ln->next->prev = ln->prev;
|
||||
if (ln->prev != NULL)
|
||||
ln->prev->next = ln->next;
|
||||
/* unlink it from its neighbors */
|
||||
if (ln->next != NULL)
|
||||
ln->next->prev = ln->prev;
|
||||
if (ln->prev != NULL)
|
||||
ln->prev->next = ln->next;
|
||||
|
||||
/* unlink it from the list */
|
||||
if (list->first == ln)
|
||||
list->first = ln->next;
|
||||
if (list->last == ln)
|
||||
list->last = ln->prev;
|
||||
/* unlink it from the list */
|
||||
if (list->first == ln)
|
||||
list->first = ln->next;
|
||||
if (list->last == ln)
|
||||
list->last = ln->prev;
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node with the new datum. */
|
||||
void
|
||||
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
|
||||
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 *
|
||||
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)
|
||||
if (ln->datum == datum)
|
||||
return ln;
|
||||
for (ln = list->first; ln != NULL; ln = ln->next)
|
||||
if (ln->datum == datum)
|
||||
return ln;
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
|
||||
{
|
||||
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. */
|
||||
/*
|
||||
* Move all nodes from src to the end of dst.
|
||||
* The source list becomes empty but is not freed.
|
||||
*/
|
||||
void
|
||||
Lst_MoveAll(List *dst, List *src)
|
||||
{
|
||||
if (src->first != NULL) {
|
||||
src->first->prev = dst->last;
|
||||
if (dst->last != NULL)
|
||||
dst->last->next = src->first;
|
||||
else
|
||||
dst->first = src->first;
|
||||
if (src->first != NULL) {
|
||||
src->first->prev = dst->last;
|
||||
if (dst->last != NULL)
|
||||
dst->last->next = src->first;
|
||||
else
|
||||
dst->first = src->first;
|
||||
|
||||
dst->last = src->last;
|
||||
}
|
||||
free(src);
|
||||
dst->last = src->last;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the element data from src to the start of dst. */
|
||||
void
|
||||
Lst_PrependAll(List *dst, List *src)
|
||||
{
|
||||
ListNode *node;
|
||||
for (node = src->last; node != NULL; node = node->prev)
|
||||
Lst_Prepend(dst, node->datum);
|
||||
ListNode *ln;
|
||||
|
||||
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. */
|
||||
void
|
||||
Lst_AppendAll(List *dst, List *src)
|
||||
{
|
||||
ListNode *node;
|
||||
for (node = src->first; node != NULL; node = node->next)
|
||||
Lst_Append(dst, node->datum);
|
||||
}
|
||||
ListNode *ln;
|
||||
|
||||
/*
|
||||
* for using the list as a queue
|
||||
*/
|
||||
|
||||
/* Add the datum to the tail of the given list. */
|
||||
void
|
||||
Lst_Enqueue(List *list, void *datum)
|
||||
{
|
||||
Lst_Append(list, datum);
|
||||
for (ln = src->first; ln != NULL; ln = ln->next)
|
||||
Lst_Append(dst, ln->datum);
|
||||
}
|
||||
|
||||
/* Remove and return the datum at the head of the given list. */
|
||||
void *
|
||||
Lst_Dequeue(List *list)
|
||||
{
|
||||
void *datum = list->first->datum;
|
||||
Lst_Remove(list, list->first);
|
||||
assert(datum != NULL); /* since NULL would mean end of the list */
|
||||
return datum;
|
||||
void *datum = list->first->datum;
|
||||
Lst_Remove(list, list->first);
|
||||
assert(datum != NULL); /* since NULL would mean end of the list */
|
||||
return datum;
|
||||
}
|
||||
|
||||
void
|
||||
Vector_Init(Vector *v, size_t itemSize)
|
||||
{
|
||||
v->len = 0;
|
||||
v->priv_cap = 10;
|
||||
v->itemSize = itemSize;
|
||||
v->items = bmake_malloc(v->priv_cap * v->itemSize);
|
||||
v->len = 0;
|
||||
v->cap = 10;
|
||||
v->itemSize = 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 *
|
||||
Vector_Push(Vector *v)
|
||||
{
|
||||
if (v->len >= v->priv_cap) {
|
||||
v->priv_cap *= 2;
|
||||
v->items = bmake_realloc(v->items, v->priv_cap * v->itemSize);
|
||||
}
|
||||
v->len++;
|
||||
return Vector_Get(v, v->len - 1);
|
||||
if (v->len >= v->cap) {
|
||||
v->cap *= 2;
|
||||
v->items = bmake_realloc(v->items, v->cap * v->itemSize);
|
||||
}
|
||||
v->len++;
|
||||
return Vector_Get(v, v->len - 1);
|
||||
}
|
||||
|
||||
/* Return the pointer to the last item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
/*
|
||||
* Return the pointer to the last item in the vector.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
void *
|
||||
Vector_Pop(Vector *v)
|
||||
{
|
||||
assert(v->len > 0);
|
||||
v->len--;
|
||||
return Vector_Get(v, v->len);
|
||||
}
|
||||
|
||||
void
|
||||
Vector_Done(Vector *v)
|
||||
{
|
||||
free(v->items);
|
||||
assert(v->len > 0);
|
||||
v->len--;
|
||||
return Vector_Get(v, v->len);
|
||||
}
|
||||
|
88
lst.h
88
lst.h
@ -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.
|
||||
@ -78,48 +78,62 @@
|
||||
#ifndef 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>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/* A doubly-linked list of pointers. */
|
||||
typedef struct List List;
|
||||
typedef struct List List;
|
||||
/* A single node in the doubly-linked list. */
|
||||
typedef struct ListNode ListNode;
|
||||
typedef struct ListNode ListNode;
|
||||
|
||||
struct ListNode {
|
||||
ListNode *prev; /* previous node in list, or NULL */
|
||||
ListNode *next; /* next node in list, or NULL */
|
||||
union {
|
||||
ListNode *prev; /* previous node in list, or NULL */
|
||||
ListNode *next; /* next node in list, or NULL */
|
||||
void *datum; /* datum associated with this element */
|
||||
const struct GNode *priv_gnode; /* alias, just for debugging */
|
||||
const char *priv_str; /* alias, just for debugging */
|
||||
};
|
||||
};
|
||||
|
||||
struct List {
|
||||
ListNode *first; /* first node in list */
|
||||
ListNode *last; /* last node in list */
|
||||
ListNode *first;
|
||||
ListNode *last;
|
||||
};
|
||||
|
||||
/* Free the datum of a node, called before freeing the node itself. */
|
||||
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 a new list. */
|
||||
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. */
|
||||
void Lst_Free(List *);
|
||||
/* Free the list, freeing the node data using the given function. */
|
||||
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 */
|
||||
|
||||
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. */
|
||||
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. */
|
||||
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 */
|
||||
|
||||
/* 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. */
|
||||
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 {
|
||||
void *items; /* memory holding the items */
|
||||
size_t itemSize; /* size of a single item in bytes */
|
||||
size_t len; /* number of actually usable elements */
|
||||
size_t priv_cap; /* capacity */
|
||||
void *items; /* memory holding the items */
|
||||
size_t itemSize; /* size of a single item */
|
||||
size_t len; /* number of actually usable elements */
|
||||
size_t cap; /* capacity */
|
||||
} Vector;
|
||||
|
||||
void Vector_Init(Vector *, size_t);
|
||||
|
||||
/* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
/*
|
||||
* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
MAKE_INLINE void *
|
||||
Vector_Get(Vector *v, size_t i)
|
||||
{
|
||||
unsigned char *items = v->items;
|
||||
return items + i * v->itemSize;
|
||||
unsigned char *items = v->items;
|
||||
return items + i * v->itemSize;
|
||||
}
|
||||
|
||||
void *Vector_Push(Vector *);
|
||||
void *Vector_Pop(Vector *);
|
||||
void Vector_Done(Vector *);
|
||||
|
||||
MAKE_INLINE void
|
||||
Vector_Done(Vector *v) {
|
||||
free(v->items);
|
||||
}
|
||||
|
||||
#endif /* MAKE_LST_H */
|
||||
|
364
main.c
364
main.c
@ -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
|
||||
@ -68,7 +68,8 @@
|
||||
* 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:
|
||||
*
|
||||
@ -109,17 +110,13 @@
|
||||
#include "trace.h"
|
||||
|
||||
/* "@(#)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)
|
||||
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
|
||||
"The Regents of the University of California. "
|
||||
"All rights reserved.");
|
||||
#endif
|
||||
|
||||
#ifndef DEFMAXLOCAL
|
||||
#define DEFMAXLOCAL DEFMAXJOBS
|
||||
#endif
|
||||
|
||||
#ifndef __arraycount
|
||||
# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
|
||||
#endif
|
||||
@ -133,7 +130,6 @@ Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
|
||||
static int maxJobTokens; /* -j argument */
|
||||
Boolean enterFlagObj; /* -w and objdir != srcdir */
|
||||
|
||||
Boolean preserveUndefined;
|
||||
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
|
||||
Boolean doing_depend; /* Set while reading .depend */
|
||||
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 char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
|
||||
char curdir[MAXPATHLEN + 1]; /* Startup directory */
|
||||
char *progname; /* the program name */
|
||||
const char *progname;
|
||||
char *makeDependfile;
|
||||
pid_t myPid;
|
||||
int makelevel;
|
||||
|
||||
Boolean forceJobs = FALSE;
|
||||
static int errors = 0;
|
||||
static int main_errors = 0;
|
||||
static HashTable cached_realpaths;
|
||||
|
||||
/*
|
||||
@ -167,16 +163,16 @@ explode(const char *flags)
|
||||
if (flags == NULL)
|
||||
return NULL;
|
||||
|
||||
for (f = flags; *f; f++)
|
||||
for (f = flags; *f != '\0'; f++)
|
||||
if (!ch_isalpha(*f))
|
||||
break;
|
||||
|
||||
if (*f)
|
||||
if (*f != '\0')
|
||||
return bmake_strdup(flags);
|
||||
|
||||
len = strlen(flags);
|
||||
st = nf = bmake_malloc(len * 3 + 1);
|
||||
while (*flags) {
|
||||
while (*flags != '\0') {
|
||||
*nf++ = '-';
|
||||
*nf++ = *flags++;
|
||||
*nf++ = ' ';
|
||||
@ -251,7 +247,7 @@ parse_debug_options(const char *argvalue)
|
||||
const char *modules;
|
||||
DebugFlags debug = opts.debug;
|
||||
|
||||
for (modules = argvalue; *modules; ++modules) {
|
||||
for (modules = argvalue; *modules != '\0'; ++modules) {
|
||||
switch (*modules) {
|
||||
case '0': /* undocumented, only intended for tests */
|
||||
debug = DEBUG_NONE;
|
||||
@ -296,7 +292,7 @@ parse_debug_options(const char *argvalue)
|
||||
debug |= DEBUG_JOB;
|
||||
break;
|
||||
case 'L':
|
||||
opts.lint = TRUE;
|
||||
opts.strict = TRUE;
|
||||
break;
|
||||
case 'l':
|
||||
debug |= DEBUG_LOUD;
|
||||
@ -381,7 +377,7 @@ MainParseArgChdir(const char *argvalue)
|
||||
if (chdir(argvalue) == -1) {
|
||||
(void)fprintf(stderr, "%s: chdir %s: %s\n",
|
||||
progname, argvalue, strerror(errno));
|
||||
exit(1);
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
if (getcwd(curdir, MAXPATHLEN) == NULL) {
|
||||
(void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
|
||||
@ -434,7 +430,7 @@ MainParseArgJobs(const char *argvalue)
|
||||
(void)fprintf(stderr,
|
||||
"%s: illegal argument to -j -- must be positive integer!\n",
|
||||
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, argvalue, VAR_GLOBAL);
|
||||
@ -504,7 +500,7 @@ MainParseArg(char c, const char *argvalue)
|
||||
case 'V':
|
||||
case 'v':
|
||||
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? */
|
||||
Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
|
||||
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
|
||||
@ -532,7 +528,7 @@ MainParseArg(char c, const char *argvalue)
|
||||
Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
|
||||
break;
|
||||
case 'f':
|
||||
Lst_Append(opts.makefiles, bmake_strdup(argvalue));
|
||||
Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
|
||||
break;
|
||||
case 'i':
|
||||
opts.ignoreErrors = TRUE;
|
||||
@ -581,13 +577,15 @@ MainParseArg(char c, const char *argvalue)
|
||||
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.
|
||||
*
|
||||
* The arguments must be treated as read-only and will be freed after the
|
||||
* call.
|
||||
*
|
||||
* XXX: Deal with command line overriding .MAKEFLAGS in makefile */
|
||||
* XXX: Deal with command line overriding .MAKEFLAGS in makefile
|
||||
*/
|
||||
static void
|
||||
MainParseArgs(int argc, char **argv)
|
||||
{
|
||||
@ -668,7 +666,7 @@ MainParseArgs(int argc, char **argv)
|
||||
Punt("illegal (null) argument.");
|
||||
if (argv[1][0] == '-' && !dashDash)
|
||||
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();
|
||||
}
|
||||
|
||||
/* 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
|
||||
* by main() when reading the MAKEFLAGS environment variable. */
|
||||
* by main() when reading the MAKEFLAGS environment variable.
|
||||
*/
|
||||
void
|
||||
Main_ParseArgLine(const char *line)
|
||||
{
|
||||
@ -691,6 +691,7 @@ Main_ParseArgLine(const char *line)
|
||||
|
||||
if (line == NULL)
|
||||
return;
|
||||
/* XXX: don't use line as an iterator variable */
|
||||
for (; *line == ' '; ++line)
|
||||
continue;
|
||||
if (line[0] == '\0')
|
||||
@ -711,10 +712,9 @@ Main_ParseArgLine(const char *line)
|
||||
}
|
||||
#endif
|
||||
{
|
||||
void *freeIt;
|
||||
const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &freeIt);
|
||||
buf = str_concat3(argv0, " ", line);
|
||||
free(freeIt);
|
||||
FStr argv0 = Var_Value(".MAKE", VAR_GLOBAL);
|
||||
buf = str_concat3(argv0.str, " ", line);
|
||||
FStr_Done(&argv0);
|
||||
}
|
||||
|
||||
words = Str_Words(buf, TRUE);
|
||||
@ -772,34 +772,34 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
|
||||
static Boolean
|
||||
SetVarObjdir(Boolean writable, const char *var, const char *suffix)
|
||||
{
|
||||
void *path_freeIt;
|
||||
const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt);
|
||||
const char *xpath;
|
||||
char *xpath_freeIt;
|
||||
FStr path = Var_Value(var, VAR_CMDLINE);
|
||||
FStr xpath;
|
||||
|
||||
if (path == NULL || path[0] == '\0') {
|
||||
bmake_free(path_freeIt);
|
||||
if (path.str == NULL || path.str[0] == '\0') {
|
||||
FStr_Done(&path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* expand variable substitutions */
|
||||
xpath = path;
|
||||
xpath_freeIt = NULL;
|
||||
if (strchr(path, '$') != 0) {
|
||||
(void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt);
|
||||
xpath = FStr_InitRefer(path.str);
|
||||
if (strchr(path.str, '$') != 0) {
|
||||
char *expanded;
|
||||
(void)Var_Subst(path.str, VAR_GLOBAL, VARE_WANTRES, &expanded);
|
||||
/* 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);
|
||||
bmake_free(path_freeIt);
|
||||
FStr_Done(&xpath);
|
||||
FStr_Done(&path);
|
||||
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
|
||||
str2Lst_Append(StringList *lp, char *str)
|
||||
{
|
||||
@ -808,7 +808,7 @@ str2Lst_Append(StringList *lp, char *str)
|
||||
|
||||
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);
|
||||
n++;
|
||||
}
|
||||
@ -831,39 +831,38 @@ siginfo(int signo MAKE_ATTR_UNUSED)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allow makefiles some control over the mode we run in.
|
||||
*/
|
||||
void
|
||||
MakeMode(const char *mode)
|
||||
/* Allow makefiles some control over the mode we run in. */
|
||||
static void
|
||||
MakeMode(void)
|
||||
{
|
||||
char *mode_freeIt = NULL;
|
||||
FStr mode = FStr_InitRefer(NULL);
|
||||
|
||||
if (mode == NULL) {
|
||||
if (mode.str == NULL) {
|
||||
char *expanded;
|
||||
(void)Var_Subst("${" MAKE_MODE ":tl}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &mode_freeIt);
|
||||
VAR_GLOBAL, VARE_WANTRES, &expanded);
|
||||
/* TODO: handle errors */
|
||||
mode = mode_freeIt;
|
||||
mode = FStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
if (mode[0] != '\0') {
|
||||
if (strstr(mode, "compat")) {
|
||||
if (mode.str[0] != '\0') {
|
||||
if (strstr(mode.str, "compat") != NULL) {
|
||||
opts.compatMake = TRUE;
|
||||
forceJobs = FALSE;
|
||||
}
|
||||
#if USE_META
|
||||
if (strstr(mode, "meta"))
|
||||
meta_mode_init(mode);
|
||||
if (strstr(mode.str, "meta") != NULL)
|
||||
meta_mode_init(mode.str);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(mode_freeIt);
|
||||
FStr_Done(&mode);
|
||||
}
|
||||
|
||||
static void
|
||||
PrintVar(const char *varname, Boolean expandVars)
|
||||
{
|
||||
if (strchr(varname, '$')) {
|
||||
if (strchr(varname, '$') != NULL) {
|
||||
char *evalue;
|
||||
(void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue);
|
||||
/* TODO: handle errors */
|
||||
@ -880,10 +879,9 @@ PrintVar(const char *varname, Boolean expandVars)
|
||||
bmake_free(evalue);
|
||||
|
||||
} else {
|
||||
void *freeIt;
|
||||
const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt);
|
||||
printf("%s\n", value ? value : "");
|
||||
bmake_free(freeIt);
|
||||
FStr value = Var_Value(varname, VAR_GLOBAL);
|
||||
printf("%s\n", value.str != NULL ? value.str : "");
|
||||
FStr_Done(&value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,7 +920,7 @@ doPrintVars(void)
|
||||
else
|
||||
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;
|
||||
PrintVar(varname, expandVars);
|
||||
}
|
||||
@ -931,7 +929,7 @@ doPrintVars(void)
|
||||
static Boolean
|
||||
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 */
|
||||
|
||||
/*
|
||||
@ -940,10 +938,10 @@ runTargets(void)
|
||||
* we consult the parsing module to find the main target(s)
|
||||
* to create.
|
||||
*/
|
||||
if (Lst_IsEmpty(opts.create))
|
||||
targs = Parse_MainName();
|
||||
if (Lst_IsEmpty(&opts.create))
|
||||
Parse_MainName(&targs);
|
||||
else
|
||||
targs = Targ_FindList(opts.create);
|
||||
Targ_FindList(&targs, &opts.create);
|
||||
|
||||
if (!opts.compatMake) {
|
||||
/*
|
||||
@ -959,16 +957,16 @@ runTargets(void)
|
||||
}
|
||||
|
||||
/* Traverse the graph, checking on all the targets */
|
||||
outOfDate = Make_Run(targs);
|
||||
outOfDate = Make_Run(&targs);
|
||||
} else {
|
||||
/*
|
||||
* Compat_Init will take care of creating all the
|
||||
* targets as well as initializing the module.
|
||||
*/
|
||||
Compat_Run(targs);
|
||||
Compat_Run(&targs);
|
||||
outOfDate = FALSE;
|
||||
}
|
||||
Lst_Free(targs);
|
||||
Lst_Done(&targs); /* Don't free the nodes. */
|
||||
return outOfDate;
|
||||
}
|
||||
|
||||
@ -982,12 +980,12 @@ InitVarTargets(void)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
if (Lst_IsEmpty(opts.create)) {
|
||||
if (Lst_IsEmpty(&opts.create)) {
|
||||
Var_Set(".TARGETS", "", VAR_GLOBAL);
|
||||
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;
|
||||
Var_Append(".TARGETS", name, VAR_GLOBAL);
|
||||
}
|
||||
@ -1040,11 +1038,11 @@ InitVarMachineArch(void)
|
||||
const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
|
||||
size_t len = sizeof machine_arch_buf;
|
||||
|
||||
if (sysctl(mib, __arraycount(mib), machine_arch_buf,
|
||||
&len, NULL, 0) < 0) {
|
||||
(void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
|
||||
strerror(errno));
|
||||
exit(2);
|
||||
if (sysctl(mib, (unsigned int)__arraycount(mib),
|
||||
machine_arch_buf, &len, NULL, 0) < 0) {
|
||||
(void)fprintf(stderr, "%s: sysctl failed (%s).\n",
|
||||
progname, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
return machine_arch_buf;
|
||||
@ -1077,21 +1075,20 @@ static void
|
||||
HandlePWD(const struct stat *curdir_st)
|
||||
{
|
||||
char *pwd;
|
||||
void *prefix_freeIt, *makeobjdir_freeIt;
|
||||
const char *makeobjdir;
|
||||
FStr prefix, makeobjdir;
|
||||
struct stat pwd_st;
|
||||
|
||||
if (ignorePWD || (pwd = getenv("PWD")) == NULL)
|
||||
return;
|
||||
|
||||
if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) !=
|
||||
NULL) {
|
||||
bmake_free(prefix_freeIt);
|
||||
prefix = Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE);
|
||||
if (prefix.str != NULL) {
|
||||
FStr_Done(&prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt);
|
||||
if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL)
|
||||
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE);
|
||||
if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL)
|
||||
goto ignore_pwd;
|
||||
|
||||
if (stat(pwd, &pwd_st) == 0 &&
|
||||
@ -1100,7 +1097,7 @@ HandlePWD(const struct stat *curdir_st)
|
||||
(void)strncpy(curdir, pwd, MAXPATHLEN);
|
||||
|
||||
ignore_pwd:
|
||||
bmake_free(makeobjdir_freeIt);
|
||||
FStr_Done(&makeobjdir);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1118,7 +1115,7 @@ InitObjdir(const char *machine, const char *machine_arch)
|
||||
{
|
||||
Boolean writable;
|
||||
|
||||
Dir_InitDir(curdir);
|
||||
Dir_InitCur(curdir);
|
||||
writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE);
|
||||
(void)Main_SetObjdir(FALSE, "%s", curdir);
|
||||
|
||||
@ -1147,35 +1144,37 @@ UnlimitFiles(void)
|
||||
static void
|
||||
CmdOpts_Init(void)
|
||||
{
|
||||
opts.compatMake = FALSE; /* No compat mode */
|
||||
opts.debug = 0; /* No debug verbosity, please. */
|
||||
opts.compatMake = FALSE;
|
||||
opts.debug = DEBUG_NONE;
|
||||
/* opts.debug_file has been initialized earlier */
|
||||
opts.lint = FALSE;
|
||||
opts.strict = FALSE;
|
||||
opts.debugVflag = FALSE;
|
||||
opts.checkEnvFirst = FALSE;
|
||||
opts.makefiles = Lst_New();
|
||||
Lst_Init(&opts.makefiles);
|
||||
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.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
|
||||
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.beSilent = FALSE; /* Print commands as executed */
|
||||
opts.touchFlag = FALSE; /* Actually update targets */
|
||||
opts.touchFlag = FALSE;
|
||||
opts.printVars = PVM_NONE;
|
||||
opts.variables = Lst_New();
|
||||
Lst_Init(&opts.variables);
|
||||
opts.parseWarnFatal = FALSE;
|
||||
opts.enterFlag = 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.
|
||||
*
|
||||
* 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
|
||||
InitVarMake(const char *argv0)
|
||||
{
|
||||
@ -1183,18 +1182,21 @@ InitVarMake(const char *argv0)
|
||||
|
||||
if (argv0[0] != '/' && strchr(argv0, '/') != NULL) {
|
||||
char pathbuf[MAXPATHLEN];
|
||||
const char *abs = cached_realpath(argv0, pathbuf);
|
||||
const char *abspath = cached_realpath(argv0, pathbuf);
|
||||
struct stat st;
|
||||
if (abs != NULL && abs[0] == '/' && stat(make, &st) == 0)
|
||||
make = abs;
|
||||
if (abspath != NULL && abspath[0] == '/' &&
|
||||
stat(make, &st) == 0)
|
||||
make = abspath;
|
||||
}
|
||||
|
||||
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
|
||||
InitDefSysIncPath(char *syspath)
|
||||
{
|
||||
@ -1237,25 +1239,25 @@ static void
|
||||
ReadBuiltinRules(void)
|
||||
{
|
||||
StringListNode *ln;
|
||||
StringList *sysMkPath = Lst_New();
|
||||
StringList sysMkPath = LST_INIT;
|
||||
|
||||
Dir_Expand(_PATH_DEFSYSMK,
|
||||
Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath,
|
||||
sysMkPath);
|
||||
if (Lst_IsEmpty(sysMkPath))
|
||||
&sysMkPath);
|
||||
if (Lst_IsEmpty(&sysMkPath))
|
||||
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)
|
||||
break;
|
||||
|
||||
if (ln == NULL)
|
||||
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
|
||||
* be used in GNodes. */
|
||||
Lst_Free(sysMkPath);
|
||||
Lst_Done(&sysMkPath);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1276,7 +1278,7 @@ InitMaxJobs(void)
|
||||
"%s: illegal value for .MAKE.JOBS "
|
||||
"-- must be positive integer!\n",
|
||||
progname);
|
||||
exit(1);
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
|
||||
if (n != opts.maxJobs) {
|
||||
@ -1315,7 +1317,7 @@ InitVpath(void)
|
||||
savec = *cp;
|
||||
*cp = '\0';
|
||||
/* Add directory to search path */
|
||||
(void)Dir_AddDir(dirSearchPath, path);
|
||||
(void)Dir_AddDir(&dirSearchPath, path);
|
||||
*cp = savec;
|
||||
path = cp + 1;
|
||||
} while (savec == ':');
|
||||
@ -1348,18 +1350,20 @@ ReadFirstDefaultMakefile(void)
|
||||
* since these makefiles do not come from the command line. They
|
||||
* also have different semantics in that only the first file that
|
||||
* 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)
|
||||
break;
|
||||
|
||||
free(prefs);
|
||||
}
|
||||
|
||||
/* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS.
|
||||
/*
|
||||
* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS.
|
||||
* Initialize a few modules.
|
||||
* Parse the arguments from MAKEFLAGS and the command line. */
|
||||
* Parse the arguments from MAKEFLAGS and the command line.
|
||||
*/
|
||||
static void
|
||||
main_Init(int argc, char **argv)
|
||||
{
|
||||
@ -1380,10 +1384,7 @@ main_Init(int argc, char **argv)
|
||||
|
||||
InitRandom();
|
||||
|
||||
if ((progname = strrchr(argv[0], '/')) != NULL)
|
||||
progname++;
|
||||
else
|
||||
progname = argv[0];
|
||||
progname = str_basename(argv[0]);
|
||||
|
||||
UnlimitFiles();
|
||||
|
||||
@ -1469,6 +1470,10 @@ main_Init(int argc, char **argv)
|
||||
Var_Set(".MAKE.PID", tmp, VAR_GLOBAL);
|
||||
snprintf(tmp, sizeof tmp, "%u", getppid());
|
||||
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) {
|
||||
char pn[1024];
|
||||
@ -1545,8 +1550,10 @@ main_Init(int argc, char **argv)
|
||||
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
|
||||
main_ReadFiles(void)
|
||||
{
|
||||
@ -1554,8 +1561,8 @@ main_ReadFiles(void)
|
||||
if (!opts.noBuiltins)
|
||||
ReadBuiltinRules();
|
||||
|
||||
if (!Lst_IsEmpty(opts.makefiles))
|
||||
ReadAllMakefiles(opts.makefiles);
|
||||
if (!Lst_IsEmpty(&opts.makefiles))
|
||||
ReadAllMakefiles(&opts.makefiles);
|
||||
else
|
||||
ReadFirstDefaultMakefile();
|
||||
}
|
||||
@ -1566,8 +1573,7 @@ main_PrepareMaking(void)
|
||||
{
|
||||
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
|
||||
if (!opts.noBuiltins || opts.printVars == PVM_NONE) {
|
||||
/* ignore /dev/null and anything starting with "no" */
|
||||
(void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}",
|
||||
(void)Var_Subst("${.MAKE.DEPENDFILE}",
|
||||
VAR_CMDLINE, VARE_WANTRES, &makeDependfile);
|
||||
if (makeDependfile[0] != '\0') {
|
||||
/* TODO: handle errors */
|
||||
@ -1580,13 +1586,12 @@ main_PrepareMaking(void)
|
||||
if (enterFlagObj)
|
||||
printf("%s: Entering directory `%s'\n", progname, objdir);
|
||||
|
||||
MakeMode(NULL);
|
||||
MakeMode();
|
||||
|
||||
{
|
||||
void *freeIt;
|
||||
Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt),
|
||||
VAR_GLOBAL);
|
||||
bmake_free(freeIt);
|
||||
FStr makeflags = Var_Value(MAKEFLAGS, VAR_GLOBAL);
|
||||
Var_Append("MFLAGS", makeflags.str, VAR_GLOBAL);
|
||||
FStr_Done(&makeflags);
|
||||
}
|
||||
|
||||
InitMaxJobs();
|
||||
@ -1624,9 +1629,11 @@ main_PrepareMaking(void)
|
||||
Targ_PrintGraph(1);
|
||||
}
|
||||
|
||||
/* Make the targets.
|
||||
/*
|
||||
* Make the targets.
|
||||
* 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
|
||||
main_Run(void)
|
||||
{
|
||||
@ -1644,9 +1651,13 @@ static void
|
||||
main_CleanUp(void)
|
||||
{
|
||||
#ifdef CLEANUP
|
||||
Lst_Destroy(opts.variables, free);
|
||||
Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */
|
||||
Lst_Destroy(opts.create, free);
|
||||
Lst_DoneCall(&opts.variables, 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
|
||||
|
||||
/* print the graph now it's been processed if the user requested it */
|
||||
@ -1677,7 +1688,7 @@ main_CleanUp(void)
|
||||
static int
|
||||
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 outOfDate ? 1 : 0;
|
||||
}
|
||||
@ -1695,7 +1706,8 @@ main(int argc, char **argv)
|
||||
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:
|
||||
* 0 if ok. -1 if couldn't open file.
|
||||
@ -1776,7 +1788,7 @@ char *
|
||||
Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
{
|
||||
const char *args[4]; /* Args for invoking the shell */
|
||||
int fds[2]; /* Pipe streams */
|
||||
int pipefds[2];
|
||||
int cpid; /* Child PID */
|
||||
int pid; /* PID from wait() */
|
||||
int status; /* command exit status */
|
||||
@ -1789,7 +1801,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
|
||||
*errfmt = NULL;
|
||||
|
||||
if (!shellName)
|
||||
if (shellName == NULL)
|
||||
Shell_Init();
|
||||
/*
|
||||
* Set up arguments for shell
|
||||
@ -1802,27 +1814,27 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
/*
|
||||
* Open a pipe for fetching its output
|
||||
*/
|
||||
if (pipe(fds) == -1) {
|
||||
if (pipe(pipefds) == -1) {
|
||||
*errfmt = "Couldn't create pipe for \"%s\"";
|
||||
goto bad;
|
||||
}
|
||||
|
||||
Var_ReexportVars();
|
||||
|
||||
/*
|
||||
* Fork
|
||||
*/
|
||||
switch (cpid = vFork()) {
|
||||
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
|
||||
* shut the extra thing down. Note we don't fetch the error
|
||||
* stream...why not? Why?
|
||||
*/
|
||||
(void)dup2(fds[1], 1);
|
||||
(void)close(fds[1]);
|
||||
|
||||
Var_ExportVars();
|
||||
(void)dup2(pipefds[1], 1);
|
||||
(void)close(pipefds[1]);
|
||||
|
||||
(void)execv(shellPath, UNCONST(args));
|
||||
_exit(1);
|
||||
@ -1833,14 +1845,14 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
goto bad;
|
||||
|
||||
default:
|
||||
(void)close(fds[1]); /* No need for the writing half */
|
||||
(void)close(pipefds[1]); /* No need for the writing half */
|
||||
|
||||
savederr = 0;
|
||||
Buf_Init(&buf);
|
||||
|
||||
do {
|
||||
char result[BUFSIZ];
|
||||
bytes_read = read(fds[0], result, sizeof result);
|
||||
bytes_read = read(pipefds[0], result, sizeof result);
|
||||
if (bytes_read > 0)
|
||||
Buf_AddBytes(&buf, result, (size_t)bytes_read);
|
||||
} while (bytes_read > 0 ||
|
||||
@ -1848,8 +1860,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
if (bytes_read == -1)
|
||||
savederr = errno;
|
||||
|
||||
(void)close(
|
||||
fds[0]); /* Close the input side of the pipe. */
|
||||
(void)close(pipefds[0]); /* Close the input side of the pipe. */
|
||||
|
||||
/* Wait for the process to exit. */
|
||||
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
|
||||
@ -1879,10 +1890,12 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
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
|
||||
* 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
|
||||
Error(const char *fmt, ...)
|
||||
{
|
||||
@ -1904,14 +1917,16 @@ Error(const char *fmt, ...)
|
||||
break;
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
Fatal(const char *fmt, ...)
|
||||
{
|
||||
@ -1935,8 +1950,10 @@ Fatal(const char *fmt, ...)
|
||||
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
|
||||
Punt(const char *fmt, ...)
|
||||
{
|
||||
@ -1964,12 +1981,14 @@ DieHorribly(void)
|
||||
if (DEBUG(GRAPH2))
|
||||
Targ_PrintGraph(2);
|
||||
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.
|
||||
* Errors is the number of errors encountered in Make_Make. */
|
||||
* Errors is the number of errors encountered in Make_Make.
|
||||
*/
|
||||
void
|
||||
Finish(int errs)
|
||||
{
|
||||
@ -2101,7 +2120,7 @@ shouldDieQuietly(GNode *gn, int bf)
|
||||
else if (bf >= 0)
|
||||
quietly = bf;
|
||||
else
|
||||
quietly = gn != NULL && (gn->type & OP_MAKE);
|
||||
quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0;
|
||||
}
|
||||
return quietly;
|
||||
}
|
||||
@ -2117,7 +2136,7 @@ SetErrorVars(GNode *gn)
|
||||
Var_Set(".ERROR_TARGET", gn->name, 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;
|
||||
|
||||
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
|
||||
PrintOnError(GNode *gn, const char *msg)
|
||||
{
|
||||
@ -2138,16 +2159,16 @@ PrintOnError(GNode *gn, const char *msg)
|
||||
Var_Stats();
|
||||
}
|
||||
|
||||
/* we generally want to keep quiet if a sub-make died */
|
||||
if (shouldDieQuietly(gn, -1))
|
||||
return;
|
||||
if (errorNode != NULL)
|
||||
return; /* we've been here! */
|
||||
|
||||
if (msg != NULL)
|
||||
printf("%s", msg);
|
||||
printf("\n%s: stopped in %s\n", progname, curdir);
|
||||
|
||||
if (errorNode != NULL)
|
||||
return; /* we've been here! */
|
||||
/* we generally want to keep quiet if a sub-make died */
|
||||
if (shouldDieQuietly(gn, -1))
|
||||
return;
|
||||
|
||||
if (gn != NULL)
|
||||
SetErrorVars(gn);
|
||||
@ -2241,11 +2262,10 @@ mkTempFile(const char *pattern, char **out_fname)
|
||||
if ((fd = mkstemp(tfile)) < 0)
|
||||
Punt("Could not create temporary file %s: %s", tfile,
|
||||
strerror(errno));
|
||||
if (out_fname) {
|
||||
if (out_fname != NULL) {
|
||||
*out_fname = bmake_strdup(tfile);
|
||||
} else {
|
||||
unlink(
|
||||
tfile); /* we just want the descriptor */
|
||||
unlink(tfile); /* we just want the descriptor */
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
20
make-conf.h
20
make-conf.h
@ -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.
|
||||
@ -72,19 +72,6 @@
|
||||
* from: @(#)config.h 8.1 (Berkeley) 6/6/93
|
||||
*/
|
||||
|
||||
/*
|
||||
* DEFMAXJOBS
|
||||
* DEFMAXLOCAL
|
||||
* These control the default concurrency. On no occasion will more
|
||||
* than DEFMAXJOBS targets be created at once (locally or remotely).
|
||||
*
|
||||
* DEFMAXLOCAL is the highest number of targets which will be
|
||||
* created on the local machine at once. Note that if you set this
|
||||
* to 0, nothing will ever happen.
|
||||
*/
|
||||
#define DEFMAXJOBS 4
|
||||
#define DEFMAXLOCAL 1
|
||||
|
||||
/*
|
||||
* INCLUDES
|
||||
* LIBRARIES
|
||||
@ -104,7 +91,7 @@
|
||||
* 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.
|
||||
*/
|
||||
#define LIBSUFF ".a"
|
||||
#define LIBSUFF ".a"
|
||||
|
||||
/*
|
||||
* RECHECK
|
||||
@ -119,14 +106,13 @@
|
||||
* On systems that don't have this problem, you should define this.
|
||||
* Under NFS you probably should not, unless you aren't exporting jobs.
|
||||
*/
|
||||
#define RECHECK
|
||||
#define RECHECK
|
||||
|
||||
/*
|
||||
* POSIX
|
||||
* Adhere to the POSIX 1003.2 draft for the make(1) program.
|
||||
* - Use MAKEFLAGS instead of MAKE to pick arguments from the
|
||||
* environment.
|
||||
* - Allow empty command lines if starting with tab.
|
||||
*/
|
||||
#define POSIX
|
||||
|
||||
|
18
make.1
18
make.1
@ -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
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd November 14, 2020
|
||||
.Dd December 22, 2020
|
||||
.Dt MAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1019,6 +1019,12 @@ If set to false,
|
||||
becomes
|
||||
.Ql $
|
||||
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
|
||||
When
|
||||
.Nm
|
||||
@ -1097,7 +1103,7 @@ to that directory before executing any targets.
|
||||
.Pp
|
||||
Except in the case of an explicit
|
||||
.Ql Ic .OBJDIR
|
||||
target,
|
||||
target,
|
||||
.Nm
|
||||
will check that the specified directory is writable and ignore it if not.
|
||||
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.
|
||||
.It Ic .info Ar message
|
||||
The message is printed along with the name of the makefile and line number.
|
||||
.It Ic .undef Ar variable
|
||||
Un-define the specified global variable.
|
||||
Only global variables may be un-defined.
|
||||
.It Ic .undef Ar variable ...
|
||||
Un-define the specified global variables.
|
||||
Only global variables can be un-defined.
|
||||
.It Ic .unexport Ar variable ...
|
||||
The opposite of
|
||||
.Ql .export .
|
||||
|
776
make.h
776
make.h
@ -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
|
||||
@ -107,25 +107,25 @@
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define MAKE_GNUC_PREREQ(x, y) \
|
||||
#define MAKE_GNUC_PREREQ(x, y) \
|
||||
((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \
|
||||
(__GNUC__ > (x)))
|
||||
#else /* defined(__GNUC__) */
|
||||
#define MAKE_GNUC_PREREQ(x, y) 0
|
||||
#define MAKE_GNUC_PREREQ(x, y) 0
|
||||
#endif /* defined(__GNUC__) */
|
||||
|
||||
#if MAKE_GNUC_PREREQ(2, 7)
|
||||
#define MAKE_ATTR_UNUSED __attribute__((__unused__))
|
||||
#define MAKE_ATTR_UNUSED __attribute__((__unused__))
|
||||
#else
|
||||
#define MAKE_ATTR_UNUSED /* delete */
|
||||
#define MAKE_ATTR_UNUSED /* delete */
|
||||
#endif
|
||||
|
||||
#if MAKE_GNUC_PREREQ(2, 5)
|
||||
#define MAKE_ATTR_DEAD __attribute__((__noreturn__))
|
||||
#define MAKE_ATTR_DEAD __attribute__((__noreturn__))
|
||||
#elif defined(__GNUC__)
|
||||
#define MAKE_ATTR_DEAD __volatile
|
||||
#define MAKE_ATTR_DEAD __volatile
|
||||
#else
|
||||
#define MAKE_ATTR_DEAD /* delete */
|
||||
#define MAKE_ATTR_DEAD /* delete */
|
||||
#endif
|
||||
|
||||
#if MAKE_GNUC_PREREQ(2, 7)
|
||||
@ -141,21 +141,29 @@
|
||||
* A boolean type is defined as an integer, not an enum, for historic reasons.
|
||||
* The only allowed values are the constants TRUE and FALSE (1 and 0).
|
||||
*/
|
||||
|
||||
#ifdef USE_DOUBLE_BOOLEAN
|
||||
#if defined(lint) || defined(USE_C99_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. */
|
||||
typedef double Boolean;
|
||||
#define TRUE 1.0
|
||||
#define FALSE 0.0
|
||||
#elif defined(USE_UCHAR_BOOLEAN)
|
||||
/* During development, to find code that depends on the exact value of TRUE or
|
||||
* that stores other values in Boolean variables. */
|
||||
/*
|
||||
* During development, to find code that depends on the exact value of TRUE or
|
||||
* that stores other values in Boolean variables.
|
||||
*/
|
||||
typedef unsigned char Boolean;
|
||||
#define TRUE ((unsigned char)0xFF)
|
||||
#define FALSE ((unsigned char)0x00)
|
||||
#elif defined(USE_CHAR_BOOLEAN)
|
||||
/* During development, to find code that uses a boolean as array index, via
|
||||
* -Wchar-subscripts. */
|
||||
/*
|
||||
* During development, to find code that uses a boolean as array index, via
|
||||
* -Wchar-subscripts.
|
||||
*/
|
||||
typedef char Boolean;
|
||||
#define TRUE ((char)-1)
|
||||
#define FALSE ((char)0x00)
|
||||
@ -186,134 +194,181 @@ typedef int Boolean;
|
||||
#endif
|
||||
|
||||
#if defined(sun) && (defined(__svr4__) || defined(__SVR4))
|
||||
#define POSIX_SIGNALS
|
||||
# define POSIX_SIGNALS
|
||||
#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 {
|
||||
UNMADE, /* Not examined yet */
|
||||
DEFERRED, /* Examined once (building child) */
|
||||
REQUESTED, /* on toBeMade list */
|
||||
BEINGMADE, /* Target is already being made.
|
||||
* Indicates a cycle in the graph. */
|
||||
MADE, /* Was out-of-date and has been made */
|
||||
UPTODATE, /* Was already up-to-date */
|
||||
ERROR, /* An error occurred while it was being
|
||||
* made (used only in compat mode) */
|
||||
ABORTED /* The target was aborted due to an error
|
||||
* making an inferior (compat). */
|
||||
/* Not examined yet. */
|
||||
UNMADE,
|
||||
/* The node has been examined but is not yet ready since its
|
||||
* dependencies have to be made first. */
|
||||
DEFERRED,
|
||||
|
||||
/* The node is on the toBeMade list. */
|
||||
REQUESTED,
|
||||
|
||||
/* The node is already being made. Trying to build a node in this
|
||||
* 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;
|
||||
|
||||
/* 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
|
||||
* 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 {
|
||||
OP_NONE = 0,
|
||||
OP_NONE = 0,
|
||||
|
||||
/* The dependency operator ':' is the most common one. The commands of
|
||||
* this node are executed if any child is out-of-date. */
|
||||
OP_DEPENDS = 1 << 0,
|
||||
/* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date. */
|
||||
OP_FORCE = 1 << 1,
|
||||
/* The dependency operator '::' behaves like ':', except that it allows
|
||||
* multiple dependency groups to be defined. Each of these groups is
|
||||
* executed on its own, independently from the others. Each individual
|
||||
* dependency group is called a cohort. */
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
/* The dependency operator ':' is the most common one. The commands
|
||||
* of this node are executed if any child is out-of-date. */
|
||||
OP_DEPENDS = 1 << 0,
|
||||
/* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date. */
|
||||
OP_FORCE = 1 << 1,
|
||||
/* The dependency operator '::' behaves like ':', except that it
|
||||
* allows multiple dependency groups to be defined. Each of these
|
||||
* groups is executed on its own, independently from the others.
|
||||
* Each individual dependency group is called a cohort. */
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP,
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP,
|
||||
|
||||
/* Don't care if the target doesn't exist and can't be created */
|
||||
OP_OPTIONAL = 1 << 3,
|
||||
/* Use associated commands for parents */
|
||||
OP_USE = 1 << 4,
|
||||
/* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of */
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore non-zero exit status from shell commands when creating the node */
|
||||
OP_IGNORE = 1 << 6,
|
||||
/* Don't remove the target when interrupted */
|
||||
OP_PRECIOUS = 1 << 7,
|
||||
/* Don't echo commands when executed */
|
||||
OP_SILENT = 1 << 8,
|
||||
/* Target is a recursive make so its commands should always be executed
|
||||
* when it is out of date, regardless of the state of the -n or -t flags */
|
||||
OP_MAKE = 1 << 9,
|
||||
/* Target is out-of-date only if any of its children was out-of-date */
|
||||
OP_JOIN = 1 << 10,
|
||||
/* Assume the children of the node have been already made */
|
||||
OP_MADE = 1 << 11,
|
||||
/* Special .BEGIN, .END, .INTERRUPT */
|
||||
OP_SPECIAL = 1 << 12,
|
||||
/* Like .USE, only prepend commands */
|
||||
OP_USEBEFORE = 1 << 13,
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in the
|
||||
* parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
OP_INVISIBLE = 1 << 14,
|
||||
/* The node is exempt from normal 'main target' processing in parse.c */
|
||||
OP_NOTMAIN = 1 << 15,
|
||||
/* Not a file target; run always */
|
||||
OP_PHONY = 1 << 16,
|
||||
/* Don't search for file in the path */
|
||||
OP_NOPATH = 1 << 17,
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is made
|
||||
* first, including its children. Once that is finished, source2 is made,
|
||||
* including its children. The .WAIT keyword may appear more than once in
|
||||
* a single dependency declaration. */
|
||||
OP_WAIT = 1 << 18,
|
||||
/* .NOMETA do not create a .meta file */
|
||||
OP_NOMETA = 1 << 19,
|
||||
/* .META we _do_ want a .meta file */
|
||||
OP_META = 1 << 20,
|
||||
/* Do not compare commands in .meta file */
|
||||
OP_NOMETA_CMP = 1 << 21,
|
||||
/* Possibly a submake node */
|
||||
OP_SUBMAKE = 1 << 22,
|
||||
/* Don't care if the target doesn't exist and can't be created. */
|
||||
OP_OPTIONAL = 1 << 3,
|
||||
/* Use associated commands for parents. */
|
||||
OP_USE = 1 << 4,
|
||||
/* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of. */
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore non-zero exit status from shell commands when creating the
|
||||
* node. */
|
||||
OP_IGNORE = 1 << 6,
|
||||
/* Don't remove the target when interrupted. */
|
||||
OP_PRECIOUS = 1 << 7,
|
||||
/* Don't echo commands when executed. */
|
||||
OP_SILENT = 1 << 8,
|
||||
/* Target is a recursive make so its commands should always be
|
||||
* executed when it is out of date, regardless of the state of the
|
||||
* -n or -t flags. */
|
||||
OP_MAKE = 1 << 9,
|
||||
/* Target is out-of-date only if any of its children was out-of-date. */
|
||||
OP_JOIN = 1 << 10,
|
||||
/* Assume the children of the node have been already made. */
|
||||
OP_MADE = 1 << 11,
|
||||
/* Special .BEGIN, .END or .INTERRUPT. */
|
||||
OP_SPECIAL = 1 << 12,
|
||||
/* Like .USE, only prepend commands. */
|
||||
OP_USEBEFORE = 1 << 13,
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in
|
||||
* the parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
OP_INVISIBLE = 1 << 14,
|
||||
/* The node does not become the main target, even if it is the first
|
||||
* target in the first makefile. */
|
||||
OP_NOTMAIN = 1 << 15,
|
||||
/* Not a file target; run always. */
|
||||
OP_PHONY = 1 << 16,
|
||||
/* Don't search for the file in the path. */
|
||||
OP_NOPATH = 1 << 17,
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is
|
||||
* made first, including its children. Once that is finished,
|
||||
* source2 is made, including its children. The .WAIT keyword may
|
||||
* appear more than once in a single dependency declaration. */
|
||||
OP_WAIT = 1 << 18,
|
||||
/* .NOMETA do not create a .meta file */
|
||||
OP_NOMETA = 1 << 19,
|
||||
/* .META we _do_ want a .meta file */
|
||||
OP_META = 1 << 20,
|
||||
/* Do not compare commands in .meta file */
|
||||
OP_NOMETA_CMP = 1 << 21,
|
||||
/* Possibly a submake node */
|
||||
OP_SUBMAKE = 1 << 22,
|
||||
|
||||
/* Attributes applied by PMake */
|
||||
/* Attributes applied by PMake */
|
||||
|
||||
/* The node is a transformation rule, such as ".c.o". */
|
||||
OP_TRANSFORM = 1 << 31,
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
OP_MEMBER = 1 << 30,
|
||||
/* The node is a library,
|
||||
* its name has the form "-l<libname>" */
|
||||
OP_LIB = 1 << 29,
|
||||
/* The node is an archive member,
|
||||
* its name has the form "archive(member)" */
|
||||
/* XXX: How does this differ from OP_MEMBER? */
|
||||
OP_ARCHV = 1 << 28,
|
||||
/* Target has all the commands it should. Used when parsing to catch
|
||||
* multiple command groups for a target. Only applies to the dependency
|
||||
* operators ':' and '!', but not to '::'. */
|
||||
OP_HAS_COMMANDS = 1 << 27,
|
||||
/* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed at
|
||||
* the very end. */
|
||||
OP_SAVE_CMDS = 1 << 26,
|
||||
/* Already processed by Suff_FindDeps */
|
||||
OP_DEPS_FOUND = 1 << 25,
|
||||
/* Node found while expanding .ALLSRC */
|
||||
OP_MARK = 1 << 24,
|
||||
/* The node is a transformation rule, such as ".c.o". */
|
||||
OP_TRANSFORM = 1 << 30,
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
OP_MEMBER = 1 << 29,
|
||||
/* The node is a library,
|
||||
* its name has the form "-l<libname>" */
|
||||
OP_LIB = 1 << 28,
|
||||
/* The node is an archive member,
|
||||
* its name has the form "archive(member)" */
|
||||
/* XXX: How does this differ from OP_MEMBER? */
|
||||
OP_ARCHV = 1 << 27,
|
||||
/* Target has all the commands it should. Used when parsing to catch
|
||||
* multiple command groups for a target. Only applies to the
|
||||
* dependency operators ':' and '!', but not to '::'. */
|
||||
OP_HAS_COMMANDS = 1 << 26,
|
||||
/* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed at
|
||||
* the very end. */
|
||||
OP_SAVE_CMDS = 1 << 25,
|
||||
/* Already processed by Suff_FindDeps, to find dependencies from
|
||||
* suffix transformation rules. */
|
||||
OP_DEPS_FOUND = 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;
|
||||
|
||||
typedef enum GNodeFlags {
|
||||
REMAKE = 0x0001, /* this target needs to be (re)made */
|
||||
CHILDMADE = 0x0002, /* children of this target were made */
|
||||
FORCE = 0x0004, /* children don't exist, and we pretend made */
|
||||
DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */
|
||||
DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */
|
||||
FROM_DEPEND = 0x0020, /* Node created from .depend */
|
||||
DONE_ALLSRC = 0x0040, /* We do it once only */
|
||||
CYCLE = 0x1000, /* Used by MakePrintStatus */
|
||||
DONECYCLE = 0x2000, /* Used by MakePrintStatus */
|
||||
INTERNAL = 0x4000 /* Internal use only */
|
||||
GNF_NONE = 0,
|
||||
/* this target needs to be (re)made */
|
||||
REMAKE = 0x0001,
|
||||
/* children of this target were made */
|
||||
CHILDMADE = 0x0002,
|
||||
/* children don't exist, and we pretend made */
|
||||
FORCE = 0x0004,
|
||||
/* Set by Make_ProcessWait() */
|
||||
DONE_WAIT = 0x0008,
|
||||
/* 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;
|
||||
|
||||
typedef struct List StringList;
|
||||
@ -324,112 +379,118 @@ typedef struct ListNode GNodeListNode;
|
||||
|
||||
typedef struct List /* of CachedDir */ SearchPath;
|
||||
|
||||
/* A graph node represents a target that can possibly be made, including its
|
||||
* relation to other targets and a lot of other details. */
|
||||
/*
|
||||
* 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 {
|
||||
/* The target's name, such as "clean" or "make.c" */
|
||||
char *name;
|
||||
/* The unexpanded name of a .USE node */
|
||||
char *uname;
|
||||
/* The full pathname of the file belonging to the target.
|
||||
* XXX: What about .PHONY targets? These don't have an associated path. */
|
||||
char *path;
|
||||
/* The target's name, such as "clean" or "make.c" */
|
||||
char *name;
|
||||
/* The unexpanded name of a .USE node */
|
||||
char *uname;
|
||||
/* The full pathname of the file belonging to the target.
|
||||
* XXX: What about .PHONY targets? These don't have an associated
|
||||
* path. */
|
||||
char *path;
|
||||
|
||||
/* The type of operator used to define the sources (see the OP flags below).
|
||||
* XXX: This looks like a wild mixture of type and flags. */
|
||||
GNodeType type;
|
||||
GNodeFlags flags;
|
||||
/* The type of operator used to define the sources (see the OP flags
|
||||
* below).
|
||||
* XXX: This looks like a wild mixture of type and flags. */
|
||||
GNodeType type;
|
||||
GNodeFlags flags;
|
||||
|
||||
/* The state of processing on this node */
|
||||
GNodeMade made;
|
||||
int unmade; /* The number of unmade children */
|
||||
/* The state of processing on this node */
|
||||
GNodeMade made;
|
||||
/* The number of unmade children */
|
||||
int unmade;
|
||||
|
||||
/* The modification time; 0 means the node does not have a corresponding
|
||||
* file; see GNode_IsOODate. */
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
/* The modification time; 0 means the node does not have a
|
||||
* corresponding file; see GNode_IsOODate. */
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
|
||||
/* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node for
|
||||
* file.c has the node for file.o in this list. */
|
||||
GNodeList *implicitParents;
|
||||
/* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node for
|
||||
* file.c has the node for file.o in this list. */
|
||||
GNodeList implicitParents;
|
||||
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
GNodeList *parents;
|
||||
/* The nodes on which this one depends. */
|
||||
GNodeList *children;
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
GNodeList parents;
|
||||
/* The nodes on which this one depends. */
|
||||
GNodeList children;
|
||||
|
||||
/* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
* made) before this node can be made, but that do not enter into the
|
||||
* datedness of this node. */
|
||||
GNodeList *order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're made
|
||||
* at all) after this node is made, but that do not depend on this node,
|
||||
* in the normal sense. */
|
||||
GNodeList *order_succ;
|
||||
/* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
* made) before this node can be made, but that do not enter into the
|
||||
* datedness of this node. */
|
||||
GNodeList order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're
|
||||
* made at all) after this node is made, but that do not depend on
|
||||
* this node, in the normal sense. */
|
||||
GNodeList order_succ;
|
||||
|
||||
/* Other nodes of the same name, for the '::' dependency operator. */
|
||||
GNodeList *cohorts;
|
||||
/* The "#n" suffix for this cohort, or "" for other nodes */
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
int unmade_cohorts;
|
||||
/* Pointer to the first instance of a '::' node; only set when on a
|
||||
* cohorts list */
|
||||
struct GNode *centurion;
|
||||
/* Other nodes of the same name, for the '::' dependency operator. */
|
||||
GNodeList cohorts;
|
||||
/* The "#n" suffix for this cohort, or "" for other nodes */
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
int unmade_cohorts;
|
||||
/* Pointer to the first instance of a '::' node; only set when on a
|
||||
* cohorts list */
|
||||
struct GNode *centurion;
|
||||
|
||||
/* Last time (sequence number) we tried to make this node */
|
||||
unsigned int checked_seqno;
|
||||
/* Last time (sequence number) we tried to make this node */
|
||||
unsigned int checked_seqno;
|
||||
|
||||
/* The "local" variables that are specific to this target and this target
|
||||
* only, such as $@, $<, $?.
|
||||
*
|
||||
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
|
||||
* VAR_INTERNAL, which contain variables with arbitrary names. */
|
||||
HashTable /* of Var pointer */ context;
|
||||
/* The "local" variables that are specific to this target and this
|
||||
* target only, such as $@, $<, $?.
|
||||
*
|
||||
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
|
||||
* VAR_INTERNAL, which contain variables with arbitrary names. */
|
||||
HashTable /* of Var pointer */ vars;
|
||||
|
||||
/* The commands to be given to a shell to create this target. */
|
||||
StringList *commands;
|
||||
/* The commands to be given to a shell to create this target. */
|
||||
StringList commands;
|
||||
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
|
||||
* but the Suff module) */
|
||||
struct Suff *suffix;
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to
|
||||
* everyone but the Suff module) */
|
||||
struct Suffix *suffix;
|
||||
|
||||
/* Filename where the GNode got defined */
|
||||
/* XXX: What is the lifetime of this string? */
|
||||
const char *fname;
|
||||
/* Line number where the GNode got defined */
|
||||
int lineno;
|
||||
/* Filename where the GNode got defined */
|
||||
/* XXX: What is the lifetime of this string? */
|
||||
const char *fname;
|
||||
/* Line number where the GNode got defined */
|
||||
int lineno;
|
||||
} GNode;
|
||||
|
||||
/* Error levels for diagnostics during parsing. */
|
||||
typedef enum ParseErrorLevel {
|
||||
/* Exit when the current top-level makefile has been parsed completely. */
|
||||
PARSE_FATAL = 1,
|
||||
/* Print "warning"; may be upgraded to fatal by the -w option. */
|
||||
PARSE_WARNING,
|
||||
/* Informational, mainly used during development of makefiles. */
|
||||
PARSE_INFO
|
||||
/* Exit when the current top-level makefile has been parsed
|
||||
* completely. */
|
||||
PARSE_FATAL = 1,
|
||||
/* Print "warning"; may be upgraded to fatal by the -w option. */
|
||||
PARSE_WARNING,
|
||||
/* Informational, mainly used during development of makefiles. */
|
||||
PARSE_INFO
|
||||
} ParseErrorLevel;
|
||||
|
||||
/*
|
||||
* Values returned by Cond_EvalLine and Cond_EvalCondition.
|
||||
*/
|
||||
typedef enum CondEvalResult {
|
||||
COND_PARSE, /* Parse the next lines */
|
||||
COND_SKIP, /* Skip the next lines */
|
||||
COND_INVALID /* Not a conditional statement */
|
||||
COND_PARSE, /* Parse the next lines */
|
||||
COND_SKIP, /* Skip the next lines */
|
||||
COND_INVALID /* Not a conditional statement */
|
||||
} CondEvalResult;
|
||||
|
||||
/* Names of the variables that are "local" to a specific target. */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
|
||||
/*
|
||||
* Global Variables
|
||||
@ -444,43 +505,36 @@ extern Boolean doing_depend;
|
||||
/* .DEFAULT rule */
|
||||
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;
|
||||
/* Variables defined in a global context, e.g in the Makefile itself. */
|
||||
extern GNode *VAR_GLOBAL;
|
||||
/* Variables defined on the command line. */
|
||||
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[];
|
||||
|
||||
/* The time at the start of this whole process */
|
||||
extern time_t now;
|
||||
|
||||
/*
|
||||
* If FALSE (the default behavior), undefined subexpressions in a variable
|
||||
* expression are discarded. If TRUE (only during variable assignments using
|
||||
* 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)".
|
||||
* The list of directories to search when looking for targets (set by the
|
||||
* special target .PATH).
|
||||
*/
|
||||
extern Boolean preserveUndefined;
|
||||
|
||||
/* The list of directories to search when looking for targets (set by the
|
||||
* special target .PATH). */
|
||||
extern SearchPath *dirSearchPath;
|
||||
extern SearchPath dirSearchPath;
|
||||
/* Used for .include "...". */
|
||||
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;
|
||||
/* The default for sysIncPath. */
|
||||
extern SearchPath *defSysIncPath;
|
||||
@ -488,7 +542,7 @@ extern SearchPath *defSysIncPath;
|
||||
/* Startup directory */
|
||||
extern char curdir[];
|
||||
/* The basename of the program name, suffixed with [n] for sub-makes. */
|
||||
extern char *progname;
|
||||
extern const char *progname;
|
||||
/* Name of the .depend makefile */
|
||||
extern char *makeDependfile;
|
||||
/* If we replaced environ, this will be non-NULL. */
|
||||
@ -503,156 +557,152 @@ extern int makelevel;
|
||||
#define vFork() ((getpid() == myPid) ? vfork() : fork())
|
||||
extern pid_t myPid;
|
||||
|
||||
#define MAKEFLAGS ".MAKEFLAGS"
|
||||
#define MAKEOVERRIDES ".MAKEOVERRIDES"
|
||||
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */
|
||||
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
|
||||
#define MAKEFLAGS ".MAKEFLAGS"
|
||||
#define MAKEOVERRIDES ".MAKEOVERRIDES"
|
||||
/* prefix when printing the target of a job */
|
||||
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX"
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* exported variables */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all loaded makefiles */
|
||||
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
|
||||
#define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
|
||||
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
|
||||
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
|
||||
#define MAKE_MODE ".MAKE.MODE"
|
||||
#ifndef MAKE_LEVEL_ENV
|
||||
# define MAKE_LEVEL_ENV "MAKELEVEL"
|
||||
#endif
|
||||
|
||||
typedef enum DebugFlags {
|
||||
DEBUG_NONE = 0,
|
||||
DEBUG_ARCH = 1 << 0,
|
||||
DEBUG_COND = 1 << 1,
|
||||
DEBUG_CWD = 1 << 2,
|
||||
DEBUG_DIR = 1 << 3,
|
||||
DEBUG_ERROR = 1 << 4,
|
||||
DEBUG_FOR = 1 << 5,
|
||||
DEBUG_GRAPH1 = 1 << 6,
|
||||
DEBUG_GRAPH2 = 1 << 7,
|
||||
DEBUG_GRAPH3 = 1 << 8,
|
||||
DEBUG_HASH = 1 << 9,
|
||||
DEBUG_JOB = 1 << 10,
|
||||
DEBUG_LOUD = 1 << 11,
|
||||
DEBUG_MAKE = 1 << 12,
|
||||
DEBUG_META = 1 << 13,
|
||||
DEBUG_PARSE = 1 << 14,
|
||||
DEBUG_SCRIPT = 1 << 15,
|
||||
DEBUG_SHELL = 1 << 16,
|
||||
DEBUG_SUFF = 1 << 17,
|
||||
DEBUG_TARG = 1 << 18,
|
||||
DEBUG_VAR = 1 << 19,
|
||||
DEBUG_ALL = (1 << 20) - 1
|
||||
DEBUG_NONE = 0,
|
||||
DEBUG_ARCH = 1 << 0,
|
||||
DEBUG_COND = 1 << 1,
|
||||
DEBUG_CWD = 1 << 2,
|
||||
DEBUG_DIR = 1 << 3,
|
||||
DEBUG_ERROR = 1 << 4,
|
||||
DEBUG_FOR = 1 << 5,
|
||||
DEBUG_GRAPH1 = 1 << 6,
|
||||
DEBUG_GRAPH2 = 1 << 7,
|
||||
DEBUG_GRAPH3 = 1 << 8,
|
||||
DEBUG_HASH = 1 << 9,
|
||||
DEBUG_JOB = 1 << 10,
|
||||
DEBUG_LOUD = 1 << 11,
|
||||
DEBUG_MAKE = 1 << 12,
|
||||
DEBUG_META = 1 << 13,
|
||||
DEBUG_PARSE = 1 << 14,
|
||||
DEBUG_SCRIPT = 1 << 15,
|
||||
DEBUG_SHELL = 1 << 16,
|
||||
DEBUG_SUFF = 1 << 17,
|
||||
DEBUG_TARG = 1 << 18,
|
||||
DEBUG_VAR = 1 << 19,
|
||||
DEBUG_ALL = (1 << 20) - 1
|
||||
} 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);
|
||||
|
||||
#define DEBUG_IMPL(module, args) \
|
||||
do { \
|
||||
if (DEBUG(module)) \
|
||||
debug_printf args; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define DEBUG0(module, text) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf("%s", text)
|
||||
|
||||
DEBUG_IMPL(module, ("%s", text))
|
||||
#define DEBUG1(module, fmt, arg1) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1))
|
||||
#define DEBUG2(module, fmt, arg1, arg2) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2))
|
||||
#define DEBUG3(module, fmt, arg1, arg2, arg3) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3))
|
||||
#define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4))
|
||||
#define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5))
|
||||
|
||||
typedef enum PrintVarsMode {
|
||||
PVM_NONE,
|
||||
PVM_UNEXPANDED,
|
||||
PVM_EXPANDED
|
||||
PVM_NONE,
|
||||
PVM_UNEXPANDED,
|
||||
PVM_EXPANDED
|
||||
} PrintVarsMode;
|
||||
|
||||
/* Command line options */
|
||||
typedef struct CmdOpts {
|
||||
/* -B: whether we are make compatible */
|
||||
Boolean compatMake;
|
||||
/* -B: whether we are make compatible */
|
||||
Boolean compatMake;
|
||||
|
||||
/* -d: debug control: There is one bit per module. It is up to the
|
||||
* module what debug information to print. */
|
||||
DebugFlags debug;
|
||||
/* -d: debug control: There is one bit per module. It is up to the
|
||||
* module what debug information to print. */
|
||||
DebugFlags debug;
|
||||
|
||||
/* -df: debug output is written here - default stderr */
|
||||
FILE *debug_file;
|
||||
/* -df: debug output is written here - default stderr */
|
||||
FILE *debug_file;
|
||||
|
||||
/* -dL: lint mode
|
||||
*
|
||||
* Runs make in strict mode, with additional checks and better error
|
||||
* handling. */
|
||||
Boolean lint;
|
||||
/* -dL: lint mode
|
||||
*
|
||||
* Runs make in strict mode, with additional checks and better error
|
||||
* handling. */
|
||||
Boolean strict;
|
||||
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
Boolean debugVflag;
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
Boolean debugVflag;
|
||||
|
||||
/* -e: check environment variables before global variables */
|
||||
Boolean checkEnvFirst;
|
||||
/* -e: check environment variables before global variables */
|
||||
Boolean checkEnvFirst;
|
||||
|
||||
/* -f: the makefiles to read */
|
||||
StringList *makefiles;
|
||||
/* -f: the makefiles to read */
|
||||
StringList makefiles;
|
||||
|
||||
/* -i: if true, ignore all errors from shell commands */
|
||||
Boolean ignoreErrors;
|
||||
/* -i: if true, ignore all errors from shell commands */
|
||||
Boolean ignoreErrors;
|
||||
|
||||
/* -j: the maximum number of jobs that can run in parallel;
|
||||
* this is coordinated with the submakes */
|
||||
int maxJobs;
|
||||
/* -j: the maximum number of jobs that can run in parallel;
|
||||
* this is coordinated with the submakes */
|
||||
int maxJobs;
|
||||
|
||||
/* -k: if true, continue on unaffected portions of the graph when an
|
||||
* error occurs in one portion */
|
||||
Boolean keepgoing;
|
||||
/* -k: if true and an error occurs while making a node, continue
|
||||
* making nodes that do not depend on the erroneous node */
|
||||
Boolean keepgoing;
|
||||
|
||||
/* -N: execute no commands from the targets */
|
||||
Boolean noRecursiveExecute;
|
||||
/* -N: execute no commands from the targets */
|
||||
Boolean noRecursiveExecute;
|
||||
|
||||
/* -n: execute almost no commands from the targets */
|
||||
Boolean noExecute;
|
||||
/* -n: execute almost no commands from the targets */
|
||||
Boolean noExecute;
|
||||
|
||||
/* -q: if true, we aren't supposed to really make anything, just see if
|
||||
* the targets are out-of-date */
|
||||
Boolean queryFlag;
|
||||
/* -q: if true, we aren't supposed to really make anything, just see
|
||||
* if the targets are out-of-date */
|
||||
Boolean queryFlag;
|
||||
|
||||
/* -r: raw mode, without loading the builtin rules. */
|
||||
Boolean noBuiltins;
|
||||
/* -r: raw mode, without loading the builtin rules. */
|
||||
Boolean noBuiltins;
|
||||
|
||||
/* -s: don't echo the shell commands before executing them */
|
||||
Boolean beSilent;
|
||||
/* -s: don't echo the shell commands before executing them */
|
||||
Boolean beSilent;
|
||||
|
||||
/* -t: touch the targets if they are out-of-date, but don't actually
|
||||
* make them */
|
||||
Boolean touchFlag;
|
||||
/* -t: touch the targets if they are out-of-date, but don't actually
|
||||
* make them */
|
||||
Boolean touchFlag;
|
||||
|
||||
/* -[Vv]: print expanded or unexpanded selected variables */
|
||||
PrintVarsMode printVars;
|
||||
/* -[Vv]: the variables to print */
|
||||
StringList *variables;
|
||||
/* -[Vv]: print expanded or unexpanded selected variables */
|
||||
PrintVarsMode printVars;
|
||||
/* -[Vv]: the variables to print */
|
||||
StringList variables;
|
||||
|
||||
/* -W: if true, makefile parsing warnings are treated as errors */
|
||||
Boolean parseWarnFatal;
|
||||
/* -W: if true, makefile parsing warnings are treated as errors */
|
||||
Boolean parseWarnFatal;
|
||||
|
||||
/* -w: print Entering and Leaving for submakes */
|
||||
Boolean enterFlag;
|
||||
/* -w: print Entering and Leaving for submakes */
|
||||
Boolean enterFlag;
|
||||
|
||||
/* -X: if true, do not export variables set on the command line to the
|
||||
* environment. */
|
||||
Boolean varNoExportEnv;
|
||||
/* -X: if true, do not export variables set on the command line to the
|
||||
* environment. */
|
||||
Boolean varNoExportEnv;
|
||||
|
||||
/* The target names specified on the command line.
|
||||
* Used to resolve .if make(...) statements. */
|
||||
StringList *create;
|
||||
/* The target names specified on the command line.
|
||||
* Used to resolve .if make(...) statements. */
|
||||
StringList create;
|
||||
|
||||
} CmdOpts;
|
||||
|
||||
@ -681,13 +731,37 @@ Boolean GNode_ShouldExecute(GNode *gn);
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsTarget(const GNode *gn)
|
||||
{
|
||||
return (gn->type & OP_OPMASK) != 0;
|
||||
return (gn->type & OP_OPMASK) != 0;
|
||||
}
|
||||
|
||||
MAKE_INLINE const char *
|
||||
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 *
|
||||
@ -728,9 +802,9 @@ GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
|
||||
#endif
|
||||
|
||||
#if defined(SYSV)
|
||||
#define KILLPG(pid, sig) kill(-(pid), (sig))
|
||||
#define KILLPG(pid, sig) kill(-(pid), (sig))
|
||||
#else
|
||||
#define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
#define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
#endif
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
@ -751,36 +825,42 @@ ch_toupper(char ch) { return (char)toupper((unsigned char)ch); }
|
||||
MAKE_INLINE void
|
||||
cpp_skip_whitespace(const char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
cpp_skip_hspace(const char **pp)
|
||||
{
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*pp)++;
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
pp_skip_whitespace(char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
pp_skip_hspace(char **pp)
|
||||
{
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*pp)++;
|
||||
while (**pp == ' ' || **pp == '\t')
|
||||
(*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>
|
||||
# ifndef lint
|
||||
# define MAKE_RCSID(id) __RCSID(id)
|
||||
# endif
|
||||
# define MAKE_RCSID(id) __RCSID(id)
|
||||
#elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
|
||||
# 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
|
||||
# define MAKE_RCSID(id) static volatile char rcsid[] = id
|
||||
#endif
|
||||
|
@ -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.
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
#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
|
||||
|
||||
@ -61,8 +61,7 @@ bmake_strdup(const char *str)
|
||||
char *p;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if ((p = malloc(len)) == NULL)
|
||||
enomem();
|
||||
p = bmake_malloc(len);
|
||||
return memcpy(p, str, len);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
@ -41,14 +41,16 @@ char *bmake_strldup(const char *, size_t);
|
||||
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
bmake_free(void *p)
|
||||
{
|
||||
if (p != NULL)
|
||||
free(p);
|
||||
if (p != NULL)
|
||||
free(p);
|
||||
}
|
||||
|
359
meta.c
359
meta.c
@ -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.
|
||||
@ -55,9 +55,9 @@ char * dirname(char *);
|
||||
#endif
|
||||
|
||||
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 StringList *metaIgnorePaths; /* paths we deliberately ignore */
|
||||
static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */
|
||||
static char *metaIgnorePathsStr; /* string storage for the list */
|
||||
|
||||
#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 metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
|
||||
static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
|
||||
static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
|
||||
static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
|
||||
static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
|
||||
static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
|
||||
|
||||
extern Boolean forceJobs;
|
||||
extern char **environ;
|
||||
|
||||
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
|
||||
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
|
||||
|
||||
#ifndef N2U
|
||||
# define N2U(n, u) (((n) + ((u) - 1)) / (u))
|
||||
@ -128,7 +128,7 @@ meta_open_filemon(BuildMon *pbm)
|
||||
|
||||
pbm->mon_fd = -1;
|
||||
pbm->filemon = NULL;
|
||||
if (!useFilemon || !pbm->mfp)
|
||||
if (!useFilemon || pbm->mfp == NULL)
|
||||
return;
|
||||
|
||||
pbm->filemon = filemon_open();
|
||||
@ -222,7 +222,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
|
||||
|
||||
do {
|
||||
cp = strstr(buf, eat);
|
||||
if (cp) {
|
||||
if (cp != NULL) {
|
||||
cp2 = cp + eatlen;
|
||||
if (dots == 2 && cp > buf) {
|
||||
do {
|
||||
@ -235,7 +235,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
|
||||
return; /* can't happen? */
|
||||
}
|
||||
}
|
||||
} while (cp);
|
||||
} while (cp != NULL);
|
||||
}
|
||||
|
||||
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
|
||||
* basename as given to us.
|
||||
*/
|
||||
if ((cp = strrchr(tname, '/'))) {
|
||||
if (cached_realpath(tname, buf)) {
|
||||
if ((rp = strrchr(buf, '/'))) {
|
||||
if ((cp = strrchr(tname, '/')) != NULL) {
|
||||
if (cached_realpath(tname, buf) != NULL) {
|
||||
if ((rp = strrchr(buf, '/')) != NULL) {
|
||||
rp++;
|
||||
cp++;
|
||||
if (strcmp(cp, rp) != 0)
|
||||
@ -316,25 +316,22 @@ meta_name(char *mname, size_t mnamelen,
|
||||
* Return true if running ${.MAKE}
|
||||
* Bypassed if target is flagged .MAKE
|
||||
*/
|
||||
static int
|
||||
is_submake(void *cmdp, void *gnp)
|
||||
static Boolean
|
||||
is_submake(const char *cmd, GNode *gn)
|
||||
{
|
||||
static const char *p_make = NULL;
|
||||
static size_t p_len;
|
||||
char *cmd = cmdp;
|
||||
GNode *gn = gnp;
|
||||
char *mp = NULL;
|
||||
char *cp;
|
||||
char *cp2;
|
||||
int rc = 0; /* keep looking */
|
||||
Boolean rc = FALSE;
|
||||
|
||||
if (p_make == NULL) {
|
||||
void *dontFreeIt;
|
||||
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
|
||||
p_make = Var_Value(".MAKE", gn).str;
|
||||
p_len = strlen(p_make);
|
||||
}
|
||||
cp = strchr(cmd, '$');
|
||||
if ((cp)) {
|
||||
if (cp != NULL) {
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
cmd = mp;
|
||||
@ -346,17 +343,17 @@ is_submake(void *cmdp, void *gnp)
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
rc = 1;
|
||||
rc = TRUE;
|
||||
break;
|
||||
}
|
||||
if (cp2 > cmd && rc > 0) {
|
||||
if (cp2 > cmd && rc) {
|
||||
switch (cp2[-1]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
break;
|
||||
default:
|
||||
rc = 0; /* no match */
|
||||
rc = FALSE; /* no match */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -365,32 +362,38 @@ is_submake(void *cmdp, void *gnp)
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct meta_file_s {
|
||||
FILE *fp;
|
||||
GNode *gn;
|
||||
} meta_file_t;
|
||||
static Boolean
|
||||
any_is_submake(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
|
||||
if (is_submake(ln->datum, gn))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
printCMD(const char *cmd, meta_file_t *mfp)
|
||||
printCMD(const char *cmd, FILE *fp, GNode *gn)
|
||||
{
|
||||
char *cmd_freeIt = NULL;
|
||||
|
||||
if (strchr(cmd, '$')) {
|
||||
(void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt);
|
||||
if (strchr(cmd, '$') != NULL) {
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmd_freeIt);
|
||||
/* TODO: handle errors */
|
||||
cmd = cmd_freeIt;
|
||||
}
|
||||
fprintf(mfp->fp, "CMD %s\n", cmd);
|
||||
fprintf(fp, "CMD %s\n", cmd);
|
||||
free(cmd_freeIt);
|
||||
}
|
||||
|
||||
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)
|
||||
printCMD(ln->datum, mf);
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
|
||||
printCMD(ln->datum, fp, gn);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -404,7 +407,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
|
||||
} \
|
||||
return FALSE; \
|
||||
} \
|
||||
} while (0)
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
|
||||
/*
|
||||
@ -412,7 +415,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
|
||||
*/
|
||||
static Boolean
|
||||
meta_needed(GNode *gn, const char *dname,
|
||||
char *objdir, int verbose)
|
||||
char *objdir_realpath, Boolean verbose)
|
||||
{
|
||||
struct cached_stat cst;
|
||||
|
||||
@ -431,14 +434,14 @@ meta_needed(GNode *gn, const char *dname,
|
||||
}
|
||||
|
||||
/* Check if there are no commands to execute. */
|
||||
if (Lst_IsEmpty(gn->commands)) {
|
||||
if (Lst_IsEmpty(&gn->commands)) {
|
||||
if (verbose)
|
||||
debug_printf("Skipping meta for %s: no commands\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
|
||||
/* OP_SUBMAKE is a bit too aggressive */
|
||||
if (Lst_ForEachUntil(gn->commands, is_submake, gn)) {
|
||||
if (any_is_submake(gn)) {
|
||||
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
@ -452,8 +455,8 @@ meta_needed(GNode *gn, const char *dname,
|
||||
}
|
||||
|
||||
/* make sure these are canonical */
|
||||
if (cached_realpath(dname, objdir))
|
||||
dname = objdir;
|
||||
if (cached_realpath(dname, objdir_realpath) != NULL)
|
||||
dname = objdir_realpath;
|
||||
|
||||
/* If we aren't in the object directory, don't create a meta file. */
|
||||
if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
|
||||
@ -469,25 +472,24 @@ meta_needed(GNode *gn, const char *dname,
|
||||
static FILE *
|
||||
meta_create(BuildMon *pbm, GNode *gn)
|
||||
{
|
||||
meta_file_t mf;
|
||||
FILE *fp;
|
||||
char buf[MAXPATHLEN];
|
||||
char objdir[MAXPATHLEN];
|
||||
char objdir_realpath[MAXPATHLEN];
|
||||
char **ptr;
|
||||
const char *dname;
|
||||
FStr dname;
|
||||
const char *tname;
|
||||
char *fname;
|
||||
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);
|
||||
|
||||
/* if this succeeds objdir is realpath of dname */
|
||||
if (!meta_needed(gn, dname, objdir, TRUE))
|
||||
/* if this succeeds objdir_realpath is realpath of dname */
|
||||
if (!meta_needed(gn, dname.str, objdir_realpath, TRUE))
|
||||
goto out;
|
||||
dname = objdir;
|
||||
dname.str = objdir_realpath;
|
||||
|
||||
if (metaVerbose) {
|
||||
char *mp;
|
||||
@ -495,16 +497,12 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
/* Describe the target we are building */
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
if (*mp)
|
||||
if (mp[0] != '\0')
|
||||
fprintf(stdout, "%s\n", mp);
|
||||
free(mp);
|
||||
}
|
||||
/* Get the basename of the target */
|
||||
if ((cp = strrchr(tname, '/')) == NULL) {
|
||||
cp = tname;
|
||||
} else {
|
||||
cp++;
|
||||
}
|
||||
cp = str_basename(tname);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@ -513,34 +511,32 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
goto out;
|
||||
|
||||
fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname,
|
||||
dname, tname, objdir);
|
||||
dname.str, tname, objdir_realpath);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_create: %s\n", fname);
|
||||
#endif
|
||||
|
||||
if ((mf.fp = fopen(fname, "w")) == NULL)
|
||||
if ((fp = fopen(fname, "w")) == NULL)
|
||||
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(mf.fp, "CWD %s\n", getcwd(buf, sizeof buf));
|
||||
fprintf(mf.fp, "TARGET %s\n", tname);
|
||||
fprintf(fp, "CWD %s\n", getcwd(buf, sizeof buf));
|
||||
fprintf(fp, "TARGET %s\n", tname);
|
||||
cp = GNode_VarOodate(gn);
|
||||
if (cp && *cp) {
|
||||
fprintf(mf.fp, "OODATE %s\n", cp);
|
||||
if (cp != NULL && *cp != '\0') {
|
||||
fprintf(fp, "OODATE %s\n", cp);
|
||||
}
|
||||
if (metaEnv) {
|
||||
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");
|
||||
fflush(mf.fp);
|
||||
fprintf(fp, "-- command output --\n");
|
||||
fflush(fp);
|
||||
|
||||
Var_Append(".MAKE.META.FILES", 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;
|
||||
}
|
||||
out:
|
||||
bmake_free(objdir_freeIt);
|
||||
FStr_Done(&dname);
|
||||
|
||||
return mf.fp;
|
||||
return fp;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
@ -583,7 +579,7 @@ meta_init(void)
|
||||
|
||||
|
||||
#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)
|
||||
|
||||
/*
|
||||
@ -592,24 +588,24 @@ meta_init(void)
|
||||
void
|
||||
meta_mode_init(const char *make_mode)
|
||||
{
|
||||
static int once = 0;
|
||||
static Boolean once = FALSE;
|
||||
char *cp;
|
||||
void *freeIt;
|
||||
FStr value;
|
||||
|
||||
useMeta = TRUE;
|
||||
useFilemon = TRUE;
|
||||
writeMeta = TRUE;
|
||||
|
||||
if (make_mode) {
|
||||
if (strstr(make_mode, "env"))
|
||||
if (make_mode != NULL) {
|
||||
if (strstr(make_mode, "env") != NULL)
|
||||
metaEnv = TRUE;
|
||||
if (strstr(make_mode, "verb"))
|
||||
if (strstr(make_mode, "verb") != NULL)
|
||||
metaVerbose = TRUE;
|
||||
if (strstr(make_mode, "read"))
|
||||
if (strstr(make_mode, "read") != NULL)
|
||||
writeMeta = FALSE;
|
||||
if (strstr(make_mode, "nofilemon"))
|
||||
if (strstr(make_mode, "nofilemon") != NULL)
|
||||
useFilemon = FALSE;
|
||||
if (strstr(make_mode, "ignore-cmd"))
|
||||
if (strstr(make_mode, "ignore-cmd") != NULL)
|
||||
metaIgnoreCMDs = TRUE;
|
||||
if (useFilemon)
|
||||
get_mode_bf(filemonMissing, "missing-filemon=");
|
||||
@ -628,39 +624,37 @@ meta_mode_init(const char *make_mode)
|
||||
}
|
||||
if (once)
|
||||
return;
|
||||
once = 1;
|
||||
once = TRUE;
|
||||
memset(&Mybm, 0, sizeof Mybm);
|
||||
/*
|
||||
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
|
||||
*/
|
||||
metaBailiwick = Lst_New();
|
||||
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaBailiwick, metaBailiwickStr);
|
||||
str2Lst_Append(&metaBailiwick, metaBailiwickStr);
|
||||
/*
|
||||
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
|
||||
*/
|
||||
metaIgnorePaths = Lst_New();
|
||||
Var_Append(MAKE_META_IGNORE_PATHS,
|
||||
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
|
||||
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr);
|
||||
str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
|
||||
|
||||
/*
|
||||
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
||||
*/
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) {
|
||||
value = Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL);
|
||||
if (value.str != NULL) {
|
||||
metaIgnorePatterns = TRUE;
|
||||
bmake_free(freeIt);
|
||||
FStr_Done(&value);
|
||||
}
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) {
|
||||
value = Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL);
|
||||
if (value.str != NULL) {
|
||||
metaIgnoreFilter = TRUE;
|
||||
bmake_free(freeIt);
|
||||
FStr_Done(&value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,7 +704,7 @@ meta_job_child(Job *job)
|
||||
}
|
||||
if (pbm->mfp != NULL) {
|
||||
close(fileno(pbm->mfp));
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
pid_t pid;
|
||||
|
||||
pid = getpid();
|
||||
@ -733,7 +727,7 @@ meta_job_parent(Job *job, pid_t pid)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
filemon_setpid_parent(pbm->filemon, pid);
|
||||
}
|
||||
#endif
|
||||
@ -750,7 +744,7 @@ meta_job_fd(Job *job)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
return filemon_readfd(pbm->filemon);
|
||||
}
|
||||
#endif
|
||||
@ -768,7 +762,7 @@ meta_job_event(Job *job)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
return filemon_process(pbm->filemon);
|
||||
}
|
||||
#endif
|
||||
@ -776,7 +770,7 @@ meta_job_event(Job *job)
|
||||
}
|
||||
|
||||
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];
|
||||
BuildMon *pbm;
|
||||
@ -790,11 +784,9 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
}
|
||||
if (pbm->mfp != NULL) {
|
||||
fprintf(pbm->mfp, "\n*** Error code %d%s\n",
|
||||
status,
|
||||
(flags & JOB_IGNERR) ?
|
||||
"(ignored)" : "");
|
||||
status, ignerr ? "(ignored)" : "");
|
||||
}
|
||||
if (gn) {
|
||||
if (gn != NULL) {
|
||||
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
|
||||
}
|
||||
getcwd(cwd, sizeof cwd);
|
||||
@ -826,15 +818,16 @@ meta_job_output(Job *job, char *cp, const char *nl)
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
|
||||
/* TODO: handle errors */
|
||||
if ((cp2 = strchr(meta_prefix, '$')))
|
||||
if ((cp2 = strchr(meta_prefix, '$')) != NULL)
|
||||
meta_prefix_len = (size_t)(cp2 - meta_prefix);
|
||||
else
|
||||
meta_prefix_len = strlen(meta_prefix);
|
||||
}
|
||||
if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
|
||||
cp = strchr(cp+1, '\n');
|
||||
if (!cp++)
|
||||
cp = strchr(cp + 1, '\n');
|
||||
if (cp == NULL)
|
||||
return;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
fprintf(pbm->mfp, "%s%s", cp, nl);
|
||||
@ -854,7 +847,7 @@ meta_cmd_finish(void *pbmp)
|
||||
pbm = &Mybm;
|
||||
|
||||
#ifdef USE_FILEMON
|
||||
if (pbm->filemon) {
|
||||
if (pbm->filemon != NULL) {
|
||||
while (filemon_process(pbm->filemon) > 0)
|
||||
continue;
|
||||
if (filemon_close(pbm->filemon) == -1)
|
||||
@ -898,11 +891,9 @@ meta_job_finish(Job *job)
|
||||
void
|
||||
meta_finish(void)
|
||||
{
|
||||
if (metaBailiwick != NULL)
|
||||
Lst_Free(metaBailiwick);
|
||||
Lst_Done(&metaBailiwick);
|
||||
free(metaBailiwickStr);
|
||||
if (metaIgnorePaths != NULL)
|
||||
Lst_Free(metaIgnorePaths);
|
||||
Lst_Done(&metaIgnorePaths);
|
||||
free(metaIgnorePathsStr);
|
||||
}
|
||||
|
||||
@ -936,32 +927,39 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
|
||||
if (newsz <= bufsz)
|
||||
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);
|
||||
if (p) {
|
||||
*bufp = buf = p;
|
||||
*szp = bufsz = newsz;
|
||||
/* fetch the rest */
|
||||
if (fgets(&buf[x], (int)bufsz - x, fp) == NULL)
|
||||
return x; /* truncated! */
|
||||
goto check_newline;
|
||||
}
|
||||
*bufp = buf = p;
|
||||
*szp = bufsz = newsz;
|
||||
/* fetch the rest */
|
||||
if (fgets(&buf[x], (int)bufsz - x, fp) == NULL)
|
||||
return x; /* truncated! */
|
||||
goto check_newline;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lst_ForEachUntil wants 1 to stop search */
|
||||
static int
|
||||
prefix_match(void *p, void *q)
|
||||
static Boolean
|
||||
prefix_match(const char *prefix, const char *path)
|
||||
{
|
||||
const char *prefix = p;
|
||||
const char *path = q;
|
||||
size_t n = strlen(prefix);
|
||||
|
||||
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/". */
|
||||
static Boolean
|
||||
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] == '/';
|
||||
}
|
||||
|
||||
static int
|
||||
static Boolean
|
||||
meta_ignore(GNode *gn, const char *p)
|
||||
{
|
||||
char fname[MAXPATHLEN];
|
||||
@ -983,7 +981,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
|
||||
if (*p == '/') {
|
||||
cached_realpath(p, fname); /* clean it up */
|
||||
if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) {
|
||||
if (has_any_prefix(fname, &metaIgnorePaths)) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
|
||||
#endif
|
||||
@ -999,7 +997,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
|
||||
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
|
||||
/* TODO: handle errors */
|
||||
if (*pm) {
|
||||
if (pm[0] != '\0') {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
|
||||
#endif
|
||||
@ -1043,7 +1041,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
* if we detect this we want to reproduce it.
|
||||
* 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); \
|
||||
oodate = TRUE; \
|
||||
continue; \
|
||||
@ -1052,7 +1050,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
#define DEQUOTE(p) if (*p == '\'') { \
|
||||
char *ep; \
|
||||
p++; \
|
||||
if ((ep = strchr(p, '\''))) \
|
||||
if ((ep = strchr(p, '\'')) != NULL) \
|
||||
*ep = '\0'; \
|
||||
}
|
||||
|
||||
@ -1080,32 +1078,30 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
char fname1[MAXPATHLEN];
|
||||
char fname2[MAXPATHLEN];
|
||||
char fname3[MAXPATHLEN];
|
||||
const char *dname;
|
||||
FStr dname;
|
||||
const char *tname;
|
||||
char *p;
|
||||
char *cp;
|
||||
char *link_src;
|
||||
char *move_target;
|
||||
static size_t cwdlen = 0;
|
||||
static size_t tmplen = 0;
|
||||
FILE *fp;
|
||||
Boolean needOODATE = FALSE;
|
||||
StringList *missingFiles;
|
||||
StringList missingFiles;
|
||||
Boolean have_filemon = FALSE;
|
||||
void *objdir_freeIt;
|
||||
|
||||
if (oodate)
|
||||
return oodate; /* we're done */
|
||||
|
||||
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
|
||||
dname = Var_Value(".OBJDIR", gn);
|
||||
tname = GNode_VarTarget(gn);
|
||||
|
||||
/* 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;
|
||||
dname = fname3;
|
||||
dname.str = fname3;
|
||||
|
||||
missingFiles = Lst_New();
|
||||
Lst_Init(&missingFiles);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
meta_name(fname, sizeof fname, dname, tname, dname);
|
||||
meta_name(fname, sizeof fname, dname.str, tname, dname.str);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
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 */
|
||||
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) {
|
||||
lineno++;
|
||||
if (buf[x - 1] == '\n')
|
||||
@ -1221,8 +1217,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
CHECK_VALID_META(p);
|
||||
pid = atoi(p);
|
||||
if (pid > 0 && pid != lastpid) {
|
||||
const char *ldir;
|
||||
void *tp;
|
||||
FStr ldir;
|
||||
|
||||
if (lastpid > 0) {
|
||||
/* 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(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
|
||||
lastpid = pid;
|
||||
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(latestdir, ldir, sizeof latestdir);
|
||||
bmake_free(tp);
|
||||
ldir = Var_Value(ldir_vname, VAR_GLOBAL);
|
||||
if (ldir.str != NULL) {
|
||||
strlcpy(latestdir, ldir.str, sizeof latestdir);
|
||||
FStr_Done(&ldir);
|
||||
}
|
||||
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(lcwd, ldir, sizeof lcwd);
|
||||
bmake_free(tp);
|
||||
ldir = Var_Value(lcwd_vname, VAR_GLOBAL);
|
||||
if (ldir.str != NULL) {
|
||||
strlcpy(lcwd, ldir.str, sizeof lcwd);
|
||||
FStr_Done(&ldir);
|
||||
}
|
||||
}
|
||||
/* Skip past the pid. */
|
||||
@ -1305,13 +1300,15 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* the src as for 'R'ead
|
||||
* and the target as for 'W'rite.
|
||||
*/
|
||||
cp = p; /* save this for a second */
|
||||
/* now get target */
|
||||
if (strsep(&p, " ") == NULL)
|
||||
continue;
|
||||
CHECK_VALID_META(p);
|
||||
move_target = p;
|
||||
p = cp;
|
||||
{
|
||||
char *cp = p; /* save this for a second */
|
||||
/* now get target */
|
||||
if (strsep(&p, " ") == NULL)
|
||||
continue;
|
||||
CHECK_VALID_META(p);
|
||||
move_target = p;
|
||||
p = cp;
|
||||
}
|
||||
/* 'L' and 'M' put single quotes around the args */
|
||||
DEQUOTE(p);
|
||||
DEQUOTE(move_target);
|
||||
@ -1319,12 +1316,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
case 'D': /* unlink */
|
||||
if (*p == '/') {
|
||||
/* remove any missingFiles entries that match p */
|
||||
StringListNode *ln = missingFiles->first;
|
||||
StringListNode *ln = missingFiles.first;
|
||||
while (ln != NULL) {
|
||||
StringListNode *next = ln->next;
|
||||
if (path_starts_with(ln->datum, p)) {
|
||||
free(ln->datum);
|
||||
Lst_Remove(missingFiles, ln);
|
||||
Lst_Remove(&missingFiles, ln);
|
||||
}
|
||||
ln = next;
|
||||
}
|
||||
@ -1368,14 +1365,14 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
if (*p != '/')
|
||||
break;
|
||||
|
||||
if (Lst_IsEmpty(metaBailiwick))
|
||||
if (Lst_IsEmpty(&metaBailiwick))
|
||||
break;
|
||||
|
||||
/* ignore cwd - normal dependencies handle those */
|
||||
if (strncmp(p, cwd, cwdlen) == 0)
|
||||
break;
|
||||
|
||||
if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p))
|
||||
if (!has_any_prefix(p, &metaBailiwick))
|
||||
break;
|
||||
|
||||
/* tmpdir might be within */
|
||||
@ -1384,13 +1381,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
/* ignore anything containing the string "tmp" */
|
||||
/* XXX: The arguments to strstr must be swapped. */
|
||||
if ((strstr("tmp", p)))
|
||||
if (strstr("tmp", p) != NULL)
|
||||
break;
|
||||
|
||||
if ((link_src != NULL && cached_lstat(p, &cst) < 0) ||
|
||||
(link_src == NULL && cached_stat(p, &cst) < 0)) {
|
||||
if (!meta_ignore(gn, p))
|
||||
append_if_new(missingFiles, p);
|
||||
append_if_new(&missingFiles, p);
|
||||
}
|
||||
break;
|
||||
check_link_src:
|
||||
@ -1418,13 +1415,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
char *sdirs[4];
|
||||
char **sdp;
|
||||
int sdx = 0;
|
||||
int found = 0;
|
||||
Boolean found = FALSE;
|
||||
|
||||
if (*p == '/') {
|
||||
sdirs[sdx++] = p; /* done */
|
||||
} else {
|
||||
if (strcmp(".", p) == 0)
|
||||
continue; /* no point */
|
||||
continue; /* no point */
|
||||
|
||||
/* Check vs latestdir */
|
||||
snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p);
|
||||
@ -1443,13 +1440,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
sdirs[sdx++] = NULL;
|
||||
|
||||
for (sdp = sdirs; *sdp && !found; sdp++) {
|
||||
for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG3(META, "%s: %d: looking for: %s\n",
|
||||
fname, lineno, *sdp);
|
||||
#endif
|
||||
if (cached_stat(*sdp, &cst) == 0) {
|
||||
found = 1;
|
||||
found = TRUE;
|
||||
p = *sdp;
|
||||
}
|
||||
}
|
||||
@ -1473,7 +1470,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* A referenced file outside of CWD is missing.
|
||||
* We cannot catch every eventuality here...
|
||||
*/
|
||||
append_if_new(missingFiles, p);
|
||||
append_if_new(&missingFiles, p);
|
||||
}
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
@ -1496,12 +1493,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
fname, lineno);
|
||||
oodate = TRUE;
|
||||
} else {
|
||||
const char *cp;
|
||||
char *cmd = cmdNode->datum;
|
||||
Boolean hasOODATE = FALSE;
|
||||
|
||||
if (strstr(cmd, "$?"))
|
||||
if (strstr(cmd, "$?") != NULL)
|
||||
hasOODATE = TRUE;
|
||||
else if ((cp = strstr(cmd, ".OODATE"))) {
|
||||
else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
|
||||
/* check for $[{(].OODATE[:)}] */
|
||||
if (cp > cmd + 2 && cp[-2] == '$')
|
||||
hasOODATE = TRUE;
|
||||
@ -1514,7 +1512,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
|
||||
/* TODO: handle errors */
|
||||
|
||||
if ((cp = strchr(cmd, '\n'))) {
|
||||
if ((cp = strchr(cmd, '\n')) != NULL) {
|
||||
int n;
|
||||
|
||||
/*
|
||||
@ -1534,8 +1532,8 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
||||
break;
|
||||
}
|
||||
cp = strchr(++cp, '\n');
|
||||
} while (cp);
|
||||
cp = strchr(cp + 1, '\n');
|
||||
} while (cp != NULL);
|
||||
if (buf[x - 1] == '\n')
|
||||
buf[x - 1] = '\0';
|
||||
}
|
||||
@ -1571,9 +1569,9 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if (!Lst_IsEmpty(missingFiles)) {
|
||||
if (!Lst_IsEmpty(&missingFiles)) {
|
||||
DEBUG2(META, "%s: missing files: %s...\n",
|
||||
fname, (char *)missingFiles->first->datum);
|
||||
fname, (char *)missingFiles.first->datum);
|
||||
oodate = TRUE;
|
||||
}
|
||||
if (!oodate && !have_filemon && filemonMissing) {
|
||||
@ -1582,10 +1580,11 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
} else {
|
||||
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 (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) {
|
||||
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) {
|
||||
/*
|
||||
@ -1611,7 +1610,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
|
||||
oodate_out:
|
||||
bmake_free(objdir_freeIt);
|
||||
FStr_Done(&dname);
|
||||
return oodate;
|
||||
}
|
||||
|
||||
@ -1661,7 +1660,7 @@ meta_compat_parent(pid_t child)
|
||||
close(childPipe[1]); /* child side */
|
||||
outfd = childPipe[0];
|
||||
#ifdef USE_FILEMON
|
||||
metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1;
|
||||
metafd = Mybm.filemon != NULL ? filemon_readfd(Mybm.filemon) : -1;
|
||||
#else
|
||||
metafd = -1;
|
||||
#endif
|
||||
@ -1686,7 +1685,7 @@ meta_compat_parent(pid_t child)
|
||||
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 */
|
||||
ssize_t nread = read(outfd, buf, sizeof buf - 1);
|
||||
if (nread == -1)
|
||||
@ -1700,12 +1699,12 @@ meta_compat_parent(pid_t child)
|
||||
fflush(stdout);
|
||||
buf[nread] = '\0';
|
||||
meta_job_output(NULL, buf, "");
|
||||
} while (0);
|
||||
if (metafd != -1 && FD_ISSET(metafd, &readfds)) {
|
||||
} while (/*CONSTCOND*/0);
|
||||
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
|
||||
if (meta_job_event(NULL) <= 0)
|
||||
metafd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USE_META */
|
||||
#endif /* USE_META */
|
||||
|
4
meta.h
4
meta.h
@ -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.
|
||||
@ -48,7 +48,7 @@ void meta_job_child(struct Job *);
|
||||
void meta_job_parent(struct Job *, pid_t);
|
||||
int meta_job_fd(struct Job *);
|
||||
int meta_job_event(struct Job *);
|
||||
void meta_job_error(struct Job *, GNode *, int, int);
|
||||
void meta_job_error(struct Job *, GNode *, Boolean, int);
|
||||
void meta_job_output(struct Job *, char *, const char *);
|
||||
int meta_cmd_finish(void *);
|
||||
int meta_job_finish(struct Job *);
|
||||
|
@ -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.
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
extern unsigned char _metachar[];
|
||||
|
||||
#define is_shell_metachar(c) _metachar[(c) & 0x7f]
|
||||
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
|
||||
|
||||
MAKE_INLINE int
|
||||
needshell(const char *cmd)
|
||||
|
39
mk/ChangeLog
39
mk/ChangeLog
@ -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>
|
||||
|
||||
* install-mk (MK_VERSION): 20201106
|
||||
|
@ -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
|
||||
#
|
||||
@ -59,7 +59,8 @@ DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
|
||||
# note that we need to include $o in the variable _o$o
|
||||
# to ensure correct evaluation.
|
||||
.for o in ${DIRDEPS_OPTIONS}
|
||||
.undef _o$o _v$o
|
||||
.undef _o$o
|
||||
.undef _v$o
|
||||
.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
|
||||
.if defined(MK_$o.$x)
|
||||
_o$o ?= MK_$o.$x
|
||||
|
@ -1,5 +1,5 @@
|
||||
# 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
|
||||
#
|
||||
@ -41,6 +41,7 @@
|
||||
.-include <local.dirdeps-targets.mk>
|
||||
|
||||
# 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
|
||||
# these prefixes can modify how we behave
|
||||
# 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}
|
||||
|
||||
# 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*} != ""
|
||||
.info tdeps=${tdeps}
|
||||
.endif
|
||||
@ -135,7 +136,7 @@ DIRDEPS := ${DIRDEPS:O:u}
|
||||
# if we got DIRDEPS get to work
|
||||
.if !empty(DIRDEPS)
|
||||
DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@}
|
||||
# some targets what to tweak options we might want to process now
|
||||
# some targets want to tweak options we might want to process now
|
||||
.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,}
|
||||
.-include <$m>
|
||||
.endfor
|
||||
|
@ -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.
|
||||
# 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 empty(_DEP_TARGET_SPEC)
|
||||
# we may or may not have included a dependfile yet
|
||||
.if defined(.INCLUDEDFROMFILE)
|
||||
_last_dependfile := ${.INCLUDEDFROMFILE:M${.MAKE.DEPENDFILE_PREFIX}*}
|
||||
.else
|
||||
_last_dependfile := ${.MAKE.MAKEFILES:M*/${.MAKE.DEPENDFILE_PREFIX}*:[-1]}
|
||||
.endif
|
||||
.if ${_debug_reldir:U0}
|
||||
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _last_dependfile='${_last_dependfile}'
|
||||
.endif
|
||||
|
||||
.if empty(_last_dependfile) || ${_last_dependfile:E:${N_notmachine}} == ""
|
||||
# this is all we have to work with
|
||||
DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}}
|
||||
_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC}
|
||||
.else
|
||||
_DEP_TARGET_SPEC = ${_last_dependfile:${M_dep_qual_fixes:ts:}:E}
|
||||
.endif
|
||||
.if !empty(_last_dependfile)
|
||||
# if not, just use TARGET_SPEC
|
||||
_DEP_TARGET_SPEC := ${TARGET_SPEC}
|
||||
.if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != ""
|
||||
# record that we've read dependfile for this
|
||||
_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}:
|
||||
.endif
|
||||
|
17
mk/init.mk
17
mk/init.mk
@ -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
|
||||
#
|
||||
@ -65,14 +65,15 @@ CC_PIC?= -DPIC
|
||||
CXX_PIC?= ${CC_PIC}
|
||||
PROFFLAGS?= -DGPROF -DPROF
|
||||
|
||||
# targets that are ok at level 0
|
||||
LEVEL0_TARGETS += clean* destory*
|
||||
M_ListToSkip= O:u:S,^,N,:ts:
|
||||
|
||||
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != ""
|
||||
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
|
||||
# targets that are ok at level 0
|
||||
DIRDEPS_BUILD_LEVEL0_TARGETS += clean* destroy*
|
||||
M_ListToSkip?= O:u:S,^,N,:ts:
|
||||
.if ${.TARGETS:Uall:${DIRDEPS_BUILD_LEVEL0_TARGETS:${M_ListToSkip}}} != ""
|
||||
# this tells lib.mk and prog.mk to not actually build anything
|
||||
_SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
.endif
|
||||
|
||||
.if !defined(.PARSEDIR)
|
||||
# no-op is the best we can do if not bmake.
|
||||
@ -80,13 +81,15 @@ _SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
|
||||
# define this once for consistency
|
||||
.if empty(_SKIP_BUILD)
|
||||
.if !defined(_SKIP_BUILD)
|
||||
# beforebuild is a hook for things that must be done early
|
||||
all: beforebuild .WAIT realbuild
|
||||
.else
|
||||
all: .PHONY
|
||||
.if !empty(_SKIP_BUILD) && ${.MAKEFLAGS:M-V} == ""
|
||||
.warning ${_SKIP_BUILD}
|
||||
.endif
|
||||
.endif
|
||||
beforebuild:
|
||||
realbuild:
|
||||
|
||||
|
4
mk/install-mk
Normal file → Executable file
4
mk/install-mk
Normal file → Executable file
@ -55,7 +55,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# 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
|
||||
#
|
||||
@ -70,7 +70,7 @@
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
MK_VERSION=20201106
|
||||
MK_VERSION=20210101
|
||||
OWNER=
|
||||
GROUP=
|
||||
MODE=444
|
||||
|
@ -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
|
||||
@ -17,7 +17,7 @@
|
||||
.if !defined(NO_SUBDIR) && !empty(SUBDIR)
|
||||
.if make(destroy*) || make(clean*)
|
||||
.MAKE.MODE = compat
|
||||
.if !commands(destroy)
|
||||
.if !commands(obj)
|
||||
.-include <bsd.obj.mk>
|
||||
.endif
|
||||
.elif ${.MAKE.LEVEL} == 0
|
||||
|
0
mk/mkopt.sh
Executable file → Normal file
0
mk/mkopt.sh
Executable file → Normal 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}__)
|
||||
__${.PARSEFILE}__:
|
||||
@ -125,10 +125,10 @@ OPTIONS_DEFAULT_DEPENDENT+= \
|
||||
|
||||
.if ${MK_INSTALL_AS_USER} == "yes"
|
||||
# We ignore this if user is root.
|
||||
_uid!= id -u
|
||||
_uid:= ${.MAKE.UID:U${id -u:L:sh}}
|
||||
.if ${_uid} != 0
|
||||
.if !defined(USERGRP)
|
||||
USERGRP!= id -g
|
||||
USERGRP:= ${.MAKE.GID:U${id -g:L:sh}}
|
||||
.export USERGRP
|
||||
.endif
|
||||
.for x in BIN CONF DOC INC INFO FILES KMOD LIB MAN NLS PROG SHARE
|
||||
|
@ -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
|
||||
#
|
||||
@ -118,8 +118,8 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
|
||||
unix ?= We run ${_HOST_OSNAME}.
|
||||
|
||||
# We need a Bourne/POSIX shell
|
||||
MAKE_SHELL ?= sh
|
||||
SHELL ?= ${MAKE_SHELL}
|
||||
MAKE_SHELL ?= ${.SHELL:Ush}
|
||||
SHELL := ${MAKE_SHELL}
|
||||
|
||||
# A race condition in mkdir, means that it can bail if another
|
||||
# process made a dir that mkdir expected to.
|
||||
|
266
nonints.h
266
nonints.h
@ -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
|
||||
@ -86,7 +86,7 @@ Boolean Arch_LibOODate(GNode *);
|
||||
Boolean Arch_IsLib(GNode *);
|
||||
|
||||
/* compat.c */
|
||||
int Compat_RunCommand(const char *, GNode *);
|
||||
int Compat_RunCommand(const char *, GNode *, StringListNode *);
|
||||
void Compat_Run(GNodeList *);
|
||||
void Compat_Make(GNode *, GNode *);
|
||||
|
||||
@ -96,6 +96,21 @@ CondEvalResult Cond_EvalLine(const char *);
|
||||
void Cond_restore_depth(unsigned int);
|
||||
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 */
|
||||
int For_Eval(const char *);
|
||||
Boolean For_Accum(const char *);
|
||||
@ -109,7 +124,6 @@ void JobReapChild(pid_t, WAIT_T, Boolean);
|
||||
/* main.c */
|
||||
Boolean GetBooleanVar(const char *, Boolean);
|
||||
void Main_ParseArgLine(const char *);
|
||||
void MakeMode(const char *);
|
||||
char *Cmd_Exec(const char *, const char **);
|
||||
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
@ -127,42 +141,96 @@ void Parse_Init(void);
|
||||
void Parse_End(void);
|
||||
|
||||
typedef enum VarAssignOp {
|
||||
VAR_NORMAL, /* = */
|
||||
VAR_SUBST, /* := */
|
||||
VAR_SHELL, /* != or :sh= */
|
||||
VAR_APPEND, /* += */
|
||||
VAR_DEFAULT /* ?= */
|
||||
VAR_NORMAL, /* = */
|
||||
VAR_SUBST, /* := */
|
||||
VAR_SHELL, /* != or :sh= */
|
||||
VAR_APPEND, /* += */
|
||||
VAR_DEFAULT /* ?= */
|
||||
} VarAssignOp;
|
||||
|
||||
typedef struct VarAssign {
|
||||
char *varname; /* unexpanded */
|
||||
VarAssignOp op;
|
||||
const char *value; /* unexpanded */
|
||||
char *varname; /* unexpanded */
|
||||
VarAssignOp op;
|
||||
const char *value; /* unexpanded */
|
||||
} VarAssign;
|
||||
|
||||
typedef char *(*NextBufProc)(void *, size_t *);
|
||||
typedef char *(*ReadMoreProc)(void *, size_t *);
|
||||
|
||||
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
Boolean Parse_IsVar(const char *, VarAssign *out_var);
|
||||
void Parse_DoVar(VarAssign *, GNode *);
|
||||
void Parse_AddIncludeDir(const char *);
|
||||
void Parse_File(const char *, int);
|
||||
void Parse_SetInput(const char *, int, int, NextBufProc, void *);
|
||||
GNodeList *Parse_MainName(void);
|
||||
void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
|
||||
void Parse_MainName(GNodeList *);
|
||||
int Parse_GetFatals(void);
|
||||
|
||||
/* 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 {
|
||||
char **words;
|
||||
size_t len;
|
||||
void *freeIt;
|
||||
char **words;
|
||||
size_t len;
|
||||
void *freeIt;
|
||||
} 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);
|
||||
MAKE_INLINE void
|
||||
Words_Free(Words w) {
|
||||
free(w.words);
|
||||
free(w.freeIt);
|
||||
Words_Free(Words w)
|
||||
{
|
||||
free(w.words);
|
||||
free(w.freeIt);
|
||||
}
|
||||
|
||||
char *str_concat2(const char *, const char *);
|
||||
@ -204,15 +272,13 @@ GNode *Targ_FindNode(const char *);
|
||||
GNode *Targ_GetNode(const char *);
|
||||
GNode *Targ_NewInternalNode(const char *);
|
||||
GNode *Targ_GetEndNode(void);
|
||||
GNodeList *Targ_FindList(StringList *);
|
||||
Boolean Targ_Ignore(const GNode *);
|
||||
Boolean Targ_Silent(const GNode *);
|
||||
void Targ_FindList(GNodeList *, StringList *);
|
||||
Boolean Targ_Precious(const GNode *);
|
||||
void Targ_SetMain(GNode *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
void Targ_PrintNodes(GNodeList *, int);
|
||||
char *Targ_FmtTime(time_t);
|
||||
const char *Targ_FmtTime(time_t);
|
||||
void Targ_PrintType(int);
|
||||
void Targ_PrintGraph(int);
|
||||
void Targ_Propagate(void);
|
||||
@ -222,112 +288,104 @@ void Var_Init(void);
|
||||
void Var_End(void);
|
||||
|
||||
typedef enum VarEvalFlags {
|
||||
VARE_NONE = 0,
|
||||
VARE_NONE = 0,
|
||||
|
||||
/* Expand and evaluate variables during parsing.
|
||||
*
|
||||
* TODO: Document what Var_Parse and Var_Subst return when this flag
|
||||
* is not set. */
|
||||
VARE_WANTRES = 1 << 0,
|
||||
/* Expand and evaluate variables during parsing.
|
||||
*
|
||||
* TODO: Document what Var_Parse and Var_Subst return when this flag
|
||||
* is not set. */
|
||||
VARE_WANTRES = 1 << 0,
|
||||
|
||||
/* Treat undefined variables as errors.
|
||||
* Must only be used in combination with VARE_WANTRES. */
|
||||
VARE_UNDEFERR = 1 << 1,
|
||||
/* Treat undefined variables as errors.
|
||||
* Must only be used in combination with VARE_WANTRES. */
|
||||
VARE_UNDEFERR = 1 << 1,
|
||||
|
||||
/* Keep '$$' as '$$' instead of reducing it to a single '$'.
|
||||
*
|
||||
* Used in variable assignments using the ':=' operator. It allows
|
||||
* multiple such assignments to be chained without accidentally expanding
|
||||
* '$$file' to '$file' in the first assignment and interpreting it as
|
||||
* '${f}' followed by 'ile' in the next assignment.
|
||||
*
|
||||
* See also preserveUndefined, which preserves subexpressions that are
|
||||
* based on undefined variables; maybe that can be converted to a flag
|
||||
* as well. */
|
||||
VARE_KEEP_DOLLAR = 1 << 2
|
||||
/* Keep '$$' as '$$' instead of reducing it to a single '$'.
|
||||
*
|
||||
* Used in variable assignments using the ':=' operator. It allows
|
||||
* multiple such assignments to be chained without accidentally
|
||||
* expanding '$$file' to '$file' in the first assignment and
|
||||
* interpreting it as '${f}' followed by 'ile' in the next assignment.
|
||||
*
|
||||
* See also preserveUndefined, which preserves subexpressions that are
|
||||
* based on undefined variables; maybe that can be converted to a flag
|
||||
* as well. */
|
||||
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;
|
||||
|
||||
typedef enum VarSetFlags {
|
||||
VAR_SET_NONE = 0,
|
||||
VAR_SET_NONE = 0,
|
||||
|
||||
/* do not export */
|
||||
VAR_SET_NO_EXPORT = 1 << 0,
|
||||
/* do not export */
|
||||
VAR_SET_NO_EXPORT = 1 << 0,
|
||||
|
||||
/* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag. */
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
/* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag. */
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
} VarSetFlags;
|
||||
|
||||
/* 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. */
|
||||
/* The state of error handling returned by Var_Parse. */
|
||||
typedef enum VarParseResult {
|
||||
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK = 0x0000,
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK,
|
||||
|
||||
/* See if a message has already been printed for this error. */
|
||||
VPR_ANY_MSG = 0x0001,
|
||||
/* Parsing or evaluating failed, with an error message. */
|
||||
VPR_ERR,
|
||||
|
||||
/* Parsing failed.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_PARSE_MSG instead. */
|
||||
VPR_PARSE_SILENT = 0x0002,
|
||||
/*
|
||||
* Parsing succeeded, undefined expressions are allowed and the
|
||||
* expression was still undefined after applying all modifiers.
|
||||
* 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;
|
||||
|
||||
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_Undef(const char *);
|
||||
void Var_Set(const char *, const char *, GNode *);
|
||||
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags);
|
||||
void Var_Append(const char *, 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 *);
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags,
|
||||
const char **, void **);
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *);
|
||||
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
|
||||
void Var_Stats(void);
|
||||
void Var_Dump(GNode *);
|
||||
void Var_ExportVars(void);
|
||||
void Var_Export(const char *, Boolean);
|
||||
void Var_UnExport(const char *);
|
||||
void Var_ReexportVars(void);
|
||||
void Var_Export(VarExportMode, const char *);
|
||||
void Var_ExportVars(const char *);
|
||||
void Var_UnExport(Boolean, const char *);
|
||||
|
||||
/* util.c */
|
||||
typedef void (*SignalProc)(int);
|
||||
|
15
pathnames.h
15
pathnames.h
@ -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
|
||||
@ -29,7 +29,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* 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
|
||||
@ -43,12 +43,13 @@
|
||||
#ifdef HAVE_PATHS_H
|
||||
#include <paths.h>
|
||||
#endif
|
||||
#define _PATH_OBJDIR "obj"
|
||||
#define _PATH_OBJDIRPREFIX "/usr/obj"
|
||||
|
||||
#define _PATH_OBJDIR "obj"
|
||||
#define _PATH_OBJDIRPREFIX "/usr/obj"
|
||||
#ifndef _PATH_DEFSHELLDIR
|
||||
#define _PATH_DEFSHELLDIR "/bin"
|
||||
#define _PATH_DEFSHELLDIR "/bin"
|
||||
#endif
|
||||
#define _PATH_DEFSYSMK "sys.mk"
|
||||
#define _PATH_DEFSYSMK "sys.mk"
|
||||
#define _path_defsyspath "/usr/share/mk:/usr/local/share/mk:/opt/share/mk"
|
||||
#ifndef _PATH_DEFSYSPATH
|
||||
# ifdef _PATH_PREFIX_SYSPATH
|
||||
@ -58,5 +59,5 @@
|
||||
# endif
|
||||
#endif
|
||||
#ifndef _PATH_TMP
|
||||
#define _PATH_TMP "/tmp/" /* with trailing slash */
|
||||
#define _PATH_TMP "/tmp/" /* with trailing slash */
|
||||
#endif
|
||||
|
15
str.c
15
str.c
@ -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
|
||||
@ -71,7 +71,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)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. */
|
||||
char *
|
||||
@ -115,7 +115,8 @@ str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
|
||||
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.
|
||||
*
|
||||
* 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'. */
|
||||
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 = bmake_malloc((words_cap + 1) * sizeof(char *));
|
||||
@ -160,7 +161,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
switch (ch) {
|
||||
case '"':
|
||||
case '\'':
|
||||
if (inquote) {
|
||||
if (inquote != '\0') {
|
||||
if (inquote == ch)
|
||||
inquote = '\0';
|
||||
else
|
||||
@ -188,7 +189,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
if (inquote)
|
||||
if (inquote != '\0')
|
||||
break;
|
||||
if (word_start == NULL)
|
||||
continue;
|
||||
@ -211,7 +212,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
words[words_len++] = word_start;
|
||||
word_start = NULL;
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
if (expand && inquote) {
|
||||
if (expand && inquote != '\0') {
|
||||
free(words);
|
||||
free(words_buf);
|
||||
return (Words){ NULL, 0, NULL };
|
||||
|
554
targ.c
554
targ.c
@ -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
|
||||
@ -93,12 +93,6 @@
|
||||
* Targ_FindList Given a list of names, find nodes for all
|
||||
* 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
|
||||
* should not be removed if we are interrupted.
|
||||
*
|
||||
@ -108,7 +102,7 @@
|
||||
*
|
||||
* Debugging:
|
||||
* 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
|
||||
* something for suffixes, too, but...
|
||||
*/
|
||||
@ -119,14 +113,17 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)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;
|
||||
|
||||
#ifdef CLEANUP
|
||||
static GNodeList *allNodes;
|
||||
static GNodeList allNodes = LST_INIT;
|
||||
|
||||
static void GNode_Free(void *);
|
||||
#endif
|
||||
@ -134,28 +131,24 @@ static void GNode_Free(void *);
|
||||
void
|
||||
Targ_Init(void)
|
||||
{
|
||||
allTargets = Lst_New();
|
||||
HashTable_Init(&allTargetsByName);
|
||||
#ifdef CLEANUP
|
||||
allNodes = Lst_New();
|
||||
#endif
|
||||
HashTable_Init(&allTargetsByName);
|
||||
}
|
||||
|
||||
void
|
||||
Targ_End(void)
|
||||
{
|
||||
Targ_Stats();
|
||||
Targ_Stats();
|
||||
#ifdef CLEANUP
|
||||
Lst_Free(allTargets);
|
||||
HashTable_Done(&allTargetsByName);
|
||||
Lst_Destroy(allNodes, GNode_Free);
|
||||
Lst_Done(&allTargets);
|
||||
HashTable_Done(&allTargetsByName);
|
||||
Lst_DoneCall(&allNodes, GNode_Free);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Targ_Stats(void)
|
||||
{
|
||||
HashTable_DebugStats(&allTargetsByName, "targets");
|
||||
HashTable_DebugStats(&allTargetsByName, "targets");
|
||||
}
|
||||
|
||||
/*
|
||||
@ -166,10 +159,11 @@ Targ_Stats(void)
|
||||
GNodeList *
|
||||
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
|
||||
* as "target: source" are called targets. XXX: In some cases (like the
|
||||
@ -186,68 +180,95 @@ Targ_List(void)
|
||||
GNode *
|
||||
GNode_New(const char *name)
|
||||
{
|
||||
GNode *gn;
|
||||
GNode *gn;
|
||||
|
||||
gn = bmake_malloc(sizeof *gn);
|
||||
gn->name = bmake_strdup(name);
|
||||
gn->uname = NULL;
|
||||
gn->path = NULL;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
|
||||
gn->flags = 0;
|
||||
gn->made = UNMADE;
|
||||
gn->unmade = 0;
|
||||
gn->mtime = 0;
|
||||
gn->youngestChild = NULL;
|
||||
gn->implicitParents = Lst_New();
|
||||
gn->parents = Lst_New();
|
||||
gn->children = Lst_New();
|
||||
gn->order_pred = Lst_New();
|
||||
gn->order_succ = Lst_New();
|
||||
gn->cohorts = Lst_New();
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->centurion = NULL;
|
||||
gn->checked_seqno = 0;
|
||||
HashTable_Init(&gn->context);
|
||||
gn->commands = Lst_New();
|
||||
gn->suffix = NULL;
|
||||
gn->fname = NULL;
|
||||
gn->lineno = 0;
|
||||
gn = bmake_malloc(sizeof *gn);
|
||||
gn->name = bmake_strdup(name);
|
||||
gn->uname = NULL;
|
||||
gn->path = NULL;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE;
|
||||
gn->flags = GNF_NONE;
|
||||
gn->made = UNMADE;
|
||||
gn->unmade = 0;
|
||||
gn->mtime = 0;
|
||||
gn->youngestChild = NULL;
|
||||
Lst_Init(&gn->implicitParents);
|
||||
Lst_Init(&gn->parents);
|
||||
Lst_Init(&gn->children);
|
||||
Lst_Init(&gn->order_pred);
|
||||
Lst_Init(&gn->order_succ);
|
||||
Lst_Init(&gn->cohorts);
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->centurion = NULL;
|
||||
gn->checked_seqno = 0;
|
||||
HashTable_Init(&gn->vars);
|
||||
Lst_Init(&gn->commands);
|
||||
gn->suffix = NULL;
|
||||
gn->fname = NULL;
|
||||
gn->lineno = 0;
|
||||
|
||||
#ifdef CLEANUP
|
||||
Lst_Append(allNodes, gn);
|
||||
Lst_Append(&allNodes, gn);
|
||||
#endif
|
||||
|
||||
return gn;
|
||||
return gn;
|
||||
}
|
||||
|
||||
#ifdef CLEANUP
|
||||
static void
|
||||
GNode_Free(void *gnp)
|
||||
{
|
||||
GNode *gn = gnp;
|
||||
GNode *gn = gnp;
|
||||
|
||||
free(gn->name);
|
||||
free(gn->uname);
|
||||
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->name);
|
||||
free(gn->uname);
|
||||
free(gn->path);
|
||||
|
||||
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
|
||||
|
||||
@ -255,23 +276,23 @@ GNode_Free(void *gnp)
|
||||
GNode *
|
||||
Targ_FindNode(const char *name)
|
||||
{
|
||||
return HashTable_FindValue(&allTargetsByName, name);
|
||||
return HashTable_FindValue(&allTargetsByName, name);
|
||||
}
|
||||
|
||||
/* Get the existing global node, or create it. */
|
||||
GNode *
|
||||
Targ_GetNode(const char *name)
|
||||
{
|
||||
Boolean isNew;
|
||||
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
|
||||
if (!isNew)
|
||||
return HashEntry_Get(he);
|
||||
Boolean isNew;
|
||||
HashEntry *he = HashTable_CreateEntry(&allTargetsByName, name, &isNew);
|
||||
if (!isNew)
|
||||
return HashEntry_Get(he);
|
||||
|
||||
{
|
||||
GNode *gn = Targ_NewInternalNode(name);
|
||||
HashEntry_Set(he, gn);
|
||||
return gn;
|
||||
}
|
||||
{
|
||||
GNode *gn = Targ_NewInternalNode(name);
|
||||
HashEntry_Set(he, gn);
|
||||
return gn;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -283,63 +304,54 @@ Targ_GetNode(const char *name)
|
||||
GNode *
|
||||
Targ_NewInternalNode(const char *name)
|
||||
{
|
||||
GNode *gn = GNode_New(name);
|
||||
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
|
||||
Lst_Append(allTargets, gn);
|
||||
if (doing_depend)
|
||||
gn->flags |= FROM_DEPEND;
|
||||
return gn;
|
||||
GNode *gn = GNode_New(name);
|
||||
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
|
||||
Lst_Append(&allTargets, gn);
|
||||
DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
|
||||
if (doing_depend)
|
||||
gn->flags |= FROM_DEPEND;
|
||||
return gn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the .END node, which contains the commands to be run when
|
||||
* 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;
|
||||
if (endNode == NULL) {
|
||||
endNode = Targ_GetNode(".END");
|
||||
endNode->type = OP_SPECIAL;
|
||||
}
|
||||
return endNode;
|
||||
/*
|
||||
* Save the node locally to avoid having to search for it all
|
||||
* the time.
|
||||
*/
|
||||
static GNode *endNode = NULL;
|
||||
|
||||
if (endNode == NULL) {
|
||||
endNode = Targ_GetNode(".END");
|
||||
endNode->type = OP_SPECIAL;
|
||||
}
|
||||
return endNode;
|
||||
}
|
||||
|
||||
/* Return the named nodes, creating them as necessary. */
|
||||
GNodeList *
|
||||
Targ_FindList(StringList *names)
|
||||
/* Add the named nodes to the list, creating them as necessary. */
|
||||
void
|
||||
Targ_FindList(GNodeList *gns, StringList *names)
|
||||
{
|
||||
StringListNode *ln;
|
||||
GNodeList *nodes = Lst_New();
|
||||
for (ln = names->first; ln != NULL; ln = ln->next) {
|
||||
const char *name = ln->datum;
|
||||
GNode *gn = Targ_GetNode(name);
|
||||
Lst_Append(nodes, gn);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
StringListNode *ln;
|
||||
|
||||
/* Return true if should ignore errors when creating gn. */
|
||||
Boolean
|
||||
Targ_Ignore(const GNode *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;
|
||||
for (ln = names->first; ln != NULL; ln = ln->next) {
|
||||
const char *name = ln->datum;
|
||||
GNode *gn = Targ_GetNode(name);
|
||||
Lst_Append(gns, gn);
|
||||
}
|
||||
}
|
||||
|
||||
/* See if the given target is precious. */
|
||||
Boolean
|
||||
Targ_Precious(const GNode *gn)
|
||||
{
|
||||
/* XXX: Why are '::' targets precious? */
|
||||
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
|
||||
/* XXX: Why are '::' targets precious? */
|
||||
return allPrecious || gn->type & (OP_PRECIOUS | OP_DOUBLEDEP);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -352,198 +364,202 @@ static GNode *mainTarg;
|
||||
void
|
||||
Targ_SetMain(GNode *gn)
|
||||
{
|
||||
mainTarg = gn;
|
||||
mainTarg = gn;
|
||||
}
|
||||
|
||||
static void
|
||||
PrintNodeNames(GNodeList *gnodes)
|
||||
{
|
||||
GNodeListNode *node;
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (node = gnodes->first; node != NULL; node = node->next) {
|
||||
GNode *gn = node->datum;
|
||||
debug_printf(" %s%s", gn->name, gn->cohort_num);
|
||||
}
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
debug_printf(" %s%s", gn->name, gn->cohort_num);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
PrintNodeNamesLine(const char *label, GNodeList *gnodes)
|
||||
{
|
||||
if (Lst_IsEmpty(gnodes))
|
||||
return;
|
||||
debug_printf("# %s:", label);
|
||||
PrintNodeNames(gnodes);
|
||||
debug_printf("\n");
|
||||
if (Lst_IsEmpty(gnodes))
|
||||
return;
|
||||
debug_printf("# %s:", label);
|
||||
PrintNodeNames(gnodes);
|
||||
debug_printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
Targ_PrintCmds(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
debug_printf("\t%s\n", cmd);
|
||||
}
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
debug_printf("\t%s\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Format a modification time in some reasonable way and return it.
|
||||
* The time is placed in a static area, so it is overwritten with each call. */
|
||||
char *
|
||||
/*
|
||||
* Format a modification time in some reasonable way and return it.
|
||||
* The formatted time is placed in a static area, so it is overwritten
|
||||
* with each call.
|
||||
*/
|
||||
const char *
|
||||
Targ_FmtTime(time_t tm)
|
||||
{
|
||||
struct tm *parts;
|
||||
static char buf[128];
|
||||
static char buf[128];
|
||||
|
||||
parts = localtime(&tm);
|
||||
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
|
||||
return buf;
|
||||
struct tm *parts = localtime(&tm);
|
||||
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Print out a type field giving only those attributes the user can set. */
|
||||
void
|
||||
Targ_PrintType(int type)
|
||||
{
|
||||
int tbit;
|
||||
int tbit;
|
||||
|
||||
#define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break
|
||||
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break
|
||||
type &= ~OP_OPMASK;
|
||||
|
||||
type &= ~OP_OPMASK;
|
||||
while (type != 0) {
|
||||
tbit = 1 << (ffs(type) - 1);
|
||||
type &= ~tbit;
|
||||
|
||||
while (type) {
|
||||
tbit = 1 << (ffs(type) - 1);
|
||||
type &= ~tbit;
|
||||
|
||||
switch(tbit) {
|
||||
PRINTBIT(OPTIONAL);
|
||||
PRINTBIT(USE);
|
||||
PRINTBIT(EXEC);
|
||||
PRINTBIT(IGNORE);
|
||||
PRINTBIT(PRECIOUS);
|
||||
PRINTBIT(SILENT);
|
||||
PRINTBIT(MAKE);
|
||||
PRINTBIT(JOIN);
|
||||
PRINTBIT(INVISIBLE);
|
||||
PRINTBIT(NOTMAIN);
|
||||
PRINTDBIT(LIB);
|
||||
/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
|
||||
case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break;
|
||||
PRINTDBIT(ARCHV);
|
||||
PRINTDBIT(MADE);
|
||||
PRINTDBIT(PHONY);
|
||||
switch (tbit) {
|
||||
#define PRINTBIT(bit, attr) case bit: debug_printf(" " attr); break
|
||||
#define PRINTDBIT(bit, attr) case bit: DEBUG0(TARG, " " attr); break
|
||||
PRINTBIT(OP_OPTIONAL, ".OPTIONAL");
|
||||
PRINTBIT(OP_USE, ".USE");
|
||||
PRINTBIT(OP_EXEC, ".EXEC");
|
||||
PRINTBIT(OP_IGNORE, ".IGNORE");
|
||||
PRINTBIT(OP_PRECIOUS, ".PRECIOUS");
|
||||
PRINTBIT(OP_SILENT, ".SILENT");
|
||||
PRINTBIT(OP_MAKE, ".MAKE");
|
||||
PRINTBIT(OP_JOIN, ".JOIN");
|
||||
PRINTBIT(OP_INVISIBLE, ".INVISIBLE");
|
||||
PRINTBIT(OP_NOTMAIN, ".NOTMAIN");
|
||||
PRINTDBIT(OP_LIB, ".LIB");
|
||||
PRINTDBIT(OP_MEMBER, ".MEMBER");
|
||||
PRINTDBIT(OP_ARCHV, ".ARCHV");
|
||||
PRINTDBIT(OP_MADE, ".MADE");
|
||||
PRINTDBIT(OP_PHONY, ".PHONY");
|
||||
#undef PRINTBIT
|
||||
#undef PRINTDBIT
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
made_name(GNodeMade made)
|
||||
{
|
||||
switch (made) {
|
||||
case UNMADE: return "unmade";
|
||||
case DEFERRED: return "deferred";
|
||||
case REQUESTED: return "requested";
|
||||
case BEINGMADE: return "being made";
|
||||
case MADE: return "made";
|
||||
case UPTODATE: return "up-to-date";
|
||||
case ERROR: return "error when made";
|
||||
case ABORTED: return "aborted";
|
||||
default: return "unknown enum_made value";
|
||||
}
|
||||
switch (made) {
|
||||
case UNMADE: return "unmade";
|
||||
case DEFERRED: return "deferred";
|
||||
case REQUESTED: return "requested";
|
||||
case BEINGMADE: return "being made";
|
||||
case MADE: return "made";
|
||||
case UPTODATE: return "up-to-date";
|
||||
case ERROR: return "error when made";
|
||||
case ABORTED: return "aborted";
|
||||
default: return "unknown enum_made value";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *
|
||||
GNode_OpName(const GNode *gn)
|
||||
{
|
||||
switch (gn->type & OP_OPMASK) {
|
||||
case OP_DEPENDS:
|
||||
return ":";
|
||||
case OP_FORCE:
|
||||
return "!";
|
||||
case OP_DOUBLEDEP:
|
||||
return "::";
|
||||
}
|
||||
return "";
|
||||
switch (gn->type & OP_OPMASK) {
|
||||
case OP_DEPENDS:
|
||||
return ":";
|
||||
case OP_FORCE:
|
||||
return "!";
|
||||
case OP_DOUBLEDEP:
|
||||
return "::";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Print the contents of a node. */
|
||||
void
|
||||
Targ_PrintNode(GNode *gn, int pass)
|
||||
{
|
||||
debug_printf("# %s%s", gn->name, gn->cohort_num);
|
||||
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
|
||||
if (gn->flags == 0)
|
||||
return;
|
||||
debug_printf("# %s%s", gn->name, gn->cohort_num);
|
||||
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
|
||||
if (gn->flags == 0)
|
||||
return;
|
||||
|
||||
if (!GNode_IsTarget(gn))
|
||||
return;
|
||||
|
||||
if (GNode_IsTarget(gn)) {
|
||||
debug_printf("#\n");
|
||||
if (gn == mainTarg) {
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
}
|
||||
if (gn == mainTarg)
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
|
||||
if (pass >= 2) {
|
||||
if (gn->unmade > 0) {
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
} else {
|
||||
debug_printf("# No unmade children\n");
|
||||
}
|
||||
if (!(gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
|
||||
if (gn->mtime != 0) {
|
||||
debug_printf("# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
made_name(gn->made));
|
||||
} else if (gn->made != UNMADE) {
|
||||
debug_printf("# non-existent (maybe): %s\n",
|
||||
made_name(gn->made));
|
||||
} else {
|
||||
debug_printf("# unmade\n");
|
||||
if (gn->unmade > 0)
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
else
|
||||
debug_printf("# No unmade children\n");
|
||||
if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) {
|
||||
if (gn->mtime != 0) {
|
||||
debug_printf("# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
made_name(gn->made));
|
||||
} else if (gn->made != UNMADE) {
|
||||
debug_printf("# nonexistent (maybe): %s\n",
|
||||
made_name(gn->made));
|
||||
} else
|
||||
debug_printf("# unmade\n");
|
||||
}
|
||||
}
|
||||
PrintNodeNamesLine("implicit parents", gn->implicitParents);
|
||||
PrintNodeNamesLine("implicit parents", &gn->implicitParents);
|
||||
} else {
|
||||
if (gn->unmade)
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
if (gn->unmade != 0)
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
}
|
||||
PrintNodeNamesLine("parents", gn->parents);
|
||||
PrintNodeNamesLine("order_pred", gn->order_pred);
|
||||
PrintNodeNamesLine("order_succ", gn->order_succ);
|
||||
|
||||
PrintNodeNamesLine("parents", &gn->parents);
|
||||
PrintNodeNamesLine("order_pred", &gn->order_pred);
|
||||
PrintNodeNamesLine("order_succ", &gn->order_succ);
|
||||
|
||||
debug_printf("%-16s%s", gn->name, GNode_OpName(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
PrintNodeNames(gn->children);
|
||||
PrintNodeNames(&gn->children);
|
||||
debug_printf("\n");
|
||||
Targ_PrintCmds(gn);
|
||||
debug_printf("\n\n");
|
||||
if (gn->type & OP_DOUBLEDEP) {
|
||||
Targ_PrintNodes(gn->cohorts, pass);
|
||||
}
|
||||
}
|
||||
if (gn->type & OP_DOUBLEDEP)
|
||||
Targ_PrintNodes(&gn->cohorts, pass);
|
||||
}
|
||||
|
||||
void
|
||||
Targ_PrintNodes(GNodeList *gnodes, int pass)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next)
|
||||
Targ_PrintNode(ln->datum, pass);
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next)
|
||||
Targ_PrintNode(ln->datum, pass);
|
||||
}
|
||||
|
||||
/* Print only those targets that are just a source. */
|
||||
static void
|
||||
PrintOnlySources(void)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
if (GNode_IsTarget(gn))
|
||||
continue;
|
||||
for (ln = allTargets.first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
if (GNode_IsTarget(gn))
|
||||
continue;
|
||||
|
||||
debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
debug_printf("\n");
|
||||
}
|
||||
debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
debug_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Input:
|
||||
/*
|
||||
* Input:
|
||||
* pass 1 => before processing
|
||||
* 2 => after processing
|
||||
* 3 => after processing, an error occurred
|
||||
@ -551,49 +567,51 @@ PrintOnlySources(void)
|
||||
void
|
||||
Targ_PrintGraph(int pass)
|
||||
{
|
||||
debug_printf("#*** Input graph:\n");
|
||||
Targ_PrintNodes(allTargets, pass);
|
||||
debug_printf("\n");
|
||||
debug_printf("\n");
|
||||
debug_printf("#*** Input graph:\n");
|
||||
Targ_PrintNodes(&allTargets, pass);
|
||||
debug_printf("\n");
|
||||
debug_printf("\n");
|
||||
|
||||
debug_printf("#\n");
|
||||
debug_printf("# Files that are only sources:\n");
|
||||
PrintOnlySources();
|
||||
debug_printf("#\n");
|
||||
debug_printf("# Files that are only sources:\n");
|
||||
PrintOnlySources();
|
||||
|
||||
debug_printf("#*** Global Variables:\n");
|
||||
Var_Dump(VAR_GLOBAL);
|
||||
debug_printf("#*** Global Variables:\n");
|
||||
Var_Dump(VAR_GLOBAL);
|
||||
|
||||
debug_printf("#*** Command-line Variables:\n");
|
||||
Var_Dump(VAR_CMDLINE);
|
||||
debug_printf("#*** Command-line Variables:\n");
|
||||
Var_Dump(VAR_CMDLINE);
|
||||
|
||||
debug_printf("\n");
|
||||
Dir_PrintDirectories();
|
||||
debug_printf("\n");
|
||||
debug_printf("\n");
|
||||
Dir_PrintDirectories();
|
||||
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).
|
||||
*
|
||||
* Should be called after the makefiles are parsed but before any action is
|
||||
* taken. */
|
||||
* taken.
|
||||
*/
|
||||
void
|
||||
Targ_Propagate(void)
|
||||
{
|
||||
GNodeListNode *ln, *cln;
|
||||
GNodeListNode *ln, *cln;
|
||||
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
GNodeType type = gn->type;
|
||||
for (ln = allTargets.first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
GNodeType type = gn->type;
|
||||
|
||||
if (!(type & OP_DOUBLEDEP))
|
||||
continue;
|
||||
if (!(type & OP_DOUBLEDEP))
|
||||
continue;
|
||||
|
||||
for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) {
|
||||
GNode *cohort = cln->datum;
|
||||
for (cln = gn->cohorts.first; cln != NULL; cln = cln->next) {
|
||||
GNode *cohort = cln->datum;
|
||||
|
||||
cohort->type |= type & ~OP_OPMASK;
|
||||
cohort->type |= type & ~OP_OPMASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
trace.c
16
trace.c
@ -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.
|
||||
@ -48,7 +48,7 @@
|
||||
#include "job.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 pid_t trpid;
|
||||
@ -67,11 +67,12 @@ void
|
||||
Trace_Init(const char *pathname)
|
||||
{
|
||||
if (pathname != NULL) {
|
||||
void *dontFreeIt;
|
||||
FStr curDir;
|
||||
trpid = getpid();
|
||||
/* XXX: This variable may get overwritten later, which
|
||||
* 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");
|
||||
}
|
||||
@ -92,8 +93,11 @@ Trace_Log(TrEvent event, Job *job)
|
||||
jobTokensRunning,
|
||||
evname[event], trpid, trwd);
|
||||
if (job != NULL) {
|
||||
fprintf(trfile, " %s %d %x %x", job->node->name,
|
||||
job->pid, job->flags, job->node->type);
|
||||
char flags[4];
|
||||
|
||||
Job_FlagsToString(job, flags, sizeof flags);
|
||||
fprintf(trfile, " %s %d %s %x", job->node->name,
|
||||
job->pid, flags, job->node->type);
|
||||
}
|
||||
fputc('\n', trfile);
|
||||
fflush(trfile);
|
||||
|
6
trace.h
6
trace.h
@ -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.
|
||||
@ -34,6 +34,9 @@
|
||||
* Definitions pertaining to the tracing of jobs in parallel mode.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_TRACE_H
|
||||
#define MAKE_TRACE_H
|
||||
|
||||
typedef enum TrEvent {
|
||||
MAKESTART,
|
||||
MAKEEND,
|
||||
@ -47,3 +50,4 @@ void Trace_Init(const char *);
|
||||
void Trace_Log(TrEvent, Job *);
|
||||
void Trace_End(void);
|
||||
|
||||
#endif
|
||||
|
@ -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)
|
||||
#
|
||||
@ -30,18 +30,24 @@
|
||||
# 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.
|
||||
# Keep the list sorted.
|
||||
# Any test that is commented out must be ignored in
|
||||
# src/tests/usr.bin/make/t_make.sh as well.
|
||||
#TESTS+= archive
|
||||
TESTS+= archive-suffix
|
||||
#TESTS+= archive-suffix
|
||||
TESTS+= cmd-errors
|
||||
TESTS+= cmd-errors-jobs
|
||||
TESTS+= cmd-errors-lint
|
||||
TESTS+= cmd-interrupt
|
||||
TESTS+= cmdline
|
||||
TESTS+= cmdline-undefined
|
||||
TESTS+= comment
|
||||
TESTS+= compat-error
|
||||
TESTS+= cond-cmp-numeric
|
||||
TESTS+= cond-cmp-numeric-eq
|
||||
TESTS+= cond-cmp-numeric-ge
|
||||
@ -51,12 +57,14 @@ TESTS+= cond-cmp-numeric-lt
|
||||
TESTS+= cond-cmp-numeric-ne
|
||||
TESTS+= cond-cmp-string
|
||||
TESTS+= cond-cmp-unary
|
||||
TESTS+= cond-eof
|
||||
TESTS+= cond-func
|
||||
TESTS+= cond-func-commands
|
||||
TESTS+= cond-func-defined
|
||||
TESTS+= cond-func-empty
|
||||
TESTS+= cond-func-exists
|
||||
TESTS+= cond-func-make
|
||||
TESTS+= cond-func-make-main
|
||||
TESTS+= cond-func-target
|
||||
TESTS+= cond-late
|
||||
TESTS+= cond-op
|
||||
@ -107,9 +115,14 @@ TESTS+= depsrc-usebefore-double-colon
|
||||
TESTS+= depsrc-wait
|
||||
TESTS+= deptgt
|
||||
TESTS+= deptgt-begin
|
||||
TESTS+= deptgt-begin-fail
|
||||
TESTS+= deptgt-begin-fail-indirect
|
||||
TESTS+= deptgt-default
|
||||
TESTS+= deptgt-delete_on_error
|
||||
TESTS+= deptgt-end
|
||||
TESTS+= deptgt-end-fail
|
||||
TESTS+= deptgt-end-fail-all
|
||||
TESTS+= deptgt-end-fail-indirect
|
||||
TESTS+= deptgt-end-jobs
|
||||
TESTS+= deptgt-error
|
||||
TESTS+= deptgt-ignore
|
||||
@ -139,14 +152,20 @@ TESTS+= directive-elifmake
|
||||
TESTS+= directive-elifndef
|
||||
TESTS+= directive-elifnmake
|
||||
TESTS+= directive-else
|
||||
TESTS+= directive-endfor
|
||||
TESTS+= directive-endif
|
||||
TESTS+= directive-error
|
||||
TESTS+= directive-export
|
||||
TESTS+= directive-export-env
|
||||
TESTS+= directive-export-impl
|
||||
TESTS+= directive-export-gmake
|
||||
TESTS+= directive-export-literal
|
||||
TESTS+= directive-for
|
||||
TESTS+= directive-for-errors
|
||||
TESTS+= directive-for-escape
|
||||
TESTS+= directive-for-generating-endif
|
||||
TESTS+= directive-for-lines
|
||||
TESTS+= directive-for-null
|
||||
TESTS+= directive-hyphen-include
|
||||
TESTS+= directive-if
|
||||
TESTS+= directive-if-nested
|
||||
@ -157,6 +176,7 @@ TESTS+= directive-ifnmake
|
||||
TESTS+= directive-include
|
||||
TESTS+= directive-include-fatal
|
||||
TESTS+= directive-info
|
||||
TESTS+= directive-misspellings
|
||||
TESTS+= directive-sinclude
|
||||
TESTS+= directive-undef
|
||||
TESTS+= directive-unexport
|
||||
@ -180,14 +200,20 @@ TESTS+= impsrc
|
||||
TESTS+= include-main
|
||||
TESTS+= job-flags
|
||||
#TESTS+= job-output-long-lines
|
||||
TESTS+= jobs-error-indirect
|
||||
TESTS+= jobs-error-nested
|
||||
TESTS+= jobs-error-nested-make
|
||||
TESTS+= lint
|
||||
TESTS+= make-exported
|
||||
TESTS+= meta-cmd-cmp
|
||||
TESTS+= moderrs
|
||||
TESTS+= modmatch
|
||||
TESTS+= modmisc
|
||||
TESTS+= modts
|
||||
TESTS+= modword
|
||||
.if ${.MAKE.UID} > 0
|
||||
TESTS+= objdir-writable
|
||||
.endif
|
||||
TESTS+= opt
|
||||
TESTS+= opt-backwards
|
||||
TESTS+= opt-chdir
|
||||
@ -223,10 +249,13 @@ TESTS+= opt-ignore
|
||||
TESTS+= opt-include-dir
|
||||
TESTS+= opt-jobs
|
||||
TESTS+= opt-jobs-internal
|
||||
TESTS+= opt-jobs-no-action
|
||||
TESTS+= opt-keep-going
|
||||
TESTS+= opt-keep-going-multiple
|
||||
TESTS+= opt-m-include-dir
|
||||
TESTS+= opt-no-action
|
||||
TESTS+= opt-no-action-at-all
|
||||
TESTS+= opt-no-action-runflags
|
||||
TESTS+= opt-query
|
||||
TESTS+= opt-raw
|
||||
TESTS+= opt-silent
|
||||
@ -243,10 +272,11 @@ TESTS+= parse-var
|
||||
TESTS+= phony-end
|
||||
TESTS+= posix
|
||||
TESTS+= # posix1 # broken by reverting POSIX changes
|
||||
TESTS+= qequals
|
||||
TESTS+= recursive
|
||||
TESTS+= sh
|
||||
TESTS+= sh-dots
|
||||
TESTS+= sh-errctl
|
||||
TESTS+= sh-flags
|
||||
TESTS+= sh-jobs
|
||||
TESTS+= sh-jobs-error
|
||||
TESTS+= sh-leading-at
|
||||
@ -264,10 +294,14 @@ TESTS+= shell-sh
|
||||
TESTS+= suff-add-later
|
||||
TESTS+= suff-clear-regular
|
||||
TESTS+= suff-clear-single
|
||||
TESTS+= suff-incomplete
|
||||
TESTS+= suff-lookup
|
||||
TESTS+= suff-main
|
||||
TESTS+= suff-main-several
|
||||
TESTS+= suff-phony
|
||||
TESTS+= suff-rebuild
|
||||
TESTS+= suff-self
|
||||
TESTS+= suff-transform-debug
|
||||
TESTS+= suff-transform-endless
|
||||
TESTS+= suff-transform-expand
|
||||
TESTS+= suff-transform-select
|
||||
@ -304,6 +338,7 @@ TESTS+= varmod-gmtime
|
||||
TESTS+= varmod-hash
|
||||
TESTS+= varmod-head
|
||||
TESTS+= varmod-ifelse
|
||||
TESTS+= varmod-indirect
|
||||
TESTS+= varmod-l-name-to-value
|
||||
TESTS+= varmod-localtime
|
||||
TESTS+= varmod-loop
|
||||
@ -361,6 +396,7 @@ TESTS+= varname-dot-make-path_filemon
|
||||
TESTS+= varname-dot-make-pid
|
||||
TESTS+= varname-dot-make-ppid
|
||||
TESTS+= varname-dot-make-save_dollars
|
||||
TESTS+= varname-dot-makeflags
|
||||
TESTS+= varname-dot-makeoverrides
|
||||
TESTS+= varname-dot-newline
|
||||
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.
|
||||
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
|
||||
# settings FLAGS.test=-dv here, since that is closer to the test code.
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none, especially not -k
|
||||
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none, especially not -k
|
||||
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.
|
||||
SED_CMDS.export= \
|
||||
-e '/^[^=_A-Za-z0-9]*=/d'
|
||||
# these all share the same requirement
|
||||
.for t in export-all export-env
|
||||
SED_CMDS.$t= ${SED_CMDS.export}
|
||||
.endfor
|
||||
SED_CMDS.directive-export-gmake= \
|
||||
${:D dash is a pain } \
|
||||
-e /non-zero/d
|
||||
SED_CMDS.dir= ${:D remove output from -DCLEANUP mode }
|
||||
SED_CMDS.dir+= -e '/^OpenDirs_Done:/d'
|
||||
SED_CMDS.dir+= -e '/^CachedDir /d'
|
||||
SED_CMDS.export= -e '/^[^=_A-Za-z0-9]*=/d'
|
||||
SED_CMDS.export-all= ${SED_CMDS.export}
|
||||
SED_CMDS.export-env= ${SED_CMDS.export}
|
||||
SED_CMDS.cmdline= -e 's,uid${.MAKE.UID}/,,'
|
||||
SED_CMDS.job-output-long-lines= \
|
||||
${:D Job separators on their own line are ok. } \
|
||||
-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. } \
|
||||
-e '/^aa*--- job-b ---$$/d' \
|
||||
-e '/^bb*--- job-a ---$$/d'
|
||||
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
|
||||
SED_CMDS.opt-debug-graph1= \
|
||||
-e 's,${.CURDIR},CURDIR,'
|
||||
SED_CMDS.opt-debug-graph1+= \
|
||||
-e '/Global Variables:/,/Suffixes:/d'
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
|
||||
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
|
||||
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
|
||||
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
|
||||
# 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.var-op-shell+= -e 's,^${.SHELL:T}: ,,'
|
||||
SED_CMDS.var-op-shell+= -e '/command/{ s,^[1-9]: ,,;s,No such.*,not found,; }'
|
||||
SED_CMDS.vardebug= \
|
||||
${:D canonicalize .SHELL } \
|
||||
-e 's,${.SHELL},</path/to/shell>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
|
||||
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
|
||||
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
|
||||
# For Compat_RunCommand, useShell == FALSE.
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
|
||||
# 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+= \
|
||||
-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-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
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'
|
||||
|
||||
# Some tests need an additional round of postprocessing.
|
||||
POSTPROC.deptgt-suffixes= \
|
||||
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
|
||||
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
|
||||
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
|
||||
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
|
||||
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
|
||||
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
|
||||
|
||||
# Some tests reuse other tests, which makes them unnecessarily fragile.
|
||||
export-all.rawout: export.mk
|
||||
@ -487,6 +531,35 @@ unexport-env.rawout: export.mk
|
||||
|
||||
# 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
|
||||
|
||||
.-include "Makefile.inc"
|
||||
@ -532,6 +605,11 @@ _MKMSG_TEST= :
|
||||
|
||||
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 other, and because they use different environment variables and
|
||||
# command line options.
|
||||
@ -539,6 +617,7 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
|
||||
.mk.rawout:
|
||||
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
|
||||
@set -eu; \
|
||||
${LIMIT_RESOURCES}; \
|
||||
cd ${.OBJDIR}; \
|
||||
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
|
||||
${TEST_MAKE} \
|
||||
@ -561,6 +640,10 @@ _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
|
||||
# strip ${.CURDIR}/ from the output
|
||||
_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
|
||||
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
|
||||
# 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:
|
||||
@${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
|
||||
@mv ${.TARGET}.tmp2 ${.TARGET}
|
||||
|
||||
.if empty(DIFF_FLAGS)
|
||||
DIFF_ECHO= echo
|
||||
.else
|
||||
DIFF_ECHO= :
|
||||
.endif
|
||||
|
||||
# Compare all output files
|
||||
test: ${OUTFILES} .PHONY
|
||||
@failed= ; \
|
||||
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 \
|
||||
|| failed="$${failed}$${failed:+ }$${test}" ; \
|
||||
done ; \
|
||||
|
9
unit-tests/cmd-errors-jobs.exp
Normal file
9
unit-tests/cmd-errors-jobs.exp
Normal 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
|
32
unit-tests/cmd-errors-jobs.mk
Normal file
32
unit-tests/cmd-errors-jobs.mk
Normal 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.
|
@ -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
|
||||
# are actually executed.
|
||||
# are actually executed in compat mode.
|
||||
|
||||
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# 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
|
||||
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
|
||||
MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r
|
||||
|
15
unit-tests/compat-error.exp
Normal file
15
unit-tests/compat-error.exp
Normal 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
|
37
unit-tests/compat-error.mk
Normal file
37
unit-tests/compat-error.mk
Normal 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}'>'
|
9
unit-tests/cond-eof.exp
Normal file
9
unit-tests/cond-eof.exp
Normal 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
|
20
unit-tests/cond-eof.mk
Normal file
20
unit-tests/cond-eof.mk
Normal 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
|
@ -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
|
||||
# expression for emptiness.
|
||||
@ -155,5 +155,30 @@ ${:U WORD }= variable name with spaces
|
||||
. error
|
||||
.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:
|
||||
@:;
|
||||
|
@ -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.
|
||||
|
||||
@ -38,5 +38,14 @@
|
||||
. error
|
||||
.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:
|
||||
@:;
|
||||
|
3
unit-tests/cond-func-make-main.exp
Normal file
3
unit-tests/cond-func-make-main.exp
Normal file
@ -0,0 +1,3 @@
|
||||
: Making dot-main-target-1a.
|
||||
: Making dot-main-target-1b.
|
||||
exit status 0
|
62
unit-tests/cond-func-make-main.mk
Normal file
62
unit-tests/cond-func-make-main.mk
Normal 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
|
@ -7,10 +7,10 @@ expected M pattern
|
||||
expected or
|
||||
expected or exists
|
||||
expected or empty
|
||||
defined(V42) && 42 > 0: Ok
|
||||
defined(V66) && ( "" < 42 ): Ok
|
||||
1 || 42 < 42: Ok
|
||||
1 || < 42: Ok
|
||||
0 || 42 <= 42: Ok
|
||||
0 || < 42: Ok
|
||||
defined(V42) && ${V42} > 0: Ok
|
||||
defined(V66) && ( "${iV2}" < ${V42} ): Ok
|
||||
1 || ${iV1} < ${V42}: Ok
|
||||
1 || ${iV2:U2} < ${V42}: Ok
|
||||
0 || ${iV1} <= ${V42}: Ok
|
||||
0 || ${iV2:U2} < ${V42}: Ok
|
||||
exit status 0
|
||||
|
@ -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 ||
|
||||
# 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
|
||||
# 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 ||
|
||||
# operator was always evaluated, which was wrong.
|
||||
# TODO: Had the evaluation been correct at some time before 2015-11-12?
|
||||
# Before 2020-06-28, the right-hand side of an && or || operator was always
|
||||
# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
|
||||
# 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.
|
||||
|
||||
@ -128,33 +132,56 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
|
||||
x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
|
||||
|
||||
# this one throws both String comparison operator and
|
||||
# Malformed conditional with cond.c 1.78
|
||||
# indirect iV2 would expand to "" and treated as 0
|
||||
# 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 defined(V66) && ( ${iV2} < ${V42} )
|
||||
x= Fail
|
||||
.else
|
||||
x= Ok
|
||||
.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}
|
||||
x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.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}
|
||||
x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo
|
||||
|
||||
# the same expressions are fine when the lhs is expanded
|
||||
# ${iV1} expands to 42
|
||||
@ -163,7 +190,7 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
|
||||
x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
|
||||
|
||||
# ${iV2:U2} expands to 2
|
||||
.if 0 || ${iV2:U2} < ${V42}
|
||||
@ -171,11 +198,12 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.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
|
||||
# 2000, when make would complain about the "Malformed conditional" because
|
||||
# UNDEF is not defined.
|
||||
# The right-hand side of the '&&' is irrelevant since the left-hand side
|
||||
# already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
|
||||
# expanded nevertheless, although with a small modification: undefined
|
||||
# variables may be used in these expressions without generating an error.
|
||||
.if defined(UNDEF) && ${UNDEF} != "undefined"
|
||||
. error
|
||||
.endif
|
||||
|
@ -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 18: xvalue is not defined.
|
||||
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")
|
||||
|
@ -1,3 +1,6 @@
|
||||
make: don't know how to make dep-percent.o (continuing)
|
||||
`all' not remade because of errors.
|
||||
exit status 0
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
# 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:
|
||||
@:;
|
||||
@${MAKE} -f ${MAKEFILE} actual-test
|
||||
@${MAKE} -f ${MAKEFILE} check-results
|
||||
|
||||
.endif
|
||||
|
@ -5,16 +5,16 @@ ExamineLater: need to examine "optional"
|
||||
ExamineLater: need to examine "optional-cohort"
|
||||
Make_ExpandUse: examine optional
|
||||
Make_ExpandUse: examine optional-cohort
|
||||
Examining optional...non-existent...up-to-date.
|
||||
Examining optional-cohort...non-existent...:: operator and no sources...out-of-date.
|
||||
Examining optional...nonexistent...up-to-date.
|
||||
Examining optional-cohort...nonexistent...:: operator and no sources...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
|
||||
Examining important...non-existent...modified before source "optional-cohort"...out-of-date.
|
||||
recheck(optional-cohort): update time from nonexistent to now
|
||||
Examining important...nonexistent...modified before source "optional-cohort"...out-of-date.
|
||||
: important is made.
|
||||
recheck(important): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining all...non-existent...modified before source "important"...out-of-date.
|
||||
recheck(important): update time from nonexistent to now
|
||||
Examining all...nonexistent...modified before source "important"...out-of-date.
|
||||
: all is made.
|
||||
recheck(all): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining .END...non-existent...non-existent and no sources...out-of-date.
|
||||
recheck(.END): update time from 0:00:00 Jan 01, 1970 to now
|
||||
recheck(all): update time from nonexistent to now
|
||||
Examining .END...nonexistent...nonexistent and no sources...out-of-date.
|
||||
recheck(.END): update time from nonexistent to now
|
||||
exit status 0
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
# uppercase letters) in dependency declarations, such as .PHONY.
|
||||
@ -7,5 +7,20 @@
|
||||
|
||||
# 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:
|
||||
@:;
|
||||
|
6
unit-tests/deptgt-begin-fail-indirect.exp
Normal file
6
unit-tests/deptgt-begin-fail-indirect.exp
Normal file
@ -0,0 +1,6 @@
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
16
unit-tests/deptgt-begin-fail-indirect.mk
Normal file
16
unit-tests/deptgt-begin-fail-indirect.mk
Normal 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.
|
6
unit-tests/deptgt-begin-fail.exp
Normal file
6
unit-tests/deptgt-begin-fail.exp
Normal file
@ -0,0 +1,6 @@
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
10
unit-tests/deptgt-begin-fail.mk
Normal file
10
unit-tests/deptgt-begin-fail.mk
Normal 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.
|
7
unit-tests/deptgt-end-fail-all.exp
Normal file
7
unit-tests/deptgt-end-fail-all.exp
Normal file
@ -0,0 +1,7 @@
|
||||
: Making all out of nothing.
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
19
unit-tests/deptgt-end-fail-all.mk
Normal file
19
unit-tests/deptgt-end-fail-all.mk
Normal 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
|
7
unit-tests/deptgt-end-fail-indirect.exp
Normal file
7
unit-tests/deptgt-end-fail-indirect.exp
Normal file
@ -0,0 +1,7 @@
|
||||
: all
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
16
unit-tests/deptgt-end-fail-indirect.mk
Normal file
16
unit-tests/deptgt-end-fail-indirect.mk
Normal 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
|
163
unit-tests/deptgt-end-fail.exp
Normal file
163
unit-tests/deptgt-end-fail.exp
Normal 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
|
69
unit-tests/deptgt-end-fail.mk
Normal file
69
unit-tests/deptgt-end-fail.mk
Normal 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.
|
@ -3,5 +3,31 @@
|
||||
# To:
|
||||
# From:
|
||||
# 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:
|
||||
.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
|
||||
|
@ -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.
|
||||
#
|
||||
@ -8,11 +8,28 @@
|
||||
|
||||
.MAKEFLAGS: -dg1
|
||||
|
||||
.MAIN: all
|
||||
|
||||
.SUFFIXES: .custom-null
|
||||
|
||||
# TODO: What is the effect of this? How is it useful?
|
||||
.NULL: .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
|
||||
|
@ -8,6 +8,7 @@ ParseDoDependency(: empty-source)
|
||||
ParseReadLine (37): ' : command for empty targets list'
|
||||
ParseReadLine (38): '.MAKEFLAGS: -d0'
|
||||
ParseDoDependency(.MAKEFLAGS: -d0)
|
||||
make: "deptgt.mk" line 46: Unknown modifier 'Z'
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -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
|
||||
# declarations.
|
||||
@ -37,5 +37,13 @@ ${:U}: empty-source
|
||||
: command for empty targets list
|
||||
.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:
|
||||
@:;
|
||||
|
@ -1,17 +1,21 @@
|
||||
make: "directive-elif.mk" line 7: begin .elif misspellings tests, part 1
|
||||
make: "directive-elif.mk" line 9: 1-then
|
||||
make: "directive-elif.mk" line 18: begin .elif misspellings tests, part 2
|
||||
make: "directive-elif.mk" line 29: begin .elif misspellings tests, part 3
|
||||
make: "directive-elif.mk" line 41: which branch is taken on misspelling after false?
|
||||
make: "directive-elif.mk" line 49: else
|
||||
make: "directive-elif.mk" line 52: which branch is taken on misspelling after true?
|
||||
make: "directive-elif.mk" line 54: 1-then
|
||||
make: "directive-elif.mk" line 55: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 56: 1-elsif
|
||||
make: "directive-elif.mk" line 57: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 58: 2-elsif
|
||||
make: "directive-elif.mk" line 64: if-less elif
|
||||
make: "directive-elif.mk" line 69: warning: extra elif
|
||||
make: "directive-elif.mk" line 47: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 52: This branch is taken.
|
||||
make: "directive-elif.mk" line 60: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 63: This branch is taken.
|
||||
make: "directive-elif.mk" line 69: This branch is taken.
|
||||
make: "directive-elif.mk" line 89: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 90: This misspelling is detected.
|
||||
make: "directive-elif.mk" line 91: This branch is taken because of the .else.
|
||||
make: "directive-elif.mk" line 109: What happens on misspelling in a skipped branch?
|
||||
make: "directive-elif.mk" line 119: else
|
||||
make: "directive-elif.mk" line 122: What happens on misspelling in a taken branch?
|
||||
make: "directive-elif.mk" line 124: 1-then
|
||||
make: "directive-elif.mk" line 125: Unknown directive "elsif"
|
||||
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: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -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.
|
||||
#
|
||||
# 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
|
||||
|
||||
.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
|
||||
. info 1-then
|
||||
.elif 1 # ok
|
||||
. info 1-elif
|
||||
.elsif 1 # oops: misspelled
|
||||
. info 1-elsif
|
||||
.elseif 1 # oops: misspelled
|
||||
. info 1-elseif
|
||||
# This misspelling is in an active branch and is therefore detected.
|
||||
.elsif 0
|
||||
# The only thing that make detects here is a misspelled directive, make
|
||||
# doesn't recognize that it was meant to be a conditional directive.
|
||||
# Therefore the branch continues here, even though the '.elsif' condition
|
||||
# evaluates to false.
|
||||
. info This branch is taken.
|
||||
.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
|
||||
. 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
|
||||
. info 0-elif
|
||||
.elsif 0 # oops: misspelled
|
||||
. info 0-elsif
|
||||
.elseif 0 # oops: misspelled
|
||||
. info 0-elseif
|
||||
. info This branch is not taken.
|
||||
.elsif 0
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info This branch is not taken.
|
||||
.elseif 0
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info This branch is not taken.
|
||||
.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
|
||||
. info 0-then
|
||||
.elsif 1
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info 1-elsif
|
||||
.elsif 2
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info 2-elsif
|
||||
.else
|
||||
. info else
|
||||
.endif
|
||||
|
||||
.info which branch is taken on misspelling after true?
|
||||
.info What happens on misspelling in a taken branch?
|
||||
.if 1
|
||||
. info 1-then
|
||||
.elsif 1
|
||||
@ -65,7 +135,7 @@
|
||||
|
||||
.if 1
|
||||
.else
|
||||
# Expect: "warning: if-less elif"
|
||||
# Expect: "warning: extra elif"
|
||||
.elif
|
||||
.endif
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
make: "directive-else.mk" line 11: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 12: ok
|
||||
make: "directive-else.mk" line 16: ok
|
||||
make: "directive-else.mk" line 17: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 22: if-less else
|
||||
make: "directive-else.mk" line 28: ok
|
||||
make: "directive-else.mk" line 29: warning: extra else
|
||||
make: "directive-else.mk" line 42: 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 15: ok
|
||||
make: "directive-else.mk" line 19: ok
|
||||
make: "directive-else.mk" line 21: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 26: if-less else
|
||||
make: "directive-else.mk" line 32: ok
|
||||
make: "directive-else.mk" line 33: warning: extra else
|
||||
make: "directive-else.mk" line 45: The .else directive does not take arguments.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -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.
|
||||
#
|
||||
# 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
|
||||
. warning must not be reached
|
||||
# The .else directive does not take any arguments.
|
||||
.else 123
|
||||
. info ok
|
||||
.endif
|
||||
|
||||
.if 1
|
||||
. info ok
|
||||
# The .else directive does not take any arguments.
|
||||
.else 123
|
||||
. warning must not be reached
|
||||
.endif
|
||||
@ -37,7 +41,6 @@
|
||||
.endif
|
||||
|
||||
# A variable expression does count as an argument, even if it is empty.
|
||||
# XXX: This should be a parse error.
|
||||
.if 0
|
||||
.else ${:U}
|
||||
.endif
|
||||
|
4
unit-tests/directive-endfor.exp
Normal file
4
unit-tests/directive-endfor.exp
Normal 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
|
9
unit-tests/directive-endfor.mk
Normal file
9
unit-tests/directive-endfor.mk
Normal 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
|
@ -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
|
||||
|
@ -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.
|
||||
#
|
||||
# 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:
|
||||
# Cond_EvalLine
|
||||
|
||||
@ -10,18 +13,37 @@
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
# Error: .endif does not take arguments
|
||||
# XXX: Missing error message
|
||||
.if 0
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif 0
|
||||
|
||||
# Error: .endif does not take arguments
|
||||
# XXX: Missing error message
|
||||
.if 1
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif 1
|
||||
|
||||
# Comments are allowed after an '.endif'.
|
||||
.if 2
|
||||
.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:
|
||||
@:;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user