Update to bmake-20201101
Lots of new unit-tests increase code coverage. Lots of refactoring, cleanup and simlpification to reduce code size. Fixes for Bug 223564 and 245807 Updates to dirdeps.mk and meta2deps.py
This commit is contained in:
commit
956e45f6fb
@ -1,3 +1,267 @@
|
||||
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201101
|
||||
Merge with NetBSD make, pick up
|
||||
o negate NoExecute to GNode_ShouldExecute
|
||||
o job.c: rename JobMatchShell to FindShellByName
|
||||
extract EscapeShellDblQuot from JobPrintCommand
|
||||
extract ParseRunOptions from JobPrintCommand
|
||||
o var.c: extract ApplyModifiersIndirect from ApplyModifiers
|
||||
treat malformed :range, :ts and :[...] as errors
|
||||
add tests for the variable modifiers :[words] and :range
|
||||
|
||||
2020-10-31 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201031
|
||||
Merge with NetBSD make, pick up
|
||||
o format #include directives consistently
|
||||
o do not look up local variables like .TARGET anywhere else
|
||||
o main.c: Main_SetObjdir is first called for curdir which may be
|
||||
readonly
|
||||
reduce the scope where recursive expressions are detected
|
||||
remove redundant :tl from getBoolean
|
||||
clean up mkTempFile
|
||||
o meta.c: simplify memory allocation in meta_create and meta_oodate
|
||||
o parse.c: extract loadedfile_mmap from loadfile
|
||||
o trace.c: document possible undefined behavior with .CURDIR
|
||||
o var.c: make parsing of the :gmtime and :localtime modifiers stricter
|
||||
rename ismeta to is_shell_metachar
|
||||
remove debug logging for the :Q variable modifier
|
||||
rename VarIsDynamic to VarnameIsDynamic
|
||||
use consistent parameter order in varname parsing functions
|
||||
extract ParseVarnameLong from Var_Parse
|
||||
extract ParseVarnameShort from Var_Parse
|
||||
fix type of ParseModifierPart parameter delim
|
||||
extract IsEscapedModifierPart from ParseModifierPart
|
||||
clean up ModifyWords
|
||||
add test for combining the :@ and :? variable modifiers
|
||||
|
||||
2020-10-30 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201030
|
||||
Merge with NetBSD make, pick up
|
||||
o change char * to void * in Var_Value
|
||||
o make iterating over HashTable simpler
|
||||
o rename VAR_CMD to VAR_CMDLINE
|
||||
o cond.c: clean up is_separator
|
||||
fix parse error in string literal in conditional
|
||||
o main.c: do not use objdir that is not writable
|
||||
in lint mode, exit with error status on errors
|
||||
o parse.c: clean up StrContainsWord
|
||||
fix out-of-bounds pointer in ParseTrackInput
|
||||
o var.c: rename Str_SYSVMatch and its parameters
|
||||
remove unsatisfiable conditions in Var_Set_with_flags
|
||||
document where the variable name is expanded
|
||||
fix documentation for VARP_SUB_ONE
|
||||
rename VAR_EXPORTED_YES to VAR_EXPORTED_SOME
|
||||
document VAR_READONLY
|
||||
prevent appending to read-only variables
|
||||
extract MayExport from Var_Export1
|
||||
remove redundant evaluations in VarFind
|
||||
replace VarFindFlags with a simple Boolean
|
||||
rename FIND_CMD to FIND_CMDLINE, to match VAR_CMDLINE
|
||||
|
||||
2020-10-28 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201028
|
||||
Merge with NetBSD make, pick up
|
||||
o rename defIncPath to defSysIncPath
|
||||
o initialize all CmdOpts fields
|
||||
o lst.c: inline Vector_Get
|
||||
o main.c: refactor main extract
|
||||
InitMaxJobs,InitObjdir,InitVarMake,InitRandom,
|
||||
ReadMakefiles,CleanUp,InitVpath,ReadBuiltinRules,
|
||||
InitDefIncPath,CmdOpts_Init,UnlimitFiles
|
||||
o parse.c: merge curFile into includes
|
||||
rename predecessor to order_pred
|
||||
sort ParseSpecial alphabetically
|
||||
remove unused, undocumented .NOEXPORT
|
||||
rename ParseSpecial enum values consistently
|
||||
rename some fields of struct IFile
|
||||
|
||||
2020-10-26 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201026
|
||||
Merge with NetBSD make, pick up
|
||||
o group the command line options and arguments into a struct
|
||||
o rename GNode.cmgn to youngestChild
|
||||
o rename hash functions to identify the type name
|
||||
o negate OP_NOP and rename it to GNode_IsTarget
|
||||
o add GNode_Path to access the path of a GNode
|
||||
o remove macros MIN and MAX
|
||||
o remove unused Lst_Find and Lst_FindFrom
|
||||
o arch.c: and make Arch_FindLib simpler
|
||||
clean up code layout
|
||||
make Arch_ParseArchive simpler
|
||||
o cond.c: inline CondFindStrMatch into FuncMake
|
||||
o dir.c: replace Dir_CopyDir with Dir_CopyDirSearchPath
|
||||
omit trailing space in debug output for expanding file patterns
|
||||
refactor DirMatchFiles
|
||||
document that the SearchPath of Dir_FindFile may be NULL
|
||||
remove UNCONST from Dir_Expand
|
||||
inline DirFindName
|
||||
o for.c: clean up code for handling .for loops
|
||||
o hash.c: print hash in debug log with fixed width
|
||||
clean up hash table functions
|
||||
reduce amount of string hashing
|
||||
o job.c: refactor JobDeleteTarget
|
||||
use proper enum constants for aborting
|
||||
convert result of JobStart from macros to enum
|
||||
convert abort reason macros to enum
|
||||
rework Job_CheckCommands to reduce indentation
|
||||
rename Shell fields
|
||||
add field names in declaration of DEFSHELL_CUSTOM
|
||||
convert JobState and JobFlags to enum types
|
||||
move handling of the "..." command to JobPrintCommands
|
||||
o lst.c: clean up
|
||||
refactor LstNodeNew
|
||||
remove Lst_Open, Lst_Next, Lst_Close
|
||||
remove code for circular lists from Lst_Next
|
||||
o main.c: do not attempt to read .MAKE.DEPENFILE if set to
|
||||
/dev/null or anything starting with "no"
|
||||
convert macros for debug flags into enum
|
||||
o make.c: inline Lst_Copy in Make_ExpandUse
|
||||
o meta.c: inline Lst_Find in meta_oodate
|
||||
make Lst_RemoveIf simpler in meta_oodate
|
||||
o parse.c: convert error level for Parse_Error to an enum
|
||||
o suff.c: properly terminate debug output with newline
|
||||
add more details to DEBUG_SRC log
|
||||
replace Dir_CopyDir with Dir_CopyDirSearchPath
|
||||
don't modify GNode name while rebuilding the suffix graph
|
||||
o var.c: reduce duplicate code in VarFind
|
||||
|
||||
2020-10-22 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201022
|
||||
Merge with NetBSD make, pick up
|
||||
o more refactoring and simplification to reduce code size
|
||||
o var.c: extract CanonicalVarname from VarFind
|
||||
o make.c: extract UpdateImplicitParentsVars from Make_Update
|
||||
o main.c: extract PrintVar from doPrintVars
|
||||
extract HandlePWD from main
|
||||
o lst.c: inline simple Lst getters
|
||||
remove unused Lst_ForEach
|
||||
o job.c: move struct Shell from job.h to job.c
|
||||
o more unit tests
|
||||
|
||||
2020-10-19 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* configure.in: remove inappropriate use of AC_INCLUDES_DEFAULT
|
||||
|
||||
2020-10-18 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201018
|
||||
Merge with NetBSD make, pick up
|
||||
o remove USE_IOVEC
|
||||
o rename some Hash_* apis to Hash*
|
||||
o replace execError with execDie
|
||||
o rename Lst_Init to Lst_New
|
||||
o add tags to enum types
|
||||
o rename Stack to Vector
|
||||
o parse.c: more refactoring
|
||||
o unit-tests: make some tests use line buffered stdout
|
||||
o unit-tests/Makefile: in meta mode do not make all tests depend on
|
||||
Makefile, it isn't necessary.
|
||||
|
||||
2020-10-10 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* main.c: check for CTL_HW being defined.
|
||||
* unit-tests/Makefile: ensure export tests output are POSIX compliant
|
||||
disable opt-debug-jobs test until it works on ubuntu
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201010
|
||||
Merge with NetBSD make, pick up
|
||||
o dir.c: remove pathname limit for Dir_FindHereOrAbove
|
||||
o hash.c: replace strcpy with memcpy in Hash_CreateEntry
|
||||
o main.c: extract init_machine and init_machine_arch from main
|
||||
allow to disable debug logging options
|
||||
o parse.c: enable format string truncation warnings
|
||||
extract parsing of sources from ParseDoDependency
|
||||
split ParseDoSrc into smaller functions
|
||||
hide implementation details from Parse_DoVar
|
||||
clean up parsing of variable assignments
|
||||
split Parse_DoVar into manageable pieces
|
||||
don't modify the given line during Parse_DoVar
|
||||
fix out-of-bounds memory access in Parse_DoVar
|
||||
fix parsing of the :sh assignment modifier
|
||||
o var.c: rework memory allocation for the name of variables
|
||||
extract ApplyModifier_Literal into separate function
|
||||
in lint mode, reject modifiers without delimiter
|
||||
do not export variable names starting with '-'
|
||||
o fix double-free bug in -DCLEANUP mode
|
||||
o more cleanup to enable higher warnings level
|
||||
o more unit tests
|
||||
|
||||
2020-10-02 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201002
|
||||
Merge with NetBSD make, pick up
|
||||
o dir.c: use hash table for looking up open directories by name
|
||||
o main.c: clean up option handling
|
||||
o parse.c: add missing const for Parse_AddIncludeDir
|
||||
o var.c: ApplyModifier_To, update pp in each branch
|
||||
o remove redundant function prototypes
|
||||
o more unit tests
|
||||
|
||||
2020-10-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201001
|
||||
Merge with NetBSD make, pick up
|
||||
o compat.c: comment about "..."
|
||||
|
||||
2020-09-30 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20200930
|
||||
Merge with NetBSD make, pick up
|
||||
o job.c: split Job.jobPipe into 2 separate fields
|
||||
replace Lst_Open with direct iteration
|
||||
o lst.c: remove redundant assertions
|
||||
o targ.c: replace Lst_Open with direct iteration
|
||||
o var.c: fix bug in evaluation of indirect variable modifiers
|
||||
extract ApplyModifier_Quote into separate function
|
||||
o make debug logging simpler
|
||||
|
||||
2020-09-27 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20200927
|
||||
Merge with NetBSD make, pick up
|
||||
o parse.c: ensure parse errors result in 'stopped in' message.
|
||||
o compat.c: make parameter of Compat_RunCommand const
|
||||
o main.c: extract InitVarTarget from main
|
||||
o parse.c: rename ParseFinishLine to FinishDependencyGroup
|
||||
refactor ParseDoDependency
|
||||
o var.c: Var_Subst no longer returns string result
|
||||
rename Var_ParsePP back to Var_Parse
|
||||
in lint mode, improve error handling for undefined variables
|
||||
extract ParseVarname from Var_Parse
|
||||
o rename Lst_ForEach to Lst_ForEachUntil
|
||||
o inline Lst_ForEachUntil in several cases
|
||||
o clean up API for finding and creating GNodes
|
||||
o fix assertion failure in -j mode with .END node
|
||||
o inline and remove LstNode_Prev and LstNode_Next
|
||||
o use fine-grained type names for lists and their nodes
|
||||
o more unit tests
|
||||
|
||||
2020-09-11 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20200911
|
||||
Merge with NetBSD make, pick up
|
||||
o cond.c: split EvalComparison into smaller functions
|
||||
reorder parameters of condition parsing functions
|
||||
reduce code size in CondParser_Eval
|
||||
rename CondGetString to CondParser_String
|
||||
add CondLexer_SkipWhitespace
|
||||
group the condition parsing state into a struct
|
||||
in CondGetString, replace repeated Buf_Add with Buf_AddStr
|
||||
o migrate Var_Parse to Var_ParsePP
|
||||
o add wrappers around ctype.h functions
|
||||
o lst.c: use a stack instead of a list for the nested include path
|
||||
o more unit tests
|
||||
|
||||
2020-09-04 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* make-bootstrap.sh.in: adjust object list
|
||||
|
||||
2020-09-02 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20200902
|
||||
|
@ -65,18 +65,16 @@ sigcompat.c
|
||||
str.c
|
||||
stresep.c
|
||||
strlcpy.c
|
||||
strlist.c
|
||||
strlist.h
|
||||
suff.c
|
||||
targ.c
|
||||
trace.c
|
||||
trace.h
|
||||
unit-tests/Makefile
|
||||
unit-tests/Makefile.config.in
|
||||
unit-tests/archive.exp
|
||||
unit-tests/archive.mk
|
||||
unit-tests/archive-suffix.exp
|
||||
unit-tests/archive-suffix.mk
|
||||
unit-tests/archive.exp
|
||||
unit-tests/archive.mk
|
||||
unit-tests/cmd-interrupt.exp
|
||||
unit-tests/cmd-interrupt.mk
|
||||
unit-tests/cmdline.exp
|
||||
@ -99,8 +97,8 @@ unit-tests/cond-cmp-numeric.exp
|
||||
unit-tests/cond-cmp-numeric.mk
|
||||
unit-tests/cond-cmp-string.exp
|
||||
unit-tests/cond-cmp-string.mk
|
||||
unit-tests/cond-func.exp
|
||||
unit-tests/cond-func.mk
|
||||
unit-tests/cond-cmp-unary.exp
|
||||
unit-tests/cond-cmp-unary.mk
|
||||
unit-tests/cond-func-commands.exp
|
||||
unit-tests/cond-func-commands.mk
|
||||
unit-tests/cond-func-defined.exp
|
||||
@ -113,6 +111,8 @@ unit-tests/cond-func-make.exp
|
||||
unit-tests/cond-func-make.mk
|
||||
unit-tests/cond-func-target.exp
|
||||
unit-tests/cond-func-target.mk
|
||||
unit-tests/cond-func.exp
|
||||
unit-tests/cond-func.mk
|
||||
unit-tests/cond-late.exp
|
||||
unit-tests/cond-late.mk
|
||||
unit-tests/cond-op-and.exp
|
||||
@ -135,26 +135,36 @@ unit-tests/cond-token-string.exp
|
||||
unit-tests/cond-token-string.mk
|
||||
unit-tests/cond-token-var.exp
|
||||
unit-tests/cond-token-var.mk
|
||||
unit-tests/cond-undef-lint.exp
|
||||
unit-tests/cond-undef-lint.mk
|
||||
unit-tests/cond1.exp
|
||||
unit-tests/cond1.mk
|
||||
unit-tests/cond2.exp
|
||||
unit-tests/cond2.mk
|
||||
unit-tests/counter-append.exp
|
||||
unit-tests/counter-append.mk
|
||||
unit-tests/counter.exp
|
||||
unit-tests/counter.mk
|
||||
unit-tests/dep-colon-bug-cross-file.exp
|
||||
unit-tests/dep-colon-bug-cross-file.mk
|
||||
unit-tests/dep-colon.exp
|
||||
unit-tests/dep-colon.mk
|
||||
unit-tests/dep-double-colon-indep.exp
|
||||
unit-tests/dep-double-colon-indep.mk
|
||||
unit-tests/dep-double-colon.exp
|
||||
unit-tests/dep-double-colon.mk
|
||||
unit-tests/dep-exclam.exp
|
||||
unit-tests/dep-exclam.mk
|
||||
unit-tests/dep-none.exp
|
||||
unit-tests/dep-none.mk
|
||||
unit-tests/dep-percent.exp
|
||||
unit-tests/dep-percent.mk
|
||||
unit-tests/dep-var.exp
|
||||
unit-tests/dep-var.mk
|
||||
unit-tests/dep-wildcards.exp
|
||||
unit-tests/dep-wildcards.mk
|
||||
unit-tests/dep.exp
|
||||
unit-tests/dep.mk
|
||||
unit-tests/depsrc-end.exp
|
||||
unit-tests/depsrc-end.mk
|
||||
unit-tests/depsrc-exec.exp
|
||||
unit-tests/depsrc-exec.mk
|
||||
unit-tests/depsrc-ignore.exp
|
||||
@ -185,10 +195,10 @@ unit-tests/depsrc-silent.exp
|
||||
unit-tests/depsrc-silent.mk
|
||||
unit-tests/depsrc-use.exp
|
||||
unit-tests/depsrc-use.mk
|
||||
unit-tests/depsrc-usebefore.exp
|
||||
unit-tests/depsrc-usebefore.mk
|
||||
unit-tests/depsrc-usebefore-double-colon.exp
|
||||
unit-tests/depsrc-usebefore-double-colon.mk
|
||||
unit-tests/depsrc-usebefore.exp
|
||||
unit-tests/depsrc-usebefore.mk
|
||||
unit-tests/depsrc-wait.exp
|
||||
unit-tests/depsrc-wait.mk
|
||||
unit-tests/depsrc.exp
|
||||
@ -199,6 +209,8 @@ unit-tests/deptgt-default.exp
|
||||
unit-tests/deptgt-default.mk
|
||||
unit-tests/deptgt-delete_on_error.exp
|
||||
unit-tests/deptgt-delete_on_error.mk
|
||||
unit-tests/deptgt-end-jobs.exp
|
||||
unit-tests/deptgt-end-jobs.mk
|
||||
unit-tests/deptgt-end.exp
|
||||
unit-tests/deptgt-end.mk
|
||||
unit-tests/deptgt-error.exp
|
||||
@ -239,10 +251,12 @@ unit-tests/deptgt-suffixes.exp
|
||||
unit-tests/deptgt-suffixes.mk
|
||||
unit-tests/deptgt.exp
|
||||
unit-tests/deptgt.mk
|
||||
unit-tests/dir.exp
|
||||
unit-tests/dir.mk
|
||||
unit-tests/dir-expand-path.exp
|
||||
unit-tests/dir-expand-path.mk
|
||||
unit-tests/dir.exp
|
||||
unit-tests/dir.mk
|
||||
unit-tests/directive-dinclude.exp
|
||||
unit-tests/directive-dinclude.mk
|
||||
unit-tests/directive-elif.exp
|
||||
unit-tests/directive-elif.mk
|
||||
unit-tests/directive-elifdef.exp
|
||||
@ -261,14 +275,18 @@ unit-tests/directive-error.exp
|
||||
unit-tests/directive-error.mk
|
||||
unit-tests/directive-export-env.exp
|
||||
unit-tests/directive-export-env.mk
|
||||
unit-tests/directive-export-gmake.exp
|
||||
unit-tests/directive-export-gmake.mk
|
||||
unit-tests/directive-export-literal.exp
|
||||
unit-tests/directive-export-literal.mk
|
||||
unit-tests/directive-export.exp
|
||||
unit-tests/directive-export.mk
|
||||
unit-tests/directive-for.exp
|
||||
unit-tests/directive-for.mk
|
||||
unit-tests/directive-for-generating-endif.exp
|
||||
unit-tests/directive-for-generating-endif.mk
|
||||
unit-tests/directive-for.exp
|
||||
unit-tests/directive-for.mk
|
||||
unit-tests/directive-hyphen-include.exp
|
||||
unit-tests/directive-hyphen-include.mk
|
||||
unit-tests/directive-if.exp
|
||||
unit-tests/directive-if.mk
|
||||
unit-tests/directive-ifdef.exp
|
||||
@ -279,8 +297,14 @@ unit-tests/directive-ifndef.exp
|
||||
unit-tests/directive-ifndef.mk
|
||||
unit-tests/directive-ifnmake.exp
|
||||
unit-tests/directive-ifnmake.mk
|
||||
unit-tests/directive-include-fatal.exp
|
||||
unit-tests/directive-include-fatal.mk
|
||||
unit-tests/directive-include.exp
|
||||
unit-tests/directive-include.mk
|
||||
unit-tests/directive-info.exp
|
||||
unit-tests/directive-info.mk
|
||||
unit-tests/directive-sinclude.exp
|
||||
unit-tests/directive-sinclude.mk
|
||||
unit-tests/directive-undef.exp
|
||||
unit-tests/directive-undef.mk
|
||||
unit-tests/directive-unexport-env.exp
|
||||
@ -317,20 +341,20 @@ unit-tests/forloop.exp
|
||||
unit-tests/forloop.mk
|
||||
unit-tests/forsubst.exp
|
||||
unit-tests/forsubst.mk
|
||||
unit-tests/hash.exp
|
||||
unit-tests/hash.mk
|
||||
unit-tests/hanoi-include.exp
|
||||
unit-tests/hanoi-include.mk
|
||||
unit-tests/impsrc.exp
|
||||
unit-tests/impsrc.mk
|
||||
unit-tests/include-main.exp
|
||||
unit-tests/include-main.mk
|
||||
unit-tests/include-sub.mk
|
||||
unit-tests/include-subsub.mk
|
||||
unit-tests/job-output-long-lines.exp
|
||||
unit-tests/job-output-long-lines.mk
|
||||
unit-tests/lint.exp
|
||||
unit-tests/lint.mk
|
||||
unit-tests/make-exported.exp
|
||||
unit-tests/make-exported.mk
|
||||
unit-tests/misc.exp
|
||||
unit-tests/misc.mk
|
||||
unit-tests/moderrs.exp
|
||||
unit-tests/moderrs.mk
|
||||
unit-tests/modmatch.exp
|
||||
@ -345,10 +369,56 @@ unit-tests/opt-backwards.exp
|
||||
unit-tests/opt-backwards.mk
|
||||
unit-tests/opt-chdir.exp
|
||||
unit-tests/opt-chdir.mk
|
||||
unit-tests/opt-debug-all.exp
|
||||
unit-tests/opt-debug-all.mk
|
||||
unit-tests/opt-debug-archive.exp
|
||||
unit-tests/opt-debug-archive.mk
|
||||
unit-tests/opt-debug-cond.exp
|
||||
unit-tests/opt-debug-cond.mk
|
||||
unit-tests/opt-debug-curdir.exp
|
||||
unit-tests/opt-debug-curdir.mk
|
||||
unit-tests/opt-debug-dir.exp
|
||||
unit-tests/opt-debug-dir.mk
|
||||
unit-tests/opt-debug-errors.exp
|
||||
unit-tests/opt-debug-errors.mk
|
||||
unit-tests/opt-debug-file.exp
|
||||
unit-tests/opt-debug-file.mk
|
||||
unit-tests/opt-debug-for.exp
|
||||
unit-tests/opt-debug-for.mk
|
||||
unit-tests/opt-debug-graph1.exp
|
||||
unit-tests/opt-debug-graph1.mk
|
||||
unit-tests/opt-debug-graph2.exp
|
||||
unit-tests/opt-debug-graph2.mk
|
||||
unit-tests/opt-debug-graph3.exp
|
||||
unit-tests/opt-debug-graph3.mk
|
||||
unit-tests/opt-debug-hash.exp
|
||||
unit-tests/opt-debug-hash.mk
|
||||
unit-tests/opt-debug-jobs.exp
|
||||
unit-tests/opt-debug-jobs.mk
|
||||
unit-tests/opt-debug-lint.exp
|
||||
unit-tests/opt-debug-lint.mk
|
||||
unit-tests/opt-debug-loud.exp
|
||||
unit-tests/opt-debug-loud.mk
|
||||
unit-tests/opt-debug-making.exp
|
||||
unit-tests/opt-debug-making.mk
|
||||
unit-tests/opt-debug-meta.exp
|
||||
unit-tests/opt-debug-meta.mk
|
||||
unit-tests/opt-debug-no-rm.exp
|
||||
unit-tests/opt-debug-no-rm.mk
|
||||
unit-tests/opt-debug-parse.exp
|
||||
unit-tests/opt-debug-parse.mk
|
||||
unit-tests/opt-debug-suff.exp
|
||||
unit-tests/opt-debug-suff.mk
|
||||
unit-tests/opt-debug-targets.exp
|
||||
unit-tests/opt-debug-targets.mk
|
||||
unit-tests/opt-debug-var.exp
|
||||
unit-tests/opt-debug-var.mk
|
||||
unit-tests/opt-debug-varraw.exp
|
||||
unit-tests/opt-debug-varraw.mk
|
||||
unit-tests/opt-debug-x-trace.exp
|
||||
unit-tests/opt-debug-x-trace.mk
|
||||
unit-tests/opt-debug.exp
|
||||
unit-tests/opt-debug.mk
|
||||
unit-tests/opt-debug-g1.exp
|
||||
unit-tests/opt-debug-g1.mk
|
||||
unit-tests/opt-define.exp
|
||||
unit-tests/opt-define.mk
|
||||
unit-tests/opt-env.exp
|
||||
@ -395,6 +465,8 @@ unit-tests/opt.exp
|
||||
unit-tests/opt.mk
|
||||
unit-tests/order.exp
|
||||
unit-tests/order.mk
|
||||
unit-tests/parse-var.exp
|
||||
unit-tests/parse-var.mk
|
||||
unit-tests/phony-end.exp
|
||||
unit-tests/phony-end.mk
|
||||
unit-tests/posix.exp
|
||||
@ -425,12 +497,34 @@ unit-tests/sh-single-line.exp
|
||||
unit-tests/sh-single-line.mk
|
||||
unit-tests/sh.exp
|
||||
unit-tests/sh.mk
|
||||
unit-tests/suffixes.exp
|
||||
unit-tests/suffixes.mk
|
||||
unit-tests/shell-csh.exp
|
||||
unit-tests/shell-csh.mk
|
||||
unit-tests/shell-custom.exp
|
||||
unit-tests/shell-custom.mk
|
||||
unit-tests/shell-ksh.exp
|
||||
unit-tests/shell-ksh.mk
|
||||
unit-tests/shell-sh.exp
|
||||
unit-tests/shell-sh.mk
|
||||
unit-tests/suff-add-later.exp
|
||||
unit-tests/suff-add-later.mk
|
||||
unit-tests/suff-clear-regular.exp
|
||||
unit-tests/suff-clear-regular.mk
|
||||
unit-tests/suff-clear-single.exp
|
||||
unit-tests/suff-clear-single.mk
|
||||
unit-tests/suff-lookup.exp
|
||||
unit-tests/suff-lookup.mk
|
||||
unit-tests/suff-main.exp
|
||||
unit-tests/suff-main.mk
|
||||
unit-tests/suff-rebuild.exp
|
||||
unit-tests/suff-rebuild.mk
|
||||
unit-tests/suff-transform-endless.exp
|
||||
unit-tests/suff-transform-endless.mk
|
||||
unit-tests/suff-transform-expand.exp
|
||||
unit-tests/suff-transform-expand.mk
|
||||
unit-tests/suff-transform-select.exp
|
||||
unit-tests/suff-transform-select.mk
|
||||
unit-tests/sunshcmd.exp
|
||||
unit-tests/sunshcmd.mk
|
||||
unit-tests/sysv.exp
|
||||
unit-tests/sysv.mk
|
||||
unit-tests/ternary.exp
|
||||
unit-tests/ternary.mk
|
||||
unit-tests/unexport-env.exp
|
||||
@ -461,8 +555,12 @@ unit-tests/var-op-expand.exp
|
||||
unit-tests/var-op-expand.mk
|
||||
unit-tests/var-op-shell.exp
|
||||
unit-tests/var-op-shell.mk
|
||||
unit-tests/var-op-sunsh.exp
|
||||
unit-tests/var-op-sunsh.mk
|
||||
unit-tests/var-op.exp
|
||||
unit-tests/var-op.mk
|
||||
unit-tests/var-recursive.exp
|
||||
unit-tests/var-recursive.mk
|
||||
unit-tests/varcmd.exp
|
||||
unit-tests/varcmd.mk
|
||||
unit-tests/vardebug.exp
|
||||
@ -555,12 +653,12 @@ unit-tests/varname-dot-alltargets.exp
|
||||
unit-tests/varname-dot-alltargets.mk
|
||||
unit-tests/varname-dot-curdir.exp
|
||||
unit-tests/varname-dot-curdir.mk
|
||||
unit-tests/varname-dot-includes.exp
|
||||
unit-tests/varname-dot-includes.mk
|
||||
unit-tests/varname-dot-includedfromdir.exp
|
||||
unit-tests/varname-dot-includedfromdir.mk
|
||||
unit-tests/varname-dot-includedfromfile.exp
|
||||
unit-tests/varname-dot-includedfromfile.mk
|
||||
unit-tests/varname-dot-includes.exp
|
||||
unit-tests/varname-dot-includes.mk
|
||||
unit-tests/varname-dot-libs.exp
|
||||
unit-tests/varname-dot-libs.mk
|
||||
unit-tests/varname-dot-make-dependfile.exp
|
||||
@ -623,8 +721,12 @@ unit-tests/varname-empty.exp
|
||||
unit-tests/varname-empty.mk
|
||||
unit-tests/varname-make.exp
|
||||
unit-tests/varname-make.mk
|
||||
unit-tests/varname-make_print_var_on_error-jobs.exp
|
||||
unit-tests/varname-make_print_var_on_error-jobs.mk
|
||||
unit-tests/varname-make_print_var_on_error.exp
|
||||
unit-tests/varname-make_print_var_on_error.mk
|
||||
unit-tests/varname-makefile.exp
|
||||
unit-tests/varname-makefile.mk
|
||||
unit-tests/varname-makeflags.exp
|
||||
unit-tests/varname-makeflags.mk
|
||||
unit-tests/varname-pwd.exp
|
||||
@ -635,6 +737,10 @@ unit-tests/varname.exp
|
||||
unit-tests/varname.mk
|
||||
unit-tests/varparse-dynamic.exp
|
||||
unit-tests/varparse-dynamic.mk
|
||||
unit-tests/varparse-mod.exp
|
||||
unit-tests/varparse-mod.mk
|
||||
unit-tests/varparse-undef-partial.exp
|
||||
unit-tests/varparse-undef-partial.mk
|
||||
unit-tests/varquote.exp
|
||||
unit-tests/varquote.mk
|
||||
unit-tests/varshell.exp
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: Makefile,v 1.112 2020/08/28 16:26:17 sjg Exp $
|
||||
# $Id: Makefile,v 1.113 2020/10/26 17:55:09 sjg Exp $
|
||||
|
||||
PROG= bmake
|
||||
|
||||
@ -20,7 +20,6 @@ SRCS= \
|
||||
metachar.c \
|
||||
parse.c \
|
||||
str.c \
|
||||
strlist.c \
|
||||
suff.c \
|
||||
targ.c \
|
||||
trace.c \
|
||||
|
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20200902
|
||||
_MAKE_VERSION=20201101
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd August 28, 2020
|
||||
.Dd November 1, 2020
|
||||
.Dt BMAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1872,7 +1872,7 @@ has been defined and has commands associated with it.
|
||||
.Ar Expression
|
||||
may also be an arithmetic or string comparison.
|
||||
Variable expansion is
|
||||
performed on both sides of the comparison, after which the integral
|
||||
performed on both sides of the comparison, after which the numerical
|
||||
values are compared.
|
||||
A value is interpreted as hexadecimal if it is
|
||||
preceded by 0x, otherwise it is decimal; octal numbers are not supported.
|
||||
@ -1882,7 +1882,7 @@ variable expansion, either the left or right hand side of a
|
||||
.Ql Ic ==
|
||||
or
|
||||
.Ql Ic "!="
|
||||
operator is not an integral value, then
|
||||
operator is not a numerical value, then
|
||||
string comparison is performed between the expanded
|
||||
variables.
|
||||
If no relational operator is given, it is assumed that the expanded
|
||||
|
@ -1197,11 +1197,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[4mExpression[24m may also be an arithmetic or string comparison. Variable
|
||||
expansion is performed on both sides of the comparison, after which the
|
||||
integral values are compared. A value is interpreted as hexadecimal if
|
||||
numerical values are compared. A value is interpreted as hexadecimal if
|
||||
it is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
|
||||
ported. The standard C relational operators are all supported. If after
|
||||
variable expansion, either the left or right hand side of a `[1m==[22m' or `[1m!=[22m'
|
||||
operator is not an integral value, then string comparison is performed
|
||||
operator is not a numerical value, then string comparison is performed
|
||||
between the expanded variables. If no relational operator is given, it
|
||||
is assumed that the expanded variable is being compared against 0, or an
|
||||
empty string in the case of a string comparison.
|
||||
@ -1568,4 +1568,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
There is no way of escaping a space character in a filename.
|
||||
|
||||
FreeBSD 11.3 August 28, 2020 FreeBSD 11.3
|
||||
FreeBSD 11.3 November 1, 2020 FreeBSD 11.3
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
@ -115,7 +115,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: boot-strap,v 1.51 2020/02/19 16:46:23 sjg Exp $
|
||||
# $Id: boot-strap,v 1.53 2020/09/16 02:12:01 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2001 Simon J. Gerraty
|
||||
#
|
||||
@ -458,17 +458,25 @@ op_all() {
|
||||
else
|
||||
op_test
|
||||
MAKE_VERSION=`sed -n '/^_MAKE_VERSION/ { s,.*= *,,;p; }' $srcdir/Makefile`
|
||||
echo You can install by running:
|
||||
echo
|
||||
echo $0 $cmd_args op=install
|
||||
echo
|
||||
echo "Use --install-prefix=/something to install somewhere other than $prefix"
|
||||
echo "Use --install-destdir=/somewhere to set DESTDIR during install"
|
||||
echo "Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin"
|
||||
echo "Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION"
|
||||
echo "Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink"
|
||||
echo "Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk"
|
||||
cat << EOM
|
||||
You can install by running:
|
||||
|
||||
$0 $cmd_args op=install
|
||||
|
||||
Use --install-prefix=/something to install somewhere other than $prefix
|
||||
Use --install-destdir=/somewhere to set DESTDIR during install
|
||||
Use --install-host-target to use INSTALL_BIN=$HOST_TARGET/bin
|
||||
Use -DWITH_PROG_VERSION to install as bmake-$MAKE_VERSION
|
||||
Use -DWITHOUT_PROG_LINK to suppress bmake -> bmake-$MAKE_VERSION symlink
|
||||
Use -DWITHOUT_INSTALL_MK to skip installing files to $prefix/share/mk
|
||||
EOM
|
||||
fi
|
||||
cat << EOM
|
||||
|
||||
Note: bmake.cat1 contains ANSI escape sequences.
|
||||
You may need the -r or -R option to more/less to view it correctly.
|
||||
|
||||
EOM
|
||||
}
|
||||
|
||||
op_$op
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $ */
|
||||
/* $NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -69,115 +69,103 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: buf.c,v 1.37 2020/08/23 08:21:50 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
/* Functions for automatically-expanded null-terminated buffers. */
|
||||
/* Automatically-expanding null-terminated buffers. */
|
||||
|
||||
#include <limits.h>
|
||||
#include "make.h"
|
||||
|
||||
/* Extend the buffer for adding a single byte. */
|
||||
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.42 2020/10/24 20:51:49 rillig Exp $");
|
||||
|
||||
/* Make space in the buffer for adding a single byte. */
|
||||
void
|
||||
Buf_Expand_1(Buffer *bp)
|
||||
Buf_Expand_1(Buffer *buf)
|
||||
{
|
||||
bp->size += MAX(bp->size, 16);
|
||||
bp->buffer = bmake_realloc(bp->buffer, bp->size);
|
||||
buf->cap += buf->cap > 16 ? buf->cap : 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
|
||||
/* Add the given bytes to the buffer. */
|
||||
/* Add the bytes to the buffer. */
|
||||
void
|
||||
Buf_AddBytes(Buffer *bp, const char *bytesPtr, size_t numBytes)
|
||||
Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
|
||||
{
|
||||
size_t count = bp->count;
|
||||
char *ptr;
|
||||
size_t old_len = buf->len;
|
||||
char *end;
|
||||
|
||||
if (__predict_false(count + numBytes >= bp->size)) {
|
||||
bp->size += MAX(bp->size, numBytes + 16);
|
||||
bp->buffer = bmake_realloc(bp->buffer, bp->size);
|
||||
if (__predict_false(old_len + bytes_len >= buf->cap)) {
|
||||
buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
|
||||
ptr = bp->buffer + count;
|
||||
bp->count = count + numBytes;
|
||||
memcpy(ptr, bytesPtr, numBytes);
|
||||
ptr[numBytes] = '\0';
|
||||
end = buf->data + old_len;
|
||||
buf->len = old_len + bytes_len;
|
||||
memcpy(end, bytes, bytes_len);
|
||||
end[bytes_len] = '\0';
|
||||
}
|
||||
|
||||
/* Add the bytes between start and end to the buffer. */
|
||||
void
|
||||
Buf_AddBytesBetween(Buffer *bp, const char *start, const char *end)
|
||||
Buf_AddBytesBetween(Buffer *buf, const char *start, const char *end)
|
||||
{
|
||||
Buf_AddBytes(bp, start, (size_t)(end - start));
|
||||
Buf_AddBytes(buf, start, (size_t)(end - start));
|
||||
}
|
||||
|
||||
/* Add the given string to the buffer. */
|
||||
/* Add the string to the buffer. */
|
||||
void
|
||||
Buf_AddStr(Buffer *bp, const char *str)
|
||||
Buf_AddStr(Buffer *buf, const char *str)
|
||||
{
|
||||
Buf_AddBytes(bp, str, strlen(str));
|
||||
Buf_AddBytes(buf, str, strlen(str));
|
||||
}
|
||||
|
||||
/* Add the given number to the buffer. */
|
||||
/* Add the number to the buffer. */
|
||||
void
|
||||
Buf_AddInt(Buffer *bp, int n)
|
||||
Buf_AddInt(Buffer *buf, int n)
|
||||
{
|
||||
enum {
|
||||
bits = sizeof(int) * CHAR_BIT,
|
||||
max_octal_digits = (bits + 2) / 3,
|
||||
max_decimal_digits = /* at most */ max_octal_digits,
|
||||
max_sign_chars = 1,
|
||||
buf_size = max_sign_chars + max_decimal_digits + 1
|
||||
str_size = max_sign_chars + max_decimal_digits + 1
|
||||
};
|
||||
char buf[buf_size];
|
||||
char str[str_size];
|
||||
|
||||
size_t len = (size_t)snprintf(buf, sizeof buf, "%d", n);
|
||||
Buf_AddBytes(bp, buf, len);
|
||||
size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
|
||||
Buf_AddBytes(buf, str, len);
|
||||
}
|
||||
|
||||
/* Get the data (usually a string) from the buffer.
|
||||
* The returned data is valid until the next modifying operation
|
||||
* on the buffer.
|
||||
*
|
||||
* Returns the pointer to the data and optionally the length of the
|
||||
* data in the buffer. */
|
||||
* Returns the data and optionally the length of the data. */
|
||||
char *
|
||||
Buf_GetAll(Buffer *bp, size_t *numBytesPtr)
|
||||
Buf_GetAll(Buffer *buf, size_t *out_len)
|
||||
{
|
||||
if (numBytesPtr != NULL)
|
||||
*numBytesPtr = bp->count;
|
||||
return bp->buffer;
|
||||
if (out_len != NULL)
|
||||
*out_len = buf->len;
|
||||
return buf->data;
|
||||
}
|
||||
|
||||
/* Mark the buffer as empty, so it can be filled with data again. */
|
||||
void
|
||||
Buf_Empty(Buffer *bp)
|
||||
Buf_Empty(Buffer *buf)
|
||||
{
|
||||
bp->count = 0;
|
||||
bp->buffer[0] = '\0';
|
||||
buf->len = 0;
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Initialize a buffer.
|
||||
* If the given initial size is 0, a reasonable default is used. */
|
||||
* If the given initial capacity is 0, a reasonable default is used. */
|
||||
void
|
||||
Buf_Init(Buffer *bp, size_t size)
|
||||
Buf_Init(Buffer *buf, size_t cap)
|
||||
{
|
||||
if (size <= 0) {
|
||||
size = 256;
|
||||
}
|
||||
bp->size = size;
|
||||
bp->count = 0;
|
||||
bp->buffer = bmake_malloc(size);
|
||||
bp->buffer[0] = '\0';
|
||||
if (cap <= 0)
|
||||
cap = 256;
|
||||
buf->cap = cap;
|
||||
buf->len = 0;
|
||||
buf->data = bmake_malloc(cap);
|
||||
buf->data[0] = '\0';
|
||||
}
|
||||
|
||||
/* Reset the buffer.
|
||||
@ -186,15 +174,15 @@ Buf_Init(Buffer *bp, size_t size)
|
||||
char *
|
||||
Buf_Destroy(Buffer *buf, Boolean freeData)
|
||||
{
|
||||
char *data = buf->buffer;
|
||||
char *data = buf->data;
|
||||
if (freeData) {
|
||||
free(data);
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
buf->size = 0;
|
||||
buf->count = 0;
|
||||
buf->buffer = NULL;
|
||||
buf->cap = 0;
|
||||
buf->len = 0;
|
||||
buf->data = NULL;
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -211,10 +199,10 @@ char *
|
||||
Buf_DestroyCompact(Buffer *buf)
|
||||
{
|
||||
#if BUF_COMPACT_LIMIT > 0
|
||||
if (buf->size - buf->count >= BUF_COMPACT_LIMIT) {
|
||||
if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) {
|
||||
/* We trust realloc to be smart */
|
||||
char *data = bmake_realloc(buf->buffer, buf->count + 1);
|
||||
data[buf->count] = '\0';
|
||||
char *data = bmake_realloc(buf->data, buf->len + 1);
|
||||
data[buf->len] = '\0'; /* XXX: unnecessary */
|
||||
Buf_Destroy(buf, FALSE);
|
||||
return data;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.h,v 1.28 2020/09/01 17:38:26 rillig Exp $ */
|
||||
/* $NetBSD: buf.h,v 1.34 2020/09/27 16:59:02 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -81,9 +81,9 @@
|
||||
|
||||
/* An automatically growing null-terminated buffer of characters. */
|
||||
typedef struct Buffer {
|
||||
size_t size; /* Allocated size of the buffer, including the null */
|
||||
size_t count; /* Number of bytes in buffer, excluding the null */
|
||||
char *buffer; /* The buffer itself (always null-terminated) */
|
||||
size_t cap; /* Allocated size of the buffer, including the null */
|
||||
size_t len; /* Number of bytes in buffer, excluding the null */
|
||||
char *data; /* The buffer itself (always null-terminated) */
|
||||
} Buffer;
|
||||
|
||||
/* If we aren't on NetBSD, __predict_false() might not be defined. */
|
||||
@ -94,22 +94,28 @@ typedef struct Buffer {
|
||||
void Buf_Expand_1(Buffer *);
|
||||
|
||||
/* Buf_AddByte adds a single byte to a buffer. */
|
||||
static inline void MAKE_ATTR_UNUSED
|
||||
Buf_AddByte(Buffer *bp, char byte)
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
Buf_AddByte(Buffer *buf, char byte)
|
||||
{
|
||||
size_t count = ++bp->count;
|
||||
char *ptr;
|
||||
if (__predict_false(count >= bp->size))
|
||||
Buf_Expand_1(bp);
|
||||
ptr = bp->buffer + count;
|
||||
ptr[-1] = byte;
|
||||
ptr[0] = 0;
|
||||
size_t old_len = buf->len++;
|
||||
char *end;
|
||||
if (__predict_false(old_len + 1 >= buf->cap))
|
||||
Buf_Expand_1(buf);
|
||||
end = buf->data + old_len;
|
||||
end[0] = byte;
|
||||
end[1] = '\0';
|
||||
}
|
||||
|
||||
static inline size_t MAKE_ATTR_UNUSED
|
||||
Buf_Size(const Buffer *bp)
|
||||
static inline MAKE_ATTR_UNUSED size_t
|
||||
Buf_Len(const Buffer *buf)
|
||||
{
|
||||
return bp->count;
|
||||
return buf->len;
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED Boolean
|
||||
Buf_EndsWith(const Buffer *buf, char ch)
|
||||
{
|
||||
return buf->len > 0 && buf->data[buf->len - 1] == ch;
|
||||
}
|
||||
|
||||
void Buf_AddBytes(Buffer *, const char *, size_t);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $ */
|
||||
/* $NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -69,19 +69,6 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
/*-
|
||||
* compat.c --
|
||||
* The routines in this file implement the full-compatibility
|
||||
@ -91,33 +78,30 @@ __RCSID("$NetBSD: compat.c,v 1.139 2020/08/30 20:08:47 rillig Exp $");
|
||||
* - friendly variable substitution.
|
||||
*
|
||||
* Interface:
|
||||
* Compat_Run Initialize things for this module and recreate
|
||||
* thems as need creatin'
|
||||
* Compat_Run Initialize things for this module and recreate
|
||||
* thems as need creatin'
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "wait.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "wait.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "make.h"
|
||||
#include "hash.h"
|
||||
#include "dir.h"
|
||||
#include "job.h"
|
||||
#include "metachar.h"
|
||||
#include "pathnames.h"
|
||||
#include "make.h"
|
||||
#include "dir.h"
|
||||
#include "job.h"
|
||||
#include "metachar.h"
|
||||
#include "pathnames.h"
|
||||
|
||||
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.173 2020/11/01 17:47:26 rillig Exp $");
|
||||
|
||||
static GNode *curTarg = NULL;
|
||||
static GNode *ENDNode;
|
||||
static void CompatInterrupt(int);
|
||||
static GNode *curTarg = NULL;
|
||||
static pid_t compatChild;
|
||||
static int compatSigno;
|
||||
|
||||
@ -128,15 +112,12 @@ static int compatSigno;
|
||||
static void
|
||||
CompatDeleteTarget(GNode *gn)
|
||||
{
|
||||
if ((gn != NULL) && !Targ_Precious (gn)) {
|
||||
char *p1;
|
||||
const char *file = Var_Value(TARGET, gn, &p1);
|
||||
if (gn != NULL && !Targ_Precious(gn)) {
|
||||
const char *file = GNode_VarTarget(gn);
|
||||
|
||||
if (!noExecute && eunlink(file) != -1) {
|
||||
if (!opts.noExecute && eunlink(file) != -1) {
|
||||
Error("*** %s removed", file);
|
||||
}
|
||||
|
||||
bmake_free(p1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,22 +136,24 @@ CompatInterrupt(int signo)
|
||||
|
||||
CompatDeleteTarget(curTarg);
|
||||
|
||||
if ((curTarg != NULL) && !Targ_Precious (curTarg)) {
|
||||
if (curTarg != NULL && !Targ_Precious(curTarg)) {
|
||||
/*
|
||||
* Run .INTERRUPT only if hit with interrupt signal
|
||||
*/
|
||||
if (signo == SIGINT) {
|
||||
gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
|
||||
gn = Targ_FindNode(".INTERRUPT");
|
||||
if (gn != NULL) {
|
||||
Compat_Make(gn, gn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (signo == SIGQUIT)
|
||||
_exit(signo);
|
||||
|
||||
/*
|
||||
* If there is a child running, pass the signal on
|
||||
* we will exist after it has exited.
|
||||
* If there is a child running, pass the signal on.
|
||||
* We will exist after it has exited.
|
||||
*/
|
||||
compatSigno = signo;
|
||||
if (compatChild > 0) {
|
||||
@ -181,11 +164,8 @@ CompatInterrupt(int signo)
|
||||
}
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* CompatRunCommand --
|
||||
* Execute the next command for a target. If the command returns an
|
||||
* error, the node's made field is set to ERROR and creation stops.
|
||||
/* Execute the next command for a target. If the command returns an error,
|
||||
* the node's made field is set to ERROR and creation stops.
|
||||
*
|
||||
* Input:
|
||||
* cmdp Command to execute
|
||||
@ -193,45 +173,38 @@ CompatInterrupt(int signo)
|
||||
*
|
||||
* Results:
|
||||
* 0 if the command succeeded, 1 if an error occurred.
|
||||
*
|
||||
* Side Effects:
|
||||
* The node's 'made' field may be set to ERROR.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
CompatRunCommand(void *cmdp, void *gnp)
|
||||
Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
{
|
||||
char *cmdStart; /* Start of expanded command */
|
||||
char *cp, *bp;
|
||||
Boolean silent, /* Don't print command */
|
||||
doIt; /* Execute even if -n */
|
||||
volatile Boolean errCheck; /* Check errors */
|
||||
WAIT_T reason; /* Reason for child's death */
|
||||
int status; /* Description of child's death */
|
||||
pid_t cpid; /* Child actually found */
|
||||
pid_t retstat; /* Result of wait */
|
||||
LstNode cmdNode; /* Node where current command is located */
|
||||
const char ** volatile av; /* Argument vector for thing to exec */
|
||||
char ** volatile mav;/* Copy of the argument vector for freeing */
|
||||
Boolean useShell; /* TRUE if command should be executed
|
||||
char *cmdStart; /* Start of expanded command */
|
||||
char *bp;
|
||||
Boolean silent; /* Don't print command */
|
||||
Boolean doIt; /* Execute even if -n */
|
||||
volatile Boolean errCheck; /* Check errors */
|
||||
WAIT_T reason; /* Reason for child's death */
|
||||
int status; /* Description of child's death */
|
||||
pid_t cpid; /* Child actually found */
|
||||
pid_t retstat; /* Result of wait */
|
||||
StringListNode *cmdNode; /* Node where current command is located */
|
||||
const char **volatile av; /* Argument vector for thing to exec */
|
||||
char **volatile mav; /* Copy of the argument vector for freeing */
|
||||
Boolean useShell; /* TRUE if command should be executed
|
||||
* using a shell */
|
||||
char * volatile cmd = (char *)cmdp;
|
||||
GNode *gn = (GNode *)gnp;
|
||||
const char *volatile cmd = cmdp;
|
||||
|
||||
silent = (gn->type & OP_SILENT) != 0;
|
||||
errCheck = !(gn->type & OP_IGNORE);
|
||||
doIt = FALSE;
|
||||
|
||||
/* Luckily the commands don't end up in a string pool, otherwise
|
||||
* this comparison could match too early, in a dependency using "..."
|
||||
* for delayed commands, run in parallel mode, using the same shell
|
||||
* command line more than once; see JobPrintCommand.
|
||||
* TODO: write a unit-test to protect against this potential bug. */
|
||||
cmdNode = Lst_FindDatum(gn->commands, cmd);
|
||||
cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
|
||||
|
||||
/*
|
||||
* brk_string will return an argv with a NULL in av[0], thus causing
|
||||
* execvp to choke and die horribly. Besides, how can we execute a null
|
||||
* command? In any case, we warn the user that the command expanded to
|
||||
* nothing (is this the right thing to do?).
|
||||
*/
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
|
||||
/* TODO: handle errors */
|
||||
|
||||
if (*cmdStart == '\0') {
|
||||
free(cmdStart);
|
||||
@ -240,17 +213,19 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
cmd = cmdStart;
|
||||
LstNode_Set(cmdNode, cmdStart);
|
||||
|
||||
if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
|
||||
assert(ENDNode != NULL);
|
||||
Lst_Append(ENDNode->commands, cmdStart);
|
||||
return 0;
|
||||
if (gn->type & OP_SAVE_CMDS) {
|
||||
GNode *endNode = Targ_GetEndNode();
|
||||
if (gn != endNode) {
|
||||
Lst_Append(endNode->commands, cmdStart);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (strcmp(cmdStart, "...") == 0) {
|
||||
gn->type |= OP_SAVE_CMDS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
|
||||
while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
|
||||
switch (*cmd) {
|
||||
case '@':
|
||||
silent = !DEBUG(LOUD);
|
||||
@ -267,7 +242,7 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
cmd++;
|
||||
}
|
||||
|
||||
while (isspace((unsigned char)*cmd))
|
||||
while (ch_isspace(*cmd))
|
||||
cmd++;
|
||||
|
||||
/*
|
||||
@ -295,14 +270,14 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
* meta characters as documented in make(1).
|
||||
*/
|
||||
|
||||
useShell = needshell(cmd, FALSE);
|
||||
useShell = needshell(cmd);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Print the command before echoing if we're not supposed to be quiet for
|
||||
* this one. We also print the command if -n given.
|
||||
*/
|
||||
if (!silent || NoExecute(gn)) {
|
||||
if (!silent || !GNode_ShouldExecute(gn)) {
|
||||
printf("%s\n", cmd);
|
||||
fflush(stdout);
|
||||
}
|
||||
@ -311,11 +286,10 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
* If we're not supposed to execute any commands, this is as far as
|
||||
* we go...
|
||||
*/
|
||||
if (!doIt && NoExecute(gn)) {
|
||||
if (!doIt && !GNode_ShouldExecute(gn)) {
|
||||
return 0;
|
||||
}
|
||||
if (DEBUG(JOB))
|
||||
fprintf(debug_file, "Execute: '%s'\n", cmd);
|
||||
DEBUG1(JOB, "Execute: '%s'\n", cmd);
|
||||
|
||||
if (useShell) {
|
||||
/*
|
||||
@ -374,8 +348,7 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
}
|
||||
#endif
|
||||
(void)execvp(av[0], (char *const *)UNCONST(av));
|
||||
execError("exec", av[0]);
|
||||
_exit(1);
|
||||
execDie("exec", av[0]);
|
||||
}
|
||||
|
||||
free(mav);
|
||||
@ -394,85 +367,77 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
/*
|
||||
* The child is off and running. Now all we can do is wait...
|
||||
*/
|
||||
while (1) {
|
||||
|
||||
while ((retstat = wait(&reason)) != cpid) {
|
||||
if (retstat > 0)
|
||||
JobReapChild(retstat, reason, FALSE); /* not ours? */
|
||||
if (retstat == -1 && errno != EINTR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (retstat > -1) {
|
||||
if (WIFSTOPPED(reason)) {
|
||||
status = WSTOPSIG(reason); /* stopped */
|
||||
} else if (WIFEXITED(reason)) {
|
||||
status = WEXITSTATUS(reason); /* exited */
|
||||
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
|
||||
if (useMeta) {
|
||||
meta_cmd_finish(NULL);
|
||||
}
|
||||
#endif
|
||||
if (status != 0) {
|
||||
if (DEBUG(ERROR)) {
|
||||
fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
for (cp = cmd; *cp; ) {
|
||||
if (isspace((unsigned char)*cp)) {
|
||||
fprintf(debug_file, " ");
|
||||
while (isspace((unsigned char)*cp))
|
||||
cp++;
|
||||
} else {
|
||||
fprintf(debug_file, "%c", *cp);
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
fprintf(debug_file, "\n");
|
||||
}
|
||||
printf("*** Error code %d", status);
|
||||
}
|
||||
} else {
|
||||
status = WTERMSIG(reason); /* signaled */
|
||||
printf("*** Signal %d", status);
|
||||
}
|
||||
|
||||
|
||||
if (!WIFEXITED(reason) || (status != 0)) {
|
||||
if (errCheck) {
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
meta_job_error(NULL, gn, 0, status);
|
||||
}
|
||||
#endif
|
||||
gn->made = ERROR;
|
||||
if (keepgoing) {
|
||||
/*
|
||||
* Abort the current target, but let others
|
||||
* continue.
|
||||
*/
|
||||
printf(" (continuing)\n");
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
if (deleteOnError) {
|
||||
CompatDeleteTarget(gn);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Continue executing commands for this target.
|
||||
* If we return 0, this will happen...
|
||||
*/
|
||||
printf(" (ignored)\n");
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
while ((retstat = wait(&reason)) != cpid) {
|
||||
if (retstat > 0)
|
||||
JobReapChild(retstat, reason, FALSE); /* not ours? */
|
||||
if (retstat == -1 && errno != EINTR) {
|
||||
break;
|
||||
} else {
|
||||
Fatal("error in wait: %d: %s", retstat, strerror(errno));
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
}
|
||||
|
||||
if (retstat < 0)
|
||||
Fatal("error in wait: %d: %s", retstat, strerror(errno));
|
||||
|
||||
if (WIFSTOPPED(reason)) {
|
||||
status = WSTOPSIG(reason); /* stopped */
|
||||
} else if (WIFEXITED(reason)) {
|
||||
status = WEXITSTATUS(reason); /* exited */
|
||||
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
|
||||
if (useMeta) {
|
||||
meta_cmd_finish(NULL);
|
||||
}
|
||||
#endif
|
||||
if (status != 0) {
|
||||
if (DEBUG(ERROR)) {
|
||||
const char *cp;
|
||||
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
for (cp = cmd; *cp; ) {
|
||||
if (ch_isspace(*cp)) {
|
||||
debug_printf(" ");
|
||||
while (ch_isspace(*cp))
|
||||
cp++;
|
||||
} else {
|
||||
debug_printf("%c", *cp);
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
}
|
||||
printf("*** Error code %d", status);
|
||||
}
|
||||
} else {
|
||||
status = WTERMSIG(reason); /* signaled */
|
||||
printf("*** Signal %d", status);
|
||||
}
|
||||
|
||||
|
||||
if (!WIFEXITED(reason) || status != 0) {
|
||||
if (errCheck) {
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
meta_job_error(NULL, gn, 0, status);
|
||||
}
|
||||
#endif
|
||||
gn->made = ERROR;
|
||||
if (opts.keepgoing) {
|
||||
/* Abort the current target, but let others continue. */
|
||||
printf(" (continuing)\n");
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
if (deleteOnError)
|
||||
CompatDeleteTarget(gn);
|
||||
} else {
|
||||
/*
|
||||
* Continue executing commands for this target.
|
||||
* If we return 0, this will happen...
|
||||
*/
|
||||
printf(" (ignored)\n");
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(cmdStart);
|
||||
compatChild = 0;
|
||||
if (compatSigno) {
|
||||
@ -483,32 +448,41 @@ CompatRunCommand(void *cmdp, void *gnp)
|
||||
return status;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* Compat_Make --
|
||||
* Make a target.
|
||||
static void
|
||||
RunCommands(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
if (Compat_RunCommand(cmd, gn) != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MakeNodes(GNodeList *gnodes, GNode *pgn)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next) {
|
||||
GNode *cohort = ln->datum;
|
||||
Compat_Make(cohort, pgn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make a target.
|
||||
*
|
||||
* If an error is detected and not being ignored, the process exits.
|
||||
*
|
||||
* Input:
|
||||
* gnp The node to make
|
||||
* pgnp Parent to abort if necessary
|
||||
*
|
||||
* Results:
|
||||
* 0
|
||||
*
|
||||
* Side Effects:
|
||||
* If an error is detected and not being ignored, the process exits.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
* gn The node to make
|
||||
* pgn Parent to abort if necessary
|
||||
*/
|
||||
int
|
||||
Compat_Make(void *gnp, void *pgnp)
|
||||
void
|
||||
Compat_Make(GNode *gn, GNode *pgn)
|
||||
{
|
||||
GNode *gn = (GNode *)gnp;
|
||||
GNode *pgn = (GNode *)pgnp;
|
||||
|
||||
if (!shellName) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) {
|
||||
if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
|
||||
/*
|
||||
* First mark ourselves to be made, then apply whatever transformations
|
||||
* the suffix module thinks are necessary. Once that's done, we can
|
||||
@ -519,45 +493,37 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
*/
|
||||
gn->flags |= REMAKE;
|
||||
gn->made = BEINGMADE;
|
||||
if ((gn->type & OP_MADE) == 0)
|
||||
if (!(gn->type & OP_MADE))
|
||||
Suff_FindDeps(gn);
|
||||
Lst_ForEach(gn->children, Compat_Make, gn);
|
||||
if ((gn->flags & REMAKE) == 0) {
|
||||
MakeNodes(gn->children, gn);
|
||||
if (!(gn->flags & REMAKE)) {
|
||||
gn->made = ABORTED;
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
goto cohorts;
|
||||
}
|
||||
|
||||
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
|
||||
char *p1;
|
||||
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
|
||||
bmake_free(p1);
|
||||
}
|
||||
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
|
||||
Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
|
||||
|
||||
/*
|
||||
* All the children were made ok. Now cmgn->mtime contains the
|
||||
* All the children were made ok. Now youngestChild->mtime contains the
|
||||
* modification time of the newest child, we need to find out if we
|
||||
* exist and when we were modified last. The criteria for datedness
|
||||
* are defined by the Make_OODate function.
|
||||
*/
|
||||
if (DEBUG(MAKE)) {
|
||||
fprintf(debug_file, "Examining %s...", gn->name);
|
||||
}
|
||||
if (! Make_OODate(gn)) {
|
||||
DEBUG1(MAKE, "Examining %s...", gn->name);
|
||||
if (!Make_OODate(gn)) {
|
||||
gn->made = UPTODATE;
|
||||
if (DEBUG(MAKE)) {
|
||||
fprintf(debug_file, "up-to-date.\n");
|
||||
}
|
||||
DEBUG0(MAKE, "up-to-date.\n");
|
||||
goto cohorts;
|
||||
} else if (DEBUG(MAKE)) {
|
||||
fprintf(debug_file, "out-of-date.\n");
|
||||
}
|
||||
} else
|
||||
DEBUG0(MAKE, "out-of-date.\n");
|
||||
|
||||
/*
|
||||
* If the user is just seeing if something is out-of-date, exit now
|
||||
* to tell him/her "yes".
|
||||
*/
|
||||
if (queryFlag) {
|
||||
if (opts.queryFlag) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -572,26 +538,24 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
* Alter our type to tell if errors should be ignored or things
|
||||
* should not be printed so CompatRunCommand knows what to do.
|
||||
*/
|
||||
if (Targ_Ignore(gn)) {
|
||||
if (Targ_Ignore(gn))
|
||||
gn->type |= OP_IGNORE;
|
||||
}
|
||||
if (Targ_Silent(gn)) {
|
||||
if (Targ_Silent(gn))
|
||||
gn->type |= OP_SILENT;
|
||||
}
|
||||
|
||||
if (Job_CheckCommands(gn, Fatal)) {
|
||||
/*
|
||||
* Our commands are ok, but we still have to worry about the -t
|
||||
* flag...
|
||||
*/
|
||||
if (!touchFlag || (gn->type & OP_MAKE)) {
|
||||
if (!opts.touchFlag || (gn->type & OP_MAKE)) {
|
||||
curTarg = gn;
|
||||
#ifdef USE_META
|
||||
if (useMeta && !NoExecute(gn)) {
|
||||
if (useMeta && GNode_ShouldExecute(gn)) {
|
||||
meta_job_start(NULL, gn);
|
||||
}
|
||||
#endif
|
||||
Lst_ForEach(gn->commands, CompatRunCommand, gn);
|
||||
RunCommands(gn);
|
||||
curTarg = NULL;
|
||||
} else {
|
||||
Job_Touch(gn, (gn->type & OP_SILENT) != 0);
|
||||
@ -600,7 +564,7 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
gn->made = ERROR;
|
||||
}
|
||||
#ifdef USE_META
|
||||
if (useMeta && !NoExecute(gn)) {
|
||||
if (useMeta && GNode_ShouldExecute(gn)) {
|
||||
if (meta_job_finish(NULL) != 0)
|
||||
gn->made = ERROR;
|
||||
}
|
||||
@ -619,24 +583,19 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
pgn->flags |= CHILDMADE;
|
||||
Make_TimeStamp(pgn, gn);
|
||||
}
|
||||
} else if (keepgoing) {
|
||||
} else if (opts.keepgoing) {
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
} else {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
exit(1);
|
||||
}
|
||||
} else if (gn->made == ERROR) {
|
||||
/*
|
||||
* Already had an error when making this beastie. Tell the parent
|
||||
* to abort.
|
||||
*/
|
||||
/* Already had an error when making this. Tell the parent to abort. */
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
} else {
|
||||
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
|
||||
char *p1;
|
||||
const char *target = Var_Value(TARGET, gn, &p1);
|
||||
const char *target = GNode_VarTarget(gn);
|
||||
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
|
||||
bmake_free(p1);
|
||||
}
|
||||
switch(gn->made) {
|
||||
case BEINGMADE:
|
||||
@ -661,8 +620,7 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
}
|
||||
|
||||
cohorts:
|
||||
Lst_ForEach(gn->cohorts, Compat_Make, pgnp);
|
||||
return 0;
|
||||
MakeNodes(gn->cohorts, pgn);
|
||||
}
|
||||
|
||||
/* Initialize this module and start making.
|
||||
@ -671,35 +629,33 @@ Compat_Make(void *gnp, void *pgnp)
|
||||
* targs The target nodes to re-create
|
||||
*/
|
||||
void
|
||||
Compat_Run(Lst targs)
|
||||
Compat_Run(GNodeList *targs)
|
||||
{
|
||||
GNode *gn = NULL;/* Current root target */
|
||||
int errors; /* Number of targets not remade due to errors */
|
||||
GNode *gn = NULL; /* Current root target */
|
||||
int errors; /* Number of targets not remade due to errors */
|
||||
|
||||
if (!shellName)
|
||||
Shell_Init();
|
||||
|
||||
if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) {
|
||||
if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGINT, CompatInterrupt);
|
||||
}
|
||||
if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) {
|
||||
if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGTERM, CompatInterrupt);
|
||||
}
|
||||
if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) {
|
||||
if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGHUP, CompatInterrupt);
|
||||
}
|
||||
if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
|
||||
if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGQUIT, CompatInterrupt);
|
||||
}
|
||||
|
||||
ENDNode = Targ_FindNode(".END", TARG_CREATE);
|
||||
ENDNode->type = OP_SPECIAL;
|
||||
/* Create the .END node now, to keep the (debug) output of the
|
||||
* counter.mk test the same as before 2020-09-23. This implementation
|
||||
* detail probably doesn't matter though. */
|
||||
(void)Targ_GetEndNode();
|
||||
/*
|
||||
* If the user has defined a .BEGIN target, execute the commands attached
|
||||
* to it.
|
||||
*/
|
||||
if (!queryFlag) {
|
||||
gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
|
||||
if (!opts.queryFlag) {
|
||||
gn = Targ_FindNode(".BEGIN");
|
||||
if (gn != NULL) {
|
||||
Compat_Make(gn, gn);
|
||||
if (gn->made == ERROR) {
|
||||
@ -719,11 +675,11 @@ Compat_Run(Lst targs)
|
||||
* For each entry in the list of targets to create, call Compat_Make on
|
||||
* it to create the thing. Compat_Make will leave the 'made' field of gn
|
||||
* in one of several states:
|
||||
* UPTODATE gn was already up-to-date
|
||||
* MADE gn was recreated successfully
|
||||
* ERROR An error occurred while gn was being created
|
||||
* ABORTED gn was not remade because one of its inferiors
|
||||
* could not be made due to errors.
|
||||
* UPTODATE gn was already up-to-date
|
||||
* MADE gn was recreated successfully
|
||||
* ERROR An error occurred while gn was being created
|
||||
* ABORTED gn was not remade because one of its inferiors
|
||||
* could not be made due to errors.
|
||||
*/
|
||||
errors = 0;
|
||||
while (!Lst_IsEmpty(targs)) {
|
||||
@ -734,7 +690,7 @@ Compat_Run(Lst targs)
|
||||
printf("`%s' is up to date.\n", gn->name);
|
||||
} else if (gn->made == ABORTED) {
|
||||
printf("`%s' not remade because of errors.\n", gn->name);
|
||||
errors += 1;
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -742,7 +698,9 @@ Compat_Run(Lst targs)
|
||||
* If the user has defined a .END target, run its commands.
|
||||
*/
|
||||
if (errors == 0) {
|
||||
Compat_Make(ENDNode, ENDNode);
|
||||
GNode *endNode = Targ_GetEndNode();
|
||||
Compat_Make(endNode, endNode);
|
||||
/* XXX: Did you mean endNode->made instead of gn->made? */
|
||||
if (gn->made == ERROR) {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
exit(1);
|
||||
|
File diff suppressed because it is too large
Load Diff
27
contrib/bmake/configure
vendored
27
contrib/bmake/configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for bmake 20200710.
|
||||
# Generated by GNU Autoconf 2.69 for bmake 20201018.
|
||||
#
|
||||
# Report bugs to <sjg@NetBSD.org>.
|
||||
#
|
||||
@ -580,8 +580,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='bmake'
|
||||
PACKAGE_TARNAME='bmake'
|
||||
PACKAGE_VERSION='20200710'
|
||||
PACKAGE_STRING='bmake 20200710'
|
||||
PACKAGE_VERSION='20201018'
|
||||
PACKAGE_STRING='bmake 20201018'
|
||||
PACKAGE_BUGREPORT='sjg@NetBSD.org'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@ -1254,7 +1254,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures bmake 20200710 to adapt to many kinds of systems.
|
||||
\`configure' configures bmake 20201018 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@ -1315,7 +1315,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of bmake 20200710:";;
|
||||
short | recursive ) echo "Configuration of bmake 20201018:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1323,16 +1323,16 @@ Optional Features:
|
||||
--disable-option-checking ignore unrecognized --enable/--with options
|
||||
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
|
||||
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
|
||||
--disable-pwd-override disable \$PWD overriding getcwd()
|
||||
--disable-pwd-override disable $PWD overriding getcwd()
|
||||
--disable-check-make-chdir disable make trying to guess
|
||||
when it should automatically cd \${.CURDIR}
|
||||
when it should automatically cd ${.CURDIR}
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions
|
||||
--without-makefile dissable use of generated makefile
|
||||
--without-meta dissable use of meta-mode
|
||||
--without-makefile disable use of generated makefile
|
||||
--without-meta disable use of meta-mode
|
||||
--with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev
|
||||
--with-machine=MACHINE explicitly set MACHINE
|
||||
--with-force-machine=MACHINE set FORCE_MACHINE
|
||||
@ -1421,7 +1421,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
bmake configure 20200710
|
||||
bmake configure 20201018
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@ -2001,7 +2001,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by bmake $as_me 20200710, which was
|
||||
It was created by bmake $as_me 20201018, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@ -4543,7 +4543,6 @@ if test $bmake_path_max -gt 1024; then
|
||||
fi
|
||||
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
|
||||
|
||||
$ac_includes_default
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
|
||||
$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
|
||||
if ${ac_cv_header_sys_wait_h+:} false; then :
|
||||
@ -6665,7 +6664,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by bmake $as_me 20200710, which was
|
||||
This file was extended by bmake $as_me 20201018, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@ -6727,7 +6726,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
bmake config.status 20200710
|
||||
bmake config.status 20201018
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
dnl
|
||||
dnl RCSid:
|
||||
dnl $Id: configure.in,v 1.66 2020/07/10 16:34:38 sjg Exp $
|
||||
dnl $Id: configure.in,v 1.67 2020/10/19 19:47:50 sjg Exp $
|
||||
dnl
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
dnl
|
||||
AC_PREREQ(2.50)
|
||||
AC_INIT([bmake], [20200710], [sjg@NetBSD.org])
|
||||
AC_INIT([bmake], [20201018], [sjg@NetBSD.org])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
dnl make srcdir absolute
|
||||
@ -38,7 +38,7 @@ CYGWIN*|MINGW*) use_makefile=no;;
|
||||
*) use_makefile=yes;;
|
||||
esac
|
||||
AC_ARG_WITH(makefile,
|
||||
[ --without-makefile dissable use of generated makefile],
|
||||
[ --without-makefile disable use of generated makefile],
|
||||
[case "${withval}" in
|
||||
yes|no) use_makefile=${withval};;
|
||||
*) AC_MSG_ERROR(bad value ${withval} given for makefile) ;;
|
||||
@ -46,7 +46,7 @@ esac])
|
||||
dnl
|
||||
use_meta=yes
|
||||
AC_ARG_WITH(meta,
|
||||
[ --without-meta dissable use of meta-mode],
|
||||
[ --without-meta disable use of meta-mode],
|
||||
[case "${withval}" in
|
||||
yes|no) use_meta=${withval};;
|
||||
*) AC_MSG_ERROR(bad value ${withval} given for meta) ;;
|
||||
@ -128,7 +128,6 @@ dnl AC_C_CROSS
|
||||
dnl
|
||||
|
||||
dnl Checks for header files.
|
||||
AC_INCLUDES_DEFAULT
|
||||
AC_HEADER_SYS_WAIT
|
||||
AC_HEADER_DIRENT
|
||||
dnl Keep this list sorted
|
||||
@ -352,7 +351,7 @@ dnl
|
||||
dnl And this can be handy to do with out.
|
||||
dnl
|
||||
AC_ARG_ENABLE(pwd-override,
|
||||
[ --disable-pwd-override disable \$PWD overriding getcwd()],
|
||||
[ --disable-pwd-override disable $PWD overriding getcwd()],
|
||||
[case "${enableval}" in
|
||||
yes) ;;
|
||||
no) CPPFLAGS="$CPPFLAGS -DNO_PWD_OVERRIDE" ;;
|
||||
@ -363,7 +362,7 @@ dnl Just for grins
|
||||
dnl
|
||||
AC_ARG_ENABLE(check-make-chdir,
|
||||
[ --disable-check-make-chdir disable make trying to guess
|
||||
when it should automatically cd \${.CURDIR}],
|
||||
when it should automatically cd ${.CURDIR}],
|
||||
[case "${enableval}" in
|
||||
yes) ;;
|
||||
no) CPPFLAGS="$CPPFLAGS -DNO_CHECK_MAKE_CHDIR" ;;
|
||||
|
1187
contrib/bmake/dir.c
1187
contrib/bmake/dir.c
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.h,v 1.23 2020/09/02 04:08:54 rillig Exp $ */
|
||||
/* $NetBSD: dir.h,v 1.32 2020/10/25 10:00:20 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -75,20 +75,21 @@
|
||||
#ifndef MAKE_DIR_H
|
||||
#define MAKE_DIR_H
|
||||
|
||||
/* A cache of a directory, remembering all the files that exist in that
|
||||
* directory. */
|
||||
typedef struct {
|
||||
char *name; /* Name of directory */
|
||||
int refCount; /* Number of paths with this directory */
|
||||
int hits; /* the number of times a file in this
|
||||
/* A cache for the filenames in a directory. */
|
||||
typedef struct CachedDir {
|
||||
char *name; /* Name of directory, either absolute or
|
||||
* relative to the current directory.
|
||||
* The name is not normalized in any way,
|
||||
* that is, "." and "./." are different.
|
||||
*
|
||||
* Not sure what happens when .CURDIR is
|
||||
* assigned a new value; see Parse_DoVar. */
|
||||
int refCount; /* Number of SearchPaths with this directory */
|
||||
int hits; /* The number of times a file in this
|
||||
* directory has been found */
|
||||
Hash_Table files; /* Hash set of files in directory */
|
||||
} Path;
|
||||
|
||||
struct make_stat {
|
||||
time_t mst_mtime;
|
||||
mode_t mst_mode;
|
||||
};
|
||||
HashTable files; /* Hash set of files in directory;
|
||||
* all values are NULL. */
|
||||
} CachedDir;
|
||||
|
||||
void Dir_Init(void);
|
||||
void Dir_InitDir(const char *);
|
||||
@ -97,18 +98,24 @@ void Dir_InitDot(void);
|
||||
void Dir_End(void);
|
||||
void Dir_SetPATH(void);
|
||||
Boolean Dir_HasWildcards(const char *);
|
||||
void Dir_Expand(const char *, Lst, Lst);
|
||||
char *Dir_FindFile(const char *, Lst);
|
||||
Boolean Dir_FindHereOrAbove(const char *, const char *, char *, int);
|
||||
int Dir_MTime(GNode *, Boolean);
|
||||
Path *Dir_AddDir(Lst, const char *);
|
||||
char *Dir_MakeFlags(const char *, Lst);
|
||||
void Dir_ClearPath(Lst);
|
||||
void Dir_Concat(Lst, Lst);
|
||||
void Dir_Expand(const char *, SearchPath *, StringList *);
|
||||
char *Dir_FindFile(const char *, SearchPath *);
|
||||
char *Dir_FindHereOrAbove(const char *, const char *);
|
||||
time_t Dir_MTime(GNode *, Boolean);
|
||||
CachedDir *Dir_AddDir(SearchPath *, const char *);
|
||||
char *Dir_MakeFlags(const char *, SearchPath *);
|
||||
void Dir_ClearPath(SearchPath *);
|
||||
void Dir_Concat(SearchPath *, SearchPath *);
|
||||
void Dir_PrintDirectories(void);
|
||||
void Dir_PrintPath(Lst);
|
||||
void Dir_PrintPath(SearchPath *);
|
||||
void Dir_Destroy(void *);
|
||||
void *Dir_CopyDir(void *);
|
||||
SearchPath *Dir_CopyDirSearchPath(void);
|
||||
|
||||
/* Stripped-down variant of struct stat. */
|
||||
struct make_stat {
|
||||
time_t mst_mtime;
|
||||
mode_t mst_mode;
|
||||
};
|
||||
|
||||
int cached_lstat(const char *, struct make_stat *);
|
||||
int cached_stat(const char *, struct make_stat *);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $ */
|
||||
/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */
|
||||
|
||||
/*
|
||||
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
|
||||
@ -27,20 +27,9 @@
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: enum.c,v 1.6 2020/09/01 20:34:51 rillig Exp $");
|
||||
#endif
|
||||
#endif
|
||||
#include "make.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "enum.h"
|
||||
MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $");
|
||||
|
||||
/* Convert a bitset into a string representation, showing the names of the
|
||||
* individual bits.
|
||||
@ -59,7 +48,7 @@ Enum_FlagsToString(char *buf, size_t buf_size,
|
||||
size_t name_len;
|
||||
|
||||
if ((value & spec->es_value) != spec->es_value)
|
||||
continue;
|
||||
continue;
|
||||
value &= ~spec->es_value;
|
||||
|
||||
assert(buf_size >= sep_len + 1);
|
||||
@ -93,8 +82,8 @@ const char *
|
||||
Enum_ValueToString(int value, const EnumToStringSpec *spec)
|
||||
{
|
||||
for (; spec->es_name[0] != '\0'; spec++) {
|
||||
if (value == spec->es_value)
|
||||
return spec->es_name;
|
||||
if (value == spec->es_value)
|
||||
return spec->es_name;
|
||||
}
|
||||
abort(/* unknown enum value */);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: enum.h,v 1.9 2020/09/01 20:34:51 rillig Exp $ */
|
||||
/* $NetBSD: enum.h,v 1.12 2020/09/25 15:54:50 rillig Exp $ */
|
||||
|
||||
/*
|
||||
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
typedef struct EnumToStringSpec {
|
||||
int es_value;
|
||||
const char *es_name;
|
||||
} EnumToStringSpec;
|
||||
@ -107,16 +107,38 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
|
||||
enum { typnam ## _ ## ToStringSize = sizeof joined }
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 2 flags. */
|
||||
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_1(v1), \
|
||||
ENUM__SPEC_1(v2)), \
|
||||
ENUM__JOIN_2( \
|
||||
ENUM__JOIN_STR_1(v1), \
|
||||
ENUM__JOIN_STR_1(v2)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 3 flags. */
|
||||
#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_2(v1, v2), \
|
||||
ENUM__SPEC_1(v3)), \
|
||||
ENUM__SPEC_2(v1, v2), \
|
||||
ENUM__SPEC_1(v3)), \
|
||||
ENUM__JOIN_2( \
|
||||
ENUM__JOIN_STR_2(v1, v2), \
|
||||
ENUM__JOIN_STR_1(v3)))
|
||||
ENUM__JOIN_STR_2(v1, v2), \
|
||||
ENUM__JOIN_STR_1(v3)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 6 flags. */
|
||||
#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_4(v1, v2, v3, v4), \
|
||||
ENUM__SPEC_2(v5, v6)), \
|
||||
ENUM__JOIN_2( \
|
||||
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
|
||||
ENUM__JOIN_STR_2(v5, v6)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 8 flags. */
|
||||
@ -156,8 +178,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
v17, v18, v19, v20, v21, v22, v23, v24, \
|
||||
v25, v26, v27, v28, v29, v30, v31) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_5( \
|
||||
ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
ENUM__SPECS_5( \
|
||||
ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16), \
|
||||
ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \
|
||||
ENUM__SPEC_4(v25, v26, v27, v28), \
|
||||
@ -179,8 +201,8 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
v17, v18, v19, v20, v21, v22, v23, v24, \
|
||||
v25, v26, v27, v28, v29, v30, v31, v32) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16), \
|
||||
ENUM__SPEC_16(v17, v18, v19, v20, v21, v22, v23, v24, \
|
||||
v25, v26, v27, v28, v29, v30, v31, v32)), \
|
||||
@ -188,6 +210,6 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16), \
|
||||
ENUM__JOIN_STR_16(v17, v18, v19, v20, v21, v22, v23, v24, \
|
||||
v25, v26, v27, v28, v29, v30, v31, v32)))
|
||||
v25, v26, v27, v28, v29, v30, v31, v32)))
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon.h,v 1.2 2020/01/22 22:10:36 sjg Exp $ */
|
||||
/* $NetBSD: filemon.h,v 1.3 2020/10/18 11:49:47 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
@ -29,8 +29,8 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef FILEMON_H_
|
||||
#define FILEMON_H_
|
||||
#ifndef MAKE_FILEMON_H
|
||||
#define MAKE_FILEMON_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
@ -50,4 +50,4 @@ int filemon_setpid_child(const struct filemon *, pid_t);
|
||||
int filemon_readfd(const struct filemon *);
|
||||
int filemon_process(struct filemon *);
|
||||
|
||||
#endif /* FILEMON_H_ */
|
||||
#endif /* MAKE_FILEMON_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.2 2020/01/19 20:22:57 riastradh Exp $ */
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.3 2020/10/18 11:54:43 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
@ -478,7 +478,7 @@ filemon_dispatch(struct filemon *F)
|
||||
*/
|
||||
/* XXX What to do if syscall code doesn't match? */
|
||||
if (S->i == S->npath && S->syscode == ret->ktr_code)
|
||||
(*S->show)(F, S, ret);
|
||||
S->show(F, S, ret);
|
||||
|
||||
/* Free the state now that it is no longer active. */
|
||||
for (i = 0; i < S->i; i++)
|
||||
@ -771,7 +771,7 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
const register_t *args = (const void *)&call[1];
|
||||
int status = args[0];
|
||||
int status = (int)args[0];
|
||||
|
||||
if (F->out) {
|
||||
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
|
||||
@ -806,7 +806,7 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
|
||||
|
||||
if (call->ktr_argsize < 2)
|
||||
return NULL;
|
||||
flags = args[1];
|
||||
flags = (int)args[1];
|
||||
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1, &show_open_readwrite);
|
||||
@ -827,8 +827,8 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
|
||||
|
||||
if (call->ktr_argsize < 3)
|
||||
return NULL;
|
||||
fd = args[0];
|
||||
flags = args[2];
|
||||
fd = (int)args[0];
|
||||
flags = (int)args[2];
|
||||
|
||||
if (fd == AT_CWD) {
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $ */
|
||||
/* $NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1992, The Regents of the University of California.
|
||||
@ -29,88 +29,136 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: for.c,v 1.67 2020/08/30 19:56:02 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
/*-
|
||||
* for.c --
|
||||
* Functions to handle loops in a makefile.
|
||||
* Handling of .for/.endfor loops in a makefile.
|
||||
*
|
||||
* Interface:
|
||||
* For_Eval Evaluate the loop in the passed line.
|
||||
* For_Run Run accumulated loop
|
||||
* For loops are of the form:
|
||||
*
|
||||
*/
|
||||
|
||||
#include "make.h"
|
||||
#include "strlist.h"
|
||||
|
||||
#define FOR_SUB_ESCAPE_CHAR 1
|
||||
#define FOR_SUB_ESCAPE_BRACE 2
|
||||
#define FOR_SUB_ESCAPE_PAREN 4
|
||||
|
||||
/*
|
||||
* For statements are of the form:
|
||||
*
|
||||
* .for <variable> in <varlist>
|
||||
* .for <varname...> in <value...>
|
||||
* ...
|
||||
* .endfor
|
||||
*
|
||||
* The trick is to look for the matching end inside for for loop
|
||||
* To do that, we count the current nesting level of the for loops.
|
||||
* and the .endfor statements, accumulating all the statements between
|
||||
* the initial .for loop and the matching .endfor;
|
||||
* then we evaluate the for loop for each variable in the varlist.
|
||||
* When a .for line is parsed, all following lines are accumulated into a
|
||||
* buffer, up to but excluding the corresponding .endfor line. To find the
|
||||
* corresponding .endfor, the number of nested .for and .endfor directives
|
||||
* are counted.
|
||||
*
|
||||
* Note that any nested fors are just passed through; they get handled
|
||||
* recursively in For_Eval when we're expanding the enclosing for in
|
||||
* For_Run.
|
||||
* During parsing, any nested .for loops are just passed through; they get
|
||||
* handled recursively in For_Eval when the enclosing .for loop is evaluated
|
||||
* in For_Run.
|
||||
*
|
||||
* When the .for loop has been parsed completely, the variable expressions
|
||||
* for the iteration variables are replaced with expressions of the form
|
||||
* ${:Uvalue}, and then this modified body is "included" as a special file.
|
||||
*
|
||||
* Interface:
|
||||
* For_Eval Evaluate the loop in the passed line.
|
||||
*
|
||||
* For_Run Run accumulated loop
|
||||
*/
|
||||
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.112 2020/10/31 18:41:07 rillig Exp $");
|
||||
|
||||
/* The .for loop substitutes the items as ${:U<value>...}, which means
|
||||
* that characters that break this syntax must be backslash-escaped. */
|
||||
typedef enum ForEscapes {
|
||||
FOR_SUB_ESCAPE_CHAR = 0x0001,
|
||||
FOR_SUB_ESCAPE_BRACE = 0x0002,
|
||||
FOR_SUB_ESCAPE_PAREN = 0x0004
|
||||
} ForEscapes;
|
||||
|
||||
static int forLevel = 0; /* Nesting level */
|
||||
|
||||
/* One of the variables to the left of the "in" in a .for loop. */
|
||||
typedef struct ForVar {
|
||||
char *name;
|
||||
size_t len;
|
||||
} ForVar;
|
||||
|
||||
/*
|
||||
* State of a for loop.
|
||||
*/
|
||||
typedef struct {
|
||||
Buffer buf; /* Body of loop */
|
||||
strlist_t vars; /* Iteration variables */
|
||||
strlist_t items; /* Substitution items */
|
||||
char *parse_buf;
|
||||
int short_var;
|
||||
int sub_next;
|
||||
typedef struct For {
|
||||
Buffer body; /* Unexpanded body of the loop */
|
||||
Vector /* of ForVar */ vars; /* Iteration variables */
|
||||
Words items; /* Substitution items */
|
||||
Buffer curBody; /* Expanded body of the current iteration */
|
||||
/* Is any of the names 1 character long? If so, when the variable values
|
||||
* are substituted, the parser must handle $V expressions as well, not
|
||||
* only ${V} and $(V). */
|
||||
Boolean short_var;
|
||||
unsigned int sub_next; /* Where to continue iterating */
|
||||
} For;
|
||||
|
||||
static For *accumFor; /* Loop being accumulated */
|
||||
|
||||
|
||||
static void
|
||||
For_Free(For *arg)
|
||||
ForAddVar(For *f, const char *name, size_t len)
|
||||
{
|
||||
Buf_Destroy(&arg->buf, TRUE);
|
||||
strlist_clean(&arg->vars);
|
||||
strlist_clean(&arg->items);
|
||||
free(arg->parse_buf);
|
||||
|
||||
free(arg);
|
||||
ForVar *var = Vector_Push(&f->vars);
|
||||
var->name = bmake_strldup(name, len);
|
||||
var->len = len;
|
||||
}
|
||||
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* For_Eval --
|
||||
* Evaluate the for loop in the passed line. The line
|
||||
* looks like this:
|
||||
* .for <variable> in <varlist>
|
||||
static void
|
||||
For_Free(For *f)
|
||||
{
|
||||
Buf_Destroy(&f->body, TRUE);
|
||||
|
||||
while (f->vars.len > 0) {
|
||||
ForVar *var = Vector_Pop(&f->vars);
|
||||
free(var->name);
|
||||
}
|
||||
Vector_Done(&f->vars);
|
||||
|
||||
Words_Free(f->items);
|
||||
Buf_Destroy(&f->curBody, TRUE);
|
||||
|
||||
free(f);
|
||||
}
|
||||
|
||||
static ForEscapes
|
||||
GetEscapes(const char *word)
|
||||
{
|
||||
const char *p;
|
||||
ForEscapes escapes = 0;
|
||||
|
||||
for (p = word; *p != '\0'; p++) {
|
||||
switch (*p) {
|
||||
case ':':
|
||||
case '$':
|
||||
case '\\':
|
||||
escapes |= FOR_SUB_ESCAPE_CHAR;
|
||||
break;
|
||||
case ')':
|
||||
escapes |= FOR_SUB_ESCAPE_PAREN;
|
||||
break;
|
||||
case '}':
|
||||
escapes |= FOR_SUB_ESCAPE_BRACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return escapes;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
IsFor(const char *p)
|
||||
{
|
||||
return p[0] == 'f' && p[1] == 'o' && p[2] == 'r' && ch_isspace(p[3]);
|
||||
}
|
||||
|
||||
static Boolean
|
||||
IsEndfor(const char *p)
|
||||
{
|
||||
return p[0] == 'e' && strncmp(p, "endfor", 6) == 0 &&
|
||||
(p[6] == '\0' || ch_isspace(p[6]));
|
||||
}
|
||||
|
||||
/* Evaluate the for loop in the passed line. The line looks like this:
|
||||
* .for <varname...> in <value...>
|
||||
*
|
||||
* Input:
|
||||
* line Line to parse
|
||||
@ -119,178 +167,130 @@ For_Free(For *arg)
|
||||
* 0: Not a .for statement, parse the line
|
||||
* 1: We found a for loop
|
||||
* -1: A .for statement with a bad syntax error, discard.
|
||||
*
|
||||
* Side Effects:
|
||||
* None.
|
||||
*
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
int
|
||||
For_Eval(char *line)
|
||||
For_Eval(const char *line)
|
||||
{
|
||||
For *new_for;
|
||||
char *ptr = line, *sub;
|
||||
size_t len;
|
||||
int escapes;
|
||||
unsigned char ch;
|
||||
Words words;
|
||||
For *f;
|
||||
const char *p;
|
||||
|
||||
/* Skip the '.' and any following whitespace */
|
||||
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
|
||||
continue;
|
||||
p = line + 1; /* skip the '.' */
|
||||
cpp_skip_whitespace(&p);
|
||||
|
||||
/*
|
||||
* If we are not in a for loop quickly determine if the statement is
|
||||
* a for.
|
||||
*/
|
||||
if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' ||
|
||||
!isspace((unsigned char)ptr[3])) {
|
||||
if (ptr[0] == 'e' && strncmp(ptr + 1, "ndfor", 5) == 0) {
|
||||
if (!IsFor(p)) {
|
||||
if (IsEndfor(p)) {
|
||||
Parse_Error(PARSE_FATAL, "for-less endfor");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ptr += 3;
|
||||
p += 3;
|
||||
|
||||
/*
|
||||
* we found a for loop, and now we are going to parse it.
|
||||
*/
|
||||
|
||||
new_for = bmake_malloc(sizeof *new_for);
|
||||
memset(new_for, 0, sizeof *new_for);
|
||||
f = bmake_malloc(sizeof *f);
|
||||
Buf_Init(&f->body, 0);
|
||||
Vector_Init(&f->vars, sizeof(ForVar));
|
||||
f->items.words = NULL;
|
||||
f->items.freeIt = NULL;
|
||||
Buf_Init(&f->curBody, 0);
|
||||
f->short_var = FALSE;
|
||||
f->sub_next = 0;
|
||||
|
||||
/* Grab the variables. Terminate on "in". */
|
||||
for (;; ptr += len) {
|
||||
while (*ptr && isspace((unsigned char)*ptr))
|
||||
ptr++;
|
||||
if (*ptr == '\0') {
|
||||
for (;;) {
|
||||
size_t len;
|
||||
|
||||
cpp_skip_whitespace(&p);
|
||||
if (*p == '\0') {
|
||||
Parse_Error(PARSE_FATAL, "missing `in' in for");
|
||||
For_Free(new_for);
|
||||
For_Free(f);
|
||||
return -1;
|
||||
}
|
||||
for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++)
|
||||
|
||||
/* XXX: This allows arbitrary variable names; see directive-for.mk. */
|
||||
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
|
||||
continue;
|
||||
if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') {
|
||||
ptr += 2;
|
||||
|
||||
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (len == 1)
|
||||
new_for->short_var = 1;
|
||||
strlist_add_str(&new_for->vars, bmake_strldup(ptr, len), len);
|
||||
f->short_var = TRUE;
|
||||
|
||||
ForAddVar(f, p, len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
if (strlist_num(&new_for->vars) == 0) {
|
||||
if (f->vars.len == 0) {
|
||||
Parse_Error(PARSE_FATAL, "no iteration variables in for");
|
||||
For_Free(new_for);
|
||||
For_Free(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (*ptr && isspace((unsigned char)*ptr))
|
||||
ptr++;
|
||||
|
||||
/*
|
||||
* Make a list with the remaining words
|
||||
* The values are substituted as ${:U<value>...} so we must \ escape
|
||||
* characters that break that syntax.
|
||||
* Variables are fully expanded - so it is safe for escape $.
|
||||
* We can't do the escapes here - because we don't know whether
|
||||
* we are substuting into ${...} or $(...).
|
||||
*/
|
||||
sub = Var_Subst(ptr, VAR_GLOBAL, VARE_WANTRES);
|
||||
|
||||
/*
|
||||
* Split into words allowing for quoted strings.
|
||||
*/
|
||||
words = Str_Words(sub, FALSE);
|
||||
|
||||
free(sub);
|
||||
cpp_skip_whitespace(&p);
|
||||
|
||||
{
|
||||
size_t n;
|
||||
char *items;
|
||||
(void)Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items);
|
||||
/* TODO: handle errors */
|
||||
f->items = Str_Words(items, FALSE);
|
||||
free(items);
|
||||
|
||||
for (n = 0; n < words.len; n++) {
|
||||
ptr = words.words[n];
|
||||
if (!*ptr)
|
||||
continue;
|
||||
escapes = 0;
|
||||
while ((ch = *ptr++)) {
|
||||
switch (ch) {
|
||||
case ':':
|
||||
case '$':
|
||||
case '\\':
|
||||
escapes |= FOR_SUB_ESCAPE_CHAR;
|
||||
break;
|
||||
case ')':
|
||||
escapes |= FOR_SUB_ESCAPE_PAREN;
|
||||
break;
|
||||
case /*{*/ '}':
|
||||
escapes |= FOR_SUB_ESCAPE_BRACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We have to dup words[n] to maintain the semantics of
|
||||
* strlist.
|
||||
*/
|
||||
strlist_add_str(&new_for->items, bmake_strdup(words.words[n]),
|
||||
escapes);
|
||||
}
|
||||
if (f->items.len == 1 && f->items.words[0][0] == '\0')
|
||||
f->items.len = 0; /* .for var in ${:U} */
|
||||
}
|
||||
|
||||
Words_Free(words);
|
||||
{
|
||||
size_t nitems, nvars;
|
||||
|
||||
if ((len = strlist_num(&new_for->items)) > 0 &&
|
||||
len % (n = strlist_num(&new_for->vars))) {
|
||||
if ((nitems = f->items.len) > 0 && nitems % (nvars = f->vars.len)) {
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"Wrong number of words (%zu) in .for substitution list"
|
||||
" with %zu vars", len, n);
|
||||
" with %zu variables", nitems, nvars);
|
||||
/*
|
||||
* Return 'success' so that the body of the .for loop is
|
||||
* accumulated.
|
||||
* Remove all items so that the loop doesn't iterate.
|
||||
*/
|
||||
strlist_clean(&new_for->items);
|
||||
f->items.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Buf_Init(&new_for->buf, 0);
|
||||
accumFor = new_for;
|
||||
accumFor = f;
|
||||
forLevel = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add another line to a .for loop.
|
||||
* Returns 0 when the matching .endfor is reached.
|
||||
* Returns FALSE when the matching .endfor is reached.
|
||||
*/
|
||||
|
||||
int
|
||||
For_Accum(char *line)
|
||||
Boolean
|
||||
For_Accum(const char *line)
|
||||
{
|
||||
char *ptr = line;
|
||||
const char *ptr = line;
|
||||
|
||||
if (*ptr == '.') {
|
||||
ptr++;
|
||||
cpp_skip_whitespace(&ptr);
|
||||
|
||||
for (ptr++; *ptr && isspace((unsigned char)*ptr); ptr++)
|
||||
continue;
|
||||
|
||||
if (strncmp(ptr, "endfor", 6) == 0 &&
|
||||
(isspace((unsigned char)ptr[6]) || !ptr[6])) {
|
||||
if (DEBUG(FOR))
|
||||
(void)fprintf(debug_file, "For: end for %d\n", forLevel);
|
||||
if (IsEndfor(ptr)) {
|
||||
DEBUG1(FOR, "For: end for %d\n", forLevel);
|
||||
if (--forLevel <= 0)
|
||||
return 0;
|
||||
} else if (strncmp(ptr, "for", 3) == 0 &&
|
||||
isspace((unsigned char)ptr[3])) {
|
||||
return FALSE;
|
||||
} else if (IsFor(ptr)) {
|
||||
forLevel++;
|
||||
if (DEBUG(FOR))
|
||||
(void)fprintf(debug_file, "For: new loop %d\n", forLevel);
|
||||
DEBUG1(FOR, "For: new loop %d\n", forLevel);
|
||||
}
|
||||
}
|
||||
|
||||
Buf_AddStr(&accumFor->buf, line);
|
||||
Buf_AddByte(&accumFor->buf, '\n');
|
||||
return 1;
|
||||
Buf_AddStr(&accumFor->body, line);
|
||||
Buf_AddByte(&accumFor->body, '\n');
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@ -326,23 +326,25 @@ for_var_len(const char *var)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* While expanding the body of a .for loop, write the item in the ${:U...}
|
||||
* expression, escaping characters as needed. See ApplyModifier_Defined. */
|
||||
static void
|
||||
for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
|
||||
Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
|
||||
{
|
||||
ForEscapes escapes = GetEscapes(item);
|
||||
char ch;
|
||||
|
||||
const char *item = strlist_str(items, item_no);
|
||||
|
||||
/* If there were no escapes, or the only escape is the other variable
|
||||
* terminator, then just substitute the full string */
|
||||
if (!(strlist_info(items, item_no) &
|
||||
(ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) {
|
||||
if (!(escapes & (ech == ')' ? ~(unsigned)FOR_SUB_ESCAPE_BRACE
|
||||
: ~(unsigned)FOR_SUB_ESCAPE_PAREN))) {
|
||||
Buf_AddStr(cmds, item);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Escape ':', '$', '\\' and 'ech' - removed by :U processing */
|
||||
while ((ch = *item++) != 0) {
|
||||
/* Escape ':', '$', '\\' and 'ech' - these will be removed later by
|
||||
* :U processing, see ApplyModifier_Defined. */
|
||||
while ((ch = *item++) != '\0') {
|
||||
if (ch == '$') {
|
||||
size_t len = for_var_len(item);
|
||||
if (len != 0) {
|
||||
@ -357,111 +359,141 @@ for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech)
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
For_Iterate(void *v_arg, size_t *ret_len)
|
||||
/* While expanding the body of a .for loop, replace expressions like
|
||||
* ${i}, ${i:...}, $(i) or $(i:...) with their ${:U...} expansion. */
|
||||
static void
|
||||
SubstVarLong(For *f, const char **pp, const char **inout_mark, char ech)
|
||||
{
|
||||
For *arg = v_arg;
|
||||
int i;
|
||||
char *var;
|
||||
char *cp;
|
||||
char *cmd_cp;
|
||||
char *body_end;
|
||||
char ch;
|
||||
Buffer cmds;
|
||||
size_t cmd_len;
|
||||
size_t i;
|
||||
const char *p = *pp;
|
||||
|
||||
if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) {
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
ForVar *forVar = Vector_Get(&f->vars, i);
|
||||
char *var = forVar->name;
|
||||
size_t vlen = forVar->len;
|
||||
|
||||
/* XXX: undefined behavior for p if vlen is longer than p? */
|
||||
if (memcmp(p, var, vlen) != 0)
|
||||
continue;
|
||||
/* XXX: why test for backslash here? */
|
||||
if (p[vlen] != ':' && p[vlen] != ech && p[vlen] != '\\')
|
||||
continue;
|
||||
|
||||
/* Found a variable match. Replace with :U<value> */
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
|
||||
Buf_AddStr(&f->curBody, ":U");
|
||||
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], ech);
|
||||
|
||||
p += vlen;
|
||||
*inout_mark = p;
|
||||
break;
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
}
|
||||
|
||||
/* While expanding the body of a .for loop, replace single-character
|
||||
* variable expressions like $i with their ${:U...} expansion. */
|
||||
static void
|
||||
SubstVarShort(For *f, char const ch, const char **pp, const char **inout_mark)
|
||||
{
|
||||
const char *p = *pp;
|
||||
size_t i;
|
||||
|
||||
/* Probably a single character name, ignore $$ and stupid ones. */
|
||||
if (!f->short_var || strchr("}):$", ch) != NULL) {
|
||||
p++;
|
||||
*pp = p;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
ForVar *var = Vector_Get(&f->vars, i);
|
||||
const char *varname = var->name;
|
||||
if (varname[0] != ch || varname[1] != '\0')
|
||||
continue;
|
||||
|
||||
/* Found a variable match. Replace with ${:U<value>} */
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
|
||||
Buf_AddStr(&f->curBody, "{:U");
|
||||
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
|
||||
Buf_AddByte(&f->curBody, '}');
|
||||
|
||||
*inout_mark = ++p;
|
||||
break;
|
||||
}
|
||||
|
||||
*pp = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the for loop body and replace references to the loop variables
|
||||
* with variable references that expand to the required text.
|
||||
*
|
||||
* Using variable expansions ensures that the .for loop can't generate
|
||||
* syntax, and that the later parsing will still see a variable.
|
||||
* We assume that the null variable will never be defined.
|
||||
*
|
||||
* The detection of substitutions of the loop control variable is naive.
|
||||
* Many of the modifiers use \ to escape $ (not $) so it is possible
|
||||
* to contrive a makefile where an unwanted substitution happens.
|
||||
*/
|
||||
static char *
|
||||
ForIterate(void *v_arg, size_t *out_len)
|
||||
{
|
||||
For *f = v_arg;
|
||||
const char *p;
|
||||
const char *mark; /* where the last replacement left off */
|
||||
const char *body_end;
|
||||
char *cmds_str;
|
||||
|
||||
if (f->sub_next + f->vars.len > f->items.len) {
|
||||
/* No more iterations */
|
||||
For_Free(arg);
|
||||
For_Free(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(arg->parse_buf);
|
||||
arg->parse_buf = NULL;
|
||||
Buf_Empty(&f->curBody);
|
||||
|
||||
/*
|
||||
* Scan the for loop body and replace references to the loop variables
|
||||
* with variable references that expand to the required text.
|
||||
* Using variable expansions ensures that the .for loop can't generate
|
||||
* syntax, and that the later parsing will still see a variable.
|
||||
* We assume that the null variable will never be defined.
|
||||
*
|
||||
* The detection of substitions of the loop control variable is naive.
|
||||
* Many of the modifiers use \ to escape $ (not $) so it is possible
|
||||
* to contrive a makefile where an unwanted substitution happens.
|
||||
*/
|
||||
|
||||
cmd_cp = Buf_GetAll(&arg->buf, &cmd_len);
|
||||
body_end = cmd_cp + cmd_len;
|
||||
Buf_Init(&cmds, cmd_len + 256);
|
||||
for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) {
|
||||
char ech;
|
||||
ch = *++cp;
|
||||
mark = Buf_GetAll(&f->body, NULL);
|
||||
body_end = mark + Buf_Len(&f->body);
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
char ch, ech;
|
||||
ch = *++p;
|
||||
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) {
|
||||
cp++;
|
||||
p++;
|
||||
/* Check variable name against the .for loop variables */
|
||||
STRLIST_FOREACH(var, &arg->vars, i) {
|
||||
size_t vlen = strlist_info(&arg->vars, i);
|
||||
if (memcmp(cp, var, vlen) != 0)
|
||||
continue;
|
||||
if (cp[vlen] != ':' && cp[vlen] != ech && cp[vlen] != '\\')
|
||||
continue;
|
||||
/* Found a variable match. Replace with :U<value> */
|
||||
Buf_AddBytesBetween(&cmds, cmd_cp, cp);
|
||||
Buf_AddStr(&cmds, ":U");
|
||||
cp += vlen;
|
||||
cmd_cp = cp;
|
||||
for_substitute(&cmds, &arg->items, arg->sub_next + i, ech);
|
||||
break;
|
||||
}
|
||||
SubstVarLong(f, &p, &mark, ech);
|
||||
continue;
|
||||
}
|
||||
if (ch == 0)
|
||||
if (ch == '\0')
|
||||
break;
|
||||
/* Probably a single character name, ignore $$ and stupid ones. {*/
|
||||
if (!arg->short_var || strchr("}):$", ch) != NULL) {
|
||||
cp++;
|
||||
continue;
|
||||
}
|
||||
STRLIST_FOREACH(var, &arg->vars, i) {
|
||||
if (var[0] != ch || var[1] != 0)
|
||||
continue;
|
||||
/* Found a variable match. Replace with ${:U<value>} */
|
||||
Buf_AddBytesBetween(&cmds, cmd_cp, cp);
|
||||
Buf_AddStr(&cmds, "{:U");
|
||||
cmd_cp = ++cp;
|
||||
for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}');
|
||||
Buf_AddByte(&cmds, '}');
|
||||
break;
|
||||
}
|
||||
|
||||
SubstVarShort(f, ch, &p, &mark);
|
||||
}
|
||||
Buf_AddBytesBetween(&cmds, cmd_cp, body_end);
|
||||
Buf_AddBytesBetween(&f->curBody, mark, body_end);
|
||||
|
||||
cp = Buf_Destroy(&cmds, FALSE);
|
||||
if (DEBUG(FOR))
|
||||
(void)fprintf(debug_file, "For: loop body:\n%s", cp);
|
||||
*out_len = Buf_Len(&f->curBody);
|
||||
cmds_str = Buf_GetAll(&f->curBody, NULL);
|
||||
DEBUG1(FOR, "For: loop body:\n%s", cmds_str);
|
||||
|
||||
arg->sub_next += strlist_num(&arg->vars);
|
||||
f->sub_next += f->vars.len;
|
||||
|
||||
arg->parse_buf = cp;
|
||||
*ret_len = strlen(cp);
|
||||
return cp;
|
||||
return cmds_str;
|
||||
}
|
||||
|
||||
/* Run the for loop, imitating the actions of an include file. */
|
||||
void
|
||||
For_Run(int lineno)
|
||||
{
|
||||
For *arg;
|
||||
|
||||
arg = accumFor;
|
||||
For *f = accumFor;
|
||||
accumFor = NULL;
|
||||
|
||||
if (strlist_num(&arg->items) == 0) {
|
||||
if (f->items.len == 0) {
|
||||
/* Nothing to expand - possibly due to an earlier syntax error. */
|
||||
For_Free(arg);
|
||||
For_Free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
Parse_SetInput(NULL, lineno, -1, For_Iterate, arg);
|
||||
Parse_SetInput(NULL, lineno, -1, ForIterate, f);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $ */
|
||||
/* $NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -69,336 +69,242 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93";
|
||||
#else
|
||||
__RCSID("$NetBSD: hash.c,v 1.29 2020/09/01 21:11:31 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
/* Hash tables with string keys. */
|
||||
|
||||
/* hash.c --
|
||||
*
|
||||
* This module contains routines to manipulate a hash table.
|
||||
* See hash.h for a definition of the structure of the hash
|
||||
* table. Hash tables grow automatically as the amount of
|
||||
* information increases.
|
||||
*/
|
||||
#include "make.h"
|
||||
|
||||
/*
|
||||
* Forward references to local procedures that are used before they're
|
||||
* defined:
|
||||
*/
|
||||
|
||||
static void RebuildTable(Hash_Table *);
|
||||
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.55 2020/10/25 19:28:44 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The following defines the ratio of # entries to # buckets
|
||||
* at which we rebuild the table to make it larger.
|
||||
* The ratio of # entries to # buckets at which we rebuild the table to
|
||||
* make it larger.
|
||||
*/
|
||||
|
||||
#define rebuildLimit 3
|
||||
|
||||
/* The hash function(s) */
|
||||
|
||||
#ifndef HASH
|
||||
/* The default: this one matches Gosling's emacs */
|
||||
#define HASH(h, key, p) do { \
|
||||
for (h = 0, p = key; *p;) \
|
||||
h = (h << 5) - h + *p++; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
/* Sets up the hash table.
|
||||
*
|
||||
* Input:
|
||||
* t Structure to to hold the table.
|
||||
* numBuckets How many buckets to create for starters. This
|
||||
* number is rounded up to a power of two. If
|
||||
* <= 0, a reasonable default is chosen. The
|
||||
* table will grow in size later as needed.
|
||||
*/
|
||||
void
|
||||
Hash_InitTable(Hash_Table *t, int numBuckets)
|
||||
/* This hash function matches Gosling's emacs and java.lang.String. */
|
||||
static unsigned int
|
||||
hash(const char *key, size_t *out_keylen)
|
||||
{
|
||||
int i;
|
||||
struct Hash_Entry **hp;
|
||||
|
||||
/*
|
||||
* Round up the size to a power of two.
|
||||
*/
|
||||
if (numBuckets <= 0)
|
||||
i = 16;
|
||||
else {
|
||||
for (i = 2; i < numBuckets; i <<= 1)
|
||||
continue;
|
||||
}
|
||||
t->numEntries = 0;
|
||||
t->maxchain = 0;
|
||||
t->bucketsSize = i;
|
||||
t->bucketsMask = i - 1;
|
||||
t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
|
||||
while (--i >= 0)
|
||||
*hp++ = NULL;
|
||||
unsigned int h = 0;
|
||||
const char *p = key;
|
||||
while (*p != '\0')
|
||||
h = (h << 5) - h + (unsigned char)*p++;
|
||||
if (out_keylen != NULL)
|
||||
*out_keylen = (size_t)(p - key);
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Removes everything from the hash table and frees up the memory space it
|
||||
* occupied (except for the space in the Hash_Table structure). */
|
||||
void
|
||||
Hash_DeleteTable(Hash_Table *t)
|
||||
unsigned int
|
||||
Hash_Hash(const char *key)
|
||||
{
|
||||
struct Hash_Entry **hp, *h, *nexth = NULL;
|
||||
int i;
|
||||
return hash(key, NULL);
|
||||
}
|
||||
|
||||
for (hp = t->buckets, i = t->bucketsSize; --i >= 0;) {
|
||||
for (h = *hp++; h != NULL; h = nexth) {
|
||||
nexth = h->next;
|
||||
free(h);
|
||||
static HashEntry *
|
||||
HashTable_Find(HashTable *t, unsigned int h, const char *key)
|
||||
{
|
||||
HashEntry *e;
|
||||
unsigned int chainlen = 0;
|
||||
|
||||
#ifdef DEBUG_HASH_LOOKUP
|
||||
DEBUG4(HASH, "%s: %p h=%08x key=%s\n", __func__, t, h, key);
|
||||
#endif
|
||||
|
||||
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
|
||||
chainlen++;
|
||||
if (e->key_hash == h && strcmp(e->key, key) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (chainlen > t->maxchain)
|
||||
t->maxchain = chainlen;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Set up the hash table. */
|
||||
void
|
||||
HashTable_Init(HashTable *t)
|
||||
{
|
||||
unsigned int n = 16, i;
|
||||
HashEntry **buckets = bmake_malloc(sizeof(*buckets) * n);
|
||||
for (i = 0; i < n; i++)
|
||||
buckets[i] = NULL;
|
||||
|
||||
t->buckets = buckets;
|
||||
t->bucketsSize = n;
|
||||
t->numEntries = 0;
|
||||
t->bucketsMask = n - 1;
|
||||
t->maxchain = 0;
|
||||
}
|
||||
|
||||
/* Remove everything from the hash table and frees up the memory. */
|
||||
void
|
||||
HashTable_Done(HashTable *t)
|
||||
{
|
||||
HashEntry **buckets = t->buckets;
|
||||
size_t i, n = t->bucketsSize;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
HashEntry *he = buckets[i];
|
||||
while (he != NULL) {
|
||||
HashEntry *next = he->next;
|
||||
free(he);
|
||||
he = next;
|
||||
}
|
||||
}
|
||||
free(t->buckets);
|
||||
|
||||
/*
|
||||
* Set up the hash table to cause memory faults on any future access
|
||||
* attempts until re-initialization.
|
||||
*/
|
||||
#ifdef CLEANUP
|
||||
t->buckets = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Searches the hash table for an entry corresponding to the key.
|
||||
*
|
||||
* Input:
|
||||
* t Hash table to search.
|
||||
* key A hash key.
|
||||
*
|
||||
* Results:
|
||||
* Returns a pointer to the entry for key, or NULL if the table contains
|
||||
* no entry for the key.
|
||||
*/
|
||||
Hash_Entry *
|
||||
Hash_FindEntry(Hash_Table *t, const char *key)
|
||||
/* Find the entry corresponding to the key, or return NULL. */
|
||||
HashEntry *
|
||||
HashTable_FindEntry(HashTable *t, const char *key)
|
||||
{
|
||||
Hash_Entry *e;
|
||||
unsigned h;
|
||||
const char *p;
|
||||
int chainlen;
|
||||
|
||||
if (t == NULL || t->buckets == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
HASH(h, key, p);
|
||||
p = key;
|
||||
chainlen = 0;
|
||||
#ifdef DEBUG_HASH_LOOKUP
|
||||
if (DEBUG(HASH))
|
||||
fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
|
||||
t, h, key);
|
||||
#endif
|
||||
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
|
||||
chainlen++;
|
||||
if (e->namehash == h && strcmp(e->name, p) == 0)
|
||||
break;
|
||||
}
|
||||
if (chainlen > t->maxchain)
|
||||
t->maxchain = chainlen;
|
||||
return e;
|
||||
unsigned int h = hash(key, NULL);
|
||||
return HashTable_Find(t, h, key);
|
||||
}
|
||||
|
||||
/* Searches the hash table for an entry corresponding to the key.
|
||||
* If no entry is found, then one is created.
|
||||
*
|
||||
* Input:
|
||||
* t Hash table to search.
|
||||
* key A hash key.
|
||||
* newPtr Filled with TRUE if new entry created,
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
Hash_Entry *
|
||||
Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
|
||||
/* Find the value corresponding to the key, or return NULL. */
|
||||
void *
|
||||
HashTable_FindValue(HashTable *t, const char *key)
|
||||
{
|
||||
Hash_Entry *e;
|
||||
unsigned h;
|
||||
const char *p;
|
||||
int keylen;
|
||||
int chainlen;
|
||||
struct Hash_Entry **hp;
|
||||
HashEntry *he = HashTable_FindEntry(t, key);
|
||||
return he != NULL ? he->value : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash the key. As a side effect, save the length (strlen) of the
|
||||
* key in case we need to create the entry.
|
||||
*/
|
||||
HASH(h, key, p);
|
||||
keylen = p - key;
|
||||
p = key;
|
||||
chainlen = 0;
|
||||
#ifdef DEBUG_HASH_LOOKUP
|
||||
if (DEBUG(HASH))
|
||||
fprintf(debug_file, "%s: %p h=%x key=%s\n", __func__,
|
||||
t, h, key);
|
||||
#endif
|
||||
for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
|
||||
chainlen++;
|
||||
if (e->namehash == h && strcmp(e->name, p) == 0) {
|
||||
if (newPtr != NULL)
|
||||
*newPtr = FALSE;
|
||||
break;
|
||||
/* Find the value corresponding to the key and the precomputed hash,
|
||||
* or return NULL. */
|
||||
void *
|
||||
HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
|
||||
{
|
||||
HashEntry *he = HashTable_Find(t, h, key);
|
||||
return he != NULL ? he->value : NULL;
|
||||
}
|
||||
|
||||
/* Make the hash table larger. Any bucket numbers from the old table become
|
||||
* invalid; the hash codes stay valid though. */
|
||||
static void
|
||||
HashTable_Enlarge(HashTable *t)
|
||||
{
|
||||
unsigned int oldSize = t->bucketsSize;
|
||||
HashEntry **oldBuckets = t->buckets;
|
||||
unsigned int newSize = 2 * oldSize;
|
||||
unsigned int newMask = newSize - 1;
|
||||
HashEntry **newBuckets = bmake_malloc(sizeof(*newBuckets) * newSize);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < newSize; i++)
|
||||
newBuckets[i] = NULL;
|
||||
|
||||
for (i = 0; i < oldSize; i++) {
|
||||
HashEntry *he = oldBuckets[i];
|
||||
while (he != NULL) {
|
||||
HashEntry *next = he->next;
|
||||
he->next = newBuckets[he->key_hash & newMask];
|
||||
newBuckets[he->key_hash & newMask] = he;
|
||||
he = next;
|
||||
}
|
||||
}
|
||||
if (chainlen > t->maxchain)
|
||||
t->maxchain = chainlen;
|
||||
if (e)
|
||||
return e;
|
||||
|
||||
/*
|
||||
* The desired entry isn't there. Before allocating a new entry,
|
||||
* expand the table if necessary (and this changes the resulting
|
||||
* bucket chain).
|
||||
*/
|
||||
if (t->numEntries >= rebuildLimit * t->bucketsSize)
|
||||
RebuildTable(t);
|
||||
e = bmake_malloc(sizeof(*e) + keylen);
|
||||
hp = &t->buckets[h & t->bucketsMask];
|
||||
e->next = *hp;
|
||||
*hp = e;
|
||||
Hash_SetValue(e, NULL);
|
||||
e->namehash = h;
|
||||
(void)strcpy(e->name, p);
|
||||
t->numEntries++;
|
||||
free(oldBuckets);
|
||||
|
||||
if (newPtr != NULL)
|
||||
*newPtr = TRUE;
|
||||
return e;
|
||||
t->bucketsSize = newSize;
|
||||
t->bucketsMask = newMask;
|
||||
t->buckets = newBuckets;
|
||||
DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
|
||||
__func__, t, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
t->maxchain = 0;
|
||||
}
|
||||
|
||||
/* Delete the given hash table entry and free memory associated with it. */
|
||||
void
|
||||
Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
|
||||
/* Find or create an entry corresponding to the key.
|
||||
* Return in out_isNew whether a new entry has been created. */
|
||||
HashEntry *
|
||||
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
|
||||
{
|
||||
Hash_Entry **hp, *p;
|
||||
size_t keylen;
|
||||
unsigned int h = hash(key, &keylen);
|
||||
HashEntry *he = HashTable_Find(t, h, key);
|
||||
|
||||
if (e == NULL)
|
||||
return;
|
||||
for (hp = &t->buckets[e->namehash & t->bucketsMask];
|
||||
(p = *hp) != NULL; hp = &p->next) {
|
||||
if (p == e) {
|
||||
*hp = p->next;
|
||||
if (he != NULL) {
|
||||
if (out_isNew != NULL)
|
||||
*out_isNew = FALSE;
|
||||
return he;
|
||||
}
|
||||
|
||||
if (t->numEntries >= rebuildLimit * t->bucketsSize)
|
||||
HashTable_Enlarge(t);
|
||||
|
||||
he = bmake_malloc(sizeof(*he) + keylen);
|
||||
he->value = NULL;
|
||||
he->key_hash = h;
|
||||
memcpy(he->key, key, keylen + 1);
|
||||
|
||||
he->next = t->buckets[h & t->bucketsMask];
|
||||
t->buckets[h & t->bucketsMask] = he;
|
||||
t->numEntries++;
|
||||
|
||||
if (out_isNew != NULL)
|
||||
*out_isNew = TRUE;
|
||||
return he;
|
||||
}
|
||||
|
||||
/* Delete the entry from the table and free the associated memory. */
|
||||
void
|
||||
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
|
||||
{
|
||||
HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
|
||||
HashEntry *p;
|
||||
|
||||
for (; (p = *ref) != NULL; ref = &p->next) {
|
||||
if (p == he) {
|
||||
*ref = p->next;
|
||||
free(p);
|
||||
t->numEntries--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
(void)write(2, "bad call to Hash_DeleteEntry\n", 29);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Sets things up for enumerating all entries in the hash table.
|
||||
*
|
||||
* Input:
|
||||
* t Table to be searched.
|
||||
* searchPtr Area in which to keep state about search.
|
||||
*
|
||||
* Results:
|
||||
* The return value is the address of the first entry in
|
||||
* the hash table, or NULL if the table is empty.
|
||||
*/
|
||||
Hash_Entry *
|
||||
Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr)
|
||||
/* Set things up for iterating over all entries in the hash table. */
|
||||
void
|
||||
HashIter_Init(HashIter *hi, HashTable *t)
|
||||
{
|
||||
searchPtr->table = t;
|
||||
searchPtr->nextBucket = 0;
|
||||
searchPtr->entry = NULL;
|
||||
return Hash_EnumNext(searchPtr);
|
||||
hi->table = t;
|
||||
hi->nextBucket = 0;
|
||||
hi->entry = NULL;
|
||||
}
|
||||
|
||||
/* Returns the next entry in the hash table, or NULL if the end of the table
|
||||
* is reached.
|
||||
*
|
||||
* Input:
|
||||
* searchPtr Area used to keep state about search.
|
||||
*/
|
||||
Hash_Entry *
|
||||
Hash_EnumNext(Hash_Search *searchPtr)
|
||||
/* Return the next entry in the hash table, or NULL if the end of the table
|
||||
* is reached. */
|
||||
HashEntry *
|
||||
HashIter_Next(HashIter *hi)
|
||||
{
|
||||
Hash_Entry *e;
|
||||
Hash_Table *t = searchPtr->table;
|
||||
HashTable *t = hi->table;
|
||||
HashEntry *he = hi->entry;
|
||||
HashEntry **buckets = t->buckets;
|
||||
unsigned int bucketsSize = t->bucketsSize;
|
||||
|
||||
/*
|
||||
* The entry field points to the most recently returned
|
||||
* entry, or is NULL if we are starting up. If not NULL, we have
|
||||
* to start at the next one in the chain.
|
||||
*/
|
||||
e = searchPtr->entry;
|
||||
if (e != NULL)
|
||||
e = e->next;
|
||||
/*
|
||||
* If the chain ran out, or if we are starting up, we need to
|
||||
* find the next nonempty chain.
|
||||
*/
|
||||
while (e == NULL) {
|
||||
if (searchPtr->nextBucket >= t->bucketsSize)
|
||||
if (he != NULL)
|
||||
he = he->next; /* skip the most recently returned entry */
|
||||
|
||||
while (he == NULL) { /* find the next nonempty chain */
|
||||
if (hi->nextBucket >= bucketsSize)
|
||||
return NULL;
|
||||
e = t->buckets[searchPtr->nextBucket++];
|
||||
he = buckets[hi->nextBucket++];
|
||||
}
|
||||
searchPtr->entry = e;
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Makes a new hash table that is larger than the old one. The entire hash
|
||||
* table is moved, so any bucket numbers from the old table become invalid. */
|
||||
static void
|
||||
RebuildTable(Hash_Table *t)
|
||||
{
|
||||
Hash_Entry *e, *next = NULL, **hp, **xp;
|
||||
int i, mask;
|
||||
Hash_Entry **oldhp;
|
||||
int oldsize;
|
||||
|
||||
oldhp = t->buckets;
|
||||
oldsize = i = t->bucketsSize;
|
||||
i <<= 1;
|
||||
t->bucketsSize = i;
|
||||
t->bucketsMask = mask = i - 1;
|
||||
t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
|
||||
while (--i >= 0)
|
||||
*hp++ = NULL;
|
||||
for (hp = oldhp, i = oldsize; --i >= 0;) {
|
||||
for (e = *hp++; e != NULL; e = next) {
|
||||
next = e->next;
|
||||
xp = &t->buckets[e->namehash & mask];
|
||||
e->next = *xp;
|
||||
*xp = e;
|
||||
}
|
||||
}
|
||||
free(oldhp);
|
||||
if (DEBUG(HASH))
|
||||
fprintf(debug_file, "%s: %p size=%d entries=%d maxchain=%d\n",
|
||||
__func__, t, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
t->maxchain = 0;
|
||||
hi->entry = he;
|
||||
return he;
|
||||
}
|
||||
|
||||
void
|
||||
Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
|
||||
HashTable_DebugStats(HashTable *t, const char *name)
|
||||
{
|
||||
Hash_Search search;
|
||||
Hash_Entry *e;
|
||||
|
||||
for (e = Hash_EnumFirst(t, &search);
|
||||
e != NULL;
|
||||
e = Hash_EnumNext(&search))
|
||||
action(Hash_GetValue(e), data);
|
||||
}
|
||||
|
||||
void
|
||||
Hash_DebugStats(Hash_Table *t, const char *name)
|
||||
{
|
||||
if (DEBUG(HASH))
|
||||
fprintf(debug_file, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n",
|
||||
name, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
DEBUG4(HASH, "HashTable %s: size=%u numEntries=%u maxchain=%u\n",
|
||||
name, t->bucketsSize, t->numEntries, t->maxchain);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.h,v 1.21 2020/09/01 21:11:31 rillig Exp $ */
|
||||
/* $NetBSD: hash.h,v 1.31 2020/10/25 19:19:07 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -78,54 +78,54 @@
|
||||
#define MAKE_HASH_H
|
||||
|
||||
/* A single key-value entry in the hash table. */
|
||||
typedef struct Hash_Entry {
|
||||
struct Hash_Entry *next; /* Used to link together all the entries
|
||||
typedef struct HashEntry {
|
||||
struct HashEntry *next; /* Used to link together all the entries
|
||||
* associated with the same bucket. */
|
||||
void *value;
|
||||
unsigned namehash; /* hash value of key */
|
||||
char name[1]; /* key string, variable length */
|
||||
} Hash_Entry;
|
||||
void *value;
|
||||
unsigned int key_hash; /* hash value of the key */
|
||||
char key[1]; /* key string, variable length */
|
||||
} HashEntry;
|
||||
|
||||
/* The hash table containing the entries. */
|
||||
typedef struct Hash_Table {
|
||||
Hash_Entry **buckets; /* Pointers to Hash_Entry, one
|
||||
typedef struct HashTable {
|
||||
HashEntry **buckets; /* Pointers to HashEntry, one
|
||||
* for each bucket in the table. */
|
||||
int bucketsSize;
|
||||
int numEntries; /* Number of entries in the table. */
|
||||
int bucketsMask; /* Used to select the bucket for a hash. */
|
||||
int maxchain; /* max length of chain detected */
|
||||
} Hash_Table;
|
||||
unsigned int bucketsSize;
|
||||
unsigned int numEntries; /* Number of entries in the table. */
|
||||
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
|
||||
unsigned int maxchain; /* max length of chain detected */
|
||||
} HashTable;
|
||||
|
||||
/*
|
||||
* The following structure is used by the searching routines
|
||||
* to record where we are in the search.
|
||||
*/
|
||||
typedef struct Hash_Search {
|
||||
Hash_Table *table; /* Table being searched. */
|
||||
int nextBucket; /* Next bucket to check (after current). */
|
||||
Hash_Entry *entry; /* Next entry to check in current bucket. */
|
||||
} Hash_Search;
|
||||
/* State of an iteration over all entries in a table. */
|
||||
typedef struct HashIter {
|
||||
HashTable *table; /* Table being searched. */
|
||||
unsigned int nextBucket; /* Next bucket to check (after current). */
|
||||
HashEntry *entry; /* Next entry to check in current bucket. */
|
||||
} HashIter;
|
||||
|
||||
static inline void * MAKE_ATTR_UNUSED
|
||||
Hash_GetValue(Hash_Entry *h)
|
||||
static inline MAKE_ATTR_UNUSED void *
|
||||
HashEntry_Get(HashEntry *h)
|
||||
{
|
||||
return h->value;
|
||||
}
|
||||
|
||||
static inline void MAKE_ATTR_UNUSED
|
||||
Hash_SetValue(Hash_Entry *h, void *datum)
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
HashEntry_Set(HashEntry *h, void *datum)
|
||||
{
|
||||
h->value = datum;
|
||||
}
|
||||
|
||||
void Hash_InitTable(Hash_Table *, int);
|
||||
void Hash_DeleteTable(Hash_Table *);
|
||||
Hash_Entry *Hash_FindEntry(Hash_Table *, const char *);
|
||||
Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *);
|
||||
void Hash_DeleteEntry(Hash_Table *, Hash_Entry *);
|
||||
Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *);
|
||||
Hash_Entry *Hash_EnumNext(Hash_Search *);
|
||||
void Hash_ForEach(Hash_Table *, void (*)(void *, void *), void *);
|
||||
void Hash_DebugStats(Hash_Table *, const char *);
|
||||
void HashTable_Init(HashTable *);
|
||||
void HashTable_Done(HashTable *);
|
||||
HashEntry *HashTable_FindEntry(HashTable *, const char *);
|
||||
void *HashTable_FindValue(HashTable *, const char *);
|
||||
unsigned int Hash_Hash(const char *);
|
||||
void *HashTable_FindValueHash(HashTable *, const char *, unsigned int);
|
||||
HashEntry *HashTable_CreateEntry(HashTable *, const char *, Boolean *);
|
||||
void HashTable_DeleteEntry(HashTable *, HashEntry *);
|
||||
void HashTable_DebugStats(HashTable *, const char *);
|
||||
|
||||
void HashIter_Init(HashIter *, HashTable *);
|
||||
HashEntry *HashIter_Next(HashIter *);
|
||||
|
||||
#endif /* MAKE_HASH_H */
|
||||
|
1610
contrib/bmake/job.c
1610
contrib/bmake/job.c
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.h,v 1.47 2020/08/29 12:20:17 rillig Exp $ */
|
||||
/* $NetBSD: job.h,v 1.58 2020/10/26 21:34:10 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -73,10 +73,10 @@
|
||||
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
|
||||
*/
|
||||
|
||||
/*-
|
||||
* job.h --
|
||||
* Definitions pertaining to the running of jobs in parallel mode.
|
||||
/*
|
||||
* Running of jobs in parallel mode.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_JOB_H
|
||||
#define MAKE_JOB_H
|
||||
|
||||
@ -110,28 +110,6 @@ emul_poll(struct pollfd *fd, int nfd, int timeout);
|
||||
*/
|
||||
#define POLL_MSEC 5000
|
||||
|
||||
/*-
|
||||
* Job Table definitions.
|
||||
*
|
||||
* Each job has several things associated with it:
|
||||
* 1) The process id of the child shell
|
||||
* 2) The graph node describing the target being made by this job
|
||||
* 3) A LstNode for the first command to be saved after the job
|
||||
* completes. This is NULL if there was no "..." in the job's
|
||||
* commands.
|
||||
* 4) An FILE* for writing out the commands. This is only
|
||||
* used before the job is actually started.
|
||||
* 5) The output is being caught via a pipe and
|
||||
* the descriptors of our pipe, an array in which output is line
|
||||
* buffered and the current position in that buffer are all
|
||||
* maintained for each job.
|
||||
* 6) A word of flags which determine how the module handles errors,
|
||||
* echoing, etc. for the job
|
||||
*
|
||||
* When a job is finished, the Make_Update function is called on each of the
|
||||
* parents of the node which was just remade. This takes care of the upward
|
||||
* traversal of the dependency graph.
|
||||
*/
|
||||
struct pollfd;
|
||||
|
||||
|
||||
@ -139,122 +117,90 @@ struct pollfd;
|
||||
# include "meta.h"
|
||||
#endif
|
||||
|
||||
#define JOB_BUFSIZE 1024
|
||||
typedef struct Job {
|
||||
int pid; /* The child's process ID */
|
||||
GNode *node; /* The target the child is making */
|
||||
LstNode tailCmds; /* The node of the first command to be
|
||||
* saved when the job has been run */
|
||||
FILE *cmdFILE; /* When creating the shell script, this is
|
||||
* where the commands go */
|
||||
int exit_status; /* from wait4() in signal handler */
|
||||
char job_state; /* status of the job entry */
|
||||
#define JOB_ST_FREE 0 /* Job is available */
|
||||
#define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */
|
||||
#define JOB_ST_RUNNING 3 /* Job is running, pid valid */
|
||||
#define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */
|
||||
char job_suspended;
|
||||
short flags; /* Flags to control treatment of job */
|
||||
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
|
||||
#define JOB_SILENT 0x002 /* no output */
|
||||
#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally
|
||||
* if we can't export it and maxLocal is 0 */
|
||||
#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
|
||||
* commands */
|
||||
#define JOB_TRACED 0x400 /* we've sent 'set -x' */
|
||||
typedef enum JobState {
|
||||
JOB_ST_FREE = 0, /* Job is available */
|
||||
JOB_ST_SETUP = 1, /* Job is allocated but otherwise invalid */
|
||||
JOB_ST_RUNNING = 3, /* Job is running, pid valid */
|
||||
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
|
||||
} JobState;
|
||||
|
||||
int jobPipe[2]; /* Pipe for reading output from job */
|
||||
typedef enum JobFlags {
|
||||
/* Ignore non-zero exits */
|
||||
JOB_IGNERR = 0x001,
|
||||
/* no output */
|
||||
JOB_SILENT = 0x002,
|
||||
/* Target is a special one. i.e. run it locally
|
||||
* if we can't export it and maxLocal is 0 */
|
||||
JOB_SPECIAL = 0x004,
|
||||
/* Ignore "..." lines when processing commands */
|
||||
JOB_IGNDOTS = 0x008,
|
||||
/* we've sent 'set -x' */
|
||||
JOB_TRACED = 0x400
|
||||
} JobFlags;
|
||||
|
||||
/* A Job manages the shell commands that are run to create a single target.
|
||||
* Each job is run in a separate subprocess by a shell. Several jobs can run
|
||||
* in parallel.
|
||||
*
|
||||
* The shell commands for the target are written to a temporary file,
|
||||
* then the shell is run with the temporary file as stdin, and the output
|
||||
* of that shell is captured via a pipe.
|
||||
*
|
||||
* When a job is finished, Make_Update updates all parents of the node
|
||||
* that was just remade, marking them as ready to be made next if all
|
||||
* other dependencies are finished as well. */
|
||||
typedef struct Job {
|
||||
/* The process ID of the shell running the commands */
|
||||
int pid;
|
||||
|
||||
/* The target the child is making */
|
||||
GNode *node;
|
||||
|
||||
/* If one of the shell commands is "...", all following commands are
|
||||
* delayed until the .END node is made. This list node points to the
|
||||
* first of these commands, if any. */
|
||||
StringListNode *tailCmds;
|
||||
|
||||
/* When creating the shell script, this is where the commands go.
|
||||
* This is only used before the job is actually started. */
|
||||
FILE *cmdFILE;
|
||||
|
||||
int exit_status; /* from wait4() in signal handler */
|
||||
|
||||
JobState job_state; /* status of the job entry */
|
||||
|
||||
char job_suspended;
|
||||
|
||||
JobFlags flags; /* Flags to control treatment of job */
|
||||
|
||||
int inPipe; /* Pipe for reading output from job */
|
||||
int outPipe; /* Pipe for writing control commands */
|
||||
struct pollfd *inPollfd; /* pollfd associated with inPipe */
|
||||
char outBuf[JOB_BUFSIZE + 1];
|
||||
/* Buffer for storing the output of the
|
||||
* job, line by line */
|
||||
int curPos; /* Current position in op_outBuf */
|
||||
|
||||
#define JOB_BUFSIZE 1024
|
||||
/* Buffer for storing the output of the job, line by line. */
|
||||
char outBuf[JOB_BUFSIZE + 1];
|
||||
size_t curPos; /* Current position in outBuf. */
|
||||
|
||||
#ifdef USE_META
|
||||
struct BuildMon bm;
|
||||
struct BuildMon bm;
|
||||
#endif
|
||||
} Job;
|
||||
|
||||
#define inPipe jobPipe[0]
|
||||
#define outPipe jobPipe[1]
|
||||
|
||||
/*-
|
||||
* Shell Specifications:
|
||||
* Each shell type has associated with it the following information:
|
||||
* 1) The string which must match the last character of the shell name
|
||||
* for the shell to be considered of this type. The longest match
|
||||
* wins.
|
||||
* 2) A command to issue to turn off echoing of command lines
|
||||
* 3) A command to issue to turn echoing back on again
|
||||
* 4) What the shell prints, and its length, when given the echo-off
|
||||
* command. This line will not be printed when received from the shell
|
||||
* 5) A boolean to tell if the shell has the ability to control
|
||||
* error checking for individual commands.
|
||||
* 6) The string to turn this checking on.
|
||||
* 7) The string to turn it off.
|
||||
* 8) The command-flag to give to cause the shell to start echoing
|
||||
* commands right away.
|
||||
* 9) The command-flag to cause the shell to Lib_Exit when an error is
|
||||
* detected in one of the commands.
|
||||
*
|
||||
* Some special stuff goes on if a shell doesn't have error control. In such
|
||||
* a case, errCheck becomes a printf template for echoing the command,
|
||||
* should echoing be on and ignErr becomes another printf template for
|
||||
* executing the command while ignoring the return status. Finally errOut
|
||||
* is a printf template for running the command and causing the shell to
|
||||
* exit on error. If any of these strings are empty when hasErrCtl is FALSE,
|
||||
* the command will be executed anyway as is and if it causes an error, so be
|
||||
* it. Any templates setup to echo the command will escape any '$ ` \ "'i
|
||||
* characters in the command string to avoid common problems with
|
||||
* echo "%s\n" as a template.
|
||||
*/
|
||||
typedef struct Shell {
|
||||
const char *name; /* the name of the shell. For Bourne and C
|
||||
* shells, this is used only to find the
|
||||
* shell description when used as the single
|
||||
* source of a .SHELL target. For user-defined
|
||||
* shells, this is the full path of the shell.
|
||||
*/
|
||||
Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */
|
||||
const char *echoOff; /* command to turn off echo */
|
||||
const char *echoOn; /* command to turn it back on again */
|
||||
const char *noPrint; /* command to skip when printing output from
|
||||
* shell. This is usually the command which
|
||||
* was executed to turn off echoing */
|
||||
int noPLen; /* length of noPrint command */
|
||||
Boolean hasErrCtl; /* set if can control error checking for
|
||||
* individual commands */
|
||||
const char *errCheck; /* string to turn error checking on */
|
||||
const char *ignErr; /* string to turn off error checking */
|
||||
const char *errOut; /* string to use for testing exit code */
|
||||
const char *newline; /* string literal that results in a newline
|
||||
* character when it appears outside of any
|
||||
* 'quote' or "quote" characters */
|
||||
char commentChar; /* character used by shell for comment lines */
|
||||
|
||||
/*
|
||||
* command-line flags
|
||||
*/
|
||||
const char *echo; /* echo commands */
|
||||
const char *exit; /* exit on error */
|
||||
} Shell;
|
||||
|
||||
extern const char *shellPath;
|
||||
extern const char *shellName;
|
||||
extern char *shellErrFlag;
|
||||
|
||||
extern int jobTokensRunning; /* tokens currently "out" */
|
||||
extern int maxJobs; /* Max jobs we can run */
|
||||
extern int jobTokensRunning; /* tokens currently "out" */
|
||||
|
||||
void Shell_Init(void);
|
||||
const char *Shell_GetNewline(void);
|
||||
void Job_Touch(GNode *, Boolean);
|
||||
Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...));
|
||||
Boolean Job_CheckCommands(GNode *, void (*abortProc)(const char *, ...));
|
||||
void Job_CatchChildren(void);
|
||||
void Job_CatchOutput(void);
|
||||
void Job_Make(GNode *);
|
||||
void Job_Init(void);
|
||||
Boolean Job_Empty(void);
|
||||
Boolean Job_ParseShell(char *);
|
||||
int Job_Finish(void);
|
||||
void Job_End(void);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $ */
|
||||
/* $NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -32,9 +32,9 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.91 2020/10/28 02:43:16 rillig Exp $");
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
@ -42,110 +42,34 @@
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "make.h"
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: lst.c,v 1.60 2020/08/31 05:56:02 rillig Exp $");
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
struct ListNode {
|
||||
struct ListNode *prev; /* previous element in list */
|
||||
struct ListNode *next; /* next in list */
|
||||
uint8_t useCount; /* Count of functions using the node.
|
||||
* node may not be deleted until count
|
||||
* goes to 0 */
|
||||
Boolean deleted; /* List node should be removed when done */
|
||||
union {
|
||||
void *datum; /* datum associated with this element */
|
||||
const GNode *gnode; /* alias, just for debugging */
|
||||
const char *str; /* alias, just for debugging */
|
||||
};
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Head, Middle, Tail, Unknown
|
||||
} Where;
|
||||
|
||||
struct List {
|
||||
LstNode first; /* first node in list */
|
||||
LstNode last; /* last node in list */
|
||||
|
||||
/* fields for sequential access */
|
||||
Boolean isOpen; /* true if list has been Lst_Open'ed */
|
||||
Where lastAccess; /* Where in the list the last access was */
|
||||
LstNode curr; /* current node, if open. NULL if
|
||||
* *just* opened */
|
||||
LstNode prev; /* Previous node, if open. Used by Lst_Remove */
|
||||
};
|
||||
|
||||
/* Allocate and initialize a list node.
|
||||
*
|
||||
* The fields 'prev' and 'next' must be initialized by the caller.
|
||||
*/
|
||||
static LstNode
|
||||
LstNodeNew(void *datum)
|
||||
static ListNode *
|
||||
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
|
||||
{
|
||||
LstNode node = bmake_malloc(sizeof *node);
|
||||
node->useCount = 0;
|
||||
node->deleted = FALSE;
|
||||
ListNode *node = bmake_malloc(sizeof *node);
|
||||
node->prev = prev;
|
||||
node->next = next;
|
||||
node->datum = datum;
|
||||
return node;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
LstIsEmpty(Lst list)
|
||||
{
|
||||
return list->first == NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize a new, empty list. */
|
||||
Lst
|
||||
Lst_Init(void)
|
||||
List *
|
||||
Lst_New(void)
|
||||
{
|
||||
Lst list = bmake_malloc(sizeof *list);
|
||||
List *list = bmake_malloc(sizeof *list);
|
||||
|
||||
list->first = NULL;
|
||||
list->last = NULL;
|
||||
list->isOpen = FALSE;
|
||||
list->lastAccess = Unknown;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Duplicate an entire list, usually by copying the datum pointers.
|
||||
* If copyProc is given, that function is used to create the new datum from the
|
||||
* old datum, usually by creating a copy of it. */
|
||||
Lst
|
||||
Lst_Copy(Lst list, LstCopyProc copyProc)
|
||||
{
|
||||
Lst newList;
|
||||
LstNode node;
|
||||
|
||||
assert(list != NULL);
|
||||
|
||||
newList = Lst_Init();
|
||||
|
||||
for (node = list->first; node != NULL; node = node->next) {
|
||||
void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
|
||||
Lst_Append(newList, datum);
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
/* Free a list and all its nodes. The list data itself are not freed though. */
|
||||
/* Free a list and all its nodes. The node data are not freed though. */
|
||||
void
|
||||
Lst_Free(Lst list)
|
||||
Lst_Free(List *list)
|
||||
{
|
||||
LstNode node;
|
||||
LstNode next;
|
||||
|
||||
assert(list != NULL);
|
||||
ListNode *node;
|
||||
ListNode *next;
|
||||
|
||||
for (node = list->first; node != NULL; node = next) {
|
||||
next = node->next;
|
||||
@ -158,13 +82,10 @@ Lst_Free(Lst list)
|
||||
/* Destroy a list and free all its resources. The freeProc is called with the
|
||||
* datum from each node in turn before the node is freed. */
|
||||
void
|
||||
Lst_Destroy(Lst list, LstFreeProc freeProc)
|
||||
Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
{
|
||||
LstNode node;
|
||||
LstNode next;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(freeProc != NULL);
|
||||
ListNode *node;
|
||||
ListNode *next;
|
||||
|
||||
for (node = list->first; node != NULL; node = next) {
|
||||
next = node->next;
|
||||
@ -179,44 +100,33 @@ Lst_Destroy(Lst list, LstFreeProc freeProc)
|
||||
* Functions to modify a list
|
||||
*/
|
||||
|
||||
/* Insert a new node with the given piece of data before the given node in the
|
||||
* given list. */
|
||||
/* Insert a new node with the datum before the given node. */
|
||||
void
|
||||
Lst_InsertBefore(Lst list, LstNode node, void *datum)
|
||||
Lst_InsertBefore(List *list, ListNode *node, void *datum)
|
||||
{
|
||||
LstNode newNode;
|
||||
ListNode *newNode;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(!LstIsEmpty(list));
|
||||
assert(node != NULL);
|
||||
assert(datum != NULL);
|
||||
|
||||
newNode = LstNodeNew(datum);
|
||||
newNode->prev = node->prev;
|
||||
newNode->next = node;
|
||||
newNode = LstNodeNew(node->prev, node, datum);
|
||||
|
||||
if (node->prev != NULL) {
|
||||
if (node->prev != NULL)
|
||||
node->prev->next = newNode;
|
||||
}
|
||||
node->prev = newNode;
|
||||
|
||||
if (node == list->first) {
|
||||
if (node == list->first)
|
||||
list->first = newNode;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a piece of data at the start of the given list. */
|
||||
void
|
||||
Lst_Prepend(Lst list, void *datum)
|
||||
Lst_Prepend(List *list, void *datum)
|
||||
{
|
||||
LstNode node;
|
||||
ListNode *node;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(datum != NULL);
|
||||
|
||||
node = LstNodeNew(datum);
|
||||
node->prev = NULL;
|
||||
node->next = list->first;
|
||||
node = LstNodeNew(NULL, list->first, datum);
|
||||
|
||||
if (list->first == NULL) {
|
||||
list->first = node;
|
||||
@ -229,16 +139,13 @@ Lst_Prepend(Lst list, void *datum)
|
||||
|
||||
/* Add a piece of data at the end of the given list. */
|
||||
void
|
||||
Lst_Append(Lst list, void *datum)
|
||||
Lst_Append(List *list, void *datum)
|
||||
{
|
||||
LstNode node;
|
||||
ListNode *node;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(datum != NULL);
|
||||
|
||||
node = LstNodeNew(datum);
|
||||
node->prev = list->last;
|
||||
node->next = NULL;
|
||||
node = LstNodeNew(list->last, NULL, datum);
|
||||
|
||||
if (list->last == NULL) {
|
||||
list->first = node;
|
||||
@ -252,265 +159,83 @@ Lst_Append(Lst list, void *datum)
|
||||
/* Remove the given node from the given list.
|
||||
* The datum stored in the node must be freed by the caller, if necessary. */
|
||||
void
|
||||
Lst_Remove(Lst list, LstNode node)
|
||||
Lst_Remove(List *list, ListNode *node)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(node != NULL);
|
||||
|
||||
/*
|
||||
* unlink it from the list
|
||||
*/
|
||||
if (node->next != NULL) {
|
||||
/* unlink it from its neighbors */
|
||||
if (node->next != NULL)
|
||||
node->next->prev = node->prev;
|
||||
}
|
||||
if (node->prev != NULL) {
|
||||
if (node->prev != NULL)
|
||||
node->prev->next = node->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* if either the first or last of the list point to this node,
|
||||
* adjust them accordingly
|
||||
*/
|
||||
if (list->first == node) {
|
||||
/* unlink it from the list */
|
||||
if (list->first == node)
|
||||
list->first = node->next;
|
||||
}
|
||||
if (list->last == node) {
|
||||
if (list->last == node)
|
||||
list->last = node->prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sequential access stuff. If the node we're removing is the current
|
||||
* node in the list, reset the current node to the previous one. If the
|
||||
* previous one was non-existent (prev == NULL), we set the
|
||||
* end to be Unknown, since it is.
|
||||
*/
|
||||
if (list->isOpen && list->curr == node) {
|
||||
list->curr = list->prev;
|
||||
if (list->curr == NULL) {
|
||||
list->lastAccess = Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* note that the datum is unmolested. The caller must free it as
|
||||
* necessary and as expected.
|
||||
*/
|
||||
if (node->useCount == 0) {
|
||||
free(node);
|
||||
} else {
|
||||
node->deleted = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node with the new datum. */
|
||||
void
|
||||
LstNode_Set(LstNode node, void *datum)
|
||||
LstNode_Set(ListNode *node, void *datum)
|
||||
{
|
||||
assert(node != NULL);
|
||||
assert(datum != NULL);
|
||||
|
||||
node->datum = datum;
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node to NULL. */
|
||||
/* Replace the datum in the given node to NULL.
|
||||
* Having NULL values in a list is unusual though. */
|
||||
void
|
||||
LstNode_SetNull(LstNode node)
|
||||
LstNode_SetNull(ListNode *node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
|
||||
node->datum = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Node-specific functions
|
||||
*/
|
||||
|
||||
/* Return the first node from the given list, or NULL if the list is empty. */
|
||||
LstNode
|
||||
Lst_First(Lst list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
|
||||
return list->first;
|
||||
}
|
||||
|
||||
/* Return the last node from the given list, or NULL if the list is empty. */
|
||||
LstNode
|
||||
Lst_Last(Lst list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
|
||||
return list->last;
|
||||
}
|
||||
|
||||
/* Return the successor to the given node on its list, or NULL. */
|
||||
LstNode
|
||||
LstNode_Next(LstNode node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
|
||||
return node->next;
|
||||
}
|
||||
|
||||
/* Return the predecessor to the given node on its list, or NULL. */
|
||||
LstNode
|
||||
LstNode_Prev(LstNode node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return node->prev;
|
||||
}
|
||||
|
||||
/* Return the datum stored in the given node. */
|
||||
void *
|
||||
LstNode_Datum(LstNode node)
|
||||
{
|
||||
assert(node != NULL);
|
||||
return node->datum;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Functions for entire lists
|
||||
*/
|
||||
|
||||
/* Return TRUE if the given list is empty. */
|
||||
Boolean
|
||||
Lst_IsEmpty(Lst list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
|
||||
return LstIsEmpty(list);
|
||||
}
|
||||
|
||||
/* Return the first node from the list for which the match function returns
|
||||
* TRUE, or NULL if none of the nodes matched. */
|
||||
LstNode
|
||||
Lst_Find(Lst list, LstFindProc match, const void *matchArgs)
|
||||
{
|
||||
return Lst_FindFrom(list, Lst_First(list), match, matchArgs);
|
||||
}
|
||||
|
||||
/* Return the first node from the list, starting at the given node, for which
|
||||
* the match function returns TRUE, or NULL if none of the nodes matches.
|
||||
*
|
||||
* The start node may be NULL, in which case nothing is found. This allows
|
||||
* for passing Lst_First or LstNode_Next as the start node. */
|
||||
LstNode
|
||||
Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs)
|
||||
{
|
||||
LstNode tln;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(match != NULL);
|
||||
|
||||
for (tln = node; tln != NULL; tln = tln->next) {
|
||||
if (match(tln->datum, matchArgs))
|
||||
return tln;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the first node that contains the given datum, or NULL. */
|
||||
LstNode
|
||||
Lst_FindDatum(Lst list, const void *datum)
|
||||
ListNode *
|
||||
Lst_FindDatum(List *list, const void *datum)
|
||||
{
|
||||
LstNode node;
|
||||
ListNode *node;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(datum != NULL);
|
||||
|
||||
for (node = list->first; node != NULL; node = node->next) {
|
||||
if (node->datum == datum) {
|
||||
for (node = list->first; node != NULL; node = node->next)
|
||||
if (node->datum == datum)
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Apply the given function to each element of the given list. The function
|
||||
* should return 0 if traversal should continue and non-zero if it should
|
||||
* abort. */
|
||||
int
|
||||
Lst_ForEach(Lst list, LstActionProc proc, void *procData)
|
||||
Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
|
||||
{
|
||||
if (LstIsEmpty(list))
|
||||
return 0; /* XXX: Document what this value means. */
|
||||
return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
|
||||
}
|
||||
|
||||
/* Apply the given function to each element of the given list, starting from
|
||||
* the given node. The function should return 0 if traversal should continue,
|
||||
* and non-zero if it should abort. */
|
||||
int
|
||||
Lst_ForEachFrom(Lst list, LstNode node,
|
||||
LstActionProc proc, void *procData)
|
||||
{
|
||||
LstNode tln = node;
|
||||
LstNode next;
|
||||
Boolean done;
|
||||
int result;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(node != NULL);
|
||||
assert(proc != NULL);
|
||||
|
||||
do {
|
||||
/*
|
||||
* Take care of having the current element deleted out from under
|
||||
* us.
|
||||
*/
|
||||
|
||||
next = tln->next;
|
||||
|
||||
/*
|
||||
* We're done with the traversal if
|
||||
* - the next node to examine doesn't exist and
|
||||
* - nothing's been added after the current node (check this
|
||||
* after proc() has been called).
|
||||
*/
|
||||
done = next == NULL;
|
||||
|
||||
tln->useCount++;
|
||||
result = (*proc)(tln->datum, procData);
|
||||
tln->useCount--;
|
||||
|
||||
/*
|
||||
* Now check whether a node has been added.
|
||||
* Note: this doesn't work if this node was deleted before
|
||||
* the new node was added.
|
||||
*/
|
||||
if (next != tln->next) {
|
||||
next = tln->next;
|
||||
done = 0;
|
||||
}
|
||||
|
||||
if (tln->deleted) {
|
||||
free((char *)tln);
|
||||
}
|
||||
tln = next;
|
||||
} while (!result && !LstIsEmpty(list) && !done);
|
||||
ListNode *node;
|
||||
int result = 0;
|
||||
|
||||
for (node = list->first; node != NULL; node = node->next) {
|
||||
result = proc(node->datum, procData);
|
||||
if (result != 0)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Move all nodes from list2 to the end of list1.
|
||||
* List2 is destroyed and freed. */
|
||||
void
|
||||
Lst_MoveAll(Lst list1, Lst list2)
|
||||
Lst_MoveAll(List *list1, List *list2)
|
||||
{
|
||||
assert(list1 != NULL);
|
||||
assert(list2 != NULL);
|
||||
|
||||
if (list2->first != NULL) {
|
||||
list2->first->prev = list1->last;
|
||||
if (list1->last != NULL) {
|
||||
if (list1->last != NULL)
|
||||
list1->last->next = list2->first;
|
||||
} else {
|
||||
else
|
||||
list1->first = list2->first;
|
||||
}
|
||||
|
||||
list1->last = list2->last;
|
||||
}
|
||||
free(list2);
|
||||
@ -518,124 +243,77 @@ Lst_MoveAll(Lst list1, Lst list2)
|
||||
|
||||
/* Copy the element data from src to the start of dst. */
|
||||
void
|
||||
Lst_PrependAll(Lst dst, Lst src)
|
||||
Lst_PrependAll(List *dst, List *src)
|
||||
{
|
||||
LstNode node;
|
||||
ListNode *node;
|
||||
for (node = src->last; node != NULL; node = node->prev)
|
||||
Lst_Prepend(dst, node->datum);
|
||||
}
|
||||
|
||||
/* Copy the element data from src to the end of dst. */
|
||||
void
|
||||
Lst_AppendAll(Lst dst, Lst src)
|
||||
Lst_AppendAll(List *dst, List *src)
|
||||
{
|
||||
LstNode node;
|
||||
ListNode *node;
|
||||
for (node = src->first; node != NULL; node = node->next)
|
||||
Lst_Append(dst, node->datum);
|
||||
}
|
||||
|
||||
/*
|
||||
* these functions are for dealing with a list as a table, of sorts.
|
||||
* An idea of the "current element" is kept and used by all the functions
|
||||
* between Lst_Open() and Lst_Close().
|
||||
*
|
||||
* The sequential functions access the list in a slightly different way.
|
||||
* CurPtr points to their idea of the current node in the list and they
|
||||
* access the list based on it.
|
||||
*/
|
||||
|
||||
/* Open a list for sequential access. A list can still be searched, etc.,
|
||||
* without confusing these functions. */
|
||||
void
|
||||
Lst_Open(Lst list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(!list->isOpen);
|
||||
|
||||
list->isOpen = TRUE;
|
||||
list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
|
||||
list->curr = NULL;
|
||||
}
|
||||
|
||||
/* Return the next node for the given list, or NULL if the end has been
|
||||
* reached. */
|
||||
LstNode
|
||||
Lst_Next(Lst list)
|
||||
{
|
||||
LstNode node;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(list->isOpen);
|
||||
|
||||
list->prev = list->curr;
|
||||
|
||||
if (list->curr == NULL) {
|
||||
if (list->lastAccess == Unknown) {
|
||||
/*
|
||||
* If we're just starting out, lastAccess will be Unknown.
|
||||
* Then we want to start this thing off in the right
|
||||
* direction -- at the start with lastAccess being Middle.
|
||||
*/
|
||||
list->curr = node = list->first;
|
||||
list->lastAccess = Middle;
|
||||
} else {
|
||||
node = NULL;
|
||||
list->lastAccess = Tail;
|
||||
}
|
||||
} else {
|
||||
node = list->curr->next;
|
||||
list->curr = node;
|
||||
|
||||
if (node == list->first || node == NULL) {
|
||||
/*
|
||||
* If back at the front, then we've hit the end...
|
||||
*/
|
||||
list->lastAccess = Tail;
|
||||
} else {
|
||||
/*
|
||||
* Reset to Middle if gone past first.
|
||||
*/
|
||||
list->lastAccess = Middle;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Close a list which was opened for sequential access. */
|
||||
void
|
||||
Lst_Close(Lst list)
|
||||
{
|
||||
assert(list != NULL);
|
||||
assert(list->isOpen);
|
||||
|
||||
list->isOpen = FALSE;
|
||||
list->lastAccess = Unknown;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* for using the list as a queue
|
||||
*/
|
||||
|
||||
/* Add the datum to the tail of the given list. */
|
||||
void
|
||||
Lst_Enqueue(Lst list, void *datum)
|
||||
Lst_Enqueue(List *list, void *datum)
|
||||
{
|
||||
Lst_Append(list, datum);
|
||||
}
|
||||
|
||||
/* Remove and return the datum at the head of the given list. */
|
||||
void *
|
||||
Lst_Dequeue(Lst list)
|
||||
Lst_Dequeue(List *list)
|
||||
{
|
||||
void *datum;
|
||||
|
||||
assert(list != NULL);
|
||||
assert(!LstIsEmpty(list));
|
||||
|
||||
datum = list->first->datum;
|
||||
void *datum = list->first->datum;
|
||||
Lst_Remove(list, list->first);
|
||||
assert(datum != NULL);
|
||||
assert(datum != NULL); /* since NULL would mean end of the list */
|
||||
return datum;
|
||||
}
|
||||
|
||||
void
|
||||
Vector_Init(Vector *v, size_t itemSize)
|
||||
{
|
||||
v->len = 0;
|
||||
v->priv_cap = 10;
|
||||
v->itemSize = itemSize;
|
||||
v->items = bmake_malloc(v->priv_cap * v->itemSize);
|
||||
}
|
||||
|
||||
/* Add space for a new item to the vector and return a pointer to that space.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
void *
|
||||
Vector_Push(Vector *v)
|
||||
{
|
||||
if (v->len >= v->priv_cap) {
|
||||
v->priv_cap *= 2;
|
||||
v->items = bmake_realloc(v->items, v->priv_cap * v->itemSize);
|
||||
}
|
||||
v->len++;
|
||||
return Vector_Get(v, v->len - 1);
|
||||
}
|
||||
|
||||
/* Return the pointer to the last item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
void *
|
||||
Vector_Pop(Vector *v)
|
||||
{
|
||||
assert(v->len > 0);
|
||||
v->len--;
|
||||
return Vector_Get(v, v->len);
|
||||
}
|
||||
|
||||
void
|
||||
Vector_Done(Vector *v)
|
||||
{
|
||||
free(v->items);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.h,v 1.60 2020/09/02 23:33:13 rillig Exp $ */
|
||||
/* $NetBSD: lst.h,v 1.84 2020/10/28 02:43:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -79,101 +79,109 @@
|
||||
#define MAKE_LST_H
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* A doubly-linked list of pointers. */
|
||||
typedef struct List *Lst;
|
||||
typedef struct List List;
|
||||
/* A single node in the doubly-linked list. */
|
||||
typedef struct ListNode *LstNode;
|
||||
typedef struct ListNode ListNode;
|
||||
|
||||
struct ListNode {
|
||||
ListNode *prev; /* previous node in list, or NULL */
|
||||
ListNode *next; /* next node in list, or NULL */
|
||||
union {
|
||||
void *datum; /* datum associated with this element */
|
||||
const struct GNode *priv_gnode; /* alias, just for debugging */
|
||||
const char *priv_str; /* alias, just for debugging */
|
||||
};
|
||||
};
|
||||
|
||||
struct List {
|
||||
ListNode *first; /* first node in list */
|
||||
ListNode *last; /* last node in list */
|
||||
};
|
||||
|
||||
/* Copy a node, usually by allocating a copy of the given object.
|
||||
* For reference-counted objects, the original object may need to be
|
||||
* modified, therefore the parameter is not const. */
|
||||
typedef void *LstCopyProc(void *);
|
||||
/* Free the datum of a node, called before freeing the node itself. */
|
||||
typedef void LstFreeProc(void *);
|
||||
/* Return TRUE if the datum matches the args, for Lst_Find. */
|
||||
typedef Boolean LstFindProc(const void *datum, const void *args);
|
||||
/* An action for Lst_ForEach. */
|
||||
typedef int LstActionProc(void *datum, void *args);
|
||||
/* An action for Lst_ForEachUntil and Lst_ForEachUntilConcurrent. */
|
||||
typedef int LstActionUntilProc(void *datum, void *args);
|
||||
|
||||
/* Create or destroy a list */
|
||||
|
||||
/* Create a new list. */
|
||||
Lst Lst_Init(void);
|
||||
/* Duplicate an existing list. */
|
||||
Lst Lst_Copy(Lst, LstCopyProc);
|
||||
List *Lst_New(void);
|
||||
/* Free the list, leaving the node data unmodified. */
|
||||
void Lst_Free(Lst);
|
||||
void Lst_Free(List *);
|
||||
/* Free the list, freeing the node data using the given function. */
|
||||
void Lst_Destroy(Lst, LstFreeProc);
|
||||
void Lst_Destroy(List *, LstFreeProc);
|
||||
|
||||
/* Get information about a list */
|
||||
|
||||
Boolean Lst_IsEmpty(Lst);
|
||||
/* Return the first node of the list, or NULL. */
|
||||
LstNode Lst_First(Lst);
|
||||
/* Return the last node of the list, or NULL. */
|
||||
LstNode Lst_Last(Lst);
|
||||
/* Find the first node for which the function returns TRUE, or NULL. */
|
||||
LstNode Lst_Find(Lst, LstFindProc, const void *);
|
||||
/* Find the first node for which the function returns TRUE, or NULL.
|
||||
* The search starts at the given node, towards the end of the list. */
|
||||
LstNode Lst_FindFrom(Lst, LstNode, LstFindProc, const void *);
|
||||
static inline MAKE_ATTR_UNUSED Boolean
|
||||
Lst_IsEmpty(List *list) { return list->first == NULL; }
|
||||
|
||||
/* Find the first node that contains the given datum, or NULL. */
|
||||
LstNode Lst_FindDatum(Lst, const void *);
|
||||
ListNode *Lst_FindDatum(List *, const void *);
|
||||
|
||||
/* Modify a list */
|
||||
|
||||
/* Insert a datum before the given node. */
|
||||
void Lst_InsertBefore(Lst, LstNode, void *);
|
||||
void Lst_InsertBefore(List *, ListNode *, void *);
|
||||
/* Place a datum at the front of the list. */
|
||||
void Lst_Prepend(Lst, void *);
|
||||
void Lst_Prepend(List *, void *);
|
||||
/* Place a datum at the end of the list. */
|
||||
void Lst_Append(Lst, void *);
|
||||
void Lst_Append(List *, void *);
|
||||
/* Remove the node from the list. */
|
||||
void Lst_Remove(Lst, LstNode);
|
||||
void Lst_PrependAll(Lst, Lst);
|
||||
void Lst_AppendAll(Lst, Lst);
|
||||
void Lst_MoveAll(Lst, Lst);
|
||||
void Lst_Remove(List *, ListNode *);
|
||||
void Lst_PrependAll(List *, List *);
|
||||
void Lst_AppendAll(List *, List *);
|
||||
void Lst_MoveAll(List *, List *);
|
||||
|
||||
/* Node-specific functions */
|
||||
|
||||
/* Return the successor of the node, or NULL. */
|
||||
LstNode LstNode_Next(LstNode);
|
||||
/* Return the predecessor of the node, or NULL. */
|
||||
LstNode LstNode_Prev(LstNode);
|
||||
/* Return the datum of the node. Usually not NULL. */
|
||||
void *LstNode_Datum(LstNode);
|
||||
/* Replace the value of the node. */
|
||||
void LstNode_Set(LstNode, void *);
|
||||
void LstNode_Set(ListNode *, void *);
|
||||
/* Set the value of the node to NULL. Having NULL in a list is unusual. */
|
||||
void LstNode_SetNull(LstNode);
|
||||
void LstNode_SetNull(ListNode *);
|
||||
|
||||
/* Iterating over a list, using a callback function */
|
||||
|
||||
/* Apply a function to each datum of the list, until the callback function
|
||||
* returns non-zero. */
|
||||
int Lst_ForEach(Lst, LstActionProc, void *);
|
||||
/* Apply a function to each datum of the list, starting at the node,
|
||||
* until the callback function returns non-zero. */
|
||||
int Lst_ForEachFrom(Lst, LstNode, LstActionProc, void *);
|
||||
|
||||
/* Iterating over a list while keeping track of the current node and possible
|
||||
* concurrent modifications */
|
||||
|
||||
/* Start iterating the list. */
|
||||
void Lst_Open(Lst);
|
||||
/* Return the next node, or NULL. */
|
||||
LstNode Lst_Next(Lst);
|
||||
/* Finish iterating the list. */
|
||||
void Lst_Close(Lst);
|
||||
/* Run the action for each datum of the list, until the action returns
|
||||
* non-zero.
|
||||
*
|
||||
* During this iteration, the list must not be modified structurally. */
|
||||
int Lst_ForEachUntil(List *, LstActionUntilProc, void *);
|
||||
|
||||
/* Using the list as a queue */
|
||||
|
||||
/* Add a datum at the tail of the queue. */
|
||||
void Lst_Enqueue(Lst, void *);
|
||||
void Lst_Enqueue(List *, void *);
|
||||
/* Remove the head node of the queue and return its datum. */
|
||||
void *Lst_Dequeue(Lst);
|
||||
void *Lst_Dequeue(List *);
|
||||
|
||||
/* A vector is an ordered collection of items, allowing for fast indexed
|
||||
* access. */
|
||||
typedef struct Vector {
|
||||
void *items; /* memory holding the items */
|
||||
size_t itemSize; /* size of a single item in bytes */
|
||||
size_t len; /* number of actually usable elements */
|
||||
size_t priv_cap; /* capacity */
|
||||
} Vector;
|
||||
|
||||
void Vector_Init(Vector *, size_t);
|
||||
|
||||
/* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
static inline MAKE_ATTR_UNUSED void *
|
||||
Vector_Get(Vector *v, size_t i)
|
||||
{
|
||||
unsigned char *items = v->items;
|
||||
return items + i * v->itemSize;
|
||||
}
|
||||
|
||||
void *Vector_Push(Vector *);
|
||||
void *Vector_Pop(Vector *);
|
||||
void Vector_Done(Vector *);
|
||||
|
||||
#endif /* MAKE_LST_H */
|
||||
|
1855
contrib/bmake/main.c
1855
contrib/bmake/main.c
File diff suppressed because it is too large
Load Diff
@ -59,14 +59,14 @@ do_link() {
|
||||
}
|
||||
|
||||
BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \
|
||||
lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o strlist.o \
|
||||
lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o \
|
||||
suff.o targ.o trace.o var.o util.o"
|
||||
|
||||
LIB_OBJECTS="@LIBOBJS@"
|
||||
|
||||
do_compile main.o ${MDEFS}
|
||||
|
||||
for o in ${BASE_OBJECTS} ${LST_OBJECTS} ${LIB_OBJECTS}
|
||||
for o in ${BASE_OBJECTS} ${LIB_OBJECTS}
|
||||
do
|
||||
do_compile "$o"
|
||||
done
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: config.h,v 1.22 2020/09/01 17:40:34 rillig Exp $ */
|
||||
/* $NetBSD: config.h,v 1.25 2020/10/19 23:43:55 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -76,10 +76,11 @@
|
||||
* DEFMAXJOBS
|
||||
* DEFMAXLOCAL
|
||||
* These control the default concurrency. On no occasion will more
|
||||
* than DEFMAXJOBS targets be created at once (locally or remotely)
|
||||
* than DEFMAXJOBS targets be created at once (locally or remotely).
|
||||
*
|
||||
* DEFMAXLOCAL is the highest number of targets which will be
|
||||
* created on the local machine at once. Note that if you set this
|
||||
* to 0, nothing will ever happen...
|
||||
* to 0, nothing will ever happen.
|
||||
*/
|
||||
#define DEFMAXJOBS 4
|
||||
#define DEFMAXLOCAL 1
|
||||
@ -88,10 +89,12 @@
|
||||
* INCLUDES
|
||||
* LIBRARIES
|
||||
* These control the handling of the .INCLUDES and .LIBS variables.
|
||||
*
|
||||
* If INCLUDES is defined, the .INCLUDES variable will be filled
|
||||
* from the search paths of those suffixes which are marked by
|
||||
* .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS
|
||||
* See suff.c for more details.
|
||||
* .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS.
|
||||
*
|
||||
* See varname-dot-include.mk and varname-dot-libs.mk for more details.
|
||||
*/
|
||||
#define INCLUDES
|
||||
#define LIBRARIES
|
||||
@ -108,11 +111,13 @@
|
||||
* If defined, Make_Update will check a target for its current
|
||||
* modification time after it has been re-made, setting it to the
|
||||
* starting time of the make only if the target still doesn't exist.
|
||||
*
|
||||
* Unfortunately, under NFS the modification time often doesn't
|
||||
* get updated in time, so a target will appear to not have been
|
||||
* re-made, causing later targets to appear up-to-date. On systems
|
||||
* that don't have this problem, you should define this. Under
|
||||
* NFS you probably should not, unless you aren't exporting jobs.
|
||||
* re-made, causing later targets to appear up-to-date.
|
||||
*
|
||||
* On systems that don't have this problem, you should define this.
|
||||
* Under NFS you probably should not, unless you aren't exporting jobs.
|
||||
*/
|
||||
#define RECHECK
|
||||
|
||||
@ -128,8 +133,10 @@
|
||||
/*
|
||||
* SYSVINCLUDE
|
||||
* Recognize system V like include directives [include "filename"]
|
||||
* (required by POSIX 2018)
|
||||
* SYSVVARSUB
|
||||
* Recognize system V like ${VAR:x=y} variable substitutions
|
||||
* (required by POSIX 2018)
|
||||
*/
|
||||
#define SYSVINCLUDE
|
||||
#define SYSVVARSUB
|
||||
@ -149,14 +156,6 @@
|
||||
*/
|
||||
#define SUNSHCMD
|
||||
|
||||
/*
|
||||
* USE_IOVEC
|
||||
* We have writev(2)
|
||||
*/
|
||||
#ifdef HAVE_SYS_UIO_H
|
||||
# define USE_IOVEC
|
||||
#endif
|
||||
|
||||
#if defined(MAKE_NATIVE) && !defined(__ELF__)
|
||||
# ifndef RANLIBMAG
|
||||
# define RANLIBMAG "__.SYMDEF"
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.289 2020/08/28 17:15:04 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.290 2020/11/01 20:24:45 rillig Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd August 28, 2020
|
||||
.Dd November 1, 2020
|
||||
.Dt MAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1883,7 +1883,7 @@ has been defined and has commands associated with it.
|
||||
.Ar Expression
|
||||
may also be an arithmetic or string comparison.
|
||||
Variable expansion is
|
||||
performed on both sides of the comparison, after which the integral
|
||||
performed on both sides of the comparison, after which the numerical
|
||||
values are compared.
|
||||
A value is interpreted as hexadecimal if it is
|
||||
preceded by 0x, otherwise it is decimal; octal numbers are not supported.
|
||||
@ -1893,7 +1893,7 @@ variable expansion, either the left or right hand side of a
|
||||
.Ql Ic ==
|
||||
or
|
||||
.Ql Ic "!="
|
||||
operator is not an integral value, then
|
||||
operator is not a numerical value, then
|
||||
string comparison is performed between the expanded
|
||||
variables.
|
||||
If no relational operator is given, it is assumed that the expanded
|
||||
|
1000
contrib/bmake/make.c
1000
contrib/bmake/make.c
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.h,v 1.137 2020/09/02 23:42:58 rillig Exp $ */
|
||||
/* $NetBSD: make.h,v 1.179 2020/11/01 17:47:26 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -91,6 +91,7 @@
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
@ -142,23 +143,31 @@
|
||||
#ifdef USE_DOUBLE_BOOLEAN
|
||||
/* During development, to find type mismatches in function declarations. */
|
||||
typedef double Boolean;
|
||||
#define TRUE 1.0
|
||||
#define FALSE 0.0
|
||||
#elif defined(USE_UCHAR_BOOLEAN)
|
||||
/* During development, to find code that depends on the exact value of TRUE or
|
||||
* that stores other values in Boolean variables. */
|
||||
typedef unsigned char Boolean;
|
||||
#define TRUE ((unsigned char)0xFF)
|
||||
#define FALSE ((unsigned char)0x00)
|
||||
#elif defined(USE_CHAR_BOOLEAN)
|
||||
/* During development, to find code that uses a boolean as array index, via
|
||||
* -Wchar-subscripts. */
|
||||
typedef char Boolean;
|
||||
#define TRUE ((char)-1)
|
||||
#define FALSE ((char)0x00)
|
||||
#elif defined(USE_ENUM_BOOLEAN)
|
||||
typedef enum { FALSE, TRUE} Boolean;
|
||||
typedef enum Boolean { FALSE, TRUE } Boolean;
|
||||
#else
|
||||
typedef int Boolean;
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif /* TRUE */
|
||||
#endif
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif /* FALSE */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "lst.h"
|
||||
#include "enum.h"
|
||||
@ -196,18 +205,20 @@ typedef enum {
|
||||
* communicating to other parts of the program the way in which a target
|
||||
* should be made.
|
||||
*
|
||||
* These constants are bitwise-OR'ed together and placed in the 'type' field
|
||||
* of each node. Any node that has a 'type' field which satisfies the OP_NOP
|
||||
* function was never never on the left-hand side of an operator, though it
|
||||
* may have been on the right-hand side... */
|
||||
typedef enum {
|
||||
/* Execution of commands depends on children (:) */
|
||||
* Some of the OP_ constants can be combined, others cannot. */
|
||||
typedef enum GNodeType {
|
||||
/* The dependency operator ':' is the most common one. The commands of
|
||||
* this node are executed if any child is out-of-date. */
|
||||
OP_DEPENDS = 1 << 0,
|
||||
/* Always execute commands (!) */
|
||||
/* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date. */
|
||||
OP_FORCE = 1 << 1,
|
||||
/* Execution of commands depends on children per line (::) */
|
||||
/* The dependency operator '::' behaves like ':', except that it allows
|
||||
* multiple dependency groups to be defined. Each of these groups is
|
||||
* executed on its own, independently from the others. */
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
OP_OPMASK = OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP,
|
||||
|
||||
/* Don't care if the target doesn't exist and can't be created */
|
||||
@ -216,8 +227,8 @@ typedef enum {
|
||||
OP_USE = 1 << 4,
|
||||
/* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of */
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore errors when creating the node */
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore non-zero exit status from shell commands when creating the node */
|
||||
OP_IGNORE = 1 << 6,
|
||||
/* Don't remove the target when interrupted */
|
||||
OP_PRECIOUS = 1 << 7,
|
||||
@ -259,23 +270,32 @@ typedef enum {
|
||||
/* The node is a transformation rule */
|
||||
OP_TRANSFORM = 1 << 31,
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
OP_MEMBER = 1 << 30,
|
||||
/* Target is a library */
|
||||
/* The node is a library,
|
||||
* its name has the form "-l<libname>" */
|
||||
OP_LIB = 1 << 29,
|
||||
/* Target is an archive construct */
|
||||
/* The node is an archive member,
|
||||
* its name has the form "archive(member)" */
|
||||
/* XXX: How does this differ from OP_MEMBER? */
|
||||
OP_ARCHV = 1 << 28,
|
||||
/* Target has all the commands it should. Used when parsing to catch
|
||||
* multiple commands for a target. */
|
||||
* multiple command groups for a target. Only applies to the dependency
|
||||
* operators ':' and '!', but not to '::'. */
|
||||
OP_HAS_COMMANDS = 1 << 27,
|
||||
/* Saving commands on .END (Compat) */
|
||||
/* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed at
|
||||
* the very end. */
|
||||
OP_SAVE_CMDS = 1 << 26,
|
||||
/* Already processed by Suff_FindDeps */
|
||||
OP_DEPS_FOUND = 1 << 25,
|
||||
/* Node found while expanding .ALLSRC */
|
||||
OP_MARK = 1 << 24
|
||||
OP_MARK = 1 << 24,
|
||||
|
||||
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
|
||||
} GNodeType;
|
||||
|
||||
typedef enum {
|
||||
typedef enum GNodeFlags {
|
||||
REMAKE = 0x0001, /* this target needs to be (re)made */
|
||||
CHILDMADE = 0x0002, /* children of this target were made */
|
||||
FORCE = 0x0004, /* children don't exist, and we pretend made */
|
||||
@ -288,6 +308,14 @@ typedef enum {
|
||||
INTERNAL = 0x4000 /* Internal use only */
|
||||
} GNodeFlags;
|
||||
|
||||
typedef struct List StringList;
|
||||
typedef struct ListNode StringListNode;
|
||||
|
||||
typedef struct List GNodeList;
|
||||
typedef struct ListNode GNodeListNode;
|
||||
|
||||
typedef struct List /* of CachedDir */ SearchPath;
|
||||
|
||||
/* A graph node represents a target that can possibly be made, including its
|
||||
* relation to other targets and a lot of other details. */
|
||||
typedef struct GNode {
|
||||
@ -302,40 +330,41 @@ typedef struct GNode {
|
||||
/* The type of operator used to define the sources (see the OP flags below).
|
||||
* XXX: This looks like a wild mixture of type and flags. */
|
||||
GNodeType type;
|
||||
/* whether it is involved in this invocation of make */
|
||||
GNodeFlags flags;
|
||||
|
||||
/* The state of processing on this node */
|
||||
GNodeMade made;
|
||||
int unmade; /* The number of unmade children */
|
||||
|
||||
time_t mtime; /* Its modification time */
|
||||
struct GNode *cmgn; /* The youngest child */
|
||||
/* The modification time; 0 means the node does not have a corresponding
|
||||
* file; see Make_OODate. */
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
|
||||
/* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node for
|
||||
* file.c has the node for file.o in this list. */
|
||||
Lst implicitParents;
|
||||
GNodeList *implicitParents;
|
||||
|
||||
/* Other nodes of the same name for the :: operator. */
|
||||
Lst cohorts;
|
||||
/* Other nodes of the same name, for the '::' operator. */
|
||||
GNodeList *cohorts;
|
||||
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
Lst parents;
|
||||
GNodeList *parents;
|
||||
/* The nodes on which this one depends. */
|
||||
Lst children;
|
||||
GNodeList *children;
|
||||
|
||||
/* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
* made) before this node can be made, but that do not enter into the
|
||||
* datedness of this node. */
|
||||
Lst order_pred;
|
||||
GNodeList *order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're made
|
||||
* at all) after this node is made, but that do not depend on this node,
|
||||
* in the normal sense. */
|
||||
Lst order_succ;
|
||||
GNodeList *order_succ;
|
||||
|
||||
/* #n for this cohort */
|
||||
/* The "#n" suffix for this cohort, or "" for other nodes */
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
int unmade_cohorts;
|
||||
@ -344,14 +373,17 @@ typedef struct GNode {
|
||||
struct GNode *centurion;
|
||||
|
||||
/* Last time (sequence number) we tried to make this node */
|
||||
unsigned int checked;
|
||||
unsigned int checked_seqno;
|
||||
|
||||
/* The "local" variables that are specific to this target and this target
|
||||
* only, such as $@, $<, $?. */
|
||||
Hash_Table context;
|
||||
* only, such as $@, $<, $?.
|
||||
*
|
||||
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
|
||||
* VAR_INTERNAL, which contain variables with arbitrary names. */
|
||||
HashTable /* of Var pointer */ context;
|
||||
|
||||
/* The commands to be given to a shell to create this target. */
|
||||
Lst commands;
|
||||
StringList *commands;
|
||||
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
|
||||
* but the Suff module) */
|
||||
@ -363,40 +395,21 @@ typedef struct GNode {
|
||||
int lineno;
|
||||
} GNode;
|
||||
|
||||
#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute)
|
||||
/*
|
||||
* OP_NOP will return TRUE if the node with the given type was not the
|
||||
* object of a dependency operator
|
||||
*/
|
||||
#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000)
|
||||
|
||||
#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)
|
||||
|
||||
/*
|
||||
* The TARG_ constants are used when calling the Targ_FindNode and
|
||||
* Targ_FindList functions in targ.c. They simply tell the functions what to
|
||||
* do if the desired node(s) is (are) not found. If the TARG_CREATE constant
|
||||
* is given, a new, empty node will be created for the target, placed in the
|
||||
* table of all targets and its address returned. If TARG_NOCREATE is given,
|
||||
* a NULL pointer will be returned.
|
||||
*/
|
||||
#define TARG_NOCREATE 0x00 /* don't create it */
|
||||
#define TARG_CREATE 0x01 /* create node if not found */
|
||||
#define TARG_NOHASH 0x02 /* don't look in/add to hash table */
|
||||
|
||||
/*
|
||||
* Error levels for parsing. PARSE_FATAL means the process cannot continue
|
||||
* once the makefile has been parsed. PARSE_WARNING means it can. Passed
|
||||
* as the first argument to Parse_Error.
|
||||
* once the top-level makefile has been parsed. PARSE_WARNING and PARSE_INFO
|
||||
* mean it can.
|
||||
*/
|
||||
#define PARSE_INFO 3
|
||||
#define PARSE_WARNING 2
|
||||
#define PARSE_FATAL 1
|
||||
typedef enum ParseErrorLevel {
|
||||
PARSE_FATAL = 1,
|
||||
PARSE_WARNING,
|
||||
PARSE_INFO
|
||||
} ParseErrorLevel;
|
||||
|
||||
/*
|
||||
* Values returned by Cond_Eval.
|
||||
* Values returned by Cond_EvalLine and Cond_EvalCondition.
|
||||
*/
|
||||
typedef enum {
|
||||
typedef enum CondEvalResult {
|
||||
COND_PARSE, /* Parse the next lines */
|
||||
COND_SKIP, /* Skip the next lines */
|
||||
COND_INVALID /* Not a conditional statement */
|
||||
@ -405,77 +418,52 @@ typedef enum {
|
||||
/*
|
||||
* Definitions for the "local" variables. Used only for clarity.
|
||||
*/
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
#define TARGET "@" /* Target of dependency */
|
||||
#define OODATE "?" /* All out-of-date sources */
|
||||
#define ALLSRC ">" /* All sources */
|
||||
#define IMPSRC "<" /* Source implied by transformation */
|
||||
#define PREFIX "*" /* Common prefix */
|
||||
#define ARCHIVE "!" /* Archive in "archive(member)" syntax */
|
||||
#define MEMBER "%" /* Member in "archive(member)" syntax */
|
||||
|
||||
#define FTARGET "@F" /* file part of TARGET */
|
||||
#define DTARGET "@D" /* directory part of TARGET */
|
||||
#define FIMPSRC "<F" /* file part of IMPSRC */
|
||||
#define DIMPSRC "<D" /* directory part of IMPSRC */
|
||||
#define FPREFIX "*F" /* file part of PREFIX */
|
||||
#define DPREFIX "*D" /* directory part of PREFIX */
|
||||
#define FTARGET "@F" /* file part of TARGET */
|
||||
#define DTARGET "@D" /* directory part of TARGET */
|
||||
#define FIMPSRC "<F" /* file part of IMPSRC */
|
||||
#define DIMPSRC "<D" /* directory part of IMPSRC */
|
||||
#define FPREFIX "*F" /* file part of PREFIX */
|
||||
#define DPREFIX "*D" /* directory part of PREFIX */
|
||||
|
||||
/*
|
||||
* Global Variables
|
||||
*/
|
||||
extern Lst create; /* The list of target names specified on the
|
||||
* command line. used to resolve #if
|
||||
* make(...) statements */
|
||||
extern Lst dirSearchPath; /* The list of directories to search when
|
||||
extern SearchPath *dirSearchPath;
|
||||
/* The list of directories to search when
|
||||
* looking for targets */
|
||||
|
||||
extern Boolean compatMake; /* True if we are make compatible */
|
||||
extern Boolean ignoreErrors; /* True if should ignore all errors */
|
||||
extern Boolean beSilent; /* True if should print no commands */
|
||||
extern Boolean noExecute; /* True if should execute nothing */
|
||||
extern Boolean noRecursiveExecute; /* True if should execute nothing */
|
||||
extern Boolean allPrecious; /* True if every target is precious */
|
||||
extern Boolean allPrecious; /* True if every target is precious */
|
||||
extern Boolean deleteOnError; /* True if failed targets should be deleted */
|
||||
extern Boolean keepgoing; /* True if should continue on unaffected
|
||||
* portions of the graph when have an error
|
||||
* in one portion */
|
||||
extern Boolean touchFlag; /* TRUE if targets should just be 'touched'
|
||||
* if out of date. Set by the -t flag */
|
||||
extern Boolean queryFlag; /* TRUE if we aren't supposed to really make
|
||||
* anything, just see if the targets are out-
|
||||
* of-date */
|
||||
extern Boolean doing_depend; /* TRUE if processing .depend */
|
||||
|
||||
extern Boolean checkEnvFirst; /* TRUE if environment should be searched for
|
||||
* variables before the global context */
|
||||
|
||||
extern Boolean parseWarnFatal; /* TRUE if makefile parsing warnings are
|
||||
* treated as errors */
|
||||
|
||||
extern Boolean varNoExportEnv; /* TRUE if we should not export variables
|
||||
* set on the command line to the env. */
|
||||
|
||||
extern GNode *DEFAULT; /* .DEFAULT rule */
|
||||
extern GNode *DEFAULT; /* .DEFAULT rule */
|
||||
|
||||
extern GNode *VAR_INTERNAL; /* Variables defined internally by make
|
||||
* which should not override those set by
|
||||
* makefiles.
|
||||
*/
|
||||
extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
|
||||
extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g
|
||||
* in the Makefile itself */
|
||||
extern GNode *VAR_CMD; /* Variables defined on the command line */
|
||||
extern char var_Error[]; /* Value returned by Var_Parse when an error
|
||||
extern GNode *VAR_CMDLINE; /* Variables defined on the command line */
|
||||
extern char var_Error[]; /* Value returned by Var_Parse when an error
|
||||
* is encountered. It actually points to
|
||||
* an empty string, so naive callers needn't
|
||||
* worry about it. */
|
||||
|
||||
extern time_t now; /* The time at the start of this whole
|
||||
extern time_t now; /* The time at the start of this whole
|
||||
* process */
|
||||
|
||||
extern Boolean oldVars; /* Do old-style variable substitution */
|
||||
extern Boolean oldVars; /* Do old-style variable substitution */
|
||||
|
||||
extern Lst sysIncPath; /* The system include path. */
|
||||
extern Lst defIncPath; /* The default include path. */
|
||||
extern SearchPath *sysIncPath; /* The system include path. */
|
||||
extern SearchPath *defSysIncPath; /* The default system include path. */
|
||||
|
||||
extern char curdir[]; /* Startup directory */
|
||||
extern char *progname; /* The program name */
|
||||
@ -495,7 +483,7 @@ extern pid_t myPid;
|
||||
#define MAKEOVERRIDES ".MAKEOVERRIDES"
|
||||
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all the makefiles we read */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */
|
||||
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
|
||||
#define MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
|
||||
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
|
||||
@ -504,61 +492,191 @@ extern pid_t myPid;
|
||||
# define MAKE_LEVEL_ENV "MAKELEVEL"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* debug control:
|
||||
* There is one bit per module. It is up to the module what debug
|
||||
* information to print.
|
||||
*/
|
||||
extern FILE *debug_file; /* Output is written here - default stderr */
|
||||
extern int debug;
|
||||
#define DEBUG_ARCH 0x00001
|
||||
#define DEBUG_COND 0x00002
|
||||
#define DEBUG_DIR 0x00004
|
||||
#define DEBUG_GRAPH1 0x00008
|
||||
#define DEBUG_GRAPH2 0x00010
|
||||
#define DEBUG_JOB 0x00020
|
||||
#define DEBUG_MAKE 0x00040
|
||||
#define DEBUG_SUFF 0x00080
|
||||
#define DEBUG_TARG 0x00100
|
||||
#define DEBUG_VAR 0x00200
|
||||
#define DEBUG_FOR 0x00400
|
||||
#define DEBUG_SHELL 0x00800
|
||||
#define DEBUG_ERROR 0x01000
|
||||
#define DEBUG_LOUD 0x02000
|
||||
#define DEBUG_META 0x04000
|
||||
#define DEBUG_HASH 0x08000
|
||||
typedef enum DebugFlags {
|
||||
DEBUG_ARCH = 1 << 0,
|
||||
DEBUG_COND = 1 << 1,
|
||||
DEBUG_DIR = 1 << 2,
|
||||
DEBUG_GRAPH1 = 1 << 3,
|
||||
DEBUG_GRAPH2 = 1 << 4,
|
||||
DEBUG_JOB = 1 << 5,
|
||||
DEBUG_MAKE = 1 << 6,
|
||||
DEBUG_SUFF = 1 << 7,
|
||||
DEBUG_TARG = 1 << 8,
|
||||
DEBUG_VAR = 1 << 9,
|
||||
DEBUG_FOR = 1 << 10,
|
||||
DEBUG_SHELL = 1 << 11,
|
||||
DEBUG_ERROR = 1 << 12,
|
||||
DEBUG_LOUD = 1 << 13,
|
||||
DEBUG_META = 1 << 14,
|
||||
DEBUG_HASH = 1 << 15,
|
||||
|
||||
#define DEBUG_GRAPH3 0x10000
|
||||
#define DEBUG_SCRIPT 0x20000
|
||||
#define DEBUG_PARSE 0x40000
|
||||
#define DEBUG_CWD 0x80000
|
||||
DEBUG_GRAPH3 = 1 << 16,
|
||||
DEBUG_SCRIPT = 1 << 17,
|
||||
DEBUG_PARSE = 1 << 18,
|
||||
DEBUG_CWD = 1 << 19,
|
||||
|
||||
#define DEBUG_LINT 0x100000
|
||||
DEBUG_LINT = 1 << 20
|
||||
} DebugFlags;
|
||||
|
||||
#define CONCAT(a,b) a##b
|
||||
|
||||
#define DEBUG(module) (debug & CONCAT(DEBUG_,module))
|
||||
#define DEBUG(module) (opts.debug & CONCAT(DEBUG_,module))
|
||||
|
||||
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
|
||||
#define DEBUG0(module, text) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf("%s", text)
|
||||
|
||||
#define DEBUG1(module, fmt, arg1) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1)
|
||||
|
||||
#define DEBUG2(module, fmt, arg1, arg2) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2)
|
||||
|
||||
#define DEBUG3(module, fmt, arg1, arg2, arg3) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3)
|
||||
|
||||
#define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4)
|
||||
|
||||
#define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
|
||||
|
||||
typedef enum PrintVarsMode {
|
||||
COMPAT_VARS = 1,
|
||||
EXPAND_VARS
|
||||
} PrintVarsMode;
|
||||
|
||||
/* Command line options */
|
||||
typedef struct CmdOpts {
|
||||
/* -B: whether we are make compatible */
|
||||
Boolean compatMake;
|
||||
|
||||
/* -d: debug control: There is one bit per module. It is up to the
|
||||
* module what debug information to print. */
|
||||
DebugFlags debug;
|
||||
|
||||
/* -df: debug output is written here - default stderr */
|
||||
FILE *debug_file;
|
||||
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
Boolean debugVflag;
|
||||
|
||||
/* -e: check environment variables before global variables */
|
||||
Boolean checkEnvFirst;
|
||||
|
||||
/* -f: the makefiles to read */
|
||||
StringList *makefiles;
|
||||
|
||||
/* -i: if true, ignore all errors from shell commands */
|
||||
Boolean ignoreErrors;
|
||||
|
||||
/* -j: the maximum number of jobs that can run in parallel;
|
||||
* this is coordinated with the submakes */
|
||||
int maxJobs;
|
||||
|
||||
/* -k: if true, continue on unaffected portions of the graph when an
|
||||
* error occurs in one portion */
|
||||
Boolean keepgoing;
|
||||
|
||||
/* -N: execute no commands from the targets */
|
||||
Boolean noRecursiveExecute;
|
||||
|
||||
/* -n: execute almost no commands from the targets */
|
||||
Boolean noExecute;
|
||||
|
||||
/* -q: if true, we aren't supposed to really make anything, just see if
|
||||
* the targets are out-of-date */
|
||||
Boolean queryFlag;
|
||||
|
||||
/* -r: raw mode, without loading the builtin rules. */
|
||||
Boolean noBuiltins;
|
||||
|
||||
/* -s: don't echo the shell commands before executing them */
|
||||
Boolean beSilent;
|
||||
|
||||
/* -t: touch the targets if they are out-of-date, but don't actually
|
||||
* make them */
|
||||
Boolean touchFlag;
|
||||
|
||||
/* -[Vv]: print expanded or unexpanded selected variables */
|
||||
PrintVarsMode printVars;
|
||||
/* -[Vv]: the variables to print */
|
||||
StringList *variables;
|
||||
|
||||
/* -W: if true, makefile parsing warnings are treated as errors */
|
||||
Boolean parseWarnFatal;
|
||||
|
||||
/* -w: print Entering and Leaving for submakes */
|
||||
Boolean enterFlag;
|
||||
|
||||
/* -X: if true, do not export variables set on the command line to the
|
||||
* environment. */
|
||||
Boolean varNoExportEnv;
|
||||
|
||||
/* The target names specified on the command line.
|
||||
* Used to resolve .if make(...) statements. */
|
||||
StringList *create;
|
||||
|
||||
} CmdOpts;
|
||||
|
||||
extern CmdOpts opts;
|
||||
|
||||
#include "nonints.h"
|
||||
|
||||
int Make_TimeStamp(GNode *, GNode *);
|
||||
void Make_TimeStamp(GNode *, GNode *);
|
||||
Boolean Make_OODate(GNode *);
|
||||
void Make_ExpandUse(Lst);
|
||||
void Make_ExpandUse(GNodeList *);
|
||||
time_t Make_Recheck(GNode *);
|
||||
void Make_HandleUse(GNode *, GNode *);
|
||||
void Make_Update(GNode *);
|
||||
void Make_DoAllVar(GNode *);
|
||||
Boolean Make_Run(Lst);
|
||||
Boolean Make_Run(GNodeList *);
|
||||
int dieQuietly(GNode *, int);
|
||||
void PrintOnError(GNode *, const char *);
|
||||
void Main_ExportMAKEFLAGS(Boolean);
|
||||
Boolean Main_SetObjdir(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
int mkTempFile(const char *, char **);
|
||||
int str2Lst_Append(Lst, char *, const char *);
|
||||
int str2Lst_Append(StringList *, char *, const char *);
|
||||
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
|
||||
Boolean GNode_ShouldExecute(GNode *gn);
|
||||
|
||||
/* See if the node was seen on the left-hand side of a dependency operator. */
|
||||
static MAKE_ATTR_UNUSED Boolean
|
||||
GNode_IsTarget(const GNode *gn)
|
||||
{
|
||||
return (gn->type & OP_OPMASK) != 0;
|
||||
}
|
||||
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_Path(const GNode *gn)
|
||||
{
|
||||
return gn->path != NULL ? gn->path : gn->name;
|
||||
}
|
||||
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarAllsrc(GNode *gn) { return Var_ValueDirect(ALLSRC, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarPrefix(GNode *gn) { return Var_ValueDirect(PREFIX, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); }
|
||||
static MAKE_ATTR_UNUSED const char *
|
||||
GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); }
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define UNCONST(ptr) ({ \
|
||||
#define UNCONST(ptr) ({ \
|
||||
union __unconst { \
|
||||
const void *__cp; \
|
||||
void *__p; \
|
||||
@ -568,13 +686,6 @@ void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
|
||||
#define UNCONST(ptr) (void *)(ptr)
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
@ -592,4 +703,42 @@ void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
|
||||
#define KILLPG(pid, sig) killpg((pid), (sig))
|
||||
#endif
|
||||
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isalnum(char ch)
|
||||
{ return isalnum((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isalpha(char ch)
|
||||
{ return isalpha((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isdigit(char ch)
|
||||
{ return isdigit((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isspace(char ch)
|
||||
{ return isspace((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED Boolean ch_isupper(char ch)
|
||||
{ return isupper((unsigned char)ch) != 0; }
|
||||
static inline MAKE_ATTR_UNUSED char ch_tolower(char ch)
|
||||
{ return (char)tolower((unsigned char)ch); }
|
||||
static inline MAKE_ATTR_UNUSED char ch_toupper(char ch)
|
||||
{ return (char)toupper((unsigned char)ch); }
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
cpp_skip_whitespace(const char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
pp_skip_whitespace(char **pp)
|
||||
{
|
||||
while (ch_isspace(**pp))
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
#ifdef MAKE_NATIVE
|
||||
# include <sys/cdefs.h>
|
||||
# ifndef lint
|
||||
# define MAKE_RCSID(id) __RCSID(id)
|
||||
# endif
|
||||
#else
|
||||
# define MAKE_RCSID(id) static volatile char rcsid[] = id
|
||||
#endif
|
||||
|
||||
#endif /* MAKE_MAKE_H */
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -26,20 +26,13 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef MAKE_NATIVE
|
||||
#include <sys/cdefs.h>
|
||||
__RCSID("$NetBSD: make_malloc.c,v 1.18 2020/09/02 06:10:44 rillig Exp $");
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $");
|
||||
|
||||
#ifndef USE_EMALLOC
|
||||
static MAKE_ATTR_DEAD void enomem(void);
|
||||
|
||||
/* die when out of memory. */
|
||||
static MAKE_ATTR_DEAD void
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.h,v 1.10 2020/08/29 16:47:45 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.h,v 1.12 2020/10/19 23:43:55 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -33,20 +33,20 @@ char *bmake_strdup(const char *);
|
||||
char *bmake_strldup(const char *, size_t);
|
||||
#else
|
||||
#include <util.h>
|
||||
#define bmake_malloc(x) emalloc(x)
|
||||
#define bmake_realloc(x,y) erealloc(x,y)
|
||||
#define bmake_strdup(x) estrdup(x)
|
||||
#define bmake_strldup(x,y) estrndup(x,y)
|
||||
#define bmake_malloc(n) emalloc(n)
|
||||
#define bmake_realloc(p, n) erealloc(p, n)
|
||||
#define bmake_strdup(s) estrdup(s)
|
||||
#define bmake_strldup(s, n) estrndup(s, n)
|
||||
#endif
|
||||
|
||||
char *bmake_strsedup(const char *, const char *);
|
||||
|
||||
/* Thin wrapper around free(3) to avoid the extra function call in case
|
||||
* p is NULL, which on x86_64 costs about 12 machine instructions.
|
||||
* Other platforms are similarly affected.
|
||||
* p is NULL, to save a few machine instructions.
|
||||
*
|
||||
* The case of a NULL pointer happens especially often after Var_Value,
|
||||
* since only environment variables need to be freed, but not others. */
|
||||
static inline void MAKE_ATTR_UNUSED
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
bmake_free(void *p)
|
||||
{
|
||||
if (p != NULL)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.c,v 1.113 2020/09/02 04:08:54 rillig Exp $ */
|
||||
/* $NetBSD: meta.c,v 1.136 2020/10/31 12:04:24 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Implement 'meta' mode.
|
||||
@ -55,9 +55,9 @@ char * dirname(char *);
|
||||
#endif
|
||||
|
||||
static BuildMon Mybm; /* for compat */
|
||||
static Lst metaBailiwick; /* our scope of control */
|
||||
static StringList *metaBailiwick; /* our scope of control */
|
||||
static char *metaBailiwickStr; /* string storage for the list */
|
||||
static Lst metaIgnorePaths; /* paths we deliberately ignore */
|
||||
static StringList *metaIgnorePaths; /* paths we deliberately ignore */
|
||||
static char *metaIgnorePathsStr; /* string storage for the list */
|
||||
|
||||
#ifndef MAKE_META_IGNORE_PATHS
|
||||
@ -165,7 +165,6 @@ static int
|
||||
filemon_read(FILE *mfp, int fd)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
int n;
|
||||
int error;
|
||||
|
||||
/* Check if we're not writing to a meta data file.*/
|
||||
@ -180,11 +179,13 @@ filemon_read(FILE *mfp, int fd)
|
||||
warn("Could not rewind filemon");
|
||||
fprintf(mfp, "\n");
|
||||
} else {
|
||||
ssize_t n;
|
||||
|
||||
error = 0;
|
||||
fprintf(mfp, "\n-- filemon acquired metadata --\n");
|
||||
|
||||
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||
if ((int)fwrite(buf, 1, n, mfp) < n)
|
||||
if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n)
|
||||
error = EIO;
|
||||
}
|
||||
}
|
||||
@ -230,7 +231,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
|
||||
} while (cp > buf && *cp != '/');
|
||||
}
|
||||
if (*cp == '/') {
|
||||
strlcpy(cp, cp2, bufsz - (cp - buf));
|
||||
strlcpy(cp, cp2, bufsz - (size_t)(cp - buf));
|
||||
} else {
|
||||
return; /* can't happen? */
|
||||
}
|
||||
@ -264,7 +265,7 @@ meta_name(char *mname, size_t mnamelen,
|
||||
rp++;
|
||||
cp++;
|
||||
if (strcmp(cp, rp) != 0)
|
||||
strlcpy(rp, cp, sizeof(buf) - (rp - buf));
|
||||
strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf));
|
||||
}
|
||||
tname = buf;
|
||||
} else {
|
||||
@ -320,7 +321,7 @@ static int
|
||||
is_submake(void *cmdp, void *gnp)
|
||||
{
|
||||
static const char *p_make = NULL;
|
||||
static int p_len;
|
||||
static size_t p_len;
|
||||
char *cmd = cmdp;
|
||||
GNode *gn = gnp;
|
||||
char *mp = NULL;
|
||||
@ -329,12 +330,14 @@ is_submake(void *cmdp, void *gnp)
|
||||
int rc = 0; /* keep looking */
|
||||
|
||||
if (!p_make) {
|
||||
p_make = Var_Value(".MAKE", gn, &cp);
|
||||
void *dontFreeIt;
|
||||
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
|
||||
p_len = strlen(p_make);
|
||||
}
|
||||
cp = strchr(cmd, '$');
|
||||
if ((cp)) {
|
||||
mp = Var_Subst(cmd, gn, VARE_WANTRES);
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
cmd = mp;
|
||||
}
|
||||
cp2 = strstr(cmd, p_make);
|
||||
@ -368,29 +371,37 @@ typedef struct meta_file_s {
|
||||
GNode *gn;
|
||||
} meta_file_t;
|
||||
|
||||
static int
|
||||
printCMD(void *cmdp, void *mfpp)
|
||||
static void
|
||||
printCMD(const char *cmd, meta_file_t *mfp)
|
||||
{
|
||||
meta_file_t *mfp = mfpp;
|
||||
char *cmd = cmdp;
|
||||
char *cmd_freeIt = NULL;
|
||||
|
||||
if (strchr(cmd, '$')) {
|
||||
cmd = cmd_freeIt = Var_Subst(cmd, mfp->gn, VARE_WANTRES);
|
||||
(void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt);
|
||||
/* TODO: handle errors */
|
||||
cmd = cmd_freeIt;
|
||||
}
|
||||
fprintf(mfp->fp, "CMD %s\n", cmd);
|
||||
free(cmd_freeIt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
printCMDs(GNode *gn, meta_file_t *mf)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next)
|
||||
printCMD(ln->datum, mf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Certain node types never get a .meta file
|
||||
*/
|
||||
#define SKIP_META_TYPE(_type) do { \
|
||||
if ((gn->type & __CONCAT(OP_, _type))) { \
|
||||
if ((gn->type & __CONCAT(OP_, _type))) { \
|
||||
if (verbose) { \
|
||||
fprintf(debug_file, "Skipping meta for %s: .%s\n", \
|
||||
gn->name, __STRING(_type)); \
|
||||
debug_printf("Skipping meta for %s: .%s\n", \
|
||||
gn->name, __STRING(_type)); \
|
||||
} \
|
||||
return FALSE; \
|
||||
} \
|
||||
@ -423,16 +434,13 @@ meta_needed(GNode *gn, const char *dname,
|
||||
/* Check if there are no commands to execute. */
|
||||
if (Lst_IsEmpty(gn->commands)) {
|
||||
if (verbose)
|
||||
fprintf(debug_file, "Skipping meta for %s: no commands\n",
|
||||
gn->name);
|
||||
debug_printf("Skipping meta for %s: no commands\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
|
||||
/* OP_SUBMAKE is a bit too aggressive */
|
||||
if (Lst_ForEach(gn->commands, is_submake, gn)) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n",
|
||||
gn->name);
|
||||
if (Lst_ForEachUntil(gn->commands, is_submake, gn)) {
|
||||
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@ -440,8 +448,7 @@ meta_needed(GNode *gn, const char *dname,
|
||||
/* The object directory may not exist. Check it.. */
|
||||
if (cached_stat(dname, &mst) != 0) {
|
||||
if (verbose)
|
||||
fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
|
||||
gn->name);
|
||||
debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -452,8 +459,8 @@ meta_needed(GNode *gn, const char *dname,
|
||||
/* If we aren't in the object directory, don't create a meta file. */
|
||||
if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
|
||||
if (verbose)
|
||||
fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n",
|
||||
gn->name);
|
||||
debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n",
|
||||
gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
@ -471,14 +478,12 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
const char *tname;
|
||||
char *fname;
|
||||
const char *cp;
|
||||
char *p[5]; /* >= possible uses */
|
||||
int i;
|
||||
void *objdir_freeIt;
|
||||
|
||||
mf.fp = NULL;
|
||||
i = 0;
|
||||
|
||||
dname = Var_Value(".OBJDIR", gn, &p[i++]);
|
||||
tname = Var_Value(TARGET, gn, &p[i++]);
|
||||
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
|
||||
tname = GNode_VarTarget(gn);
|
||||
|
||||
/* if this succeeds objdir is realpath of dname */
|
||||
if (!meta_needed(gn, dname, objdir, TRUE))
|
||||
@ -489,7 +494,8 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
char *mp;
|
||||
|
||||
/* Describe the target we are building */
|
||||
mp = Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES);
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
if (*mp)
|
||||
fprintf(stdout, "%s\n", mp);
|
||||
free(mp);
|
||||
@ -511,8 +517,7 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
dname, tname, objdir);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_create: %s\n", fname);
|
||||
DEBUG1(META, "meta_create: %s\n", fname);
|
||||
#endif
|
||||
|
||||
if ((mf.fp = fopen(fname, "w")) == NULL)
|
||||
@ -522,11 +527,11 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
|
||||
mf.gn = gn;
|
||||
|
||||
Lst_ForEach(gn->commands, printCMD, &mf);
|
||||
printCMDs(gn, &mf);
|
||||
|
||||
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
|
||||
fprintf(mf.fp, "TARGET %s\n", tname);
|
||||
cp = Var_Value(".OODATE", gn, &p[i++]);
|
||||
cp = GNode_VarOodate(gn);
|
||||
if (cp && *cp) {
|
||||
fprintf(mf.fp, "OODATE %s\n", cp);
|
||||
}
|
||||
@ -546,9 +551,7 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
gn->type |= OP_SILENT;
|
||||
}
|
||||
out:
|
||||
for (i--; i >= 0; i--) {
|
||||
bmake_free(p[i]);
|
||||
}
|
||||
bmake_free(objdir_freeIt);
|
||||
|
||||
return mf.fp;
|
||||
}
|
||||
@ -592,6 +595,7 @@ meta_mode_init(const char *make_mode)
|
||||
{
|
||||
static int once = 0;
|
||||
char *cp;
|
||||
void *freeIt;
|
||||
|
||||
useMeta = TRUE;
|
||||
useFilemon = TRUE;
|
||||
@ -630,32 +634,34 @@ meta_mode_init(const char *make_mode)
|
||||
/*
|
||||
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
|
||||
*/
|
||||
metaBailiwick = Lst_Init();
|
||||
metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES);
|
||||
metaBailiwick = Lst_New();
|
||||
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
|
||||
/*
|
||||
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
|
||||
*/
|
||||
metaIgnorePaths = Lst_Init();
|
||||
metaIgnorePaths = Lst_New();
|
||||
Var_Append(MAKE_META_IGNORE_PATHS,
|
||||
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
|
||||
metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES);
|
||||
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
|
||||
|
||||
/*
|
||||
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
||||
*/
|
||||
cp = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) {
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) {
|
||||
metaIgnorePatterns = TRUE;
|
||||
bmake_free(cp);
|
||||
bmake_free(freeIt);
|
||||
}
|
||||
cp = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) {
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) {
|
||||
metaIgnoreFilter = TRUE;
|
||||
bmake_free(cp);
|
||||
bmake_free(freeIt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,7 +796,7 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
"(ignored)" : "");
|
||||
}
|
||||
if (gn) {
|
||||
Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL);
|
||||
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
|
||||
}
|
||||
getcwd(cwd, sizeof(cwd));
|
||||
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL);
|
||||
@ -813,15 +819,16 @@ meta_job_output(Job *job, char *cp, const char *nl)
|
||||
if (pbm->mfp != NULL) {
|
||||
if (metaVerbose) {
|
||||
static char *meta_prefix = NULL;
|
||||
static int meta_prefix_len;
|
||||
static size_t meta_prefix_len;
|
||||
|
||||
if (!meta_prefix) {
|
||||
char *cp2;
|
||||
|
||||
meta_prefix = Var_Subst("${" MAKE_META_PREFIX "}",
|
||||
VAR_GLOBAL, VARE_WANTRES);
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
|
||||
/* TODO: handle errors */
|
||||
if ((cp2 = strchr(meta_prefix, '$')))
|
||||
meta_prefix_len = cp2 - meta_prefix;
|
||||
meta_prefix_len = (size_t)(cp2 - meta_prefix);
|
||||
else
|
||||
meta_prefix_len = strlen(meta_prefix);
|
||||
}
|
||||
@ -910,9 +917,9 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
struct stat fs;
|
||||
int x;
|
||||
|
||||
if (fgets(&buf[o], bufsz - o, fp) != NULL) {
|
||||
if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) {
|
||||
check_newline:
|
||||
x = o + strlen(&buf[o]);
|
||||
x = o + (int)strlen(&buf[o]);
|
||||
if (buf[x - 1] == '\n')
|
||||
return x;
|
||||
/*
|
||||
@ -923,20 +930,18 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
size_t newsz;
|
||||
char *p;
|
||||
|
||||
newsz = ROUNDUP((fs.st_size / 2), BUFSIZ);
|
||||
newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ);
|
||||
if (newsz <= bufsz)
|
||||
newsz = ROUNDUP(fs.st_size, BUFSIZ);
|
||||
newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
|
||||
if (newsz <= bufsz)
|
||||
return x; /* truncated */
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "growing buffer %u -> %u\n",
|
||||
(unsigned)bufsz, (unsigned)newsz);
|
||||
DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz);
|
||||
p = bmake_realloc(buf, newsz);
|
||||
if (p) {
|
||||
*bufp = buf = p;
|
||||
*szp = bufsz = newsz;
|
||||
/* fetch the rest */
|
||||
if (!fgets(&buf[x], bufsz - x, fp))
|
||||
if (!fgets(&buf[x], (int)bufsz - x, fp))
|
||||
return x; /* truncated! */
|
||||
goto check_newline;
|
||||
}
|
||||
@ -945,7 +950,7 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lst_ForEach wants 1 to stop search */
|
||||
/* Lst_ForEachUntil wants 1 to stop search */
|
||||
static int
|
||||
prefix_match(void *p, void *q)
|
||||
{
|
||||
@ -958,24 +963,15 @@ prefix_match(void *p, void *q)
|
||||
|
||||
/* See if the path equals prefix or starts with "prefix/". */
|
||||
static Boolean
|
||||
path_match(const void *p, const void *q)
|
||||
path_starts_with(const char *path, const char *prefix)
|
||||
{
|
||||
const char *path = p;
|
||||
const char *prefix = q;
|
||||
size_t n = strlen(prefix);
|
||||
|
||||
if (strncmp(path, prefix, n) != 0)
|
||||
return FALSE;
|
||||
return FALSE;
|
||||
return path[n] == '\0' || path[n] == '/';
|
||||
}
|
||||
|
||||
static Boolean
|
||||
string_match(const void *p, const void *q)
|
||||
{
|
||||
return strcmp(p, q) == 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
meta_ignore(GNode *gn, const char *p)
|
||||
{
|
||||
@ -986,11 +982,9 @@ meta_ignore(GNode *gn, const char *p)
|
||||
|
||||
if (*p == '/') {
|
||||
cached_realpath(p, fname); /* clean it up */
|
||||
if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) {
|
||||
if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: ignoring path: %s\n",
|
||||
p);
|
||||
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
@ -1002,12 +996,11 @@ meta_ignore(GNode *gn, const char *p)
|
||||
|
||||
Var_Set(".p.", p, gn);
|
||||
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
|
||||
pm = Var_Subst(expr, gn, VARE_WANTRES);
|
||||
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
|
||||
/* TODO: handle errors */
|
||||
if (*pm) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n",
|
||||
p);
|
||||
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
|
||||
#endif
|
||||
free(pm);
|
||||
return TRUE;
|
||||
@ -1022,12 +1015,11 @@ meta_ignore(GNode *gn, const char *p)
|
||||
snprintf(fname, sizeof(fname),
|
||||
"${%s:L:${%s:ts:}}",
|
||||
p, MAKE_META_IGNORE_FILTER);
|
||||
fm = Var_Subst(fname, gn, VARE_WANTRES);
|
||||
(void)Var_Subst(fname, gn, VARE_WANTRES, &fm);
|
||||
/* TODO: handle errors */
|
||||
if (*fm == '\0') {
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n",
|
||||
p);
|
||||
DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p);
|
||||
#endif
|
||||
free(fm);
|
||||
return TRUE;
|
||||
@ -1063,6 +1055,17 @@ meta_ignore(GNode *gn, const char *p)
|
||||
*ep = '\0'; \
|
||||
}
|
||||
|
||||
static void
|
||||
append_if_new(StringList *list, const char *str)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = list->first; ln != NULL; ln = ln->next)
|
||||
if (strcmp(ln->datum, str) == 0)
|
||||
return;
|
||||
Lst_Append(list, bmake_strdup(str));
|
||||
}
|
||||
|
||||
Boolean
|
||||
meta_oodate(GNode *gn, Boolean oodate)
|
||||
{
|
||||
@ -1086,25 +1089,22 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
static size_t tmplen = 0;
|
||||
FILE *fp;
|
||||
Boolean needOODATE = FALSE;
|
||||
Lst missingFiles;
|
||||
char *pa[4]; /* >= possible uses */
|
||||
int i;
|
||||
StringList *missingFiles;
|
||||
int have_filemon = FALSE;
|
||||
void *objdir_freeIt;
|
||||
|
||||
if (oodate)
|
||||
return oodate; /* we're done */
|
||||
|
||||
i = 0;
|
||||
|
||||
dname = Var_Value(".OBJDIR", gn, &pa[i++]);
|
||||
tname = Var_Value(TARGET, gn, &pa[i++]);
|
||||
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
|
||||
tname = GNode_VarTarget(gn);
|
||||
|
||||
/* if this succeeds fname3 is realpath of dname */
|
||||
if (!meta_needed(gn, dname, fname3, FALSE))
|
||||
goto oodate_out;
|
||||
dname = fname3;
|
||||
|
||||
missingFiles = Lst_Init();
|
||||
missingFiles = Lst_New();
|
||||
|
||||
/*
|
||||
* We need to check if the target is out-of-date. This includes
|
||||
@ -1117,8 +1117,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
meta_name(fname, sizeof(fname), dname, tname, dname);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: %s\n", fname);
|
||||
DEBUG1(META, "meta_oodate: %s\n", fname);
|
||||
#endif
|
||||
|
||||
if ((fp = fopen(fname, "r")) != NULL) {
|
||||
@ -1128,7 +1127,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
int lastpid = 0;
|
||||
int pid;
|
||||
int x;
|
||||
LstNode ln;
|
||||
StringListNode *cmdNode;
|
||||
struct make_stat mst;
|
||||
|
||||
if (!buf) {
|
||||
@ -1152,7 +1151,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
/* we want to track all the .meta we read */
|
||||
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
|
||||
|
||||
ln = Lst_First(gn->commands);
|
||||
cmdNode = gn->commands->first;
|
||||
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
|
||||
lineno++;
|
||||
if (buf[x - 1] == '\n')
|
||||
@ -1179,8 +1178,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
/* Delimit the record type. */
|
||||
p = buf;
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
|
||||
DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf);
|
||||
#endif
|
||||
strsep(&p, " ");
|
||||
if (have_filemon) {
|
||||
@ -1223,7 +1221,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
pid = atoi(p);
|
||||
if (pid > 0 && pid != lastpid) {
|
||||
const char *ldir;
|
||||
char *tp;
|
||||
void *tp;
|
||||
|
||||
if (lastpid > 0) {
|
||||
/* We need to remember these. */
|
||||
@ -1249,9 +1247,9 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
continue;
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
pid, buf[0], cwd, lcwd, latestdir);
|
||||
debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
pid, buf[0], cwd, lcwd, latestdir);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@ -1279,9 +1277,10 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
Var_Set(cldir, latestdir, VAR_GLOBAL);
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
child, cwd, lcwd, latestdir);
|
||||
debug_printf(
|
||||
"%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
|
||||
fname, lineno,
|
||||
child, cwd, lcwd, latestdir);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1294,8 +1293,8 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL);
|
||||
Var_Set(ldir_vname, lcwd, VAR_GLOBAL);
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd);
|
||||
DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n",
|
||||
fname, lineno, cwd, lcwd);
|
||||
#endif
|
||||
break;
|
||||
|
||||
@ -1317,29 +1316,23 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
DEQUOTE(move_target);
|
||||
/* FALLTHROUGH */
|
||||
case 'D': /* unlink */
|
||||
if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
|
||||
if (*p == '/') {
|
||||
/* remove any missingFiles entries that match p */
|
||||
ln = Lst_Find(missingFiles, path_match, p);
|
||||
if (ln != NULL) {
|
||||
LstNode nln;
|
||||
char *tp;
|
||||
|
||||
do {
|
||||
nln = Lst_FindFrom(missingFiles,
|
||||
LstNode_Next(ln),
|
||||
path_match, p);
|
||||
tp = LstNode_Datum(ln);
|
||||
StringListNode *ln = missingFiles->first;
|
||||
while (ln != NULL) {
|
||||
StringListNode *next = ln->next;
|
||||
if (path_starts_with(ln->datum, p)) {
|
||||
free(ln->datum);
|
||||
Lst_Remove(missingFiles, ln);
|
||||
free(tp);
|
||||
} while ((ln = nln) != NULL);
|
||||
}
|
||||
ln = next;
|
||||
}
|
||||
}
|
||||
if (buf[0] == 'M') {
|
||||
/* the target of the mv is a file 'W'ritten */
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: M %s -> %s\n",
|
||||
p, move_target);
|
||||
DEBUG2(META, "meta_oodate: M %s -> %s\n",
|
||||
p, move_target);
|
||||
#endif
|
||||
p = move_target;
|
||||
goto check_write;
|
||||
@ -1360,9 +1353,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
DEQUOTE(p);
|
||||
DEQUOTE(link_src);
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: L %s -> %s\n",
|
||||
link_src, p);
|
||||
DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p);
|
||||
#endif
|
||||
/* FALLTHROUGH */
|
||||
case 'W': /* Write */
|
||||
@ -1383,7 +1374,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
if (strncmp(p, cwd, cwdlen) == 0)
|
||||
break;
|
||||
|
||||
if (!Lst_ForEach(metaBailiwick, prefix_match, p))
|
||||
if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p))
|
||||
break;
|
||||
|
||||
/* tmpdir might be within */
|
||||
@ -1396,18 +1387,15 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
if ((link_src != NULL && cached_lstat(p, &mst) < 0) ||
|
||||
(link_src == NULL && cached_stat(p, &mst) < 0)) {
|
||||
if (!meta_ignore(gn, p)) {
|
||||
if (Lst_Find(missingFiles, string_match, p) == NULL)
|
||||
Lst_Append(missingFiles, bmake_strdup(p));
|
||||
}
|
||||
if (!meta_ignore(gn, p))
|
||||
append_if_new(missingFiles, p);
|
||||
}
|
||||
break;
|
||||
check_link_src:
|
||||
p = link_src;
|
||||
link_src = NULL;
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "meta_oodate: L src %s\n", p);
|
||||
DEBUG1(META, "meta_oodate: L src %s\n", p);
|
||||
#endif
|
||||
/* FALLTHROUGH */
|
||||
case 'R': /* Read */
|
||||
@ -1455,8 +1443,8 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
for (sdp = sdirs; *sdp && !found; sdp++) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
|
||||
DEBUG3(META, "%s: %d: looking for: %s\n",
|
||||
fname, lineno, *sdp);
|
||||
#endif
|
||||
if (cached_stat(*sdp, &mst) == 0) {
|
||||
found = 1;
|
||||
@ -1465,13 +1453,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
if (found) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
|
||||
DEBUG3(META, "%s: %d: found: %s\n",
|
||||
fname, lineno, p);
|
||||
#endif
|
||||
if (!S_ISDIR(mst.mst_mode) &&
|
||||
mst.mst_mtime > gn->mtime) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p);
|
||||
DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n",
|
||||
fname, lineno, p);
|
||||
oodate = TRUE;
|
||||
} else if (S_ISDIR(mst.mst_mode)) {
|
||||
/* Update the latest directory. */
|
||||
@ -1483,8 +1471,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* A referenced file outside of CWD is missing.
|
||||
* We cannot catch every eventuality here...
|
||||
*/
|
||||
if (Lst_Find(missingFiles, string_match, p) == NULL)
|
||||
Lst_Append(missingFiles, bmake_strdup(p));
|
||||
append_if_new(missingFiles, p);
|
||||
}
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
@ -1502,12 +1489,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* Compare the current command with the one in the
|
||||
* meta data file.
|
||||
*/
|
||||
if (ln == NULL) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno);
|
||||
if (cmdNode == NULL) {
|
||||
DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n",
|
||||
fname, lineno);
|
||||
oodate = TRUE;
|
||||
} else {
|
||||
char *cmd = LstNode_Datum(ln);
|
||||
char *cmd = cmdNode->datum;
|
||||
Boolean hasOODATE = FALSE;
|
||||
|
||||
if (strstr(cmd, "$?"))
|
||||
@ -1519,10 +1506,11 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
if (hasOODATE) {
|
||||
needOODATE = TRUE;
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
|
||||
DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n",
|
||||
fname, lineno);
|
||||
}
|
||||
cmd = Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR);
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
|
||||
/* TODO: handle errors */
|
||||
|
||||
if ((cp = strchr(cmd, '\n'))) {
|
||||
int n;
|
||||
@ -1553,28 +1541,28 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
!hasOODATE &&
|
||||
!(gn->type & OP_NOMETA_CMP) &&
|
||||
strcmp(p, cmd) != 0) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd);
|
||||
DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n",
|
||||
fname, lineno, p, cmd);
|
||||
if (!metaIgnoreCMDs)
|
||||
oodate = TRUE;
|
||||
}
|
||||
free(cmd);
|
||||
ln = LstNode_Next(ln);
|
||||
cmdNode = cmdNode->next;
|
||||
}
|
||||
} else if (strcmp(buf, "CWD") == 0) {
|
||||
/*
|
||||
* Check if there are extra commands now
|
||||
* that weren't in the meta data file.
|
||||
*/
|
||||
if (!oodate && ln != NULL) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno);
|
||||
if (!oodate && cmdNode != NULL) {
|
||||
DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n",
|
||||
fname, lineno);
|
||||
oodate = TRUE;
|
||||
}
|
||||
CHECK_VALID_META(p);
|
||||
if (strcmp(p, cwd) != 0) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir);
|
||||
DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n",
|
||||
fname, lineno, p, curdir);
|
||||
oodate = TRUE;
|
||||
}
|
||||
}
|
||||
@ -1582,14 +1570,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
fclose(fp);
|
||||
if (!Lst_IsEmpty(missingFiles)) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: missing files: %s...\n",
|
||||
fname, (char *)LstNode_Datum(Lst_First(missingFiles)));
|
||||
DEBUG2(META, "%s: missing files: %s...\n",
|
||||
fname, (char *)missingFiles->first->datum);
|
||||
oodate = TRUE;
|
||||
}
|
||||
if (!oodate && !have_filemon && filemonMissing) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: missing filemon data\n", fname);
|
||||
DEBUG1(META, "%s: missing filemon data\n", fname);
|
||||
oodate = TRUE;
|
||||
}
|
||||
} else {
|
||||
@ -1598,13 +1584,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
/* if target is in .CURDIR we do not need a meta file */
|
||||
if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) {
|
||||
if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) {
|
||||
if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
|
||||
cp = NULL; /* not in .CURDIR */
|
||||
}
|
||||
}
|
||||
if (!cp) {
|
||||
if (DEBUG(META))
|
||||
fprintf(debug_file, "%s: required but missing\n", fname);
|
||||
DEBUG1(META, "%s: required but missing\n", fname);
|
||||
oodate = TRUE;
|
||||
needOODATE = TRUE; /* assume the worst */
|
||||
}
|
||||
@ -1620,14 +1605,11 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* All we can sanely do is set it to .ALLSRC.
|
||||
*/
|
||||
Var_Delete(OODATE, gn);
|
||||
Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn);
|
||||
bmake_free(cp);
|
||||
Var_Set(OODATE, GNode_VarAllsrc(gn), gn);
|
||||
}
|
||||
|
||||
oodate_out:
|
||||
for (i--; i >= 0; i--) {
|
||||
bmake_free(pa[i]);
|
||||
}
|
||||
bmake_free(objdir_freeIt);
|
||||
return oodate;
|
||||
}
|
||||
|
||||
@ -1662,11 +1644,8 @@ void
|
||||
meta_compat_child(void)
|
||||
{
|
||||
meta_job_child(NULL);
|
||||
if (dup2(childPipe[1], 1) < 0 ||
|
||||
dup2(1, 2) < 0) {
|
||||
execError("dup2", "pipe");
|
||||
_exit(1);
|
||||
}
|
||||
if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0)
|
||||
execDie("dup2", "pipe");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.h,v 1.7 2020/07/03 08:13:23 rillig Exp $ */
|
||||
/* $NetBSD: meta.h,v 1.8 2020/10/19 23:43:55 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Things needed for 'meta' mode.
|
||||
@ -38,9 +38,8 @@ typedef struct BuildMon {
|
||||
FILE *mfp;
|
||||
} BuildMon;
|
||||
|
||||
extern Boolean useMeta;
|
||||
struct Job;
|
||||
|
||||
struct Job; /* not defined yet */
|
||||
void meta_init(void);
|
||||
void meta_finish(void);
|
||||
void meta_mode_init(const char *);
|
||||
@ -57,3 +56,5 @@ Boolean meta_oodate(GNode *, Boolean);
|
||||
void meta_compat_start(void);
|
||||
void meta_compat_child(void);
|
||||
void meta_compat_parent(pid_t);
|
||||
|
||||
extern Boolean useMeta;
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $ */
|
||||
/* $NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2015 The NetBSD Foundation, Inc.
|
||||
@ -37,18 +37,15 @@
|
||||
#include <sys/cdefs.h>
|
||||
#endif
|
||||
|
||||
#if defined(__RCSID) && !defined(lint)
|
||||
__RCSID("$NetBSD: metachar.c,v 1.6 2020/08/03 20:43:41 rillig Exp $");
|
||||
#endif
|
||||
|
||||
#include "metachar.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The following array is used to make a fast determination of which
|
||||
* characters are interpreted specially by the shell. If a command
|
||||
* contains any of these characters, it is executed by the shell, not
|
||||
* directly by us.
|
||||
*
|
||||
* perhaps move it to ctype?
|
||||
*/
|
||||
|
||||
unsigned char _metachar[128] = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: metachar.h,v 1.7 2020/08/25 17:37:09 rillig Exp $ */
|
||||
/* $NetBSD: metachar.h,v 1.11 2020/10/31 18:20:00 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2015 The NetBSD Foundation, Inc.
|
||||
@ -35,17 +35,13 @@
|
||||
|
||||
extern unsigned char _metachar[];
|
||||
|
||||
#define ismeta(c) _metachar[(c) & 0x7f]
|
||||
#define is_shell_metachar(c) _metachar[(c) & 0x7f]
|
||||
|
||||
static inline int MAKE_ATTR_UNUSED
|
||||
needshell(const char *cmd, int white)
|
||||
static inline MAKE_ATTR_UNUSED int
|
||||
needshell(const char *cmd)
|
||||
{
|
||||
while (!ismeta(*cmd) && *cmd != ':' && *cmd != '=') {
|
||||
if (white && isspace((unsigned char)*cmd))
|
||||
break;
|
||||
while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=')
|
||||
cmd++;
|
||||
}
|
||||
|
||||
return *cmd != '\0';
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,31 @@
|
||||
2020-11-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201101
|
||||
|
||||
* dirdeps.mk: most leaf makefiles are not suitable for building
|
||||
dirdeps.cache so if RELDIR is not "." use dirdeps.mk
|
||||
|
||||
2020-10-28 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201028
|
||||
|
||||
* dirdeps.mk: if we don't have :range use equivalent of M_RANGE
|
||||
when building dirdeps.cache for leaf directory use -f dirdeps.mk
|
||||
|
||||
* sys.vars.mk: add M_JOT and M_RANGE
|
||||
|
||||
2020-10-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201001
|
||||
|
||||
* meta2deps.{py,sh}: throw an error if we don't see filemon version
|
||||
|
||||
2020-09-09 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20200909
|
||||
|
||||
* dirdeps-cache-update.mk: use cache_update_dirdep as guard target
|
||||
|
||||
2020-08-26 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* dirdeps.mk: ensure we cannot confuse a static cache for dynamic
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: dirdeps-cache-update.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: dirdeps-cache-update.mk,v 1.22 2020/09/10 00:14:38 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2020, Simon J. Gerraty
|
||||
#
|
||||
@ -86,7 +86,7 @@ _debug_cache = 0
|
||||
.endif
|
||||
|
||||
.if ${MK_STATIC_DIRDEPS_CACHE} == "yes" && defined(STATIC_DIRDEPS_CACHE) && exists(${STATIC_DIRDEPS_CACHE})
|
||||
.if !make(dirdeps)
|
||||
.if !make(dirdeps) && !target(cache_update_dirdep)
|
||||
# We are using static cache and this is the only look we will get.
|
||||
# We want to generate an updated cache while we build
|
||||
# so need to hook cache-update to dirdeps now.
|
||||
@ -99,12 +99,10 @@ _debug_cache = 0
|
||||
cache_update_dirdep ?= $d.${TARGET_SPEC}
|
||||
.endif
|
||||
.endfor
|
||||
.if !target(${cache_update_dirdep})
|
||||
dirdeps: ${cache_update_dirdep}
|
||||
dirdeps cache_update_dirdep: ${cache_update_dirdep}
|
||||
${cache_update_dirdep}: _DIRDEP_USE
|
||||
DYNAMIC_DIRDEPS_CACHE := ${OBJTOP}/dirdeps.cache.${STATIC_DIRDEPS_CACHE:H:T}-update
|
||||
.export DYNAMIC_DIRDEPS_CACHE STATIC_DIRDEPS_CACHE
|
||||
.endif
|
||||
.endif # make(dirdeps)
|
||||
.endif # MK_*
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: dirdeps.mk,v 1.125 2020/08/26 21:49:45 sjg Exp $
|
||||
# $Id: dirdeps.mk,v 1.130 2020/11/02 00:34:30 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2020, Simon J. Gerraty
|
||||
# Copyright (c) 2010-2018, Juniper Networks, Inc.
|
||||
@ -209,12 +209,9 @@ DEP_$v ?= ${$v}
|
||||
# so we need to construct a set of modifiers to fill in the gaps.
|
||||
.if ${MAKE_VERSION} >= 20170130
|
||||
_tspec_x := ${TARGET_SPEC_VARS:range}
|
||||
.elif ${TARGET_SPEC_VARS:[#]} > 10
|
||||
# seriously? better have jot(1) or equivalent to produce suitable sequence
|
||||
_tspec_x := ${${JOT:Ujot} ${TARGET_SPEC_VARS:[#]}:L:sh}
|
||||
.else
|
||||
# we can provide the sequence ourselves
|
||||
_tspec_x := ${1 2 3 4 5 6 7 8 9 10:L:[1..${TARGET_SPEC_VARS:[#]}]}
|
||||
# do it the hard way
|
||||
_tspec_x := ${TARGET_SPEC_VARS:[#]:@x@i=1;while [ $$i -le $x ]; do echo $$i; i=$$((i + 1)); done;@:sh}
|
||||
.endif
|
||||
# this handles unqualified entries
|
||||
M_dep_qual_fixes = C;(/[^/.,]+)$$;\1.$${DEP_TARGET_SPEC};
|
||||
@ -494,6 +491,11 @@ dirdeps-cached: ${DIRDEPS_CACHE} .MAKE
|
||||
@MAKELEVEL=${.MAKE.LEVEL} ${.MAKE} -C ${_CURDIR} -f ${DIRDEPS_CACHE} \
|
||||
dirdeps MK_DIRDEPS_CACHE=no BUILD_DIRDEPS=no
|
||||
|
||||
# leaf makefiles rarely work for building DIRDEPS_CACHE
|
||||
.if ${RELDIR} != "."
|
||||
BUILD_DIRDEPS_MAKEFILE ?= -f dirdeps.mk
|
||||
.endif
|
||||
|
||||
# these should generally do
|
||||
BUILD_DIRDEPS_MAKEFILE ?=
|
||||
BUILD_DIRDEPS_TARGETS ?= ${.TARGETS}
|
||||
@ -517,6 +519,7 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
|
||||
${BUILD_DIRDEPS_MAKEFILE} \
|
||||
${BUILD_DIRDEPS_TARGETS} BUILD_DIRDEPS_CACHE=yes \
|
||||
.MAKE.DEPENDFILE=.none \
|
||||
${"${DEBUG_DIRDEPS:Nno}":?DEBUG_DIRDEPS='${DEBUG_DIRDEPS}':} \
|
||||
${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \
|
||||
${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \
|
||||
3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \
|
||||
@ -627,6 +630,7 @@ __qual_depdirs += ${__hostdpadd}
|
||||
.endif
|
||||
|
||||
.if ${_debug_reldir}
|
||||
.info DEP_DIRDEPS_FILTER=${DEP_DIRDEPS_FILTER:ts:}
|
||||
.info depdirs=${__depdirs}
|
||||
.info qualified=${__qual_depdirs}
|
||||
.info unqualified=${__unqual_depdirs}
|
||||
|
@ -55,7 +55,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: install-mk,v 1.179 2020/08/26 21:49:45 sjg Exp $
|
||||
# $Id: install-mk,v 1.183 2020/11/02 16:34:12 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 1994 Simon J. Gerraty
|
||||
#
|
||||
@ -70,7 +70,7 @@
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
MK_VERSION=20200826
|
||||
MK_VERSION=20201101
|
||||
OWNER=
|
||||
GROUP=
|
||||
MODE=444
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
"""
|
||||
RCSid:
|
||||
$Id: meta2deps.py,v 1.33 2020/08/19 17:51:53 sjg Exp $
|
||||
$Id: meta2deps.py,v 1.34 2020/10/02 03:11:17 sjg Exp $
|
||||
|
||||
Copyright (c) 2011-2020, Simon J. Gerraty
|
||||
Copyright (c) 2011-2017, Juniper Networks, Inc.
|
||||
@ -510,6 +510,7 @@ def parse(self, name=None, file=None):
|
||||
continue
|
||||
self.parse_path(path, cwd, w[0], w)
|
||||
|
||||
assert(version > 0)
|
||||
if not file:
|
||||
f.close()
|
||||
|
||||
|
@ -75,7 +75,7 @@
|
||||
|
||||
|
||||
# RCSid:
|
||||
# $Id: meta2deps.sh,v 1.13 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: meta2deps.sh,v 1.14 2020/10/02 03:11:17 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2013, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
@ -141,6 +141,11 @@ _excludes_f() {
|
||||
egrep -v "$EXCLUDES"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "ERROR: $@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
meta2deps() {
|
||||
DPDEPS=
|
||||
SRCTOPS=$SRCTOP
|
||||
@ -234,8 +239,8 @@ meta2deps() {
|
||||
;;
|
||||
*) cat /dev/null "$@";;
|
||||
esac 2> /dev/null |
|
||||
sed -e 's,^CWD,C C,;/^[CREFLM] /!d' -e "s,',,g" |
|
||||
$_excludes |
|
||||
sed -e 's,^CWD,C C,;/^[CREFLMV] /!d' -e "s,',,g" |
|
||||
$_excludes | ( version=no
|
||||
while read op pid path junk
|
||||
do
|
||||
: op=$op pid=$pid path=$path
|
||||
@ -247,6 +252,12 @@ meta2deps() {
|
||||
SB=`echo $CWD | sed 's,/obj.*,,'`
|
||||
fi
|
||||
SRCTOP=${SRCTOP:-$SB/src}
|
||||
case "$verion" in
|
||||
no) ;; # ignore
|
||||
0) error "no filemon data";;
|
||||
*) ;;
|
||||
esac
|
||||
version=0
|
||||
continue
|
||||
;;
|
||||
$pid,$pid) ;;
|
||||
@ -261,6 +272,7 @@ meta2deps() {
|
||||
esac
|
||||
|
||||
case "$op,$path" in
|
||||
V,*) version=$path; continue;;
|
||||
W,*srcrel|*.dirdep) continue;;
|
||||
C,*)
|
||||
case "$path" in
|
||||
@ -366,6 +378,9 @@ meta2deps() {
|
||||
echo $dir;;
|
||||
esac
|
||||
done > $tf.dirdep
|
||||
case "$version" in
|
||||
0) error "no filemon data";;
|
||||
esac ) || exit 1
|
||||
_nl=echo
|
||||
for f in $tf.dirdep $tf.qual $tf.srcdep
|
||||
do
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: sys.vars.mk,v 1.5 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: sys.vars.mk,v 1.6 2020/10/28 20:50:04 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
|
||||
#
|
||||
@ -57,6 +57,20 @@ _type_sh = which
|
||||
M_type = @x@(${_type_sh:Utype} $$x) 2> /dev/null; echo;@:sh:[0]:N* found*:[@]:C,[()],,g
|
||||
M_whence = ${M_type}:M/*:[1]
|
||||
|
||||
# produce similar output to jot(1)
|
||||
# eg. ${LIST:[#]:${M_JOT}}
|
||||
# would be 1 2 3 4 5 if LIST has 5 words
|
||||
# ${9:L:${M_JOT}}
|
||||
# would be 1 2 3 4 5 6 7 8 9
|
||||
M_JOT = @x@i=1;while [ $$$$i -le $$x ]; do echo $$$$i; i=$$$$((i + 1)); done;@:sh
|
||||
|
||||
# ${LIST:${M_RANGE}} is 1 2 3 4 5 if LIST has 5 words
|
||||
.if ${MAKE_VERSION} >= 20170130
|
||||
M_RANGE = range
|
||||
.else
|
||||
M_RANGE = [#]:${M_JOT}
|
||||
.endif
|
||||
|
||||
# convert a path to a valid shell variable
|
||||
M_P2V = tu:C,[./-],_,g
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: nonints.h,v 1.102 2020/08/30 19:56:02 rillig Exp $ */
|
||||
/* $NetBSD: nonints.h,v 1.149 2020/11/01 00:24:57 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -73,32 +73,32 @@
|
||||
*/
|
||||
|
||||
/* arch.c */
|
||||
Boolean Arch_ParseArchive(char **, Lst, GNode *);
|
||||
void Arch_Init(void);
|
||||
void Arch_End(void);
|
||||
|
||||
Boolean Arch_ParseArchive(char **, GNodeList *, GNode *);
|
||||
void Arch_Touch(GNode *);
|
||||
void Arch_TouchLib(GNode *);
|
||||
time_t Arch_MTime(GNode *);
|
||||
time_t Arch_MemMTime(GNode *);
|
||||
void Arch_FindLib(GNode *, Lst);
|
||||
void Arch_FindLib(GNode *, SearchPath *);
|
||||
Boolean Arch_LibOODate(GNode *);
|
||||
void Arch_Init(void);
|
||||
void Arch_End(void);
|
||||
Boolean Arch_IsLib(GNode *);
|
||||
|
||||
/* compat.c */
|
||||
int CompatRunCommand(void *, void *);
|
||||
void Compat_Run(Lst);
|
||||
int Compat_Make(void *, void *);
|
||||
int Compat_RunCommand(const char *, GNode *);
|
||||
void Compat_Run(GNodeList *);
|
||||
void Compat_Make(GNode *, GNode *);
|
||||
|
||||
/* cond.c */
|
||||
struct If;
|
||||
CondEvalResult Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean);
|
||||
CondEvalResult Cond_Eval(char *);
|
||||
CondEvalResult Cond_EvalCondition(const char *, Boolean *);
|
||||
CondEvalResult Cond_EvalLine(const char *);
|
||||
void Cond_restore_depth(unsigned int);
|
||||
unsigned int Cond_save_depth(void);
|
||||
|
||||
/* for.c */
|
||||
int For_Eval(char *);
|
||||
int For_Accum(char *);
|
||||
int For_Eval(const char *);
|
||||
Boolean For_Accum(const char *);
|
||||
void For_Run(int);
|
||||
|
||||
/* job.c */
|
||||
@ -114,35 +114,52 @@ void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
void DieHorribly(void) MAKE_ATTR_DEAD;
|
||||
int PrintAddr(void *, void *);
|
||||
void Finish(int) MAKE_ATTR_DEAD;
|
||||
int eunlink(const char *);
|
||||
void execError(const char *, const char *);
|
||||
void execDie(const char *, const char *);
|
||||
char *getTmpdir(void);
|
||||
Boolean s2Boolean(const char *, Boolean);
|
||||
Boolean getBoolean(const char *, Boolean);
|
||||
char *cached_realpath(const char *, char *);
|
||||
|
||||
/* parse.c */
|
||||
void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
Boolean Parse_IsVar(char *);
|
||||
void Parse_DoVar(char *, GNode *);
|
||||
void Parse_AddIncludeDir(char *);
|
||||
void Parse_File(const char *, int);
|
||||
void Parse_Init(void);
|
||||
void Parse_End(void);
|
||||
void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *);
|
||||
Lst Parse_MainName(void);
|
||||
|
||||
typedef enum VarAssignOp {
|
||||
VAR_NORMAL, /* = */
|
||||
VAR_SUBST, /* := */
|
||||
VAR_SHELL, /* != or :sh= */
|
||||
VAR_APPEND, /* += */
|
||||
VAR_DEFAULT /* ?= */
|
||||
} VarAssignOp;
|
||||
|
||||
typedef struct VarAssign {
|
||||
char *varname; /* unexpanded */
|
||||
VarAssignOp op;
|
||||
const char *value; /* unexpanded */
|
||||
} VarAssign;
|
||||
|
||||
typedef char *(*NextBufProc)(void *, size_t *);
|
||||
|
||||
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
Boolean Parse_IsVar(const char *, VarAssign *out_var);
|
||||
void Parse_DoVar(VarAssign *, GNode *);
|
||||
void Parse_AddIncludeDir(const char *);
|
||||
void Parse_File(const char *, int);
|
||||
void Parse_SetInput(const char *, int, int, NextBufProc, void *);
|
||||
GNodeList *Parse_MainName(void);
|
||||
int Parse_GetFatals(void);
|
||||
|
||||
/* str.c */
|
||||
typedef struct {
|
||||
typedef struct Words {
|
||||
char **words;
|
||||
size_t len;
|
||||
void *freeIt;
|
||||
} Words;
|
||||
|
||||
Words Str_Words(const char *, Boolean);
|
||||
static inline void MAKE_ATTR_UNUSED
|
||||
static inline MAKE_ATTR_UNUSED void
|
||||
Words_Free(Words w) {
|
||||
free(w.words);
|
||||
free(w.freeIt);
|
||||
@ -151,7 +168,6 @@ Words_Free(Words w) {
|
||||
char *str_concat2(const char *, const char *);
|
||||
char *str_concat3(const char *, const char *, const char *);
|
||||
char *str_concat4(const char *, const char *, const char *, const char *);
|
||||
char *Str_FindSubstring(const char *, const char *);
|
||||
Boolean Str_Match(const char *, const char *);
|
||||
|
||||
#ifndef HAVE_STRLCPY
|
||||
@ -160,69 +176,134 @@ size_t strlcpy(char *, const char *, size_t);
|
||||
#endif
|
||||
|
||||
/* suff.c */
|
||||
void Suff_ClearSuffixes(void);
|
||||
Boolean Suff_IsTransform(char *);
|
||||
GNode *Suff_AddTransform(char *);
|
||||
int Suff_EndTransform(void *, void *);
|
||||
void Suff_AddSuffix(const char *, GNode **);
|
||||
Lst Suff_GetPath(char *);
|
||||
void Suff_DoPaths(void);
|
||||
void Suff_AddInclude(char *);
|
||||
void Suff_AddLib(const char *);
|
||||
void Suff_FindDeps(GNode *);
|
||||
Lst Suff_FindPath(GNode *);
|
||||
void Suff_SetNull(char *);
|
||||
void Suff_Init(void);
|
||||
void Suff_End(void);
|
||||
|
||||
void Suff_ClearSuffixes(void);
|
||||
Boolean Suff_IsTransform(const char *);
|
||||
GNode *Suff_AddTransform(const char *);
|
||||
void Suff_EndTransform(GNode *);
|
||||
void Suff_AddSuffix(const char *, GNode **);
|
||||
SearchPath *Suff_GetPath(const char *);
|
||||
void Suff_DoPaths(void);
|
||||
void Suff_AddInclude(const char *);
|
||||
void Suff_AddLib(const char *);
|
||||
void Suff_FindDeps(GNode *);
|
||||
SearchPath *Suff_FindPath(GNode *);
|
||||
void Suff_SetNull(const char *);
|
||||
void Suff_PrintAll(void);
|
||||
|
||||
/* targ.c */
|
||||
void Targ_Init(void);
|
||||
void Targ_End(void);
|
||||
|
||||
void Targ_Stats(void);
|
||||
Lst Targ_List(void);
|
||||
GNodeList *Targ_List(void);
|
||||
GNode *Targ_NewGN(const char *);
|
||||
GNode *Targ_FindNode(const char *, int);
|
||||
Lst Targ_FindList(Lst, int);
|
||||
GNode *Targ_FindNode(const char *);
|
||||
GNode *Targ_GetNode(const char *);
|
||||
GNode *Targ_NewInternalNode(const char *);
|
||||
GNode *Targ_GetEndNode(void);
|
||||
GNodeList *Targ_FindList(StringList *);
|
||||
Boolean Targ_Ignore(GNode *);
|
||||
Boolean Targ_Silent(GNode *);
|
||||
Boolean Targ_Precious(GNode *);
|
||||
void Targ_SetMain(GNode *);
|
||||
int Targ_PrintCmd(void *, void *);
|
||||
int Targ_PrintNode(void *, void *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
void Targ_PrintNodes(GNodeList *, int);
|
||||
char *Targ_FmtTime(time_t);
|
||||
void Targ_PrintType(int);
|
||||
void Targ_PrintGraph(int);
|
||||
void Targ_Propagate(void);
|
||||
|
||||
/* var.c */
|
||||
void Var_Init(void);
|
||||
void Var_End(void);
|
||||
|
||||
typedef enum {
|
||||
typedef enum VarEvalFlags {
|
||||
VARE_NONE = 0,
|
||||
/* Treat undefined variables as errors. */
|
||||
VARE_UNDEFERR = 0x01,
|
||||
/* Expand and evaluate variables during parsing. */
|
||||
VARE_WANTRES = 0x02,
|
||||
/* In an assignment using the ':=' operator, keep '$$' as '$$' instead
|
||||
* of reducing it to a single '$'. */
|
||||
VARE_ASSIGN = 0x04
|
||||
} VarEvalFlags;
|
||||
|
||||
typedef enum {
|
||||
typedef enum VarSet_Flags {
|
||||
VAR_NO_EXPORT = 0x01, /* do not export */
|
||||
/* Make the variable read-only. No further modification is possible,
|
||||
* except for another call to Var_Set with the same flag. */
|
||||
VAR_SET_READONLY = 0x02
|
||||
} VarSet_Flags;
|
||||
|
||||
/* The state of error handling returned by Var_Parse.
|
||||
*
|
||||
* As of 2020-09-13, this bitset looks quite bloated,
|
||||
* with all the constants doubled.
|
||||
*
|
||||
* Its purpose is to first document the existing behavior,
|
||||
* and then migrate away from the SILENT constants, step by step,
|
||||
* as these are not suited for reliable, consistent error handling
|
||||
* and reporting. */
|
||||
typedef enum VarParseResult {
|
||||
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK = 0x0000,
|
||||
|
||||
/* See if a message has already been printed for this error. */
|
||||
VPR_ANY_MSG = 0x0001,
|
||||
|
||||
/* Parsing failed.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_PARSE_MSG instead. */
|
||||
VPR_PARSE_SILENT = 0x0002,
|
||||
|
||||
/* Parsing failed.
|
||||
* An error message has already been printed. */
|
||||
VPR_PARSE_MSG = VPR_PARSE_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* During evaluation, VARE_UNDEFERR was set and there was an undefined
|
||||
* variable.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_UNDEF_MSG instead. */
|
||||
VPR_UNDEF_SILENT = 0x0004,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* During evaluation, VARE_UNDEFERR was set and there was an undefined
|
||||
* variable.
|
||||
* An error message has already been printed. */
|
||||
VPR_UNDEF_MSG = VPR_UNDEF_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* Evaluation failed.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_EVAL_MSG instead. */
|
||||
VPR_EVAL_SILENT = 0x0006,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* Evaluation failed.
|
||||
* An error message has already been printed. */
|
||||
VPR_EVAL_MSG = VPR_EVAL_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* The exact error handling status is not known yet.
|
||||
* Deprecated, migrate to VPR_OK or any VPE_*_MSG instead. */
|
||||
VPR_UNKNOWN = 0x0008
|
||||
} VarParseResult;
|
||||
|
||||
void Var_Delete(const char *, GNode *);
|
||||
void Var_Set(const char *, const char *, GNode *);
|
||||
void Var_Set_with_flags(const char *, const char *, GNode *, VarSet_Flags);
|
||||
void Var_Append(const char *, const char *, GNode *);
|
||||
Boolean Var_Exists(const char *, GNode *);
|
||||
const char *Var_Value(const char *, GNode *, char **);
|
||||
const char *Var_Parse(const char *, GNode *, VarEvalFlags, int *, void **);
|
||||
char *Var_Subst(const char *, GNode *, VarEvalFlags);
|
||||
void Var_Init(void);
|
||||
void Var_End(void);
|
||||
const char *Var_Value(const char *, GNode *, void **);
|
||||
const char *Var_ValueDirect(const char *, GNode *);
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags,
|
||||
const char **, void **);
|
||||
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
|
||||
void Var_Stats(void);
|
||||
void Var_Dump(GNode *);
|
||||
void Var_ExportVars(void);
|
||||
@ -230,4 +311,5 @@ void Var_Export(const char *, Boolean);
|
||||
void Var_UnExport(const char *);
|
||||
|
||||
/* util.c */
|
||||
void (*bmake_signal(int, void (*)(int)))(int);
|
||||
typedef void (*SignalProc)(int);
|
||||
SignalProc bmake_signal(int, SignalProc);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $ */
|
||||
/* $NetBSD: str.c,v 1.70 2020/10/24 20:51:49 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,21 +68,11 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
|
||||
#else
|
||||
__RCSID("$NetBSD: str.c,v 1.64 2020/08/30 19:56:02 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.70 2020/10/24 20:51:49 rillig Exp $");
|
||||
|
||||
/* Return the concatenation of s1 and s2, freshly allocated. */
|
||||
char *
|
||||
str_concat2(const char *s1, const char *s2)
|
||||
@ -157,7 +147,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
str_len = strlen(str);
|
||||
words_buf = bmake_malloc(strlen(str) + 1);
|
||||
|
||||
words_cap = MAX((str_len / 5), 50);
|
||||
words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
|
||||
words = bmake_malloc((words_cap + 1) * sizeof(char *));
|
||||
|
||||
/*
|
||||
@ -179,7 +169,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
else
|
||||
break;
|
||||
} else {
|
||||
inquote = (char)ch;
|
||||
inquote = ch;
|
||||
/* Don't miss "" or '' */
|
||||
if (word_start == NULL && str_p[1] == inquote) {
|
||||
if (!expand) {
|
||||
@ -278,46 +268,6 @@ Str_Words(const char *str, Boolean expand)
|
||||
return (Words){ words, words_len, words_buf };
|
||||
}
|
||||
|
||||
/*
|
||||
* Str_FindSubstring -- See if a string contains a particular substring.
|
||||
*
|
||||
* Input:
|
||||
* string String to search.
|
||||
* substring Substring to find in string.
|
||||
*
|
||||
* Results: If string contains substring, the return value is the location of
|
||||
* the first matching instance of substring in string. If string doesn't
|
||||
* contain substring, the return value is NULL. Matching is done on an exact
|
||||
* character-for-character basis with no wildcards or special characters.
|
||||
*
|
||||
* Side effects: None.
|
||||
*/
|
||||
char *
|
||||
Str_FindSubstring(const char *string, const char *substring)
|
||||
{
|
||||
const char *a, *b;
|
||||
|
||||
/*
|
||||
* First scan quickly through the two strings looking for a single-
|
||||
* character match. When it's found, then compare the rest of the
|
||||
* substring.
|
||||
*/
|
||||
|
||||
for (b = substring; *string != 0; string++) {
|
||||
if (*string != *b)
|
||||
continue;
|
||||
a = string;
|
||||
for (;;) {
|
||||
if (*b == 0)
|
||||
return UNCONST(string);
|
||||
if (*a++ != *b++)
|
||||
break;
|
||||
}
|
||||
b = substring;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Str_Match -- Test if a string matches a pattern like "*.[ch]".
|
||||
*
|
||||
|
@ -1,91 +0,0 @@
|
||||
/* $NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by David Laight.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: strlist.c,v 1.6 2020/08/25 17:37:09 rillig Exp $");
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
#include "make.h"
|
||||
#include "strlist.h"
|
||||
|
||||
void
|
||||
strlist_init(strlist_t *sl)
|
||||
{
|
||||
sl->sl_num = 0;
|
||||
sl->sl_max = 0;
|
||||
sl->sl_items = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
strlist_clean(strlist_t *sl)
|
||||
{
|
||||
char *str;
|
||||
int i;
|
||||
|
||||
STRLIST_FOREACH(str, sl, i)
|
||||
free(str);
|
||||
free(sl->sl_items);
|
||||
|
||||
sl->sl_num = 0;
|
||||
sl->sl_max = 0;
|
||||
sl->sl_items = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
strlist_add_str(strlist_t *sl, char *str, unsigned int info)
|
||||
{
|
||||
unsigned int n;
|
||||
strlist_item_t *items;
|
||||
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
n = sl->sl_num + 1;
|
||||
sl->sl_num = n;
|
||||
items = sl->sl_items;
|
||||
if (n >= sl->sl_max) {
|
||||
items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items);
|
||||
sl->sl_items = items;
|
||||
sl->sl_max = n + 6;
|
||||
}
|
||||
items += n - 1;
|
||||
items->si_str = str;
|
||||
items->si_info = info;
|
||||
items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/* $NetBSD: strlist.h,v 1.4 2020/08/13 03:54:57 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by David Laight.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of The NetBSD Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_STRLIST_H
|
||||
#define MAKE_STRLIST_H
|
||||
|
||||
typedef struct {
|
||||
char *si_str;
|
||||
unsigned int si_info;
|
||||
} strlist_item_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int sl_num;
|
||||
unsigned int sl_max;
|
||||
strlist_item_t *sl_items;
|
||||
} strlist_t;
|
||||
|
||||
void strlist_init(strlist_t *);
|
||||
void strlist_clean(strlist_t *);
|
||||
void strlist_add_str(strlist_t *, char *, unsigned int);
|
||||
|
||||
#define strlist_num(sl) ((sl)->sl_num)
|
||||
#define strlist_str(sl, n) ((sl)->sl_items[n].si_str)
|
||||
#define strlist_info(sl, n) ((sl)->sl_items[n].si_info)
|
||||
#define strlist_set_info(sl, n, v) ((void)((sl)->sl_items[n].si_info = (v)))
|
||||
|
||||
#define STRLIST_FOREACH(v, sl, index) \
|
||||
if ((sl)->sl_items != NULL) \
|
||||
for (index = 0; (v = strlist_str(sl, index)) != NULL; index++)
|
||||
|
||||
#endif /* MAKE_STRLIST_H */
|
1591
contrib/bmake/suff.c
1591
contrib/bmake/suff.c
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $ */
|
||||
/* $NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,80 +68,67 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94";
|
||||
#else
|
||||
__RCSID("$NetBSD: targ.c,v 1.81 2020/09/01 20:54:00 rillig Exp $");
|
||||
#endif
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
/*-
|
||||
* targ.c --
|
||||
* Functions for maintaining the Lst allTargets. Target nodes are
|
||||
* kept in two structures: a Lst and a hash table.
|
||||
*
|
||||
* Interface:
|
||||
* Targ_Init Initialization procedure.
|
||||
* Targ_Init Initialization procedure.
|
||||
*
|
||||
* Targ_End Cleanup the module
|
||||
* Targ_End Clean up the module
|
||||
*
|
||||
* Targ_List Return the list of all targets so far.
|
||||
* Targ_List Return the list of all targets so far.
|
||||
*
|
||||
* Targ_NewGN Create a new GNode for the passed target
|
||||
* (string). The node is *not* placed in the
|
||||
* hash table, though all its fields are
|
||||
* initialized.
|
||||
* Targ_NewGN Create a new GNode for the passed target
|
||||
* (string). The node is *not* placed in the
|
||||
* hash table, though all its fields are
|
||||
* initialized.
|
||||
*
|
||||
* Targ_FindNode Find the node for a given target, creating
|
||||
* and storing it if it doesn't exist and the
|
||||
* flags are right (TARG_CREATE)
|
||||
* Targ_FindNode Find the node, or return NULL.
|
||||
*
|
||||
* Targ_FindList Given a list of names, find nodes for all
|
||||
* of them. If a name doesn't exist and the
|
||||
* TARG_NOCREATE flag was given, an error message
|
||||
* is printed. Else, if a name doesn't exist,
|
||||
* its node is created.
|
||||
* Targ_GetNode Find the node, or create it.
|
||||
*
|
||||
* Targ_Ignore Return TRUE if errors should be ignored when
|
||||
* creating the given target.
|
||||
* Targ_NewInternalNode
|
||||
* Create an internal node.
|
||||
*
|
||||
* Targ_Silent Return TRUE if we should be silent when
|
||||
* creating the given target.
|
||||
* Targ_FindList Given a list of names, find nodes for all
|
||||
* of them, creating them as necessary.
|
||||
*
|
||||
* Targ_Precious Return TRUE if the target is precious and
|
||||
* should not be removed if we are interrupted.
|
||||
* Targ_Ignore Return TRUE if errors should be ignored when
|
||||
* creating the given target.
|
||||
*
|
||||
* Targ_Propagate Propagate information between related
|
||||
* nodes. Should be called after the
|
||||
* makefiles are parsed but before any
|
||||
* action is taken.
|
||||
* Targ_Silent Return TRUE if we should be silent when
|
||||
* creating the given target.
|
||||
*
|
||||
* Targ_Precious Return TRUE if the target is precious and
|
||||
* should not be removed if we are interrupted.
|
||||
*
|
||||
* Targ_Propagate Propagate information between related nodes.
|
||||
* Should be called after the makefiles are parsed
|
||||
* but before any action is taken.
|
||||
*
|
||||
* Debugging:
|
||||
* Targ_PrintGraph Print out the entire graphm all variables
|
||||
* and statistics for the directory cache. Should
|
||||
* print something for suffixes, too, but...
|
||||
* Targ_PrintGraph
|
||||
* Print out the entire graphm all variables and
|
||||
* statistics for the directory cache. Should print
|
||||
* something for suffixes, too, but...
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "make.h"
|
||||
#include "dir.h"
|
||||
#include "make.h"
|
||||
#include "dir.h"
|
||||
|
||||
static Lst allTargets; /* the list of all targets found so far */
|
||||
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.126 2020/10/30 07:19:30 rillig Exp $");
|
||||
|
||||
static GNodeList *allTargets; /* the list of all targets found so far */
|
||||
#ifdef CLEANUP
|
||||
static Lst allGNs; /* List of all the GNodes */
|
||||
static GNodeList *allGNs; /* List of all the GNodes */
|
||||
#endif
|
||||
static Hash_Table targets; /* a hash table of same */
|
||||
static HashTable targets; /* a hash table of same */
|
||||
|
||||
static int TargPrintOnlySrc(void *, void *);
|
||||
static int TargPrintName(void *, void *);
|
||||
#ifdef CLEANUP
|
||||
static void TargFreeGN(void *);
|
||||
#endif
|
||||
@ -149,8 +136,8 @@ static void TargFreeGN(void *);
|
||||
void
|
||||
Targ_Init(void)
|
||||
{
|
||||
allTargets = Lst_Init();
|
||||
Hash_InitTable(&targets, 191);
|
||||
allTargets = Lst_New();
|
||||
HashTable_Init(&targets);
|
||||
}
|
||||
|
||||
void
|
||||
@ -161,18 +148,18 @@ Targ_End(void)
|
||||
Lst_Free(allTargets);
|
||||
if (allGNs != NULL)
|
||||
Lst_Destroy(allGNs, TargFreeGN);
|
||||
Hash_DeleteTable(&targets);
|
||||
HashTable_Done(&targets);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Targ_Stats(void)
|
||||
{
|
||||
Hash_DebugStats(&targets, "targets");
|
||||
HashTable_DebugStats(&targets, "targets");
|
||||
}
|
||||
|
||||
/* Return the list of all targets. */
|
||||
Lst
|
||||
GNodeList *
|
||||
Targ_List(void)
|
||||
{
|
||||
return allTargets;
|
||||
@ -182,7 +169,7 @@ Targ_List(void)
|
||||
* all gnodes.
|
||||
*
|
||||
* Input:
|
||||
* name the name of the node, such as "clean", "src.c"
|
||||
* name the name of the node, such as "clean", "src.c", ".END"
|
||||
*/
|
||||
GNode *
|
||||
Targ_NewGN(const char *name)
|
||||
@ -194,30 +181,30 @@ Targ_NewGN(const char *name)
|
||||
gn->uname = NULL;
|
||||
gn->path = NULL;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
|
||||
gn->unmade = 0;
|
||||
gn->unmade = 0;
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->cohort_num[0] = 0;
|
||||
gn->centurion = NULL;
|
||||
gn->made = UNMADE;
|
||||
gn->flags = 0;
|
||||
gn->checked = 0;
|
||||
gn->mtime = 0;
|
||||
gn->cmgn = NULL;
|
||||
gn->implicitParents = Lst_Init();
|
||||
gn->cohorts = Lst_Init();
|
||||
gn->parents = Lst_Init();
|
||||
gn->children = Lst_Init();
|
||||
gn->order_pred = Lst_Init();
|
||||
gn->order_succ = Lst_Init();
|
||||
Hash_InitTable(&gn->context, 0);
|
||||
gn->commands = Lst_Init();
|
||||
gn->suffix = NULL;
|
||||
gn->fname = NULL;
|
||||
gn->lineno = 0;
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->centurion = NULL;
|
||||
gn->made = UNMADE;
|
||||
gn->flags = 0;
|
||||
gn->checked_seqno = 0;
|
||||
gn->mtime = 0;
|
||||
gn->youngestChild = NULL;
|
||||
gn->implicitParents = Lst_New();
|
||||
gn->cohorts = Lst_New();
|
||||
gn->parents = Lst_New();
|
||||
gn->children = Lst_New();
|
||||
gn->order_pred = Lst_New();
|
||||
gn->order_succ = Lst_New();
|
||||
HashTable_Init(&gn->context);
|
||||
gn->commands = Lst_New();
|
||||
gn->suffix = NULL;
|
||||
gn->fname = NULL;
|
||||
gn->lineno = 0;
|
||||
|
||||
#ifdef CLEANUP
|
||||
if (allGNs == NULL)
|
||||
allGNs = Lst_Init();
|
||||
allGNs = Lst_New();
|
||||
Lst_Append(allGNs, gn);
|
||||
#endif
|
||||
|
||||
@ -228,7 +215,7 @@ Targ_NewGN(const char *name)
|
||||
static void
|
||||
TargFreeGN(void *gnp)
|
||||
{
|
||||
GNode *gn = (GNode *)gnp;
|
||||
GNode *gn = gnp;
|
||||
|
||||
free(gn->name);
|
||||
free(gn->uname);
|
||||
@ -240,53 +227,46 @@ TargFreeGN(void *gnp)
|
||||
Lst_Free(gn->children);
|
||||
Lst_Free(gn->order_succ);
|
||||
Lst_Free(gn->order_pred);
|
||||
Hash_DeleteTable(&gn->context);
|
||||
HashTable_Done(&gn->context);
|
||||
Lst_Free(gn->commands);
|
||||
|
||||
/* XXX: does gn->suffix need to be freed? It is reference-counted. */
|
||||
/* gn->fname points to name allocated when file was opened, don't free */
|
||||
|
||||
free(gn);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* Find a node in the list using the given name for matching.
|
||||
* If the node is created, it is added to the .ALLTARGETS list.
|
||||
*
|
||||
* Input:
|
||||
* name the name to find
|
||||
* flags flags governing events when target not found
|
||||
*
|
||||
* Results:
|
||||
* The node in the list if it was. If it wasn't, return NULL if
|
||||
* flags was TARG_NOCREATE or the newly created and initialized node
|
||||
* if it was TARG_CREATE
|
||||
*/
|
||||
/* Get the existing global node, or return NULL. */
|
||||
GNode *
|
||||
Targ_FindNode(const char *name, int flags)
|
||||
Targ_FindNode(const char *name)
|
||||
{
|
||||
GNode *gn; /* node in that element */
|
||||
Hash_Entry *he = NULL; /* New or used hash entry for node */
|
||||
Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */
|
||||
/* an entry for the node */
|
||||
return HashTable_FindValue(&targets, name);
|
||||
}
|
||||
|
||||
if (!(flags & (TARG_CREATE | TARG_NOHASH))) {
|
||||
he = Hash_FindEntry(&targets, name);
|
||||
if (he == NULL)
|
||||
return NULL;
|
||||
return (GNode *)Hash_GetValue(he);
|
||||
/* Get the existing global node, or create it. */
|
||||
GNode *
|
||||
Targ_GetNode(const char *name)
|
||||
{
|
||||
Boolean isNew;
|
||||
HashEntry *he = HashTable_CreateEntry(&targets, name, &isNew);
|
||||
if (!isNew)
|
||||
return HashEntry_Get(he);
|
||||
|
||||
{
|
||||
GNode *gn = Targ_NewInternalNode(name);
|
||||
HashEntry_Set(he, gn);
|
||||
return gn;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & TARG_NOHASH)) {
|
||||
he = Hash_CreateEntry(&targets, name, &isNew);
|
||||
if (!isNew)
|
||||
return (GNode *)Hash_GetValue(he);
|
||||
}
|
||||
|
||||
gn = Targ_NewGN(name);
|
||||
if (!(flags & TARG_NOHASH))
|
||||
Hash_SetValue(he, gn);
|
||||
/* Create a node, register it in .ALLTARGETS but don't store it in the
|
||||
* table of global nodes. This means it cannot be found by name.
|
||||
*
|
||||
* This is used for internal nodes, such as cohorts or .WAIT nodes. */
|
||||
GNode *
|
||||
Targ_NewInternalNode(const char *name)
|
||||
{
|
||||
GNode *gn = Targ_NewGN(name);
|
||||
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
|
||||
Lst_Append(allTargets, gn);
|
||||
if (doing_depend)
|
||||
@ -294,45 +274,30 @@ Targ_FindNode(const char *name, int flags)
|
||||
return gn;
|
||||
}
|
||||
|
||||
/* Make a complete list of GNodes from the given list of names.
|
||||
* If flags is TARG_CREATE, nodes will be created for all names in
|
||||
* names which do not yet have graph nodes. If flags is TARG_NOCREATE,
|
||||
* an error message will be printed for each name which can't be found.
|
||||
*
|
||||
* Input:
|
||||
* name list of names to find
|
||||
* flags flags used if no node is found for a given name
|
||||
*
|
||||
* Results:
|
||||
* A complete list of graph nodes corresponding to all instances of all
|
||||
* the names in names.
|
||||
*/
|
||||
Lst
|
||||
Targ_FindList(Lst names, int flags)
|
||||
/* Return the .END node, which contains the commands to be executed when
|
||||
* everything else is done. */
|
||||
GNode *Targ_GetEndNode(void)
|
||||
{
|
||||
Lst nodes; /* result list */
|
||||
LstNode ln; /* name list element */
|
||||
GNode *gn; /* node in tLn */
|
||||
char *name;
|
||||
|
||||
nodes = Lst_Init();
|
||||
|
||||
Lst_Open(names);
|
||||
while ((ln = Lst_Next(names)) != NULL) {
|
||||
name = LstNode_Datum(ln);
|
||||
gn = Targ_FindNode(name, flags);
|
||||
if (gn != NULL) {
|
||||
/*
|
||||
* Note: Lst_Append must come before the Lst_Concat so the nodes
|
||||
* are added to the list in the order in which they were
|
||||
* encountered in the makefile.
|
||||
*/
|
||||
Lst_Append(nodes, gn);
|
||||
} else if (flags == TARG_NOCREATE) {
|
||||
Error("\"%s\" -- target unknown.", name);
|
||||
}
|
||||
/* Save the node locally to avoid having to search for it all the time. */
|
||||
static GNode *endNode = NULL;
|
||||
if (endNode == NULL) {
|
||||
endNode = Targ_GetNode(".END");
|
||||
endNode->type = OP_SPECIAL;
|
||||
}
|
||||
return endNode;
|
||||
}
|
||||
|
||||
/* Return the named nodes, creating them as necessary. */
|
||||
GNodeList *
|
||||
Targ_FindList(StringList *names)
|
||||
{
|
||||
StringListNode *ln;
|
||||
GNodeList *nodes = Lst_New();
|
||||
for (ln = names->first; ln != NULL; ln = ln->next) {
|
||||
const char *name = ln->datum;
|
||||
GNode *gn = Targ_GetNode(name);
|
||||
Lst_Append(nodes, gn);
|
||||
}
|
||||
Lst_Close(names);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@ -340,14 +305,14 @@ Targ_FindList(Lst names, int flags)
|
||||
Boolean
|
||||
Targ_Ignore(GNode *gn)
|
||||
{
|
||||
return ignoreErrors || gn->type & OP_IGNORE;
|
||||
return opts.ignoreErrors || gn->type & OP_IGNORE;
|
||||
}
|
||||
|
||||
/* Return true if be silent when creating gn. */
|
||||
Boolean
|
||||
Targ_Silent(GNode *gn)
|
||||
{
|
||||
return beSilent || gn->type & OP_SILENT;
|
||||
return opts.beSilent || gn->type & OP_SILENT;
|
||||
}
|
||||
|
||||
/* See if the given target is precious. */
|
||||
@ -369,22 +334,35 @@ Targ_SetMain(GNode *gn)
|
||||
mainTarg = gn;
|
||||
}
|
||||
|
||||
static int
|
||||
TargPrintName(void *gnp, void *pflags MAKE_ATTR_UNUSED)
|
||||
static void
|
||||
PrintNodeNames(GNodeList *gnodes)
|
||||
{
|
||||
GNode *gn = (GNode *)gnp;
|
||||
GNodeListNode *node;
|
||||
|
||||
fprintf(debug_file, "%s%s ", gn->name, gn->cohort_num);
|
||||
|
||||
return 0;
|
||||
for (node = gnodes->first; node != NULL; node = node->next) {
|
||||
GNode *gn = node->datum;
|
||||
debug_printf(" %s%s", gn->name, gn->cohort_num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED)
|
||||
static void
|
||||
PrintNodeNamesLine(const char *label, GNodeList *gnodes)
|
||||
{
|
||||
fprintf(debug_file, "\t%s\n", (char *)cmd);
|
||||
return 0;
|
||||
if (Lst_IsEmpty(gnodes))
|
||||
return;
|
||||
debug_printf("# %s:", label);
|
||||
PrintNodeNames(gnodes);
|
||||
debug_printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
Targ_PrintCmds(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
debug_printf("\t%s\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Format a modification time in some reasonable way and return it.
|
||||
@ -392,8 +370,8 @@ Targ_PrintCmd(void *cmd, void *dummy MAKE_ATTR_UNUSED)
|
||||
char *
|
||||
Targ_FmtTime(time_t tm)
|
||||
{
|
||||
struct tm *parts;
|
||||
static char buf[128];
|
||||
struct tm *parts;
|
||||
static char buf[128];
|
||||
|
||||
parts = localtime(&tm);
|
||||
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
|
||||
@ -406,8 +384,8 @@ Targ_PrintType(int type)
|
||||
{
|
||||
int tbit;
|
||||
|
||||
#define PRINTBIT(attr) case CONCAT(OP_,attr): fprintf(debug_file, "." #attr " "); break
|
||||
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))fprintf(debug_file, "." #attr " "); break
|
||||
#define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break
|
||||
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break
|
||||
|
||||
type &= ~OP_OPMASK;
|
||||
|
||||
@ -416,22 +394,22 @@ Targ_PrintType(int type)
|
||||
type &= ~tbit;
|
||||
|
||||
switch(tbit) {
|
||||
PRINTBIT(OPTIONAL);
|
||||
PRINTBIT(USE);
|
||||
PRINTBIT(EXEC);
|
||||
PRINTBIT(IGNORE);
|
||||
PRINTBIT(PRECIOUS);
|
||||
PRINTBIT(SILENT);
|
||||
PRINTBIT(MAKE);
|
||||
PRINTBIT(JOIN);
|
||||
PRINTBIT(INVISIBLE);
|
||||
PRINTBIT(NOTMAIN);
|
||||
PRINTDBIT(LIB);
|
||||
PRINTBIT(OPTIONAL);
|
||||
PRINTBIT(USE);
|
||||
PRINTBIT(EXEC);
|
||||
PRINTBIT(IGNORE);
|
||||
PRINTBIT(PRECIOUS);
|
||||
PRINTBIT(SILENT);
|
||||
PRINTBIT(MAKE);
|
||||
PRINTBIT(JOIN);
|
||||
PRINTBIT(INVISIBLE);
|
||||
PRINTBIT(NOTMAIN);
|
||||
PRINTDBIT(LIB);
|
||||
/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
|
||||
case OP_MEMBER: if (DEBUG(TARG))fprintf(debug_file, ".MEMBER "); break;
|
||||
PRINTDBIT(ARCHV);
|
||||
PRINTDBIT(MADE);
|
||||
PRINTDBIT(PHONY);
|
||||
case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break;
|
||||
PRINTDBIT(ARCHV);
|
||||
PRINTDBIT(MADE);
|
||||
PRINTDBIT(PHONY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,102 +430,96 @@ made_name(GNodeMade made)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print the contents of a node. */
|
||||
int
|
||||
Targ_PrintNode(void *gnp, void *passp)
|
||||
static const char *
|
||||
GNode_OpName(const GNode *gn)
|
||||
{
|
||||
GNode *gn = (GNode *)gnp;
|
||||
int pass = passp ? *(int *)passp : 0;
|
||||
switch (gn->type & OP_OPMASK) {
|
||||
case OP_DEPENDS:
|
||||
return ":";
|
||||
case OP_FORCE:
|
||||
return "!";
|
||||
case OP_DOUBLEDEP:
|
||||
return "::";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
fprintf(debug_file, "# %s%s", gn->name, gn->cohort_num);
|
||||
GNode_FprintDetails(debug_file, ", ", gn, "\n");
|
||||
/* Print the contents of a node. */
|
||||
void
|
||||
Targ_PrintNode(GNode *gn, int pass)
|
||||
{
|
||||
debug_printf("# %s%s", gn->name, gn->cohort_num);
|
||||
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
|
||||
if (gn->flags == 0)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (!OP_NOP(gn->type)) {
|
||||
fprintf(debug_file, "#\n");
|
||||
if (GNode_IsTarget(gn)) {
|
||||
debug_printf("#\n");
|
||||
if (gn == mainTarg) {
|
||||
fprintf(debug_file, "# *** MAIN TARGET ***\n");
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
}
|
||||
if (pass >= 2) {
|
||||
if (gn->unmade) {
|
||||
fprintf(debug_file, "# %d unmade children\n", gn->unmade);
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
} else {
|
||||
fprintf(debug_file, "# No unmade children\n");
|
||||
debug_printf("# No unmade children\n");
|
||||
}
|
||||
if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) {
|
||||
if (gn->mtime != 0) {
|
||||
fprintf(debug_file, "# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
made_name(gn->made));
|
||||
debug_printf("# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
made_name(gn->made));
|
||||
} else if (gn->made != UNMADE) {
|
||||
fprintf(debug_file, "# non-existent (maybe): %s\n",
|
||||
made_name(gn->made));
|
||||
debug_printf("# non-existent (maybe): %s\n",
|
||||
made_name(gn->made));
|
||||
} else {
|
||||
fprintf(debug_file, "# unmade\n");
|
||||
debug_printf("# unmade\n");
|
||||
}
|
||||
}
|
||||
if (!Lst_IsEmpty(gn->implicitParents)) {
|
||||
fprintf(debug_file, "# implicit parents: ");
|
||||
Lst_ForEach(gn->implicitParents, TargPrintName, NULL);
|
||||
fprintf(debug_file, "\n");
|
||||
}
|
||||
PrintNodeNamesLine("implicit parents", gn->implicitParents);
|
||||
} else {
|
||||
if (gn->unmade)
|
||||
fprintf(debug_file, "# %d unmade children\n", gn->unmade);
|
||||
}
|
||||
if (!Lst_IsEmpty(gn->parents)) {
|
||||
fprintf(debug_file, "# parents: ");
|
||||
Lst_ForEach(gn->parents, TargPrintName, NULL);
|
||||
fprintf(debug_file, "\n");
|
||||
}
|
||||
if (!Lst_IsEmpty(gn->order_pred)) {
|
||||
fprintf(debug_file, "# order_pred: ");
|
||||
Lst_ForEach(gn->order_pred, TargPrintName, NULL);
|
||||
fprintf(debug_file, "\n");
|
||||
}
|
||||
if (!Lst_IsEmpty(gn->order_succ)) {
|
||||
fprintf(debug_file, "# order_succ: ");
|
||||
Lst_ForEach(gn->order_succ, TargPrintName, NULL);
|
||||
fprintf(debug_file, "\n");
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
}
|
||||
PrintNodeNamesLine("parents", gn->parents);
|
||||
PrintNodeNamesLine("order_pred", gn->order_pred);
|
||||
PrintNodeNamesLine("order_succ", gn->order_succ);
|
||||
|
||||
fprintf(debug_file, "%-16s", gn->name);
|
||||
switch (gn->type & OP_OPMASK) {
|
||||
case OP_DEPENDS:
|
||||
fprintf(debug_file, ": "); break;
|
||||
case OP_FORCE:
|
||||
fprintf(debug_file, "! "); break;
|
||||
case OP_DOUBLEDEP:
|
||||
fprintf(debug_file, ":: "); break;
|
||||
}
|
||||
debug_printf("%-16s%s", gn->name, GNode_OpName(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
Lst_ForEach(gn->children, TargPrintName, NULL);
|
||||
fprintf(debug_file, "\n");
|
||||
Lst_ForEach(gn->commands, Targ_PrintCmd, NULL);
|
||||
fprintf(debug_file, "\n\n");
|
||||
PrintNodeNames(gn->children);
|
||||
debug_printf("\n");
|
||||
Targ_PrintCmds(gn);
|
||||
debug_printf("\n\n");
|
||||
if (gn->type & OP_DOUBLEDEP) {
|
||||
Lst_ForEach(gn->cohorts, Targ_PrintNode, &pass);
|
||||
Targ_PrintNodes(gn->cohorts, pass);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print only those targets that are just a source.
|
||||
* The name of each file is printed, preceded by #\t. */
|
||||
static int
|
||||
TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED)
|
||||
void
|
||||
Targ_PrintNodes(GNodeList *gnodes, int pass)
|
||||
{
|
||||
GNode *gn = (GNode *)gnp;
|
||||
if (!OP_NOP(gn->type))
|
||||
return 0;
|
||||
GNodeListNode *ln;
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next)
|
||||
Targ_PrintNode(ln->datum, pass);
|
||||
}
|
||||
|
||||
fprintf(debug_file, "#\t%s [%s] ",
|
||||
gn->name, gn->path ? gn->path : gn->name);
|
||||
Targ_PrintType(gn->type);
|
||||
fprintf(debug_file, "\n");
|
||||
/* Print only those targets that are just a source. */
|
||||
static void
|
||||
PrintOnlySources(void)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
return 0;
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
if (GNode_IsTarget(gn))
|
||||
continue;
|
||||
|
||||
debug_printf("#\t%s [%s]", gn->name, GNode_Path(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
debug_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Input:
|
||||
@ -558,18 +530,18 @@ TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED)
|
||||
void
|
||||
Targ_PrintGraph(int pass)
|
||||
{
|
||||
fprintf(debug_file, "#*** Input graph:\n");
|
||||
Lst_ForEach(allTargets, Targ_PrintNode, &pass);
|
||||
fprintf(debug_file, "\n\n");
|
||||
fprintf(debug_file, "#\n# Files that are only sources:\n");
|
||||
Lst_ForEach(allTargets, TargPrintOnlySrc, NULL);
|
||||
fprintf(debug_file, "#*** Global Variables:\n");
|
||||
debug_printf("#*** Input graph:\n");
|
||||
Targ_PrintNodes(allTargets, pass);
|
||||
debug_printf("\n\n");
|
||||
debug_printf("#\n# Files that are only sources:\n");
|
||||
PrintOnlySources();
|
||||
debug_printf("#*** Global Variables:\n");
|
||||
Var_Dump(VAR_GLOBAL);
|
||||
fprintf(debug_file, "#*** Command-line Variables:\n");
|
||||
Var_Dump(VAR_CMD);
|
||||
fprintf(debug_file, "\n");
|
||||
debug_printf("#*** Command-line Variables:\n");
|
||||
Var_Dump(VAR_CMDLINE);
|
||||
debug_printf("\n");
|
||||
Dir_PrintDirectories();
|
||||
fprintf(debug_file, "\n");
|
||||
debug_printf("\n");
|
||||
Suff_PrintAll();
|
||||
}
|
||||
|
||||
@ -581,18 +553,19 @@ Targ_PrintGraph(int pass)
|
||||
void
|
||||
Targ_Propagate(void)
|
||||
{
|
||||
LstNode pn, cn;
|
||||
GNodeListNode *ln, *cln;
|
||||
|
||||
for (pn = Lst_First(allTargets); pn != NULL; pn = LstNode_Next(pn)) {
|
||||
GNode *pgn = LstNode_Datum(pn);
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
GNodeType type = gn->type;
|
||||
|
||||
if (!(pgn->type & OP_DOUBLEDEP))
|
||||
if (!(type & OP_DOUBLEDEP))
|
||||
continue;
|
||||
|
||||
for (cn = Lst_First(pgn->cohorts); cn != NULL; cn = LstNode_Next(cn)) {
|
||||
GNode *cgn = LstNode_Datum(cn);
|
||||
for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) {
|
||||
GNode *cohort = cln->datum;
|
||||
|
||||
cgn->type |= pgn->type & ~OP_OPMASK;
|
||||
cohort->type |= type & ~OP_OPMASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $ */
|
||||
/* $NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
@ -29,16 +29,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MAKE_NATIVE
|
||||
static char rcsid[] = "$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $";
|
||||
#else
|
||||
#include <sys/cdefs.h>
|
||||
#ifndef lint
|
||||
__RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $");
|
||||
#endif /* not lint */
|
||||
#endif
|
||||
|
||||
/*-
|
||||
* trace.c --
|
||||
* handle logging of trace events generated by various parts of make.
|
||||
@ -54,13 +44,12 @@ __RCSID("$NetBSD: trace.c,v 1.15 2020/08/03 20:26:09 rillig Exp $");
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "make.h"
|
||||
#include "job.h"
|
||||
#include "trace.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $");
|
||||
|
||||
static FILE *trfile;
|
||||
static pid_t trpid;
|
||||
const char *trwd;
|
||||
@ -78,8 +67,10 @@ void
|
||||
Trace_Init(const char *pathname)
|
||||
{
|
||||
if (pathname != NULL) {
|
||||
char *dontFreeIt;
|
||||
void *dontFreeIt;
|
||||
trpid = getpid();
|
||||
/* XXX: This variable may get overwritten later, which
|
||||
* would make trwd point to undefined behavior. */
|
||||
trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt);
|
||||
|
||||
trfile = fopen(pathname, "a");
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: trace.h,v 1.3 2008/04/28 20:24:14 martin Exp $ */
|
||||
/* $NetBSD: trace.h,v 1.4 2020/10/18 17:19:54 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
@ -34,7 +34,7 @@
|
||||
* Definitions pertaining to the tracing of jobs in parallel mode.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
typedef enum TrEvent {
|
||||
MAKESTART,
|
||||
MAKEEND,
|
||||
MAKEERROR,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $Id: Makefile,v 1.92 2020/09/02 18:39:29 sjg Exp $
|
||||
# $Id: Makefile,v 1.107 2020/11/02 00:40:25 sjg Exp $
|
||||
#
|
||||
# $NetBSD: Makefile,v 1.130 2020/09/02 05:33:57 rillig Exp $
|
||||
# $NetBSD: Makefile,v 1.181 2020/11/01 19:02:22 rillig Exp $
|
||||
#
|
||||
# Unit tests for make(1)
|
||||
#
|
||||
@ -34,7 +34,7 @@
|
||||
# Keep the list sorted.
|
||||
# Any test that is commented out must be ignored in
|
||||
# src/tests/usr.bin/make/t_make.sh as well.
|
||||
TESTS+= # archive # broken on FreeBSD, enabled in t_make.sh
|
||||
#TESTS+= archive
|
||||
TESTS+= archive-suffix
|
||||
TESTS+= cmd-interrupt
|
||||
TESTS+= cmdline
|
||||
@ -47,6 +47,7 @@ TESTS+= cond-cmp-numeric-le
|
||||
TESTS+= cond-cmp-numeric-lt
|
||||
TESTS+= cond-cmp-numeric-ne
|
||||
TESTS+= cond-cmp-string
|
||||
TESTS+= cond-cmp-unary
|
||||
TESTS+= cond-func
|
||||
TESTS+= cond-func-commands
|
||||
TESTS+= cond-func-defined
|
||||
@ -65,17 +66,22 @@ TESTS+= cond-token-number
|
||||
TESTS+= cond-token-plain
|
||||
TESTS+= cond-token-string
|
||||
TESTS+= cond-token-var
|
||||
TESTS+= cond-undef-lint
|
||||
TESTS+= cond1
|
||||
TESTS+= cond2
|
||||
TESTS+= counter
|
||||
TESTS+= counter-append
|
||||
TESTS+= dep
|
||||
TESTS+= dep-colon
|
||||
TESTS+= dep-colon-bug-cross-file
|
||||
TESTS+= dep-double-colon
|
||||
TESTS+= dep-double-colon-indep
|
||||
TESTS+= dep-exclam
|
||||
TESTS+= dep-none
|
||||
TESTS+= dep-percent
|
||||
TESTS+= dep-var
|
||||
TESTS+= dep-wildcards
|
||||
TESTS+= depsrc
|
||||
TESTS+= depsrc-end
|
||||
TESTS+= depsrc-exec
|
||||
TESTS+= depsrc-ignore
|
||||
TESTS+= depsrc-made
|
||||
@ -99,6 +105,7 @@ TESTS+= deptgt-begin
|
||||
TESTS+= deptgt-default
|
||||
TESTS+= deptgt-delete_on_error
|
||||
TESTS+= deptgt-end
|
||||
TESTS+= deptgt-end-jobs
|
||||
TESTS+= deptgt-error
|
||||
TESTS+= deptgt-ignore
|
||||
TESTS+= deptgt-interrupt
|
||||
@ -120,6 +127,7 @@ TESTS+= deptgt-suffixes
|
||||
TESTS+= dir
|
||||
TESTS+= dir-expand-path
|
||||
TESTS+= directive
|
||||
TESTS+= directive-dinclude
|
||||
TESTS+= directive-elif
|
||||
TESTS+= directive-elifdef
|
||||
TESTS+= directive-elifmake
|
||||
@ -130,15 +138,20 @@ TESTS+= directive-endif
|
||||
TESTS+= directive-error
|
||||
TESTS+= directive-export
|
||||
TESTS+= directive-export-env
|
||||
TESTS+= directive-export-gmake
|
||||
TESTS+= directive-export-literal
|
||||
TESTS+= directive-for
|
||||
TESTS+= directive-for-generating-endif
|
||||
TESTS+= directive-hyphen-include
|
||||
TESTS+= directive-if
|
||||
TESTS+= directive-ifdef
|
||||
TESTS+= directive-ifmake
|
||||
TESTS+= directive-ifndef
|
||||
TESTS+= directive-ifnmake
|
||||
TESTS+= directive-include
|
||||
TESTS+= directive-include-fatal
|
||||
TESTS+= directive-info
|
||||
TESTS+= directive-sinclude
|
||||
TESTS+= directive-undef
|
||||
TESTS+= directive-unexport
|
||||
TESTS+= directive-unexport-env
|
||||
@ -156,12 +169,12 @@ TESTS+= export-env
|
||||
TESTS+= export-variants
|
||||
TESTS+= forloop
|
||||
TESTS+= forsubst
|
||||
TESTS+= hash
|
||||
TESTS+= hanoi-include
|
||||
TESTS+= impsrc
|
||||
TESTS+= include-main
|
||||
#TESTS+= job-output-long-lines
|
||||
TESTS+= lint
|
||||
TESTS+= make-exported
|
||||
TESTS+= misc
|
||||
TESTS+= moderrs
|
||||
TESTS+= modmatch
|
||||
TESTS+= modmisc
|
||||
@ -171,7 +184,30 @@ TESTS+= opt
|
||||
TESTS+= opt-backwards
|
||||
TESTS+= opt-chdir
|
||||
TESTS+= opt-debug
|
||||
TESTS+= opt-debug-g1
|
||||
TESTS+= opt-debug-all
|
||||
TESTS+= opt-debug-archive
|
||||
TESTS+= opt-debug-curdir
|
||||
TESTS+= opt-debug-cond
|
||||
TESTS+= opt-debug-dir
|
||||
TESTS+= opt-debug-errors
|
||||
TESTS+= opt-debug-file
|
||||
TESTS+= opt-debug-for
|
||||
TESTS+= opt-debug-graph1
|
||||
TESTS+= opt-debug-graph2
|
||||
TESTS+= opt-debug-graph3
|
||||
TESTS+= opt-debug-hash
|
||||
#TESTS+= opt-debug-jobs
|
||||
TESTS+= opt-debug-lint
|
||||
TESTS+= opt-debug-loud
|
||||
TESTS+= opt-debug-meta
|
||||
TESTS+= opt-debug-making
|
||||
TESTS+= opt-debug-no-rm
|
||||
TESTS+= opt-debug-parse
|
||||
TESTS+= opt-debug-suff
|
||||
TESTS+= opt-debug-targets
|
||||
TESTS+= opt-debug-varraw
|
||||
TESTS+= opt-debug-var
|
||||
TESTS+= opt-debug-x-trace
|
||||
TESTS+= opt-define
|
||||
TESTS+= opt-env
|
||||
TESTS+= opt-file
|
||||
@ -194,6 +230,7 @@ TESTS+= opt-warnings-as-errors
|
||||
TESTS+= opt-where-am-i
|
||||
TESTS+= opt-x-reduce-exported
|
||||
TESTS+= order
|
||||
TESTS+= parse-var
|
||||
TESTS+= phony-end
|
||||
TESTS+= posix
|
||||
TESTS+= # posix1 # broken by reverting POSIX changes
|
||||
@ -209,9 +246,20 @@ TESTS+= sh-leading-plus
|
||||
TESTS+= sh-meta-chars
|
||||
TESTS+= sh-multi-line
|
||||
TESTS+= sh-single-line
|
||||
TESTS+= # suffixes # runs into an endless loop (try -dA)
|
||||
TESTS+= shell-csh
|
||||
TESTS+= shell-custom
|
||||
TESTS+= shell-ksh
|
||||
TESTS+= shell-sh
|
||||
TESTS+= suff-add-later
|
||||
TESTS+= suff-clear-regular
|
||||
TESTS+= suff-clear-single
|
||||
TESTS+= suff-lookup
|
||||
TESTS+= suff-main
|
||||
TESTS+= suff-rebuild
|
||||
TESTS+= suff-transform-endless
|
||||
TESTS+= suff-transform-expand
|
||||
TESTS+= suff-transform-select
|
||||
TESTS+= sunshcmd
|
||||
TESTS+= sysv
|
||||
TESTS+= ternary
|
||||
TESTS+= unexport
|
||||
TESTS+= unexport-env
|
||||
@ -228,6 +276,8 @@ TESTS+= var-op-assign
|
||||
TESTS+= var-op-default
|
||||
TESTS+= var-op-expand
|
||||
TESTS+= var-op-shell
|
||||
TESTS+= var-op-sunsh
|
||||
TESTS+= var-recursive
|
||||
TESTS+= varcmd
|
||||
TESTS+= vardebug
|
||||
TESTS+= varfind
|
||||
@ -310,10 +360,14 @@ TESTS+= varname-dot-targets
|
||||
TESTS+= varname-empty
|
||||
TESTS+= varname-make
|
||||
TESTS+= varname-make_print_var_on_error
|
||||
TESTS+= varname-make_print_var_on_error-jobs
|
||||
TESTS+= varname-makefile
|
||||
TESTS+= varname-makeflags
|
||||
TESTS+= varname-pwd
|
||||
TESTS+= varname-vpath
|
||||
TESTS+= varparse-dynamic
|
||||
TESTS+= varparse-mod
|
||||
TESTS+= varparse-undef-partial
|
||||
TESTS+= varquote
|
||||
TESTS+= varshell
|
||||
|
||||
@ -323,18 +377,16 @@ ENV.envfirst= FROM_ENV=value-from-env
|
||||
ENV.varmisc= FROM_ENV=env
|
||||
ENV.varmisc+= FROM_ENV_BEFORE=env
|
||||
ENV.varmisc+= FROM_ENV_AFTER=env
|
||||
ENV.varmod-localtime+= TZ=Europe/Berlin
|
||||
|
||||
# Override make flags for some of the tests; default is -k.
|
||||
# If possible, write ".MAKEFLAGS: -dv" in the test .mk file instead of
|
||||
# settings FLAGS.test=-dv here, since that is closer to the test code.
|
||||
FLAGS.archive= -dA
|
||||
FLAGS.counter= -dv
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none
|
||||
FLAGS.envfirst= -e
|
||||
FLAGS.export= # none
|
||||
FLAGS.lint= -dL -k
|
||||
FLAGS.opt-debug-g1= -dg1
|
||||
FLAGS.opt-ignore= -i
|
||||
FLAGS.opt-keep-going= -k
|
||||
FLAGS.opt-no-action= -n
|
||||
@ -345,32 +397,52 @@ FLAGS.opt-warnings-as-errors= -W
|
||||
FLAGS.order= -j1
|
||||
FLAGS.recursive= -dL
|
||||
FLAGS.sh-leading-plus= -n
|
||||
FLAGS.vardebug= -k -dv FROM_CMDLINE=
|
||||
FLAGS.varmod-match-escape= -dv
|
||||
FLAGS.varname-dot-shell= -dpv
|
||||
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmline-plain'
|
||||
|
||||
# Some tests need extra post-processing.
|
||||
SED_CMDS.opt-debug-g1= -e 's,${.CURDIR},CURDIR,'
|
||||
SED_CMDS.opt-debug-g1+= -e '/Global Variables:/,/Suffixes:/d'
|
||||
# Some tests need extra postprocessing.
|
||||
SED_CMDS.export= \
|
||||
-e '/^[^=_A-Za-z0-9]*=/d'
|
||||
# these all share the same requirement
|
||||
.for t in export-all export-env
|
||||
SED_CMDS.$t= ${SED_CMDS.export}
|
||||
.endfor
|
||||
SED_CMDS.job-output-long-lines= \
|
||||
${:D Job separators on their own line are ok. } \
|
||||
-e '/^--- job-[ab] ---$$/d' \
|
||||
${:D Plain output lines are ok as well. } \
|
||||
${:D They may come in multiples of 1024 or as 10000. } \
|
||||
-e '/^aa*$$/d' \
|
||||
-e '/^bb*$$/d' \
|
||||
${:D The following lines should rather not occur since the job } \
|
||||
${:D marker should always be at the beginning of the line. } \
|
||||
-e '/^aa*--- job-b ---$$/d' \
|
||||
-e '/^bb*--- job-a ---$$/d'
|
||||
SED_CMDS.opt-debug-graph1= \
|
||||
-e 's,${.CURDIR},CURDIR,'
|
||||
SED_CMDS.opt-debug-graph1+= \
|
||||
-e '/Global Variables:/,/Suffixes:/d'
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
|
||||
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
|
||||
# The "-q" may be there or not, see jobs.c, variable shells.
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: sh\) -q,\1,'
|
||||
SED_CMDS.varmod-subst-regex+= \
|
||||
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
|
||||
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
|
||||
SED_CMDS.varshell+= -e 's,^${.SHELL:T}: ,,'
|
||||
SED_CMDS.varshell+= -e '/command/s,No such.*,not found,'
|
||||
SED_CMDS.varname-dot-shell= -e 's, = /.*, = (details omitted),'
|
||||
SED_CMDS.varname-dot-shell+= -e 's,"/[^"]*","(details omitted)",'
|
||||
SED_CMDS.varname-dot-shell+= -e 's,\[/[^]]*\],[(details omitted)],'
|
||||
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
|
||||
SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
|
||||
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
|
||||
|
||||
# Some tests need an additional round of postprocessing.
|
||||
POSTPROC.counter= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
|
||||
POSTPROC.deptgt-suffixes= \
|
||||
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
|
||||
POSTPROC.vardebug= ${TOOL_SED} -n -e '/:RELEVANT = yes/,/:RELEVANT = no/p'
|
||||
POSTPROC.varmod-match-escape= ${TOOL_SED} -n -e '/^Pattern/p'
|
||||
POSTPROC.varname-dot-shell= \
|
||||
awk '/\.SHELL/ || /^ParseReadLine/'
|
||||
POSTPROC.varname= ${TOOL_SED} -n -e '/^MAGIC/p' -e '/^ORDER_/p'
|
||||
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
|
||||
|
||||
# Some tests reuse other tests, which makes them unnecessarily fragile.
|
||||
@ -388,7 +460,11 @@ unexport-env.rawout: export.mk
|
||||
UNIT_TESTS:= ${.PARSEDIR}
|
||||
.PATH: ${UNIT_TESTS}
|
||||
|
||||
.if ${USE_ABSOLUTE_TESTNAMES:Uno} == yes
|
||||
OUTFILES= ${TESTS:@test@${.CURDIR:tA}/${test}.out@}
|
||||
.else
|
||||
OUTFILES= ${TESTS:=.out}
|
||||
.endif
|
||||
|
||||
all: ${OUTFILES}
|
||||
|
||||
@ -419,22 +495,26 @@ LANG= C
|
||||
_MKMSG_TEST= :
|
||||
.endif
|
||||
|
||||
# the tests are actually done with sub-makes.
|
||||
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
|
||||
|
||||
# Each test is run in a sub-make, to keep the tests for interfering with
|
||||
# each other, and because they use different environment variables and
|
||||
# command line options.
|
||||
.SUFFIXES: .mk .rawout .out
|
||||
.mk.rawout:
|
||||
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
|
||||
@set -eu; \
|
||||
cd ${.OBJDIR}; \
|
||||
env -i PATH="$$PATH" ${ENV.${.TARGET:R}} \
|
||||
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
|
||||
${TEST_MAKE} \
|
||||
-r -C ${.CURDIR} -f ${.IMPSRC} \
|
||||
${FLAGS.${.TARGET:R}:U-k} \
|
||||
${FLAGS.${.PREFIX:T}:U-k} \
|
||||
> ${.TARGET}.tmp 2>&1 \
|
||||
&& status=$$? || status=$$?; \
|
||||
echo $$status > ${.TARGET:R}.status
|
||||
@mv ${.TARGET}.tmp ${.TARGET}
|
||||
|
||||
# Post-process the test output so that the results can be compared.
|
||||
# Postprocess the test output so that the results can be compared.
|
||||
#
|
||||
# always pretend .MAKE was called 'make'
|
||||
_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
|
||||
@ -446,9 +526,9 @@ _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
|
||||
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
|
||||
|
||||
.rawout.out:
|
||||
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.TARGET:R}} \
|
||||
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
|
||||
< ${.IMPSRC} > ${.TARGET}.tmp1
|
||||
@${POSTPROC.${.TARGET:R}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
|
||||
@${POSTPROC.${.PREFIX:T}:Ucat} < ${.TARGET}.tmp1 > ${.TARGET}.tmp2
|
||||
@rm ${.TARGET}.tmp1
|
||||
@echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
|
||||
@mv ${.TARGET}.tmp2 ${.TARGET}
|
||||
@ -474,7 +554,12 @@ accept:
|
||||
done
|
||||
|
||||
.if exists(${TEST_MAKE})
|
||||
${TESTS:=.rawout}: ${TEST_MAKE} ${.PARSEDIR}/Makefile
|
||||
${TESTS:=.rawout}: ${TEST_MAKE}
|
||||
# in meta mode, we *know* if a target script is impacted
|
||||
# by a makefile change.
|
||||
.if ${.MAKE.MODE:Unormal:Mmeta} == ""
|
||||
${TESTS:=.rawout}: ${.PARSEDIR}/Makefile
|
||||
.endif
|
||||
.endif
|
||||
|
||||
.-include <obj.mk>
|
||||
|
@ -1,13 +1,28 @@
|
||||
Making remove-archive
|
||||
rm -f libprog.a
|
||||
|
||||
Making libprog.a out-of-date archive.mk modmisc.mk varmisc.mk
|
||||
ar cru libprog.a archive.mk modmisc.mk varmisc.mk
|
||||
ranlib libprog.a
|
||||
|
||||
Making create-archive out-of-date libprog.a
|
||||
|
||||
Making list-archive out-of-date libprog.a
|
||||
ar t libprog.a
|
||||
archive.mk
|
||||
modmisc.mk
|
||||
varmisc.mk
|
||||
|
||||
Making list-archive-wildcard out-of-date archive-suffix.mk archive.mk ternary.mk
|
||||
list-archive-wildcard: archive-suffix.mk
|
||||
list-archive-wildcard: archive.mk
|
||||
list-archive-wildcard: ternary.mk
|
||||
|
||||
Making depend-on-existing-member out-of-date archive.mk
|
||||
depend-on-existing-member
|
||||
|
||||
`depend-on-nonexistent-member' is up to date.
|
||||
Making remove-archive
|
||||
rm -f libprog.a
|
||||
|
||||
exit status 0
|
||||
|
@ -1,18 +1,25 @@
|
||||
# $NetBSD: archive.mk,v 1.5 2020/08/23 17:51:24 rillig Exp $
|
||||
# $NetBSD: archive.mk,v 1.10 2020/10/09 06:44:42 rillig Exp $
|
||||
#
|
||||
# Very basic demonstration of handling archives, based on the description
|
||||
# in PSD.doc/tutorial.ms.
|
||||
#
|
||||
# This test aims at covering the code, not at being an introduction to
|
||||
# archive handling. That's why it is more complicated and detailed than
|
||||
# strictly necessary.
|
||||
|
||||
ARCHIVE= libprog.${EXT.a}
|
||||
FILES= archive.${EXT.mk} modmisc.${EXT.mk} varmisc.mk
|
||||
|
||||
EXT.a= a
|
||||
EXT.mk= mk
|
||||
ARCHIVE= libprog.a
|
||||
FILES= archive.mk modmisc.mk varmisc.mk
|
||||
|
||||
MAKE_CMD= ${.MAKE} -f ${MAKEFILE}
|
||||
RUN?= @set -eu;
|
||||
|
||||
all:
|
||||
.if ${.PARSEDIR:tA} != ${.CURDIR:tA}
|
||||
@cd ${MAKEFILE:H} && cp ${FILES} [at]*.mk ${.CURDIR}
|
||||
.endif
|
||||
# The following targets create and remove files. The filesystem cache in
|
||||
# dir.c would probably not handle this correctly, therefore each of the
|
||||
# targets is run in its separate sub-make.
|
||||
${RUN} ${MAKE_CMD} remove-archive
|
||||
${RUN} ${MAKE_CMD} create-archive
|
||||
${RUN} ${MAKE_CMD} list-archive
|
||||
@ -21,25 +28,35 @@ all:
|
||||
${RUN} ${MAKE_CMD} depend-on-nonexistent-member
|
||||
${RUN} ${MAKE_CMD} remove-archive
|
||||
|
||||
create-archive: ${ARCHIVE}
|
||||
${ARCHIVE}: ${ARCHIVE}(${FILES})
|
||||
ar cru ${.TARGET} ${.OODATE}
|
||||
create-archive: ${ARCHIVE} pre post
|
||||
|
||||
# The indirect references with the $$ cover the code in Arch_ParseArchive
|
||||
# that calls Var_Parse. It's an esoteric scenario since at the point where
|
||||
# Arch_ParseArchive is called, the dependency line is already fully expanded.
|
||||
#
|
||||
${ARCHIVE}: $${:Ulibprog.a}(archive.mk modmisc.mk $${:Uvarmisc.mk}) pre post
|
||||
ar cru ${.TARGET} ${.OODATE:O}
|
||||
ranlib ${.TARGET}
|
||||
|
||||
list-archive: ${ARCHIVE}
|
||||
list-archive: ${ARCHIVE} pre post
|
||||
ar t ${.ALLSRC}
|
||||
|
||||
# XXX: I had expected that this dependency would select all *.mk files from
|
||||
# the archive. Instead, the globbing is done in the current directory.
|
||||
# To prevent an overly long file list, the pattern is restricted to [at]*.mk.
|
||||
list-archive-wildcard: ${ARCHIVE}([at]*.mk)
|
||||
list-archive-wildcard: ${ARCHIVE}([at]*.mk) pre post
|
||||
${RUN} printf '%s\n' ${.ALLSRC:O:@member@${.TARGET:Q}': '${member:Q}@}
|
||||
|
||||
depend-on-existing-member: ${ARCHIVE}(archive.mk)
|
||||
depend-on-existing-member: ${ARCHIVE}(archive.mk) pre post
|
||||
${RUN} echo $@
|
||||
|
||||
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk)
|
||||
depend-on-nonexistent-member: ${ARCHIVE}(nonexistent.mk) pre post
|
||||
${RUN} echo $@
|
||||
|
||||
remove-archive:
|
||||
remove-archive: pre post
|
||||
rm -f ${ARCHIVE}
|
||||
|
||||
pre: .USEBEFORE
|
||||
@echo Making ${.TARGET} ${.OODATE:C,.+,out-of-date,W} ${.OODATE:O}
|
||||
post: .USE
|
||||
@echo
|
||||
|
@ -1,5 +1,6 @@
|
||||
comment testing start
|
||||
this is foo
|
||||
This is how a comment looks: # comment
|
||||
comment testing done
|
||||
echo This is a shell comment: # comment
|
||||
This is a shell comment:
|
||||
echo This is not a shell comment: '# comment'
|
||||
This is not a shell comment: # comment
|
||||
A shell comment can#not start in the middle of a word.
|
||||
exit status 0
|
||||
|
@ -1,31 +1,74 @@
|
||||
# This is a comment
|
||||
.if ${MACHINE_ARCH} == something
|
||||
FOO=bar
|
||||
.endif
|
||||
# $NetBSD: comment.mk,v 1.2 2020/09/07 19:17:36 rillig Exp $
|
||||
#
|
||||
# Demonstrate how comments are written in makefiles.
|
||||
|
||||
# This is a comment.
|
||||
|
||||
#\
|
||||
Multiline comment
|
||||
This is a multiline comment.
|
||||
|
||||
BAR=# defined
|
||||
FOOBAR= # defined
|
||||
|
||||
# This is an escaped comment \
|
||||
that keeps going until the end of this line
|
||||
|
||||
# Another escaped comment \
|
||||
# Another multiline comment \
|
||||
that \
|
||||
goes \
|
||||
on
|
||||
on and on.
|
||||
|
||||
# Comments can be indented, but that is rather unusual.
|
||||
|
||||
# Comments can be indented with a tab.
|
||||
# These are not shell commands, they are just makefile comments.
|
||||
|
||||
.if 1 # There can be comments after conditions.
|
||||
.endif # And after the closing directive.
|
||||
|
||||
VAR= # This comment makes the variable value empty.
|
||||
.if ${VAR} != ""
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The comment does not need to start at the beginning of a word (as in the
|
||||
# shell), it can start anywhere.
|
||||
VAR=# defined but empty
|
||||
|
||||
# The space before the comment is always trimmed.
|
||||
VAR= value
|
||||
.if ${VAR} != "value"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This is NOT an escaped comment due to the double backslashes \\
|
||||
all: hi foo bar
|
||||
@echo comment testing done
|
||||
VAR= not part of the comment
|
||||
.if ${VAR} != "not part of the comment"
|
||||
. error
|
||||
.endif
|
||||
|
||||
hi:
|
||||
@echo comment testing start
|
||||
# To escape a comment sign, precede it with a backslash.
|
||||
VAR= \# # Both in the assignment.
|
||||
.if ${VAR} != "\#" # And in the comparison.
|
||||
. error
|
||||
.endif
|
||||
|
||||
foo:
|
||||
@echo this is $@
|
||||
# Since 2012-03-24 the variable modifier :[#] does not need to be escaped.
|
||||
# To keep the parsing code simple, any "[#" does not start a comment, even
|
||||
# outside of a variable expression.
|
||||
WORDS= ${VAR:[#]} [#
|
||||
.if ${WORDS} != "1 [#"
|
||||
. error
|
||||
.endif
|
||||
|
||||
bar:
|
||||
@echo This is how a comment looks: '# comment'
|
||||
# An odd number of comment signs makes a line continuation, \\\
|
||||
no matter if it is 3 or 5 \\\\\
|
||||
or 9 backslashes. \\\\\\\\\
|
||||
This is the last line of the comment.
|
||||
VAR= no comment anymore
|
||||
.if ${VAR} != "no comment anymore"
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
# In the commands associated with a target, the '#' does not start a makefile
|
||||
# comment. The '#' is just passed to the shell, like any ordinary character.
|
||||
echo This is a shell comment: # comment
|
||||
# If the '#' were to start a makefile comment, the following shell command
|
||||
# would have unbalanced quotes.
|
||||
echo This is not a shell comment: '# comment'
|
||||
@echo A shell comment can#not start in the middle of a word.
|
||||
|
@ -1 +1,6 @@
|
||||
exit status 0
|
||||
make: "cond-cmp-numeric-eq.mk" line 54: warning: Unknown operator
|
||||
make: "cond-cmp-numeric-eq.mk" line 54: Malformed conditional (!(12345 = 12345))
|
||||
make: "cond-cmp-numeric-eq.mk" line 61: Malformed conditional (!(12345 === 12345))
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,43 +1,43 @@
|
||||
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-eq.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the == operator in .if conditions.
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 == 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 == 2
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 == 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 == 2000e4
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2000e4 == 2e7
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Trailing zeroes after the decimal point are irrelevant for the numeric
|
||||
# value.
|
||||
.if 3.30000 == 3.3
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.3 == 3.30000
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -46,7 +46,22 @@
|
||||
# be equal.
|
||||
.if 1.000000000000000001 == 1.000000000000000002
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
|
||||
# There is no = operator for numbers.
|
||||
.if !(12345 = 12345)
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no === operator for numbers either.
|
||||
.if !(12345 === 12345)
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,65 +1,65 @@
|
||||
# $NetBSD: cond-cmp-numeric-ge.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-ge.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the >= operator in .if conditions.
|
||||
|
||||
# When both sides are equal, the >= operator always yields true.
|
||||
.if 1 >= 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 >= 2
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 >= 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If this comparison were character-based instead of numerical, the
|
||||
# 5 would be >= 14 since its first digit is greater.
|
||||
.if 5 >= 14
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 14 >= 5
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 >= 1e8
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 1e8 >= 2e7
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Floating pointer numbers can be compared as well.
|
||||
# This might be tempting to use for version numbers, but there are a few pitfalls.
|
||||
.if 3.141 >= 111.222
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 111.222 >= 3.141
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When parsed as a version number, 3.30 is greater than 3.7.
|
||||
# Since make parses numbers as plain numbers, that leads to wrong results.
|
||||
# Numeric comparisons are not suited for comparing version number.
|
||||
.if 3.30 >= 3.7
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.7 >= 3.30
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -68,7 +68,7 @@
|
||||
# be equal.
|
||||
.if 1.000000000000000001 >= 1.000000000000000002
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,64 +1,64 @@
|
||||
# $NetBSD: cond-cmp-numeric-gt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-gt.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the > operator in .if conditions.
|
||||
|
||||
# When both sides are equal, the > operator always yields false.
|
||||
.if 1 > 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 > 2
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 > 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If this comparison were character-based instead of numerical, the
|
||||
# 5 would be > 14 since its first digit is greater.
|
||||
.if 5 > 14
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 14 > 5
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 > 1e8
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 1e8 > 2e7
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Floating pointer numbers can be compared as well.
|
||||
# This might be tempting to use for version numbers, but there are a few pitfalls.
|
||||
.if 3.141 > 111.222
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 111.222 > 3.141
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When parsed as a version number, 3.30 is greater than 3.7.
|
||||
# Since make parses numbers as plain numbers, that leads to wrong results.
|
||||
# Numeric comparisons are not suited for comparing version number.
|
||||
.if 3.30 > 3.7
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.7 > 3.30
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -66,7 +66,7 @@
|
||||
# typically 16 or 17 significant digits, therefore these two numbers seem to
|
||||
# be equal.
|
||||
.if 1.000000000000000001 > 1.000000000000000002
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,53 +1,53 @@
|
||||
# $NetBSD: cond-cmp-numeric-le.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-le.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the <= operator in .if conditions.
|
||||
|
||||
# When both sides are equal, the <= operator always yields true.
|
||||
.if 1 <= 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 <= 2
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 <= 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If this comparison were character-based instead of numerical, the
|
||||
# 5 would be >= 14 since its first digit is greater.
|
||||
.if 5 <= 14
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 14 <= 5
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 <= 1e8
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 1e8 <= 2e7
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Floating pointer numbers can be compared as well.
|
||||
# This might be tempting to use for version numbers, but there are a few pitfalls.
|
||||
.if 3.141 <= 111.222
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 111.222 <= 3.141
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When parsed as a version number, 3.30 is greater than 3.7.
|
||||
@ -55,11 +55,11 @@
|
||||
# Numeric comparisons are not suited for comparing version number.
|
||||
.if 3.30 <= 3.7
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.7 <= 3.30
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -68,7 +68,7 @@
|
||||
# be equal.
|
||||
.if 1.000000000000000001 <= 1.000000000000000002
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,52 +1,52 @@
|
||||
# $NetBSD: cond-cmp-numeric-lt.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-lt.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the < operator in .if conditions.
|
||||
|
||||
# When both sides are equal, the < operator always yields false.
|
||||
.if 1 < 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 < 2
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 < 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If this comparison were character-based instead of numerical, the
|
||||
# 5 would be > 14 since its first digit is greater.
|
||||
.if 5 < 14
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 14 < 5
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 < 1e8
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 1e8 < 2e7
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Floating pointer numbers can be compared as well.
|
||||
# This might be tempting to use for version numbers, but there are a few pitfalls.
|
||||
.if 3.141 < 111.222
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 111.222 < 3.141
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When parsed as a version number, 3.30 is greater than 3.7.
|
||||
@ -54,11 +54,11 @@
|
||||
# Numeric comparisons are not suited for comparing version number.
|
||||
.if 3.30 < 3.7
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.7 < 3.30
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -66,7 +66,7 @@
|
||||
# typically 16 or 17 significant digits, therefore these two numbers seem to
|
||||
# be equal.
|
||||
.if 1.000000000000000001 < 1.000000000000000002
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,40 +1,40 @@
|
||||
# $NetBSD: cond-cmp-numeric-ne.mk,v 1.1 2020/08/23 13:50:17 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric-ne.mk,v 1.2 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons with the != operator in .if conditions.
|
||||
|
||||
# When both sides are equal, the != operator always yields false.
|
||||
.if 1 != 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This comparison yields the same result, whether numeric or character-based.
|
||||
.if 1 != 2
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2 != 1
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Scientific notation is supported, as per strtod.
|
||||
.if 2e7 != 2000e4
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 2000e4 != 2e7
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Trailing zeroes after the decimal point are irrelevant for the numeric
|
||||
# value.
|
||||
.if 3.30000 != 3.3
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 3.3 != 3.30000
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As of 2020-08-23, numeric comparison is implemented as parsing both sides
|
||||
@ -42,7 +42,7 @@
|
||||
# typically 16 or 17 significant digits, therefore these two numbers seem to
|
||||
# be equal.
|
||||
.if 1.000000000000000001 != 1.000000000000000002
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1 +1,11 @@
|
||||
exit status 0
|
||||
CondParser_Eval: !(${:UINF} > 1e100)
|
||||
make: "cond-cmp-numeric.mk" line 11: warning: String comparison operator must be either == or !=
|
||||
make: "cond-cmp-numeric.mk" line 11: Malformed conditional (!(${:UINF} > 1e100))
|
||||
CondParser_Eval: ${:UNaN} > NaN
|
||||
make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be either == or !=
|
||||
make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN)
|
||||
CondParser_Eval: !(${:UNaN} == NaN)
|
||||
lhs = "NaN", rhs = "NaN", op = ==
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,29 @@
|
||||
# $NetBSD: cond-cmp-numeric.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-cmp-numeric.mk,v 1.3 2020/09/12 18:01:51 rillig Exp $
|
||||
#
|
||||
# Tests for numeric comparisons in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
.MAKEFLAGS: -dc
|
||||
|
||||
# The ${:U...} on the left-hand side is necessary for the parser.
|
||||
|
||||
# Even if strtod(3) parses "INF" as +Infinity, make does not accept this
|
||||
# since it is not really a number; see TryParseNumber.
|
||||
.if !(${:UINF} > 1e100)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Neither is NaN a number; see TryParseNumber.
|
||||
.if ${:UNaN} > NaN
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Since NaN is not parsed as a number, both operands are interpreted
|
||||
# as strings and are therefore equal. If they were parsed as numbers,
|
||||
# they would compare unequal, since NaN is unequal to any and everything,
|
||||
# including itself.
|
||||
.if !(${:UNaN} == NaN)
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,5 +1,8 @@
|
||||
make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str)
|
||||
make: "cond-cmp-string.mk" line 37: Malformed conditional ("string" != "str""ing")
|
||||
make: "cond-cmp-string.mk" line 42: warning: String comparison operator must be either == or !=
|
||||
make: "cond-cmp-string.mk" line 42: Malformed conditional (!("value" = "value"))
|
||||
make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" === "value"))
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,39 +1,90 @@
|
||||
# $NetBSD: cond-cmp-string.mk,v 1.3 2020/08/20 18:43:19 rillig Exp $
|
||||
# $NetBSD: cond-cmp-string.mk,v 1.11 2020/10/30 14:53:31 rillig Exp $
|
||||
#
|
||||
# Tests for string comparisons in .if conditions.
|
||||
|
||||
# This is a simple comparison of string literals.
|
||||
# Nothing surprising here.
|
||||
.if "str" != "str"
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The right-hand side of the comparison may be written without quotes.
|
||||
.if "str" != str
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The left-hand side of the comparison must be enclosed in quotes.
|
||||
# This one is not enclosed in quotes and thus generates an error message.
|
||||
.if str != str
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The left-hand side of the comparison requires a defined variable.
|
||||
# The variable named "" is not defined, but applying the :U modifier to it
|
||||
# makes it "kind of defined" (see VAR_KEEP). Therefore it is ok here.
|
||||
.if ${:Ustr} != "str"
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Any character in a string literal may be escaped using a backslash.
|
||||
# This means that "\n" does not mean a newline but a simple "n".
|
||||
.if "string" != "\s\t\r\i\n\g"
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# It is not possible to concatenate two string literals to form a single
|
||||
# string.
|
||||
.if "string" != "str""ing"
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no = operator for strings.
|
||||
.if !("value" = "value")
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no === operator for strings either.
|
||||
.if !("value" === "value")
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A variable expression can be enclosed in double quotes.
|
||||
.if ${:Uword} != "${:Uword}"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the
|
||||
# characters " \t!=><" directly after a variable expression resulted in a
|
||||
# "Malformed conditional", even though the string was well-formed.
|
||||
.if ${:Uword } != "${:Uword} "
|
||||
. error
|
||||
.endif
|
||||
# Some other characters worked though, and some didn't.
|
||||
# Those that are mentioned in is_separator didn't work.
|
||||
.if ${:Uword0} != "${:Uword}0"
|
||||
. error
|
||||
.endif
|
||||
.if ${:Uword&} != "${:Uword}&"
|
||||
. error
|
||||
.endif
|
||||
.if ${:Uword!} != "${:Uword}!"
|
||||
. error
|
||||
.endif
|
||||
.if ${:Uword<} != "${:Uword}<"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Adding another variable expression to the string literal works though.
|
||||
.if ${:Uword} != "${:Uwo}${:Urd}"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Adding a space at the beginning of the quoted variable expression works
|
||||
# though.
|
||||
.if ${:U word } != " ${:Uword} "
|
||||
. error
|
||||
.endif
|
||||
|
0
contrib/bmake/unit-tests/misc.exp → contrib/bmake/unit-tests/cond-cmp-unary.exp
Normal file → Executable file
0
contrib/bmake/unit-tests/misc.exp → contrib/bmake/unit-tests/cond-cmp-unary.exp
Normal file → Executable file
43
contrib/bmake/unit-tests/cond-cmp-unary.mk
Executable file
43
contrib/bmake/unit-tests/cond-cmp-unary.mk
Executable file
@ -0,0 +1,43 @@
|
||||
# $NetBSD: cond-cmp-unary.mk,v 1.1 2020/09/14 06:22:59 rillig Exp $
|
||||
#
|
||||
# Tests for unary comparisons in .if conditions, that is, comparisons with
|
||||
# a single operand. If the operand is a number, it is compared to zero,
|
||||
# if it is a string, it is tested for emptiness.
|
||||
|
||||
# The number 0 evaluates to false.
|
||||
.if 0
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Any other number evaluates to true.
|
||||
.if !12345
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The empty string evaluates to false.
|
||||
.if ""
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Any other string evaluates to true.
|
||||
.if !"0"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The empty string may come from a variable expression.
|
||||
.if ${:U}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A variable expression that is not surrounded by quotes is interpreted
|
||||
# as a number if possible, otherwise as a string.
|
||||
.if ${:U0}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A non-zero number from a variable expression evaluates to true.
|
||||
.if !${:U12345}
|
||||
. error
|
||||
.endif
|
||||
|
||||
all: # nothing
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-commands.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
|
||||
# $NetBSD: cond-func-commands.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the commands() function in .if conditions.
|
||||
|
||||
@ -6,14 +6,14 @@
|
||||
|
||||
# The target "target" does not exist yet, therefore it cannot have commands.
|
||||
.if commands(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
|
||||
# Now the target exists, but it still has no commands.
|
||||
.if commands(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
@ -21,7 +21,7 @@ target:
|
||||
|
||||
# Even after the comment, the target still has no commands.
|
||||
.if commands(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
@ -29,7 +29,7 @@ target:
|
||||
|
||||
# Finally the target has commands.
|
||||
.if !commands(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-defined.mk,v 1.3 2020/08/20 17:23:43 rillig Exp $
|
||||
# $NetBSD: cond-func-defined.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the defined() function in .if conditions.
|
||||
|
||||
@ -6,27 +6,27 @@ DEF= defined
|
||||
${:UA B}= variable name with spaces
|
||||
|
||||
.if !defined(DEF)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Horizontal whitespace after the opening parenthesis is ignored.
|
||||
# Horizontal whitespace (space tab) after the opening parenthesis is ignored.
|
||||
.if !defined( DEF)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Horizontal whitespace before the closing parenthesis is ignored.
|
||||
# Horizontal whitespace (space tab) before the closing parenthesis is ignored.
|
||||
.if !defined(DEF )
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The argument of a function must not directly contain whitespace.
|
||||
.if !defined(A B)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If necessary, the whitespace can be generated by a variable expression.
|
||||
.if !defined(${:UA B})
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,8 +1,150 @@
|
||||
# $NetBSD: cond-func-empty.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-func-empty.mk,v 1.8 2020/09/23 08:11:28 rillig Exp $
|
||||
#
|
||||
# Tests for the empty() function in .if conditions, which tests a variable
|
||||
# expression for emptiness.
|
||||
#
|
||||
# Note that the argument in the parentheses is indeed a variable name,
|
||||
# optionally followed by variable modifiers. This is like the defined()
|
||||
# function.
|
||||
#
|
||||
# Tests for the empty() function in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
.undef UNDEF
|
||||
EMPTY= # empty
|
||||
SPACE= ${:U }
|
||||
WORD= word
|
||||
|
||||
# An undefined variable is empty.
|
||||
.if !empty(UNDEF)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# An undefined variable has the empty string as the value, and the :M
|
||||
# variable modifier does not change that.
|
||||
#
|
||||
.if !empty(UNDEF:M*)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The :S modifier replaces the empty value with an actual word, and
|
||||
# after that the expression is no longer empty. Because the variable
|
||||
# was undefined in the first place, the expression has the flag VAR_JUNK
|
||||
# but not VAR_KEEP, therefore it is still considered undefined.
|
||||
# Only very few variable modifiers turn an undefined variable expression
|
||||
# into a defined variable expression. The :U and :D modifiers belong to
|
||||
# that group, but :S doesn't (see VAR_KEEP).
|
||||
#
|
||||
# XXX: This is hard to explain to someone who doesn't know these
|
||||
# implementation details.
|
||||
#
|
||||
.if !empty(UNDEF:S,^$,value,W)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The :U modifier modifies expressions based on undefined variables
|
||||
# (VAR_JUNK) by adding the VAR_KEEP flag, which marks the expression
|
||||
# as "being interesting enough to be further processed".
|
||||
#
|
||||
.if empty(UNDEF:S,^$,value,W:Ufallback)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# And now to the surprising part. Applying the following :S modifier to the
|
||||
# undefined variable makes it non-empty, but the marker VAR_JUNK is preserved
|
||||
# nevertheless. The :U modifier that follows only looks at VAR_JUNK to decide
|
||||
# whether the variable is defined or not. This kind of makes sense since the
|
||||
# :U modifier tests the _variable_, not the _expression_.
|
||||
#
|
||||
# But since the variable was undefined to begin with, the fallback value is
|
||||
# used in this expression.
|
||||
#
|
||||
.if ${UNDEF:S,^$,value,W:Ufallback} != "fallback"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable EMPTY is completely empty (0 characters).
|
||||
.if !empty(EMPTY)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable SPACE has a single space, which counts as being empty.
|
||||
.if !empty(SPACE)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable .newline has a single newline, which counts as being empty.
|
||||
.if !empty(.newline)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The empty variable named "" gets a fallback value of " ", which counts as
|
||||
# empty.
|
||||
#
|
||||
# Contrary to the other functions in conditionals, the trailing space is not
|
||||
# stripped off, as can be seen in the -dv debug log. If the space had been
|
||||
# stripped, it wouldn't make a difference in this case.
|
||||
#
|
||||
.if !empty(:U )
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Now the variable named " " gets a non-empty value, which demonstrates that
|
||||
# neither leading nor trailing spaces are trimmed in the argument of the
|
||||
# function. If the spaces were trimmed, the variable name would be "" and
|
||||
# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse
|
||||
# without VARE_UNDEFERR, the value of the undefined variable is returned as
|
||||
# an empty string.
|
||||
${:U }= space
|
||||
.if empty( )
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The value of the following expression is " word", which is not empty.
|
||||
.if empty(:U word)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The :L modifier creates a variable expression that has the same value as
|
||||
# its name, which both are "VAR" in this case. The value is therefore not
|
||||
# empty.
|
||||
.if empty(VAR:L)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable WORD has the value "word", which does not count as empty.
|
||||
.if empty(WORD)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The expression ${} for a variable with the empty name always evaluates
|
||||
# to an empty string (see Var_Parse, varUndefined).
|
||||
.if !empty()
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Ensure that variable expressions that appear as part of the argument are
|
||||
# properly parsed. Typical use cases for this are .for loops, which are
|
||||
# expanded to exactly these ${:U} expressions.
|
||||
#
|
||||
# If everything goes well, the argument expands to "WORD", and that variable
|
||||
# is defined at the beginning of this file. The surrounding 'W' and 'D'
|
||||
# ensure that the parser in ParseEmptyArg has the correct position, both
|
||||
# before and after the call to Var_ParsePP.
|
||||
.if empty(W${:UOR}D)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There may be spaces at the outside of the parentheses.
|
||||
# Spaces inside the parentheses are interpreted as part of the variable name.
|
||||
.if ! empty ( WORD )
|
||||
. error
|
||||
.endif
|
||||
|
||||
${:U WORD }= variable name with spaces
|
||||
|
||||
# Now there is a variable named " WORD ", and it is not empty.
|
||||
.if empty ( WORD )
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,41 +1,41 @@
|
||||
# $NetBSD: cond-func-exists.mk,v 1.4 2020/08/28 12:59:36 rillig Exp $
|
||||
# $NetBSD: cond-func-exists.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the exists() function in .if conditions.
|
||||
|
||||
.if !exists(.)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The argument to the function must not be enclosed in quotes.
|
||||
# Neither double quotes nor single quotes are allowed.
|
||||
.if exists(".")
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if exists('.')
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The only way to escape characters that would otherwise influence the parser
|
||||
# is to enclose them in a variable expression. For function arguments,
|
||||
# neither the backslash nor the dollar sign act as escape character.
|
||||
.if exists(\.)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !exists(${:U.})
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The argument to the function can have several variable expressions.
|
||||
# See cond-func.mk for the characters that cannot be used directly.
|
||||
.if !exists(${.PARSEDIR}/${.PARSEFILE})
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Whitespace is trimmed on both sides of the function argument.
|
||||
.if !exists( . )
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1 +1,3 @@
|
||||
: via-cmdline
|
||||
: via-dot-makeflags
|
||||
exit status 0
|
||||
|
@ -1,8 +1,24 @@
|
||||
# $NetBSD: cond-func-make.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-func-make.mk,v 1.3 2020/09/25 20:11:06 rillig Exp $
|
||||
#
|
||||
# Tests for the make() function in .if conditions.
|
||||
# Tests for the make() function in .if conditions, which tests whether
|
||||
# the argument has been passed as a target via the command line or later
|
||||
# via the .MAKEFLAGS special dependency target.
|
||||
|
||||
# TODO: Implementation
|
||||
.if !make(via-cmdline)
|
||||
. error
|
||||
.endif
|
||||
.if make(via-dot-makeflags)
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
.MAKEFLAGS: via-dot-makeflags
|
||||
|
||||
.if !make(via-cmdline)
|
||||
. error
|
||||
.endif
|
||||
.if !make(via-dot-makeflags)
|
||||
. error
|
||||
.endif
|
||||
|
||||
via-cmdline via-dot-makeflags:
|
||||
: $@
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-target.mk,v 1.3 2020/08/23 14:07:20 rillig Exp $
|
||||
# $NetBSD: cond-func-target.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the target() function in .if conditions.
|
||||
|
||||
@ -6,14 +6,14 @@
|
||||
|
||||
# The target "target" does not exist yet.
|
||||
.if target(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
|
||||
# The target exists, even though it does not have any commands.
|
||||
.if !target(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
@ -22,7 +22,7 @@ target:
|
||||
# Adding a comment to an existing target does not change whether the target
|
||||
# is defined or not.
|
||||
.if !target(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
target:
|
||||
@ -31,7 +31,7 @@ target:
|
||||
# Adding a command to an existing target does not change whether the target
|
||||
# is defined or not.
|
||||
.if !target(target)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func.mk,v 1.1 2020/08/20 17:45:47 rillig Exp $
|
||||
# $NetBSD: cond-func.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for those parts of the functions in .if conditions that are common
|
||||
# among several functions.
|
||||
@ -12,27 +12,27 @@ ${:UVAR(value)}= variable name with parentheses
|
||||
${:UVAR{value}}= variable name with braces
|
||||
|
||||
.if !defined(DEF)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Horizontal whitespace after the opening parenthesis is ignored.
|
||||
# Horizontal whitespace (space tab) after the opening parenthesis is ignored.
|
||||
.if !defined( DEF)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Horizontal whitespace before the closing parenthesis is ignored.
|
||||
# Horizontal whitespace (space tab) before the closing parenthesis is ignored.
|
||||
.if !defined(DEF )
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The argument of a function must not directly contain whitespace.
|
||||
.if !defined(A B)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If necessary, the whitespace can be generated by a variable expression.
|
||||
.if !defined(${:UA B})
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Characters that could be mistaken for operators must not appear directly
|
||||
@ -42,21 +42,29 @@ ${:UVAR{value}}= variable name with braces
|
||||
# It's not entirely clear why these characters are forbidden.
|
||||
# The most plausible reason seems to be typo detection.
|
||||
.if !defined(A&B)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
.if !defined(A|B)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even parentheses may appear in variable names.
|
||||
# They must be balanced though.
|
||||
.if !defined(VAR(value))
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Braces do not have any special meaning when parsing arguments.
|
||||
.if !defined(VAR{value})
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There may be spaces around the operators and parentheses, and even
|
||||
# inside the parentheses. The spaces inside the parentheses are not
|
||||
# allowed for the empty() function (see cond-func-empty.mk), therefore
|
||||
# they are typically omitted for the other functions as well.
|
||||
.if ! defined ( DEF )
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "cond-op-and.mk" line 43: Malformed conditional (0 &&& 0)
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,21 +1,21 @@
|
||||
# $NetBSD: cond-op-and.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
|
||||
# $NetBSD: cond-op-and.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the && operator in .if conditions.
|
||||
|
||||
.if 0 && 0
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 1 && 0
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if 0 && 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !(1 && 1)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The right-hand side is not evaluated since the left-hand side is already
|
||||
@ -23,5 +23,26 @@
|
||||
.if 0 && ${UNDEF}
|
||||
.endif
|
||||
|
||||
# The && operator may be abbreviated as &. This is not widely known though
|
||||
# and is also not documented in the manual page.
|
||||
|
||||
.if 0 & 0
|
||||
. error
|
||||
.endif
|
||||
.if 1 & 0
|
||||
. error
|
||||
.endif
|
||||
.if 0 & 1
|
||||
. error
|
||||
.endif
|
||||
.if !(1 & 1)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no operator &&&.
|
||||
.if 0 &&& 0
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,21 +1,21 @@
|
||||
# $NetBSD: cond-op-not.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
|
||||
# $NetBSD: cond-op-not.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the ! operator in .if conditions.
|
||||
|
||||
# The exclamation mark negates its operand.
|
||||
.if !1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Exclamation marks can be chained.
|
||||
# This doesn't happen in practice though.
|
||||
.if !!!1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The ! binds more tightly than the &&.
|
||||
.if !!0 && 1
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
|
@ -1 +1,4 @@
|
||||
exit status 0
|
||||
make: "cond-op-or.mk" line 43: Malformed conditional (0 ||| 0)
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,21 +1,21 @@
|
||||
# $NetBSD: cond-op-or.mk,v 1.3 2020/08/28 14:48:37 rillig Exp $
|
||||
# $NetBSD: cond-op-or.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for the || operator in .if conditions.
|
||||
|
||||
.if 0 || 0
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !(1 || 0)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !(0 || 1)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
.if !(1 || 1)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The right-hand side is not evaluated since the left-hand side is already
|
||||
@ -23,5 +23,26 @@
|
||||
.if 1 || ${UNDEF}
|
||||
.endif
|
||||
|
||||
# The || operator may be abbreviated as |. This is not widely known though
|
||||
# and is also not documented in the manual page.
|
||||
|
||||
.if 0 | 0
|
||||
. error
|
||||
.endif
|
||||
.if !(1 | 0)
|
||||
. error
|
||||
.endif
|
||||
.if !(0 | 1)
|
||||
. error
|
||||
.endif
|
||||
.if !(1 | 1)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# There is no operator |||.
|
||||
.if 0 ||| 0
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,5 +1,7 @@
|
||||
make: "cond-op.mk" line 45: Malformed conditional ("!word" == !word)
|
||||
make: "cond-op.mk" line 57: Parsing continues until here.
|
||||
make: "cond-op.mk" line 70: Malformed conditional (0 ${ERR::=evaluated})
|
||||
make: "cond-op.mk" line 74: warning: After detecting a parse error, the rest is evaluated.
|
||||
make: "cond-op.mk" line 78: Parsing continues until here.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-op.mk,v 1.4 2020/08/28 14:07:51 rillig Exp $
|
||||
# $NetBSD: cond-op.mk,v 1.8 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for operators like &&, ||, ! in .if conditions.
|
||||
#
|
||||
@ -14,24 +14,24 @@
|
||||
# If || were to bind more tightly than &&, the result would be different
|
||||
# as well.
|
||||
.if !(1 || 1 && 0)
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# If make were to interpret the && and || operators like the shell, the
|
||||
# implicit binding would be this:
|
||||
.if (1 || 1) && 0
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The precedence of the ! operator is different from C though. It has a
|
||||
# lower precedence than the comparison operators.
|
||||
.if !"word" == "word"
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# This is how the above condition is actually interpreted.
|
||||
.if !("word" == "word")
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# TODO: Demonstrate that the precedence of the ! and == operators actually
|
||||
@ -41,16 +41,37 @@
|
||||
# This condition is malformed because the '!' on the right-hand side must not
|
||||
# appear unquoted. If any, it must be enclosed in quotes.
|
||||
# In any case, it is not interpreted as a negation of an unquoted string.
|
||||
# See CondGetString.
|
||||
# See CondParser_String.
|
||||
.if "!word" == !word
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Surprisingly, the ampersand and pipe are allowed in bare strings.
|
||||
# That's another opportunity for writing confusing code.
|
||||
# See CondGetString, which only has '!' in the list of stop characters.
|
||||
# See CondParser_String, which only has '!' in the list of stop characters.
|
||||
.if "a&&b||c" != a&&b||c
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# As soon as the parser sees the '$', it knows that the condition will
|
||||
# be malformed. Therefore there is no point in evaluating it.
|
||||
#
|
||||
# As of 2020-09-11, that part of the condition is evaluated nevertheless,
|
||||
# since CondParser_Expr just requests the next token, without restricting
|
||||
# the token to the expected tokens. If the parser were to restrict the
|
||||
# valid follow tokens for the token "0" to those that can actually produce
|
||||
# a correct condition (which in this case would be comparison operators,
|
||||
# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have
|
||||
# to be evaluated.
|
||||
#
|
||||
# This would add a good deal of complexity to the code though, for almost
|
||||
# no benefit, especially since most expressions and conditions are side
|
||||
# effect free.
|
||||
.if 0 ${ERR::=evaluated}
|
||||
. error
|
||||
.endif
|
||||
.if ${ERR:Uundefined} == evaluated
|
||||
. warning After detecting a parse error, the rest is evaluated.
|
||||
.endif
|
||||
|
||||
# Just in case that parsing should ever stop on the first error.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-short.mk,v 1.9 2020/08/19 22:47:09 rillig Exp $
|
||||
# $NetBSD: cond-short.mk,v 1.11 2020/10/24 08:50:17 rillig Exp $
|
||||
#
|
||||
# Demonstrates that in conditions, the right-hand side of an && or ||
|
||||
# is only evaluated if it can actually influence the result.
|
||||
@ -76,7 +76,7 @@ VAR= # empty again, for the following tests
|
||||
.if 0 && ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@}
|
||||
.endif
|
||||
.if defined(FIRST) || defined(LAST) || defined(APPENDED) || defined(RAN)
|
||||
.warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
|
||||
. warning first=${FIRST} last=${LAST} appended=${APPENDED} ran=${RAN}
|
||||
.endif
|
||||
|
||||
# The || operator.
|
||||
@ -113,59 +113,59 @@ VAR= # empty again, for the following tests
|
||||
# make sure these do not cause complaint
|
||||
#.MAKEFLAGS: -dc
|
||||
|
||||
V42 = 42
|
||||
iV1 = ${V42}
|
||||
iV2 = ${V66}
|
||||
V42= 42
|
||||
iV1= ${V42}
|
||||
iV2= ${V66}
|
||||
|
||||
.if defined(V42) && ${V42} > 0
|
||||
x=Ok
|
||||
x= Ok
|
||||
.else
|
||||
x=Fail
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
|
||||
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
|
||||
|
||||
# this one throws both String comparison operator and
|
||||
# Malformed conditional with cond.c 1.78
|
||||
# indirect iV2 would expand to "" and treated as 0
|
||||
.if defined(V66) && ( ${iV2} < ${V42} )
|
||||
x=Fail
|
||||
x= Fail
|
||||
.else
|
||||
x=Ok
|
||||
x= Ok
|
||||
.endif
|
||||
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
|
||||
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
|
||||
|
||||
# next two thow String comparison operator with cond.c 1.78
|
||||
# indirect iV1 would expand to 42
|
||||
.if 1 || ${iV1} < ${V42}
|
||||
x=Ok
|
||||
x= Ok
|
||||
.else
|
||||
x=Fail
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
|
||||
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
|
||||
|
||||
.if 1 || ${iV2:U2} < ${V42}
|
||||
x=Ok
|
||||
x= Ok
|
||||
.else
|
||||
x=Fail
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
|
||||
# the same expressions are fine when the lhs is expanded
|
||||
# ${iV1} expands to 42
|
||||
.if 0 || ${iV1} <= ${V42}
|
||||
x=Ok
|
||||
x= Ok
|
||||
.else
|
||||
x=Fail
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
|
||||
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
|
||||
|
||||
# ${iV2:U2} expands to 2
|
||||
.if 0 || ${iV2:U2} < ${V42}
|
||||
x=Ok
|
||||
x= Ok
|
||||
.else
|
||||
x=Fail
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
|
||||
all:
|
||||
@:;:
|
||||
|
@ -1 +1,8 @@
|
||||
exit status 0
|
||||
make: "cond-token-number.mk" line 13: Malformed conditional (-0)
|
||||
make: "cond-token-number.mk" line 21: Malformed conditional (+0)
|
||||
make: "cond-token-number.mk" line 29: Malformed conditional (!-1)
|
||||
make: "cond-token-number.mk" line 37: Malformed conditional (!+1)
|
||||
make: "cond-token-number.mk" line 54: End of the tests.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,8 +1,56 @@
|
||||
# $NetBSD: cond-token-number.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-token-number.mk,v 1.3 2020/09/14 06:22:59 rillig Exp $
|
||||
#
|
||||
# Tests for number tokens in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
.if 0
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
# Even though -0 is a number and would be accepted by strtod, it is not
|
||||
# accepted by the condition parser.
|
||||
#
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if -0
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though +0 is a number and would be accepted by strtod, it is not
|
||||
# accepted by the condition parser.
|
||||
#
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if +0
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though -1 is a number and would be accepted by strtod, it is not
|
||||
# accepted by the condition parser.
|
||||
#
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if !-1
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Even though +1 is a number and would be accepted by strtod, it is not
|
||||
# accepted by the condition parser.
|
||||
#
|
||||
# See the ch_isdigit call in CondParser_String.
|
||||
.if !+1
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When the number comes from a variable expression though, it may be signed.
|
||||
# XXX: This is inconsistent.
|
||||
.if ${:U+0}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# When the number comes from a variable expression though, it may be signed.
|
||||
# XXX: This is inconsistent.
|
||||
.if !${:U+1}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Ensure that parsing continues until here.
|
||||
.info End of the tests.
|
||||
|
||||
all: # nothing
|
||||
|
@ -1 +1,29 @@
|
||||
CondParser_Eval: ${:Uvalue} != value
|
||||
lhs = "value", rhs = "value", op = !=
|
||||
CondParser_Eval: ${:U} != "
|
||||
lhs = "", rhs = "", op = !=
|
||||
CondParser_Eval: ${:U#hash} != "#hash"
|
||||
lhs = "#hash", rhs = "#hash", op = !=
|
||||
CondParser_Eval: ${:U\\} != "\\
|
||||
lhs = "\", rhs = "\", op = !=
|
||||
CondParser_Eval: ${:U#hash} != #hash
|
||||
lhs = "#hash", rhs = "#hash", op = !=
|
||||
CondParser_Eval: 0 # This is treated as a comment, but why?
|
||||
CondParser_Eval: ${0 # comment :?yes:no} != no
|
||||
CondParser_Eval: 0 # comment
|
||||
lhs = "no", rhs = "no", op = !=
|
||||
CondParser_Eval: ${1 # comment :?yes:no} != yes
|
||||
CondParser_Eval: 1 # comment
|
||||
lhs = "yes", rhs = "yes", op = !=
|
||||
CondParser_Eval: ${UNDEF:Uundefined}!=undefined
|
||||
lhs = "undefined", rhs = "undefined", op = !=
|
||||
CondParser_Eval: ${UNDEF:U12345}>12345
|
||||
lhs = 12345.000000, rhs = 12345.000000, op = >1
|
||||
CondParser_Eval: ${UNDEF:U12345}<12345
|
||||
lhs = 12345.000000, rhs = 12345.000000, op = <1
|
||||
CondParser_Eval: (${UNDEF:U0})||0
|
||||
CondParser_Eval: ${:Uvar}&&name != "var&&name"
|
||||
lhs = "var&&name", rhs = "var&&name", op = !=
|
||||
CondParser_Eval: ${:Uvar}||name != "var||name"
|
||||
lhs = "var||name", rhs = "var||name", op = !=
|
||||
exit status 0
|
||||
|
@ -1,9 +1,94 @@
|
||||
# $NetBSD: cond-token-plain.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: cond-token-plain.mk,v 1.4 2020/09/12 17:47:24 rillig Exp $
|
||||
#
|
||||
# Tests for plain tokens (that is, string literals without quotes)
|
||||
# in .if conditions.
|
||||
|
||||
# TODO: Implementation
|
||||
.MAKEFLAGS: -dc
|
||||
|
||||
.if ${:Uvalue} != value
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Malformed condition since comment parsing is done in an early phase
|
||||
# and removes the '#' and everything behind it long before the condition
|
||||
# parser gets to see it.
|
||||
#
|
||||
# XXX: The error message is missing for this malformed condition.
|
||||
# The right-hand side of the comparison is just a '"'.
|
||||
.if ${:U} != "#hash"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# To get a '#' into a condition, it has to be escaped using a backslash.
|
||||
# This prevents the comment parser from removing it, and in turn, it becomes
|
||||
# visible to CondParser_String.
|
||||
.if ${:U\#hash} != "\#hash"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Since 2002-12-30, and still as of 2020-09-11, CondParser_Token handles
|
||||
# the '#' specially, even though at this point, there should be no need for
|
||||
# comment handling anymore. The comments are supposed to be stripped off
|
||||
# in a very early parsing phase.
|
||||
#
|
||||
# XXX: Missing error message for the malformed condition. The right-hand
|
||||
# side is double-quotes, backslash, backslash.
|
||||
# XXX: It is unexpected that the right-hand side evaluates to a single
|
||||
# backslash.
|
||||
.if ${:U\\} != "\\#hash"
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The right-hand side of a comparison is not parsed as a token, therefore
|
||||
# the code from CondParser_Token does not apply to it.
|
||||
.if ${:U\#hash} != \#hash
|
||||
. error
|
||||
.endif
|
||||
|
||||
# XXX: What is the purpose of treating an escaped '#' in the following
|
||||
# condition as a comment? And why only at the beginning of a token,
|
||||
# just as in the shell?
|
||||
.if 0 \# This is treated as a comment, but why?
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Ah, ok, this can be used to add an end-of-condition comment. But does
|
||||
# anybody really use this? This is neither documented nor obvious since
|
||||
# the '#' is escaped. It's much clearer to write a comment in the line
|
||||
# above the condition.
|
||||
.if ${0 \# comment :?yes:no} != no
|
||||
. error
|
||||
.endif
|
||||
.if ${1 \# comment :?yes:no} != yes
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Usually there is whitespace around the comparison operator, but this is
|
||||
# not required.
|
||||
.if ${UNDEF:Uundefined}!=undefined
|
||||
. error
|
||||
.endif
|
||||
.if ${UNDEF:U12345}>12345
|
||||
. error
|
||||
.endif
|
||||
.if ${UNDEF:U12345}<12345
|
||||
. error
|
||||
.endif
|
||||
.if (${UNDEF:U0})||0
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Only the comparison operator terminates the comparison operand, and it's
|
||||
# a coincidence that the '!' is both used in the '!=' comparison operator
|
||||
# as well as for negating a comparison result.
|
||||
#
|
||||
# The boolean operators '&' and '|' don't terminate a comparison operand.
|
||||
.if ${:Uvar}&&name != "var&&name"
|
||||
. error
|
||||
.endif
|
||||
.if ${:Uvar}||name != "var||name"
|
||||
. error
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-token-var.mk,v 1.3 2020/08/20 19:43:42 rillig Exp $
|
||||
# $NetBSD: cond-token-var.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
|
||||
#
|
||||
# Tests for variables in .if conditions.
|
||||
|
||||
@ -6,19 +6,19 @@ DEF= defined
|
||||
|
||||
# A defined variable may appear on either side of the comparison.
|
||||
.if ${DEF} == ${DEF}
|
||||
.info ok
|
||||
. info ok
|
||||
.else
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A variable that appears on the left-hand side must be defined.
|
||||
.if ${UNDEF} == ${DEF}
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A variable that appears on the right-hand side must be defined.
|
||||
.if ${DEF} == ${UNDEF}
|
||||
.error
|
||||
. error
|
||||
.endif
|
||||
|
||||
# A defined variable may appear as an expression of its own.
|
||||
|
7
contrib/bmake/unit-tests/cond-undef-lint.exp
Executable file
7
contrib/bmake/unit-tests/cond-undef-lint.exp
Executable file
@ -0,0 +1,7 @@
|
||||
make: "cond-undef-lint.mk" line 23: Variable "UNDEF" is undefined
|
||||
make: "cond-undef-lint.mk" line 38: Variable "UNDEF" is undefined
|
||||
make: "cond-undef-lint.mk" line 38: Variable "VAR." is undefined
|
||||
make: "cond-undef-lint.mk" line 45: Variable "VAR.defined" is undefined
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
69
contrib/bmake/unit-tests/cond-undef-lint.mk
Executable file
69
contrib/bmake/unit-tests/cond-undef-lint.mk
Executable file
@ -0,0 +1,69 @@
|
||||
# $NetBSD: cond-undef-lint.mk,v 1.2 2020/09/14 07:13:29 rillig Exp $
|
||||
#
|
||||
# Tests for defined and undefined variables in .if conditions, in lint mode.
|
||||
#
|
||||
# As of 2020-09-14, lint mode contains experimental code for printing
|
||||
# accurate error messages in case of undefined variables, instead of the
|
||||
# wrong "Malformed condition".
|
||||
#
|
||||
# See also:
|
||||
# opt-debug-lint.mk
|
||||
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
# DEF is defined, UNDEF is not.
|
||||
DEF= defined
|
||||
|
||||
# An expression based on a defined variable is fine.
|
||||
.if !${DEF}
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Since the condition fails to evaluate, neither of the branches is taken.
|
||||
.if ${UNDEF}
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable name depends on the undefined variable, which is probably a
|
||||
# mistake. The variable UNDEF, as used here, can be easily turned into
|
||||
# an expression that is always defined, using the :U modifier.
|
||||
#
|
||||
# The outer expression does not generate an error message since there was
|
||||
# already an error evaluating this variable's name.
|
||||
#
|
||||
# TODO: Suppress the error message "Variable VAR. is undefined". That part
|
||||
# of the expression must not be evaluated at all.
|
||||
.if ${VAR.${UNDEF}}
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The variable VAR.defined is not defined and thus generates an error message.
|
||||
.if ${VAR.${DEF}}
|
||||
. error
|
||||
.else
|
||||
. error
|
||||
.endif
|
||||
|
||||
|
||||
# Variables that are referenced indirectly may be undefined in a condition.
|
||||
#
|
||||
# A practical example for this is CFLAGS, which consists of CWARNS, COPTS
|
||||
# and a few others. Just because these nested variables are not defined,
|
||||
# this does not make the condition invalid.
|
||||
#
|
||||
# The crucial point is that at the point where the variable appears in the
|
||||
# condition, there is no way to influence the definedness of the nested
|
||||
# variables. In particular, there is no modifier that would turn undefined
|
||||
# nested variables into empty strings, as an equivalent to the :U modifier.
|
||||
INDIRECT= ${NESTED_UNDEF} ${NESTED_DEF}
|
||||
NESTED_DEF= nested-defined
|
||||
|
||||
# Since NESTED_UNDEF is not controllable at this point, it must not generate
|
||||
# an error message, and it doesn't do so, since 2020-09-14.
|
||||
.if !${INDIRECT}
|
||||
. error
|
||||
.endif
|
@ -16,7 +16,7 @@ Passed:
|
||||
4 is not prime
|
||||
5 is prime
|
||||
|
||||
make: warning: String comparison operator should be either == or !=
|
||||
make: warning: String comparison operator must be either == or !=
|
||||
make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
|
||||
|
||||
OK
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: cond1.mk,v 1.1.1.1 2014/08/30 18:57:18 sjg Exp $
|
||||
# $NetBSD: cond1.mk,v 1.2 2020/10/24 08:34:59 rillig Exp $
|
||||
|
||||
# hard code these!
|
||||
TEST_UNAME_S= NetBSD
|
||||
|
@ -1,7 +0,0 @@
|
||||
make: Bad conditional expression ` == "empty"' in == "empty"?oops:ok
|
||||
make: "cond2.mk" line 13: Malformed conditional ({TEST_TYPO} == "Ok")
|
||||
TEST_NOT_SET is empty or not defined
|
||||
make: "cond2.mk" line 20: Malformed conditional (${TEST_NOT_SET} == "empty")
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
@ -1,29 +0,0 @@
|
||||
# $Id: cond2.mk,v 1.1.1.2 2015/12/02 00:34:27 sjg Exp $
|
||||
|
||||
TEST_UNAME_S= NetBSD
|
||||
|
||||
# this should be ok
|
||||
X:= ${${TEST_UNAME_S} == "NetBSD":?Ok:fail}
|
||||
.if $X == "Ok"
|
||||
Y= good
|
||||
.endif
|
||||
# expect: Bad conditional expression ` == "empty"' in == "empty"?oops:ok
|
||||
X:= ${${TEST_NOT_SET} == "empty":?oops:ok}
|
||||
# expect: Malformed conditional ({TEST_TYPO} == "Ok")
|
||||
.if {TEST_TYPO} == "Ok"
|
||||
Y= oops
|
||||
.endif
|
||||
.if empty(TEST_NOT_SET)
|
||||
Y!= echo TEST_NOT_SET is empty or not defined >&2; echo
|
||||
.endif
|
||||
# expect: Malformed conditional (${TEST_NOT_SET} == "empty")
|
||||
.if ${TEST_NOT_SET} == "empty"
|
||||
Y= oops
|
||||
.endif
|
||||
|
||||
.if defined(.NDEF) && ${.NDEF} > 0
|
||||
Z= yes
|
||||
.endif
|
||||
|
||||
all:
|
||||
@echo $@
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user