Import bmake-20210110
Quite a lot of churn on style, but lots of good work refactoring complicated functions and lots more unit-tests. Thanks mostly to rillig at NetBSD Some interesting entries from ChangeLog o .MAKE.{UID,GID} represent uid and gid running make. o allow env var MAKE_OBJDIR_CHECK_WRITABLE=no to skip writable checks in InitObjdir. Explicit .OBJDIR target always allows read-only directory. o add more unit tests for META MODE Change-Id: I4d3bcf08b4c864d98b343f602efe5a75dbfa7a94
This commit is contained in:
parent
1b65f0bd2b
commit
8e11a9b425
241
ChangeLog
241
ChangeLog
@ -1,8 +1,247 @@
|
||||
2021-01-10 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20210110
|
||||
Merge with NetBSD make, pick up
|
||||
o fix lint warnings
|
||||
o consistently use boolean expressions in conditions
|
||||
|
||||
2021-01-08 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20210108
|
||||
Merge with NetBSD make, pick up
|
||||
o job.c: back to polling token pipe if we want a token
|
||||
o main.c: always print 'stopped in' on first call
|
||||
The execption is if we bail because of an abort token
|
||||
in which case just exit 6.
|
||||
|
||||
2021-01-01 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20210101
|
||||
Merge with NetBSD make, pick up
|
||||
o Happy New Year!
|
||||
o rename CmdOpts.lint to strict
|
||||
o exit 2 on technical errors
|
||||
o replace pointers in controlling conditions with booleans
|
||||
o replace global preserveUndefined with VARE_KEEP_UNDEF
|
||||
o compat.c: re-export variables from the actual make process
|
||||
if using vfork this is the effect anyway
|
||||
o cond.c: clean up VarParseResult constants
|
||||
o for.c: fix undefined behavior in SubstVarLong
|
||||
make control flow in SubstVarLong of .for loops more obvious
|
||||
clean up SubstVarShort in .for loops
|
||||
extract ForSubstBody from ForReadMore
|
||||
clean up ForReadMore
|
||||
simplify termination condition for .for loop
|
||||
add error handling for .for loop items
|
||||
job.c: re-export variables from the actual make process
|
||||
parse.c: remove mmap for loading files, only allow files < 1 GiB
|
||||
fix edge case in := with undefined in variable name
|
||||
skip variable expansion in ParseDependencyTargetWord
|
||||
var.c: split ExportVar into separate functions
|
||||
clean up code in extracted ExportVar functions
|
||||
remove dead code from ApplyModifiersIndirect
|
||||
split Var_Subst into easily understandable functions
|
||||
clean up VarParseResult constants
|
||||
|
||||
2020-12-25 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* main.c: use .MAKE.DEPENDFILE as set by makefiles
|
||||
|
||||
2020-12-22 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201222
|
||||
Merge with NetBSD make, pick up
|
||||
o make DEBUG macro return boolean
|
||||
o parse.c: fix assertion failure for files without trailing newline
|
||||
o var.c: allow .undef to undefine multiple variables at once
|
||||
remove excess newline from parse errors
|
||||
|
||||
2020-12-21 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201221
|
||||
Merge with NetBSD make, pick up
|
||||
o some unit-test updates
|
||||
|
||||
2020-12-20 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201220
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit tests
|
||||
o return FStr from Var_Parse and Var_Value
|
||||
o spell nonexistent consistently
|
||||
o add str_basename to reduce duplicate code
|
||||
o compat.c: fix .ERROR_TARGET in compat -k mode
|
||||
extract InitSignals from Compat_Run
|
||||
extract UseShell from Compat_RunCommand
|
||||
o cond.c: error out if an '.endif' or '.else' contain extraneous text
|
||||
o for.c: rename ForIterate to ForReadMore
|
||||
o hash.c: clean up hash function for HashTable
|
||||
o lst.c: rename Vector.priv_cap to cap
|
||||
o main.c: remove constant parameter from MakeMode
|
||||
o make.c: use symbolic time for 0 in Make_Recheck
|
||||
extract MakeChildren from MakeStartJobs
|
||||
o parse.c: clean up memory handling in VarAssign_EvalShell, Parse_DoVar
|
||||
fix error message for .info/.warning/.error without argument
|
||||
extract Var_Undef from ParseDirective
|
||||
extract ParseSkippedBranches, ParseForLoop from ParseReadLine
|
||||
rename mode constants for ParseGetLine to be more expressive
|
||||
reduce debugging details in Parse_SetInput
|
||||
fix line numbers in .for loops
|
||||
split ParseGetLine into separate functions
|
||||
fix garbled output for failed shell command
|
||||
var.c: remove redundant assignment in ApplyModifier_SysV
|
||||
error out on unknown variable modifiers at parse time
|
||||
remove wrong error message for indirect modifier in lint mode
|
||||
extract ApplySingleModifier from ApplyModifiers
|
||||
use FStr for memory management in Var_SetWithFlags
|
||||
extract SetVar from Var_SetWithFlags
|
||||
use FStr in VarNew
|
||||
extract string functions from ApplyModifier_To
|
||||
error out if .undef has not exactly 1 argument
|
||||
extract Var_DeleteVar from Var_Delete
|
||||
extract Var_Undef from ParseDirective
|
||||
clean up memory management for expanding variable expressions
|
||||
|
||||
2020-12-12 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* avoid %zu
|
||||
|
||||
* lst.c: avoid anonymous union
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201212
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit tests
|
||||
o inline Targ_Ignore and Targ_Silent
|
||||
o split JobFlags into separate fields
|
||||
o remove const from function parameters (left overs from refactoring)
|
||||
o eliminate boolean argument of Var_Export
|
||||
o make API of Buf_Init simpler
|
||||
o rename ParseRunOptions to ParseCommandFlags
|
||||
o replace *line with line[0]
|
||||
o compat.c: fix wrong exit status for multiple failed main targets
|
||||
refactor Compat_Run to show the error condition more clearly
|
||||
don't make .END if the main targets already failed (-k mode)
|
||||
fix exit status in -k mode if a dependency fails
|
||||
o for.c: clean up Buf_AddEscaped in .for loops
|
||||
o job.c: extract ShellWriter_ErrOn from JobPrintCommand
|
||||
make Job_Touch simpler
|
||||
refactor JobFinish
|
||||
rename Shell.exitFlag to errFlag
|
||||
move Job.xtraced to ShellWriter
|
||||
make printing of shell commands independent from the job
|
||||
rename shell flags in struct Shell
|
||||
extract JobOpenTmpFile from JobStart
|
||||
rename RunFlags to CommandFlags
|
||||
split various Job.* into separate fields
|
||||
rename commandShell to shell
|
||||
extract InitShellNameAndPath from Shell_Init
|
||||
replace signal handling macros with local functions
|
||||
replace macro MESSAGE with local function
|
||||
parse.c: error out on null bytes in makefiles
|
||||
error out on misspelled directives
|
||||
rename IFile.nextbuf to readMore
|
||||
fix undefined behavior in ParseEOF
|
||||
str.c: remove redundant call to strlen in Str_Words
|
||||
var.c: error out on misspelled .unexport-env
|
||||
error out on misspelled .export directives
|
||||
extract ExportVars from Var_Export
|
||||
extract ExportVarsExpand from Var_Export
|
||||
eliminate boolean argument of Var_Export
|
||||
fix undefined behavior when exporting ${:U }
|
||||
rename Var_ExportVars to Var_ReexportVars
|
||||
rename Var_Export1 to ExportVar
|
||||
|
||||
2020-12-06 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201206
|
||||
Merge with NetBSD make, pick up
|
||||
o more unit tests
|
||||
o inline macros for debug logging
|
||||
o use consistent variable names for list nodes
|
||||
o define constants for enum zero-values
|
||||
o dir.c: use fixed format for debug output of the directory cache
|
||||
remove Dir_InitDir
|
||||
o lst.c: inline Lst_Enqueue, Vector_Done
|
||||
o meta.c: remove unused parameter from meta_needed
|
||||
o parse.c: rename parse functions
|
||||
o suff.c: extract ExpandChildrenRegular from ExpandChildren
|
||||
o targ.c: don't concatenate identifiers in Targ_PrintType
|
||||
o var.c: remove comment decoration
|
||||
extract UnexportVars from Var_UnExport
|
||||
extract GetVarnamesToUnexport from Var_UnExport
|
||||
extract UnexportEnv from Var_UnExport
|
||||
extract UnexportVar from Var_UnExport
|
||||
move CleanEnv to UnexportVars
|
||||
replace pointer comparisons with enum
|
||||
add FStr to var.c to make memory handling simpler
|
||||
use FStr in Var_UnExport
|
||||
move type definitions in var.c to the top
|
||||
extract FreeEnvVar from Var_Parse
|
||||
extract ShuffleStrings from ApplyModifier_Order
|
||||
|
||||
2020-11-30 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201130
|
||||
Merge with NetBSD make, pick up
|
||||
o add unit tests for META MODE
|
||||
o reduce memory allocation for dirSearchPath, GNode.parents,
|
||||
GNode.children, OpenDirs
|
||||
o reduce pointer indirection for GNode.cohorts and
|
||||
GNode.implicitParents
|
||||
o remove pointer indirection from GNode.commands
|
||||
o inline Lst_ForEachUntil in meta mode
|
||||
o dir.c: fix memory leak for lstat cache in -DCLEANUP mode
|
||||
clean up memory management for CachedDirs
|
||||
fix the reference count of dotLast going negative
|
||||
add debug logging for OpenDirs_Done
|
||||
extract CacheNewDir from Dir_AddDir
|
||||
add debug logging for reference counting of CachedDir
|
||||
rename some Dir functions to SearchPath
|
||||
o job.c: rename some global variables
|
||||
o main.c: reduce memory allocation in ReadBuiltinRules
|
||||
reduce memory allocation in CmdOpts.create, CmdOpts.variables,
|
||||
CmdOpts.makefiles
|
||||
Add .MAKE.UID and .MAKE.GID
|
||||
o make.c: reduce memory allocation for/in toBeMade,
|
||||
Make_ProcessWait, Make_ExpandUse
|
||||
o meta.c: reduce memory allocation in meta_oodate
|
||||
o parse.c: reduce memory allocations for parsing dependencies and
|
||||
targets
|
||||
o suff.c: reduce memory allocation in suffix handling
|
||||
|
||||
2020-11-24 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201124
|
||||
Merge with NetBSD make, pick up
|
||||
o .MAKE.{UID,GID} represent uid and gid running make.
|
||||
o fix error handling for .BEGIN and .END dependency in -k mode
|
||||
o fix missing "Stop." after failed .END node in -k mode
|
||||
o use properly typed comparisons in boolean contexts
|
||||
o replace a few HashTable_CreateEntry with HashTable_Set
|
||||
o add HashSet type
|
||||
o compat.c: split Compat_Make into smaller functions
|
||||
extract DebugFailedTarget from Compat_RunCommand
|
||||
o dir.c: refactor Dir_UpdateMTime
|
||||
migrate CachedDir.files from HashTable to HashSet
|
||||
o make.c: add high-level API for GNode.made
|
||||
|
||||
2020-11-22 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201122
|
||||
Merge with NetBSD make, pick up
|
||||
o rename GNode.context to vars
|
||||
o suff.c: cleanup and refactor
|
||||
rename some functions and vars to better reflect usage
|
||||
add high-level API for CandidateSearcher
|
||||
o targ.c: add more debug logging for suffix handling
|
||||
o more unit tests
|
||||
o add debug logging for setting and resetting the main target
|
||||
|
||||
2020-11-17 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* VERSION (_MAKE_VERSION): 20201117
|
||||
Merge with NetBSD make, pick up
|
||||
o fix some unit-tests when dash is .SHELL
|
||||
o fix some unit-tests when .SHELL is dash
|
||||
o rename Targ_NewGN to GNode_New
|
||||
o make some GNode functions const
|
||||
o main.c: call Targ_Init before Var_Init
|
||||
|
64
FILES
64
FILES
@ -75,6 +75,8 @@ unit-tests/archive-suffix.exp
|
||||
unit-tests/archive-suffix.mk
|
||||
unit-tests/archive.exp
|
||||
unit-tests/archive.mk
|
||||
unit-tests/cmd-errors-jobs.exp
|
||||
unit-tests/cmd-errors-jobs.mk
|
||||
unit-tests/cmd-errors-lint.exp
|
||||
unit-tests/cmd-errors-lint.mk
|
||||
unit-tests/cmd-errors.exp
|
||||
@ -87,6 +89,8 @@ unit-tests/cmdline.exp
|
||||
unit-tests/cmdline.mk
|
||||
unit-tests/comment.exp
|
||||
unit-tests/comment.mk
|
||||
unit-tests/compat-error.exp
|
||||
unit-tests/compat-error.mk
|
||||
unit-tests/cond-cmp-numeric-eq.exp
|
||||
unit-tests/cond-cmp-numeric-eq.mk
|
||||
unit-tests/cond-cmp-numeric-ge.exp
|
||||
@ -105,6 +109,8 @@ unit-tests/cond-cmp-string.exp
|
||||
unit-tests/cond-cmp-string.mk
|
||||
unit-tests/cond-cmp-unary.exp
|
||||
unit-tests/cond-cmp-unary.mk
|
||||
unit-tests/cond-eof.exp
|
||||
unit-tests/cond-eof.mk
|
||||
unit-tests/cond-func-commands.exp
|
||||
unit-tests/cond-func-commands.mk
|
||||
unit-tests/cond-func-defined.exp
|
||||
@ -113,6 +119,8 @@ unit-tests/cond-func-empty.exp
|
||||
unit-tests/cond-func-empty.mk
|
||||
unit-tests/cond-func-exists.exp
|
||||
unit-tests/cond-func-exists.mk
|
||||
unit-tests/cond-func-make-main.exp
|
||||
unit-tests/cond-func-make-main.mk
|
||||
unit-tests/cond-func-make.exp
|
||||
unit-tests/cond-func-make.mk
|
||||
unit-tests/cond-func-target.exp
|
||||
@ -213,12 +221,22 @@ unit-tests/depsrc-wait.exp
|
||||
unit-tests/depsrc-wait.mk
|
||||
unit-tests/depsrc.exp
|
||||
unit-tests/depsrc.mk
|
||||
unit-tests/deptgt-begin-fail-indirect.exp
|
||||
unit-tests/deptgt-begin-fail-indirect.mk
|
||||
unit-tests/deptgt-begin-fail.exp
|
||||
unit-tests/deptgt-begin-fail.mk
|
||||
unit-tests/deptgt-begin.exp
|
||||
unit-tests/deptgt-begin.mk
|
||||
unit-tests/deptgt-default.exp
|
||||
unit-tests/deptgt-default.mk
|
||||
unit-tests/deptgt-delete_on_error.exp
|
||||
unit-tests/deptgt-delete_on_error.mk
|
||||
unit-tests/deptgt-end-fail-all.exp
|
||||
unit-tests/deptgt-end-fail-all.mk
|
||||
unit-tests/deptgt-end-fail-indirect.exp
|
||||
unit-tests/deptgt-end-fail-indirect.mk
|
||||
unit-tests/deptgt-end-fail.exp
|
||||
unit-tests/deptgt-end-fail.mk
|
||||
unit-tests/deptgt-end-jobs.exp
|
||||
unit-tests/deptgt-end-jobs.mk
|
||||
unit-tests/deptgt-end.exp
|
||||
@ -279,6 +297,8 @@ unit-tests/directive-elifnmake.exp
|
||||
unit-tests/directive-elifnmake.mk
|
||||
unit-tests/directive-else.exp
|
||||
unit-tests/directive-else.mk
|
||||
unit-tests/directive-endfor.exp
|
||||
unit-tests/directive-endfor.mk
|
||||
unit-tests/directive-endif.exp
|
||||
unit-tests/directive-endif.mk
|
||||
unit-tests/directive-error.exp
|
||||
@ -287,12 +307,22 @@ unit-tests/directive-export-env.exp
|
||||
unit-tests/directive-export-env.mk
|
||||
unit-tests/directive-export-gmake.exp
|
||||
unit-tests/directive-export-gmake.mk
|
||||
unit-tests/directive-export-impl.exp
|
||||
unit-tests/directive-export-impl.mk
|
||||
unit-tests/directive-export-literal.exp
|
||||
unit-tests/directive-export-literal.mk
|
||||
unit-tests/directive-export.exp
|
||||
unit-tests/directive-export.mk
|
||||
unit-tests/directive-for-errors.exp
|
||||
unit-tests/directive-for-errors.mk
|
||||
unit-tests/directive-for-escape.exp
|
||||
unit-tests/directive-for-escape.mk
|
||||
unit-tests/directive-for-generating-endif.exp
|
||||
unit-tests/directive-for-generating-endif.mk
|
||||
unit-tests/directive-for-lines.exp
|
||||
unit-tests/directive-for-lines.mk
|
||||
unit-tests/directive-for-null.exp
|
||||
unit-tests/directive-for-null.mk
|
||||
unit-tests/directive-for.exp
|
||||
unit-tests/directive-for.mk
|
||||
unit-tests/directive-hyphen-include.exp
|
||||
@ -315,6 +345,8 @@ unit-tests/directive-include.exp
|
||||
unit-tests/directive-include.mk
|
||||
unit-tests/directive-info.exp
|
||||
unit-tests/directive-info.mk
|
||||
unit-tests/directive-misspellings.exp
|
||||
unit-tests/directive-misspellings.mk
|
||||
unit-tests/directive-sinclude.exp
|
||||
unit-tests/directive-sinclude.mk
|
||||
unit-tests/directive-undef.exp
|
||||
@ -365,10 +397,18 @@ unit-tests/job-flags.exp
|
||||
unit-tests/job-flags.mk
|
||||
unit-tests/job-output-long-lines.exp
|
||||
unit-tests/job-output-long-lines.mk
|
||||
unit-tests/jobs-error-indirect.exp
|
||||
unit-tests/jobs-error-indirect.mk
|
||||
unit-tests/jobs-error-nested-make.exp
|
||||
unit-tests/jobs-error-nested-make.mk
|
||||
unit-tests/jobs-error-nested.exp
|
||||
unit-tests/jobs-error-nested.mk
|
||||
unit-tests/lint.exp
|
||||
unit-tests/lint.mk
|
||||
unit-tests/make-exported.exp
|
||||
unit-tests/make-exported.mk
|
||||
unit-tests/meta-cmd-cmp.exp
|
||||
unit-tests/meta-cmd-cmp.mk
|
||||
unit-tests/moderrs.exp
|
||||
unit-tests/moderrs.mk
|
||||
unit-tests/modmatch.exp
|
||||
@ -447,14 +487,20 @@ unit-tests/opt-include-dir.exp
|
||||
unit-tests/opt-include-dir.mk
|
||||
unit-tests/opt-jobs-internal.exp
|
||||
unit-tests/opt-jobs-internal.mk
|
||||
unit-tests/opt-jobs-no-action.exp
|
||||
unit-tests/opt-jobs-no-action.mk
|
||||
unit-tests/opt-jobs.exp
|
||||
unit-tests/opt-jobs.mk
|
||||
unit-tests/opt-keep-going-multiple.exp
|
||||
unit-tests/opt-keep-going-multiple.mk
|
||||
unit-tests/opt-keep-going.exp
|
||||
unit-tests/opt-keep-going.mk
|
||||
unit-tests/opt-m-include-dir.exp
|
||||
unit-tests/opt-m-include-dir.mk
|
||||
unit-tests/opt-no-action-at-all.exp
|
||||
unit-tests/opt-no-action-at-all.mk
|
||||
unit-tests/opt-no-action-runflags.exp
|
||||
unit-tests/opt-no-action-runflags.mk
|
||||
unit-tests/opt-no-action.exp
|
||||
unit-tests/opt-no-action.mk
|
||||
unit-tests/opt-query.exp
|
||||
@ -491,12 +537,14 @@ unit-tests/posix.exp
|
||||
unit-tests/posix.mk
|
||||
unit-tests/posix1.exp
|
||||
unit-tests/posix1.mk
|
||||
unit-tests/qequals.exp
|
||||
unit-tests/qequals.mk
|
||||
unit-tests/recursive.exp
|
||||
unit-tests/recursive.mk
|
||||
unit-tests/sh-dots.exp
|
||||
unit-tests/sh-dots.mk
|
||||
unit-tests/sh-errctl.exp
|
||||
unit-tests/sh-errctl.mk
|
||||
unit-tests/sh-flags.exp
|
||||
unit-tests/sh-flags.mk
|
||||
unit-tests/sh-jobs-error.exp
|
||||
unit-tests/sh-jobs-error.mk
|
||||
unit-tests/sh-jobs.exp
|
||||
@ -529,14 +577,22 @@ unit-tests/suff-clear-regular.exp
|
||||
unit-tests/suff-clear-regular.mk
|
||||
unit-tests/suff-clear-single.exp
|
||||
unit-tests/suff-clear-single.mk
|
||||
unit-tests/suff-incomplete.exp
|
||||
unit-tests/suff-incomplete.mk
|
||||
unit-tests/suff-lookup.exp
|
||||
unit-tests/suff-lookup.mk
|
||||
unit-tests/suff-main-several.exp
|
||||
unit-tests/suff-main-several.mk
|
||||
unit-tests/suff-main.exp
|
||||
unit-tests/suff-main.mk
|
||||
unit-tests/suff-phony.exp
|
||||
unit-tests/suff-phony.mk
|
||||
unit-tests/suff-rebuild.exp
|
||||
unit-tests/suff-rebuild.mk
|
||||
unit-tests/suff-self.exp
|
||||
unit-tests/suff-self.mk
|
||||
unit-tests/suff-transform-debug.exp
|
||||
unit-tests/suff-transform-debug.mk
|
||||
unit-tests/suff-transform-endless.exp
|
||||
unit-tests/suff-transform-endless.mk
|
||||
unit-tests/suff-transform-expand.exp
|
||||
@ -607,6 +663,8 @@ unit-tests/varmod-head.exp
|
||||
unit-tests/varmod-head.mk
|
||||
unit-tests/varmod-ifelse.exp
|
||||
unit-tests/varmod-ifelse.mk
|
||||
unit-tests/varmod-indirect.exp
|
||||
unit-tests/varmod-indirect.mk
|
||||
unit-tests/varmod-l-name-to-value.exp
|
||||
unit-tests/varmod-l-name-to-value.mk
|
||||
unit-tests/varmod-localtime.exp
|
||||
@ -721,6 +779,8 @@ unit-tests/varname-dot-make-ppid.exp
|
||||
unit-tests/varname-dot-make-ppid.mk
|
||||
unit-tests/varname-dot-make-save_dollars.exp
|
||||
unit-tests/varname-dot-make-save_dollars.mk
|
||||
unit-tests/varname-dot-makeflags.exp
|
||||
unit-tests/varname-dot-makeflags.mk
|
||||
unit-tests/varname-dot-makeoverrides.exp
|
||||
unit-tests/varname-dot-makeoverrides.mk
|
||||
unit-tests/varname-dot-newline.exp
|
||||
|
4
LICENSE
4
LICENSE
@ -2,11 +2,11 @@ The individual files in this distribution are copyright their
|
||||
original contributors or assignees.
|
||||
Including:
|
||||
|
||||
Copyright (c) 1993-2020, Simon J Gerraty
|
||||
Copyright (c) 1993-2021, Simon J Gerraty
|
||||
Copyright (c) 2020, Roland Illig <rillig@NetBSD.org>
|
||||
Copyright (c) 2009-2016, Juniper Networks, Inc.
|
||||
Copyright (c) 2009, John Birrell.
|
||||
Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
|
||||
Copyright (c) 1997-2021 The NetBSD Foundation, Inc.
|
||||
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
Copyright (c) 1989 by Berkeley Softworks
|
||||
Copyright (c) 1988, 1989, 1990, 1992, 1993
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: tutorial.ms,v 1.13 2017/03/01 13:05:11 kre Exp $
|
||||
.\" $NetBSD: tutorial.ms,v 1.14 2020/12/18 15:47:34 rillig Exp $
|
||||
.\" Copyright (c) 1988, 1989, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
.\"
|
||||
@ -1918,15 +1918,15 @@ Suff_FindDeps (jive.c)
|
||||
applying .l -> .c to "jive.l"
|
||||
Suff_FindDeps (jive.l)
|
||||
Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
|
||||
Examining jive.c...non-existent...out-of-date
|
||||
Examining jive.c...nonexistent...out-of-date
|
||||
--- jive.c ---
|
||||
lex jive.l
|
||||
\&.\|.\|. meaningless lex output deleted .\|.\|.
|
||||
mv lex.yy.c jive.c
|
||||
Examining jive.o...non-existent...out-of-date
|
||||
Examining jive.o...nonexistent...out-of-date
|
||||
--- jive.o ---
|
||||
cc -c jive.c
|
||||
Examining jive.out...non-existent...out-of-date
|
||||
Examining jive.out...nonexistent...out-of-date
|
||||
--- jive.out ---
|
||||
cc -o jive.out jive.o
|
||||
.DE
|
||||
@ -2871,7 +2871,7 @@ current directory. While people have suggested that PMake should read
|
||||
the directories each time, my experience suggests that the caching seldom
|
||||
causes problems. In addition, not caching the directories slows things
|
||||
down enormously because of PMake's attempts to apply transformation
|
||||
rules through non-existent files \*- the number of extra file-system
|
||||
rules through nonexistent files \*- the number of extra file-system
|
||||
searches is truly staggering, especially if many files without
|
||||
suffixes are used and the null suffix isn't changed from
|
||||
.CW .out .
|
||||
|
2
VERSION
2
VERSION
@ -1,2 +1,2 @@
|
||||
# keep this compatible with sh and make
|
||||
_MAKE_VERSION=20201117
|
||||
_MAKE_VERSION=20210110
|
||||
|
333
arch.c
333
arch.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: arch.c,v 1.177 2020/11/14 21:29:44 rillig Exp $ */
|
||||
/* $NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,7 +68,8 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Manipulate libraries, archives and their members.
|
||||
/*
|
||||
* Manipulate libraries, archives and their members.
|
||||
*
|
||||
* The first time an archive is referenced, all of its members' headers are
|
||||
* read and cached and the archive closed again. All cached archives are kept
|
||||
@ -146,12 +147,12 @@ struct ar_hdr {
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: arch.c,v 1.177 2020/11/14 21:29:44 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $");
|
||||
|
||||
typedef struct List ArchList;
|
||||
typedef struct ListNode ArchListNode;
|
||||
|
||||
static ArchList *archives; /* The archives we've already examined */
|
||||
static ArchList archives; /* The archives we've already examined */
|
||||
|
||||
typedef struct Arch {
|
||||
char *name; /* Name of archive */
|
||||
@ -218,52 +219,47 @@ ArchFree(void *ap)
|
||||
|
||||
/*
|
||||
* Parse an archive specification such as "archive.a(member1 member2.${EXT})",
|
||||
* adding nodes for the expanded members to nodeLst. Nodes are created as
|
||||
* adding nodes for the expanded members to gns. Nodes are created as
|
||||
* necessary.
|
||||
*
|
||||
* Input:
|
||||
* pp The start of the specification.
|
||||
* nodeLst The list on which to place the nodes.
|
||||
* gns The list on which to place the nodes.
|
||||
* ctxt The context in which to expand variables.
|
||||
*
|
||||
* Output:
|
||||
* return TRUE if it was a valid specification.
|
||||
* *pp Points to the first non-space after the archive spec.
|
||||
* *nodeLst Nodes for the members have been added.
|
||||
*/
|
||||
Boolean
|
||||
Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt)
|
||||
{
|
||||
char *cp; /* Pointer into line */
|
||||
GNode *gn; /* New node */
|
||||
char *libName; /* Library-part of specification */
|
||||
char *libName_freeIt = NULL;
|
||||
MFStr libName; /* Library-part of specification */
|
||||
char *memName; /* Member-part of specification */
|
||||
char saveChar; /* Ending delimiter of member-name */
|
||||
Boolean expandLibName; /* Whether the parsed libName contains
|
||||
* variable expressions that need to be
|
||||
* expanded */
|
||||
|
||||
libName = *pp;
|
||||
libName = MFStr_InitRefer(*pp);
|
||||
expandLibName = FALSE;
|
||||
|
||||
for (cp = libName; *cp != '(' && *cp != '\0';) {
|
||||
for (cp = libName.str; *cp != '(' && *cp != '\0';) {
|
||||
if (*cp == '$') {
|
||||
/*
|
||||
* Variable spec, so call the Var module to parse the puppy
|
||||
* so we can safely advance beyond it...
|
||||
*/
|
||||
/* Expand nested variable expressions. */
|
||||
/* XXX: This code can probably be shortened. */
|
||||
const char *nested_p = cp;
|
||||
void *result_freeIt;
|
||||
const char *result;
|
||||
FStr result;
|
||||
Boolean isError;
|
||||
|
||||
/* XXX: is expanded twice: once here and once below */
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
&result, &result_freeIt);
|
||||
(void)Var_Parse(&nested_p, ctxt,
|
||||
VARE_WANTRES | VARE_UNDEFERR, &result);
|
||||
/* TODO: handle errors */
|
||||
isError = result == var_Error;
|
||||
free(result_freeIt);
|
||||
isError = result.str == var_Error;
|
||||
FStr_Done(&result);
|
||||
if (isError)
|
||||
return FALSE;
|
||||
|
||||
@ -275,9 +271,11 @@ Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
|
||||
*cp++ = '\0';
|
||||
if (expandLibName) {
|
||||
(void)Var_Subst(libName, ctxt, VARE_WANTRES | VARE_UNDEFERR, &libName);
|
||||
char *expanded;
|
||||
(void)Var_Subst(libName.str, ctxt,
|
||||
VARE_WANTRES | VARE_UNDEFERR, &expanded);
|
||||
/* TODO: handle errors */
|
||||
libName_freeIt = libName;
|
||||
libName = MFStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
|
||||
@ -287,27 +285,25 @@ Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
* place and skip to the end of it (either white-space or
|
||||
* a close paren).
|
||||
*/
|
||||
Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
|
||||
Boolean doSubst = FALSE;
|
||||
|
||||
pp_skip_whitespace(&cp);
|
||||
|
||||
memName = cp;
|
||||
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
|
||||
if (*cp == '$') {
|
||||
/*
|
||||
* Variable spec, so call the Var module to parse the puppy
|
||||
* so we can safely advance beyond it...
|
||||
*/
|
||||
void *freeIt;
|
||||
const char *result;
|
||||
/* Expand nested variable expressions. */
|
||||
/* XXX: This code can probably be shortened. */
|
||||
FStr result;
|
||||
Boolean isError;
|
||||
const char *nested_p = cp;
|
||||
|
||||
(void)Var_Parse(&nested_p, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
&result, &freeIt);
|
||||
(void)Var_Parse(&nested_p, ctxt,
|
||||
VARE_WANTRES | VARE_UNDEFERR,
|
||||
&result);
|
||||
/* TODO: handle errors */
|
||||
isError = result == var_Error;
|
||||
free(freeIt);
|
||||
isError = result.str == var_Error;
|
||||
FStr_Done(&result);
|
||||
|
||||
if (isError)
|
||||
return FALSE;
|
||||
@ -321,110 +317,118 @@ Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
|
||||
/*
|
||||
* If the specification ends without a closing parenthesis,
|
||||
* chances are there's something wrong (like a missing backslash),
|
||||
* so it's better to return failure than allow such things to happen
|
||||
* chances are there's something wrong (like a missing
|
||||
* backslash), so it's better to return failure than allow
|
||||
* such things to happen
|
||||
*/
|
||||
if (*cp == '\0') {
|
||||
Parse_Error(PARSE_FATAL, "No closing parenthesis in archive specification");
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"No closing parenthesis "
|
||||
"in archive specification");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't move anywhere, we must be done
|
||||
*/
|
||||
if (cp == memName) {
|
||||
if (cp == memName)
|
||||
break;
|
||||
}
|
||||
|
||||
saveChar = *cp;
|
||||
*cp = '\0';
|
||||
|
||||
/*
|
||||
* XXX: This should be taken care of intelligently by
|
||||
* SuffExpandChildren, both for the archive and the member portions.
|
||||
* SuffExpandChildren, both for the archive and the member
|
||||
* portions.
|
||||
*/
|
||||
/*
|
||||
* If member contains variables, try and substitute for them.
|
||||
* This will slow down archive specs with dynamic sources, of course,
|
||||
* since we'll be (non-)substituting them three times, but them's
|
||||
* the breaks -- we need to do this since SuffExpandChildren calls
|
||||
* us, otherwise we could assume the thing would be taken care of
|
||||
* later.
|
||||
* This will slow down archive specs with dynamic sources, of
|
||||
* course, since we'll be (non-)substituting them three
|
||||
* times, but them's the breaks -- we need to do this since
|
||||
* SuffExpandChildren calls us, otherwise we could assume the
|
||||
* thing would be taken care of later.
|
||||
*/
|
||||
if (doSubst) {
|
||||
char *buf;
|
||||
char *sacrifice;
|
||||
char *oldMemName = memName;
|
||||
char *fullName;
|
||||
char *p;
|
||||
char *unexpandedMemName = memName;
|
||||
|
||||
(void)Var_Subst(memName, ctxt, VARE_WANTRES | VARE_UNDEFERR,
|
||||
(void)Var_Subst(memName, ctxt,
|
||||
VARE_WANTRES | VARE_UNDEFERR,
|
||||
&memName);
|
||||
/* TODO: handle errors */
|
||||
|
||||
/*
|
||||
* Now form an archive spec and recurse to deal with nested
|
||||
* variables and multi-word variable values.... The results
|
||||
* are just placed at the end of the nodeLst we're returning.
|
||||
* Now form an archive spec and recurse to deal with
|
||||
* nested variables and multi-word variable values.
|
||||
*/
|
||||
buf = sacrifice = str_concat4(libName, "(", memName, ")");
|
||||
fullName = str_concat4(libName.str, "(", memName, ")");
|
||||
p = fullName;
|
||||
|
||||
if (strchr(memName, '$') != NULL &&
|
||||
strcmp(memName, oldMemName) == 0) {
|
||||
strcmp(memName, unexpandedMemName) == 0) {
|
||||
/*
|
||||
* Must contain dynamic sources, so we can't deal with it now.
|
||||
* Just create an ARCHV node for the thing and let
|
||||
* SuffExpandChildren handle it...
|
||||
* Must contain dynamic sources, so we can't
|
||||
* deal with it now. Just create an ARCHV node
|
||||
* for the thing and let SuffExpandChildren
|
||||
* handle it.
|
||||
*/
|
||||
gn = Targ_GetNode(buf);
|
||||
gn = Targ_GetNode(fullName);
|
||||
gn->type |= OP_ARCHV;
|
||||
Lst_Append(nodeLst, gn);
|
||||
Lst_Append(gns, gn);
|
||||
|
||||
} else if (!Arch_ParseArchive(&sacrifice, nodeLst, ctxt)) {
|
||||
} else if (!Arch_ParseArchive(&p, gns, ctxt)) {
|
||||
/* Error in nested call. */
|
||||
free(buf);
|
||||
free(fullName);
|
||||
/* XXX: does unexpandedMemName leak? */
|
||||
return FALSE;
|
||||
}
|
||||
free(buf);
|
||||
free(fullName);
|
||||
/* XXX: does unexpandedMemName leak? */
|
||||
|
||||
} else if (Dir_HasWildcards(memName)) {
|
||||
StringList *members = Lst_New();
|
||||
Dir_Expand(memName, dirSearchPath, members);
|
||||
StringList members = LST_INIT;
|
||||
Dir_Expand(memName, &dirSearchPath, &members);
|
||||
|
||||
while (!Lst_IsEmpty(members)) {
|
||||
char *member = Lst_Dequeue(members);
|
||||
char *fullname = str_concat4(libName, "(", member, ")");
|
||||
while (!Lst_IsEmpty(&members)) {
|
||||
char *member = Lst_Dequeue(&members);
|
||||
char *fullname = str_concat4(libName.str, "(",
|
||||
member, ")");
|
||||
free(member);
|
||||
|
||||
gn = Targ_GetNode(fullname);
|
||||
free(fullname);
|
||||
|
||||
gn->type |= OP_ARCHV;
|
||||
Lst_Append(nodeLst, gn);
|
||||
Lst_Append(gns, gn);
|
||||
}
|
||||
Lst_Free(members);
|
||||
Lst_Done(&members);
|
||||
|
||||
} else {
|
||||
char *fullname = str_concat4(libName, "(", memName, ")");
|
||||
char *fullname = str_concat4(libName.str, "(", memName,
|
||||
")");
|
||||
gn = Targ_GetNode(fullname);
|
||||
free(fullname);
|
||||
|
||||
/*
|
||||
* We've found the node, but have to make sure the rest of the
|
||||
* world knows it's an archive member, without having to
|
||||
* constantly check for parentheses, so we type the thing with
|
||||
* the OP_ARCHV bit before we place it on the end of the
|
||||
* provided list.
|
||||
* We've found the node, but have to make sure the
|
||||
* rest of the world knows it's an archive member,
|
||||
* without having to constantly check for parentheses,
|
||||
* so we type the thing with the OP_ARCHV bit before
|
||||
* we place it on the end of the provided list.
|
||||
*/
|
||||
gn->type |= OP_ARCHV;
|
||||
Lst_Append(nodeLst, gn);
|
||||
Lst_Append(gns, gn);
|
||||
}
|
||||
if (doSubst) {
|
||||
if (doSubst)
|
||||
free(memName);
|
||||
}
|
||||
|
||||
*cp = saveChar;
|
||||
}
|
||||
|
||||
free(libName_freeIt);
|
||||
MFStr_Done(&libName);
|
||||
|
||||
cp++; /* skip the ')' */
|
||||
/* We promised that pp would be set up at the next non-space. */
|
||||
@ -433,7 +437,8 @@ Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Locate a member of an archive, given the path of the archive and the path
|
||||
/*
|
||||
* Locate a member of an archive, given the path of the archive and the path
|
||||
* of the desired member.
|
||||
*
|
||||
* Input:
|
||||
@ -449,8 +454,8 @@ Arch_ParseArchive(char **pp, GNodeList *nodeLst, GNode *ctxt)
|
||||
static struct ar_hdr *
|
||||
ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
{
|
||||
#define AR_MAX_NAME_LEN (sizeof arh.AR_NAME - 1)
|
||||
FILE *arch; /* Stream to archive */
|
||||
#define AR_MAX_NAME_LEN (sizeof arh.ar_name - 1)
|
||||
FILE *arch;
|
||||
size_t size; /* Size of archive member */
|
||||
char magic[SARMAG];
|
||||
ArchListNode *ln;
|
||||
@ -463,11 +468,9 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
* Because of space constraints and similar things, files are archived
|
||||
* using their basename, not the entire path.
|
||||
*/
|
||||
const char *lastSlash = strrchr(member, '/');
|
||||
if (lastSlash != NULL)
|
||||
member = lastSlash + 1;
|
||||
member = str_basename(member);
|
||||
|
||||
for (ln = archives->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = archives.first; ln != NULL; ln = ln->next) {
|
||||
const Arch *a = ln->datum;
|
||||
if (strcmp(a->name, archive) == 0)
|
||||
break;
|
||||
@ -487,7 +490,6 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
size_t len = strlen(member);
|
||||
|
||||
if (len > AR_MAX_NAME_LEN) {
|
||||
len = AR_MAX_NAME_LEN;
|
||||
snprintf(copy, sizeof copy, "%s", member);
|
||||
hdr = HashTable_FindValue(&ar->members, copy);
|
||||
}
|
||||
@ -497,11 +499,11 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
|
||||
if (!addToCache) {
|
||||
/*
|
||||
* Caller doesn't want the thing cached, just use ArchFindMember
|
||||
* to read the header for the member out and close down the stream
|
||||
* again. Since the archive is not to be cached, we assume there's
|
||||
* no need to allocate extra room for the header we're returning,
|
||||
* so just declare it static.
|
||||
* Caller doesn't want the thing cached, just use
|
||||
* ArchFindMember to read the header for the member out and
|
||||
* close down the stream again. Since the archive is not to be
|
||||
* cached, we assume there's no need to allocate extra room
|
||||
* for the header we're returning, so just declare it static.
|
||||
*/
|
||||
static struct ar_hdr sarh;
|
||||
|
||||
@ -548,8 +550,9 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
/*
|
||||
* We need to advance the stream's pointer to the start of the
|
||||
* next header. Files are padded with newlines to an even-byte
|
||||
* boundary, so we need to extract the size of the file from the
|
||||
* 'size' field of the header and round it up during the seek.
|
||||
* boundary, so we need to extract the size of the file from
|
||||
* the 'size' field of the header and round it up during the
|
||||
* seek.
|
||||
*/
|
||||
arh.AR_SIZE[sizeof arh.AR_SIZE - 1] = '\0';
|
||||
size = (size_t)strtol(arh.AR_SIZE, NULL, 10);
|
||||
@ -562,12 +565,11 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
|
||||
#ifdef SVR4ARCHIVES
|
||||
/*
|
||||
* svr4 names are slash terminated. Also svr4 extended AR format.
|
||||
* svr4 names are slash-terminated.
|
||||
* Also svr4 extended the AR format.
|
||||
*/
|
||||
if (memName[0] == '/') {
|
||||
/*
|
||||
* svr4 magic mode; handle it
|
||||
*/
|
||||
/* svr4 magic mode; handle it */
|
||||
switch (ArchSVR4Entry(ar, memName, size, arch)) {
|
||||
case -1: /* Invalid data */
|
||||
goto badarch;
|
||||
@ -600,13 +602,16 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
if (fseek(arch, -elen, SEEK_CUR) != 0)
|
||||
goto badarch;
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("ArchStatMember: Extended format entry for %s\n",
|
||||
debug_printf(
|
||||
"ArchStatMember: "
|
||||
"Extended format entry for %s\n",
|
||||
memName);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
struct ar_hdr *cached_hdr = bmake_malloc(sizeof *cached_hdr);
|
||||
struct ar_hdr *cached_hdr = bmake_malloc(
|
||||
sizeof *cached_hdr);
|
||||
memcpy(cached_hdr, &arh, sizeof arh);
|
||||
HashTable_Set(&ar->members, memName, cached_hdr);
|
||||
}
|
||||
@ -617,7 +622,7 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
|
||||
fclose(arch);
|
||||
|
||||
Lst_Append(archives, ar);
|
||||
Lst_Append(&archives, ar);
|
||||
|
||||
/*
|
||||
* Now that the archive has been read and cached, we can look into
|
||||
@ -634,22 +639,18 @@ ArchStatMember(const char *archive, const char *member, Boolean addToCache)
|
||||
}
|
||||
|
||||
#ifdef SVR4ARCHIVES
|
||||
/*-
|
||||
*-----------------------------------------------------------------------
|
||||
* ArchSVR4Entry --
|
||||
/*
|
||||
* Parse an SVR4 style entry that begins with a slash.
|
||||
* If it is "//", then load the table of filenames
|
||||
* If it is "//", then load the table of filenames.
|
||||
* If it is "/<offset>", then try to substitute the long file name
|
||||
* from offset of a table previously read.
|
||||
* If a table is read, the file pointer is moved to the next archive
|
||||
* member.
|
||||
* If a table is read, the file pointer is moved to the next archive member.
|
||||
*
|
||||
* Results:
|
||||
* -1: Bad data in archive
|
||||
* 0: A table was loaded from the file
|
||||
* 1: Name was successfully substituted from table
|
||||
* 2: Name was not successfully substituted from table
|
||||
*-----------------------------------------------------------------------
|
||||
*/
|
||||
static int
|
||||
ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
@ -663,7 +664,8 @@ ArchSVR4Entry(Arch *ar, char *inout_name, size_t size, FILE *arch)
|
||||
strncmp(inout_name, ARLONGNAMES2, sizeof ARLONGNAMES2 - 1) == 0) {
|
||||
|
||||
if (ar->fnametab != NULL) {
|
||||
DEBUG0(ARCH, "Attempted to redefine an SVR4 name table\n");
|
||||
DEBUG0(ARCH,
|
||||
"Attempted to redefine an SVR4 name table\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -737,7 +739,8 @@ ArchiveMember_HasName(const struct ar_hdr *hdr,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Locate a member of an archive, given the path of the archive and the path
|
||||
/*
|
||||
* Locate a member of an archive, given the path of the archive and the path
|
||||
* of the desired member.
|
||||
*
|
||||
* Input:
|
||||
@ -762,8 +765,7 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
FILE *arch; /* Stream to archive */
|
||||
int size; /* Size of archive member */
|
||||
char magic[SARMAG];
|
||||
size_t len, tlen;
|
||||
const char *lastSlash;
|
||||
size_t len;
|
||||
|
||||
arch = fopen(archive, mode);
|
||||
if (arch == NULL)
|
||||
@ -783,18 +785,14 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
* Because of space constraints and similar things, files are archived
|
||||
* using their basename, not the entire path.
|
||||
*/
|
||||
lastSlash = strrchr(member, '/');
|
||||
if (lastSlash != NULL)
|
||||
member = lastSlash + 1;
|
||||
member = str_basename(member);
|
||||
|
||||
len = tlen = strlen(member);
|
||||
if (len > sizeof out_arh->AR_NAME) {
|
||||
tlen = sizeof out_arh->AR_NAME;
|
||||
}
|
||||
len = strlen(member);
|
||||
|
||||
while (fread(out_arh, sizeof *out_arh, 1, arch) == 1) {
|
||||
|
||||
if (strncmp(out_arh->AR_FMAG, ARFMAG, sizeof out_arh->AR_FMAG) != 0) {
|
||||
if (strncmp(out_arh->AR_FMAG, ARFMAG,
|
||||
sizeof out_arh->AR_FMAG) != 0) {
|
||||
/*
|
||||
* The header is bogus, so the archive is bad
|
||||
* and there's no way we can recover...
|
||||
@ -810,14 +808,15 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
|
||||
if (ArchiveMember_HasName(out_arh, member, len)) {
|
||||
/*
|
||||
* To make life easier for callers that want to update the
|
||||
* archive, we reposition the file at the start
|
||||
* of the header we just read before we return the stream.
|
||||
* In a more general situation, it might be better to leave
|
||||
* the file at the actual member, rather than its header, but
|
||||
* not here.
|
||||
* To make life easier for callers that want to update
|
||||
* the archive, we reposition the file at the start of
|
||||
* the header we just read before we return the
|
||||
* stream. In a more general situation, it might be
|
||||
* better to leave the file at the actual member,
|
||||
* rather than its header, but not here.
|
||||
*/
|
||||
if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) != 0) {
|
||||
if (fseek(arch, -(long)sizeof *out_arh, SEEK_CUR) !=
|
||||
0) {
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
}
|
||||
@ -829,9 +828,9 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
* BSD 4.4 extended AR format: #1/<namelen>, with name as the
|
||||
* first <namelen> bytes of the file
|
||||
*/
|
||||
if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
|
||||
ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))
|
||||
{
|
||||
if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) ==
|
||||
0 &&
|
||||
(ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))) {
|
||||
int elen = atoi(&out_arh->AR_NAME[sizeof AR_EFMT1 - 1]);
|
||||
char ename[MAXPATHLEN + 1];
|
||||
|
||||
@ -845,11 +844,14 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
}
|
||||
ename[elen] = '\0';
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("ArchFindMember: Extended format entry for %s\n",
|
||||
debug_printf(
|
||||
"ArchFindMember: "
|
||||
"Extended format entry for %s\n",
|
||||
ename);
|
||||
if (strncmp(ename, member, len) == 0) {
|
||||
/* Found as extended name */
|
||||
if (fseek(arch, -(long)sizeof(struct ar_hdr) - elen,
|
||||
if (fseek(arch,
|
||||
-(long)sizeof(struct ar_hdr) - elen,
|
||||
SEEK_CUR) != 0) {
|
||||
fclose(arch);
|
||||
return NULL;
|
||||
@ -882,7 +884,8 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Touch a member of an archive, on disk.
|
||||
/*
|
||||
* Touch a member of an archive, on disk.
|
||||
* The GNode's modification time is left as-is.
|
||||
*
|
||||
* The st_mtime of the entire archive is also changed.
|
||||
@ -900,7 +903,8 @@ Arch_Touch(GNode *gn)
|
||||
FILE *f;
|
||||
struct ar_hdr arh;
|
||||
|
||||
f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh, "r+");
|
||||
f = ArchFindMember(GNode_VarArchive(gn), GNode_VarMember(gn), &arh,
|
||||
"r+");
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
@ -909,11 +913,14 @@ Arch_Touch(GNode *gn)
|
||||
fclose(f); /* TODO: handle errors */
|
||||
}
|
||||
|
||||
/* Given a node which represents a library, touch the thing, making sure that
|
||||
/*
|
||||
* Given a node which represents a library, touch the thing, making sure that
|
||||
* the table of contents is also touched.
|
||||
*
|
||||
* Both the modification time of the library and of the RANLIBMAG member are
|
||||
* set to 'now'. */
|
||||
* set to 'now'.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
|
||||
{
|
||||
@ -935,8 +942,10 @@ Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Update the mtime of the GNode with the mtime from the archive member on
|
||||
* disk (or in the cache). */
|
||||
/*
|
||||
* Update the mtime of the GNode with the mtime from the archive member on
|
||||
* disk (or in the cache).
|
||||
*/
|
||||
void
|
||||
Arch_UpdateMTime(GNode *gn)
|
||||
{
|
||||
@ -949,23 +958,26 @@ Arch_UpdateMTime(GNode *gn)
|
||||
gn->mtime = 0;
|
||||
}
|
||||
|
||||
/* Given a non-existent archive member's node, update gn->mtime from its
|
||||
* archived form, if it exists. */
|
||||
/*
|
||||
* Given a nonexistent archive member's node, update gn->mtime from its
|
||||
* archived form, if it exists.
|
||||
*/
|
||||
void
|
||||
Arch_UpdateMemberMTime(GNode *gn)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = gn->parents->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = gn->parents.first; ln != NULL; ln = ln->next) {
|
||||
GNode *pgn = ln->datum;
|
||||
|
||||
if (pgn->type & OP_ARCHV) {
|
||||
/*
|
||||
* If the parent is an archive specification and is being made
|
||||
* and its member's name matches the name of the node we were
|
||||
* given, record the modification time of the parent in the
|
||||
* child. We keep searching its parents in case some other
|
||||
* parent requires this child to exist...
|
||||
* If the parent is an archive specification and is
|
||||
* being made and its member's name matches the name
|
||||
* of the node we were given, record the modification
|
||||
* time of the parent in the child. We keep searching
|
||||
* its parents in case some other parent requires this
|
||||
* child to exist.
|
||||
*/
|
||||
const char *nameStart = strchr(pgn->name, '(') + 1;
|
||||
const char *nameEnd = strchr(nameStart, ')');
|
||||
@ -978,8 +990,8 @@ Arch_UpdateMemberMTime(GNode *gn)
|
||||
}
|
||||
} else if (pgn->flags & REMAKE) {
|
||||
/*
|
||||
* Something which isn't a library depends on the existence of
|
||||
* this target, so it needs to exist.
|
||||
* Something which isn't a library depends on the
|
||||
* existence of this target, so it needs to exist.
|
||||
*/
|
||||
gn->mtime = 0;
|
||||
break;
|
||||
@ -987,7 +999,8 @@ Arch_UpdateMemberMTime(GNode *gn)
|
||||
}
|
||||
}
|
||||
|
||||
/* Search for a library along the given search path.
|
||||
/*
|
||||
* Search for a library along the given search path.
|
||||
*
|
||||
* The node's 'path' field is set to the found path (including the
|
||||
* actual file name, not -l...). If the system can handle the -L
|
||||
@ -1015,7 +1028,8 @@ Arch_FindLib(GNode *gn, SearchPath *path)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Decide if a node with the OP_LIB attribute is out-of-date. Called from
|
||||
/*
|
||||
* Decide if a node with the OP_LIB attribute is out-of-date. Called from
|
||||
* GNode_IsOODate to make its life easier.
|
||||
* The library is cached if it hasn't been already.
|
||||
*
|
||||
@ -1051,9 +1065,9 @@ Arch_LibOODate(GNode *gn)
|
||||
|
||||
if (gn->type & OP_PHONY) {
|
||||
oodate = TRUE;
|
||||
} else if (!GNode_IsTarget(gn) && Lst_IsEmpty(gn->children)) {
|
||||
} else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) {
|
||||
oodate = FALSE;
|
||||
} else if ((!Lst_IsEmpty(gn->children) && gn->youngestChild == NULL) ||
|
||||
} else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
|
||||
(gn->mtime > now) ||
|
||||
(gn->youngestChild != NULL &&
|
||||
gn->mtime < gn->youngestChild->mtime)) {
|
||||
@ -1061,7 +1075,7 @@ Arch_LibOODate(GNode *gn)
|
||||
} else {
|
||||
#ifdef RANLIBMAG
|
||||
struct ar_hdr *arh; /* Header for __.SYMDEF */
|
||||
int modTimeTOC; /* The table-of-contents's mod time */
|
||||
int modTimeTOC; /* The table-of-contents' mod time */
|
||||
|
||||
arh = ArchStatMember(gn->path, RANLIBMAG, FALSE);
|
||||
|
||||
@ -1070,11 +1084,14 @@ Arch_LibOODate(GNode *gn)
|
||||
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("%s modified %s...",
|
||||
RANLIBMAG, Targ_FmtTime(modTimeTOC));
|
||||
RANLIBMAG,
|
||||
Targ_FmtTime(modTimeTOC));
|
||||
oodate = gn->youngestChild == NULL ||
|
||||
gn->youngestChild->mtime > modTimeTOC;
|
||||
} else {
|
||||
/* A library without a table of contents is out-of-date. */
|
||||
/*
|
||||
* A library without a table of contents is out-of-date.
|
||||
*/
|
||||
if (DEBUG(ARCH) || DEBUG(MAKE))
|
||||
debug_printf("no toc...");
|
||||
oodate = TRUE;
|
||||
@ -1090,7 +1107,7 @@ Arch_LibOODate(GNode *gn)
|
||||
void
|
||||
Arch_Init(void)
|
||||
{
|
||||
archives = Lst_New();
|
||||
Lst_Init(&archives);
|
||||
}
|
||||
|
||||
/* Clean up the archives module. */
|
||||
@ -1098,7 +1115,7 @@ void
|
||||
Arch_End(void)
|
||||
{
|
||||
#ifdef CLEANUP
|
||||
Lst_Destroy(archives, ArchFree);
|
||||
Lst_DoneCall(&archives, ArchFree);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
16
bmake.1
16
bmake.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd November 14, 2020
|
||||
.Dd December 22, 2020
|
||||
.Dt BMAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1019,6 +1019,12 @@ If set to false,
|
||||
becomes
|
||||
.Ql $
|
||||
per normal evaluation rules.
|
||||
.It Va .MAKE.UID
|
||||
The user-id running
|
||||
.Nm .
|
||||
.It Va .MAKE.GID
|
||||
The group-id running
|
||||
.Nm .
|
||||
.It Va MAKE_PRINT_VAR_ON_ERROR
|
||||
When
|
||||
.Nm
|
||||
@ -1743,9 +1749,9 @@ The same as
|
||||
except that variables in the value are not expanded.
|
||||
.It Ic .info Ar message
|
||||
The message is printed along with the name of the makefile and line number.
|
||||
.It Ic .undef Ar variable
|
||||
Un-define the specified global variable.
|
||||
Only global variables may be un-defined.
|
||||
.It Ic .undef Ar variable ...
|
||||
Un-define the specified global variables.
|
||||
Only global variables can be un-defined.
|
||||
.It Ic .unexport Ar variable ...
|
||||
The opposite of
|
||||
.Ql .export .
|
||||
|
248
bmake.cat1
248
bmake.cat1
@ -45,10 +45,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
`-' they are added to the [4mMAKEFLAGS[24m environment variable and will
|
||||
be processed by any child make processes. By default, debugging
|
||||
information is printed to standard error, but this can be changed
|
||||
using the [4mF[24m debugging flag. The debugging output is always
|
||||
unbuffered; in addition, if debugging is enabled but debugging
|
||||
output is not directed to standard output, then the standard out-
|
||||
put is line buffered. [4mFlags[24m is one or more of the following:
|
||||
using the [4mF[24m debugging flag. The debugging output is always un-
|
||||
buffered; in addition, if debugging is enabled but debugging out-
|
||||
put is not directed to standard output, then the standard output
|
||||
is line buffered. [4mFlags[24m is one or more of the following:
|
||||
|
||||
[4mA[24m Print all possible debugging information; equivalent to
|
||||
specifying all of the debugging flags.
|
||||
@ -178,8 +178,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
can be used multiple times to form a search path. This path will
|
||||
override the default system include path: /usr/share/mk. Fur-
|
||||
thermore the system include path will be appended to the search
|
||||
path used for "[4mfile[24m"-style include statements (see the [1m-I[0m
|
||||
option).
|
||||
path used for "[4mfile[24m"-style include statements (see the [1m-I [22mop-
|
||||
tion).
|
||||
|
||||
If a file or directory name in the [1m-m [22margument (or the
|
||||
MAKESYSPATH environment variable) starts with the string ".../"
|
||||
@ -232,9 +232,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4m.MAKE.EXPAND_VARIABLES[24m is set to true and the [1m-dV [22moption has not
|
||||
been used to override it. Note that loop-local and target-local
|
||||
variables, as well as values taken temporarily by global vari-
|
||||
ables during makefile processing, are not accessible via this
|
||||
option. The [1m-dv [22mdebug mode can be used to see these at the cost
|
||||
of generating substantial extraneous output.
|
||||
ables during makefile processing, are not accessible via this op-
|
||||
tion. The [1m-dv [22mdebug mode can be used to see these at the cost of
|
||||
generating substantial extraneous output.
|
||||
|
||||
[1m-v [4m[22mvariable[0m
|
||||
Like [1m-V [22mbut the variable is always expanded to its complete
|
||||
@ -247,8 +247,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m-X [22mDon't export variables passed on the command line to the environ-
|
||||
ment individually. Variables passed on the command line are
|
||||
still exported via the [4mMAKEFLAGS[24m environment variable. This
|
||||
option may be useful on systems which have a small limit on the
|
||||
still exported via the [4mMAKEFLAGS[24m environment variable. This op-
|
||||
tion may be useful on systems which have a small limit on the
|
||||
size of command arguments.
|
||||
|
||||
[4mvariable=value[0m
|
||||
@ -268,9 +268,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1mFILE DEPENDENCY SPECIFICATIONS[0m
|
||||
Dependency lines consist of one or more targets, an operator, and zero or
|
||||
more sources. This creates a relationship where the targets ``depend''
|
||||
on the sources and are customarily created from them. A target is con-
|
||||
sidered out-of-date if it does not exist, or if its modification time is
|
||||
more sources. This creates a relationship where the targets "depend" on
|
||||
the sources and are customarily created from them. A target is consid-
|
||||
ered out-of-date if it does not exist, or if its modification time is
|
||||
less than that of any of its sources. An out-of-date target will be re-
|
||||
created, but not until all sources have been examined and themselves re-
|
||||
created as needed. Three operators may be used:
|
||||
@ -285,20 +285,20 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
out of date.
|
||||
|
||||
[1m:: [22mAny dependency line may have attached shell commands, but each one
|
||||
is handled independently: its sources are considered and the
|
||||
attached shell commands are run if the target is out of date with
|
||||
respect to (only) those sources. Thus, different groups of the
|
||||
attached shell commands may be run depending on the circumstances.
|
||||
is handled independently: its sources are considered and the at-
|
||||
tached shell commands are run if the target is out of date with re-
|
||||
spect to (only) those sources. Thus, different groups of the at-
|
||||
tached shell commands may be run depending on the circumstances.
|
||||
Furthermore, unlike [1m:, [22mfor dependency lines with no sources, the
|
||||
attached shell commands are always run. Also unlike [1m:, [22mthe target
|
||||
will not be removed if [1mbmake [22mis interrupted.
|
||||
All dependency lines mentioning a particular target must use the same
|
||||
operator.
|
||||
All dependency lines mentioning a particular target must use the same op-
|
||||
erator.
|
||||
|
||||
Targets and sources may contain the shell wildcard values `?', `*', `[]',
|
||||
and `{}'. The values `?', `*', and `[]' may only be used as part of the
|
||||
final component of the target or source, and must be used to describe
|
||||
existing files. The value `{}' need not necessarily be used to describe
|
||||
final component of the target or source, and must be used to describe ex-
|
||||
isting files. The value `{}' need not necessarily be used to describe
|
||||
existing files. Expansion is in directory order, not alphabetically as
|
||||
done in the shell.
|
||||
|
||||
@ -306,8 +306,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
Each target may have associated with it one or more lines of shell com-
|
||||
mands, normally used to create the target. Each of the lines in this
|
||||
script [4mmust[24m be preceded by a tab. (For historical reasons, spaces are
|
||||
not accepted.) While targets can appear in many dependency lines if
|
||||
desired, by default only one of these rules may be followed by a creation
|
||||
not accepted.) While targets can appear in many dependency lines if de-
|
||||
sired, by default only one of these rules may be followed by a creation
|
||||
script. If the `[1m::[22m' operator is used, however, all rules may include
|
||||
scripts and the scripts are executed in the order found.
|
||||
|
||||
@ -333,10 +333,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
failed.
|
||||
|
||||
Makefiles should be written so that the mode of [1mbmake [22moperation does not
|
||||
change their behavior. For example, any command which needs to use
|
||||
``cd'' or ``chdir'' without potentially changing the directory for subse-
|
||||
quent commands should be put in parentheses so it executes in a subshell.
|
||||
To force the use of one shell, escape the line breaks so as to make the
|
||||
change their behavior. For example, any command which needs to use "cd"
|
||||
or "chdir" without potentially changing the directory for subsequent com-
|
||||
mands should be put in parentheses so it executes in a subshell. To
|
||||
force the use of one shell, escape the line breaks so as to make the
|
||||
whole script one command. For example:
|
||||
|
||||
avoid-chdir-side-effects:
|
||||
@ -373,13 +373,13 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4mnot[24m expanded. This can cause problems when variable modifiers
|
||||
are used.
|
||||
|
||||
[1m!= [22mExpand the value and pass it to the shell for execution and
|
||||
assign the result to the variable. Any newlines in the result
|
||||
are replaced with spaces.
|
||||
[1m!= [22mExpand the value and pass it to the shell for execution and as-
|
||||
sign the result to the variable. Any newlines in the result are
|
||||
replaced with spaces.
|
||||
|
||||
Any white-space before the assigned [4mvalue[24m is removed; if the value is
|
||||
being appended, a single space is inserted between the previous contents
|
||||
of the variable and the appended value.
|
||||
Any white-space before the assigned [4mvalue[24m is removed; if the value is be-
|
||||
ing appended, a single space is inserted between the previous contents of
|
||||
the variable and the appended value.
|
||||
|
||||
Variables are expanded by surrounding the variable name with either curly
|
||||
braces (`{}') or parentheses (`()') and preceding it with a dollar sign
|
||||
@ -403,7 +403,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
2. Variables in shell commands are expanded when the shell command is
|
||||
executed.
|
||||
|
||||
3. ``.for'' loop index variables are expanded on each loop iteration.
|
||||
3. ".for" loop index variables are expanded on each loop iteration.
|
||||
Note that other variables are not expanded inside loops so the fol-
|
||||
lowing example code:
|
||||
|
||||
@ -423,9 +423,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
1 2 3
|
||||
3 3 3
|
||||
|
||||
Because while ${a} contains ``1 2 3'' after the loop is executed,
|
||||
${b} contains ``${j} ${j} ${j}'' which expands to ``3 3 3'' since
|
||||
after the loop completes ${j} contains ``3''.
|
||||
Because while ${a} contains "1 2 3" after the loop is executed, ${b}
|
||||
contains "${j} ${j} ${j}" which expands to "3 3 3" since after the
|
||||
loop completes ${j} contains "3".
|
||||
|
||||
[1mVariable classes[0m
|
||||
The four different classes of variables (in order of increasing prece-
|
||||
@ -454,8 +454,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[4m.IMPSRC[24m In suffix-transformation rules, the name/path of the
|
||||
source from which the target is to be transformed (the
|
||||
``implied'' source); also known as `[4m<[24m'. It is not
|
||||
defined in explicit rules.
|
||||
"implied" source); also known as `[4m<[24m'. It is not defined
|
||||
in explicit rules.
|
||||
|
||||
[4m.MEMBER[24m The name of the archive member; also known as `[4m%[24m'.
|
||||
|
||||
@ -546,10 +546,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[4m.MAKE.LEVEL[24m The recursion depth of [1mbmake[22m. The initial instance of
|
||||
[1mbmake [22mwill be 0, and an incremented value is put into the
|
||||
environment to be seen by the next generation. This
|
||||
allows tests like: .if ${.MAKE.LEVEL} == 0 to protect
|
||||
things which should only be evaluated in the initial
|
||||
instance of [1mbmake[22m.
|
||||
environment to be seen by the next generation. This al-
|
||||
lows tests like: .if ${.MAKE.LEVEL} == 0 to protect
|
||||
things which should only be evaluated in the initial in-
|
||||
stance of [1mbmake[22m.
|
||||
|
||||
[4m.MAKE.MAKEFILE_PREFERENCE[0m
|
||||
The ordered list of makefile names (default `[4mmakefile[24m',
|
||||
@ -647,9 +647,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
Building ${.TARGET:H:tA}/${.TARGET:T}
|
||||
|
||||
[4m.MAKEOVERRIDES[24m This variable is used to record the names of variables
|
||||
assigned to on the command line, so that they may be
|
||||
exported as part of `MAKEFLAGS'. This behavior can be
|
||||
disabled by assigning an empty value to `[4m.MAKEOVERRIDES[24m'
|
||||
assigned to on the command line, so that they may be ex-
|
||||
ported as part of `MAKEFLAGS'. This behavior can be dis-
|
||||
abled by assigning an empty value to `[4m.MAKEOVERRIDES[24m'
|
||||
within a makefile. Extra variables can be exported from
|
||||
a makefile by appending their names to `[4m.MAKEOVERRIDES[24m'.
|
||||
`MAKEFLAGS' is re-exported whenever `[4m.MAKEOVERRIDES[24m' is
|
||||
@ -668,8 +668,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
value should be a boolean that controls whether `$$' are
|
||||
preserved when doing `:=' assignments. The default is
|
||||
false, for backwards compatibility. Set to true for com-
|
||||
patability with other makes. If set to false, `$$'
|
||||
becomes `$' per normal evaluation rules.
|
||||
patability with other makes. If set to false, `$$' be-
|
||||
comes `$' per normal evaluation rules.
|
||||
|
||||
[4m.MAKE.UID[24m The user-id running [1mbmake[22m.
|
||||
|
||||
[4m.MAKE.GID[24m The group-id running [1mbmake[22m.
|
||||
|
||||
[4mMAKE_PRINT_VAR_ON_ERROR[0m
|
||||
When [1mbmake [22mstops due to an error, it sets `[4m.ERROR_TARGET[24m'
|
||||
@ -733,8 +737,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[4m.PARSEFILE[24m The basename of the current `[4mMakefile[24m' being parsed.
|
||||
This variable and `[4m.PARSEDIR[24m' are both set only while the
|
||||
`[4mMakefiles[24m' are being parsed. If you want to retain
|
||||
their current values, assign them to a variable using
|
||||
assignment with expansion: (`[1m:=[22m').
|
||||
their current values, assign them to a variable using as-
|
||||
signment with expansion: (`[1m:=[22m').
|
||||
|
||||
[4m.PATH[24m A variable that represents the list of directories that
|
||||
[1mbmake [22mwill search for files. The search list should be
|
||||
@ -756,14 +760,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
.TARGETS The list of targets explicitly specified on the command
|
||||
line, if any.
|
||||
|
||||
VPATH Colon-separated (``:'') lists of directories that [1mbmake[0m
|
||||
VPATH Colon-separated (":") lists of directories that [1mbmake[0m
|
||||
will search for files. The variable is supported for
|
||||
compatibility with old make programs only, use `[4m.PATH[24m'
|
||||
instead.
|
||||
|
||||
[1mVariable modifiers[0m
|
||||
Variable expansion may be modified to select or modify each word of the
|
||||
variable (where a ``word'' is white-space delimited sequence of charac-
|
||||
variable (where a "word" is white-space delimited sequence of charac-
|
||||
ters). The general format of a variable expansion is as follows:
|
||||
|
||||
${variable[:modifier[:...]]}
|
||||
@ -831,8 +835,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
passed safely to the shell.
|
||||
|
||||
[1m:q [22mQuotes every shell meta-character in the variable, and also doubles
|
||||
`$' characters so that it can be passed safely through recursive
|
||||
invocations of [1mbmake[22m. This is equivalent to: `:S/\$/&&/g:Q'.
|
||||
`$' characters so that it can be passed safely through recursive in-
|
||||
vocations of [1mbmake[22m. This is equivalent to: `:S/\$/&&/g:Q'.
|
||||
|
||||
[1m:R [22mReplaces each word in the variable with everything but its suffix.
|
||||
|
||||
@ -872,11 +876,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m:S[22m/[4mold_string[24m/[4mnew_string[24m/[[1m1gW[22m]
|
||||
Modifies the first occurrence of [4mold_string[24m in each word of the
|
||||
variable's value, replacing it with [4mnew_string[24m. If a `g' is
|
||||
appended to the last delimiter of the pattern, all occurrences in
|
||||
each word are replaced. If a `1' is appended to the last delimiter
|
||||
of the pattern, only the first occurrence is affected. If a `W' is
|
||||
appended to the last delimiter of the pattern, then the value is
|
||||
variable's value, replacing it with [4mnew_string[24m. If a `g' is ap-
|
||||
pended to the last delimiter of the pattern, all occurrences in each
|
||||
word are replaced. If a `1' is appended to the last delimiter of
|
||||
the pattern, only the first occurrence is affected. If a `W' is ap-
|
||||
pended to the last delimiter of the pattern, then the value is
|
||||
treated as a single word (possibly containing embedded white space).
|
||||
If [4mold_string[24m begins with a caret (`^'), [4mold_string[24m is anchored at
|
||||
the beginning of each word. If [4mold_string[24m ends with a dollar sign
|
||||
@ -927,8 +931,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
This is the AT&T System V UNIX style variable substitution. It must
|
||||
be the last modifier specified. If [4mold_string[24m or [4mnew_string[24m do not
|
||||
contain the pattern matching character [4m%[24m then it is assumed that
|
||||
they are anchored at the end of each word, so only suffixes or
|
||||
entire words may be replaced. Otherwise [4m%[24m is the substring of
|
||||
they are anchored at the end of each word, so only suffixes or en-
|
||||
tire words may be replaced. Otherwise [4m%[24m is the substring of
|
||||
[4mold_string[24m to be replaced in [4mnew_string[24m. If only [4mold_string[24m con-
|
||||
tains the pattern matching character [4m%[24m, and [4mold_string[24m matches, then
|
||||
the result is the [4mnew_string[24m. If only the [4mnew_string[24m contains the
|
||||
@ -971,8 +975,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
[1m:U[4m[22mnewval[0m
|
||||
If the variable is undefined, [4mnewval[24m is the value. If the variable
|
||||
is defined, the existing value is returned. This is another ODE
|
||||
make feature. It is handy for setting per-target CFLAGS for
|
||||
instance:
|
||||
make feature. It is handy for setting per-target CFLAGS for in-
|
||||
stance:
|
||||
${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}}
|
||||
If a value is only required if the variable is undefined, use:
|
||||
${VAR:D:Unewval}
|
||||
@ -1027,8 +1031,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
gers (where index 1 represents the first word), and backwards using
|
||||
negative integers (where index -1 represents the last word).
|
||||
|
||||
The [4mrange[24m is subjected to variable expansion, and the expanded
|
||||
result is then interpreted as follows:
|
||||
The [4mrange[24m is subjected to variable expansion, and the expanded re-
|
||||
sult is then interpreted as follows:
|
||||
|
||||
[4mindex[24m Selects a single word from the value.
|
||||
|
||||
@ -1037,8 +1041,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
`[1m:[2..-1][22m' selects all words from the second word to the last
|
||||
word. If [4mstart[24m is greater than [4mend[24m, then the words are out-
|
||||
put in reverse order. For example, `[1m:[-1..1][22m' selects all
|
||||
the words from last to first. If the list is already
|
||||
ordered, then this effectively reverses the list, but it is
|
||||
the words from last to first. If the list is already or-
|
||||
dered, then this effectively reverses the list, but it is
|
||||
more efficient to use `[1m:Or[22m' instead of `[1m:O:[-1..1][22m'.
|
||||
|
||||
[1m* [22mCauses subsequent modifiers to treat the value as a single
|
||||
@ -1059,18 +1063,18 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
are identified by a line beginning with a single dot (`.') character.
|
||||
Files are included with either [1m.include <[4m[22mfile[24m[1m> [22mor [1m.include "[4m[22mfile[24m[1m"[22m. Vari-
|
||||
ables between the angle brackets or double quotes are expanded to form
|
||||
the file name. If angle brackets are used, the included makefile is
|
||||
expected to be in the system makefile directory. If double quotes are
|
||||
the file name. If angle brackets are used, the included makefile is ex-
|
||||
pected to be in the system makefile directory. If double quotes are
|
||||
used, the including makefile's directory and any directories specified
|
||||
using the [1m-I [22moption are searched before the system makefile directory.
|
||||
For compatibility with other versions of [1mbmake [22m`include file ...' is also
|
||||
accepted.
|
||||
|
||||
If the include statement is written as [1m.-include [22mor as [1m.sinclude [22mthen
|
||||
errors locating and/or opening include files are ignored.
|
||||
If the include statement is written as [1m.-include [22mor as [1m.sinclude [22mthen er-
|
||||
rors locating and/or opening include files are ignored.
|
||||
|
||||
If the include statement is written as [1m.dinclude [22mnot only are errors
|
||||
locating and/or opening include files ignored, but stale dependencies
|
||||
If the include statement is written as [1m.dinclude [22mnot only are errors lo-
|
||||
cating and/or opening include files ignored, but stale dependencies
|
||||
within the included file will be ignored just like [4m.MAKE.DEPENDFILE[24m.
|
||||
|
||||
Conditional expressions are also preceded by a single dot as the first
|
||||
@ -1087,8 +1091,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
flag, so should be used with caution. For compatibility with
|
||||
other [1mbmake [22mprograms `export variable=value' is also accepted.
|
||||
|
||||
Appending a variable name to [4m.MAKE.EXPORTED[24m is equivalent to
|
||||
exporting a variable.
|
||||
Appending a variable name to [4m.MAKE.EXPORTED[24m is equivalent to ex-
|
||||
porting a variable.
|
||||
|
||||
[1m.export-env [4m[22mvariable[24m [4m...[0m
|
||||
The same as `.export', except that the variable is not appended
|
||||
@ -1103,9 +1107,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
The message is printed along with the name of the makefile and
|
||||
line number.
|
||||
|
||||
[1m.undef [4m[22mvariable[0m
|
||||
Un-define the specified global variable. Only global variables
|
||||
may be un-defined.
|
||||
[1m.undef [4m[22mvariable[24m [4m...[0m
|
||||
Un-define the specified global variables. Only global variables
|
||||
can be un-defined.
|
||||
|
||||
[1m.unexport [4m[22mvariable[24m [4m...[0m
|
||||
The opposite of `.export'. The specified global [4mvariable[24m will be
|
||||
@ -1172,7 +1176,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m|| [22mLogical OR.
|
||||
|
||||
[1m&& [22mLogical AND; of higher precedence than ``||''.
|
||||
[1m&& [22mLogical AND; of higher precedence than "||".
|
||||
|
||||
As in C, [1mbmake [22mwill only evaluate a conditional as far as is necessary to
|
||||
determine its value. Parentheses may be used to change the order of
|
||||
@ -1185,9 +1189,9 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
the variable has been defined.
|
||||
|
||||
[1mmake [22mTakes a target name as an argument and evaluates to true if the
|
||||
target was specified as part of [1mbmake[22m's command line or was
|
||||
declared the default target (either implicitly or explicitly,
|
||||
see [4m.MAIN[24m) before the line containing the conditional.
|
||||
target was specified as part of [1mbmake[22m's command line or was de-
|
||||
clared the default target (either implicitly or explicitly, see
|
||||
[4m.MAIN[24m) before the line containing the conditional.
|
||||
|
||||
[1mempty [22mTakes a variable, with possible modifiers, and evaluates to true
|
||||
if the expansion of the variable would result in an empty
|
||||
@ -1204,10 +1208,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
Takes a target name as an argument and evaluates to true if the
|
||||
target has been defined and has commands associated with it.
|
||||
|
||||
[4mExpression[24m may also be an arithmetic or string comparison. Variable
|
||||
expansion is performed on both sides of the comparison, after which the
|
||||
numerical values are compared. A value is interpreted as hexadecimal if
|
||||
it is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
|
||||
[4mExpression[24m may also be an arithmetic or string comparison. Variable ex-
|
||||
pansion is performed on both sides of the comparison, after which the nu-
|
||||
merical values are compared. A value is interpreted as hexadecimal if it
|
||||
is preceded by 0x, otherwise it is decimal; octal numbers are not sup-
|
||||
ported. The standard C relational operators are all supported. If after
|
||||
variable expansion, either the left or right hand side of a `[1m==[22m' or `[1m!=[22m'
|
||||
operator is not a numerical value, then string comparison is performed
|
||||
@ -1215,12 +1219,12 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
is assumed that the expanded variable is being compared against 0, or an
|
||||
empty string in the case of a string comparison.
|
||||
|
||||
When [1mbmake [22mis evaluating one of these conditional expressions, and it
|
||||
encounters a (white-space separated) word it doesn't recognize, either
|
||||
the ``make'' or ``defined'' expression is applied to it, depending on the
|
||||
form of the conditional. If the form is `[1m.ifdef[22m', `[1m.ifndef[22m', or `[1m.if[22m'
|
||||
the ``defined'' expression is applied. Similarly, if the form is
|
||||
`[1m.ifmake[22m' or `[1m.ifnmake[22m', the ``make'' expression is applied.
|
||||
When [1mbmake [22mis evaluating one of these conditional expressions, and it en-
|
||||
counters a (white-space separated) word it doesn't recognize, either the
|
||||
"make" or "defined" expression is applied to it, depending on the form of
|
||||
the conditional. If the form is `[1m.ifdef[22m', `[1m.ifndef[22m', or `[1m.if[22m' the
|
||||
"defined" expression is applied. Similarly, if the form is `[1m.ifmake[22m' or
|
||||
`[1m.ifnmake[22m', the "make" expression is applied.
|
||||
|
||||
If the conditional evaluates to true the parsing of the makefile contin-
|
||||
ues as before. If it evaluates to false, the following lines are
|
||||
@ -1272,8 +1276,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
always changes. If the number of commands change, though, the
|
||||
target will still be out of date. The same effect applies to
|
||||
any command line that uses the variable [4m.OODATE[24m, which can be
|
||||
used for that purpose even when not otherwise needed or
|
||||
desired:
|
||||
used for that purpose even when not otherwise needed or de-
|
||||
sired:
|
||||
|
||||
|
||||
skip-compare-for-some:
|
||||
@ -1303,8 +1307,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.PRECIOUS[0m
|
||||
When [1mbmake [22mis interrupted, it normally removes any partially
|
||||
made targets. This source prevents the target from being
|
||||
removed.
|
||||
made targets. This source prevents the target from being re-
|
||||
moved.
|
||||
|
||||
[1m.RECURSIVE[0m
|
||||
Synonym for [1m.MAKE[22m.
|
||||
@ -1324,10 +1328,10 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.WAIT [22mIf [1m.WAIT [22mappears in a dependency line, the sources that precede
|
||||
it are made before the sources that succeed it in the line.
|
||||
Since the dependents of files are not made until the file
|
||||
itself could be made, this also stops the dependents being
|
||||
built unless they are needed for another branch of the depen-
|
||||
dency tree. So given:
|
||||
Since the dependents of files are not made until the file it-
|
||||
self could be made, this also stops the dependents being built
|
||||
unless they are needed for another branch of the dependency
|
||||
tree. So given:
|
||||
|
||||
x: a .WAIT b
|
||||
echo x
|
||||
@ -1361,14 +1365,14 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
make to delete targets whose commands fail. (By default, only
|
||||
targets whose commands are interrupted during execution are
|
||||
deleted. This is the historical behavior.) This setting can be
|
||||
used to help prevent half-finished or malformed targets from
|
||||
being left around and corrupting future rebuilds.
|
||||
used to help prevent half-finished or malformed targets from be-
|
||||
ing left around and corrupting future rebuilds.
|
||||
|
||||
[1m.END [22mAny command lines attached to this target are executed after
|
||||
everything else is done.
|
||||
[1m.END [22mAny command lines attached to this target are executed after ev-
|
||||
erything else is done.
|
||||
|
||||
[1m.ERROR [22mAny command lines attached to this target are executed when
|
||||
another target fails. The [1m.ERROR_TARGET [22mvariable is set to the
|
||||
[1m.ERROR [22mAny command lines attached to this target are executed when an-
|
||||
other target fails. The [1m.ERROR_TARGET [22mvariable is set to the
|
||||
target that failed. See also [1mMAKE_PRINT_VAR_ON_ERROR[22m.
|
||||
|
||||
[1m.IGNORE [22mMark each of the sources with the [1m.IGNORE [22mattribute. If no
|
||||
@ -1425,8 +1429,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1m.PRECIOUS[0m
|
||||
Apply the [1m.PRECIOUS [22mattribute to any specified sources. If no
|
||||
sources are specified, the [1m.PRECIOUS [22mattribute is applied to
|
||||
every target in the file.
|
||||
sources are specified, the [1m.PRECIOUS [22mattribute is applied to ev-
|
||||
ery target in the file.
|
||||
|
||||
[1m.SHELL [22mSets the shell that [1mbmake [22mwill use to execute commands. The
|
||||
sources are a set of [4mfield=value[24m pairs.
|
||||
@ -1469,8 +1473,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
sources are specified, the [1m.SILENT [22mattribute is applied to every
|
||||
command in the file.
|
||||
|
||||
[1m.STALE [22mThis target gets run when a dependency file contains stale
|
||||
entries, having [4m.ALLSRC[24m set to the name of that dependency file.
|
||||
[1m.STALE [22mThis target gets run when a dependency file contains stale en-
|
||||
tries, having [4m.ALLSRC[24m set to the name of that dependency file.
|
||||
|
||||
[1m.SUFFIXES[0m
|
||||
Each source specifies a suffix to [1mbmake[22m. If no sources are
|
||||
@ -1509,8 +1513,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
The way that .for loop variables are substituted changed after NetBSD 5.0
|
||||
so that they still appear to be variable expansions. In particular this
|
||||
stops them being treated as syntax, and removes some obscure problems
|
||||
using them in .if statements.
|
||||
stops them being treated as syntax, and removes some obscure problems us-
|
||||
ing them in .if statements.
|
||||
|
||||
The way that parallel makes are scheduled changed in NetBSD 4.0 so that
|
||||
.ORDER and .WAIT apply recursively to the dependent nodes. The algo-
|
||||
@ -1518,8 +1522,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
[1mOther make dialects[0m
|
||||
Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not sup-
|
||||
port most of the features of [1mbmake [22mas described in this manual. Most
|
||||
notably:
|
||||
port most of the features of [1mbmake [22mas described in this manual. Most no-
|
||||
tably:
|
||||
|
||||
[1m+o [22mThe [1m.WAIT [22mand [1m.ORDER [22mdeclarations and most functionality per-
|
||||
taining to parallelization. (GNU make supports parallelization
|
||||
@ -1544,8 +1548,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
Some features are somewhat more portable, such as assignment with [1m+=[22m, [1m?=[22m,
|
||||
and [1m!=[22m. The [1m.PATH [22mfunctionality is based on an older feature [1mVPATH [22mfound
|
||||
in GNU make and many versions of SVR4 make; however, historically its
|
||||
behavior is too ill-defined (and too buggy) to rely upon.
|
||||
in GNU make and many versions of SVR4 make; however, historically its be-
|
||||
havior is too ill-defined (and too buggy) to rely upon.
|
||||
|
||||
The [1m$@ [22mand [1m$< [22mvariables are more or less universally portable, as is the
|
||||
[1m$(MAKE) [22mvariable. Basic use of suffix rules (for files only in the cur-
|
||||
@ -1562,11 +1566,11 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
A make command appeared in Version 7 AT&T UNIX. This make implementation
|
||||
is based on Adam De Boor's pmake program which was written for Sprite at
|
||||
Berkeley. It was designed to be a parallel distributed make running jobs
|
||||
on different machines using a daemon called ``customs''.
|
||||
on different machines using a daemon called "customs".
|
||||
|
||||
Historically the target/dependency ``FRC'' has been used to FoRCe
|
||||
rebuilding (since the target/dependency does not exist... unless someone
|
||||
creates an ``FRC'' file).
|
||||
Historically the target/dependency "FRC" has been used to FoRCe rebuild-
|
||||
ing (since the target/dependency does not exist... unless someone creates
|
||||
an "FRC" file).
|
||||
|
||||
[1mBUGS[0m
|
||||
The make syntax is difficult to parse without actually acting on the
|
||||
@ -1577,4 +1581,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
|
||||
|
||||
There is no way of escaping a space character in a filename.
|
||||
|
||||
FreeBSD 11.3 November 14, 2020 FreeBSD 11.3
|
||||
FreeBSD 13.0 December 22, 2020 FreeBSD 13.0
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
31
buf.c
31
buf.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $ */
|
||||
/* $NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -69,17 +69,17 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Automatically-expanding null-terminated buffers. */
|
||||
/* Automatically-expanding null-terminated character buffers. */
|
||||
|
||||
#include <limits.h>
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.44 2020/11/07 14:11:58 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $");
|
||||
|
||||
/* Make space in the buffer for adding a single byte. */
|
||||
/* Make space in the buffer for adding at least 16 more bytes. */
|
||||
void
|
||||
Buf_Expand_1(Buffer *buf)
|
||||
Buf_Expand(Buffer *buf)
|
||||
{
|
||||
buf->cap += buf->cap > 16 ? buf->cap : 16;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
@ -93,7 +93,8 @@ Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len)
|
||||
char *end;
|
||||
|
||||
if (__predict_false(old_len + bytes_len >= buf->cap)) {
|
||||
buf->cap += buf->cap > bytes_len + 16 ? buf->cap : bytes_len + 16;
|
||||
size_t minIncr = bytes_len + 16;
|
||||
buf->cap += buf->cap > minIncr ? buf->cap : minIncr;
|
||||
buf->data = bmake_realloc(buf->data, buf->cap);
|
||||
}
|
||||
|
||||
@ -134,11 +135,13 @@ Buf_AddInt(Buffer *buf, int n)
|
||||
Buf_AddBytes(buf, str, len);
|
||||
}
|
||||
|
||||
/* Get the data (usually a string) from the buffer.
|
||||
/*
|
||||
* Get the data (usually a string) from the buffer.
|
||||
* The returned data is valid until the next modifying operation
|
||||
* on the buffer.
|
||||
*
|
||||
* Returns the data and optionally the length of the data. */
|
||||
* Returns the data and optionally the length of the data.
|
||||
*/
|
||||
char *
|
||||
Buf_GetAll(Buffer *buf, size_t *out_len)
|
||||
{
|
||||
@ -171,9 +174,11 @@ Buf_Init(Buffer *buf)
|
||||
Buf_InitSize(buf, 256);
|
||||
}
|
||||
|
||||
/* Reset the buffer.
|
||||
/*
|
||||
* Reset the buffer.
|
||||
* If freeData is TRUE, the data from the buffer is freed as well.
|
||||
* Otherwise it is kept and returned. */
|
||||
* Otherwise it is kept and returned.
|
||||
*/
|
||||
char *
|
||||
Buf_Destroy(Buffer *buf, Boolean freeData)
|
||||
{
|
||||
@ -194,10 +199,12 @@ Buf_Destroy(Buffer *buf, Boolean freeData)
|
||||
# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */
|
||||
#endif
|
||||
|
||||
/* Reset the buffer and return its data.
|
||||
/*
|
||||
* Reset the buffer and return its data.
|
||||
*
|
||||
* If the buffer size is much greater than its content,
|
||||
* a new buffer will be allocated and the old one freed. */
|
||||
* a new buffer will be allocated and the old one freed.
|
||||
*/
|
||||
char *
|
||||
Buf_DestroyCompact(Buffer *buf)
|
||||
{
|
||||
|
6
buf.h
6
buf.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: buf.h,v 1.36 2020/11/10 00:32:12 rillig Exp $ */
|
||||
/* $NetBSD: buf.h,v 1.38 2020/12/28 15:42:53 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -91,7 +91,7 @@ typedef struct Buffer {
|
||||
#define __predict_false(x) (x)
|
||||
#endif
|
||||
|
||||
void Buf_Expand_1(Buffer *);
|
||||
void Buf_Expand(Buffer *);
|
||||
|
||||
/* Buf_AddByte adds a single byte to a buffer. */
|
||||
MAKE_INLINE void
|
||||
@ -100,7 +100,7 @@ Buf_AddByte(Buffer *buf, char byte)
|
||||
size_t old_len = buf->len++;
|
||||
char *end;
|
||||
if (__predict_false(old_len + 1 >= buf->cap))
|
||||
Buf_Expand_1(buf);
|
||||
Buf_Expand(buf);
|
||||
end = buf->data + old_len;
|
||||
end[0] = byte;
|
||||
end[1] = '\0';
|
||||
|
356
compat.c
356
compat.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: compat.c,v 1.183 2020/11/15 22:31:03 rillig Exp $ */
|
||||
/* $NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -99,7 +99,7 @@
|
||||
#include "pathnames.h"
|
||||
|
||||
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.183 2020/11/15 22:31:03 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $");
|
||||
|
||||
static GNode *curTarg = NULL;
|
||||
static pid_t compatChild;
|
||||
@ -121,7 +121,8 @@ CompatDeleteTarget(GNode *gn)
|
||||
}
|
||||
}
|
||||
|
||||
/* Interrupt the creation of the current target and remove it if it ain't
|
||||
/*
|
||||
* Interrupt the creation of the current target and remove it if it ain't
|
||||
* precious. Then exit.
|
||||
*
|
||||
* If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
|
||||
@ -162,18 +163,67 @@ CompatInterrupt(int signo)
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute the next command for a target. If the command returns an error,
|
||||
static void
|
||||
DebugFailedTarget(const char *cmd, GNode *gn)
|
||||
{
|
||||
const char *p = cmd;
|
||||
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
|
||||
/* Replace runs of whitespace with a single space, to reduce
|
||||
* the amount of whitespace for multi-line command lines. */
|
||||
while (*p != '\0') {
|
||||
if (ch_isspace(*p)) {
|
||||
debug_printf(" ");
|
||||
cpp_skip_whitespace(&p);
|
||||
} else {
|
||||
debug_printf("%c", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
}
|
||||
|
||||
static Boolean
|
||||
UseShell(const char *cmd MAKE_ATTR_UNUSED)
|
||||
{
|
||||
#if !defined(MAKE_NATIVE)
|
||||
/*
|
||||
* In a non-native build, the host environment might be weird enough
|
||||
* that it's necessary to go through a shell to get the correct
|
||||
* behaviour. Or perhaps the shell has been replaced with something
|
||||
* that does extra logging, and that should not be bypassed.
|
||||
*/
|
||||
return TRUE;
|
||||
#else
|
||||
/*
|
||||
* Search for meta characters in the command. If there are no meta
|
||||
* characters, there's no need to execute a shell to execute the
|
||||
* command.
|
||||
*
|
||||
* Additionally variable assignments and empty commands
|
||||
* go to the shell. Therefore treat '=' and ':' like shell
|
||||
* meta characters as documented in make(1).
|
||||
*/
|
||||
|
||||
return needshell(cmd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the next command for a target. If the command returns an error,
|
||||
* the node's made field is set to ERROR and creation stops.
|
||||
*
|
||||
* Input:
|
||||
* cmdp Command to execute
|
||||
* gnp Node from which the command came
|
||||
* gn Node from which the command came
|
||||
* ln List node that contains the command
|
||||
*
|
||||
* Results:
|
||||
* 0 if the command succeeded, 1 if an error occurred.
|
||||
*/
|
||||
int
|
||||
Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
|
||||
{
|
||||
char *cmdStart; /* Start of expanded command */
|
||||
char *bp;
|
||||
@ -181,10 +231,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
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 */
|
||||
WAIT_T 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
|
||||
@ -195,12 +244,6 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
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);
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
|
||||
/* TODO: handle errors */
|
||||
|
||||
@ -209,12 +252,23 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
return 0;
|
||||
}
|
||||
cmd = cmdStart;
|
||||
LstNode_Set(cmdNode, cmdStart);
|
||||
LstNode_Set(ln, cmdStart);
|
||||
|
||||
if (gn->type & OP_SAVE_CMDS) {
|
||||
GNode *endNode = Targ_GetEndNode();
|
||||
if (gn != endNode) {
|
||||
Lst_Append(endNode->commands, cmdStart);
|
||||
/*
|
||||
* Append the expanded command, to prevent the
|
||||
* local variables from being interpreted in the
|
||||
* context of the .END node.
|
||||
*
|
||||
* A probably unintended side effect of this is that
|
||||
* the expanded command will be expanded again in the
|
||||
* .END node. Therefore, a literal '$' in these
|
||||
* commands must be written as '$$$$' instead of the
|
||||
* usual '$$'.
|
||||
*/
|
||||
Lst_Append(&endNode->commands, cmdStart);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -230,7 +284,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
errCheck = FALSE;
|
||||
else if (*cmd == '+') {
|
||||
doIt = TRUE;
|
||||
if (!shellName) /* we came here from jobs */
|
||||
if (shellName == NULL) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
} else
|
||||
break;
|
||||
@ -246,31 +300,10 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
if (cmd[0] == '\0')
|
||||
return 0;
|
||||
|
||||
#if !defined(MAKE_NATIVE)
|
||||
useShell = UseShell(cmd);
|
||||
/*
|
||||
* In a non-native build, the host environment might be weird enough
|
||||
* that it's necessary to go through a shell to get the correct
|
||||
* behaviour. Or perhaps the shell has been replaced with something
|
||||
* that does extra logging, and that should not be bypassed.
|
||||
*/
|
||||
useShell = TRUE;
|
||||
#else
|
||||
/*
|
||||
* Search for meta characters in the command. If there are no meta
|
||||
* characters, there's no need to execute a shell to execute the
|
||||
* command.
|
||||
*
|
||||
* Additionally variable assignments and empty commands
|
||||
* go to the shell. Therefore treat '=' and ':' like shell
|
||||
* meta characters as documented in make(1).
|
||||
*/
|
||||
|
||||
useShell = needshell(cmd);
|
||||
#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.
|
||||
* 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 || !GNode_ShouldExecute(gn)) {
|
||||
printf("%s\n", cmd);
|
||||
@ -296,7 +329,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
/* The following work for any of the builtin shell specs. */
|
||||
int shargc = 0;
|
||||
shargv[shargc++] = shellPath;
|
||||
if (errCheck && shellErrFlag)
|
||||
if (errCheck && shellErrFlag != NULL)
|
||||
shargv[shargc++] = shellErrFlag;
|
||||
shargv[shargc++] = DEBUG(SHELL) ? "-xc" : "-c";
|
||||
shargv[shargc++] = cmd;
|
||||
@ -306,8 +339,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
mav = NULL;
|
||||
} else {
|
||||
/*
|
||||
* No meta-characters, so no need to exec a shell. Break the command
|
||||
* into words to form an argument vector we can execute.
|
||||
* No meta-characters, so no need to exec a shell. Break the
|
||||
* command into words to form an argument vector we can
|
||||
* execute.
|
||||
*/
|
||||
Words words = Str_Words(cmd, FALSE);
|
||||
mav = words.words;
|
||||
@ -321,6 +355,8 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
}
|
||||
#endif
|
||||
|
||||
Var_ReexportVars();
|
||||
|
||||
/*
|
||||
* Fork and execute the single command. If the fork fails, we abort.
|
||||
*/
|
||||
@ -329,7 +365,6 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
Fatal("Could not fork");
|
||||
}
|
||||
if (cpid == 0) {
|
||||
Var_ExportVars();
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
meta_compat_child();
|
||||
@ -344,7 +379,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
|
||||
/* XXX: Memory management looks suspicious here. */
|
||||
/* XXX: Setting a list item to NULL is unexpected. */
|
||||
LstNode_SetNull(cmdNode);
|
||||
LstNode_SetNull(ln);
|
||||
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
@ -376,24 +411,8 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
}
|
||||
#endif
|
||||
if (status != 0) {
|
||||
if (DEBUG(ERROR)) {
|
||||
const char *p = cmd;
|
||||
debug_printf("\n*** Failed target: %s\n*** Failed command: ",
|
||||
gn->name);
|
||||
|
||||
/* Replace runs of whitespace with a single space, to reduce
|
||||
* the amount of whitespace for multi-line command lines. */
|
||||
while (*p != '\0') {
|
||||
if (ch_isspace(*p)) {
|
||||
debug_printf(" ");
|
||||
cpp_skip_whitespace(&p);
|
||||
} else {
|
||||
debug_printf("%c", *p);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
}
|
||||
if (DEBUG(ERROR))
|
||||
DebugFailedTarget(cmd, gn);
|
||||
printf("*** Error code %d", status);
|
||||
}
|
||||
} else {
|
||||
@ -406,12 +425,15 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
if (errCheck) {
|
||||
#ifdef USE_META
|
||||
if (useMeta) {
|
||||
meta_job_error(NULL, gn, 0, status);
|
||||
meta_job_error(NULL, gn, FALSE, status);
|
||||
}
|
||||
#endif
|
||||
gn->made = ERROR;
|
||||
if (opts.keepgoing) {
|
||||
/* Abort the current target, but let others continue. */
|
||||
/*
|
||||
* Abort the current target,
|
||||
* but let others continue.
|
||||
*/
|
||||
printf(" (continuing)\n");
|
||||
} else {
|
||||
printf("\n");
|
||||
@ -430,7 +452,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn)
|
||||
|
||||
free(cmdStart);
|
||||
compatChild = 0;
|
||||
if (compatSigno) {
|
||||
if (compatSigno != 0) {
|
||||
bmake_signal(compatSigno, SIG_DFL);
|
||||
kill(myPid, compatSigno);
|
||||
}
|
||||
@ -442,9 +464,10 @@ static void
|
||||
RunCommands(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
if (Compat_RunCommand(cmd, gn) != 0)
|
||||
if (Compat_RunCommand(cmd, gn, ln) != 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -453,27 +476,19 @@ 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:
|
||||
* gn The node to make
|
||||
* pgn Parent to abort if necessary
|
||||
*/
|
||||
void
|
||||
Compat_Make(GNode *gn, GNode *pgn)
|
||||
static Boolean
|
||||
MakeUnmade(GNode *gn, GNode *pgn)
|
||||
{
|
||||
if (shellName == NULL) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
|
||||
if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
|
||||
assert(gn->made == UNMADE);
|
||||
|
||||
/*
|
||||
* First mark ourselves to be made, then apply whatever transformations
|
||||
* the suffix module thinks are necessary. Once that's done, we can
|
||||
@ -484,16 +499,19 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
*/
|
||||
gn->flags |= REMAKE;
|
||||
gn->made = BEINGMADE;
|
||||
|
||||
if (!(gn->type & OP_MADE))
|
||||
Suff_FindDeps(gn);
|
||||
MakeNodes(gn->children, gn);
|
||||
|
||||
MakeNodes(&gn->children, gn);
|
||||
|
||||
if (!(gn->flags & REMAKE)) {
|
||||
gn->made = ABORTED;
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
goto cohorts;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
|
||||
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
|
||||
Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
|
||||
|
||||
/*
|
||||
@ -506,37 +524,36 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
if (!GNode_IsOODate(gn)) {
|
||||
gn->made = UPTODATE;
|
||||
DEBUG0(MAKE, "up-to-date.\n");
|
||||
goto cohorts;
|
||||
} else
|
||||
DEBUG0(MAKE, "out-of-date.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user is just seeing if something is out-of-date, exit now
|
||||
* to tell him/her "yes".
|
||||
*/
|
||||
DEBUG0(MAKE, "out-of-date.\n");
|
||||
if (opts.queryFlag)
|
||||
exit(1);
|
||||
|
||||
/*
|
||||
* We need to be re-made. We also have to make sure we've got a $?
|
||||
* variable. To be nice, we also define the $> variable using
|
||||
* Make_DoAllVar().
|
||||
* We need to be re-made.
|
||||
* Ensure that $? (.OODATE) and $> (.ALLSRC) are both set.
|
||||
*/
|
||||
Make_DoAllVar(gn);
|
||||
|
||||
/*
|
||||
* Alter our type to tell if errors should be ignored or things
|
||||
* should not be printed so CompatRunCommand knows what to do.
|
||||
* should not be printed so Compat_RunCommand knows what to do.
|
||||
*/
|
||||
if (Targ_Ignore(gn))
|
||||
if (opts.ignoreErrors)
|
||||
gn->type |= OP_IGNORE;
|
||||
if (Targ_Silent(gn))
|
||||
if (opts.beSilent)
|
||||
gn->type |= OP_SILENT;
|
||||
|
||||
if (Job_CheckCommands(gn, Fatal)) {
|
||||
/*
|
||||
* Our commands are ok, but we still have to worry about the -t
|
||||
* flag...
|
||||
* Our commands are ok, but we still have to worry about
|
||||
* the -t flag.
|
||||
*/
|
||||
if (!opts.touchFlag || (gn->type & OP_MAKE)) {
|
||||
curTarg = gn;
|
||||
@ -579,14 +596,18 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
PrintOnError(gn, "\nStop.");
|
||||
exit(1);
|
||||
}
|
||||
} else if (gn->made == ERROR) {
|
||||
/* Already had an error when making this. Tell the parent to abort. */
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
} else {
|
||||
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
MakeOther(GNode *gn, GNode *pgn)
|
||||
{
|
||||
|
||||
if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) {
|
||||
const char *target = GNode_VarTarget(gn);
|
||||
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
|
||||
}
|
||||
|
||||
switch (gn->made) {
|
||||
case BEINGMADE:
|
||||
Error("Graph cycles through %s", gn->name);
|
||||
@ -608,24 +629,66 @@ Compat_Make(GNode *gn, GNode *pgn)
|
||||
}
|
||||
}
|
||||
|
||||
cohorts:
|
||||
MakeNodes(gn->cohorts, pgn);
|
||||
}
|
||||
|
||||
/* Initialize this module and start making.
|
||||
/*
|
||||
* Make a target.
|
||||
*
|
||||
* If an error is detected and not being ignored, the process exits.
|
||||
*
|
||||
* Input:
|
||||
* targs The target nodes to re-create
|
||||
* gn The node to make
|
||||
* pgn Parent to abort if necessary
|
||||
*
|
||||
* Output:
|
||||
* gn->made
|
||||
* UPTODATE gn was already up-to-date.
|
||||
* MADE gn was recreated successfully.
|
||||
* ERROR An error occurred while gn was being created,
|
||||
* either due to missing commands or in -k mode.
|
||||
* ABORTED gn was not remade because one of its
|
||||
* dependencies could not be made due to errors.
|
||||
*/
|
||||
void
|
||||
Compat_Run(GNodeList *targs)
|
||||
Compat_Make(GNode *gn, GNode *pgn)
|
||||
{
|
||||
GNode *gn = NULL; /* Current root target */
|
||||
int errors; /* Number of targets not remade due to errors */
|
||||
|
||||
if (!shellName)
|
||||
if (shellName == NULL) /* we came here from jobs */
|
||||
Shell_Init();
|
||||
|
||||
if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
|
||||
if (!MakeUnmade(gn, pgn))
|
||||
goto cohorts;
|
||||
|
||||
/* XXX: Replace with GNode_IsError(gn) */
|
||||
} else if (gn->made == ERROR) {
|
||||
/*
|
||||
* Already had an error when making this.
|
||||
* Tell the parent to abort.
|
||||
*/
|
||||
pgn->flags &= ~(unsigned)REMAKE;
|
||||
} else {
|
||||
MakeOther(gn, pgn);
|
||||
}
|
||||
|
||||
cohorts:
|
||||
MakeNodes(&gn->cohorts, pgn);
|
||||
}
|
||||
|
||||
static void
|
||||
MakeBeginNode(void)
|
||||
{
|
||||
GNode *gn = Targ_FindNode(".BEGIN");
|
||||
if (gn == NULL)
|
||||
return;
|
||||
|
||||
Compat_Make(gn, gn);
|
||||
if (GNode_IsError(gn)) {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
InitSignals(void)
|
||||
{
|
||||
if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGINT, CompatInterrupt);
|
||||
if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
|
||||
@ -634,25 +697,31 @@ Compat_Run(GNodeList *targs)
|
||||
bmake_signal(SIGHUP, CompatInterrupt);
|
||||
if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
|
||||
bmake_signal(SIGQUIT, CompatInterrupt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize this module and start making.
|
||||
*
|
||||
* Input:
|
||||
* targs The target nodes to re-create
|
||||
*/
|
||||
void
|
||||
Compat_Run(GNodeList *targs)
|
||||
{
|
||||
GNode *errorNode = NULL;
|
||||
|
||||
if (shellName == NULL)
|
||||
Shell_Init();
|
||||
|
||||
InitSignals();
|
||||
|
||||
/* 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 (!opts.queryFlag) {
|
||||
gn = Targ_FindNode(".BEGIN");
|
||||
if (gn != NULL) {
|
||||
Compat_Make(gn, gn);
|
||||
if (gn->made == ERROR) {
|
||||
PrintOnError(gn, "\nStop.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!opts.queryFlag)
|
||||
MakeBeginNode();
|
||||
|
||||
/*
|
||||
* Expand .USE nodes right now, because they can modify the structure
|
||||
@ -660,39 +729,30 @@ Compat_Run(GNodeList *targs)
|
||||
*/
|
||||
Make_ExpandUse(targs);
|
||||
|
||||
/*
|
||||
* For each entry in the list of targets to create, call Compat_Make on
|
||||
* it to create the thing. Compat_Make will leave the 'made' field of gn
|
||||
* in one of several states:
|
||||
* UPTODATE gn was already up-to-date
|
||||
* MADE gn was recreated successfully
|
||||
* ERROR An error occurred while gn was being created
|
||||
* ABORTED gn was not remade because one of its inferiors
|
||||
* could not be made due to errors.
|
||||
*/
|
||||
errors = 0;
|
||||
while (!Lst_IsEmpty(targs)) {
|
||||
gn = Lst_Dequeue(targs);
|
||||
GNode *gn = Lst_Dequeue(targs);
|
||||
Compat_Make(gn, gn);
|
||||
|
||||
if (gn->made == UPTODATE) {
|
||||
printf("`%s' is up to date.\n", gn->name);
|
||||
} else if (gn->made == ABORTED) {
|
||||
printf("`%s' not remade because of errors.\n", gn->name);
|
||||
errors++;
|
||||
printf("`%s' not remade because of errors.\n",
|
||||
gn->name);
|
||||
}
|
||||
if (GNode_IsError(gn) && errorNode == NULL)
|
||||
errorNode = gn;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the user has defined a .END target, run its commands.
|
||||
*/
|
||||
if (errors == 0) {
|
||||
/* If the user has defined a .END target, run its commands. */
|
||||
if (errorNode == NULL) {
|
||||
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.");
|
||||
if (GNode_IsError(endNode))
|
||||
errorNode = endNode;
|
||||
}
|
||||
|
||||
if (errorNode != NULL) {
|
||||
PrintOnError(errorNode, "\nStop.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
369
cond.c
369
cond.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $ */
|
||||
/* $NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -69,7 +69,8 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Handling of conditionals in a makefile.
|
||||
/*
|
||||
* Handling of conditionals in a makefile.
|
||||
*
|
||||
* Interface:
|
||||
* Cond_EvalLine Evaluate the conditional directive, such as
|
||||
@ -94,7 +95,7 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.214 2020/11/13 09:01:59 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The parsing of conditional expressions is based on this grammar:
|
||||
@ -144,10 +145,12 @@ typedef struct CondParser {
|
||||
const char *p; /* The remaining condition to parse */
|
||||
Token curr; /* Single push-back token used in parsing */
|
||||
|
||||
/* Whether an error message has already been printed for this condition.
|
||||
* The first available error message is usually the most specific one,
|
||||
* therefore it makes sense to suppress the standard "Malformed
|
||||
* conditional" message. */
|
||||
/*
|
||||
* Whether an error message has already been printed for this
|
||||
* condition. The first available error message is usually the most
|
||||
* specific one, therefore it makes sense to suppress the standard
|
||||
* "Malformed conditional" message.
|
||||
*/
|
||||
Boolean printedError;
|
||||
} CondParser;
|
||||
|
||||
@ -168,7 +171,7 @@ static unsigned int cond_min_depth = 0; /* depth at makefile open */
|
||||
*/
|
||||
static Boolean lhsStrict;
|
||||
|
||||
static int
|
||||
static Boolean
|
||||
is_token(const char *str, const char *tok, size_t len)
|
||||
{
|
||||
return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
|
||||
@ -196,7 +199,8 @@ CondParser_SkipWhitespace(CondParser *par)
|
||||
cpp_skip_whitespace(&par->p);
|
||||
}
|
||||
|
||||
/* Parse the argument of a built-in function.
|
||||
/*
|
||||
* Parse the argument of a built-in function.
|
||||
*
|
||||
* Arguments:
|
||||
* *pp initially points at the '(',
|
||||
@ -207,10 +211,12 @@ CondParser_SkipWhitespace(CondParser *par)
|
||||
* func says whether the argument belongs to an actual function, or
|
||||
* whether the parsed argument is passed to the default function.
|
||||
*
|
||||
* Return the length of the argument, or 0 on error. */
|
||||
* Return the length of the argument, or 0 on error.
|
||||
*/
|
||||
static size_t
|
||||
ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
char **out_arg) {
|
||||
char **out_arg)
|
||||
{
|
||||
const char *p = *pp;
|
||||
Buffer argBuf;
|
||||
int paren_depth;
|
||||
@ -237,20 +243,21 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
break;
|
||||
if (*p == '$') {
|
||||
/*
|
||||
* Parse the variable spec and install it as part of the argument
|
||||
* if it's valid. We tell Var_Parse to complain on an undefined
|
||||
* variable, so we don't need to do it. Nor do we return an error,
|
||||
* though perhaps we should...
|
||||
* Parse the variable expression and install it as
|
||||
* part of the argument if it's valid. We tell
|
||||
* Var_Parse to complain on an undefined variable,
|
||||
* (XXX: but Var_Parse ignores that request)
|
||||
* so we don't need to do it. Nor do we return an
|
||||
* error, though perhaps we should.
|
||||
*/
|
||||
void *nestedVal_freeIt;
|
||||
VarEvalFlags eflags = doEval ? VARE_WANTRES | VARE_UNDEFERR
|
||||
VarEvalFlags eflags = doEval
|
||||
? VARE_WANTRES | VARE_UNDEFERR
|
||||
: VARE_NONE;
|
||||
const char *nestedVal;
|
||||
(void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal,
|
||||
&nestedVal_freeIt);
|
||||
FStr nestedVal;
|
||||
(void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal);
|
||||
/* TODO: handle errors */
|
||||
Buf_AddStr(&argBuf, nestedVal);
|
||||
free(nestedVal_freeIt);
|
||||
Buf_AddStr(&argBuf, nestedVal.str);
|
||||
FStr_Done(&nestedVal);
|
||||
continue;
|
||||
}
|
||||
if (ch == '(')
|
||||
@ -267,9 +274,10 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
if (func != NULL && *p++ != ')') {
|
||||
Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()",
|
||||
Parse_Error(PARSE_WARNING,
|
||||
"Missing closing parenthesis for %s()",
|
||||
func);
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
/* The PARSE_FATAL follows in CondEvalExpression. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -278,35 +286,38 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func,
|
||||
}
|
||||
|
||||
/* Test whether the given variable is defined. */
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
{
|
||||
void *freeIt;
|
||||
Boolean result = Var_Value(arg, VAR_CMDLINE, &freeIt) != NULL;
|
||||
bmake_free(freeIt);
|
||||
FStr value = Var_Value(arg, VAR_CMDLINE);
|
||||
Boolean result = value.str != NULL;
|
||||
FStr_Done(&value);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* See if the given target is being made. */
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = opts.create->first; ln != NULL; ln = ln->next)
|
||||
for (ln = opts.create.first; ln != NULL; ln = ln->next)
|
||||
if (Str_Match(ln->datum, arg))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* See if the given file exists. */
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
{
|
||||
Boolean result;
|
||||
char *path;
|
||||
|
||||
path = Dir_FindFile(arg, dirSearchPath);
|
||||
path = Dir_FindFile(arg, &dirSearchPath);
|
||||
DEBUG2(COND, "exists(%s) result is \"%s\"\n",
|
||||
arg, path != NULL ? path : "");
|
||||
result = path != NULL;
|
||||
@ -315,6 +326,7 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
}
|
||||
|
||||
/* See if the given node exists and is an actual target. */
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
{
|
||||
@ -322,13 +334,16 @@ FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
return gn != NULL && GNode_IsTarget(gn);
|
||||
}
|
||||
|
||||
/* See if the given node exists and is an actual target with commands
|
||||
* associated with it. */
|
||||
/*
|
||||
* See if the given node exists and is an actual target with commands
|
||||
* associated with it.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
|
||||
{
|
||||
GNode *gn = Targ_FindNode(arg);
|
||||
return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(gn->commands);
|
||||
return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -385,12 +400,12 @@ is_separator(char ch)
|
||||
* Sets out_freeIt.
|
||||
*/
|
||||
/* coverity:[+alloc : arg-*4] */
|
||||
static const char *
|
||||
static void
|
||||
CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
Boolean *out_quoted, void **out_freeIt)
|
||||
FStr *out_str, Boolean *out_quoted)
|
||||
{
|
||||
Buffer buf;
|
||||
const char *str;
|
||||
FStr str;
|
||||
Boolean atStart;
|
||||
const char *nested_p;
|
||||
Boolean quoted;
|
||||
@ -399,13 +414,13 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
VarParseResult parseResult;
|
||||
|
||||
Buf_Init(&buf);
|
||||
str = NULL;
|
||||
*out_freeIt = NULL;
|
||||
str = FStr_InitRefer(NULL);
|
||||
*out_quoted = quoted = par->p[0] == '"';
|
||||
start = par->p;
|
||||
if (quoted)
|
||||
par->p++;
|
||||
while (par->p[0] != '\0' && str == NULL) {
|
||||
|
||||
while (par->p[0] != '\0' && str.str == NULL) {
|
||||
switch (par->p[0]) {
|
||||
case '\\':
|
||||
par->p++;
|
||||
@ -436,53 +451,55 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
continue;
|
||||
case '$':
|
||||
/* if we are in quotes, an undefined variable is ok */
|
||||
eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
|
||||
eflags =
|
||||
doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR :
|
||||
doEval ? VARE_WANTRES :
|
||||
VARE_NONE;
|
||||
|
||||
nested_p = par->p;
|
||||
atStart = nested_p == start;
|
||||
parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, &str,
|
||||
out_freeIt);
|
||||
parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags,
|
||||
&str);
|
||||
/* TODO: handle errors */
|
||||
if (str == var_Error) {
|
||||
if (parseResult & VPR_ANY_MSG)
|
||||
if (str.str == var_Error) {
|
||||
if (parseResult == VPR_ERR)
|
||||
par->printedError = TRUE;
|
||||
if (*out_freeIt != NULL) {
|
||||
/* XXX: Can there be any situation in which a returned
|
||||
* var_Error requires freeIt? */
|
||||
free(*out_freeIt);
|
||||
*out_freeIt = NULL;
|
||||
}
|
||||
/*
|
||||
* Even if !doEval, we still report syntax errors, which
|
||||
* is what getting var_Error back with !doEval means.
|
||||
* XXX: Can there be any situation in which
|
||||
* a returned var_Error requires freeIt?
|
||||
*/
|
||||
str = NULL;
|
||||
FStr_Done(&str);
|
||||
/*
|
||||
* Even if !doEval, we still report syntax
|
||||
* errors, which is what getting var_Error
|
||||
* back with !doEval means.
|
||||
*/
|
||||
str = FStr_InitRefer(NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
par->p = nested_p;
|
||||
|
||||
/*
|
||||
* If the '$' started the string literal (which means no quotes),
|
||||
* and the variable expression is followed by a space, looks like
|
||||
* a comparison operator or is the end of the expression, we are
|
||||
* done.
|
||||
* If the '$' started the string literal (which means
|
||||
* no quotes), and the variable expression is followed
|
||||
* by a space, looks like a comparison operator or is
|
||||
* the end of the expression, we are done.
|
||||
*/
|
||||
if (atStart && is_separator(par->p[0]))
|
||||
goto cleanup;
|
||||
|
||||
Buf_AddStr(&buf, str);
|
||||
if (*out_freeIt) {
|
||||
free(*out_freeIt);
|
||||
*out_freeIt = NULL;
|
||||
}
|
||||
str = NULL; /* not finished yet */
|
||||
Buf_AddStr(&buf, str.str);
|
||||
FStr_Done(&str);
|
||||
str = FStr_InitRefer(NULL); /* not finished yet */
|
||||
continue;
|
||||
default:
|
||||
if (strictLHS && !quoted && *start != '$' && !ch_isdigit(*start)) {
|
||||
/* lhs must be quoted, a variable reference or number */
|
||||
str = NULL;
|
||||
if (strictLHS && !quoted && *start != '$' &&
|
||||
!ch_isdigit(*start)) {
|
||||
/*
|
||||
* The left-hand side must be quoted,
|
||||
* a variable reference or a number.
|
||||
*/
|
||||
str = FStr_InitRefer(NULL);
|
||||
goto cleanup;
|
||||
}
|
||||
Buf_AddByte(&buf, par->p[0]);
|
||||
@ -491,18 +508,18 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS,
|
||||
}
|
||||
}
|
||||
got_str:
|
||||
*out_freeIt = Buf_GetAll(&buf, NULL);
|
||||
str = *out_freeIt;
|
||||
str = FStr_InitOwn(Buf_GetAll(&buf, NULL));
|
||||
cleanup:
|
||||
Buf_Destroy(&buf, FALSE);
|
||||
return str;
|
||||
*out_str = str;
|
||||
}
|
||||
|
||||
struct If {
|
||||
const char *form; /* Form of if */
|
||||
size_t formlen; /* Length of form */
|
||||
Boolean doNot; /* TRUE if default function should be negated */
|
||||
Boolean (*defProc)(size_t, const char *); /* Default function to apply */
|
||||
/* The default function to apply on unquoted bare words. */
|
||||
Boolean (*defProc)(size_t, const char *);
|
||||
};
|
||||
|
||||
/* The different forms of .if directives. */
|
||||
@ -514,7 +531,9 @@ static const struct If ifs[] = {
|
||||
{ "", 0, FALSE, FuncDefined },
|
||||
{ NULL, 0, FALSE, NULL }
|
||||
};
|
||||
enum { PLAIN_IF_INDEX = 4 };
|
||||
enum {
|
||||
PLAIN_IF_INDEX = 4
|
||||
};
|
||||
|
||||
static Boolean
|
||||
If_Eval(const struct If *if_info, const char *arg, size_t arglen)
|
||||
@ -523,8 +542,10 @@ If_Eval(const struct If *if_info, const char *arg, size_t arglen)
|
||||
return if_info->doNot ? !res : res;
|
||||
}
|
||||
|
||||
/* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
|
||||
* ".if 0". */
|
||||
/*
|
||||
* Evaluate a "comparison without operator", such as in ".if ${VAR}" or
|
||||
* ".if 0".
|
||||
*/
|
||||
static Boolean
|
||||
EvalNotEmpty(CondParser *par, const char *value, Boolean quoted)
|
||||
{
|
||||
@ -559,14 +580,14 @@ EvalCompareNum(double lhs, const char *op, double rhs)
|
||||
case '!':
|
||||
if (op[1] != '=') {
|
||||
Parse_Error(PARSE_WARNING, "Unknown operator");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
/* The PARSE_FATAL follows in CondEvalExpression. */
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return ToToken(lhs != rhs);
|
||||
case '=':
|
||||
if (op[1] != '=') {
|
||||
Parse_Error(PARSE_WARNING, "Unknown operator");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
/* The PARSE_FATAL follows in CondEvalExpression. */
|
||||
return TOK_ERROR;
|
||||
}
|
||||
return ToToken(lhs == rhs);
|
||||
@ -583,8 +604,9 @@ EvalCompareStr(const char *lhs, const char *op, const char *rhs)
|
||||
{
|
||||
if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) {
|
||||
Parse_Error(PARSE_WARNING,
|
||||
"String comparison operator must be either == or !=");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
"String comparison operator "
|
||||
"must be either == or !=");
|
||||
/* The PARSE_FATAL follows in CondEvalExpression. */
|
||||
return TOK_ERROR;
|
||||
}
|
||||
|
||||
@ -606,7 +628,8 @@ EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op,
|
||||
return EvalCompareStr(lhs, op, rhs);
|
||||
}
|
||||
|
||||
/* Parse a comparison condition such as:
|
||||
/*
|
||||
* Parse a comparison condition such as:
|
||||
*
|
||||
* 0
|
||||
* ${VAR:Mpattern}
|
||||
@ -617,16 +640,16 @@ static Token
|
||||
CondParser_Comparison(CondParser *par, Boolean doEval)
|
||||
{
|
||||
Token t = TOK_ERROR;
|
||||
const char *lhs, *op, *rhs;
|
||||
void *lhs_freeIt, *rhs_freeIt;
|
||||
FStr lhs, rhs;
|
||||
const char *op;
|
||||
Boolean lhsQuoted, rhsQuoted;
|
||||
|
||||
/*
|
||||
* Parse the variable spec and skip over it, saving its
|
||||
* value in lhs.
|
||||
*/
|
||||
lhs = CondParser_String(par, doEval, lhsStrict, &lhsQuoted, &lhs_freeIt);
|
||||
if (lhs == NULL)
|
||||
CondParser_String(par, doEval, lhsStrict, &lhs, &lhsQuoted);
|
||||
if (lhs.str == NULL)
|
||||
goto done_lhs;
|
||||
|
||||
CondParser_SkipWhitespace(par);
|
||||
@ -644,20 +667,21 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
|
||||
break;
|
||||
default:
|
||||
/* Unknown operator, compare against an empty string or 0. */
|
||||
t = ToToken(doEval && EvalNotEmpty(par, lhs, lhsQuoted));
|
||||
t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted));
|
||||
goto done_lhs;
|
||||
}
|
||||
|
||||
CondParser_SkipWhitespace(par);
|
||||
|
||||
if (par->p[0] == '\0') {
|
||||
Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator");
|
||||
/* The PARSE_FATAL is done as a follow-up by CondEvalExpression. */
|
||||
Parse_Error(PARSE_WARNING,
|
||||
"Missing right-hand-side of operator");
|
||||
/* The PARSE_FATAL follows in CondEvalExpression. */
|
||||
goto done_lhs;
|
||||
}
|
||||
|
||||
rhs = CondParser_String(par, doEval, FALSE, &rhsQuoted, &rhs_freeIt);
|
||||
if (rhs == NULL)
|
||||
CondParser_String(par, doEval, FALSE, &rhs, &rhsQuoted);
|
||||
if (rhs.str == NULL)
|
||||
goto done_rhs;
|
||||
|
||||
if (!doEval) {
|
||||
@ -665,23 +689,25 @@ CondParser_Comparison(CondParser *par, Boolean doEval)
|
||||
goto done_rhs;
|
||||
}
|
||||
|
||||
t = EvalCompare(lhs, lhsQuoted, op, rhs, rhsQuoted);
|
||||
t = EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted);
|
||||
|
||||
done_rhs:
|
||||
free(rhs_freeIt);
|
||||
FStr_Done(&rhs);
|
||||
done_lhs:
|
||||
free(lhs_freeIt);
|
||||
FStr_Done(&lhs);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* The argument to empty() is a variable name, optionally followed by
|
||||
* variable modifiers. */
|
||||
/*
|
||||
* The argument to empty() is a variable name, optionally followed by
|
||||
* variable modifiers.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static size_t
|
||||
ParseEmptyArg(const char **pp, Boolean doEval,
|
||||
const char *func MAKE_ATTR_UNUSED, char **out_arg)
|
||||
{
|
||||
void *val_freeIt;
|
||||
const char *val;
|
||||
FStr val;
|
||||
size_t magic_res;
|
||||
|
||||
/* We do all the work here and return the result as the length */
|
||||
@ -689,27 +715,31 @@ ParseEmptyArg(const char **pp, Boolean doEval,
|
||||
|
||||
(*pp)--; /* Make (*pp)[1] point to the '('. */
|
||||
(void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE,
|
||||
&val, &val_freeIt);
|
||||
&val);
|
||||
/* TODO: handle errors */
|
||||
/* If successful, *pp points beyond the closing ')' now. */
|
||||
|
||||
if (val == var_Error) {
|
||||
free(val_freeIt);
|
||||
if (val.str == var_Error) {
|
||||
FStr_Done(&val);
|
||||
return (size_t)-1;
|
||||
}
|
||||
|
||||
/* A variable is empty when it just contains spaces... 4/15/92, christos */
|
||||
cpp_skip_whitespace(&val);
|
||||
/*
|
||||
* A variable is empty when it just contains spaces...
|
||||
* 4/15/92, christos
|
||||
*/
|
||||
cpp_skip_whitespace(&val.str);
|
||||
|
||||
/*
|
||||
* For consistency with the other functions we can't generate the
|
||||
* true/false here.
|
||||
*/
|
||||
magic_res = *val != '\0' ? 2 : 1;
|
||||
free(val_freeIt);
|
||||
magic_res = val.str[0] != '\0' ? 2 : 1;
|
||||
FStr_Done(&val);
|
||||
return magic_res;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static Boolean
|
||||
FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
|
||||
{
|
||||
@ -723,7 +753,8 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
|
||||
static const struct fn_def {
|
||||
const char *fn_name;
|
||||
size_t fn_name_len;
|
||||
size_t (*fn_parse)(const char **, Boolean, const char *, char **);
|
||||
size_t (*fn_parse)(const char **, Boolean, const char *,
|
||||
char **);
|
||||
Boolean (*fn_eval)(size_t, const char *);
|
||||
} fns[] = {
|
||||
{ "defined", 7, ParseFuncArg, FuncDefined },
|
||||
@ -765,15 +796,17 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Parse a function call, a number, a variable expression or a string
|
||||
* literal. */
|
||||
/*
|
||||
* Parse a function call, a number, a variable expression or a string
|
||||
* literal.
|
||||
*/
|
||||
static Token
|
||||
CondParser_LeafToken(CondParser *par, Boolean doEval)
|
||||
{
|
||||
Token t;
|
||||
char *arg = NULL;
|
||||
size_t arglen;
|
||||
const char *cp = par->p;
|
||||
const char *cp;
|
||||
const char *cp1;
|
||||
|
||||
if (CondParser_Func(par, doEval, &t))
|
||||
@ -788,9 +821,9 @@ CondParser_LeafToken(CondParser *par, Boolean doEval)
|
||||
* Most likely we have a naked token to apply the default function to.
|
||||
* However ".if a == b" gets here when the "a" is unquoted and doesn't
|
||||
* start with a '$'. This surprises people.
|
||||
* If what follows the function argument is a '=' or '!' then the syntax
|
||||
* would be invalid if we did "defined(a)" - so instead treat as an
|
||||
* expression.
|
||||
* If what follows the function argument is a '=' or '!' then the
|
||||
* syntax would be invalid if we did "defined(a)" - so instead treat
|
||||
* as an expression.
|
||||
*/
|
||||
arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
|
||||
cp1 = cp;
|
||||
@ -838,7 +871,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
par->p++;
|
||||
if (par->p[0] == '|')
|
||||
par->p++;
|
||||
else if (opts.lint) {
|
||||
else if (opts.strict) {
|
||||
Parse_Error(PARSE_FATAL, "Unknown operator '|'");
|
||||
par->printedError = TRUE;
|
||||
return TOK_ERROR;
|
||||
@ -849,7 +882,7 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
par->p++;
|
||||
if (par->p[0] == '&')
|
||||
par->p++;
|
||||
else if (opts.lint) {
|
||||
else if (opts.strict) {
|
||||
Parse_Error(PARSE_FATAL, "Unknown operator '&'");
|
||||
par->printedError = TRUE;
|
||||
return TOK_ERROR;
|
||||
@ -875,7 +908,8 @@ CondParser_Token(CondParser *par, Boolean doEval)
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse a single term in the expression. This consists of a terminal symbol
|
||||
/*
|
||||
* Parse a single term in the expression. This consists of a terminal symbol
|
||||
* or TOK_NOT and a term (not including the binary operators):
|
||||
*
|
||||
* T -> defined(variable) | make(target) | exists(file) | symbol
|
||||
@ -918,7 +952,8 @@ CondParser_Term(CondParser *par, Boolean doEval)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Parse a conjunctive factor (nice name, wot?)
|
||||
/*
|
||||
* Parse a conjunctive factor (nice name, wot?)
|
||||
*
|
||||
* F -> T && F | T
|
||||
*
|
||||
@ -938,10 +973,10 @@ CondParser_Factor(CondParser *par, Boolean doEval)
|
||||
/*
|
||||
* F -> T && F
|
||||
*
|
||||
* If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we
|
||||
* have to parse the r.h.s. anyway (to throw it away).
|
||||
* If T is TOK_TRUE, the result is the r.h.s., be it a TOK_ERROR
|
||||
* or not.
|
||||
* If T is TOK_FALSE, the whole thing will be
|
||||
* TOK_FALSE, but we have to parse the r.h.s. anyway
|
||||
* (to throw it away). If T is TOK_TRUE, the result
|
||||
* is the r.h.s., be it a TOK_ERROR or not.
|
||||
*/
|
||||
if (l == TOK_TRUE) {
|
||||
l = CondParser_Factor(par, doEval);
|
||||
@ -958,7 +993,8 @@ CondParser_Factor(CondParser *par, Boolean doEval)
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Main expression production.
|
||||
/*
|
||||
* Main expression production.
|
||||
*
|
||||
* E -> F || E | F
|
||||
*
|
||||
@ -978,10 +1014,12 @@ CondParser_Expr(CondParser *par, Boolean doEval)
|
||||
/*
|
||||
* E -> F || E
|
||||
*
|
||||
* A similar thing occurs for ||, except that here we make sure
|
||||
* the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s.
|
||||
* Once again, if l is TOK_FALSE, the result is the r.h.s. and once
|
||||
* again if l is TOK_TRUE, we parse the r.h.s. to throw it away.
|
||||
* A similar thing occurs for ||, except that here
|
||||
* we make sure the l.h.s. is TOK_FALSE before we
|
||||
* bother to evaluate the r.h.s. Once again, if l
|
||||
* is TOK_FALSE, the result is the r.h.s. and once
|
||||
* again if l is TOK_TRUE, we parse the r.h.s. to
|
||||
* throw it away.
|
||||
*/
|
||||
if (l == TOK_FALSE) {
|
||||
l = CondParser_Expr(par, doEval);
|
||||
@ -1009,14 +1047,15 @@ CondParser_Eval(CondParser *par, Boolean *value)
|
||||
if (res != TOK_FALSE && res != TOK_TRUE)
|
||||
return COND_INVALID;
|
||||
|
||||
if (CondParser_Token(par, TRUE /* XXX: Why TRUE? */) != TOK_EOF)
|
||||
if (CondParser_Token(par, FALSE) != TOK_EOF)
|
||||
return COND_INVALID;
|
||||
|
||||
*value = res == TOK_TRUE;
|
||||
return COND_PARSE;
|
||||
}
|
||||
|
||||
/* Evaluate the condition, including any side effects from the variable
|
||||
/*
|
||||
* Evaluate the condition, including any side effects from the variable
|
||||
* expressions in the condition. The condition consists of &&, ||, !,
|
||||
* function(arg), comparisons and parenthetical groupings thereof.
|
||||
*
|
||||
@ -1050,15 +1089,25 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value,
|
||||
return rval;
|
||||
}
|
||||
|
||||
/* Evaluate a condition in a :? modifier, such as
|
||||
* ${"${VAR}" == value:?yes:no}. */
|
||||
/*
|
||||
* Evaluate a condition in a :? modifier, such as
|
||||
* ${"${VAR}" == value:?yes:no}.
|
||||
*/
|
||||
CondEvalResult
|
||||
Cond_EvalCondition(const char *cond, Boolean *out_value)
|
||||
{
|
||||
return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/* Evaluate the conditional directive in the line, which is one of:
|
||||
static Boolean
|
||||
IsEndif(const char *p)
|
||||
{
|
||||
return p[0] == 'e' && p[1] == 'n' && p[2] == 'd' &&
|
||||
p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate the conditional directive in the line, which is one of:
|
||||
*
|
||||
* .if <cond>
|
||||
* .ifmake <cond>
|
||||
@ -1088,7 +1137,7 @@ Cond_EvalCondition(const char *cond, Boolean *out_value)
|
||||
* or because the condition could not be evaluated
|
||||
*/
|
||||
CondEvalResult
|
||||
Cond_EvalLine(const char *const line)
|
||||
Cond_EvalLine(const char *line)
|
||||
{
|
||||
typedef enum IfState {
|
||||
|
||||
@ -1117,25 +1166,20 @@ Cond_EvalLine(const char *const line)
|
||||
const char *p = line;
|
||||
|
||||
if (cond_states == NULL) {
|
||||
cond_states = bmake_malloc(cond_states_cap * sizeof *cond_states);
|
||||
cond_states = bmake_malloc(
|
||||
cond_states_cap * sizeof *cond_states);
|
||||
cond_states[0] = IFS_ACTIVE;
|
||||
}
|
||||
|
||||
p++; /* skip the leading '.' */
|
||||
cpp_skip_hspace(&p);
|
||||
|
||||
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
|
||||
if (p[0] == 'e') {
|
||||
if (p[1] != 'l') {
|
||||
if (!is_token(p + 1, "ndif", 4)) {
|
||||
/* Unknown directive. It might still be a transformation
|
||||
* rule like '.elisp.scm', therefore no error message here. */
|
||||
return COND_INVALID;
|
||||
if (IsEndif(p)) { /* It is an '.endif'. */
|
||||
if (p[5] != '\0') {
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"The .endif directive does not take arguments.");
|
||||
}
|
||||
|
||||
/* It is an '.endif'. */
|
||||
/* TODO: check for extraneous <cond> */
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less endif");
|
||||
return COND_PARSE;
|
||||
@ -1147,13 +1191,25 @@ Cond_EvalLine(const char *const line)
|
||||
? COND_PARSE : COND_SKIP;
|
||||
}
|
||||
|
||||
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
|
||||
if (p[0] == 'e') {
|
||||
if (p[1] != 'l') {
|
||||
/*
|
||||
* Unknown directive. It might still be a
|
||||
* transformation rule like '.elisp.scm',
|
||||
* therefore no error message here.
|
||||
*/
|
||||
return COND_INVALID;
|
||||
}
|
||||
|
||||
/* Quite likely this is 'else' or 'elif' */
|
||||
p += 2;
|
||||
if (is_token(p, "se", 2)) { /* It is an 'else'. */
|
||||
|
||||
if (opts.lint && p[2] != '\0')
|
||||
if (p[2] != '\0')
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"The .else directive does not take arguments.");
|
||||
"The .else directive "
|
||||
"does not take arguments.");
|
||||
|
||||
if (cond_depth == cond_min_depth) {
|
||||
Parse_Error(PARSE_FATAL, "if-less else");
|
||||
@ -1165,7 +1221,8 @@ Cond_EvalLine(const char *const line)
|
||||
state = IFS_ACTIVE | IFS_SEEN_ELSE;
|
||||
} else {
|
||||
if (state & IFS_SEEN_ELSE)
|
||||
Parse_Error(PARSE_WARNING, "extra else");
|
||||
Parse_Error(PARSE_WARNING,
|
||||
"extra else");
|
||||
state = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
}
|
||||
cond_states[cond_depth] = state;
|
||||
@ -1178,8 +1235,10 @@ Cond_EvalLine(const char *const line)
|
||||
isElif = FALSE;
|
||||
|
||||
if (p[0] != 'i' || p[1] != 'f') {
|
||||
/* Unknown directive. It might still be a transformation rule like
|
||||
* '.elisp.scm', therefore no error message here. */
|
||||
/*
|
||||
* Unknown directive. It might still be a transformation rule
|
||||
* like '.elisp.scm', therefore no error message here.
|
||||
*/
|
||||
return COND_INVALID; /* Not an ifxxx or elifxxx line */
|
||||
}
|
||||
|
||||
@ -1190,10 +1249,13 @@ Cond_EvalLine(const char *const line)
|
||||
p += 2;
|
||||
for (ifp = ifs;; ifp++) {
|
||||
if (ifp->form == NULL) {
|
||||
/* TODO: Add error message about unknown directive,
|
||||
* since there is no other known directive that starts with 'el'
|
||||
* or 'if'.
|
||||
* Example: .elifx 123 */
|
||||
/*
|
||||
* TODO: Add error message about unknown directive,
|
||||
* since there is no other known directive that starts
|
||||
* with 'el' or 'if'.
|
||||
*
|
||||
* Example: .elifx 123
|
||||
*/
|
||||
return COND_INVALID;
|
||||
}
|
||||
if (is_token(p, ifp->form, ifp->formlen)) {
|
||||
@ -1212,7 +1274,8 @@ Cond_EvalLine(const char *const line)
|
||||
state = cond_states[cond_depth];
|
||||
if (state & IFS_SEEN_ELSE) {
|
||||
Parse_Error(PARSE_WARNING, "extra elif");
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
cond_states[cond_depth] =
|
||||
IFS_WAS_ACTIVE | IFS_SEEN_ELSE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
if (state != IFS_INITIAL) {
|
||||
@ -1229,12 +1292,16 @@ Cond_EvalLine(const char *const line)
|
||||
*/
|
||||
cond_states_cap += 32;
|
||||
cond_states = bmake_realloc(cond_states,
|
||||
cond_states_cap * sizeof *cond_states);
|
||||
cond_states_cap *
|
||||
sizeof *cond_states);
|
||||
}
|
||||
state = cond_states[cond_depth];
|
||||
cond_depth++;
|
||||
if (!(state & IFS_ACTIVE)) {
|
||||
/* If we aren't parsing the data, treat as always false */
|
||||
/*
|
||||
* If we aren't parsing the data,
|
||||
* treat as always false.
|
||||
*/
|
||||
cond_states[cond_depth] = IFS_WAS_ACTIVE;
|
||||
return COND_SKIP;
|
||||
}
|
||||
@ -1263,8 +1330,8 @@ Cond_restore_depth(unsigned int saved_depth)
|
||||
unsigned int open_conds = cond_depth - cond_min_depth;
|
||||
|
||||
if (open_conds != 0 || saved_depth > cond_depth) {
|
||||
Parse_Error(PARSE_FATAL, "%u open conditional%s", open_conds,
|
||||
open_conds == 1 ? "" : "s");
|
||||
Parse_Error(PARSE_FATAL, "%u open conditional%s",
|
||||
open_conds, open_conds == 1 ? "" : "s");
|
||||
cond_depth = cond_min_depth;
|
||||
}
|
||||
|
||||
|
28
dir.h
28
dir.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: dir.h,v 1.34 2020/11/14 19:24:24 rillig Exp $ */
|
||||
/* $NetBSD: dir.h,v 1.40 2020/12/01 19:28:32 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -75,24 +75,9 @@
|
||||
#ifndef MAKE_DIR_H
|
||||
#define MAKE_DIR_H
|
||||
|
||||
/* A cache for the filenames in a directory. */
|
||||
typedef struct CachedDir {
|
||||
char *name; /* Name of directory, either absolute or
|
||||
* relative to the current directory.
|
||||
* The name is not normalized in any way,
|
||||
* that is, "." and "./." are different.
|
||||
*
|
||||
* Not sure what happens when .CURDIR is
|
||||
* assigned a new value; see Parse_DoVar. */
|
||||
int refCount; /* Number of SearchPaths with this directory */
|
||||
int hits; /* The number of times a file in this
|
||||
* directory has been found */
|
||||
HashTable files; /* Hash set of files in directory;
|
||||
* all values are NULL. */
|
||||
} CachedDir;
|
||||
typedef struct CachedDir CachedDir;
|
||||
|
||||
void Dir_Init(void);
|
||||
void Dir_InitDir(const char *);
|
||||
void Dir_InitCur(const char *);
|
||||
void Dir_InitDot(void);
|
||||
void Dir_End(void);
|
||||
@ -103,12 +88,11 @@ char *Dir_FindFile(const char *, SearchPath *);
|
||||
char *Dir_FindHereOrAbove(const char *, const char *);
|
||||
void Dir_UpdateMTime(GNode *, Boolean);
|
||||
CachedDir *Dir_AddDir(SearchPath *, const char *);
|
||||
char *Dir_MakeFlags(const char *, SearchPath *);
|
||||
void Dir_ClearPath(SearchPath *);
|
||||
void Dir_Concat(SearchPath *, SearchPath *);
|
||||
char *SearchPath_ToFlags(const char *, SearchPath *);
|
||||
void SearchPath_Clear(SearchPath *);
|
||||
void SearchPath_AddAll(SearchPath *, SearchPath *);
|
||||
void Dir_PrintDirectories(void);
|
||||
void Dir_PrintPath(SearchPath *);
|
||||
void Dir_Destroy(void *);
|
||||
void SearchPath_Print(SearchPath *);
|
||||
SearchPath *Dir_CopyDirSearchPath(void);
|
||||
|
||||
/* Stripped-down variant of struct stat. */
|
||||
|
11
enum.c
11
enum.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */
|
||||
/* $NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $ */
|
||||
|
||||
/*
|
||||
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
|
||||
@ -29,13 +29,15 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $");
|
||||
|
||||
/* Convert a bitset into a string representation, showing the names of the
|
||||
/*
|
||||
* Convert a bitset into a string representation, showing the names of the
|
||||
* individual bits.
|
||||
*
|
||||
* Optionally, shortcuts for groups of bits can be added. To have an effect,
|
||||
* they need to be listed before their individual bits. */
|
||||
* they need to be listed before their individual bits.
|
||||
*/
|
||||
const char *
|
||||
Enum_FlagsToString(char *buf, size_t buf_size,
|
||||
int value, const EnumToStringSpec *spec)
|
||||
@ -86,4 +88,5 @@ Enum_ValueToString(int value, const EnumToStringSpec *spec)
|
||||
return spec->es_name;
|
||||
}
|
||||
abort(/* unknown enum value */);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
|
69
enum.h
69
enum.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: enum.h,v 1.12 2020/09/25 15:54:50 rillig Exp $ */
|
||||
/* $NetBSD: enum.h,v 1.14 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
|
||||
@ -45,8 +45,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
/* For Enum_FlagsToString, the separator between flags. */
|
||||
#define ENUM__SEP "|"
|
||||
|
||||
/* Generate the string that joins all possible flags, to see how large the
|
||||
* buffer must be. */
|
||||
/*
|
||||
* Generate the string that joins all possible flags, to see how large the
|
||||
* buffer must be.
|
||||
*/
|
||||
#define ENUM__JOIN_STR_1(v1) \
|
||||
#v1
|
||||
#define ENUM__JOIN_STR_2(v1, v2) \
|
||||
@ -107,8 +109,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
|
||||
enum { typnam ## _ ## ToStringSize = sizeof joined }
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 2 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 2 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_2(typnam, v1, v2) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -118,8 +122,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_1(v1), \
|
||||
ENUM__JOIN_STR_1(v2)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 3 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 3 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -129,8 +135,23 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_2(v1, v2), \
|
||||
ENUM__JOIN_STR_1(v3)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 6 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 4 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_4(typnam, v1, v2, v3, v4) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_2(v1, v2), \
|
||||
ENUM__SPEC_2(v3, v4)), \
|
||||
ENUM__JOIN_2( \
|
||||
ENUM__JOIN_STR_2(v1, v2), \
|
||||
ENUM__JOIN_STR_2(v3, v4)))
|
||||
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 6 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -140,8 +161,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
|
||||
ENUM__JOIN_STR_2(v5, v6)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 8 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 8 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -151,16 +174,20 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_4(v1, v2, v3, v4), \
|
||||
ENUM__JOIN_STR_4(v5, v6, v7, v8)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_ValueToString
|
||||
* for an enum with 8 constants. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_ValueToString
|
||||
* for an enum with 8 constants.
|
||||
*/
|
||||
#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \
|
||||
ENUM__VALUE_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
ENUM__SPEC_4(v1, v2, v3, v4), \
|
||||
ENUM__SPEC_4(v5, v6, v7, v8)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 10 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 10 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \
|
||||
ENUM__FLAGS_RTTI(typnam, \
|
||||
ENUM__SPECS_2( \
|
||||
@ -170,8 +197,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \
|
||||
ENUM__JOIN_STR_2(v9, v10)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 31 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 31 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_31(typnam, \
|
||||
v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16, \
|
||||
@ -193,8 +222,10 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *);
|
||||
ENUM__JOIN_STR_2(v29, v30), \
|
||||
ENUM__JOIN_STR_1(v31)))
|
||||
|
||||
/* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 32 flags. */
|
||||
/*
|
||||
* Declare the necessary data structures for calling Enum_FlagsToString
|
||||
* for an enum with 32 flags.
|
||||
*/
|
||||
#define ENUM_FLAGS_RTTI_32(typnam, \
|
||||
v01, v02, v03, v04, v05, v06, v07, v08, \
|
||||
v09, v10, v11, v12, v13, v14, v15, v16, \
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon.h,v 1.3 2020/10/18 11:49:47 rillig Exp $ */
|
||||
/* $NetBSD: filemon.h,v 1.4 2020/11/29 09:27:40 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon_dev.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */
|
||||
/* $NetBSD: filemon_dev.c,v 1.6 2020/11/29 09:27:40 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2020 The NetBSD Foundation, Inc.
|
||||
@ -127,7 +127,7 @@ filemon_close(struct filemon *F)
|
||||
free(F);
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.4 2020/11/05 17:27:16 rillig Exp $ */
|
||||
/* $NetBSD: filemon_ktrace.c,v 1.12 2021/01/10 23:59:53 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2019 The NetBSD Foundation, Inc.
|
||||
@ -130,6 +130,7 @@ struct filemon_state {
|
||||
char *path[/*npath*/];
|
||||
};
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
compare_filemon_states(void *cookie, const void *na, const void *nb)
|
||||
{
|
||||
@ -147,6 +148,7 @@ compare_filemon_states(void *cookie, const void *na, const void *nb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
compare_filemon_key(void *cookie, const void *n, const void *k)
|
||||
{
|
||||
@ -225,7 +227,6 @@ filemon_open(void)
|
||||
/* Success! */
|
||||
return F;
|
||||
|
||||
fail2: __unused
|
||||
(void)fclose(F->in);
|
||||
fail1: (void)close(ktrpipe[0]);
|
||||
(void)close(ktrpipe[1]);
|
||||
@ -262,7 +263,7 @@ filemon_closefd(struct filemon *F)
|
||||
F->out = NULL;
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
@ -373,7 +374,7 @@ filemon_close(struct filemon *F)
|
||||
free(F);
|
||||
|
||||
/* Set errno and return -1 if anything went wrong. */
|
||||
if (error) {
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
@ -518,12 +519,12 @@ filemon_process(struct filemon *F)
|
||||
return 0;
|
||||
|
||||
/* If we're waiting for input, read some. */
|
||||
if (F->resid) {
|
||||
if (F->resid > 0) {
|
||||
nread = fread(F->p, 1, F->resid, F->in);
|
||||
if (nread == 0) {
|
||||
if (feof(F->in))
|
||||
if (feof(F->in) != 0)
|
||||
return 0;
|
||||
assert(ferror(F->in));
|
||||
assert(ferror(F->in) != 0);
|
||||
/*
|
||||
* If interrupted or would block, there may be
|
||||
* more events. Otherwise fail.
|
||||
@ -538,7 +539,7 @@ filemon_process(struct filemon *F)
|
||||
assert(nread <= F->resid);
|
||||
F->p += nread;
|
||||
F->resid -= nread;
|
||||
if (F->resid) /* may be more events */
|
||||
if (F->resid > 0) /* may be more events */
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -582,7 +583,7 @@ filemon_process(struct filemon *F)
|
||||
}
|
||||
|
||||
static struct filemon_state *
|
||||
syscall_enter(struct filemon *F,
|
||||
syscall_enter(
|
||||
const struct filemon_key *key, const struct ktr_syscall *call,
|
||||
unsigned npath,
|
||||
void (*show)(struct filemon *, const struct filemon_state *,
|
||||
@ -618,7 +619,7 @@ show_paths(struct filemon *F, const struct filemon_state *S,
|
||||
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if
|
||||
* we're not producing output.
|
||||
*/
|
||||
if (ret->ktr_error && ret->ktr_error != -2)
|
||||
if (ret->ktr_error != 0 && ret->ktr_error != -2)
|
||||
return;
|
||||
if (F->out == NULL)
|
||||
return;
|
||||
@ -644,7 +645,7 @@ show_retval(struct filemon *F, const struct filemon_state *S,
|
||||
* Ignore it if it failed or yielded EJUSTRETURN (-2), or if
|
||||
* we're not producing output.
|
||||
*/
|
||||
if (ret->ktr_error && ret->ktr_error != -2)
|
||||
if (ret->ktr_error != 0 && ret->ktr_error != -2)
|
||||
return;
|
||||
if (F->out == NULL)
|
||||
return;
|
||||
@ -664,7 +665,7 @@ static void
|
||||
show_execve(struct filemon *F, const struct filemon_state *S,
|
||||
const struct ktr_sysret *ret)
|
||||
{
|
||||
return show_paths(F, S, ret, "E");
|
||||
show_paths(F, S, ret, "E");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -752,18 +753,20 @@ show_rename(struct filemon *F, const struct filemon_state *S,
|
||||
show_paths(F, S, ret, "M");
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_chdir(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_chdir);
|
||||
return syscall_enter(key, call, 1, &show_chdir);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_execve(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_execve);
|
||||
return syscall_enter(key, call, 1, &show_execve);
|
||||
}
|
||||
|
||||
static struct filemon_state *
|
||||
@ -773,7 +776,7 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
|
||||
const register_t *args = (const void *)&call[1];
|
||||
int status = (int)args[0];
|
||||
|
||||
if (F->out) {
|
||||
if (F->out != NULL) {
|
||||
fprintf(F->out, "X %jd %d\n", (intmax_t)key->pid, status);
|
||||
if (key->pid == F->child) {
|
||||
fprintf(F->out, "# Bye bye\n");
|
||||
@ -783,20 +786,23 @@ filemon_sys_exit(struct filemon *F, const struct filemon_key *key,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_fork(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 0, &show_fork);
|
||||
return syscall_enter(key, call, 0, &show_fork);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_link(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_link);
|
||||
return syscall_enter(key, call, 2, &show_link);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_open(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
@ -809,15 +815,16 @@ filemon_sys_open(struct filemon *F, const struct filemon_key *key,
|
||||
flags = (int)args[1];
|
||||
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1, &show_open_readwrite);
|
||||
return syscall_enter(key, call, 1, &show_open_readwrite);
|
||||
else if ((flags & O_WRONLY) == O_WRONLY)
|
||||
return syscall_enter(F, key, call, 1, &show_open_write);
|
||||
return syscall_enter(key, call, 1, &show_open_write);
|
||||
else if ((flags & O_RDONLY) == O_RDONLY)
|
||||
return syscall_enter(F, key, call, 1, &show_open_read);
|
||||
return syscall_enter(key, call, 1, &show_open_read);
|
||||
else
|
||||
return NULL; /* XXX Do we care if no read or write? */
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
@ -832,47 +839,47 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key,
|
||||
|
||||
if (fd == AT_CWD) {
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
return syscall_enter(key, call, 1,
|
||||
&show_open_readwrite);
|
||||
else if ((flags & O_WRONLY) == O_WRONLY)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
&show_open_write);
|
||||
return syscall_enter(key, call, 1, &show_open_write);
|
||||
else if ((flags & O_RDONLY) == O_RDONLY)
|
||||
return syscall_enter(F, key, call, 1, &show_open_read);
|
||||
return syscall_enter(key, call, 1, &show_open_read);
|
||||
else
|
||||
return NULL;
|
||||
} else {
|
||||
if ((flags & O_RDWR) == O_RDWR)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
return syscall_enter(key, call, 1,
|
||||
&show_openat_readwrite);
|
||||
else if ((flags & O_WRONLY) == O_WRONLY)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
&show_openat_write);
|
||||
return syscall_enter(key, call, 1, &show_openat_write);
|
||||
else if ((flags & O_RDONLY) == O_RDONLY)
|
||||
return syscall_enter(F, key, call, 1,
|
||||
&show_openat_read);
|
||||
return syscall_enter(key, call, 1, &show_openat_read);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_symlink(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_symlink);
|
||||
return syscall_enter(key, call, 2, &show_symlink);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_unlink(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 1, &show_unlink);
|
||||
return syscall_enter(key, call, 1, &show_unlink);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static struct filemon_state *
|
||||
filemon_sys_rename(struct filemon *F, const struct filemon_key *key,
|
||||
const struct ktr_syscall *call)
|
||||
{
|
||||
return syscall_enter(F, key, call, 2, &show_rename);
|
||||
return syscall_enter(key, call, 2, &show_rename);
|
||||
}
|
||||
|
267
for.c
267
for.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $ */
|
||||
/* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1992, The Regents of the University of California.
|
||||
@ -32,24 +32,22 @@
|
||||
/*-
|
||||
* Handling of .for/.endfor loops in a makefile.
|
||||
*
|
||||
* For loops are of the form:
|
||||
* For loops have the form:
|
||||
*
|
||||
* .for <varname...> in <value...>
|
||||
* ...
|
||||
* # the body
|
||||
* .endfor
|
||||
*
|
||||
* When a .for line is parsed, all following lines are accumulated into a
|
||||
* buffer, up to but excluding the corresponding .endfor line. To find the
|
||||
* corresponding .endfor, the number of nested .for and .endfor directives
|
||||
* are counted.
|
||||
* When a .for line is parsed, the following lines are copied to the body of
|
||||
* the .for loop, until the corresponding .endfor line is reached. In this
|
||||
* phase, the body is not yet evaluated. This also applies to any nested
|
||||
* .for loops.
|
||||
*
|
||||
* During parsing, any nested .for loops are just passed through; they get
|
||||
* handled recursively in For_Eval when the enclosing .for loop is evaluated
|
||||
* in For_Run.
|
||||
*
|
||||
* When the .for loop has been parsed completely, the variable expressions
|
||||
* for the iteration variables are replaced with expressions of the form
|
||||
* ${:Uvalue}, and then this modified body is "included" as a special file.
|
||||
* After reaching the .endfor, the values from the .for line are grouped
|
||||
* according to the number of variables. For each such group, the unexpanded
|
||||
* body is scanned for variable expressions, and those that match the variable
|
||||
* names are replaced with expressions of the form ${:U...} or $(:U...).
|
||||
* After that, the body is treated like a file from an .include directive.
|
||||
*
|
||||
* Interface:
|
||||
* For_Eval Evaluate the loop in the passed line.
|
||||
@ -60,14 +58,14 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.115 2020/11/07 21:04:43 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $");
|
||||
|
||||
static int forLevel = 0; /* Nesting level */
|
||||
|
||||
/* One of the variables to the left of the "in" in a .for loop. */
|
||||
typedef struct ForVar {
|
||||
char *name;
|
||||
size_t len;
|
||||
size_t nameLen;
|
||||
} ForVar;
|
||||
|
||||
/*
|
||||
@ -92,7 +90,7 @@ ForAddVar(For *f, const char *name, size_t len)
|
||||
{
|
||||
ForVar *var = Vector_Push(&f->vars);
|
||||
var->name = bmake_strldup(name, len);
|
||||
var->len = len;
|
||||
var->nameLen = len;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -125,7 +123,8 @@ IsEndfor(const char *p)
|
||||
(p[6] == '\0' || ch_isspace(p[6]));
|
||||
}
|
||||
|
||||
/* Evaluate the for loop in the passed line. The line looks like this:
|
||||
/*
|
||||
* Evaluate the for loop in the passed line. The line looks like this:
|
||||
* .for <varname...> in <value...>
|
||||
*
|
||||
* Input:
|
||||
@ -178,7 +177,10 @@ For_Eval(const char *line)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: This allows arbitrary variable names; see directive-for.mk. */
|
||||
/*
|
||||
* XXX: This allows arbitrary variable names;
|
||||
* see directive-for.mk.
|
||||
*/
|
||||
for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
|
||||
continue;
|
||||
|
||||
@ -203,8 +205,12 @@ For_Eval(const char *line)
|
||||
|
||||
{
|
||||
char *items;
|
||||
(void)Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items);
|
||||
/* TODO: handle errors */
|
||||
if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) {
|
||||
Parse_Error(PARSE_FATAL, "Error in .for loop items");
|
||||
f->items.len = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
f->items = Str_Words(items, FALSE);
|
||||
free(items);
|
||||
|
||||
@ -215,19 +221,22 @@ For_Eval(const char *line)
|
||||
{
|
||||
size_t nitems, nvars;
|
||||
|
||||
if ((nitems = f->items.len) > 0 && nitems % (nvars = f->vars.len)) {
|
||||
if ((nitems = f->items.len) > 0 &&
|
||||
nitems % (nvars = f->vars.len) != 0) {
|
||||
Parse_Error(PARSE_FATAL,
|
||||
"Wrong number of words (%zu) in .for substitution list"
|
||||
" with %zu variables", nitems, nvars);
|
||||
"Wrong number of words (%u) in .for "
|
||||
"substitution list with %u variables",
|
||||
(unsigned)nitems, (unsigned)nvars);
|
||||
/*
|
||||
* Return 'success' so that the body of the .for loop is
|
||||
* accumulated.
|
||||
* Return 'success' so that the body of the .for loop
|
||||
* is accumulated.
|
||||
* Remove all items so that the loop doesn't iterate.
|
||||
*/
|
||||
f->items.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
accumFor = f;
|
||||
forLevel = 1;
|
||||
return 1;
|
||||
@ -279,8 +288,7 @@ for_var_len(const char *var)
|
||||
else if (var_start == '{')
|
||||
var_end = '}';
|
||||
else
|
||||
/* Single char variable */
|
||||
return 1;
|
||||
return 1; /* Single char variable */
|
||||
|
||||
depth = 1;
|
||||
for (len = 1; (ch = var[len++]) != '\0';) {
|
||||
@ -294,8 +302,10 @@ for_var_len(const char *var)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The .for loop substitutes the items as ${:U<value>...}, which means
|
||||
* that characters that break this syntax must be backslash-escaped. */
|
||||
/*
|
||||
* The .for loop substitutes the items as ${:U<value>...}, which means
|
||||
* that characters that break this syntax must be backslash-escaped.
|
||||
*/
|
||||
static Boolean
|
||||
NeedsEscapes(const char *word, char endc)
|
||||
{
|
||||
@ -308,21 +318,23 @@ NeedsEscapes(const char *word, char endc)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* While expanding the body of a .for loop, write the item in the ${:U...}
|
||||
/*
|
||||
* While expanding the body of a .for loop, write the item in the ${:U...}
|
||||
* expression, escaping characters as needed.
|
||||
*
|
||||
* The result is later unescaped by ApplyModifier_Defined. */
|
||||
* The result is later unescaped by ApplyModifier_Defined.
|
||||
*/
|
||||
static void
|
||||
Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
|
||||
Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if (!NeedsEscapes(item, ech)) {
|
||||
if (!NeedsEscapes(item, endc)) {
|
||||
Buf_AddStr(cmds, item);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Escape ':', '$', '\\' and 'ech' - these will be removed later by
|
||||
/* Escape ':', '$', '\\' and 'endc' - these will be removed later by
|
||||
* :U processing, see ApplyModifier_Defined. */
|
||||
while ((ch = *item++) != '\0') {
|
||||
if (ch == '$') {
|
||||
@ -333,136 +345,146 @@ Buf_AddEscaped(Buffer *cmds, const char *item, char ech)
|
||||
continue;
|
||||
}
|
||||
Buf_AddByte(cmds, '\\');
|
||||
} else if (ch == ':' || ch == '\\' || ch == ech)
|
||||
} else if (ch == ':' || ch == '\\' || ch == endc)
|
||||
Buf_AddByte(cmds, '\\');
|
||||
Buf_AddByte(cmds, ch);
|
||||
}
|
||||
}
|
||||
|
||||
/* While expanding the body of a .for loop, replace expressions like
|
||||
* ${i}, ${i:...}, $(i) or $(i:...) with their ${:U...} expansion. */
|
||||
/*
|
||||
* While expanding the body of a .for loop, replace the variable name of an
|
||||
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
|
||||
*/
|
||||
static void
|
||||
SubstVarLong(For *f, const char **pp, const char **inout_mark, char ech)
|
||||
SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc,
|
||||
const char **inout_mark)
|
||||
{
|
||||
size_t i;
|
||||
const char *p = *pp;
|
||||
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
ForVar *forVar = Vector_Get(&f->vars, i);
|
||||
char *var = forVar->name;
|
||||
size_t vlen = forVar->len;
|
||||
char *varname = forVar->name;
|
||||
size_t varnameLen = forVar->nameLen;
|
||||
|
||||
/* XXX: undefined behavior for p if vlen is longer than p? */
|
||||
if (memcmp(p, var, vlen) != 0)
|
||||
if (varnameLen >= (size_t)(bodyEnd - p))
|
||||
continue;
|
||||
if (memcmp(p, varname, varnameLen) != 0)
|
||||
continue;
|
||||
/* XXX: why test for backslash here? */
|
||||
if (p[vlen] != ':' && p[vlen] != ech && p[vlen] != '\\')
|
||||
if (p[varnameLen] != ':' && p[varnameLen] != endc &&
|
||||
p[varnameLen] != '\\')
|
||||
continue;
|
||||
|
||||
/* Found a variable match. Replace with :U<value> */
|
||||
/*
|
||||
* Found a variable match. Skip over the variable name and
|
||||
* instead add ':U<value>' to the current body.
|
||||
*/
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
|
||||
Buf_AddStr(&f->curBody, ":U");
|
||||
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], ech);
|
||||
Buf_AddEscaped(&f->curBody,
|
||||
f->items.words[f->sub_next + i], endc);
|
||||
|
||||
p += vlen;
|
||||
p += varnameLen;
|
||||
*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.
|
||||
* While expanding the body of a .for loop, replace single-character
|
||||
* variable expressions like $i with their ${:U...} expansion.
|
||||
*/
|
||||
static void
|
||||
SubstVarShort(For *f, const char *p, const char **inout_mark)
|
||||
{
|
||||
const char ch = *p;
|
||||
ForVar *vars;
|
||||
size_t i;
|
||||
|
||||
/* Skip $$ and stupid ones. */
|
||||
if (!f->short_var || strchr("}):$", ch) != NULL)
|
||||
return;
|
||||
|
||||
vars = Vector_Get(&f->vars, 0);
|
||||
for (i = 0; i < f->vars.len; i++) {
|
||||
const char *varname = vars[i].name;
|
||||
if (varname[0] == ch && varname[1] == '\0')
|
||||
goto found;
|
||||
}
|
||||
return;
|
||||
|
||||
found:
|
||||
/* Replace $<ch> with ${:U<value>} */
|
||||
Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1;
|
||||
Buf_AddStr(&f->curBody, "{:U");
|
||||
Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
|
||||
Buf_AddByte(&f->curBody, '}');
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the body for the current iteration by copying the unexpanded body,
|
||||
* replacing the expressions for the iteration variables on the way.
|
||||
*
|
||||
* Using variable expansions ensures that the .for loop can't generate
|
||||
* Using variable expressions ensures that the .for loop can't generate
|
||||
* syntax, and that the later parsing will still see a variable.
|
||||
* We assume that the null variable will never be defined.
|
||||
* This code assumes that the variable with the empty name will never be
|
||||
* defined, see unit-tests/varname-empty.mk for more details.
|
||||
*
|
||||
* The detection of substitutions of the loop control variable is naive.
|
||||
* Many of the modifiers use \ to escape $ (not $) so it is possible
|
||||
* to contrive a makefile where an unwanted substitution happens.
|
||||
*/
|
||||
static void
|
||||
ForSubstBody(For *f)
|
||||
{
|
||||
const char *p, *bodyEnd;
|
||||
const char *mark; /* where the last replacement left off */
|
||||
|
||||
Buf_Empty(&f->curBody);
|
||||
|
||||
mark = f->body.data;
|
||||
bodyEnd = f->body.data + f->body.len;
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
if (p[1] == '{' || p[1] == '(') {
|
||||
p += 2;
|
||||
SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')',
|
||||
&mark);
|
||||
} else if (p[1] != '\0') {
|
||||
SubstVarShort(f, p + 1, &mark);
|
||||
p += 2;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
Buf_AddBytesBetween(&f->curBody, mark, bodyEnd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the body for the current iteration by copying the unexpanded body,
|
||||
* replacing the expressions for the iteration variables on the way.
|
||||
*/
|
||||
static char *
|
||||
ForIterate(void *v_arg, size_t *out_len)
|
||||
ForReadMore(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) {
|
||||
if (f->sub_next == f->items.len) {
|
||||
/* No more iterations */
|
||||
For_Free(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Buf_Empty(&f->curBody);
|
||||
ForSubstBody(f);
|
||||
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
|
||||
f->sub_next += (unsigned int)f->vars.len;
|
||||
|
||||
mark = Buf_GetAll(&f->body, NULL);
|
||||
body_end = mark + Buf_Len(&f->body);
|
||||
for (p = mark; (p = strchr(p, '$')) != NULL;) {
|
||||
char ch, ech;
|
||||
ch = *++p;
|
||||
if ((ch == '(' && (ech = ')', 1)) || (ch == '{' && (ech = '}', 1))) {
|
||||
p++;
|
||||
/* Check variable name against the .for loop variables */
|
||||
SubstVarLong(f, &p, &mark, ech);
|
||||
continue;
|
||||
}
|
||||
if (ch == '\0')
|
||||
break;
|
||||
|
||||
SubstVarShort(f, ch, &p, &mark);
|
||||
}
|
||||
Buf_AddBytesBetween(&f->curBody, mark, body_end);
|
||||
|
||||
*out_len = Buf_Len(&f->curBody);
|
||||
cmds_str = Buf_GetAll(&f->curBody, NULL);
|
||||
DEBUG1(FOR, "For: loop body:\n%s", cmds_str);
|
||||
|
||||
f->sub_next += f->vars.len;
|
||||
|
||||
return cmds_str;
|
||||
*out_len = f->curBody.len;
|
||||
return f->curBody.data;
|
||||
}
|
||||
|
||||
/* Run the for loop, imitating the actions of an include file. */
|
||||
/* Run the .for loop, imitating the actions of an include file. */
|
||||
void
|
||||
For_Run(int lineno)
|
||||
{
|
||||
@ -470,10 +492,13 @@ For_Run(int lineno)
|
||||
accumFor = NULL;
|
||||
|
||||
if (f->items.len == 0) {
|
||||
/* Nothing to expand - possibly due to an earlier syntax error. */
|
||||
/*
|
||||
* Nothing to expand - possibly due to an earlier syntax
|
||||
* error.
|
||||
*/
|
||||
For_Free(f);
|
||||
return;
|
||||
}
|
||||
|
||||
Parse_SetInput(NULL, lineno, -1, ForIterate, f);
|
||||
Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
|
||||
}
|
||||
|
39
hash.c
39
hash.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $ */
|
||||
/* $NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -74,7 +74,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $");
|
||||
|
||||
/*
|
||||
* The ratio of # entries to # buckets at which we rebuild the table to
|
||||
@ -86,10 +86,13 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.57 2020/11/14 21:29:44 rillig Exp $");
|
||||
static unsigned int
|
||||
hash(const char *key, size_t *out_keylen)
|
||||
{
|
||||
unsigned int h = 0;
|
||||
const char *p = key;
|
||||
while (*p != '\0')
|
||||
h = (h << 5) - h + (unsigned char)*p++;
|
||||
unsigned int h;
|
||||
const char *p;
|
||||
|
||||
h = 0;
|
||||
for (p = key; *p != '\0'; p++)
|
||||
h = 31 * h + (unsigned char)*p;
|
||||
|
||||
if (out_keylen != NULL)
|
||||
*out_keylen = (size_t)(p - key);
|
||||
return h;
|
||||
@ -177,8 +180,10 @@ HashTable_FindValue(HashTable *t, const char *key)
|
||||
return he != NULL ? he->value : NULL;
|
||||
}
|
||||
|
||||
/* Find the value corresponding to the key and the precomputed hash,
|
||||
* or return NULL. */
|
||||
/*
|
||||
* Find the value corresponding to the key and the precomputed hash,
|
||||
* or return NULL.
|
||||
*/
|
||||
void *
|
||||
HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
|
||||
{
|
||||
@ -186,8 +191,10 @@ HashTable_FindValueHash(HashTable *t, const char *key, unsigned int h)
|
||||
return he != NULL ? he->value : NULL;
|
||||
}
|
||||
|
||||
/* Make the hash table larger. Any bucket numbers from the old table become
|
||||
* invalid; the hash codes stay valid though. */
|
||||
/*
|
||||
* Make the hash table larger. Any bucket numbers from the old table become
|
||||
* invalid; the hash codes stay valid though.
|
||||
*/
|
||||
static void
|
||||
HashTable_Enlarge(HashTable *t)
|
||||
{
|
||||
@ -221,8 +228,10 @@ HashTable_Enlarge(HashTable *t)
|
||||
t->maxchain = 0;
|
||||
}
|
||||
|
||||
/* Find or create an entry corresponding to the key.
|
||||
* Return in out_isNew whether a new entry has been created. */
|
||||
/*
|
||||
* Find or create an entry corresponding to the key.
|
||||
* Return in out_isNew whether a new entry has been created.
|
||||
*/
|
||||
HashEntry *
|
||||
HashTable_CreateEntry(HashTable *t, const char *key, Boolean *out_isNew)
|
||||
{
|
||||
@ -288,8 +297,10 @@ HashIter_Init(HashIter *hi, HashTable *t)
|
||||
hi->entry = NULL;
|
||||
}
|
||||
|
||||
/* Return the next entry in the hash table, or NULL if the end of the table
|
||||
* is reached. */
|
||||
/*
|
||||
* Return the next entry in the hash table, or NULL if the end of the table
|
||||
* is reached.
|
||||
*/
|
||||
HashEntry *
|
||||
HashIter_Next(HashIter *hi)
|
||||
{
|
||||
|
40
hash.h
40
hash.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: hash.h,v 1.33 2020/11/14 21:29:44 rillig Exp $ */
|
||||
/* $NetBSD: hash.h,v 1.38 2020/12/15 01:23:55 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -103,6 +103,11 @@ typedef struct HashIter {
|
||||
HashEntry *entry; /* Next entry to check in current bucket. */
|
||||
} HashIter;
|
||||
|
||||
/* A set of strings. */
|
||||
typedef struct HashSet {
|
||||
HashTable tbl;
|
||||
} HashSet;
|
||||
|
||||
MAKE_INLINE void *
|
||||
HashEntry_Get(HashEntry *h)
|
||||
{
|
||||
@ -129,4 +134,37 @@ void HashTable_DebugStats(HashTable *, const char *);
|
||||
void HashIter_Init(HashIter *, HashTable *);
|
||||
HashEntry *HashIter_Next(HashIter *);
|
||||
|
||||
MAKE_INLINE void
|
||||
HashSet_Init(HashSet *set)
|
||||
{
|
||||
HashTable_Init(&set->tbl);
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
HashSet_Done(HashSet *set)
|
||||
{
|
||||
HashTable_Done(&set->tbl);
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
HashSet_Add(HashSet *set, const char *key)
|
||||
{
|
||||
Boolean isNew;
|
||||
|
||||
(void)HashTable_CreateEntry(&set->tbl, key, &isNew);
|
||||
return isNew;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
HashSet_Contains(HashSet *set, const char *key)
|
||||
{
|
||||
return HashTable_FindEntry(&set->tbl, key) != NULL;
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
HashIter_InitSet(HashIter *hi, HashSet *set)
|
||||
{
|
||||
HashIter_Init(hi, &set->tbl);
|
||||
}
|
||||
|
||||
#endif /* MAKE_HASH_H */
|
||||
|
87
import.sh
Executable file
87
import.sh
Executable file
@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Import bmake
|
||||
|
||||
ECHO=
|
||||
GIT=${GIT:-git}
|
||||
|
||||
# For consistency...
|
||||
Error() {
|
||||
echo ERROR: ${1+"$@"} >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
Cd() {
|
||||
[ $# -eq 1 ] || Error "Cd() takes a single parameter."
|
||||
cd $1 || Error "cannot \"cd $1\" from $PWD"
|
||||
}
|
||||
|
||||
# Call this function and then follow it by any specific import script additions
|
||||
option_parsing() {
|
||||
local _shift=$#
|
||||
# Parse command line options
|
||||
while :
|
||||
do
|
||||
case "$1" in
|
||||
*=*) eval "$1"; shift;;
|
||||
--) shift; break;;
|
||||
-a) TARBALL=$2; shift 2;;
|
||||
-n) ECHO=echo; shift;;
|
||||
-P) PR=$2; shift 2;;
|
||||
-r) REVIEWER=$2; shift 2;;
|
||||
-u) url=$2; shift 2;;
|
||||
-h) echo "Usage:";
|
||||
echo " "$0 '[-ahnPr] [TARBALL=] [PR=] [REVIEWER=]'
|
||||
echo " "$0 '-a <filename> # (a)rchive'
|
||||
echo " "$0 '-h # print usage'
|
||||
echo " "$0 '-n # do not import, check only.'
|
||||
echo " "$0 '-P <PR Number> # Use PR'
|
||||
echo " "$0 '-r <reviewer(s) list> # (r)eviewed by'
|
||||
echo " "$0 'PR=<PR Number>'
|
||||
echo " "$0 'REVIEWER=<reviewer(s) list>'
|
||||
exit 1;;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
return $(($_shift - $#))
|
||||
}
|
||||
|
||||
###
|
||||
|
||||
option_parsing "$@"
|
||||
shift $?
|
||||
|
||||
TF=/tmp/.$USER.$$
|
||||
Cd `dirname $0`
|
||||
test -s ${TARBALL:-/dev/null} || Error need TARBALL
|
||||
here=`pwd`
|
||||
# thing should match what the TARBALL contains
|
||||
thing=`basename $here`
|
||||
|
||||
case "$thing" in
|
||||
bmake) (cd .. && tar zxf $TARBALL);;
|
||||
*) Error "we should be in bmake";;
|
||||
esac
|
||||
|
||||
VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'`
|
||||
|
||||
rm -f *~
|
||||
mkdir -p ../tmp
|
||||
|
||||
if [ -z "$ECHO" ]; then
|
||||
# new files are handled automatically
|
||||
# but we need to rm if needed
|
||||
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
|
||||
test -s $TF.rm && xargs rm -f < $TF.rm
|
||||
$GIT add -A
|
||||
$GIT diff --staged | tee ../tmp/bmake-import.diff
|
||||
echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh
|
||||
echo "After you commit, run $here/../tmp/bmake-post.sh"
|
||||
else
|
||||
# FILES is kept sorted so we can determine what was added and deleted
|
||||
$GIT diff FILES | sed -n '/^+[^+]/s,^+,,p' > $TF.add
|
||||
$GIT diff FILES | sed -n '/^-[^-]/s,^-,,p' > $TF.rm
|
||||
test -s $TF.rm && { echo Removing:; cat $TF.rm; }
|
||||
test -s $TF.add && { echo Adding:; cat $TF.add; }
|
||||
$GIT diff
|
||||
fi
|
32
job.h
32
job.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: job.h,v 1.63 2020/11/14 13:27:01 rillig Exp $ */
|
||||
/* $NetBSD: job.h,v 1.71 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -125,20 +125,8 @@ typedef enum JobStatus {
|
||||
JOB_ST_FINISHED = 4 /* Job is done (ie after SIGCHILD) */
|
||||
} JobStatus;
|
||||
|
||||
typedef enum JobFlags {
|
||||
JOB_NONE = 0,
|
||||
/* Ignore non-zero exits */
|
||||
JOB_IGNERR = 1 << 0,
|
||||
/* no output */
|
||||
JOB_SILENT = 1 << 1,
|
||||
/* Target is a special one. i.e. run it locally
|
||||
* if we can't export it and maxLocal is 0 */
|
||||
JOB_SPECIAL = 1 << 2,
|
||||
/* we've sent 'set -x' */
|
||||
JOB_TRACED = 1 << 10
|
||||
} JobFlags;
|
||||
|
||||
/* A Job manages the shell commands that are run to create a single target.
|
||||
/*
|
||||
* A Job manages the shell commands that are run to create a single target.
|
||||
* Each job is run in a separate subprocess by a shell. Several jobs can run
|
||||
* in parallel.
|
||||
*
|
||||
@ -148,7 +136,8 @@ typedef enum JobFlags {
|
||||
*
|
||||
* When a job is finished, Make_Update updates all parents of the node
|
||||
* that was just remade, marking them as ready to be made next if all
|
||||
* other dependencies are finished as well. */
|
||||
* other dependencies are finished as well.
|
||||
*/
|
||||
typedef struct Job {
|
||||
/* The process ID of the shell running the commands */
|
||||
int pid;
|
||||
@ -161,8 +150,7 @@ typedef struct Job {
|
||||
* 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. */
|
||||
/* This is where the shell commands go. */
|
||||
FILE *cmdFILE;
|
||||
|
||||
int exit_status; /* from wait4() in signal handler */
|
||||
@ -171,7 +159,12 @@ typedef struct Job {
|
||||
|
||||
Boolean suspended;
|
||||
|
||||
JobFlags flags; /* Flags to control treatment of job */
|
||||
/* Ignore non-zero exits */
|
||||
Boolean ignerr;
|
||||
/* Output the command before or instead of running it. */
|
||||
Boolean echo;
|
||||
/* Target is a special one. */
|
||||
Boolean special;
|
||||
|
||||
int inPipe; /* Pipe for reading output from job */
|
||||
int outPipe; /* Pipe for writing control commands */
|
||||
@ -211,5 +204,6 @@ Boolean Job_TokenWithdraw(void);
|
||||
void Job_ServerStart(int, int, int);
|
||||
void Job_SetPrefix(void);
|
||||
Boolean Job_RunTarget(const char *, const char *);
|
||||
void Job_FlagsToString(const Job *, char *, size_t);
|
||||
|
||||
#endif /* MAKE_JOB_H */
|
||||
|
134
lst.c
134
lst.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $ */
|
||||
/* $NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -34,21 +34,17 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.92 2020/11/08 01:29:26 rillig Exp $");
|
||||
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
MAKE_RCSID("$NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $");
|
||||
|
||||
static ListNode *
|
||||
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
|
||||
{
|
||||
ListNode *ln = bmake_malloc(sizeof *ln);
|
||||
|
||||
ln->prev = prev;
|
||||
ln->next = next;
|
||||
ln->datum = datum;
|
||||
|
||||
return ln;
|
||||
}
|
||||
|
||||
@ -57,16 +53,12 @@ List *
|
||||
Lst_New(void)
|
||||
{
|
||||
List *list = bmake_malloc(sizeof *list);
|
||||
|
||||
list->first = NULL;
|
||||
list->last = NULL;
|
||||
|
||||
Lst_Init(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Free a list and all its nodes. The node data are not freed though. */
|
||||
void
|
||||
Lst_Free(List *list)
|
||||
Lst_Done(List *list)
|
||||
{
|
||||
ListNode *ln, *next;
|
||||
|
||||
@ -74,14 +66,10 @@ Lst_Free(List *list)
|
||||
next = ln->next;
|
||||
free(ln);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
/* Destroy a list and free all its resources. The freeProc is called with the
|
||||
* datum from each node in turn before the node is freed. */
|
||||
void
|
||||
Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
Lst_DoneCall(List *list, LstFreeProc freeProc)
|
||||
{
|
||||
ListNode *ln, *next;
|
||||
|
||||
@ -90,7 +78,25 @@ Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
freeProc(ln->datum);
|
||||
free(ln);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free a list and all its nodes. The node data are not freed though. */
|
||||
void
|
||||
Lst_Free(List *list)
|
||||
{
|
||||
|
||||
Lst_Done(list);
|
||||
free(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a list and free all its resources. The freeProc is called with the
|
||||
* datum from each node in turn before the node is freed.
|
||||
*/
|
||||
void
|
||||
Lst_Destroy(List *list, LstFreeProc freeProc)
|
||||
{
|
||||
Lst_DoneCall(list, freeProc);
|
||||
free(list);
|
||||
}
|
||||
|
||||
@ -150,8 +156,10 @@ Lst_Append(List *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. */
|
||||
/*
|
||||
* Remove the given node from the given list.
|
||||
* The datum stored in the node must be freed by the caller, if necessary.
|
||||
*/
|
||||
void
|
||||
Lst_Remove(List *list, ListNode *ln)
|
||||
{
|
||||
@ -177,17 +185,21 @@ LstNode_Set(ListNode *ln, void *datum)
|
||||
ln->datum = datum;
|
||||
}
|
||||
|
||||
/* Replace the datum in the given node with NULL.
|
||||
* Having NULL values in a list is unusual though. */
|
||||
/*
|
||||
* Replace the datum in the given node with NULL.
|
||||
* Having NULL values in a list is unusual though.
|
||||
*/
|
||||
void
|
||||
LstNode_SetNull(ListNode *ln)
|
||||
{
|
||||
ln->datum = NULL;
|
||||
}
|
||||
|
||||
/* Return the first node that contains the given datum, or NULL.
|
||||
/*
|
||||
* Return the first node that contains the given datum, or NULL.
|
||||
*
|
||||
* Time complexity: O(length(list)) */
|
||||
* Time complexity: O(length(list))
|
||||
*/
|
||||
ListNode *
|
||||
Lst_FindDatum(List *list, const void *datum)
|
||||
{
|
||||
@ -202,22 +214,10 @@ Lst_FindDatum(List *list, const void *datum)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
|
||||
{
|
||||
ListNode *ln;
|
||||
int result = 0;
|
||||
|
||||
for (ln = list->first; ln != NULL; ln = ln->next) {
|
||||
result = proc(ln->datum, procData);
|
||||
if (result != 0)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Move all nodes from src to the end of dst.
|
||||
* The source list is destroyed and freed. */
|
||||
/*
|
||||
* Move all nodes from src to the end of dst.
|
||||
* The source list becomes empty but is not freed.
|
||||
*/
|
||||
void
|
||||
Lst_MoveAll(List *dst, List *src)
|
||||
{
|
||||
@ -230,36 +230,26 @@ Lst_MoveAll(List *dst, List *src)
|
||||
|
||||
dst->last = src->last;
|
||||
}
|
||||
free(src);
|
||||
}
|
||||
|
||||
/* Copy the element data from src to the start of dst. */
|
||||
void
|
||||
Lst_PrependAll(List *dst, List *src)
|
||||
{
|
||||
ListNode *node;
|
||||
for (node = src->last; node != NULL; node = node->prev)
|
||||
Lst_Prepend(dst, node->datum);
|
||||
ListNode *ln;
|
||||
|
||||
for (ln = src->last; ln != NULL; ln = ln->prev)
|
||||
Lst_Prepend(dst, ln->datum);
|
||||
}
|
||||
|
||||
/* Copy the element data from src to the end of dst. */
|
||||
void
|
||||
Lst_AppendAll(List *dst, List *src)
|
||||
{
|
||||
ListNode *node;
|
||||
for (node = src->first; node != NULL; node = node->next)
|
||||
Lst_Append(dst, node->datum);
|
||||
}
|
||||
ListNode *ln;
|
||||
|
||||
/*
|
||||
* for using the list as a queue
|
||||
*/
|
||||
|
||||
/* Add the datum to the tail of the given list. */
|
||||
void
|
||||
Lst_Enqueue(List *list, void *datum)
|
||||
{
|
||||
Lst_Append(list, datum);
|
||||
for (ln = src->first; ln != NULL; ln = ln->next)
|
||||
Lst_Append(dst, ln->datum);
|
||||
}
|
||||
|
||||
/* Remove and return the datum at the head of the given list. */
|
||||
@ -276,26 +266,30 @@ void
|
||||
Vector_Init(Vector *v, size_t itemSize)
|
||||
{
|
||||
v->len = 0;
|
||||
v->priv_cap = 10;
|
||||
v->cap = 10;
|
||||
v->itemSize = itemSize;
|
||||
v->items = bmake_malloc(v->priv_cap * v->itemSize);
|
||||
v->items = bmake_malloc(v->cap * v->itemSize);
|
||||
}
|
||||
|
||||
/* Add space for a new item to the vector and return a pointer to that space.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
/*
|
||||
* Add space for a new item to the vector and return a pointer to that space.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
void *
|
||||
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);
|
||||
if (v->len >= v->cap) {
|
||||
v->cap *= 2;
|
||||
v->items = bmake_realloc(v->items, v->cap * v->itemSize);
|
||||
}
|
||||
v->len++;
|
||||
return Vector_Get(v, v->len - 1);
|
||||
}
|
||||
|
||||
/* Return the pointer to the last item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
/*
|
||||
* Return the pointer to the last item in the vector.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
void *
|
||||
Vector_Pop(Vector *v)
|
||||
{
|
||||
@ -303,9 +297,3 @@ Vector_Pop(Vector *v)
|
||||
v->len--;
|
||||
return Vector_Get(v, v->len);
|
||||
}
|
||||
|
||||
void
|
||||
Vector_Done(Vector *v)
|
||||
{
|
||||
free(v->items);
|
||||
}
|
||||
|
72
lst.h
72
lst.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: lst.h,v 1.85 2020/11/10 00:32:12 rillig Exp $ */
|
||||
/* $NetBSD: lst.h,v 1.95 2021/01/03 21:12:03 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -78,9 +78,14 @@
|
||||
#ifndef MAKE_LST_H
|
||||
#define MAKE_LST_H
|
||||
|
||||
#include <sys/param.h>
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
#include <inttypes.h>
|
||||
#elif defined(HAVE_STDINT_H)
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/* A doubly-linked list of pointers. */
|
||||
typedef struct List List;
|
||||
@ -90,36 +95,45 @@ 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 */
|
||||
ListNode *first;
|
||||
ListNode *last;
|
||||
};
|
||||
|
||||
/* Free the datum of a node, called before freeing the node itself. */
|
||||
typedef void LstFreeProc(void *);
|
||||
/* An action for Lst_ForEachUntil and Lst_ForEachUntilConcurrent. */
|
||||
typedef int LstActionUntilProc(void *datum, void *args);
|
||||
|
||||
/* Create or destroy a list */
|
||||
|
||||
/* Create a new list. */
|
||||
List *Lst_New(void);
|
||||
/* Free the list nodes, but not the list itself. */
|
||||
void Lst_Done(List *);
|
||||
/* Free the list nodes, freeing the node data using the given function. */
|
||||
void Lst_DoneCall(List *, LstFreeProc);
|
||||
/* Free the list, leaving the node data unmodified. */
|
||||
void Lst_Free(List *);
|
||||
/* Free the list, freeing the node data using the given function. */
|
||||
void Lst_Destroy(List *, LstFreeProc);
|
||||
|
||||
#define LST_INIT { NULL, NULL }
|
||||
|
||||
/* Initialize a list, without memory allocation. */
|
||||
MAKE_INLINE void
|
||||
Lst_Init(List *list)
|
||||
{
|
||||
list->first = NULL;
|
||||
list->last = NULL;
|
||||
}
|
||||
|
||||
/* Get information about a list */
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
Lst_IsEmpty(List *list) { return list->first == NULL; }
|
||||
Lst_IsEmpty(List *list)
|
||||
{ return list->first == NULL; }
|
||||
|
||||
/* Find the first node that contains the given datum, or NULL. */
|
||||
ListNode *Lst_FindDatum(List *, const void *);
|
||||
@ -145,34 +159,34 @@ void LstNode_Set(ListNode *, void *);
|
||||
/* Set the value of the node to NULL. Having NULL in a list is unusual. */
|
||||
void LstNode_SetNull(ListNode *);
|
||||
|
||||
/* Iterating over a list, using a callback function */
|
||||
|
||||
/* Run the action for each datum of the list, until the action returns
|
||||
* non-zero.
|
||||
*
|
||||
* During this iteration, the list must not be modified structurally. */
|
||||
int Lst_ForEachUntil(List *, LstActionUntilProc, void *);
|
||||
|
||||
/* Using the list as a queue */
|
||||
|
||||
/* Add a datum at the tail of the queue. */
|
||||
void Lst_Enqueue(List *, void *);
|
||||
MAKE_INLINE void
|
||||
Lst_Enqueue(List *list, void *datum) {
|
||||
Lst_Append(list, datum);
|
||||
}
|
||||
|
||||
/* Remove the head node of the queue and return its datum. */
|
||||
void *Lst_Dequeue(List *);
|
||||
|
||||
/* A vector is an ordered collection of items, allowing for fast indexed
|
||||
* access. */
|
||||
/*
|
||||
* A vector is an ordered collection of items, allowing for fast indexed
|
||||
* access.
|
||||
*/
|
||||
typedef struct Vector {
|
||||
void *items; /* memory holding the items */
|
||||
size_t itemSize; /* size of a single item in bytes */
|
||||
size_t itemSize; /* size of a single item */
|
||||
size_t len; /* number of actually usable elements */
|
||||
size_t priv_cap; /* capacity */
|
||||
size_t cap; /* capacity */
|
||||
} Vector;
|
||||
|
||||
void Vector_Init(Vector *, size_t);
|
||||
|
||||
/* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation. */
|
||||
/*
|
||||
* Return the pointer to the given item in the vector.
|
||||
* The returned data is valid until the next modifying operation.
|
||||
*/
|
||||
MAKE_INLINE void *
|
||||
Vector_Get(Vector *v, size_t i)
|
||||
{
|
||||
@ -182,6 +196,10 @@ Vector_Get(Vector *v, size_t i)
|
||||
|
||||
void *Vector_Push(Vector *);
|
||||
void *Vector_Pop(Vector *);
|
||||
void Vector_Done(Vector *);
|
||||
|
||||
MAKE_INLINE void
|
||||
Vector_Done(Vector *v) {
|
||||
free(v->items);
|
||||
}
|
||||
|
||||
#endif /* MAKE_LST_H */
|
||||
|
362
main.c
362
main.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: main.c,v 1.476 2020/11/16 22:08:20 rillig Exp $ */
|
||||
/* $NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -68,7 +68,8 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* The main file for this entire program. Exit routines etc. reside here.
|
||||
/*
|
||||
* The main file for this entire program. Exit routines etc. reside here.
|
||||
*
|
||||
* Utility functions defined in this file:
|
||||
*
|
||||
@ -109,17 +110,13 @@
|
||||
#include "trace.h"
|
||||
|
||||
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: main.c,v 1.476 2020/11/16 22:08:20 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $");
|
||||
#if defined(MAKE_NATIVE) && !defined(lint)
|
||||
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
|
||||
"The Regents of the University of California. "
|
||||
"All rights reserved.");
|
||||
#endif
|
||||
|
||||
#ifndef DEFMAXLOCAL
|
||||
#define DEFMAXLOCAL DEFMAXJOBS
|
||||
#endif
|
||||
|
||||
#ifndef __arraycount
|
||||
# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
|
||||
#endif
|
||||
@ -133,7 +130,6 @@ Boolean deleteOnError; /* .DELETE_ON_ERROR: set */
|
||||
static int maxJobTokens; /* -j argument */
|
||||
Boolean enterFlagObj; /* -w and objdir != srcdir */
|
||||
|
||||
Boolean preserveUndefined;
|
||||
static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */
|
||||
Boolean doing_depend; /* Set while reading .depend */
|
||||
static Boolean jobsRunning; /* TRUE if the jobs might be running */
|
||||
@ -144,13 +140,13 @@ static void purge_relative_cached_realpaths(void);
|
||||
static Boolean ignorePWD; /* if we use -C, PWD is meaningless */
|
||||
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
|
||||
char curdir[MAXPATHLEN + 1]; /* Startup directory */
|
||||
char *progname; /* the program name */
|
||||
const char *progname;
|
||||
char *makeDependfile;
|
||||
pid_t myPid;
|
||||
int makelevel;
|
||||
|
||||
Boolean forceJobs = FALSE;
|
||||
static int errors = 0;
|
||||
static int main_errors = 0;
|
||||
static HashTable cached_realpaths;
|
||||
|
||||
/*
|
||||
@ -167,16 +163,16 @@ explode(const char *flags)
|
||||
if (flags == NULL)
|
||||
return NULL;
|
||||
|
||||
for (f = flags; *f; f++)
|
||||
for (f = flags; *f != '\0'; f++)
|
||||
if (!ch_isalpha(*f))
|
||||
break;
|
||||
|
||||
if (*f)
|
||||
if (*f != '\0')
|
||||
return bmake_strdup(flags);
|
||||
|
||||
len = strlen(flags);
|
||||
st = nf = bmake_malloc(len * 3 + 1);
|
||||
while (*flags) {
|
||||
while (*flags != '\0') {
|
||||
*nf++ = '-';
|
||||
*nf++ = *flags++;
|
||||
*nf++ = ' ';
|
||||
@ -251,7 +247,7 @@ parse_debug_options(const char *argvalue)
|
||||
const char *modules;
|
||||
DebugFlags debug = opts.debug;
|
||||
|
||||
for (modules = argvalue; *modules; ++modules) {
|
||||
for (modules = argvalue; *modules != '\0'; ++modules) {
|
||||
switch (*modules) {
|
||||
case '0': /* undocumented, only intended for tests */
|
||||
debug = DEBUG_NONE;
|
||||
@ -296,7 +292,7 @@ parse_debug_options(const char *argvalue)
|
||||
debug |= DEBUG_JOB;
|
||||
break;
|
||||
case 'L':
|
||||
opts.lint = TRUE;
|
||||
opts.strict = TRUE;
|
||||
break;
|
||||
case 'l':
|
||||
debug |= DEBUG_LOUD;
|
||||
@ -381,7 +377,7 @@ MainParseArgChdir(const char *argvalue)
|
||||
if (chdir(argvalue) == -1) {
|
||||
(void)fprintf(stderr, "%s: chdir %s: %s\n",
|
||||
progname, argvalue, strerror(errno));
|
||||
exit(1);
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
if (getcwd(curdir, MAXPATHLEN) == NULL) {
|
||||
(void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
|
||||
@ -434,7 +430,7 @@ MainParseArgJobs(const char *argvalue)
|
||||
(void)fprintf(stderr,
|
||||
"%s: illegal argument to -j -- must be positive integer!\n",
|
||||
progname);
|
||||
exit(1); /* XXX: why not 2? */
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL);
|
||||
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
|
||||
@ -504,7 +500,7 @@ MainParseArg(char c, const char *argvalue)
|
||||
case 'V':
|
||||
case 'v':
|
||||
opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED;
|
||||
Lst_Append(opts.variables, bmake_strdup(argvalue));
|
||||
Lst_Append(&opts.variables, bmake_strdup(argvalue));
|
||||
/* XXX: Why always -V? */
|
||||
Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL);
|
||||
Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL);
|
||||
@ -532,7 +528,7 @@ MainParseArg(char c, const char *argvalue)
|
||||
Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL);
|
||||
break;
|
||||
case 'f':
|
||||
Lst_Append(opts.makefiles, bmake_strdup(argvalue));
|
||||
Lst_Append(&opts.makefiles, bmake_strdup(argvalue));
|
||||
break;
|
||||
case 'i':
|
||||
opts.ignoreErrors = TRUE;
|
||||
@ -581,13 +577,15 @@ MainParseArg(char c, const char *argvalue)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Parse the given arguments. Called from main() and from
|
||||
/*
|
||||
* Parse the given arguments. Called from main() and from
|
||||
* Main_ParseArgLine() when the .MAKEFLAGS target is used.
|
||||
*
|
||||
* The arguments must be treated as read-only and will be freed after the
|
||||
* call.
|
||||
*
|
||||
* XXX: Deal with command line overriding .MAKEFLAGS in makefile */
|
||||
* XXX: Deal with command line overriding .MAKEFLAGS in makefile
|
||||
*/
|
||||
static void
|
||||
MainParseArgs(int argc, char **argv)
|
||||
{
|
||||
@ -668,7 +666,7 @@ MainParseArgs(int argc, char **argv)
|
||||
Punt("illegal (null) argument.");
|
||||
if (argv[1][0] == '-' && !dashDash)
|
||||
goto rearg;
|
||||
Lst_Append(opts.create, bmake_strdup(argv[1]));
|
||||
Lst_Append(&opts.create, bmake_strdup(argv[1]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,10 +677,12 @@ MainParseArgs(int argc, char **argv)
|
||||
usage();
|
||||
}
|
||||
|
||||
/* Break a line of arguments into words and parse them.
|
||||
/*
|
||||
* Break a line of arguments into words and parse them.
|
||||
*
|
||||
* Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and
|
||||
* by main() when reading the MAKEFLAGS environment variable. */
|
||||
* by main() when reading the MAKEFLAGS environment variable.
|
||||
*/
|
||||
void
|
||||
Main_ParseArgLine(const char *line)
|
||||
{
|
||||
@ -691,6 +691,7 @@ Main_ParseArgLine(const char *line)
|
||||
|
||||
if (line == NULL)
|
||||
return;
|
||||
/* XXX: don't use line as an iterator variable */
|
||||
for (; *line == ' '; ++line)
|
||||
continue;
|
||||
if (line[0] == '\0')
|
||||
@ -711,10 +712,9 @@ Main_ParseArgLine(const char *line)
|
||||
}
|
||||
#endif
|
||||
{
|
||||
void *freeIt;
|
||||
const char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &freeIt);
|
||||
buf = str_concat3(argv0, " ", line);
|
||||
free(freeIt);
|
||||
FStr argv0 = Var_Value(".MAKE", VAR_GLOBAL);
|
||||
buf = str_concat3(argv0.str, " ", line);
|
||||
FStr_Done(&argv0);
|
||||
}
|
||||
|
||||
words = Str_Words(buf, TRUE);
|
||||
@ -772,34 +772,34 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...)
|
||||
static Boolean
|
||||
SetVarObjdir(Boolean writable, const char *var, const char *suffix)
|
||||
{
|
||||
void *path_freeIt;
|
||||
const char *path = Var_Value(var, VAR_CMDLINE, &path_freeIt);
|
||||
const char *xpath;
|
||||
char *xpath_freeIt;
|
||||
FStr path = Var_Value(var, VAR_CMDLINE);
|
||||
FStr xpath;
|
||||
|
||||
if (path == NULL || path[0] == '\0') {
|
||||
bmake_free(path_freeIt);
|
||||
if (path.str == NULL || path.str[0] == '\0') {
|
||||
FStr_Done(&path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* expand variable substitutions */
|
||||
xpath = path;
|
||||
xpath_freeIt = NULL;
|
||||
if (strchr(path, '$') != 0) {
|
||||
(void)Var_Subst(path, VAR_GLOBAL, VARE_WANTRES, &xpath_freeIt);
|
||||
xpath = FStr_InitRefer(path.str);
|
||||
if (strchr(path.str, '$') != 0) {
|
||||
char *expanded;
|
||||
(void)Var_Subst(path.str, VAR_GLOBAL, VARE_WANTRES, &expanded);
|
||||
/* TODO: handle errors */
|
||||
xpath = xpath_freeIt;
|
||||
xpath = FStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
(void)Main_SetObjdir(writable, "%s%s", xpath, suffix);
|
||||
(void)Main_SetObjdir(writable, "%s%s", xpath.str, suffix);
|
||||
|
||||
bmake_free(xpath_freeIt);
|
||||
bmake_free(path_freeIt);
|
||||
FStr_Done(&xpath);
|
||||
FStr_Done(&path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Splits str into words, adding them to the list.
|
||||
* The string must be kept alive as long as the list. */
|
||||
/*
|
||||
* Splits str into words, adding them to the list.
|
||||
* The string must be kept alive as long as the list.
|
||||
*/
|
||||
int
|
||||
str2Lst_Append(StringList *lp, char *str)
|
||||
{
|
||||
@ -808,7 +808,7 @@ str2Lst_Append(StringList *lp, char *str)
|
||||
|
||||
const char *sep = " \t";
|
||||
|
||||
for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) {
|
||||
for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) {
|
||||
Lst_Append(lp, cp);
|
||||
n++;
|
||||
}
|
||||
@ -831,39 +831,38 @@ siginfo(int signo MAKE_ATTR_UNUSED)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allow makefiles some control over the mode we run in.
|
||||
*/
|
||||
void
|
||||
MakeMode(const char *mode)
|
||||
/* Allow makefiles some control over the mode we run in. */
|
||||
static void
|
||||
MakeMode(void)
|
||||
{
|
||||
char *mode_freeIt = NULL;
|
||||
FStr mode = FStr_InitRefer(NULL);
|
||||
|
||||
if (mode == NULL) {
|
||||
if (mode.str == NULL) {
|
||||
char *expanded;
|
||||
(void)Var_Subst("${" MAKE_MODE ":tl}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &mode_freeIt);
|
||||
VAR_GLOBAL, VARE_WANTRES, &expanded);
|
||||
/* TODO: handle errors */
|
||||
mode = mode_freeIt;
|
||||
mode = FStr_InitOwn(expanded);
|
||||
}
|
||||
|
||||
if (mode[0] != '\0') {
|
||||
if (strstr(mode, "compat")) {
|
||||
if (mode.str[0] != '\0') {
|
||||
if (strstr(mode.str, "compat") != NULL) {
|
||||
opts.compatMake = TRUE;
|
||||
forceJobs = FALSE;
|
||||
}
|
||||
#if USE_META
|
||||
if (strstr(mode, "meta"))
|
||||
meta_mode_init(mode);
|
||||
if (strstr(mode.str, "meta") != NULL)
|
||||
meta_mode_init(mode.str);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(mode_freeIt);
|
||||
FStr_Done(&mode);
|
||||
}
|
||||
|
||||
static void
|
||||
PrintVar(const char *varname, Boolean expandVars)
|
||||
{
|
||||
if (strchr(varname, '$')) {
|
||||
if (strchr(varname, '$') != NULL) {
|
||||
char *evalue;
|
||||
(void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue);
|
||||
/* TODO: handle errors */
|
||||
@ -880,10 +879,9 @@ PrintVar(const char *varname, Boolean expandVars)
|
||||
bmake_free(evalue);
|
||||
|
||||
} else {
|
||||
void *freeIt;
|
||||
const char *value = Var_Value(varname, VAR_GLOBAL, &freeIt);
|
||||
printf("%s\n", value ? value : "");
|
||||
bmake_free(freeIt);
|
||||
FStr value = Var_Value(varname, VAR_GLOBAL);
|
||||
printf("%s\n", value.str != NULL ? value.str : "");
|
||||
FStr_Done(&value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,7 +920,7 @@ doPrintVars(void)
|
||||
else
|
||||
expandVars = GetBooleanVar(".MAKE.EXPAND_VARIABLES", FALSE);
|
||||
|
||||
for (ln = opts.variables->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = opts.variables.first; ln != NULL; ln = ln->next) {
|
||||
const char *varname = ln->datum;
|
||||
PrintVar(varname, expandVars);
|
||||
}
|
||||
@ -931,7 +929,7 @@ doPrintVars(void)
|
||||
static Boolean
|
||||
runTargets(void)
|
||||
{
|
||||
GNodeList *targs; /* target nodes to create */
|
||||
GNodeList targs = LST_INIT; /* target nodes to create */
|
||||
Boolean outOfDate; /* FALSE if all targets up to date */
|
||||
|
||||
/*
|
||||
@ -940,10 +938,10 @@ runTargets(void)
|
||||
* we consult the parsing module to find the main target(s)
|
||||
* to create.
|
||||
*/
|
||||
if (Lst_IsEmpty(opts.create))
|
||||
targs = Parse_MainName();
|
||||
if (Lst_IsEmpty(&opts.create))
|
||||
Parse_MainName(&targs);
|
||||
else
|
||||
targs = Targ_FindList(opts.create);
|
||||
Targ_FindList(&targs, &opts.create);
|
||||
|
||||
if (!opts.compatMake) {
|
||||
/*
|
||||
@ -959,16 +957,16 @@ runTargets(void)
|
||||
}
|
||||
|
||||
/* Traverse the graph, checking on all the targets */
|
||||
outOfDate = Make_Run(targs);
|
||||
outOfDate = Make_Run(&targs);
|
||||
} else {
|
||||
/*
|
||||
* Compat_Init will take care of creating all the
|
||||
* targets as well as initializing the module.
|
||||
*/
|
||||
Compat_Run(targs);
|
||||
Compat_Run(&targs);
|
||||
outOfDate = FALSE;
|
||||
}
|
||||
Lst_Free(targs);
|
||||
Lst_Done(&targs); /* Don't free the nodes. */
|
||||
return outOfDate;
|
||||
}
|
||||
|
||||
@ -982,12 +980,12 @@ InitVarTargets(void)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
if (Lst_IsEmpty(opts.create)) {
|
||||
if (Lst_IsEmpty(&opts.create)) {
|
||||
Var_Set(".TARGETS", "", VAR_GLOBAL);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ln = opts.create->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = opts.create.first; ln != NULL; ln = ln->next) {
|
||||
char *name = ln->datum;
|
||||
Var_Append(".TARGETS", name, VAR_GLOBAL);
|
||||
}
|
||||
@ -1040,10 +1038,10 @@ InitVarMachineArch(void)
|
||||
const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
|
||||
size_t len = sizeof machine_arch_buf;
|
||||
|
||||
if (sysctl(mib, __arraycount(mib), machine_arch_buf,
|
||||
&len, NULL, 0) < 0) {
|
||||
(void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
|
||||
strerror(errno));
|
||||
if (sysctl(mib, (unsigned int)__arraycount(mib),
|
||||
machine_arch_buf, &len, NULL, 0) < 0) {
|
||||
(void)fprintf(stderr, "%s: sysctl failed (%s).\n",
|
||||
progname, strerror(errno));
|
||||
exit(2);
|
||||
}
|
||||
|
||||
@ -1077,21 +1075,20 @@ static void
|
||||
HandlePWD(const struct stat *curdir_st)
|
||||
{
|
||||
char *pwd;
|
||||
void *prefix_freeIt, *makeobjdir_freeIt;
|
||||
const char *makeobjdir;
|
||||
FStr prefix, makeobjdir;
|
||||
struct stat pwd_st;
|
||||
|
||||
if (ignorePWD || (pwd = getenv("PWD")) == NULL)
|
||||
return;
|
||||
|
||||
if (Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE, &prefix_freeIt) !=
|
||||
NULL) {
|
||||
bmake_free(prefix_freeIt);
|
||||
prefix = Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE);
|
||||
if (prefix.str != NULL) {
|
||||
FStr_Done(&prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE, &makeobjdir_freeIt);
|
||||
if (makeobjdir != NULL && strchr(makeobjdir, '$') != NULL)
|
||||
makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE);
|
||||
if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL)
|
||||
goto ignore_pwd;
|
||||
|
||||
if (stat(pwd, &pwd_st) == 0 &&
|
||||
@ -1100,7 +1097,7 @@ HandlePWD(const struct stat *curdir_st)
|
||||
(void)strncpy(curdir, pwd, MAXPATHLEN);
|
||||
|
||||
ignore_pwd:
|
||||
bmake_free(makeobjdir_freeIt);
|
||||
FStr_Done(&makeobjdir);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1118,7 +1115,7 @@ InitObjdir(const char *machine, const char *machine_arch)
|
||||
{
|
||||
Boolean writable;
|
||||
|
||||
Dir_InitDir(curdir);
|
||||
Dir_InitCur(curdir);
|
||||
writable = GetBooleanVar("MAKE_OBJDIR_CHECK_WRITABLE", TRUE);
|
||||
(void)Main_SetObjdir(FALSE, "%s", curdir);
|
||||
|
||||
@ -1147,35 +1144,37 @@ UnlimitFiles(void)
|
||||
static void
|
||||
CmdOpts_Init(void)
|
||||
{
|
||||
opts.compatMake = FALSE; /* No compat mode */
|
||||
opts.debug = 0; /* No debug verbosity, please. */
|
||||
opts.compatMake = FALSE;
|
||||
opts.debug = DEBUG_NONE;
|
||||
/* opts.debug_file has been initialized earlier */
|
||||
opts.lint = FALSE;
|
||||
opts.strict = FALSE;
|
||||
opts.debugVflag = FALSE;
|
||||
opts.checkEnvFirst = FALSE;
|
||||
opts.makefiles = Lst_New();
|
||||
Lst_Init(&opts.makefiles);
|
||||
opts.ignoreErrors = FALSE; /* Pay attention to non-zero returns */
|
||||
opts.maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */
|
||||
opts.maxJobs = 1;
|
||||
opts.keepgoing = FALSE; /* Stop on error */
|
||||
opts.noRecursiveExecute = FALSE; /* Execute all .MAKE targets */
|
||||
opts.noExecute = FALSE; /* Execute all commands */
|
||||
opts.queryFlag = FALSE; /* This is not just a check-run */
|
||||
opts.queryFlag = FALSE;
|
||||
opts.noBuiltins = FALSE; /* Read the built-in rules */
|
||||
opts.beSilent = FALSE; /* Print commands as executed */
|
||||
opts.touchFlag = FALSE; /* Actually update targets */
|
||||
opts.touchFlag = FALSE;
|
||||
opts.printVars = PVM_NONE;
|
||||
opts.variables = Lst_New();
|
||||
Lst_Init(&opts.variables);
|
||||
opts.parseWarnFatal = FALSE;
|
||||
opts.enterFlag = FALSE;
|
||||
opts.varNoExportEnv = FALSE;
|
||||
opts.create = Lst_New();
|
||||
Lst_Init(&opts.create);
|
||||
}
|
||||
|
||||
/* Initialize MAKE and .MAKE to the path of the executable, so that it can be
|
||||
/*
|
||||
* Initialize MAKE and .MAKE to the path of the executable, so that it can be
|
||||
* found by execvp(3) and the shells, even after a chdir.
|
||||
*
|
||||
* If it's a relative path and contains a '/', resolve it to an absolute path.
|
||||
* Otherwise keep it as is, assuming it will be found in the PATH. */
|
||||
* Otherwise keep it as is, assuming it will be found in the PATH.
|
||||
*/
|
||||
static void
|
||||
InitVarMake(const char *argv0)
|
||||
{
|
||||
@ -1183,18 +1182,21 @@ InitVarMake(const char *argv0)
|
||||
|
||||
if (argv0[0] != '/' && strchr(argv0, '/') != NULL) {
|
||||
char pathbuf[MAXPATHLEN];
|
||||
const char *abs = cached_realpath(argv0, pathbuf);
|
||||
const char *abspath = cached_realpath(argv0, pathbuf);
|
||||
struct stat st;
|
||||
if (abs != NULL && abs[0] == '/' && stat(make, &st) == 0)
|
||||
make = abs;
|
||||
if (abspath != NULL && abspath[0] == '/' &&
|
||||
stat(make, &st) == 0)
|
||||
make = abspath;
|
||||
}
|
||||
|
||||
Var_Set("MAKE", make, VAR_GLOBAL);
|
||||
Var_Set(".MAKE", make, VAR_GLOBAL);
|
||||
}
|
||||
|
||||
/* Add the directories from the colon-separated syspath to defSysIncPath.
|
||||
* After returning, the contents of syspath is unspecified. */
|
||||
/*
|
||||
* Add the directories from the colon-separated syspath to defSysIncPath.
|
||||
* After returning, the contents of syspath is unspecified.
|
||||
*/
|
||||
static void
|
||||
InitDefSysIncPath(char *syspath)
|
||||
{
|
||||
@ -1237,25 +1239,25 @@ static void
|
||||
ReadBuiltinRules(void)
|
||||
{
|
||||
StringListNode *ln;
|
||||
StringList *sysMkPath = Lst_New();
|
||||
StringList sysMkPath = LST_INIT;
|
||||
|
||||
Dir_Expand(_PATH_DEFSYSMK,
|
||||
Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath,
|
||||
sysMkPath);
|
||||
if (Lst_IsEmpty(sysMkPath))
|
||||
&sysMkPath);
|
||||
if (Lst_IsEmpty(&sysMkPath))
|
||||
Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK);
|
||||
|
||||
for (ln = sysMkPath->first; ln != NULL; ln = ln->next)
|
||||
for (ln = sysMkPath.first; ln != NULL; ln = ln->next)
|
||||
if (ReadMakefile(ln->datum) == 0)
|
||||
break;
|
||||
|
||||
if (ln == NULL)
|
||||
Fatal("%s: cannot open %s.",
|
||||
progname, (const char *)sysMkPath->first->datum);
|
||||
progname, (const char *)sysMkPath.first->datum);
|
||||
|
||||
/* Free the list but not the actual filenames since these may still
|
||||
* be used in GNodes. */
|
||||
Lst_Free(sysMkPath);
|
||||
Lst_Done(&sysMkPath);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1276,7 +1278,7 @@ InitMaxJobs(void)
|
||||
"%s: illegal value for .MAKE.JOBS "
|
||||
"-- must be positive integer!\n",
|
||||
progname);
|
||||
exit(1);
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
|
||||
if (n != opts.maxJobs) {
|
||||
@ -1315,7 +1317,7 @@ InitVpath(void)
|
||||
savec = *cp;
|
||||
*cp = '\0';
|
||||
/* Add directory to search path */
|
||||
(void)Dir_AddDir(dirSearchPath, path);
|
||||
(void)Dir_AddDir(&dirSearchPath, path);
|
||||
*cp = savec;
|
||||
path = cp + 1;
|
||||
} while (savec == ':');
|
||||
@ -1348,18 +1350,20 @@ ReadFirstDefaultMakefile(void)
|
||||
* since these makefiles do not come from the command line. They
|
||||
* also have different semantics in that only the first file that
|
||||
* is found is processed. See ReadAllMakefiles. */
|
||||
(void)str2Lst_Append(opts.makefiles, prefs);
|
||||
(void)str2Lst_Append(&opts.makefiles, prefs);
|
||||
|
||||
for (ln = opts.makefiles->first; ln != NULL; ln = ln->next)
|
||||
for (ln = opts.makefiles.first; ln != NULL; ln = ln->next)
|
||||
if (ReadMakefile(ln->datum) == 0)
|
||||
break;
|
||||
|
||||
free(prefs);
|
||||
}
|
||||
|
||||
/* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS.
|
||||
/*
|
||||
* Initialize variables such as MAKE, MACHINE, .MAKEFLAGS.
|
||||
* Initialize a few modules.
|
||||
* Parse the arguments from MAKEFLAGS and the command line. */
|
||||
* Parse the arguments from MAKEFLAGS and the command line.
|
||||
*/
|
||||
static void
|
||||
main_Init(int argc, char **argv)
|
||||
{
|
||||
@ -1380,10 +1384,7 @@ main_Init(int argc, char **argv)
|
||||
|
||||
InitRandom();
|
||||
|
||||
if ((progname = strrchr(argv[0], '/')) != NULL)
|
||||
progname++;
|
||||
else
|
||||
progname = argv[0];
|
||||
progname = str_basename(argv[0]);
|
||||
|
||||
UnlimitFiles();
|
||||
|
||||
@ -1469,6 +1470,10 @@ main_Init(int argc, char **argv)
|
||||
Var_Set(".MAKE.PID", tmp, VAR_GLOBAL);
|
||||
snprintf(tmp, sizeof tmp, "%u", getppid());
|
||||
Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL);
|
||||
snprintf(tmp, sizeof tmp, "%u", getuid());
|
||||
Var_Set(".MAKE.UID", tmp, VAR_GLOBAL);
|
||||
snprintf(tmp, sizeof tmp, "%u", getgid());
|
||||
Var_Set(".MAKE.GID", tmp, VAR_GLOBAL);
|
||||
}
|
||||
if (makelevel > 0) {
|
||||
char pn[1024];
|
||||
@ -1545,8 +1550,10 @@ main_Init(int argc, char **argv)
|
||||
InitDefSysIncPath(syspath);
|
||||
}
|
||||
|
||||
/* Read the system makefile followed by either makefile, Makefile or the
|
||||
* files given by the -f option. Exit on parse errors. */
|
||||
/*
|
||||
* Read the system makefile followed by either makefile, Makefile or the
|
||||
* files given by the -f option. Exit on parse errors.
|
||||
*/
|
||||
static void
|
||||
main_ReadFiles(void)
|
||||
{
|
||||
@ -1554,8 +1561,8 @@ main_ReadFiles(void)
|
||||
if (!opts.noBuiltins)
|
||||
ReadBuiltinRules();
|
||||
|
||||
if (!Lst_IsEmpty(opts.makefiles))
|
||||
ReadAllMakefiles(opts.makefiles);
|
||||
if (!Lst_IsEmpty(&opts.makefiles))
|
||||
ReadAllMakefiles(&opts.makefiles);
|
||||
else
|
||||
ReadFirstDefaultMakefile();
|
||||
}
|
||||
@ -1566,8 +1573,7 @@ main_PrepareMaking(void)
|
||||
{
|
||||
/* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */
|
||||
if (!opts.noBuiltins || opts.printVars == PVM_NONE) {
|
||||
/* ignore /dev/null and anything starting with "no" */
|
||||
(void)Var_Subst("${.MAKE.DEPENDFILE:N/dev/null:Nno*:T}",
|
||||
(void)Var_Subst("${.MAKE.DEPENDFILE}",
|
||||
VAR_CMDLINE, VARE_WANTRES, &makeDependfile);
|
||||
if (makeDependfile[0] != '\0') {
|
||||
/* TODO: handle errors */
|
||||
@ -1580,13 +1586,12 @@ main_PrepareMaking(void)
|
||||
if (enterFlagObj)
|
||||
printf("%s: Entering directory `%s'\n", progname, objdir);
|
||||
|
||||
MakeMode(NULL);
|
||||
MakeMode();
|
||||
|
||||
{
|
||||
void *freeIt;
|
||||
Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &freeIt),
|
||||
VAR_GLOBAL);
|
||||
bmake_free(freeIt);
|
||||
FStr makeflags = Var_Value(MAKEFLAGS, VAR_GLOBAL);
|
||||
Var_Append("MFLAGS", makeflags.str, VAR_GLOBAL);
|
||||
FStr_Done(&makeflags);
|
||||
}
|
||||
|
||||
InitMaxJobs();
|
||||
@ -1624,9 +1629,11 @@ main_PrepareMaking(void)
|
||||
Targ_PrintGraph(1);
|
||||
}
|
||||
|
||||
/* Make the targets.
|
||||
/*
|
||||
* Make the targets.
|
||||
* If the -v or -V options are given, print variables instead.
|
||||
* Return whether any of the targets is out-of-date. */
|
||||
* Return whether any of the targets is out-of-date.
|
||||
*/
|
||||
static Boolean
|
||||
main_Run(void)
|
||||
{
|
||||
@ -1644,9 +1651,13 @@ static void
|
||||
main_CleanUp(void)
|
||||
{
|
||||
#ifdef CLEANUP
|
||||
Lst_Destroy(opts.variables, free);
|
||||
Lst_Free(opts.makefiles); /* don't free, may be used in GNodes */
|
||||
Lst_Destroy(opts.create, free);
|
||||
Lst_DoneCall(&opts.variables, free);
|
||||
/*
|
||||
* Don't free the actual strings from opts.makefiles, they may be
|
||||
* used in GNodes.
|
||||
*/
|
||||
Lst_Done(&opts.makefiles);
|
||||
Lst_DoneCall(&opts.create, free);
|
||||
#endif
|
||||
|
||||
/* print the graph now it's been processed if the user requested it */
|
||||
@ -1677,7 +1688,7 @@ main_CleanUp(void)
|
||||
static int
|
||||
main_Exit(Boolean outOfDate)
|
||||
{
|
||||
if (opts.lint && (errors > 0 || Parse_GetFatals() > 0))
|
||||
if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0))
|
||||
return 2; /* Not 1 so -q can distinguish error */
|
||||
return outOfDate ? 1 : 0;
|
||||
}
|
||||
@ -1695,7 +1706,8 @@ main(int argc, char **argv)
|
||||
return main_Exit(outOfDate);
|
||||
}
|
||||
|
||||
/* Open and parse the given makefile, with all its side effects.
|
||||
/*
|
||||
* Open and parse the given makefile, with all its side effects.
|
||||
*
|
||||
* Results:
|
||||
* 0 if ok. -1 if couldn't open file.
|
||||
@ -1776,7 +1788,7 @@ char *
|
||||
Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
{
|
||||
const char *args[4]; /* Args for invoking the shell */
|
||||
int fds[2]; /* Pipe streams */
|
||||
int pipefds[2];
|
||||
int cpid; /* Child PID */
|
||||
int pid; /* PID from wait() */
|
||||
int status; /* command exit status */
|
||||
@ -1789,7 +1801,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
|
||||
*errfmt = NULL;
|
||||
|
||||
if (!shellName)
|
||||
if (shellName == NULL)
|
||||
Shell_Init();
|
||||
/*
|
||||
* Set up arguments for shell
|
||||
@ -1802,27 +1814,27 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
/*
|
||||
* Open a pipe for fetching its output
|
||||
*/
|
||||
if (pipe(fds) == -1) {
|
||||
if (pipe(pipefds) == -1) {
|
||||
*errfmt = "Couldn't create pipe for \"%s\"";
|
||||
goto bad;
|
||||
}
|
||||
|
||||
Var_ReexportVars();
|
||||
|
||||
/*
|
||||
* Fork
|
||||
*/
|
||||
switch (cpid = vFork()) {
|
||||
case 0:
|
||||
(void)close(fds[0]); /* Close input side of pipe */
|
||||
(void)close(pipefds[0]); /* Close input side of pipe */
|
||||
|
||||
/*
|
||||
* Duplicate the output stream to the shell's output, then
|
||||
* shut the extra thing down. Note we don't fetch the error
|
||||
* stream...why not? Why?
|
||||
*/
|
||||
(void)dup2(fds[1], 1);
|
||||
(void)close(fds[1]);
|
||||
|
||||
Var_ExportVars();
|
||||
(void)dup2(pipefds[1], 1);
|
||||
(void)close(pipefds[1]);
|
||||
|
||||
(void)execv(shellPath, UNCONST(args));
|
||||
_exit(1);
|
||||
@ -1833,14 +1845,14 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
goto bad;
|
||||
|
||||
default:
|
||||
(void)close(fds[1]); /* No need for the writing half */
|
||||
(void)close(pipefds[1]); /* No need for the writing half */
|
||||
|
||||
savederr = 0;
|
||||
Buf_Init(&buf);
|
||||
|
||||
do {
|
||||
char result[BUFSIZ];
|
||||
bytes_read = read(fds[0], result, sizeof result);
|
||||
bytes_read = read(pipefds[0], result, sizeof result);
|
||||
if (bytes_read > 0)
|
||||
Buf_AddBytes(&buf, result, (size_t)bytes_read);
|
||||
} while (bytes_read > 0 ||
|
||||
@ -1848,8 +1860,7 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
if (bytes_read == -1)
|
||||
savederr = errno;
|
||||
|
||||
(void)close(
|
||||
fds[0]); /* Close the input side of the pipe. */
|
||||
(void)close(pipefds[0]); /* Close the input side of the pipe. */
|
||||
|
||||
/* Wait for the process to exit. */
|
||||
while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0)
|
||||
@ -1879,10 +1890,12 @@ Cmd_Exec(const char *cmd, const char **errfmt)
|
||||
return bmake_strdup("");
|
||||
}
|
||||
|
||||
/* Print a printf-style error message.
|
||||
/*
|
||||
* Print a printf-style error message.
|
||||
*
|
||||
* In default mode, this error message has no consequences, in particular it
|
||||
* does not affect the exit status. Only in lint mode (-dL) it does. */
|
||||
* does not affect the exit status. Only in lint mode (-dL) it does.
|
||||
*/
|
||||
void
|
||||
Error(const char *fmt, ...)
|
||||
{
|
||||
@ -1904,14 +1917,16 @@ Error(const char *fmt, ...)
|
||||
break;
|
||||
err_file = stderr;
|
||||
}
|
||||
errors++;
|
||||
main_errors++;
|
||||
}
|
||||
|
||||
/* Wait for any running jobs to finish, then produce an error message,
|
||||
/*
|
||||
* Wait for any running jobs to finish, then produce an error message,
|
||||
* finally exit immediately.
|
||||
*
|
||||
* Exiting immediately differs from Parse_Error, which exits only after the
|
||||
* current top-level makefile has been parsed completely. */
|
||||
* current top-level makefile has been parsed completely.
|
||||
*/
|
||||
void
|
||||
Fatal(const char *fmt, ...)
|
||||
{
|
||||
@ -1935,8 +1950,10 @@ Fatal(const char *fmt, ...)
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
|
||||
/* Major exception once jobs are being created.
|
||||
* Kills all jobs, prints a message and exits. */
|
||||
/*
|
||||
* Major exception once jobs are being created.
|
||||
* Kills all jobs, prints a message and exits.
|
||||
*/
|
||||
void
|
||||
Punt(const char *fmt, ...)
|
||||
{
|
||||
@ -1964,12 +1981,14 @@ DieHorribly(void)
|
||||
if (DEBUG(GRAPH2))
|
||||
Targ_PrintGraph(2);
|
||||
Trace_Log(MAKEERROR, NULL);
|
||||
exit(2); /* Not 1, so -q can distinguish error */
|
||||
exit(2); /* Not 1 so -q can distinguish error */
|
||||
}
|
||||
|
||||
/* Called when aborting due to errors in child shell to signal abnormal exit.
|
||||
/*
|
||||
* Called when aborting due to errors in child shell to signal abnormal exit.
|
||||
* The program exits.
|
||||
* Errors is the number of errors encountered in Make_Make. */
|
||||
* Errors is the number of errors encountered in Make_Make.
|
||||
*/
|
||||
void
|
||||
Finish(int errs)
|
||||
{
|
||||
@ -2101,7 +2120,7 @@ shouldDieQuietly(GNode *gn, int bf)
|
||||
else if (bf >= 0)
|
||||
quietly = bf;
|
||||
else
|
||||
quietly = gn != NULL && (gn->type & OP_MAKE);
|
||||
quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0;
|
||||
}
|
||||
return quietly;
|
||||
}
|
||||
@ -2117,7 +2136,7 @@ SetErrorVars(GNode *gn)
|
||||
Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL);
|
||||
Var_Delete(".ERROR_CMD", VAR_GLOBAL);
|
||||
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
|
||||
if (cmd == NULL)
|
||||
@ -2126,8 +2145,10 @@ SetErrorVars(GNode *gn)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print some helpful information in case of an error.
|
||||
* The caller should exit soon after calling this function. */
|
||||
/*
|
||||
* Print some helpful information in case of an error.
|
||||
* The caller should exit soon after calling this function.
|
||||
*/
|
||||
void
|
||||
PrintOnError(GNode *gn, const char *msg)
|
||||
{
|
||||
@ -2138,16 +2159,16 @@ PrintOnError(GNode *gn, const char *msg)
|
||||
Var_Stats();
|
||||
}
|
||||
|
||||
/* we generally want to keep quiet if a sub-make died */
|
||||
if (shouldDieQuietly(gn, -1))
|
||||
return;
|
||||
if (errorNode != NULL)
|
||||
return; /* we've been here! */
|
||||
|
||||
if (msg != NULL)
|
||||
printf("%s", msg);
|
||||
printf("\n%s: stopped in %s\n", progname, curdir);
|
||||
|
||||
if (errorNode != NULL)
|
||||
return; /* we've been here! */
|
||||
/* we generally want to keep quiet if a sub-make died */
|
||||
if (shouldDieQuietly(gn, -1))
|
||||
return;
|
||||
|
||||
if (gn != NULL)
|
||||
SetErrorVars(gn);
|
||||
@ -2241,11 +2262,10 @@ mkTempFile(const char *pattern, char **out_fname)
|
||||
if ((fd = mkstemp(tfile)) < 0)
|
||||
Punt("Could not create temporary file %s: %s", tfile,
|
||||
strerror(errno));
|
||||
if (out_fname) {
|
||||
if (out_fname != NULL) {
|
||||
*out_fname = bmake_strdup(tfile);
|
||||
} else {
|
||||
unlink(
|
||||
tfile); /* we just want the descriptor */
|
||||
unlink(tfile); /* we just want the descriptor */
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
16
make-conf.h
16
make-conf.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: config.h,v 1.25 2020/10/19 23:43:55 rillig Exp $ */
|
||||
/* $NetBSD: config.h,v 1.28 2020/12/11 22:53:08 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
|
||||
@ -72,19 +72,6 @@
|
||||
* from: @(#)config.h 8.1 (Berkeley) 6/6/93
|
||||
*/
|
||||
|
||||
/*
|
||||
* DEFMAXJOBS
|
||||
* DEFMAXLOCAL
|
||||
* These control the default concurrency. On no occasion will more
|
||||
* than DEFMAXJOBS targets be created at once (locally or remotely).
|
||||
*
|
||||
* DEFMAXLOCAL is the highest number of targets which will be
|
||||
* created on the local machine at once. Note that if you set this
|
||||
* to 0, nothing will ever happen.
|
||||
*/
|
||||
#define DEFMAXJOBS 4
|
||||
#define DEFMAXLOCAL 1
|
||||
|
||||
/*
|
||||
* INCLUDES
|
||||
* LIBRARIES
|
||||
@ -126,7 +113,6 @@
|
||||
* Adhere to the POSIX 1003.2 draft for the make(1) program.
|
||||
* - Use MAKEFLAGS instead of MAKE to pick arguments from the
|
||||
* environment.
|
||||
* - Allow empty command lines if starting with tab.
|
||||
*/
|
||||
#define POSIX
|
||||
|
||||
|
16
make.1
16
make.1
@ -1,4 +1,4 @@
|
||||
.\" $NetBSD: make.1,v 1.292 2020/11/14 22:19:13 rillig Exp $
|
||||
.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1990, 1993
|
||||
.\" The Regents of the University of California. All rights reserved.
|
||||
@ -29,7 +29,7 @@
|
||||
.\"
|
||||
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
|
||||
.\"
|
||||
.Dd November 14, 2020
|
||||
.Dd December 22, 2020
|
||||
.Dt MAKE 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1019,6 +1019,12 @@ If set to false,
|
||||
becomes
|
||||
.Ql $
|
||||
per normal evaluation rules.
|
||||
.It Va .MAKE.UID
|
||||
The user-id running
|
||||
.Nm .
|
||||
.It Va .MAKE.GID
|
||||
The group-id running
|
||||
.Nm .
|
||||
.It Va MAKE_PRINT_VAR_ON_ERROR
|
||||
When
|
||||
.Nm
|
||||
@ -1743,9 +1749,9 @@ The same as
|
||||
except that variables in the value are not expanded.
|
||||
.It Ic .info Ar message
|
||||
The message is printed along with the name of the makefile and line number.
|
||||
.It Ic .undef Ar variable
|
||||
Un-define the specified global variable.
|
||||
Only global variables may be un-defined.
|
||||
.It Ic .undef Ar variable ...
|
||||
Un-define the specified global variables.
|
||||
Only global variables can be un-defined.
|
||||
.It Ic .unexport Ar variable ...
|
||||
The opposite of
|
||||
.Ql .export .
|
||||
|
374
make.h
374
make.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make.h,v 1.210 2020/11/16 21:53:10 rillig Exp $ */
|
||||
/* $NetBSD: make.h,v 1.242 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -141,21 +141,29 @@
|
||||
* A boolean type is defined as an integer, not an enum, for historic reasons.
|
||||
* The only allowed values are the constants TRUE and FALSE (1 and 0).
|
||||
*/
|
||||
|
||||
#ifdef USE_DOUBLE_BOOLEAN
|
||||
#if defined(lint) || defined(USE_C99_BOOLEAN)
|
||||
#include <stdbool.h>
|
||||
typedef bool Boolean;
|
||||
#define FALSE false
|
||||
#define TRUE true
|
||||
#elif defined(USE_DOUBLE_BOOLEAN)
|
||||
/* During development, to find type mismatches in function declarations. */
|
||||
typedef double Boolean;
|
||||
#define TRUE 1.0
|
||||
#define FALSE 0.0
|
||||
#elif defined(USE_UCHAR_BOOLEAN)
|
||||
/* During development, to find code that depends on the exact value of TRUE or
|
||||
* that stores other values in Boolean variables. */
|
||||
/*
|
||||
* During development, to find code that depends on the exact value of TRUE or
|
||||
* that stores other values in Boolean variables.
|
||||
*/
|
||||
typedef unsigned char Boolean;
|
||||
#define TRUE ((unsigned char)0xFF)
|
||||
#define FALSE ((unsigned char)0x00)
|
||||
#elif defined(USE_CHAR_BOOLEAN)
|
||||
/* During development, to find code that uses a boolean as array index, via
|
||||
* -Wchar-subscripts. */
|
||||
/*
|
||||
* During development, to find code that uses a boolean as array index, via
|
||||
* -Wchar-subscripts.
|
||||
*/
|
||||
typedef char Boolean;
|
||||
#define TRUE ((char)-1)
|
||||
#define FALSE ((char)0x00)
|
||||
@ -189,80 +197,115 @@ typedef int Boolean;
|
||||
# define POSIX_SIGNALS
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The typical flow of states is:
|
||||
*
|
||||
* The direct successful path:
|
||||
* UNMADE -> BEINGMADE -> MADE.
|
||||
*
|
||||
* The direct error path:
|
||||
* UNMADE -> BEINGMADE -> ERROR.
|
||||
*
|
||||
* The successful path when dependencies need to be made first:
|
||||
* UNMADE -> DEFERRED -> REQUESTED -> BEINGMADE -> MADE.
|
||||
*
|
||||
* A node that has dependencies, and one of the dependencies cannot be made:
|
||||
* UNMADE -> DEFERRED -> ABORTED.
|
||||
*
|
||||
* A node that turns out to be up-to-date:
|
||||
* UNMADE -> BEINGMADE -> UPTODATE.
|
||||
*/
|
||||
typedef enum GNodeMade {
|
||||
UNMADE, /* Not examined yet */
|
||||
DEFERRED, /* Examined once (building child) */
|
||||
REQUESTED, /* on toBeMade list */
|
||||
BEINGMADE, /* Target is already being made.
|
||||
* Indicates a cycle in the graph. */
|
||||
MADE, /* Was out-of-date and has been made */
|
||||
UPTODATE, /* Was already up-to-date */
|
||||
ERROR, /* An error occurred while it was being
|
||||
* made (used only in compat mode) */
|
||||
ABORTED /* The target was aborted due to an error
|
||||
* making an inferior (compat). */
|
||||
/* Not examined yet. */
|
||||
UNMADE,
|
||||
/* The node has been examined but is not yet ready since its
|
||||
* dependencies have to be made first. */
|
||||
DEFERRED,
|
||||
|
||||
/* The node is on the toBeMade list. */
|
||||
REQUESTED,
|
||||
|
||||
/* The node is already being made. Trying to build a node in this
|
||||
* state indicates a cycle in the graph. */
|
||||
BEINGMADE,
|
||||
|
||||
/* Was out-of-date and has been made. */
|
||||
MADE,
|
||||
/* Was already up-to-date, does not need to be made. */
|
||||
UPTODATE,
|
||||
/* An error occurred while it was being made.
|
||||
* Used only in compat mode. */
|
||||
ERROR,
|
||||
/* The target was aborted due to an error making a dependency.
|
||||
* Used only in compat mode. */
|
||||
ABORTED
|
||||
} GNodeMade;
|
||||
|
||||
/* The OP_ constants are used when parsing a dependency line as a way of
|
||||
/*
|
||||
* The OP_ constants are used when parsing a dependency line as a way of
|
||||
* communicating to other parts of the program the way in which a target
|
||||
* should be made.
|
||||
*
|
||||
* Some of the OP_ constants can be combined, others cannot. */
|
||||
* Some of the OP_ constants can be combined, others cannot.
|
||||
*/
|
||||
typedef enum GNodeType {
|
||||
OP_NONE = 0,
|
||||
|
||||
/* The dependency operator ':' is the most common one. The commands of
|
||||
* this node are executed if any child is out-of-date. */
|
||||
/* The dependency operator ':' is the most common one. The commands
|
||||
* of this node are executed if any child is out-of-date. */
|
||||
OP_DEPENDS = 1 << 0,
|
||||
/* The dependency operator '!' always executes its commands, even if
|
||||
* its children are up-to-date. */
|
||||
OP_FORCE = 1 << 1,
|
||||
/* The dependency operator '::' behaves like ':', except that it allows
|
||||
* multiple dependency groups to be defined. Each of these groups is
|
||||
* executed on its own, independently from the others. Each individual
|
||||
* dependency group is called a cohort. */
|
||||
/* The dependency operator '::' behaves like ':', except that it
|
||||
* allows multiple dependency groups to be defined. Each of these
|
||||
* groups is executed on its own, independently from the others.
|
||||
* Each individual dependency group is called a cohort. */
|
||||
OP_DOUBLEDEP = 1 << 2,
|
||||
|
||||
/* Matches the dependency operators ':', '!' and '::'. */
|
||||
OP_OPMASK = OP_DEPENDS | OP_FORCE | OP_DOUBLEDEP,
|
||||
|
||||
/* Don't care if the target doesn't exist and can't be created */
|
||||
/* Don't care if the target doesn't exist and can't be created. */
|
||||
OP_OPTIONAL = 1 << 3,
|
||||
/* Use associated commands for parents */
|
||||
/* Use associated commands for parents. */
|
||||
OP_USE = 1 << 4,
|
||||
/* Target is never out of date, but always execute commands anyway.
|
||||
* Its time doesn't matter, so it has none...sort of */
|
||||
* Its time doesn't matter, so it has none...sort of. */
|
||||
OP_EXEC = 1 << 5,
|
||||
/* Ignore non-zero exit status from shell commands when creating the node */
|
||||
/* Ignore non-zero exit status from shell commands when creating the
|
||||
* node. */
|
||||
OP_IGNORE = 1 << 6,
|
||||
/* Don't remove the target when interrupted */
|
||||
/* Don't remove the target when interrupted. */
|
||||
OP_PRECIOUS = 1 << 7,
|
||||
/* Don't echo commands when executed */
|
||||
/* Don't echo commands when executed. */
|
||||
OP_SILENT = 1 << 8,
|
||||
/* Target is a recursive make so its commands should always be executed
|
||||
* when it is out of date, regardless of the state of the -n or -t flags */
|
||||
/* Target is a recursive make so its commands should always be
|
||||
* executed when it is out of date, regardless of the state of the
|
||||
* -n or -t flags. */
|
||||
OP_MAKE = 1 << 9,
|
||||
/* Target is out-of-date only if any of its children was out-of-date */
|
||||
/* Target is out-of-date only if any of its children was out-of-date. */
|
||||
OP_JOIN = 1 << 10,
|
||||
/* Assume the children of the node have been already made */
|
||||
/* Assume the children of the node have been already made. */
|
||||
OP_MADE = 1 << 11,
|
||||
/* Special .BEGIN, .END, .INTERRUPT */
|
||||
/* Special .BEGIN, .END or .INTERRUPT. */
|
||||
OP_SPECIAL = 1 << 12,
|
||||
/* Like .USE, only prepend commands */
|
||||
/* Like .USE, only prepend commands. */
|
||||
OP_USEBEFORE = 1 << 13,
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in the
|
||||
* parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
/* The node is invisible to its parents. I.e. it doesn't show up in
|
||||
* the parents' local variables (.IMPSRC, .ALLSRC). */
|
||||
OP_INVISIBLE = 1 << 14,
|
||||
/* The node is exempt from normal 'main target' processing in parse.c */
|
||||
/* The node does not become the main target, even if it is the first
|
||||
* target in the first makefile. */
|
||||
OP_NOTMAIN = 1 << 15,
|
||||
/* Not a file target; run always */
|
||||
/* Not a file target; run always. */
|
||||
OP_PHONY = 1 << 16,
|
||||
/* Don't search for file in the path */
|
||||
/* Don't search for the file in the path. */
|
||||
OP_NOPATH = 1 << 17,
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is made
|
||||
* first, including its children. Once that is finished, source2 is made,
|
||||
* including its children. The .WAIT keyword may appear more than once in
|
||||
* a single dependency declaration. */
|
||||
/* In a dependency line "target: source1 .WAIT source2", source1 is
|
||||
* made first, including its children. Once that is finished,
|
||||
* source2 is made, including its children. The .WAIT keyword may
|
||||
* appear more than once in a single dependency declaration. */
|
||||
OP_WAIT = 1 << 18,
|
||||
/* .NOMETA do not create a .meta file */
|
||||
OP_NOMETA = 1 << 19,
|
||||
@ -276,44 +319,56 @@ typedef enum GNodeType {
|
||||
/* Attributes applied by PMake */
|
||||
|
||||
/* The node is a transformation rule, such as ".c.o". */
|
||||
OP_TRANSFORM = 1 << 31,
|
||||
OP_TRANSFORM = 1 << 30,
|
||||
/* Target is a member of an archive */
|
||||
/* XXX: How does this differ from OP_ARCHV? */
|
||||
OP_MEMBER = 1 << 30,
|
||||
OP_MEMBER = 1 << 29,
|
||||
/* The node is a library,
|
||||
* its name has the form "-l<libname>" */
|
||||
OP_LIB = 1 << 29,
|
||||
OP_LIB = 1 << 28,
|
||||
/* The node is an archive member,
|
||||
* its name has the form "archive(member)" */
|
||||
/* XXX: How does this differ from OP_MEMBER? */
|
||||
OP_ARCHV = 1 << 28,
|
||||
OP_ARCHV = 1 << 27,
|
||||
/* Target has all the commands it should. Used when parsing to catch
|
||||
* multiple command groups for a target. Only applies to the dependency
|
||||
* operators ':' and '!', but not to '::'. */
|
||||
OP_HAS_COMMANDS = 1 << 27,
|
||||
* multiple command groups for a target. Only applies to the
|
||||
* dependency operators ':' and '!', but not to '::'. */
|
||||
OP_HAS_COMMANDS = 1 << 26,
|
||||
/* The special command "..." has been seen. All further commands from
|
||||
* this node will be saved on the .END node instead, to be executed at
|
||||
* the very end. */
|
||||
OP_SAVE_CMDS = 1 << 26,
|
||||
/* Already processed by Suff_FindDeps */
|
||||
OP_DEPS_FOUND = 1 << 25,
|
||||
OP_SAVE_CMDS = 1 << 25,
|
||||
/* Already processed by Suff_FindDeps, to find dependencies from
|
||||
* suffix transformation rules. */
|
||||
OP_DEPS_FOUND = 1 << 24,
|
||||
/* Node found while expanding .ALLSRC */
|
||||
OP_MARK = 1 << 24,
|
||||
OP_MARK = 1 << 23,
|
||||
|
||||
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
|
||||
} GNodeType;
|
||||
|
||||
typedef enum GNodeFlags {
|
||||
REMAKE = 0x0001, /* this target needs to be (re)made */
|
||||
CHILDMADE = 0x0002, /* children of this target were made */
|
||||
FORCE = 0x0004, /* children don't exist, and we pretend made */
|
||||
DONE_WAIT = 0x0008, /* Set by Make_ProcessWait() */
|
||||
DONE_ORDER = 0x0010, /* Build requested by .ORDER processing */
|
||||
FROM_DEPEND = 0x0020, /* Node created from .depend */
|
||||
DONE_ALLSRC = 0x0040, /* We do it once only */
|
||||
CYCLE = 0x1000, /* Used by MakePrintStatus */
|
||||
DONECYCLE = 0x2000, /* Used by MakePrintStatus */
|
||||
INTERNAL = 0x4000 /* Internal use only */
|
||||
GNF_NONE = 0,
|
||||
/* this target needs to be (re)made */
|
||||
REMAKE = 0x0001,
|
||||
/* children of this target were made */
|
||||
CHILDMADE = 0x0002,
|
||||
/* children don't exist, and we pretend made */
|
||||
FORCE = 0x0004,
|
||||
/* Set by Make_ProcessWait() */
|
||||
DONE_WAIT = 0x0008,
|
||||
/* Build requested by .ORDER processing */
|
||||
DONE_ORDER = 0x0010,
|
||||
/* Node created from .depend */
|
||||
FROM_DEPEND = 0x0020,
|
||||
/* We do it once only */
|
||||
DONE_ALLSRC = 0x0040,
|
||||
/* Used by MakePrintStatus */
|
||||
CYCLE = 0x1000,
|
||||
/* Used by MakePrintStatus */
|
||||
DONECYCLE = 0x2000,
|
||||
/* Internal use only */
|
||||
INTERNAL = 0x4000
|
||||
} GNodeFlags;
|
||||
|
||||
typedef struct List StringList;
|
||||
@ -324,53 +379,58 @@ typedef struct ListNode GNodeListNode;
|
||||
|
||||
typedef struct List /* of CachedDir */ SearchPath;
|
||||
|
||||
/* A graph node represents a target that can possibly be made, including its
|
||||
* relation to other targets and a lot of other details. */
|
||||
/*
|
||||
* A graph node represents a target that can possibly be made, including its
|
||||
* relation to other targets and a lot of other details.
|
||||
*/
|
||||
typedef struct GNode {
|
||||
/* The target's name, such as "clean" or "make.c" */
|
||||
char *name;
|
||||
/* The unexpanded name of a .USE node */
|
||||
char *uname;
|
||||
/* The full pathname of the file belonging to the target.
|
||||
* XXX: What about .PHONY targets? These don't have an associated path. */
|
||||
* XXX: What about .PHONY targets? These don't have an associated
|
||||
* path. */
|
||||
char *path;
|
||||
|
||||
/* The type of operator used to define the sources (see the OP flags below).
|
||||
/* The type of operator used to define the sources (see the OP flags
|
||||
* below).
|
||||
* XXX: This looks like a wild mixture of type and flags. */
|
||||
GNodeType type;
|
||||
GNodeFlags flags;
|
||||
|
||||
/* The state of processing on this node */
|
||||
GNodeMade made;
|
||||
int unmade; /* The number of unmade children */
|
||||
/* The number of unmade children */
|
||||
int unmade;
|
||||
|
||||
/* The modification time; 0 means the node does not have a corresponding
|
||||
* file; see GNode_IsOODate. */
|
||||
/* The modification time; 0 means the node does not have a
|
||||
* corresponding file; see GNode_IsOODate. */
|
||||
time_t mtime;
|
||||
struct GNode *youngestChild;
|
||||
|
||||
/* The GNodes for which this node is an implied source. May be empty.
|
||||
* For example, when there is an inference rule for .c.o, the node for
|
||||
* file.c has the node for file.o in this list. */
|
||||
GNodeList *implicitParents;
|
||||
GNodeList implicitParents;
|
||||
|
||||
/* The nodes that depend on this one, or in other words, the nodes for
|
||||
* which this is a source. */
|
||||
GNodeList *parents;
|
||||
GNodeList parents;
|
||||
/* The nodes on which this one depends. */
|
||||
GNodeList *children;
|
||||
GNodeList children;
|
||||
|
||||
/* .ORDER nodes we need made. The nodes that must be made (if they're
|
||||
* made) before this node can be made, but that do not enter into the
|
||||
* datedness of this node. */
|
||||
GNodeList *order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're made
|
||||
* at all) after this node is made, but that do not depend on this node,
|
||||
* in the normal sense. */
|
||||
GNodeList *order_succ;
|
||||
GNodeList order_pred;
|
||||
/* .ORDER nodes who need us. The nodes that must be made (if they're
|
||||
* made at all) after this node is made, but that do not depend on
|
||||
* this node, in the normal sense. */
|
||||
GNodeList order_succ;
|
||||
|
||||
/* Other nodes of the same name, for the '::' dependency operator. */
|
||||
GNodeList *cohorts;
|
||||
GNodeList cohorts;
|
||||
/* The "#n" suffix for this cohort, or "" for other nodes */
|
||||
char cohort_num[8];
|
||||
/* The number of unmade instances on the cohorts list */
|
||||
@ -382,19 +442,19 @@ typedef struct GNode {
|
||||
/* Last time (sequence number) we tried to make this node */
|
||||
unsigned int checked_seqno;
|
||||
|
||||
/* The "local" variables that are specific to this target and this target
|
||||
* only, such as $@, $<, $?.
|
||||
/* The "local" variables that are specific to this target and this
|
||||
* target only, such as $@, $<, $?.
|
||||
*
|
||||
* Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE,
|
||||
* VAR_INTERNAL, which contain variables with arbitrary names. */
|
||||
HashTable /* of Var pointer */ context;
|
||||
HashTable /* of Var pointer */ vars;
|
||||
|
||||
/* The commands to be given to a shell to create this target. */
|
||||
StringList *commands;
|
||||
StringList commands;
|
||||
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to everyone
|
||||
* but the Suff module) */
|
||||
struct Suff *suffix;
|
||||
/* Suffix for the node (determined by Suff_FindDeps and opaque to
|
||||
* everyone but the Suff module) */
|
||||
struct Suffix *suffix;
|
||||
|
||||
/* Filename where the GNode got defined */
|
||||
/* XXX: What is the lifetime of this string? */
|
||||
@ -405,7 +465,8 @@ typedef struct GNode {
|
||||
|
||||
/* Error levels for diagnostics during parsing. */
|
||||
typedef enum ParseErrorLevel {
|
||||
/* Exit when the current top-level makefile has been parsed completely. */
|
||||
/* Exit when the current top-level makefile has been parsed
|
||||
* completely. */
|
||||
PARSE_FATAL = 1,
|
||||
/* Print "warning"; may be upgraded to fatal by the -w option. */
|
||||
PARSE_WARNING,
|
||||
@ -444,43 +505,36 @@ extern Boolean doing_depend;
|
||||
/* .DEFAULT rule */
|
||||
extern GNode *defaultNode;
|
||||
|
||||
/* Variables defined internally by make which should not override those set
|
||||
* by makefiles. */
|
||||
/*
|
||||
* Variables defined internally by make which should not override those set
|
||||
* by makefiles.
|
||||
*/
|
||||
extern GNode *VAR_INTERNAL;
|
||||
/* Variables defined in a global context, e.g in the Makefile itself. */
|
||||
extern GNode *VAR_GLOBAL;
|
||||
/* Variables defined on the command line. */
|
||||
extern GNode *VAR_CMDLINE;
|
||||
|
||||
/* Value returned by Var_Parse when an error is encountered. It actually
|
||||
* points to an empty string, so naive callers needn't worry about it. */
|
||||
/*
|
||||
* Value returned by Var_Parse when an error is encountered. It actually
|
||||
* points to an empty string, so naive callers needn't worry about it.
|
||||
*/
|
||||
extern char var_Error[];
|
||||
|
||||
/* The time at the start of this whole process */
|
||||
extern time_t now;
|
||||
|
||||
/*
|
||||
* If FALSE (the default behavior), undefined subexpressions in a variable
|
||||
* expression are discarded. If TRUE (only during variable assignments using
|
||||
* the ':=' assignment operator, no matter how deeply nested), they are
|
||||
* preserved and possibly expanded later when the variable from the
|
||||
* subexpression has been defined.
|
||||
*
|
||||
* Example for a ':=' assignment:
|
||||
* CFLAGS = $(.INCLUDES)
|
||||
* CFLAGS := -I.. $(CFLAGS)
|
||||
* # If .INCLUDES (an undocumented special variable, by the way) is
|
||||
* # still undefined, the updated CFLAGS becomes "-I.. $(.INCLUDES)".
|
||||
* The list of directories to search when looking for targets (set by the
|
||||
* special target .PATH).
|
||||
*/
|
||||
extern Boolean preserveUndefined;
|
||||
|
||||
/* The list of directories to search when looking for targets (set by the
|
||||
* special target .PATH). */
|
||||
extern SearchPath *dirSearchPath;
|
||||
extern SearchPath dirSearchPath;
|
||||
/* Used for .include "...". */
|
||||
extern SearchPath *parseIncPath;
|
||||
/* Used for .include <...>, for the built-in sys.mk and makefiles from the
|
||||
* command line arguments. */
|
||||
/*
|
||||
* Used for .include <...>, for the built-in sys.mk and makefiles from the
|
||||
* command line arguments.
|
||||
*/
|
||||
extern SearchPath *sysIncPath;
|
||||
/* The default for sysIncPath. */
|
||||
extern SearchPath *defSysIncPath;
|
||||
@ -488,7 +542,7 @@ extern SearchPath *defSysIncPath;
|
||||
/* Startup directory */
|
||||
extern char curdir[];
|
||||
/* The basename of the program name, suffixed with [n] for sub-makes. */
|
||||
extern char *progname;
|
||||
extern const char *progname;
|
||||
/* Name of the .depend makefile */
|
||||
extern char *makeDependfile;
|
||||
/* If we replaced environ, this will be non-NULL. */
|
||||
@ -505,9 +559,10 @@ extern pid_t myPid;
|
||||
|
||||
#define MAKEFLAGS ".MAKEFLAGS"
|
||||
#define MAKEOVERRIDES ".MAKEOVERRIDES"
|
||||
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all makefiles already loaded */
|
||||
/* prefix when printing the target of a job */
|
||||
#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX"
|
||||
#define MAKE_EXPORTED ".MAKE.EXPORTED" /* exported variables */
|
||||
#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all loaded makefiles */
|
||||
#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */
|
||||
#define MAKE_MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE"
|
||||
#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */
|
||||
@ -543,33 +598,28 @@ typedef enum DebugFlags {
|
||||
|
||||
#define CONCAT(a, b) a##b
|
||||
|
||||
#define DEBUG(module) (opts.debug & CONCAT(DEBUG_,module))
|
||||
#define DEBUG(module) ((opts.debug & CONCAT(DEBUG_, module)) != 0)
|
||||
|
||||
void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
|
||||
#define DEBUG_IMPL(module, args) \
|
||||
do { \
|
||||
if (DEBUG(module)) \
|
||||
debug_printf args; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define DEBUG0(module, text) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf("%s", text)
|
||||
|
||||
DEBUG_IMPL(module, ("%s", text))
|
||||
#define DEBUG1(module, fmt, arg1) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1))
|
||||
#define DEBUG2(module, fmt, arg1, arg2) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2))
|
||||
#define DEBUG3(module, fmt, arg1, arg2, arg3) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3))
|
||||
#define DEBUG4(module, fmt, arg1, arg2, arg3, arg4) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4)
|
||||
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4))
|
||||
#define DEBUG5(module, fmt, arg1, arg2, arg3, arg4, arg5) \
|
||||
if (!DEBUG(module)) (void)0; \
|
||||
else debug_printf(fmt, arg1, arg2, arg3, arg4, arg5)
|
||||
DEBUG_IMPL(module, (fmt, arg1, arg2, arg3, arg4, arg5))
|
||||
|
||||
typedef enum PrintVarsMode {
|
||||
PVM_NONE,
|
||||
@ -593,7 +643,7 @@ typedef struct CmdOpts {
|
||||
*
|
||||
* Runs make in strict mode, with additional checks and better error
|
||||
* handling. */
|
||||
Boolean lint;
|
||||
Boolean strict;
|
||||
|
||||
/* -dV: for the -V option, print unexpanded variable values */
|
||||
Boolean debugVflag;
|
||||
@ -602,7 +652,7 @@ typedef struct CmdOpts {
|
||||
Boolean checkEnvFirst;
|
||||
|
||||
/* -f: the makefiles to read */
|
||||
StringList *makefiles;
|
||||
StringList makefiles;
|
||||
|
||||
/* -i: if true, ignore all errors from shell commands */
|
||||
Boolean ignoreErrors;
|
||||
@ -611,8 +661,8 @@ typedef struct CmdOpts {
|
||||
* 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 */
|
||||
/* -k: if true and an error occurs while making a node, continue
|
||||
* making nodes that do not depend on the erroneous node */
|
||||
Boolean keepgoing;
|
||||
|
||||
/* -N: execute no commands from the targets */
|
||||
@ -621,8 +671,8 @@ typedef struct CmdOpts {
|
||||
/* -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 */
|
||||
/* -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. */
|
||||
@ -638,7 +688,7 @@ typedef struct CmdOpts {
|
||||
/* -[Vv]: print expanded or unexpanded selected variables */
|
||||
PrintVarsMode printVars;
|
||||
/* -[Vv]: the variables to print */
|
||||
StringList *variables;
|
||||
StringList variables;
|
||||
|
||||
/* -W: if true, makefile parsing warnings are treated as errors */
|
||||
Boolean parseWarnFatal;
|
||||
@ -652,7 +702,7 @@ typedef struct CmdOpts {
|
||||
|
||||
/* The target names specified on the command line.
|
||||
* Used to resolve .if make(...) statements. */
|
||||
StringList *create;
|
||||
StringList create;
|
||||
|
||||
} CmdOpts;
|
||||
|
||||
@ -690,6 +740,30 @@ GNode_Path(const GNode *gn)
|
||||
return gn->path != NULL ? gn->path : gn->name;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsWaitingFor(const GNode *gn)
|
||||
{
|
||||
return (gn->flags & REMAKE) && gn->made <= REQUESTED;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsReady(const GNode *gn)
|
||||
{
|
||||
return gn->made > DEFERRED;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsDone(const GNode *gn)
|
||||
{
|
||||
return gn->made >= MADE;
|
||||
}
|
||||
|
||||
MAKE_INLINE Boolean
|
||||
GNode_IsError(const GNode *gn)
|
||||
{
|
||||
return gn->made == ERROR || gn->made == ABORTED;
|
||||
}
|
||||
|
||||
MAKE_INLINE const char *
|
||||
GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); }
|
||||
MAKE_INLINE const char *
|
||||
@ -776,11 +850,17 @@ pp_skip_hspace(char **pp)
|
||||
(*pp)++;
|
||||
}
|
||||
|
||||
#ifdef MAKE_NATIVE
|
||||
#if defined(lint)
|
||||
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
|
||||
#elif defined(MAKE_NATIVE)
|
||||
# include <sys/cdefs.h>
|
||||
# ifndef lint
|
||||
# define MAKE_RCSID(id) __RCSID(id)
|
||||
# endif
|
||||
#elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
|
||||
# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
|
||||
# define MAKE_RCSID(id) static volatile char \
|
||||
MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id
|
||||
#elif defined(MAKE_ALL_IN_ONE)
|
||||
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
|
||||
#else
|
||||
# define MAKE_RCSID(id) static volatile char rcsid[] = id
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
#include "make.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $");
|
||||
|
||||
#ifndef USE_EMALLOC
|
||||
|
||||
@ -61,8 +61,7 @@ bmake_strdup(const char *str)
|
||||
char *p;
|
||||
|
||||
len = strlen(str) + 1;
|
||||
if ((p = malloc(len)) == NULL)
|
||||
enomem();
|
||||
p = bmake_malloc(len);
|
||||
return memcpy(p, str, len);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: make_malloc.h,v 1.13 2020/11/10 00:32:12 rillig Exp $ */
|
||||
/* $NetBSD: make_malloc.h,v 1.15 2020/12/30 10:03:16 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2009 The NetBSD Foundation, Inc.
|
||||
@ -41,11 +41,13 @@ char *bmake_strldup(const char *, size_t);
|
||||
|
||||
char *bmake_strsedup(const char *, const char *);
|
||||
|
||||
/* Thin wrapper around free(3) to avoid the extra function call in case
|
||||
/*
|
||||
* Thin wrapper around free(3) to avoid the extra function call in case
|
||||
* p is NULL, to save a few machine instructions.
|
||||
*
|
||||
* The case of a NULL pointer happens especially often after Var_Value,
|
||||
* since only environment variables need to be freed, but not others. */
|
||||
* since only environment variables need to be freed, but not others.
|
||||
*/
|
||||
MAKE_INLINE void
|
||||
bmake_free(void *p)
|
||||
{
|
||||
|
325
meta.c
325
meta.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.c,v 1.144 2020/11/15 12:02:44 rillig Exp $ */
|
||||
/* $NetBSD: meta.c,v 1.168 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Implement 'meta' mode.
|
||||
@ -55,9 +55,9 @@ char * dirname(char *);
|
||||
#endif
|
||||
|
||||
static BuildMon Mybm; /* for compat */
|
||||
static StringList *metaBailiwick; /* our scope of control */
|
||||
static StringList metaBailiwick = LST_INIT; /* our scope of control */
|
||||
static char *metaBailiwickStr; /* string storage for the list */
|
||||
static StringList *metaIgnorePaths; /* paths we deliberately ignore */
|
||||
static StringList metaIgnorePaths = LST_INIT; /* paths we deliberately ignore */
|
||||
static char *metaIgnorePathsStr; /* string storage for the list */
|
||||
|
||||
#ifndef MAKE_META_IGNORE_PATHS
|
||||
@ -128,7 +128,7 @@ meta_open_filemon(BuildMon *pbm)
|
||||
|
||||
pbm->mon_fd = -1;
|
||||
pbm->filemon = NULL;
|
||||
if (!useFilemon || !pbm->mfp)
|
||||
if (!useFilemon || pbm->mfp == NULL)
|
||||
return;
|
||||
|
||||
pbm->filemon = filemon_open();
|
||||
@ -222,7 +222,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
|
||||
|
||||
do {
|
||||
cp = strstr(buf, eat);
|
||||
if (cp) {
|
||||
if (cp != NULL) {
|
||||
cp2 = cp + eatlen;
|
||||
if (dots == 2 && cp > buf) {
|
||||
do {
|
||||
@ -235,7 +235,7 @@ eat_dots(char *buf, size_t bufsz, int dots)
|
||||
return; /* can't happen? */
|
||||
}
|
||||
}
|
||||
} while (cp);
|
||||
} while (cp != NULL);
|
||||
}
|
||||
|
||||
static char *
|
||||
@ -258,9 +258,9 @@ meta_name(char *mname, size_t mnamelen,
|
||||
* So we use realpath() just to get the dirname, and leave the
|
||||
* basename as given to us.
|
||||
*/
|
||||
if ((cp = strrchr(tname, '/'))) {
|
||||
if (cached_realpath(tname, buf)) {
|
||||
if ((rp = strrchr(buf, '/'))) {
|
||||
if ((cp = strrchr(tname, '/')) != NULL) {
|
||||
if (cached_realpath(tname, buf) != NULL) {
|
||||
if ((rp = strrchr(buf, '/')) != NULL) {
|
||||
rp++;
|
||||
cp++;
|
||||
if (strcmp(cp, rp) != 0)
|
||||
@ -316,25 +316,22 @@ meta_name(char *mname, size_t mnamelen,
|
||||
* Return true if running ${.MAKE}
|
||||
* Bypassed if target is flagged .MAKE
|
||||
*/
|
||||
static int
|
||||
is_submake(void *cmdp, void *gnp)
|
||||
static Boolean
|
||||
is_submake(const char *cmd, GNode *gn)
|
||||
{
|
||||
static const char *p_make = NULL;
|
||||
static size_t p_len;
|
||||
char *cmd = cmdp;
|
||||
GNode *gn = gnp;
|
||||
char *mp = NULL;
|
||||
char *cp;
|
||||
char *cp2;
|
||||
int rc = 0; /* keep looking */
|
||||
Boolean rc = FALSE;
|
||||
|
||||
if (p_make == NULL) {
|
||||
void *dontFreeIt;
|
||||
p_make = Var_Value(".MAKE", gn, &dontFreeIt);
|
||||
p_make = Var_Value(".MAKE", gn).str;
|
||||
p_len = strlen(p_make);
|
||||
}
|
||||
cp = strchr(cmd, '$');
|
||||
if ((cp)) {
|
||||
if (cp != NULL) {
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
cmd = mp;
|
||||
@ -346,17 +343,17 @@ is_submake(void *cmdp, void *gnp)
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
rc = 1;
|
||||
rc = TRUE;
|
||||
break;
|
||||
}
|
||||
if (cp2 > cmd && rc > 0) {
|
||||
if (cp2 > cmd && rc) {
|
||||
switch (cp2[-1]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
break;
|
||||
default:
|
||||
rc = 0; /* no match */
|
||||
rc = FALSE; /* no match */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -365,32 +362,38 @@ is_submake(void *cmdp, void *gnp)
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct meta_file_s {
|
||||
FILE *fp;
|
||||
GNode *gn;
|
||||
} meta_file_t;
|
||||
static Boolean
|
||||
any_is_submake(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
|
||||
if (is_submake(ln->datum, gn))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
printCMD(const char *cmd, meta_file_t *mfp)
|
||||
printCMD(const char *cmd, FILE *fp, GNode *gn)
|
||||
{
|
||||
char *cmd_freeIt = NULL;
|
||||
|
||||
if (strchr(cmd, '$')) {
|
||||
(void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt);
|
||||
if (strchr(cmd, '$') != NULL) {
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmd_freeIt);
|
||||
/* TODO: handle errors */
|
||||
cmd = cmd_freeIt;
|
||||
}
|
||||
fprintf(mfp->fp, "CMD %s\n", cmd);
|
||||
fprintf(fp, "CMD %s\n", cmd);
|
||||
free(cmd_freeIt);
|
||||
}
|
||||
|
||||
static void
|
||||
printCMDs(GNode *gn, meta_file_t *mf)
|
||||
printCMDs(GNode *gn, FILE *fp)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next)
|
||||
printCMD(ln->datum, mf);
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next)
|
||||
printCMD(ln->datum, fp, gn);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -404,7 +407,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
|
||||
} \
|
||||
return FALSE; \
|
||||
} \
|
||||
} while (0)
|
||||
} while (/*CONSTCOND*/0)
|
||||
|
||||
|
||||
/*
|
||||
@ -412,7 +415,7 @@ printCMDs(GNode *gn, meta_file_t *mf)
|
||||
*/
|
||||
static Boolean
|
||||
meta_needed(GNode *gn, const char *dname,
|
||||
char *objdir, int verbose)
|
||||
char *objdir_realpath, Boolean verbose)
|
||||
{
|
||||
struct cached_stat cst;
|
||||
|
||||
@ -431,14 +434,14 @@ meta_needed(GNode *gn, const char *dname,
|
||||
}
|
||||
|
||||
/* Check if there are no commands to execute. */
|
||||
if (Lst_IsEmpty(gn->commands)) {
|
||||
if (Lst_IsEmpty(&gn->commands)) {
|
||||
if (verbose)
|
||||
debug_printf("Skipping meta for %s: no commands\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
|
||||
/* OP_SUBMAKE is a bit too aggressive */
|
||||
if (Lst_ForEachUntil(gn->commands, is_submake, gn)) {
|
||||
if (any_is_submake(gn)) {
|
||||
DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name);
|
||||
return FALSE;
|
||||
}
|
||||
@ -452,8 +455,8 @@ meta_needed(GNode *gn, const char *dname,
|
||||
}
|
||||
|
||||
/* make sure these are canonical */
|
||||
if (cached_realpath(dname, objdir))
|
||||
dname = objdir;
|
||||
if (cached_realpath(dname, objdir_realpath) != NULL)
|
||||
dname = objdir_realpath;
|
||||
|
||||
/* If we aren't in the object directory, don't create a meta file. */
|
||||
if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
|
||||
@ -469,25 +472,24 @@ meta_needed(GNode *gn, const char *dname,
|
||||
static FILE *
|
||||
meta_create(BuildMon *pbm, GNode *gn)
|
||||
{
|
||||
meta_file_t mf;
|
||||
FILE *fp;
|
||||
char buf[MAXPATHLEN];
|
||||
char objdir[MAXPATHLEN];
|
||||
char objdir_realpath[MAXPATHLEN];
|
||||
char **ptr;
|
||||
const char *dname;
|
||||
FStr dname;
|
||||
const char *tname;
|
||||
char *fname;
|
||||
const char *cp;
|
||||
void *objdir_freeIt;
|
||||
|
||||
mf.fp = NULL;
|
||||
fp = NULL;
|
||||
|
||||
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
|
||||
dname = Var_Value(".OBJDIR", gn);
|
||||
tname = GNode_VarTarget(gn);
|
||||
|
||||
/* if this succeeds objdir is realpath of dname */
|
||||
if (!meta_needed(gn, dname, objdir, TRUE))
|
||||
/* if this succeeds objdir_realpath is realpath of dname */
|
||||
if (!meta_needed(gn, dname.str, objdir_realpath, TRUE))
|
||||
goto out;
|
||||
dname = objdir;
|
||||
dname.str = objdir_realpath;
|
||||
|
||||
if (metaVerbose) {
|
||||
char *mp;
|
||||
@ -495,16 +497,12 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
/* Describe the target we are building */
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp);
|
||||
/* TODO: handle errors */
|
||||
if (*mp)
|
||||
if (mp[0] != '\0')
|
||||
fprintf(stdout, "%s\n", mp);
|
||||
free(mp);
|
||||
}
|
||||
/* Get the basename of the target */
|
||||
if ((cp = strrchr(tname, '/')) == NULL) {
|
||||
cp = tname;
|
||||
} else {
|
||||
cp++;
|
||||
}
|
||||
cp = str_basename(tname);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@ -513,34 +511,32 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
goto out;
|
||||
|
||||
fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname,
|
||||
dname, tname, objdir);
|
||||
dname.str, tname, objdir_realpath);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_create: %s\n", fname);
|
||||
#endif
|
||||
|
||||
if ((mf.fp = fopen(fname, "w")) == NULL)
|
||||
if ((fp = fopen(fname, "w")) == NULL)
|
||||
err(1, "Could not open meta file '%s'", fname);
|
||||
|
||||
fprintf(mf.fp, "# Meta data file %s\n", fname);
|
||||
fprintf(fp, "# Meta data file %s\n", fname);
|
||||
|
||||
mf.gn = gn;
|
||||
printCMDs(gn, fp);
|
||||
|
||||
printCMDs(gn, &mf);
|
||||
|
||||
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof buf));
|
||||
fprintf(mf.fp, "TARGET %s\n", tname);
|
||||
fprintf(fp, "CWD %s\n", getcwd(buf, sizeof buf));
|
||||
fprintf(fp, "TARGET %s\n", tname);
|
||||
cp = GNode_VarOodate(gn);
|
||||
if (cp && *cp) {
|
||||
fprintf(mf.fp, "OODATE %s\n", cp);
|
||||
if (cp != NULL && *cp != '\0') {
|
||||
fprintf(fp, "OODATE %s\n", cp);
|
||||
}
|
||||
if (metaEnv) {
|
||||
for (ptr = environ; *ptr != NULL; ptr++)
|
||||
fprintf(mf.fp, "ENV %s\n", *ptr);
|
||||
fprintf(fp, "ENV %s\n", *ptr);
|
||||
}
|
||||
|
||||
fprintf(mf.fp, "-- command output --\n");
|
||||
fflush(mf.fp);
|
||||
fprintf(fp, "-- command output --\n");
|
||||
fflush(fp);
|
||||
|
||||
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
|
||||
Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
|
||||
@ -550,9 +546,9 @@ meta_create(BuildMon *pbm, GNode *gn)
|
||||
gn->type |= OP_SILENT;
|
||||
}
|
||||
out:
|
||||
bmake_free(objdir_freeIt);
|
||||
FStr_Done(&dname);
|
||||
|
||||
return mf.fp;
|
||||
return fp;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
@ -583,7 +579,7 @@ meta_init(void)
|
||||
|
||||
|
||||
#define get_mode_bf(bf, token) \
|
||||
if ((cp = strstr(make_mode, token))) \
|
||||
if ((cp = strstr(make_mode, token)) != NULL) \
|
||||
bf = boolValue(cp + sizeof (token) - 1)
|
||||
|
||||
/*
|
||||
@ -592,24 +588,24 @@ meta_init(void)
|
||||
void
|
||||
meta_mode_init(const char *make_mode)
|
||||
{
|
||||
static int once = 0;
|
||||
static Boolean once = FALSE;
|
||||
char *cp;
|
||||
void *freeIt;
|
||||
FStr value;
|
||||
|
||||
useMeta = TRUE;
|
||||
useFilemon = TRUE;
|
||||
writeMeta = TRUE;
|
||||
|
||||
if (make_mode) {
|
||||
if (strstr(make_mode, "env"))
|
||||
if (make_mode != NULL) {
|
||||
if (strstr(make_mode, "env") != NULL)
|
||||
metaEnv = TRUE;
|
||||
if (strstr(make_mode, "verb"))
|
||||
if (strstr(make_mode, "verb") != NULL)
|
||||
metaVerbose = TRUE;
|
||||
if (strstr(make_mode, "read"))
|
||||
if (strstr(make_mode, "read") != NULL)
|
||||
writeMeta = FALSE;
|
||||
if (strstr(make_mode, "nofilemon"))
|
||||
if (strstr(make_mode, "nofilemon") != NULL)
|
||||
useFilemon = FALSE;
|
||||
if (strstr(make_mode, "ignore-cmd"))
|
||||
if (strstr(make_mode, "ignore-cmd") != NULL)
|
||||
metaIgnoreCMDs = TRUE;
|
||||
if (useFilemon)
|
||||
get_mode_bf(filemonMissing, "missing-filemon=");
|
||||
@ -628,39 +624,37 @@ meta_mode_init(const char *make_mode)
|
||||
}
|
||||
if (once)
|
||||
return;
|
||||
once = 1;
|
||||
once = TRUE;
|
||||
memset(&Mybm, 0, sizeof Mybm);
|
||||
/*
|
||||
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
|
||||
*/
|
||||
metaBailiwick = Lst_New();
|
||||
(void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaBailiwick, metaBailiwickStr);
|
||||
str2Lst_Append(&metaBailiwick, metaBailiwickStr);
|
||||
/*
|
||||
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
|
||||
*/
|
||||
metaIgnorePaths = Lst_New();
|
||||
Var_Append(MAKE_META_IGNORE_PATHS,
|
||||
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
|
||||
(void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr);
|
||||
/* TODO: handle errors */
|
||||
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr);
|
||||
str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
|
||||
|
||||
/*
|
||||
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
||||
*/
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) {
|
||||
value = Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL);
|
||||
if (value.str != NULL) {
|
||||
metaIgnorePatterns = TRUE;
|
||||
bmake_free(freeIt);
|
||||
FStr_Done(&value);
|
||||
}
|
||||
freeIt = NULL;
|
||||
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) {
|
||||
value = Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL);
|
||||
if (value.str != NULL) {
|
||||
metaIgnoreFilter = TRUE;
|
||||
bmake_free(freeIt);
|
||||
FStr_Done(&value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,7 +704,7 @@ meta_job_child(Job *job)
|
||||
}
|
||||
if (pbm->mfp != NULL) {
|
||||
close(fileno(pbm->mfp));
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
pid_t pid;
|
||||
|
||||
pid = getpid();
|
||||
@ -733,7 +727,7 @@ meta_job_parent(Job *job, pid_t pid)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
filemon_setpid_parent(pbm->filemon, pid);
|
||||
}
|
||||
#endif
|
||||
@ -750,7 +744,7 @@ meta_job_fd(Job *job)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
return filemon_readfd(pbm->filemon);
|
||||
}
|
||||
#endif
|
||||
@ -768,7 +762,7 @@ meta_job_event(Job *job)
|
||||
} else {
|
||||
pbm = &Mybm;
|
||||
}
|
||||
if (useFilemon && pbm->filemon) {
|
||||
if (useFilemon && pbm->filemon != NULL) {
|
||||
return filemon_process(pbm->filemon);
|
||||
}
|
||||
#endif
|
||||
@ -776,7 +770,7 @@ meta_job_event(Job *job)
|
||||
}
|
||||
|
||||
void
|
||||
meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status)
|
||||
{
|
||||
char cwd[MAXPATHLEN];
|
||||
BuildMon *pbm;
|
||||
@ -790,11 +784,9 @@ meta_job_error(Job *job, GNode *gn, int flags, int status)
|
||||
}
|
||||
if (pbm->mfp != NULL) {
|
||||
fprintf(pbm->mfp, "\n*** Error code %d%s\n",
|
||||
status,
|
||||
(flags & JOB_IGNERR) ?
|
||||
"(ignored)" : "");
|
||||
status, ignerr ? "(ignored)" : "");
|
||||
}
|
||||
if (gn) {
|
||||
if (gn != NULL) {
|
||||
Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL);
|
||||
}
|
||||
getcwd(cwd, sizeof cwd);
|
||||
@ -826,15 +818,16 @@ meta_job_output(Job *job, char *cp, const char *nl)
|
||||
(void)Var_Subst("${" MAKE_META_PREFIX "}",
|
||||
VAR_GLOBAL, VARE_WANTRES, &meta_prefix);
|
||||
/* TODO: handle errors */
|
||||
if ((cp2 = strchr(meta_prefix, '$')))
|
||||
if ((cp2 = strchr(meta_prefix, '$')) != NULL)
|
||||
meta_prefix_len = (size_t)(cp2 - meta_prefix);
|
||||
else
|
||||
meta_prefix_len = strlen(meta_prefix);
|
||||
}
|
||||
if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
|
||||
cp = strchr(cp + 1, '\n');
|
||||
if (!cp++)
|
||||
if (cp == NULL)
|
||||
return;
|
||||
cp++;
|
||||
}
|
||||
}
|
||||
fprintf(pbm->mfp, "%s%s", cp, nl);
|
||||
@ -854,7 +847,7 @@ meta_cmd_finish(void *pbmp)
|
||||
pbm = &Mybm;
|
||||
|
||||
#ifdef USE_FILEMON
|
||||
if (pbm->filemon) {
|
||||
if (pbm->filemon != NULL) {
|
||||
while (filemon_process(pbm->filemon) > 0)
|
||||
continue;
|
||||
if (filemon_close(pbm->filemon) == -1)
|
||||
@ -898,11 +891,9 @@ meta_job_finish(Job *job)
|
||||
void
|
||||
meta_finish(void)
|
||||
{
|
||||
if (metaBailiwick != NULL)
|
||||
Lst_Free(metaBailiwick);
|
||||
Lst_Done(&metaBailiwick);
|
||||
free(metaBailiwickStr);
|
||||
if (metaIgnorePaths != NULL)
|
||||
Lst_Free(metaIgnorePaths);
|
||||
Lst_Done(&metaIgnorePaths);
|
||||
free(metaIgnorePathsStr);
|
||||
}
|
||||
|
||||
@ -936,9 +927,9 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ);
|
||||
if (newsz <= bufsz)
|
||||
return x; /* truncated */
|
||||
DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz);
|
||||
DEBUG2(META, "growing buffer %u -> %u\n",
|
||||
(unsigned)bufsz, (unsigned)newsz);
|
||||
p = bmake_realloc(buf, newsz);
|
||||
if (p) {
|
||||
*bufp = buf = p;
|
||||
*szp = bufsz = newsz;
|
||||
/* fetch the rest */
|
||||
@ -947,21 +938,28 @@ fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
||||
goto check_newline;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lst_ForEachUntil wants 1 to stop search */
|
||||
static int
|
||||
prefix_match(void *p, void *q)
|
||||
static Boolean
|
||||
prefix_match(const char *prefix, const char *path)
|
||||
{
|
||||
const char *prefix = p;
|
||||
const char *path = q;
|
||||
size_t n = strlen(prefix);
|
||||
|
||||
return strncmp(path, prefix, n) == 0;
|
||||
}
|
||||
|
||||
static Boolean
|
||||
has_any_prefix(const char *path, StringList *prefixes)
|
||||
{
|
||||
StringListNode *ln;
|
||||
|
||||
for (ln = prefixes->first; ln != NULL; ln = ln->next)
|
||||
if (prefix_match(ln->datum, path))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* See if the path equals prefix or starts with "prefix/". */
|
||||
static Boolean
|
||||
path_starts_with(const char *path, const char *prefix)
|
||||
@ -973,7 +971,7 @@ path_starts_with(const char *path, const char *prefix)
|
||||
return path[n] == '\0' || path[n] == '/';
|
||||
}
|
||||
|
||||
static int
|
||||
static Boolean
|
||||
meta_ignore(GNode *gn, const char *p)
|
||||
{
|
||||
char fname[MAXPATHLEN];
|
||||
@ -983,7 +981,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
|
||||
if (*p == '/') {
|
||||
cached_realpath(p, fname); /* clean it up */
|
||||
if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) {
|
||||
if (has_any_prefix(fname, &metaIgnorePaths)) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: ignoring path: %s\n", p);
|
||||
#endif
|
||||
@ -999,7 +997,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}";
|
||||
(void)Var_Subst(expr, gn, VARE_WANTRES, &pm);
|
||||
/* TODO: handle errors */
|
||||
if (*pm) {
|
||||
if (pm[0] != '\0') {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p);
|
||||
#endif
|
||||
@ -1043,7 +1041,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
* if we detect this we want to reproduce it.
|
||||
* Setting oodate TRUE will have that effect.
|
||||
*/
|
||||
#define CHECK_VALID_META(p) if (!(p && *p)) { \
|
||||
#define CHECK_VALID_META(p) if (!(p != NULL && *p != '\0')) { \
|
||||
warnx("%s: %d: malformed", fname, lineno); \
|
||||
oodate = TRUE; \
|
||||
continue; \
|
||||
@ -1052,7 +1050,7 @@ meta_ignore(GNode *gn, const char *p)
|
||||
#define DEQUOTE(p) if (*p == '\'') { \
|
||||
char *ep; \
|
||||
p++; \
|
||||
if ((ep = strchr(p, '\''))) \
|
||||
if ((ep = strchr(p, '\'')) != NULL) \
|
||||
*ep = '\0'; \
|
||||
}
|
||||
|
||||
@ -1080,32 +1078,30 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
char fname1[MAXPATHLEN];
|
||||
char fname2[MAXPATHLEN];
|
||||
char fname3[MAXPATHLEN];
|
||||
const char *dname;
|
||||
FStr dname;
|
||||
const char *tname;
|
||||
char *p;
|
||||
char *cp;
|
||||
char *link_src;
|
||||
char *move_target;
|
||||
static size_t cwdlen = 0;
|
||||
static size_t tmplen = 0;
|
||||
FILE *fp;
|
||||
Boolean needOODATE = FALSE;
|
||||
StringList *missingFiles;
|
||||
StringList missingFiles;
|
||||
Boolean have_filemon = FALSE;
|
||||
void *objdir_freeIt;
|
||||
|
||||
if (oodate)
|
||||
return oodate; /* we're done */
|
||||
|
||||
dname = Var_Value(".OBJDIR", gn, &objdir_freeIt);
|
||||
dname = Var_Value(".OBJDIR", gn);
|
||||
tname = GNode_VarTarget(gn);
|
||||
|
||||
/* if this succeeds fname3 is realpath of dname */
|
||||
if (!meta_needed(gn, dname, fname3, FALSE))
|
||||
if (!meta_needed(gn, dname.str, fname3, FALSE))
|
||||
goto oodate_out;
|
||||
dname = fname3;
|
||||
dname.str = fname3;
|
||||
|
||||
missingFiles = Lst_New();
|
||||
Lst_Init(&missingFiles);
|
||||
|
||||
/*
|
||||
* We need to check if the target is out-of-date. This includes
|
||||
@ -1115,7 +1111,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
*/
|
||||
Make_DoAllVar(gn);
|
||||
|
||||
meta_name(fname, sizeof fname, dname, tname, dname);
|
||||
meta_name(fname, sizeof fname, dname.str, tname, dname.str);
|
||||
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG1(META, "meta_oodate: %s\n", fname);
|
||||
@ -1152,7 +1148,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
/* we want to track all the .meta we read */
|
||||
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
|
||||
|
||||
cmdNode = gn->commands->first;
|
||||
cmdNode = gn->commands.first;
|
||||
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
|
||||
lineno++;
|
||||
if (buf[x - 1] == '\n')
|
||||
@ -1221,8 +1217,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
CHECK_VALID_META(p);
|
||||
pid = atoi(p);
|
||||
if (pid > 0 && pid != lastpid) {
|
||||
const char *ldir;
|
||||
void *tp;
|
||||
FStr ldir;
|
||||
|
||||
if (lastpid > 0) {
|
||||
/* We need to remember these. */
|
||||
@ -1232,15 +1227,15 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid);
|
||||
snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid);
|
||||
lastpid = pid;
|
||||
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(latestdir, ldir, sizeof latestdir);
|
||||
bmake_free(tp);
|
||||
ldir = Var_Value(ldir_vname, VAR_GLOBAL);
|
||||
if (ldir.str != NULL) {
|
||||
strlcpy(latestdir, ldir.str, sizeof latestdir);
|
||||
FStr_Done(&ldir);
|
||||
}
|
||||
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
|
||||
if (ldir) {
|
||||
strlcpy(lcwd, ldir, sizeof lcwd);
|
||||
bmake_free(tp);
|
||||
ldir = Var_Value(lcwd_vname, VAR_GLOBAL);
|
||||
if (ldir.str != NULL) {
|
||||
strlcpy(lcwd, ldir.str, sizeof lcwd);
|
||||
FStr_Done(&ldir);
|
||||
}
|
||||
}
|
||||
/* Skip past the pid. */
|
||||
@ -1305,13 +1300,15 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* the src as for 'R'ead
|
||||
* and the target as for 'W'rite.
|
||||
*/
|
||||
cp = p; /* save this for a second */
|
||||
{
|
||||
char *cp = p; /* save this for a second */
|
||||
/* now get target */
|
||||
if (strsep(&p, " ") == NULL)
|
||||
continue;
|
||||
CHECK_VALID_META(p);
|
||||
move_target = p;
|
||||
p = cp;
|
||||
}
|
||||
/* 'L' and 'M' put single quotes around the args */
|
||||
DEQUOTE(p);
|
||||
DEQUOTE(move_target);
|
||||
@ -1319,12 +1316,12 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
case 'D': /* unlink */
|
||||
if (*p == '/') {
|
||||
/* remove any missingFiles entries that match p */
|
||||
StringListNode *ln = missingFiles->first;
|
||||
StringListNode *ln = missingFiles.first;
|
||||
while (ln != NULL) {
|
||||
StringListNode *next = ln->next;
|
||||
if (path_starts_with(ln->datum, p)) {
|
||||
free(ln->datum);
|
||||
Lst_Remove(missingFiles, ln);
|
||||
Lst_Remove(&missingFiles, ln);
|
||||
}
|
||||
ln = next;
|
||||
}
|
||||
@ -1368,14 +1365,14 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
if (*p != '/')
|
||||
break;
|
||||
|
||||
if (Lst_IsEmpty(metaBailiwick))
|
||||
if (Lst_IsEmpty(&metaBailiwick))
|
||||
break;
|
||||
|
||||
/* ignore cwd - normal dependencies handle those */
|
||||
if (strncmp(p, cwd, cwdlen) == 0)
|
||||
break;
|
||||
|
||||
if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p))
|
||||
if (!has_any_prefix(p, &metaBailiwick))
|
||||
break;
|
||||
|
||||
/* tmpdir might be within */
|
||||
@ -1384,13 +1381,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
|
||||
/* ignore anything containing the string "tmp" */
|
||||
/* XXX: The arguments to strstr must be swapped. */
|
||||
if ((strstr("tmp", p)))
|
||||
if (strstr("tmp", p) != NULL)
|
||||
break;
|
||||
|
||||
if ((link_src != NULL && cached_lstat(p, &cst) < 0) ||
|
||||
(link_src == NULL && cached_stat(p, &cst) < 0)) {
|
||||
if (!meta_ignore(gn, p))
|
||||
append_if_new(missingFiles, p);
|
||||
append_if_new(&missingFiles, p);
|
||||
}
|
||||
break;
|
||||
check_link_src:
|
||||
@ -1418,7 +1415,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
char *sdirs[4];
|
||||
char **sdp;
|
||||
int sdx = 0;
|
||||
int found = 0;
|
||||
Boolean found = FALSE;
|
||||
|
||||
if (*p == '/') {
|
||||
sdirs[sdx++] = p; /* done */
|
||||
@ -1443,13 +1440,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
sdirs[sdx++] = NULL;
|
||||
|
||||
for (sdp = sdirs; *sdp && !found; sdp++) {
|
||||
for (sdp = sdirs; *sdp != NULL && !found; sdp++) {
|
||||
#ifdef DEBUG_META_MODE
|
||||
DEBUG3(META, "%s: %d: looking for: %s\n",
|
||||
fname, lineno, *sdp);
|
||||
#endif
|
||||
if (cached_stat(*sdp, &cst) == 0) {
|
||||
found = 1;
|
||||
found = TRUE;
|
||||
p = *sdp;
|
||||
}
|
||||
}
|
||||
@ -1473,7 +1470,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
* A referenced file outside of CWD is missing.
|
||||
* We cannot catch every eventuality here...
|
||||
*/
|
||||
append_if_new(missingFiles, p);
|
||||
append_if_new(&missingFiles, p);
|
||||
}
|
||||
}
|
||||
if (buf[0] == 'E') {
|
||||
@ -1496,12 +1493,13 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
fname, lineno);
|
||||
oodate = TRUE;
|
||||
} else {
|
||||
const char *cp;
|
||||
char *cmd = cmdNode->datum;
|
||||
Boolean hasOODATE = FALSE;
|
||||
|
||||
if (strstr(cmd, "$?"))
|
||||
if (strstr(cmd, "$?") != NULL)
|
||||
hasOODATE = TRUE;
|
||||
else if ((cp = strstr(cmd, ".OODATE"))) {
|
||||
else if ((cp = strstr(cmd, ".OODATE")) != NULL) {
|
||||
/* check for $[{(].OODATE[:)}] */
|
||||
if (cp > cmd + 2 && cp[-2] == '$')
|
||||
hasOODATE = TRUE;
|
||||
@ -1514,7 +1512,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
(void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd);
|
||||
/* TODO: handle errors */
|
||||
|
||||
if ((cp = strchr(cmd, '\n'))) {
|
||||
if ((cp = strchr(cmd, '\n')) != NULL) {
|
||||
int n;
|
||||
|
||||
/*
|
||||
@ -1534,8 +1532,8 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
||||
break;
|
||||
}
|
||||
cp = strchr(++cp, '\n');
|
||||
} while (cp);
|
||||
cp = strchr(cp + 1, '\n');
|
||||
} while (cp != NULL);
|
||||
if (buf[x - 1] == '\n')
|
||||
buf[x - 1] = '\0';
|
||||
}
|
||||
@ -1571,9 +1569,9 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if (!Lst_IsEmpty(missingFiles)) {
|
||||
if (!Lst_IsEmpty(&missingFiles)) {
|
||||
DEBUG2(META, "%s: missing files: %s...\n",
|
||||
fname, (char *)missingFiles->first->datum);
|
||||
fname, (char *)missingFiles.first->datum);
|
||||
oodate = TRUE;
|
||||
}
|
||||
if (!oodate && !have_filemon && filemonMissing) {
|
||||
@ -1582,10 +1580,11 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
} else {
|
||||
if (writeMeta && (metaMissing || (gn->type & OP_META))) {
|
||||
cp = NULL;
|
||||
const char *cp = NULL;
|
||||
|
||||
/* if target is in .CURDIR we do not need a meta file */
|
||||
if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) {
|
||||
if (gn->path != NULL && (cp = strrchr(gn->path, '/')) != NULL &&
|
||||
(cp > gn->path)) {
|
||||
if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) {
|
||||
cp = NULL; /* not in .CURDIR */
|
||||
}
|
||||
@ -1598,7 +1597,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
}
|
||||
|
||||
Lst_Destroy(missingFiles, free);
|
||||
Lst_DoneCall(&missingFiles, free);
|
||||
|
||||
if (oodate && needOODATE) {
|
||||
/*
|
||||
@ -1611,7 +1610,7 @@ meta_oodate(GNode *gn, Boolean oodate)
|
||||
}
|
||||
|
||||
oodate_out:
|
||||
bmake_free(objdir_freeIt);
|
||||
FStr_Done(&dname);
|
||||
return oodate;
|
||||
}
|
||||
|
||||
@ -1661,7 +1660,7 @@ meta_compat_parent(pid_t child)
|
||||
close(childPipe[1]); /* child side */
|
||||
outfd = childPipe[0];
|
||||
#ifdef USE_FILEMON
|
||||
metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1;
|
||||
metafd = Mybm.filemon != NULL ? filemon_readfd(Mybm.filemon) : -1;
|
||||
#else
|
||||
metafd = -1;
|
||||
#endif
|
||||
@ -1686,7 +1685,7 @@ meta_compat_parent(pid_t child)
|
||||
err(1, "select");
|
||||
}
|
||||
|
||||
if (outfd != -1 && FD_ISSET(outfd, &readfds)) do {
|
||||
if (outfd != -1 && FD_ISSET(outfd, &readfds) != 0) do {
|
||||
/* XXX this is not line-buffered */
|
||||
ssize_t nread = read(outfd, buf, sizeof buf - 1);
|
||||
if (nread == -1)
|
||||
@ -1700,8 +1699,8 @@ meta_compat_parent(pid_t child)
|
||||
fflush(stdout);
|
||||
buf[nread] = '\0';
|
||||
meta_job_output(NULL, buf, "");
|
||||
} while (0);
|
||||
if (metafd != -1 && FD_ISSET(metafd, &readfds)) {
|
||||
} while (/*CONSTCOND*/0);
|
||||
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
|
||||
if (meta_job_event(NULL) <= 0)
|
||||
metafd = -1;
|
||||
}
|
||||
|
4
meta.h
4
meta.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: meta.h,v 1.8 2020/10/19 23:43:55 rillig Exp $ */
|
||||
/* $NetBSD: meta.h,v 1.9 2020/12/10 20:49:11 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Things needed for 'meta' mode.
|
||||
@ -48,7 +48,7 @@ void meta_job_child(struct Job *);
|
||||
void meta_job_parent(struct Job *, pid_t);
|
||||
int meta_job_fd(struct Job *);
|
||||
int meta_job_event(struct Job *);
|
||||
void meta_job_error(struct Job *, GNode *, int, int);
|
||||
void meta_job_error(struct Job *, GNode *, Boolean, int);
|
||||
void meta_job_output(struct Job *, char *, const char *);
|
||||
int meta_cmd_finish(void *);
|
||||
int meta_job_finish(struct Job *);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: metachar.h,v 1.12 2020/11/10 00:32:12 rillig Exp $ */
|
||||
/* $NetBSD: metachar.h,v 1.13 2021/01/10 21:20:46 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2015 The NetBSD Foundation, Inc.
|
||||
@ -35,7 +35,7 @@
|
||||
|
||||
extern unsigned char _metachar[];
|
||||
|
||||
#define is_shell_metachar(c) _metachar[(c) & 0x7f]
|
||||
#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
|
||||
|
||||
MAKE_INLINE int
|
||||
needshell(const char *cmd)
|
||||
|
39
mk/ChangeLog
39
mk/ChangeLog
@ -1,3 +1,42 @@
|
||||
2021-01-06 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20210101
|
||||
|
||||
* dirdeps.mk: first time we are read, just use TARGET_SPEC for
|
||||
_DEP_TARGET_SPEC
|
||||
|
||||
2020-12-22 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* sys.mk (MAKE_SHELL): use ${.SHELL:Ush}
|
||||
and use := when setting SHELL
|
||||
|
||||
2020-12-21 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201221
|
||||
|
||||
* dirdeps-options.mk: latest bmake allows only one arg to .undef
|
||||
|
||||
2020-12-11 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* dirdeps-targets.mk: allow for "." in DIRDEPS_TARGETS_DIRS
|
||||
so that any directory can be treated as a target.
|
||||
|
||||
2020-11-26 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201126
|
||||
|
||||
* own.mk: use .MAKE.{UID,GID} if available.
|
||||
|
||||
* init.mk: suppress _SKIP_BUILD warning if doing -V
|
||||
|
||||
2020-11-20 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201120
|
||||
|
||||
* init.mk: rename LEVEL0_TARGETS to DIRDEPS_BUILD_LEVEL0_TARGETS
|
||||
|
||||
* dirdeps-targets.mk: fix typo in comment
|
||||
|
||||
2020-11-06 Simon J Gerraty <sjg@beast.crufty.net>
|
||||
|
||||
* install-mk (MK_VERSION): 20201106
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: dirdeps-options.mk,v 1.17 2020/08/07 01:57:38 sjg Exp $
|
||||
# $Id: dirdeps-options.mk,v 1.18 2020/12/22 18:10:34 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2018-2020, Simon J. Gerraty
|
||||
#
|
||||
@ -59,7 +59,8 @@ DIRDEPS_OPTIONS_QUALIFIER_LIST ?= \
|
||||
# note that we need to include $o in the variable _o$o
|
||||
# to ensure correct evaluation.
|
||||
.for o in ${DIRDEPS_OPTIONS}
|
||||
.undef _o$o _v$o
|
||||
.undef _o$o
|
||||
.undef _v$o
|
||||
.for x in ${DIRDEPS_OPTIONS_QUALIFIER_LIST}
|
||||
.if defined(MK_$o.$x)
|
||||
_o$o ?= MK_$o.$x
|
||||
|
@ -1,5 +1,5 @@
|
||||
# RCSid:
|
||||
# $Id: dirdeps-targets.mk,v 1.22 2020/08/15 18:00:11 sjg Exp $
|
||||
# $Id: dirdeps-targets.mk,v 1.24 2020/12/11 18:15:43 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2019-2020 Simon J. Gerraty
|
||||
#
|
||||
@ -41,6 +41,7 @@
|
||||
.-include <local.dirdeps-targets.mk>
|
||||
|
||||
# for DIRDEPS_BUILD this is how we prime the pump
|
||||
# include . to allow any directory to work as a target
|
||||
DIRDEPS_TARGETS_DIRS ?= targets targets/pseudo
|
||||
# these prefixes can modify how we behave
|
||||
# they need to be stripped when looking for target dirs
|
||||
@ -76,7 +77,7 @@ DIRDEPS_TARGETS_MACHINE_LIST += \
|
||||
DIRDEPS_TARGETS_MACHINE_LIST := ${DIRDEPS_TARGETS_MACHINE_LIST:O:u}
|
||||
|
||||
# raw Makefile.depend* list
|
||||
tdeps != 'cd' ${SRCTOP} && 'ls' -1 ${tdirs:O:u:@d@$d/${.MAKE.DEPENDFILE_PREFIX}*@} 2> /dev/null; echo
|
||||
tdeps != 'cd' ${SRCTOP} && 'ls' -1 ${tdirs:O:u:@d@$d/${.MAKE.DEPENDFILE_PREFIX}*@:S,^./,,} 2> /dev/null; echo
|
||||
.if ${DEBUG_DIRDEPS_TARGETS:U:Mdep*} != ""
|
||||
.info tdeps=${tdeps}
|
||||
.endif
|
||||
@ -135,7 +136,7 @@ DIRDEPS := ${DIRDEPS:O:u}
|
||||
# if we got DIRDEPS get to work
|
||||
.if !empty(DIRDEPS)
|
||||
DIRDEPS.dirs := ${DIRDEPS:S,^,${SRCTOP}/,:@d@${exists($d):?$d:${d:R}}@}
|
||||
# some targets what to tweak options we might want to process now
|
||||
# some targets want to tweak options we might want to process now
|
||||
.for m in ${DIRDEPS.dirs:S,$,/Makefile.dirdeps.options,}
|
||||
.-include <$m>
|
||||
.endfor
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $Id: dirdeps.mk,v 1.130 2020/11/02 00:34:30 sjg Exp $
|
||||
# $Id: dirdeps.mk,v 1.131 2021/01/07 00:57:51 sjg Exp $
|
||||
|
||||
# Copyright (c) 2010-2020, Simon J. Gerraty
|
||||
# Copyright (c) 2010-2021, Simon J. Gerraty
|
||||
# Copyright (c) 2010-2018, Juniper Networks, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
@ -265,24 +265,9 @@ N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}}
|
||||
|
||||
# if we were included recursively _DEP_TARGET_SPEC should be valid.
|
||||
.if empty(_DEP_TARGET_SPEC)
|
||||
# we may or may not have included a dependfile yet
|
||||
.if defined(.INCLUDEDFROMFILE)
|
||||
_last_dependfile := ${.INCLUDEDFROMFILE:M${.MAKE.DEPENDFILE_PREFIX}*}
|
||||
.else
|
||||
_last_dependfile := ${.MAKE.MAKEFILES:M*/${.MAKE.DEPENDFILE_PREFIX}*:[-1]}
|
||||
.endif
|
||||
.if ${_debug_reldir:U0}
|
||||
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _last_dependfile='${_last_dependfile}'
|
||||
.endif
|
||||
|
||||
.if empty(_last_dependfile) || ${_last_dependfile:E:${N_notmachine}} == ""
|
||||
# this is all we have to work with
|
||||
DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}}
|
||||
_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC}
|
||||
.else
|
||||
_DEP_TARGET_SPEC = ${_last_dependfile:${M_dep_qual_fixes:ts:}:E}
|
||||
.endif
|
||||
.if !empty(_last_dependfile)
|
||||
# if not, just use TARGET_SPEC
|
||||
_DEP_TARGET_SPEC := ${TARGET_SPEC}
|
||||
.if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != ""
|
||||
# record that we've read dependfile for this
|
||||
_dirdeps_checked.${_CURDIR}.${TARGET_SPEC}:
|
||||
.endif
|
||||
|
15
mk/init.mk
15
mk/init.mk
@ -1,4 +1,4 @@
|
||||
# $Id: init.mk,v 1.21 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: init.mk,v 1.25 2020/11/27 17:59:46 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2002, Simon J. Gerraty
|
||||
#
|
||||
@ -65,14 +65,15 @@ CC_PIC?= -DPIC
|
||||
CXX_PIC?= ${CC_PIC}
|
||||
PROFFLAGS?= -DGPROF -DPROF
|
||||
|
||||
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes"
|
||||
# targets that are ok at level 0
|
||||
LEVEL0_TARGETS += clean* destory*
|
||||
M_ListToSkip= O:u:S,^,N,:ts:
|
||||
|
||||
.if ${.MAKE.LEVEL:U1} == 0 && ${MK_DIRDEPS_BUILD:Uno} == "yes" && ${.TARGETS:Uall:${LEVEL0_TARGETS:${M_ListToSkip}}} != ""
|
||||
DIRDEPS_BUILD_LEVEL0_TARGETS += clean* destroy*
|
||||
M_ListToSkip?= O:u:S,^,N,:ts:
|
||||
.if ${.TARGETS:Uall:${DIRDEPS_BUILD_LEVEL0_TARGETS:${M_ListToSkip}}} != ""
|
||||
# this tells lib.mk and prog.mk to not actually build anything
|
||||
_SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
.endif
|
||||
|
||||
.if !defined(.PARSEDIR)
|
||||
# no-op is the best we can do if not bmake.
|
||||
@ -80,13 +81,15 @@ _SKIP_BUILD = not building at level 0
|
||||
.endif
|
||||
|
||||
# define this once for consistency
|
||||
.if empty(_SKIP_BUILD)
|
||||
.if !defined(_SKIP_BUILD)
|
||||
# beforebuild is a hook for things that must be done early
|
||||
all: beforebuild .WAIT realbuild
|
||||
.else
|
||||
all: .PHONY
|
||||
.if !empty(_SKIP_BUILD) && ${.MAKEFLAGS:M-V} == ""
|
||||
.warning ${_SKIP_BUILD}
|
||||
.endif
|
||||
.endif
|
||||
beforebuild:
|
||||
realbuild:
|
||||
|
||||
|
4
mk/install-mk
Normal file → Executable file
4
mk/install-mk
Normal file → Executable file
@ -55,7 +55,7 @@
|
||||
# Simon J. Gerraty <sjg@crufty.net>
|
||||
|
||||
# RCSid:
|
||||
# $Id: install-mk,v 1.184 2020/11/08 05:47:56 sjg Exp $
|
||||
# $Id: install-mk,v 1.190 2021/01/07 00:58:42 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 1994 Simon J. Gerraty
|
||||
#
|
||||
@ -70,7 +70,7 @@
|
||||
# sjg@crufty.net
|
||||
#
|
||||
|
||||
MK_VERSION=20201106
|
||||
MK_VERSION=20210101
|
||||
OWNER=
|
||||
GROUP=
|
||||
MODE=444
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: meta.subdir.mk,v 1.12 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: meta.subdir.mk,v 1.13 2021/01/05 22:24:37 sjg Exp $
|
||||
|
||||
#
|
||||
# @(#) Copyright (c) 2010, Simon J. Gerraty
|
||||
@ -17,7 +17,7 @@
|
||||
.if !defined(NO_SUBDIR) && !empty(SUBDIR)
|
||||
.if make(destroy*) || make(clean*)
|
||||
.MAKE.MODE = compat
|
||||
.if !commands(destroy)
|
||||
.if !commands(obj)
|
||||
.-include <bsd.obj.mk>
|
||||
.endif
|
||||
.elif ${.MAKE.LEVEL} == 0
|
||||
|
0
mk/mkopt.sh
Executable file → Normal file
0
mk/mkopt.sh
Executable file → Normal file
@ -1,4 +1,4 @@
|
||||
# $Id: own.mk,v 1.41 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: own.mk,v 1.42 2020/11/27 18:00:08 sjg Exp $
|
||||
|
||||
.if !target(__${.PARSEFILE}__)
|
||||
__${.PARSEFILE}__:
|
||||
@ -125,10 +125,10 @@ OPTIONS_DEFAULT_DEPENDENT+= \
|
||||
|
||||
.if ${MK_INSTALL_AS_USER} == "yes"
|
||||
# We ignore this if user is root.
|
||||
_uid!= id -u
|
||||
_uid:= ${.MAKE.UID:U${id -u:L:sh}}
|
||||
.if ${_uid} != 0
|
||||
.if !defined(USERGRP)
|
||||
USERGRP!= id -g
|
||||
USERGRP:= ${.MAKE.GID:U${id -g:L:sh}}
|
||||
.export USERGRP
|
||||
.endif
|
||||
.for x in BIN CONF DOC INC INFO FILES KMOD LIB MAN NLS PROG SHARE
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $Id: sys.mk,v 1.51 2020/08/19 17:51:53 sjg Exp $
|
||||
# $Id: sys.mk,v 1.52 2020/12/22 20:44:24 sjg Exp $
|
||||
#
|
||||
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
|
||||
#
|
||||
@ -118,8 +118,8 @@ ROOT_GROUP != sed -n /:0:/s/:.*//p /etc/group
|
||||
unix ?= We run ${_HOST_OSNAME}.
|
||||
|
||||
# We need a Bourne/POSIX shell
|
||||
MAKE_SHELL ?= sh
|
||||
SHELL ?= ${MAKE_SHELL}
|
||||
MAKE_SHELL ?= ${.SHELL:Ush}
|
||||
SHELL := ${MAKE_SHELL}
|
||||
|
||||
# A race condition in mkdir, means that it can bail if another
|
||||
# process made a dir that mkdir expected to.
|
||||
|
194
nonints.h
194
nonints.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: nonints.h,v 1.162 2020/11/16 21:48:18 rillig Exp $ */
|
||||
/* $NetBSD: nonints.h,v 1.186 2020/12/28 00:46:24 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -86,7 +86,7 @@ Boolean Arch_LibOODate(GNode *);
|
||||
Boolean Arch_IsLib(GNode *);
|
||||
|
||||
/* compat.c */
|
||||
int Compat_RunCommand(const char *, GNode *);
|
||||
int Compat_RunCommand(const char *, GNode *, StringListNode *);
|
||||
void Compat_Run(GNodeList *);
|
||||
void Compat_Make(GNode *, GNode *);
|
||||
|
||||
@ -96,6 +96,21 @@ CondEvalResult Cond_EvalLine(const char *);
|
||||
void Cond_restore_depth(unsigned int);
|
||||
unsigned int Cond_save_depth(void);
|
||||
|
||||
/* dir.c; see also dir.h */
|
||||
|
||||
MAKE_INLINE const char *
|
||||
str_basename(const char *pathname)
|
||||
{
|
||||
const char *lastSlash = strrchr(pathname, '/');
|
||||
return lastSlash != NULL ? lastSlash + 1 : pathname;
|
||||
}
|
||||
|
||||
MAKE_INLINE SearchPath *
|
||||
SearchPath_New(void)
|
||||
{ return Lst_New(); }
|
||||
|
||||
void SearchPath_Free(SearchPath *);
|
||||
|
||||
/* for.c */
|
||||
int For_Eval(const char *);
|
||||
Boolean For_Accum(const char *);
|
||||
@ -109,7 +124,6 @@ void JobReapChild(pid_t, WAIT_T, Boolean);
|
||||
/* main.c */
|
||||
Boolean GetBooleanVar(const char *, Boolean);
|
||||
void Main_ParseArgLine(const char *);
|
||||
void MakeMode(const char *);
|
||||
char *Cmd_Exec(const char *, const char **);
|
||||
void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
|
||||
void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD;
|
||||
@ -140,27 +154,81 @@ typedef struct VarAssign {
|
||||
const char *value; /* unexpanded */
|
||||
} VarAssign;
|
||||
|
||||
typedef char *(*NextBufProc)(void *, size_t *);
|
||||
typedef char *(*ReadMoreProc)(void *, size_t *);
|
||||
|
||||
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
|
||||
Boolean Parse_IsVar(const char *, VarAssign *out_var);
|
||||
void Parse_DoVar(VarAssign *, GNode *);
|
||||
void Parse_AddIncludeDir(const char *);
|
||||
void Parse_File(const char *, int);
|
||||
void Parse_SetInput(const char *, int, int, NextBufProc, void *);
|
||||
GNodeList *Parse_MainName(void);
|
||||
void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
|
||||
void Parse_MainName(GNodeList *);
|
||||
int Parse_GetFatals(void);
|
||||
|
||||
/* str.c */
|
||||
|
||||
/* A read-only string that may need to be freed after use. */
|
||||
typedef struct FStr {
|
||||
const char *str;
|
||||
void *freeIt;
|
||||
} FStr;
|
||||
|
||||
/* A modifiable string that may need to be freed after use. */
|
||||
typedef struct MFStr {
|
||||
char *str;
|
||||
void *freeIt;
|
||||
} MFStr;
|
||||
|
||||
typedef struct Words {
|
||||
char **words;
|
||||
size_t len;
|
||||
void *freeIt;
|
||||
} Words;
|
||||
|
||||
/* Return a string that is the sole owner of str. */
|
||||
MAKE_INLINE FStr
|
||||
FStr_InitOwn(char *str)
|
||||
{
|
||||
return (FStr){ str, str };
|
||||
}
|
||||
|
||||
/* Return a string that refers to the shared str. */
|
||||
MAKE_INLINE FStr
|
||||
FStr_InitRefer(const char *str)
|
||||
{
|
||||
return (FStr){ str, NULL };
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
FStr_Done(FStr *fstr)
|
||||
{
|
||||
free(fstr->freeIt);
|
||||
}
|
||||
|
||||
/* Return a string that is the sole owner of str. */
|
||||
MAKE_INLINE MFStr
|
||||
MFStr_InitOwn(char *str)
|
||||
{
|
||||
return (MFStr){ str, str };
|
||||
}
|
||||
|
||||
/* Return a string that refers to the shared str. */
|
||||
MAKE_INLINE MFStr
|
||||
MFStr_InitRefer(char *str)
|
||||
{
|
||||
return (MFStr){ str, NULL };
|
||||
}
|
||||
|
||||
MAKE_INLINE void
|
||||
MFStr_Done(MFStr *mfstr)
|
||||
{
|
||||
free(mfstr->freeIt);
|
||||
}
|
||||
|
||||
Words Str_Words(const char *, Boolean);
|
||||
MAKE_INLINE void
|
||||
Words_Free(Words w) {
|
||||
Words_Free(Words w)
|
||||
{
|
||||
free(w.words);
|
||||
free(w.freeIt);
|
||||
}
|
||||
@ -204,15 +272,13 @@ GNode *Targ_FindNode(const char *);
|
||||
GNode *Targ_GetNode(const char *);
|
||||
GNode *Targ_NewInternalNode(const char *);
|
||||
GNode *Targ_GetEndNode(void);
|
||||
GNodeList *Targ_FindList(StringList *);
|
||||
Boolean Targ_Ignore(const GNode *);
|
||||
Boolean Targ_Silent(const GNode *);
|
||||
void Targ_FindList(GNodeList *, StringList *);
|
||||
Boolean Targ_Precious(const GNode *);
|
||||
void Targ_SetMain(GNode *);
|
||||
void Targ_PrintCmds(GNode *);
|
||||
void Targ_PrintNode(GNode *, int);
|
||||
void Targ_PrintNodes(GNodeList *, int);
|
||||
char *Targ_FmtTime(time_t);
|
||||
const char *Targ_FmtTime(time_t);
|
||||
void Targ_PrintType(int);
|
||||
void Targ_PrintGraph(int);
|
||||
void Targ_Propagate(void);
|
||||
@ -237,14 +303,27 @@ typedef enum VarEvalFlags {
|
||||
/* Keep '$$' as '$$' instead of reducing it to a single '$'.
|
||||
*
|
||||
* Used in variable assignments using the ':=' operator. It allows
|
||||
* multiple such assignments to be chained without accidentally expanding
|
||||
* '$$file' to '$file' in the first assignment and interpreting it as
|
||||
* '${f}' followed by 'ile' in the next assignment.
|
||||
* multiple such assignments to be chained without accidentally
|
||||
* expanding '$$file' to '$file' in the first assignment and
|
||||
* interpreting it as '${f}' followed by 'ile' in the next assignment.
|
||||
*
|
||||
* See also preserveUndefined, which preserves subexpressions that are
|
||||
* based on undefined variables; maybe that can be converted to a flag
|
||||
* as well. */
|
||||
VARE_KEEP_DOLLAR = 1 << 2
|
||||
VARE_KEEP_DOLLAR = 1 << 2,
|
||||
|
||||
/*
|
||||
* Keep undefined variables as-is instead of expanding them to an
|
||||
* empty string.
|
||||
*
|
||||
* Example for a ':=' assignment:
|
||||
* CFLAGS = $(.INCLUDES)
|
||||
* CFLAGS := -I.. $(CFLAGS)
|
||||
* # If .INCLUDES (an undocumented special variable, by the
|
||||
* # way) is still undefined, the updated CFLAGS becomes
|
||||
* # "-I.. $(.INCLUDES)".
|
||||
*/
|
||||
VARE_KEEP_UNDEF = 1 << 3
|
||||
} VarEvalFlags;
|
||||
|
||||
typedef enum VarSetFlags {
|
||||
@ -258,76 +337,55 @@ typedef enum VarSetFlags {
|
||||
VAR_SET_READONLY = 1 << 1
|
||||
} VarSetFlags;
|
||||
|
||||
/* The state of error handling returned by Var_Parse.
|
||||
*
|
||||
* As of 2020-09-13, this bitset looks quite bloated,
|
||||
* with all the constants doubled.
|
||||
*
|
||||
* Its purpose is to first document the existing behavior,
|
||||
* and then migrate away from the SILENT constants, step by step,
|
||||
* as these are not suited for reliable, consistent error handling
|
||||
* and reporting. */
|
||||
/* The state of error handling returned by Var_Parse. */
|
||||
typedef enum VarParseResult {
|
||||
|
||||
/* Both parsing and evaluation succeeded. */
|
||||
VPR_OK = 0x0000,
|
||||
VPR_OK,
|
||||
|
||||
/* See if a message has already been printed for this error. */
|
||||
VPR_ANY_MSG = 0x0001,
|
||||
/* Parsing or evaluating failed, with an error message. */
|
||||
VPR_ERR,
|
||||
|
||||
/* Parsing failed.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_PARSE_MSG instead. */
|
||||
VPR_PARSE_SILENT = 0x0002,
|
||||
/*
|
||||
* Parsing succeeded, undefined expressions are allowed and the
|
||||
* expression was still undefined after applying all modifiers.
|
||||
* No error message is printed in this case.
|
||||
*
|
||||
* Some callers handle this case differently, so return this
|
||||
* information to them, for now.
|
||||
*
|
||||
* TODO: Replace this with a new flag VARE_KEEP_UNDEFINED.
|
||||
*/
|
||||
VPR_UNDEF
|
||||
|
||||
/* Parsing failed.
|
||||
* An error message has already been printed. */
|
||||
VPR_PARSE_MSG = VPR_PARSE_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* During evaluation, VARE_UNDEFERR was set and there was an undefined
|
||||
* variable.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_UNDEF_MSG instead. */
|
||||
VPR_UNDEF_SILENT = 0x0004,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* During evaluation, VARE_UNDEFERR was set and there was an undefined
|
||||
* variable.
|
||||
* An error message has already been printed. */
|
||||
VPR_UNDEF_MSG = VPR_UNDEF_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* Evaluation failed.
|
||||
* No error message has been printed yet.
|
||||
* Deprecated, migrate to VPR_EVAL_MSG instead. */
|
||||
VPR_EVAL_SILENT = 0x0006,
|
||||
|
||||
/* Parsing succeeded.
|
||||
* Evaluation failed.
|
||||
* An error message has already been printed. */
|
||||
VPR_EVAL_MSG = VPR_EVAL_SILENT | VPR_ANY_MSG,
|
||||
|
||||
/* The exact error handling status is not known yet.
|
||||
* Deprecated, migrate to VPR_OK or any VPE_*_MSG instead. */
|
||||
VPR_UNKNOWN = 0x0008
|
||||
} VarParseResult;
|
||||
|
||||
typedef enum VarExportMode {
|
||||
/* .export-env */
|
||||
VEM_ENV,
|
||||
/* .export: Initial export or update an already exported variable. */
|
||||
VEM_PLAIN,
|
||||
/* .export-literal: Do not expand the variable value. */
|
||||
VEM_LITERAL
|
||||
} VarExportMode;
|
||||
|
||||
void Var_DeleteVar(const char *, GNode *);
|
||||
void Var_Delete(const char *, GNode *);
|
||||
void Var_Undef(const char *);
|
||||
void Var_Set(const char *, const char *, GNode *);
|
||||
void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags);
|
||||
void Var_Append(const char *, const char *, GNode *);
|
||||
Boolean Var_Exists(const char *, GNode *);
|
||||
const char *Var_Value(const char *, GNode *, void **);
|
||||
FStr Var_Value(const char *, GNode *);
|
||||
const char *Var_ValueDirect(const char *, GNode *);
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags,
|
||||
const char **, void **);
|
||||
VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *);
|
||||
VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **);
|
||||
void Var_Stats(void);
|
||||
void Var_Dump(GNode *);
|
||||
void Var_ExportVars(void);
|
||||
void Var_Export(const char *, Boolean);
|
||||
void Var_UnExport(const char *);
|
||||
void Var_ReexportVars(void);
|
||||
void Var_Export(VarExportMode, const char *);
|
||||
void Var_ExportVars(const char *);
|
||||
void Var_UnExport(Boolean, const char *);
|
||||
|
||||
/* util.c */
|
||||
typedef void (*SignalProc)(int);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: pathnames.h,v 1.17 2009/04/11 09:41:18 apb Exp $ */
|
||||
/* $NetBSD: pathnames.h,v 1.18 2020/11/29 09:27:40 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1990, 1993
|
||||
@ -29,7 +29,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90
|
||||
* $Id: pathnames.h,v 1.13 2009/08/26 23:43:42 sjg Exp $
|
||||
* $Id: pathnames.h,v 1.14 2020/11/30 19:27:41 sjg Exp $
|
||||
*/
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
@ -43,6 +43,7 @@
|
||||
#ifdef HAVE_PATHS_H
|
||||
#include <paths.h>
|
||||
#endif
|
||||
|
||||
#define _PATH_OBJDIR "obj"
|
||||
#define _PATH_OBJDIRPREFIX "/usr/obj"
|
||||
#ifndef _PATH_DEFSHELLDIR
|
||||
|
15
str.c
15
str.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: str.c,v 1.74 2020/11/16 18:28:27 rillig Exp $ */
|
||||
/* $NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -71,7 +71,7 @@
|
||||
#include "make.h"
|
||||
|
||||
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.74 2020/11/16 18:28:27 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $");
|
||||
|
||||
/* Return the concatenation of s1 and s2, freshly allocated. */
|
||||
char *
|
||||
@ -115,7 +115,8 @@ str_concat4(const char *s1, const char *s2, const char *s3, const char *s4)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Fracture a string into an array of words (as delineated by tabs or spaces)
|
||||
/*
|
||||
* Fracture a string into an array of words (as delineated by tabs or spaces)
|
||||
* taking quotation marks into account.
|
||||
*
|
||||
* If expand is TRUE, quotes are removed and escape sequences such as \r, \t,
|
||||
@ -142,7 +143,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
|
||||
/* words_buf holds the words, separated by '\0'. */
|
||||
str_len = strlen(str);
|
||||
words_buf = bmake_malloc(strlen(str) + 1);
|
||||
words_buf = bmake_malloc(str_len + 1);
|
||||
|
||||
words_cap = str_len / 5 > 50 ? str_len / 5 : 50;
|
||||
words = bmake_malloc((words_cap + 1) * sizeof(char *));
|
||||
@ -160,7 +161,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
switch (ch) {
|
||||
case '"':
|
||||
case '\'':
|
||||
if (inquote) {
|
||||
if (inquote != '\0') {
|
||||
if (inquote == ch)
|
||||
inquote = '\0';
|
||||
else
|
||||
@ -188,7 +189,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
if (inquote)
|
||||
if (inquote != '\0')
|
||||
break;
|
||||
if (word_start == NULL)
|
||||
continue;
|
||||
@ -211,7 +212,7 @@ Str_Words(const char *str, Boolean expand)
|
||||
words[words_len++] = word_start;
|
||||
word_start = NULL;
|
||||
if (ch == '\n' || ch == '\0') {
|
||||
if (expand && inquote) {
|
||||
if (expand && inquote != '\0') {
|
||||
free(words);
|
||||
free(words_buf);
|
||||
return (Words){ NULL, 0, NULL };
|
||||
|
268
targ.c
268
targ.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $ */
|
||||
/* $NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988, 1989, 1990, 1993
|
||||
@ -93,12 +93,6 @@
|
||||
* Targ_FindList Given a list of names, find nodes for all
|
||||
* of them, creating them as necessary.
|
||||
*
|
||||
* Targ_Ignore Return TRUE if errors should be ignored when
|
||||
* creating the given target.
|
||||
*
|
||||
* Targ_Silent Return TRUE if we should be silent when
|
||||
* creating the given target.
|
||||
*
|
||||
* Targ_Precious Return TRUE if the target is precious and
|
||||
* should not be removed if we are interrupted.
|
||||
*
|
||||
@ -108,7 +102,7 @@
|
||||
*
|
||||
* Debugging:
|
||||
* Targ_PrintGraph
|
||||
* Print out the entire graphm all variables and
|
||||
* Print out the entire graph, all variables and
|
||||
* statistics for the directory cache. Should print
|
||||
* something for suffixes, too, but...
|
||||
*/
|
||||
@ -119,14 +113,17 @@
|
||||
#include "dir.h"
|
||||
|
||||
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.135 2020/11/16 22:28:44 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $");
|
||||
|
||||
/* All target nodes found so far, but not the source nodes. */
|
||||
static GNodeList *allTargets;
|
||||
/*
|
||||
* All target nodes that appeared on the left-hand side of one of the
|
||||
* dependency operators ':', '::', '!'.
|
||||
*/
|
||||
static GNodeList allTargets = LST_INIT;
|
||||
static HashTable allTargetsByName;
|
||||
|
||||
#ifdef CLEANUP
|
||||
static GNodeList *allNodes;
|
||||
static GNodeList allNodes = LST_INIT;
|
||||
|
||||
static void GNode_Free(void *);
|
||||
#endif
|
||||
@ -134,11 +131,7 @@ static void GNode_Free(void *);
|
||||
void
|
||||
Targ_Init(void)
|
||||
{
|
||||
allTargets = Lst_New();
|
||||
HashTable_Init(&allTargetsByName);
|
||||
#ifdef CLEANUP
|
||||
allNodes = Lst_New();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -146,9 +139,9 @@ Targ_End(void)
|
||||
{
|
||||
Targ_Stats();
|
||||
#ifdef CLEANUP
|
||||
Lst_Free(allTargets);
|
||||
Lst_Done(&allTargets);
|
||||
HashTable_Done(&allTargetsByName);
|
||||
Lst_Destroy(allNodes, GNode_Free);
|
||||
Lst_DoneCall(&allNodes, GNode_Free);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -166,10 +159,11 @@ Targ_Stats(void)
|
||||
GNodeList *
|
||||
Targ_List(void)
|
||||
{
|
||||
return allTargets;
|
||||
return &allTargets;
|
||||
}
|
||||
|
||||
/* Create a new graph node, but don't register it anywhere.
|
||||
/*
|
||||
* Create a new graph node, but don't register it anywhere.
|
||||
*
|
||||
* Graph nodes that appear on the left-hand side of a dependency line such
|
||||
* as "target: source" are called targets. XXX: In some cases (like the
|
||||
@ -192,30 +186,30 @@ GNode_New(const char *name)
|
||||
gn->name = bmake_strdup(name);
|
||||
gn->uname = NULL;
|
||||
gn->path = NULL;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : 0;
|
||||
gn->flags = 0;
|
||||
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE;
|
||||
gn->flags = GNF_NONE;
|
||||
gn->made = UNMADE;
|
||||
gn->unmade = 0;
|
||||
gn->mtime = 0;
|
||||
gn->youngestChild = NULL;
|
||||
gn->implicitParents = Lst_New();
|
||||
gn->parents = Lst_New();
|
||||
gn->children = Lst_New();
|
||||
gn->order_pred = Lst_New();
|
||||
gn->order_succ = Lst_New();
|
||||
gn->cohorts = Lst_New();
|
||||
Lst_Init(&gn->implicitParents);
|
||||
Lst_Init(&gn->parents);
|
||||
Lst_Init(&gn->children);
|
||||
Lst_Init(&gn->order_pred);
|
||||
Lst_Init(&gn->order_succ);
|
||||
Lst_Init(&gn->cohorts);
|
||||
gn->cohort_num[0] = '\0';
|
||||
gn->unmade_cohorts = 0;
|
||||
gn->centurion = NULL;
|
||||
gn->checked_seqno = 0;
|
||||
HashTable_Init(&gn->context);
|
||||
gn->commands = Lst_New();
|
||||
HashTable_Init(&gn->vars);
|
||||
Lst_Init(&gn->commands);
|
||||
gn->suffix = NULL;
|
||||
gn->fname = NULL;
|
||||
gn->lineno = 0;
|
||||
|
||||
#ifdef CLEANUP
|
||||
Lst_Append(allNodes, gn);
|
||||
Lst_Append(&allNodes, gn);
|
||||
#endif
|
||||
|
||||
return gn;
|
||||
@ -230,22 +224,49 @@ GNode_Free(void *gnp)
|
||||
free(gn->name);
|
||||
free(gn->uname);
|
||||
free(gn->path);
|
||||
/* gn->youngestChild is not owned by this node. */
|
||||
Lst_Free(gn->implicitParents); /* ... but not the nodes themselves, */
|
||||
Lst_Free(gn->parents); /* as they are not owned by this node. */
|
||||
Lst_Free(gn->children); /* likewise */
|
||||
Lst_Free(gn->order_pred); /* likewise */
|
||||
Lst_Free(gn->order_succ); /* likewise */
|
||||
Lst_Free(gn->cohorts); /* likewise */
|
||||
HashTable_Done(&gn->context); /* ... but not the variables themselves,
|
||||
* even though they are owned by this node.
|
||||
* XXX: they should probably be freed. */
|
||||
Lst_Free(gn->commands); /* ... but not the commands themselves,
|
||||
* as they may be shared with other nodes. */
|
||||
/* gn->suffix is not owned by this node. */
|
||||
/* XXX: gn->suffix should be unreferenced here. This requires a thorough
|
||||
* check that the reference counting is done correctly in all places,
|
||||
* otherwise a suffix might be freed too early. */
|
||||
|
||||
/* Don't free gn->youngestChild since it is not owned by this node. */
|
||||
|
||||
/*
|
||||
* In the following lists, only free the list nodes, but not the
|
||||
* GNodes in them since these are not owned by this node.
|
||||
*/
|
||||
Lst_Done(&gn->implicitParents);
|
||||
Lst_Done(&gn->parents);
|
||||
Lst_Done(&gn->children);
|
||||
Lst_Done(&gn->order_pred);
|
||||
Lst_Done(&gn->order_succ);
|
||||
Lst_Done(&gn->cohorts);
|
||||
|
||||
/*
|
||||
* Do not free the variables themselves, even though they are owned
|
||||
* by this node.
|
||||
*
|
||||
* XXX: For the nodes that represent targets or sources (and not
|
||||
* VAR_GLOBAL), it should be safe to free the variables as well,
|
||||
* since each node manages the memory for all its variables itself.
|
||||
*
|
||||
* XXX: The GNodes that are only used as variable contexts (VAR_CMD,
|
||||
* VAR_GLOBAL, VAR_INTERNAL) are not freed at all (see Var_End, where
|
||||
* they are not mentioned). These might be freed at all, if their
|
||||
* variable values are indeed not used anywhere else (see Trace_Init
|
||||
* for the only suspicious use).
|
||||
*/
|
||||
HashTable_Done(&gn->vars);
|
||||
|
||||
/*
|
||||
* Do not free the commands themselves, as they may be shared with
|
||||
* other nodes.
|
||||
*/
|
||||
Lst_Done(&gn->commands);
|
||||
|
||||
/*
|
||||
* gn->suffix is not owned by this node.
|
||||
*
|
||||
* XXX: gn->suffix should be unreferenced here. This requires a
|
||||
* thorough check that the reference counting is done correctly in
|
||||
* all places, otherwise a suffix might be freed too early.
|
||||
*/
|
||||
|
||||
free(gn);
|
||||
}
|
||||
@ -285,7 +306,8 @@ Targ_NewInternalNode(const char *name)
|
||||
{
|
||||
GNode *gn = GNode_New(name);
|
||||
Var_Append(".ALLTARGETS", name, VAR_GLOBAL);
|
||||
Lst_Append(allTargets, gn);
|
||||
Lst_Append(&allTargets, gn);
|
||||
DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
|
||||
if (doing_depend)
|
||||
gn->flags |= FROM_DEPEND;
|
||||
return gn;
|
||||
@ -295,10 +317,15 @@ Targ_NewInternalNode(const char *name)
|
||||
* Return the .END node, which contains the commands to be run when
|
||||
* everything else has been made.
|
||||
*/
|
||||
GNode *Targ_GetEndNode(void)
|
||||
GNode *
|
||||
Targ_GetEndNode(void)
|
||||
{
|
||||
/* Save the node locally to avoid having to search for it all the time. */
|
||||
/*
|
||||
* 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;
|
||||
@ -306,32 +333,17 @@ GNode *Targ_GetEndNode(void)
|
||||
return endNode;
|
||||
}
|
||||
|
||||
/* Return the named nodes, creating them as necessary. */
|
||||
GNodeList *
|
||||
Targ_FindList(StringList *names)
|
||||
/* Add the named nodes to the list, creating them as necessary. */
|
||||
void
|
||||
Targ_FindList(GNodeList *gns, StringList *names)
|
||||
{
|
||||
StringListNode *ln;
|
||||
GNodeList *nodes = Lst_New();
|
||||
|
||||
for (ln = names->first; ln != NULL; ln = ln->next) {
|
||||
const char *name = ln->datum;
|
||||
GNode *gn = Targ_GetNode(name);
|
||||
Lst_Append(nodes, gn);
|
||||
Lst_Append(gns, gn);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/* Return true if should ignore errors when creating gn. */
|
||||
Boolean
|
||||
Targ_Ignore(const GNode *gn)
|
||||
{
|
||||
return opts.ignoreErrors || gn->type & OP_IGNORE;
|
||||
}
|
||||
|
||||
/* Return true if be silent when creating gn. */
|
||||
Boolean
|
||||
Targ_Silent(const GNode *gn)
|
||||
{
|
||||
return opts.beSilent || gn->type & OP_SILENT;
|
||||
}
|
||||
|
||||
/* See if the given target is precious. */
|
||||
@ -358,10 +370,10 @@ Targ_SetMain(GNode *gn)
|
||||
static void
|
||||
PrintNodeNames(GNodeList *gnodes)
|
||||
{
|
||||
GNodeListNode *node;
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (node = gnodes->first; node != NULL; node = node->next) {
|
||||
GNode *gn = node->datum;
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
debug_printf(" %s%s", gn->name, gn->cohort_num);
|
||||
}
|
||||
}
|
||||
@ -380,21 +392,24 @@ void
|
||||
Targ_PrintCmds(GNode *gn)
|
||||
{
|
||||
StringListNode *ln;
|
||||
for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
|
||||
|
||||
for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
|
||||
const char *cmd = ln->datum;
|
||||
debug_printf("\t%s\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Format a modification time in some reasonable way and return it.
|
||||
* The time is placed in a static area, so it is overwritten with each call. */
|
||||
char *
|
||||
/*
|
||||
* Format a modification time in some reasonable way and return it.
|
||||
* The formatted time is placed in a static area, so it is overwritten
|
||||
* with each call.
|
||||
*/
|
||||
const char *
|
||||
Targ_FmtTime(time_t tm)
|
||||
{
|
||||
struct tm *parts;
|
||||
static char buf[128];
|
||||
|
||||
parts = localtime(&tm);
|
||||
struct tm *parts = localtime(&tm);
|
||||
(void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts);
|
||||
return buf;
|
||||
}
|
||||
@ -405,32 +420,32 @@ Targ_PrintType(int type)
|
||||
{
|
||||
int tbit;
|
||||
|
||||
#define PRINTBIT(attr) case CONCAT(OP_,attr): debug_printf(" ." #attr); break
|
||||
#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))debug_printf(" ." #attr); break
|
||||
|
||||
type &= ~OP_OPMASK;
|
||||
|
||||
while (type) {
|
||||
while (type != 0) {
|
||||
tbit = 1 << (ffs(type) - 1);
|
||||
type &= ~tbit;
|
||||
|
||||
switch (tbit) {
|
||||
PRINTBIT(OPTIONAL);
|
||||
PRINTBIT(USE);
|
||||
PRINTBIT(EXEC);
|
||||
PRINTBIT(IGNORE);
|
||||
PRINTBIT(PRECIOUS);
|
||||
PRINTBIT(SILENT);
|
||||
PRINTBIT(MAKE);
|
||||
PRINTBIT(JOIN);
|
||||
PRINTBIT(INVISIBLE);
|
||||
PRINTBIT(NOTMAIN);
|
||||
PRINTDBIT(LIB);
|
||||
/*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */
|
||||
case OP_MEMBER: if (DEBUG(TARG))debug_printf(" .MEMBER"); break;
|
||||
PRINTDBIT(ARCHV);
|
||||
PRINTDBIT(MADE);
|
||||
PRINTDBIT(PHONY);
|
||||
#define PRINTBIT(bit, attr) case bit: debug_printf(" " attr); break
|
||||
#define PRINTDBIT(bit, attr) case bit: DEBUG0(TARG, " " attr); break
|
||||
PRINTBIT(OP_OPTIONAL, ".OPTIONAL");
|
||||
PRINTBIT(OP_USE, ".USE");
|
||||
PRINTBIT(OP_EXEC, ".EXEC");
|
||||
PRINTBIT(OP_IGNORE, ".IGNORE");
|
||||
PRINTBIT(OP_PRECIOUS, ".PRECIOUS");
|
||||
PRINTBIT(OP_SILENT, ".SILENT");
|
||||
PRINTBIT(OP_MAKE, ".MAKE");
|
||||
PRINTBIT(OP_JOIN, ".JOIN");
|
||||
PRINTBIT(OP_INVISIBLE, ".INVISIBLE");
|
||||
PRINTBIT(OP_NOTMAIN, ".NOTMAIN");
|
||||
PRINTDBIT(OP_LIB, ".LIB");
|
||||
PRINTDBIT(OP_MEMBER, ".MEMBER");
|
||||
PRINTDBIT(OP_ARCHV, ".ARCHV");
|
||||
PRINTDBIT(OP_MADE, ".MADE");
|
||||
PRINTDBIT(OP_PHONY, ".PHONY");
|
||||
#undef PRINTBIT
|
||||
#undef PRINTDBIT
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -474,54 +489,54 @@ Targ_PrintNode(GNode *gn, int pass)
|
||||
if (gn->flags == 0)
|
||||
return;
|
||||
|
||||
if (GNode_IsTarget(gn)) {
|
||||
if (!GNode_IsTarget(gn))
|
||||
return;
|
||||
|
||||
debug_printf("#\n");
|
||||
if (gn == mainTarg) {
|
||||
if (gn == mainTarg)
|
||||
debug_printf("# *** MAIN TARGET ***\n");
|
||||
}
|
||||
|
||||
if (pass >= 2) {
|
||||
if (gn->unmade > 0) {
|
||||
if (gn->unmade > 0)
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
} else {
|
||||
else
|
||||
debug_printf("# No unmade children\n");
|
||||
}
|
||||
if (!(gn->type & (OP_JOIN | OP_USE | OP_USEBEFORE | OP_EXEC))) {
|
||||
if (gn->mtime != 0) {
|
||||
debug_printf("# last modified %s: %s\n",
|
||||
Targ_FmtTime(gn->mtime),
|
||||
made_name(gn->made));
|
||||
} else if (gn->made != UNMADE) {
|
||||
debug_printf("# non-existent (maybe): %s\n",
|
||||
debug_printf("# nonexistent (maybe): %s\n",
|
||||
made_name(gn->made));
|
||||
} else {
|
||||
} else
|
||||
debug_printf("# unmade\n");
|
||||
}
|
||||
}
|
||||
PrintNodeNamesLine("implicit parents", gn->implicitParents);
|
||||
PrintNodeNamesLine("implicit parents", &gn->implicitParents);
|
||||
} else {
|
||||
if (gn->unmade)
|
||||
if (gn->unmade != 0)
|
||||
debug_printf("# %d unmade children\n", gn->unmade);
|
||||
}
|
||||
PrintNodeNamesLine("parents", gn->parents);
|
||||
PrintNodeNamesLine("order_pred", gn->order_pred);
|
||||
PrintNodeNamesLine("order_succ", gn->order_succ);
|
||||
|
||||
PrintNodeNamesLine("parents", &gn->parents);
|
||||
PrintNodeNamesLine("order_pred", &gn->order_pred);
|
||||
PrintNodeNamesLine("order_succ", &gn->order_succ);
|
||||
|
||||
debug_printf("%-16s%s", gn->name, GNode_OpName(gn));
|
||||
Targ_PrintType(gn->type);
|
||||
PrintNodeNames(gn->children);
|
||||
PrintNodeNames(&gn->children);
|
||||
debug_printf("\n");
|
||||
Targ_PrintCmds(gn);
|
||||
debug_printf("\n\n");
|
||||
if (gn->type & OP_DOUBLEDEP) {
|
||||
Targ_PrintNodes(gn->cohorts, pass);
|
||||
}
|
||||
}
|
||||
if (gn->type & OP_DOUBLEDEP)
|
||||
Targ_PrintNodes(&gn->cohorts, pass);
|
||||
}
|
||||
|
||||
void
|
||||
Targ_PrintNodes(GNodeList *gnodes, int pass)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = gnodes->first; ln != NULL; ln = ln->next)
|
||||
Targ_PrintNode(ln->datum, pass);
|
||||
}
|
||||
@ -532,7 +547,7 @@ PrintOnlySources(void)
|
||||
{
|
||||
GNodeListNode *ln;
|
||||
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = allTargets.first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
if (GNode_IsTarget(gn))
|
||||
continue;
|
||||
@ -543,7 +558,8 @@ PrintOnlySources(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Input:
|
||||
/*
|
||||
* Input:
|
||||
* pass 1 => before processing
|
||||
* 2 => after processing
|
||||
* 3 => after processing, an error occurred
|
||||
@ -552,7 +568,7 @@ void
|
||||
Targ_PrintGraph(int pass)
|
||||
{
|
||||
debug_printf("#*** Input graph:\n");
|
||||
Targ_PrintNodes(allTargets, pass);
|
||||
Targ_PrintNodes(&allTargets, pass);
|
||||
debug_printf("\n");
|
||||
debug_printf("\n");
|
||||
|
||||
@ -573,24 +589,26 @@ Targ_PrintGraph(int pass)
|
||||
Suff_PrintAll();
|
||||
}
|
||||
|
||||
/* Propagate some type information to cohort nodes (those from the '::'
|
||||
/*
|
||||
* Propagate some type information to cohort nodes (those from the '::'
|
||||
* dependency operator).
|
||||
*
|
||||
* Should be called after the makefiles are parsed but before any action is
|
||||
* taken. */
|
||||
* taken.
|
||||
*/
|
||||
void
|
||||
Targ_Propagate(void)
|
||||
{
|
||||
GNodeListNode *ln, *cln;
|
||||
|
||||
for (ln = allTargets->first; ln != NULL; ln = ln->next) {
|
||||
for (ln = allTargets.first; ln != NULL; ln = ln->next) {
|
||||
GNode *gn = ln->datum;
|
||||
GNodeType type = gn->type;
|
||||
|
||||
if (!(type & OP_DOUBLEDEP))
|
||||
continue;
|
||||
|
||||
for (cln = gn->cohorts->first; cln != NULL; cln = cln->next) {
|
||||
for (cln = gn->cohorts.first; cln != NULL; cln = cln->next) {
|
||||
GNode *cohort = cln->datum;
|
||||
|
||||
cohort->type |= type & ~OP_OPMASK;
|
||||
|
16
trace.c
16
trace.c
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $ */
|
||||
/* $NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
@ -48,7 +48,7 @@
|
||||
#include "job.h"
|
||||
#include "trace.h"
|
||||
|
||||
MAKE_RCSID("$NetBSD: trace.c,v 1.21 2020/10/31 22:05:56 rillig Exp $");
|
||||
MAKE_RCSID("$NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $");
|
||||
|
||||
static FILE *trfile;
|
||||
static pid_t trpid;
|
||||
@ -67,11 +67,12 @@ void
|
||||
Trace_Init(const char *pathname)
|
||||
{
|
||||
if (pathname != NULL) {
|
||||
void *dontFreeIt;
|
||||
FStr curDir;
|
||||
trpid = getpid();
|
||||
/* XXX: This variable may get overwritten later, which
|
||||
* would make trwd point to undefined behavior. */
|
||||
trwd = Var_Value(".CURDIR", VAR_GLOBAL, &dontFreeIt);
|
||||
curDir = Var_Value(".CURDIR", VAR_GLOBAL);
|
||||
trwd = curDir.str;
|
||||
|
||||
trfile = fopen(pathname, "a");
|
||||
}
|
||||
@ -92,8 +93,11 @@ Trace_Log(TrEvent event, Job *job)
|
||||
jobTokensRunning,
|
||||
evname[event], trpid, trwd);
|
||||
if (job != NULL) {
|
||||
fprintf(trfile, " %s %d %x %x", job->node->name,
|
||||
job->pid, job->flags, job->node->type);
|
||||
char flags[4];
|
||||
|
||||
Job_FlagsToString(job, flags, sizeof flags);
|
||||
fprintf(trfile, " %s %d %s %x", job->node->name,
|
||||
job->pid, flags, job->node->type);
|
||||
}
|
||||
fputc('\n', trfile);
|
||||
fflush(trfile);
|
||||
|
6
trace.h
6
trace.h
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: trace.h,v 1.4 2020/10/18 17:19:54 rillig Exp $ */
|
||||
/* $NetBSD: trace.h,v 1.5 2020/11/28 08:41:53 rillig Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
@ -34,6 +34,9 @@
|
||||
* Definitions pertaining to the tracing of jobs in parallel mode.
|
||||
*/
|
||||
|
||||
#ifndef MAKE_TRACE_H
|
||||
#define MAKE_TRACE_H
|
||||
|
||||
typedef enum TrEvent {
|
||||
MAKESTART,
|
||||
MAKEEND,
|
||||
@ -47,3 +50,4 @@ void Trace_Init(const char *);
|
||||
void Trace_Log(TrEvent, Job *);
|
||||
void Trace_End(void);
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
# $Id: Makefile,v 1.115 2020/11/18 04:01:07 sjg Exp $
|
||||
# $Id: Makefile,v 1.138 2021/01/01 22:55:09 sjg Exp $
|
||||
#
|
||||
# $NetBSD: Makefile,v 1.206 2020/11/18 01:12:00 sjg Exp $
|
||||
# $NetBSD: Makefile,v 1.260 2020/12/31 03:05:12 rillig Exp $
|
||||
#
|
||||
# Unit tests for make(1)
|
||||
#
|
||||
@ -30,18 +30,24 @@
|
||||
# src/tests/usr.bin/make/t_make.sh.
|
||||
#
|
||||
|
||||
# we use these below but we might be an older make
|
||||
.MAKE.OS?= ${uname -s:L:sh}
|
||||
.MAKE.UID?= ${id -u:L:sh}
|
||||
|
||||
# Each test is in a sub-makefile.
|
||||
# Keep the list sorted.
|
||||
# Any test that is commented out must be ignored in
|
||||
# src/tests/usr.bin/make/t_make.sh as well.
|
||||
#TESTS+= archive
|
||||
TESTS+= archive-suffix
|
||||
#TESTS+= archive-suffix
|
||||
TESTS+= cmd-errors
|
||||
TESTS+= cmd-errors-jobs
|
||||
TESTS+= cmd-errors-lint
|
||||
TESTS+= cmd-interrupt
|
||||
TESTS+= cmdline
|
||||
TESTS+= cmdline-undefined
|
||||
TESTS+= comment
|
||||
TESTS+= compat-error
|
||||
TESTS+= cond-cmp-numeric
|
||||
TESTS+= cond-cmp-numeric-eq
|
||||
TESTS+= cond-cmp-numeric-ge
|
||||
@ -51,12 +57,14 @@ TESTS+= cond-cmp-numeric-lt
|
||||
TESTS+= cond-cmp-numeric-ne
|
||||
TESTS+= cond-cmp-string
|
||||
TESTS+= cond-cmp-unary
|
||||
TESTS+= cond-eof
|
||||
TESTS+= cond-func
|
||||
TESTS+= cond-func-commands
|
||||
TESTS+= cond-func-defined
|
||||
TESTS+= cond-func-empty
|
||||
TESTS+= cond-func-exists
|
||||
TESTS+= cond-func-make
|
||||
TESTS+= cond-func-make-main
|
||||
TESTS+= cond-func-target
|
||||
TESTS+= cond-late
|
||||
TESTS+= cond-op
|
||||
@ -107,9 +115,14 @@ TESTS+= depsrc-usebefore-double-colon
|
||||
TESTS+= depsrc-wait
|
||||
TESTS+= deptgt
|
||||
TESTS+= deptgt-begin
|
||||
TESTS+= deptgt-begin-fail
|
||||
TESTS+= deptgt-begin-fail-indirect
|
||||
TESTS+= deptgt-default
|
||||
TESTS+= deptgt-delete_on_error
|
||||
TESTS+= deptgt-end
|
||||
TESTS+= deptgt-end-fail
|
||||
TESTS+= deptgt-end-fail-all
|
||||
TESTS+= deptgt-end-fail-indirect
|
||||
TESTS+= deptgt-end-jobs
|
||||
TESTS+= deptgt-error
|
||||
TESTS+= deptgt-ignore
|
||||
@ -139,14 +152,20 @@ TESTS+= directive-elifmake
|
||||
TESTS+= directive-elifndef
|
||||
TESTS+= directive-elifnmake
|
||||
TESTS+= directive-else
|
||||
TESTS+= directive-endfor
|
||||
TESTS+= directive-endif
|
||||
TESTS+= directive-error
|
||||
TESTS+= directive-export
|
||||
TESTS+= directive-export-env
|
||||
TESTS+= directive-export-impl
|
||||
TESTS+= directive-export-gmake
|
||||
TESTS+= directive-export-literal
|
||||
TESTS+= directive-for
|
||||
TESTS+= directive-for-errors
|
||||
TESTS+= directive-for-escape
|
||||
TESTS+= directive-for-generating-endif
|
||||
TESTS+= directive-for-lines
|
||||
TESTS+= directive-for-null
|
||||
TESTS+= directive-hyphen-include
|
||||
TESTS+= directive-if
|
||||
TESTS+= directive-if-nested
|
||||
@ -157,6 +176,7 @@ TESTS+= directive-ifnmake
|
||||
TESTS+= directive-include
|
||||
TESTS+= directive-include-fatal
|
||||
TESTS+= directive-info
|
||||
TESTS+= directive-misspellings
|
||||
TESTS+= directive-sinclude
|
||||
TESTS+= directive-undef
|
||||
TESTS+= directive-unexport
|
||||
@ -180,14 +200,20 @@ TESTS+= impsrc
|
||||
TESTS+= include-main
|
||||
TESTS+= job-flags
|
||||
#TESTS+= job-output-long-lines
|
||||
TESTS+= jobs-error-indirect
|
||||
TESTS+= jobs-error-nested
|
||||
TESTS+= jobs-error-nested-make
|
||||
TESTS+= lint
|
||||
TESTS+= make-exported
|
||||
TESTS+= meta-cmd-cmp
|
||||
TESTS+= moderrs
|
||||
TESTS+= modmatch
|
||||
TESTS+= modmisc
|
||||
TESTS+= modts
|
||||
TESTS+= modword
|
||||
.if ${.MAKE.UID} > 0
|
||||
TESTS+= objdir-writable
|
||||
.endif
|
||||
TESTS+= opt
|
||||
TESTS+= opt-backwards
|
||||
TESTS+= opt-chdir
|
||||
@ -223,10 +249,13 @@ TESTS+= opt-ignore
|
||||
TESTS+= opt-include-dir
|
||||
TESTS+= opt-jobs
|
||||
TESTS+= opt-jobs-internal
|
||||
TESTS+= opt-jobs-no-action
|
||||
TESTS+= opt-keep-going
|
||||
TESTS+= opt-keep-going-multiple
|
||||
TESTS+= opt-m-include-dir
|
||||
TESTS+= opt-no-action
|
||||
TESTS+= opt-no-action-at-all
|
||||
TESTS+= opt-no-action-runflags
|
||||
TESTS+= opt-query
|
||||
TESTS+= opt-raw
|
||||
TESTS+= opt-silent
|
||||
@ -243,10 +272,11 @@ TESTS+= parse-var
|
||||
TESTS+= phony-end
|
||||
TESTS+= posix
|
||||
TESTS+= # posix1 # broken by reverting POSIX changes
|
||||
TESTS+= qequals
|
||||
TESTS+= recursive
|
||||
TESTS+= sh
|
||||
TESTS+= sh-dots
|
||||
TESTS+= sh-errctl
|
||||
TESTS+= sh-flags
|
||||
TESTS+= sh-jobs
|
||||
TESTS+= sh-jobs-error
|
||||
TESTS+= sh-leading-at
|
||||
@ -264,10 +294,14 @@ TESTS+= shell-sh
|
||||
TESTS+= suff-add-later
|
||||
TESTS+= suff-clear-regular
|
||||
TESTS+= suff-clear-single
|
||||
TESTS+= suff-incomplete
|
||||
TESTS+= suff-lookup
|
||||
TESTS+= suff-main
|
||||
TESTS+= suff-main-several
|
||||
TESTS+= suff-phony
|
||||
TESTS+= suff-rebuild
|
||||
TESTS+= suff-self
|
||||
TESTS+= suff-transform-debug
|
||||
TESTS+= suff-transform-endless
|
||||
TESTS+= suff-transform-expand
|
||||
TESTS+= suff-transform-select
|
||||
@ -304,6 +338,7 @@ TESTS+= varmod-gmtime
|
||||
TESTS+= varmod-hash
|
||||
TESTS+= varmod-head
|
||||
TESTS+= varmod-ifelse
|
||||
TESTS+= varmod-indirect
|
||||
TESTS+= varmod-l-name-to-value
|
||||
TESTS+= varmod-localtime
|
||||
TESTS+= varmod-loop
|
||||
@ -361,6 +396,7 @@ TESTS+= varname-dot-make-path_filemon
|
||||
TESTS+= varname-dot-make-pid
|
||||
TESTS+= varname-dot-make-ppid
|
||||
TESTS+= varname-dot-make-save_dollars
|
||||
TESTS+= varname-dot-makeflags
|
||||
TESTS+= varname-dot-makeoverrides
|
||||
TESTS+= varname-dot-newline
|
||||
TESTS+= varname-dot-objdir
|
||||
@ -425,18 +461,19 @@ ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
|
||||
FLAGS.cond-func-make= via-cmdline
|
||||
FLAGS.directive-ifmake= first second
|
||||
FLAGS.doterror= # none, especially not -k
|
||||
FLAGS.jobs-error-indirect= # none, especially not -k
|
||||
FLAGS.jobs-error-nested= # none, especially not -k
|
||||
FLAGS.jobs-error-nested-make= # none, especially not -k
|
||||
FLAGS.varname-empty= -dv '$${:U}=cmdline-u' '=cmdline-plain'
|
||||
|
||||
# Some tests need extra postprocessing.
|
||||
SED_CMDS.export= \
|
||||
-e '/^[^=_A-Za-z0-9]*=/d'
|
||||
# these all share the same requirement
|
||||
.for t in export-all export-env
|
||||
SED_CMDS.$t= ${SED_CMDS.export}
|
||||
.endfor
|
||||
SED_CMDS.directive-export-gmake= \
|
||||
${:D dash is a pain } \
|
||||
-e /non-zero/d
|
||||
SED_CMDS.dir= ${:D remove output from -DCLEANUP mode }
|
||||
SED_CMDS.dir+= -e '/^OpenDirs_Done:/d'
|
||||
SED_CMDS.dir+= -e '/^CachedDir /d'
|
||||
SED_CMDS.export= -e '/^[^=_A-Za-z0-9]*=/d'
|
||||
SED_CMDS.export-all= ${SED_CMDS.export}
|
||||
SED_CMDS.export-env= ${SED_CMDS.export}
|
||||
SED_CMDS.cmdline= -e 's,uid${.MAKE.UID}/,,'
|
||||
SED_CMDS.job-output-long-lines= \
|
||||
${:D Job separators on their own line are ok. } \
|
||||
-e '/^--- job-[ab] ---$$/d' \
|
||||
@ -449,25 +486,33 @@ SED_CMDS.job-output-long-lines= \
|
||||
-e '/^aa*--- job-b ---$$/d' \
|
||||
-e '/^bb*--- job-a ---$$/d'
|
||||
SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g'
|
||||
SED_CMDS.opt-debug-graph1= \
|
||||
-e 's,${.CURDIR},CURDIR,'
|
||||
SED_CMDS.opt-debug-graph1+= \
|
||||
-e '/Global Variables:/,/Suffixes:/d'
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
|
||||
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
|
||||
SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,Command: ${.SHELL:T},Command: <shell>,'
|
||||
# The "-q" may be there or not, see jobs.c, variable shells.
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: sh\) -q,\1,'
|
||||
SED_CMDS.var-op-shell+= -e 's,^${.SHELL:T}: ,,'
|
||||
SED_CMDS.var-op-shell+= -e '/command/{ s,^[1-9]: ,,;s,No such.*,not found,; }'
|
||||
SED_CMDS.vardebug= \
|
||||
${:D canonicalize .SHELL } \
|
||||
-e 's,${.SHELL},</path/to/shell>,'
|
||||
SED_CMDS.opt-debug-jobs+= -e 's,^\(.Command: <shell>\) -q,\1,'
|
||||
SED_CMDS.opt-jobs-no-action= ${STD_SED_CMDS.hide-from-output}
|
||||
SED_CMDS.opt-no-action-runflags= ${STD_SED_CMDS.hide-from-output}
|
||||
# For Compat_RunCommand, useShell == FALSE.
|
||||
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<not found: ...>,'
|
||||
# For Compat_RunCommand, useShell == TRUE.
|
||||
SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
|
||||
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
|
||||
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
|
||||
SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
|
||||
SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
|
||||
SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
|
||||
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
|
||||
SED_CMDS.var-op-shell+= \
|
||||
-e 's,^${.SHELL:T}: [ 0-9:]*,,' \
|
||||
-e 's,^${.SHELL:T}: ,,' \
|
||||
-e '/command/s,No such.*,not found,'
|
||||
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
|
||||
SED_CMDS.varmod-subst-regex+= \
|
||||
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
|
||||
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
|
||||
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
|
||||
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
|
||||
@ -475,8 +520,7 @@ SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
|
||||
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
|
||||
|
||||
# Some tests need an additional round of postprocessing.
|
||||
POSTPROC.deptgt-suffixes= \
|
||||
${TOOL_SED} -n -e '/^\#\*\*\* Suffixes/,/^\#\*/p'
|
||||
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
|
||||
POSTPROC.gnode-submake= awk '/Input graph/, /^$$/'
|
||||
POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p'
|
||||
|
||||
@ -487,6 +531,35 @@ unexport-env.rawout: export.mk
|
||||
|
||||
# End of the configuration section.
|
||||
|
||||
# Some standard sed commands, to be used in the SED_CMDS above.
|
||||
|
||||
# Omit details such as process IDs from the output of the -dg1 option.
|
||||
STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,<curdir>,'
|
||||
STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d'
|
||||
STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d'
|
||||
STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
|
||||
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1 <details omitted>,'
|
||||
STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1 <details omitted>,'
|
||||
STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1 <details omitted>,'
|
||||
STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1 <details omitted>,'
|
||||
|
||||
# Omit details such as process IDs from the output of the -dj option.
|
||||
STD_SED_CMDS.dj= \
|
||||
-e '/Process/d;/JobFinish:/d' \
|
||||
-e 's,^\(Job_TokenWithdraw\)([0-9]*),\1(<pid>),' \
|
||||
-e 's,^([0-9][0-9]*) \(withdrew token\),(<pid>) \1,' \
|
||||
-e 's, \(pid\) [0-9][0-9]*, \1 <pid>,' \
|
||||
-e 's,^\( Command:\) .*,\1 <shell>,'
|
||||
|
||||
# Reduce the noise for tests running with the -n option, since there is no
|
||||
# other way to suppress the echoing of the commands.
|
||||
STD_SED_CMDS.hide-from-output= \
|
||||
-e '/^echo hide-from-output/d' \
|
||||
-e 's,hide-from-output ,,' \
|
||||
-e 's,hide-from-output,,'
|
||||
|
||||
# End of the configuration helpers section.
|
||||
|
||||
.MAIN: all
|
||||
|
||||
.-include "Makefile.inc"
|
||||
@ -532,6 +605,11 @@ _MKMSG_TEST= :
|
||||
|
||||
MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
|
||||
|
||||
.if ${.MAKE.OS} == "NetBSD"
|
||||
LIMIT_RESOURCES?= ulimit -v 200000
|
||||
.endif
|
||||
LIMIT_RESOURCES?= :
|
||||
|
||||
# Each test is run in a sub-make, to keep the tests for interfering with
|
||||
# each other, and because they use different environment variables and
|
||||
# command line options.
|
||||
@ -539,6 +617,7 @@ MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
|
||||
.mk.rawout:
|
||||
@${_MKMSG_TEST:Uecho '# test '} ${.PREFIX}
|
||||
@set -eu; \
|
||||
${LIMIT_RESOURCES}; \
|
||||
cd ${.OBJDIR}; \
|
||||
env -i PATH="$$PATH" ${MAKE_TEST_ENV} ${ENV.${.PREFIX:T}} \
|
||||
${TEST_MAKE} \
|
||||
@ -561,6 +640,10 @@ _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
|
||||
# strip ${.CURDIR}/ from the output
|
||||
_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
|
||||
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
|
||||
# on AT&T derrived systems; false exits 255 not 1
|
||||
.if ${.MAKE.OS:N*BSD} != ""
|
||||
_SED_CMDS+= -e 's,\(Error code\) 255,\1 1,'
|
||||
.endif
|
||||
|
||||
.rawout.out:
|
||||
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
|
||||
@ -570,10 +653,18 @@ _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
|
||||
@echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp2
|
||||
@mv ${.TARGET}.tmp2 ${.TARGET}
|
||||
|
||||
.if empty(DIFF_FLAGS)
|
||||
DIFF_ECHO= echo
|
||||
.else
|
||||
DIFF_ECHO= :
|
||||
.endif
|
||||
|
||||
# Compare all output files
|
||||
test: ${OUTFILES} .PHONY
|
||||
@failed= ; \
|
||||
for test in ${TESTS}; do \
|
||||
cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out && continue || \
|
||||
${DIFF_ECHO} diff ${UNIT_TESTS}/$${test}.exp $${test}.out; \
|
||||
${TOOL_DIFF} ${DIFF_FLAGS} ${UNIT_TESTS}/$${test}.exp $${test}.out \
|
||||
|| failed="$${failed}$${failed:+ }$${test}" ; \
|
||||
done ; \
|
||||
|
9
unit-tests/cmd-errors-jobs.exp
Normal file
9
unit-tests/cmd-errors-jobs.exp
Normal file
@ -0,0 +1,9 @@
|
||||
: undefined eol
|
||||
make: Unclosed variable "UNCLOSED"
|
||||
: unclosed-variable
|
||||
make: Unclosed variable expression (expecting '}') for "UNCLOSED"
|
||||
: unclosed-modifier
|
||||
make: Unknown modifier 'Z'
|
||||
: unknown-modifier eol
|
||||
: end eol
|
||||
exit status 0
|
32
unit-tests/cmd-errors-jobs.mk
Normal file
32
unit-tests/cmd-errors-jobs.mk
Normal file
@ -0,0 +1,32 @@
|
||||
# $NetBSD: cmd-errors-jobs.mk,v 1.1 2020/12/27 05:11:40 rillig Exp $
|
||||
#
|
||||
# Demonstrate how errors in variable expansions affect whether the commands
|
||||
# are actually executed in jobs mode.
|
||||
|
||||
.MAKEFLAGS: -j1
|
||||
|
||||
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
|
||||
|
||||
# Undefined variables are not an error. They expand to empty strings.
|
||||
undefined:
|
||||
: $@ ${UNDEFINED} eol
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-variable:
|
||||
: $@ ${UNCLOSED
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unclosed-modifier:
|
||||
: $@ ${UNCLOSED:
|
||||
|
||||
# XXX: As of 2020-11-01, this command is executed even though it contains
|
||||
# parse errors.
|
||||
unknown-modifier:
|
||||
: $@ ${UNKNOWN:Z} eol
|
||||
|
||||
end:
|
||||
: $@ eol
|
||||
|
||||
# XXX: As of 2020-11-02, despite the parse errors, the exit status is 0.
|
@ -1,7 +1,7 @@
|
||||
# $NetBSD: cmd-errors.mk,v 1.3 2020/11/09 23:36:34 rillig Exp $
|
||||
# $NetBSD: cmd-errors.mk,v 1.4 2020/12/27 05:11:40 rillig Exp $
|
||||
#
|
||||
# Demonstrate how errors in variable expansions affect whether the commands
|
||||
# are actually executed.
|
||||
# are actually executed in compat mode.
|
||||
|
||||
all: undefined unclosed-variable unclosed-modifier unknown-modifier end
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Tests for command line parsing and related special variables.
|
||||
|
||||
TMPBASE?= /tmp
|
||||
TMPBASE?= /tmp/uid${.MAKE.UID}
|
||||
SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID
|
||||
SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID
|
||||
MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r
|
||||
|
15
unit-tests/compat-error.exp
Normal file
15
unit-tests/compat-error.exp
Normal file
@ -0,0 +1,15 @@
|
||||
: Making success1 out of nothing.
|
||||
: Making fail1 out of nothing.
|
||||
false 'fail1' '${.TARGET}' '$${.TARGET}'
|
||||
*** Error code 1 (continuing)
|
||||
: Making success2 out of nothing.
|
||||
: Making fail2 out of nothing.
|
||||
false 'fail2' '${.TARGET}' '$${.TARGET}'
|
||||
*** Error code 1 (continuing)
|
||||
: Making success3 out of nothing.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
.ERROR target: <fail1>
|
||||
.ERROR command: <>
|
||||
exit status 1
|
37
unit-tests/compat-error.mk
Normal file
37
unit-tests/compat-error.mk
Normal file
@ -0,0 +1,37 @@
|
||||
# $NetBSD: compat-error.mk,v 1.3 2020/12/13 19:33:53 rillig Exp $
|
||||
#
|
||||
# Test detailed error handling in compat mode.
|
||||
#
|
||||
# Until 2020-12-13, .ERROR_TARGET was success3, which was wrong.
|
||||
# Since compat.c 1.215 from 2020-12-13, it is 'fail1', which is the first
|
||||
# failed top-level target. XXX: Even better would be if .ERROR_TARGET were
|
||||
# the smallest target that caused the build to fail, even if it were a
|
||||
# sub-sub-sub-dependency of a top-level target.
|
||||
#
|
||||
# XXX: As of 2020-12-13, .ERROR_CMD is empty, which is wrong.
|
||||
#
|
||||
# See also:
|
||||
# Compat_Run
|
||||
#
|
||||
# The commit that added the NULL command to gn->commands:
|
||||
# CVS: 1994.06.06.22.45.??
|
||||
# Git: 26a8972fd7f982502c5fbfdabd34578b99d77ca5
|
||||
# 1994: Lst_Replace (cmdNode, (ClientData) NULL);
|
||||
# 2020: LstNode_SetNull(cmdNode);
|
||||
#
|
||||
# The commit that skipped NULL commands for .ERROR_CMD:
|
||||
# CVS: 2016.08.11.19.53.??
|
||||
# Git: 58b23478b7353d46457089e726b07a49197388e4
|
||||
|
||||
.MAKEFLAGS: success1 fail1 success2 fail2 success3
|
||||
|
||||
success1 success2 success3:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
|
||||
fail1 fail2:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
false '${.TARGET}' '$${.TARGET}' '$$$${.TARGET}'
|
||||
|
||||
.ERROR:
|
||||
@echo ${.TARGET} target: '<'${.ERROR_TARGET:Q}'>'
|
||||
@echo ${.TARGET} command: '<'${.ERROR_CMD:Q}'>'
|
9
unit-tests/cond-eof.exp
Normal file
9
unit-tests/cond-eof.exp
Normal file
@ -0,0 +1,9 @@
|
||||
side effect
|
||||
make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
|
||||
side effect
|
||||
make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
|
||||
side effect
|
||||
make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
20
unit-tests/cond-eof.mk
Normal file
20
unit-tests/cond-eof.mk
Normal file
@ -0,0 +1,20 @@
|
||||
# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $
|
||||
#
|
||||
# Tests for parsing conditions, especially the end of such conditions, which
|
||||
# are represented as the token TOK_EOF.
|
||||
|
||||
SIDE_EFFECT= ${:!echo 'side effect' 1>&2!}
|
||||
SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
|
||||
|
||||
# In the following conditions, ${SIDE_EFFECT} is the position of the first
|
||||
# parse error. It is always fully evaluated, even if it were not necessary
|
||||
# to expand the variable expression. This is because these syntax errors are
|
||||
# an edge case that does not occur during normal operation, therefore there
|
||||
# is no need to optimize for this case, and it would slow down the common
|
||||
# case as well.
|
||||
.if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2}
|
||||
.endif
|
||||
.if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2}
|
||||
.endif
|
||||
.if (0) ${SIDE_EFFECT} ${SIDE_EFFECT2}
|
||||
.endif
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-empty.mk,v 1.10 2020/11/15 14:07:53 rillig Exp $
|
||||
# $NetBSD: cond-func-empty.mk,v 1.11 2020/11/28 14:08:37 rillig Exp $
|
||||
#
|
||||
# Tests for the empty() function in .if conditions, which tests a variable
|
||||
# expression for emptiness.
|
||||
@ -155,5 +155,30 @@ ${:U WORD }= variable name with spaces
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated
|
||||
# a wrong error message "Variable VARNAME is recursive".
|
||||
#
|
||||
# The bug was that the !empty() condition was evaluated, even though this was
|
||||
# not necessary since the defined() condition already evaluated to false.
|
||||
#
|
||||
# When evaluating the !empty condition, the variable name was parsed as
|
||||
# "VARNAME${:U2}", but without expanding any nested variable expression, in
|
||||
# this case the ${:U2}. Therefore, the variable name came out as simply
|
||||
# "VARNAME". Since this variable name should have been discarded quickly after
|
||||
# parsing it, this unrealistic variable name should have done no harm.
|
||||
#
|
||||
# The variable expression was expanded though, and this was wrong. The
|
||||
# expansion was done without the VARE_WANTRES flag (called VARF_WANTRES back
|
||||
# then) though. This had the effect that the ${:U1} from the value of VARNAME
|
||||
# expanded to an empty string. This in turn created the seemingly recursive
|
||||
# definition VARNAME=${VARNAME}, and that definition was never meant to be
|
||||
# expanded.
|
||||
#
|
||||
# This was fixed by expanding nested variable expressions in the variable name
|
||||
# only if the flag VARE_WANTRES is given.
|
||||
VARNAME= ${VARNAME${:U1}}
|
||||
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
|
||||
.endif
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-func-exists.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
|
||||
# $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
|
||||
#
|
||||
# Tests for the exists() function in .if conditions.
|
||||
|
||||
@ -38,5 +38,14 @@
|
||||
. error
|
||||
.endif
|
||||
|
||||
# The exists function does not really look up the file in the file system,
|
||||
# instead it uses a cache that is preloaded very early, before parsing the
|
||||
# first makefile. At that time, the file did not exist yet.
|
||||
_!= > cond-func-exists.just-created
|
||||
.if exists(cond-func-exists.just-created)
|
||||
. error
|
||||
.endif
|
||||
_!= rm cond-func-exists.just-created
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
3
unit-tests/cond-func-make-main.exp
Normal file
3
unit-tests/cond-func-make-main.exp
Normal file
@ -0,0 +1,3 @@
|
||||
: Making dot-main-target-1a.
|
||||
: Making dot-main-target-1b.
|
||||
exit status 0
|
62
unit-tests/cond-func-make-main.mk
Normal file
62
unit-tests/cond-func-make-main.mk
Normal file
@ -0,0 +1,62 @@
|
||||
# $NetBSD: cond-func-make-main.mk,v 1.1 2020/11/22 19:37:27 rillig Exp $
|
||||
#
|
||||
# Test how accurately the make() function in .if conditions reflects
|
||||
# what is actually made.
|
||||
#
|
||||
# There are several ways to specify what is being made:
|
||||
#
|
||||
# 1. The default main target is the first target in the given makefiles that
|
||||
# is not one of the special targets. For example, .PHONY is special when
|
||||
# it appears on the left-hand side of the ':'. It is not special on the
|
||||
# right-hand side though.
|
||||
#
|
||||
# 2. Command line arguments that are neither options (-ds or -k) nor variable
|
||||
# assignments (VAR=value) are interpreted as targets to be made. These
|
||||
# override the default main target from above.
|
||||
#
|
||||
# 3. All sources of the first '.MAIN: sources' line. Any further .MAIN line
|
||||
# is treated as if .MAIN were a regular name.
|
||||
#
|
||||
# This test only covers items 1 and 3. For item 2, see cond-func-make.mk.
|
||||
|
||||
first-main-target:
|
||||
: Making ${.TARGET}.
|
||||
|
||||
# Even though the main-target would actually be made at this point, it is
|
||||
# ignored by the make() function.
|
||||
.if make(first-main-target)
|
||||
. error
|
||||
.endif
|
||||
|
||||
# Declaring a target via the .MAIN dependency adds it to the targets to be
|
||||
# created (opts.create), but only that list was empty at the beginning of
|
||||
# the line. This implies that several main targets can be set at the name
|
||||
# time, but they have to be in the same dependency group.
|
||||
#
|
||||
# See ParseDoDependencyTargetSpecial, branch SP_MAIN.
|
||||
.MAIN: dot-main-target-1a dot-main-target-1b
|
||||
|
||||
.if !make(dot-main-target-1a)
|
||||
. error
|
||||
.endif
|
||||
.if !make(dot-main-target-1b)
|
||||
. error
|
||||
.endif
|
||||
|
||||
dot-main-target-{1,2}{a,b}:
|
||||
: Making ${.TARGET}.
|
||||
|
||||
# At this point, the list of targets to be made (opts.create) is not empty
|
||||
# anymore. ParseDoDependencyTargetSpecial therefore treats the .MAIN as if
|
||||
# it were an ordinary target. Since .MAIN is not listed as a dependency
|
||||
# anywhere, it is not made.
|
||||
.if target(.MAIN)
|
||||
. error
|
||||
.endif
|
||||
.MAIN: dot-main-target-2a dot-main-target-2b
|
||||
.if !target(.MAIN)
|
||||
. error
|
||||
.endif
|
||||
.if make(dot-main-target-2a)
|
||||
. error
|
||||
.endif
|
@ -7,10 +7,10 @@ expected M pattern
|
||||
expected or
|
||||
expected or exists
|
||||
expected or empty
|
||||
defined(V42) && 42 > 0: Ok
|
||||
defined(V66) && ( "" < 42 ): Ok
|
||||
1 || 42 < 42: Ok
|
||||
1 || < 42: Ok
|
||||
0 || 42 <= 42: Ok
|
||||
0 || < 42: Ok
|
||||
defined(V42) && ${V42} > 0: Ok
|
||||
defined(V66) && ( "${iV2}" < ${V42} ): Ok
|
||||
1 || ${iV1} < ${V42}: Ok
|
||||
1 || ${iV2:U2} < ${V42}: Ok
|
||||
0 || ${iV1} <= ${V42}: Ok
|
||||
0 || ${iV2:U2} < ${V42}: Ok
|
||||
exit status 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: cond-short.mk,v 1.12 2020/11/15 14:58:14 rillig Exp $
|
||||
# $NetBSD: cond-short.mk,v 1.15 2020/12/01 19:37:23 rillig Exp $
|
||||
#
|
||||
# Demonstrates that in conditions, the right-hand side of an && or ||
|
||||
# is only evaluated if it can actually influence the result.
|
||||
@ -6,9 +6,13 @@
|
||||
# mode in most programming languages. A notable exception is Ada, which
|
||||
# distinguishes between the operators 'And', 'And Then', 'Or', 'Or Else'.
|
||||
#
|
||||
# Between 2015-10-11 and 2020-06-28, the right-hand side of an && or ||
|
||||
# operator was always evaluated, which was wrong.
|
||||
# TODO: Had the evaluation been correct at some time before 2015-11-12?
|
||||
# Before 2020-06-28, the right-hand side of an && or || operator was always
|
||||
# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
|
||||
# Var_Parse got a new parameter named 'wantit'. Since then it would have been
|
||||
# possible to skip evaluation of irrelevant variable expressions and only
|
||||
# parse them. They were still evaluated though, the only difference to
|
||||
# relevant variable expressions was that in the irrelevant variable
|
||||
# expressions, undefined variables were allowed.
|
||||
|
||||
# The && operator.
|
||||
|
||||
@ -128,33 +132,56 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo 'defined(V42) && ${V42} > 0: $x' >&2; echo
|
||||
x!= echo 'defined(V42) && $${V42} > 0: $x' >&2; echo
|
||||
|
||||
# this one throws both String comparison operator and
|
||||
# Malformed conditional with cond.c 1.78
|
||||
# indirect iV2 would expand to "" and treated as 0
|
||||
# With cond.c 1.76 from 2020-07-03, the following condition triggered a
|
||||
# warning: "String comparison operator should be either == or !=".
|
||||
# This was because the variable expression ${iV2} was defined, but the
|
||||
# contained variable V66 was undefined. The left-hand side of the comparison
|
||||
# therefore evaluated to the string "${V66}", which is obviously not a number.
|
||||
#
|
||||
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
|
||||
# comparisons. Instead, they are only parsed and then discarded.
|
||||
#
|
||||
# At that time, there was not enough debug logging to see the details in the
|
||||
# -dA log. To actually see it, add debug logging at the beginning and end of
|
||||
# Var_Parse.
|
||||
.if defined(V66) && ( ${iV2} < ${V42} )
|
||||
x= Fail
|
||||
.else
|
||||
x= Ok
|
||||
.endif
|
||||
x!= echo 'defined(V66) && ( "${iV2}" < ${V42} ): $x' >&2; echo
|
||||
# XXX: This condition doesn't match the one above. The quotes are missing
|
||||
# above. This is a crucial detail since without quotes, the variable
|
||||
# expression ${iV2} evaluates to "${V66}", and with quotes, it evaluates to ""
|
||||
# since undefined variables are allowed and expand to an empty string.
|
||||
x!= echo 'defined(V66) && ( "$${iV2}" < $${V42} ): $x' >&2; echo
|
||||
|
||||
# next two thow String comparison operator with cond.c 1.78
|
||||
# indirect iV1 would expand to 42
|
||||
.if 1 || ${iV1} < ${V42}
|
||||
x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '1 || ${iV1} < ${V42}: $x' >&2; echo
|
||||
x!= echo '1 || $${iV1} < $${V42}: $x' >&2; echo
|
||||
|
||||
# With cond.c 1.76 from 2020-07-03, the following condition triggered a
|
||||
# warning: "String comparison operator should be either == or !=".
|
||||
# This was because the variable expression ${iV2} was defined, but the
|
||||
# contained variable V66 was undefined. The left-hand side of the comparison
|
||||
# therefore evaluated to the string "${V66}", which is obviously not a number.
|
||||
#
|
||||
# This was fixed in cond.c 1.79 from 2020-07-09 by not evaluating irrelevant
|
||||
# comparisons. Instead, they are only parsed and then discarded.
|
||||
#
|
||||
# At that time, there was not enough debug logging to see the details in the
|
||||
# -dA log. To actually see it, add debug logging at the beginning and end of
|
||||
# Var_Parse.
|
||||
.if 1 || ${iV2:U2} < ${V42}
|
||||
x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '1 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
x!= echo '1 || $${iV2:U2} < $${V42}: $x' >&2; echo
|
||||
|
||||
# the same expressions are fine when the lhs is expanded
|
||||
# ${iV1} expands to 42
|
||||
@ -163,7 +190,7 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV1} <= ${V42}: $x' >&2; echo
|
||||
x!= echo '0 || $${iV1} <= $${V42}: $x' >&2; echo
|
||||
|
||||
# ${iV2:U2} expands to 2
|
||||
.if 0 || ${iV2:U2} < ${V42}
|
||||
@ -171,11 +198,12 @@ x= Ok
|
||||
.else
|
||||
x= Fail
|
||||
.endif
|
||||
x!= echo '0 || ${iV2:U2} < ${V42}: $x' >&2; echo
|
||||
x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
|
||||
|
||||
# TODO: Has this always worked? There may have been a time, maybe around
|
||||
# 2000, when make would complain about the "Malformed conditional" because
|
||||
# UNDEF is not defined.
|
||||
# The right-hand side of the '&&' is irrelevant since the left-hand side
|
||||
# already evaluates to false. Before cond.c 1.79 from 2020-07-09, it was
|
||||
# expanded nevertheless, although with a small modification: undefined
|
||||
# variables may be used in these expressions without generating an error.
|
||||
.if defined(UNDEF) && ${UNDEF} != "undefined"
|
||||
. error
|
||||
.endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
make: Unknown modifier 'Z'
|
||||
make: "cond-token-string.mk" line 9: Unknown modifier 'Z'
|
||||
make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}")
|
||||
make: "cond-token-string.mk" line 18: xvalue is not defined.
|
||||
make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "")
|
||||
|
@ -1,3 +1,6 @@
|
||||
make: don't know how to make dep-percent.o (continuing)
|
||||
`all' not remade because of errors.
|
||||
exit status 0
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1 +1,5 @@
|
||||
Skipping meta for actual-test: no commands
|
||||
Skipping meta for .END: .SPECIAL
|
||||
Targets from meta mode:
|
||||
| TARGET depsrc-meta-target
|
||||
exit status 0
|
||||
|
@ -1,8 +1,31 @@
|
||||
# $NetBSD: depsrc-meta.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
|
||||
# $NetBSD: depsrc-meta.mk,v 1.4 2020/11/27 08:39:07 rillig Exp $
|
||||
#
|
||||
# Tests for the special source .META in dependency declarations.
|
||||
|
||||
# TODO: Implementation
|
||||
# TODO: Explanation
|
||||
|
||||
.if make(actual-test)
|
||||
|
||||
.MAKEFLAGS: -dM
|
||||
.MAKE.MODE= meta curDirOk=true
|
||||
|
||||
actual-test: depsrc-meta-target
|
||||
depsrc-meta-target: .META
|
||||
@> ${.TARGET}-file
|
||||
@rm -f ${.TARGET}-file
|
||||
|
||||
.elif make(check-results)
|
||||
|
||||
check-results:
|
||||
@echo 'Targets from meta mode:'
|
||||
@awk '/^TARGET/ { print "| " $$0 }' depsrc-meta-target.meta
|
||||
@rm depsrc-meta-target.meta
|
||||
|
||||
.else
|
||||
|
||||
all:
|
||||
@:;
|
||||
@${MAKE} -f ${MAKEFILE} actual-test
|
||||
@${MAKE} -f ${MAKEFILE} check-results
|
||||
|
||||
.endif
|
||||
|
@ -5,16 +5,16 @@ ExamineLater: need to examine "optional"
|
||||
ExamineLater: need to examine "optional-cohort"
|
||||
Make_ExpandUse: examine optional
|
||||
Make_ExpandUse: examine optional-cohort
|
||||
Examining optional...non-existent...up-to-date.
|
||||
Examining optional-cohort...non-existent...:: operator and no sources...out-of-date.
|
||||
Examining optional...nonexistent...up-to-date.
|
||||
Examining optional-cohort...nonexistent...:: operator and no sources...out-of-date.
|
||||
: A leaf node using '::' is considered out-of-date.
|
||||
recheck(optional-cohort): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining important...non-existent...modified before source "optional-cohort"...out-of-date.
|
||||
recheck(optional-cohort): update time from nonexistent to now
|
||||
Examining important...nonexistent...modified before source "optional-cohort"...out-of-date.
|
||||
: important is made.
|
||||
recheck(important): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining all...non-existent...modified before source "important"...out-of-date.
|
||||
recheck(important): update time from nonexistent to now
|
||||
Examining all...nonexistent...modified before source "important"...out-of-date.
|
||||
: all is made.
|
||||
recheck(all): update time from 0:00:00 Jan 01, 1970 to now
|
||||
Examining .END...non-existent...non-existent and no sources...out-of-date.
|
||||
recheck(.END): update time from 0:00:00 Jan 01, 1970 to now
|
||||
recheck(all): update time from nonexistent to now
|
||||
Examining .END...nonexistent...nonexistent and no sources...out-of-date.
|
||||
recheck(.END): update time from nonexistent to now
|
||||
exit status 0
|
||||
|
@ -1 +1,4 @@
|
||||
: 'Undefined variables are expanded directly in the dependency'
|
||||
: 'declaration. They are not preserved and maybe expanded later.'
|
||||
: 'This is in contrast to local variables such as ${.TARGET}.'
|
||||
exit status 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: depsrc.mk,v 1.3 2020/11/15 20:20:58 rillig Exp $
|
||||
# $NetBSD: depsrc.mk,v 1.4 2020/12/22 19:38:44 rillig Exp $
|
||||
#
|
||||
# Tests for special sources (those starting with a dot, followed by
|
||||
# uppercase letters) in dependency declarations, such as .PHONY.
|
||||
@ -7,5 +7,20 @@
|
||||
|
||||
# TODO: Test 'target: ${:U.SILENT}'
|
||||
|
||||
# Demonstrate when exactly undefined variables are expanded in a dependency
|
||||
# declaration.
|
||||
target: .PHONY source-${DEFINED_LATER}
|
||||
#
|
||||
DEFINED_LATER= later
|
||||
#
|
||||
source-: .PHONY
|
||||
: 'Undefined variables are expanded directly in the dependency'
|
||||
: 'declaration. They are not preserved and maybe expanded later.'
|
||||
: 'This is in contrast to local variables such as $${.TARGET}.'
|
||||
source-later: .PHONY
|
||||
: 'Undefined variables are tried to be expanded in a dependency'
|
||||
: 'declaration. If that fails because the variable is undefined,'
|
||||
: 'the expression is preserved and tried to be expanded later.'
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
6
unit-tests/deptgt-begin-fail-indirect.exp
Normal file
6
unit-tests/deptgt-begin-fail-indirect.exp
Normal file
@ -0,0 +1,6 @@
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
16
unit-tests/deptgt-begin-fail-indirect.mk
Normal file
16
unit-tests/deptgt-begin-fail-indirect.mk
Normal file
@ -0,0 +1,16 @@
|
||||
# $NetBSD: deptgt-begin-fail-indirect.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
|
||||
#
|
||||
# Test for a .BEGIN target whose dependency results in an error.
|
||||
# This stops make immediately and does not build the main targets.
|
||||
#
|
||||
# Between 2005-05-08 and 2020-11-24, a failing dependency of the .BEGIN node
|
||||
# would not stop make from running the main targets. In the end, the exit
|
||||
# status was even 0.
|
||||
|
||||
.BEGIN: failing
|
||||
|
||||
failing: .PHONY .NOTMAIN
|
||||
false
|
||||
|
||||
all:
|
||||
: This is not made.
|
6
unit-tests/deptgt-begin-fail.exp
Normal file
6
unit-tests/deptgt-begin-fail.exp
Normal file
@ -0,0 +1,6 @@
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
10
unit-tests/deptgt-begin-fail.mk
Normal file
10
unit-tests/deptgt-begin-fail.mk
Normal file
@ -0,0 +1,10 @@
|
||||
# $NetBSD: deptgt-begin-fail.mk,v 1.1 2020/11/24 19:02:59 rillig Exp $
|
||||
#
|
||||
# Test for a .BEGIN target whose command results in an error.
|
||||
# This stops make immediately and does not build the main targets.
|
||||
|
||||
.BEGIN:
|
||||
false
|
||||
|
||||
all:
|
||||
: This is not made.
|
7
unit-tests/deptgt-end-fail-all.exp
Normal file
7
unit-tests/deptgt-end-fail-all.exp
Normal file
@ -0,0 +1,7 @@
|
||||
: Making all out of nothing.
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
19
unit-tests/deptgt-end-fail-all.mk
Normal file
19
unit-tests/deptgt-end-fail-all.mk
Normal file
@ -0,0 +1,19 @@
|
||||
# $NetBSD: deptgt-end-fail-all.mk,v 1.2 2020/12/07 01:04:07 rillig Exp $
|
||||
#
|
||||
# Test whether the commands from the .END target are run even if there is
|
||||
# an error before. The manual page says "after everything else is done",
|
||||
# which leaves room for interpretation.
|
||||
#
|
||||
# Until 2020-12-07, the .END node was made even if the main nodes had failed.
|
||||
# This was not intended since the .END node had already been skipped if a
|
||||
# dependency of the main nodes had failed, just not if one of the main nodes
|
||||
# themselves had failed. This inconsistency was not worth keeping. To run
|
||||
# some commands on error, use the .ERROR target instead, see deptgt-error.mk.
|
||||
|
||||
all: .PHONY
|
||||
: Making ${.TARGET} out of nothing.
|
||||
false
|
||||
|
||||
.END:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
false
|
7
unit-tests/deptgt-end-fail-indirect.exp
Normal file
7
unit-tests/deptgt-end-fail-indirect.exp
Normal file
@ -0,0 +1,7 @@
|
||||
: all
|
||||
false
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
16
unit-tests/deptgt-end-fail-indirect.mk
Normal file
16
unit-tests/deptgt-end-fail-indirect.mk
Normal file
@ -0,0 +1,16 @@
|
||||
# $NetBSD: deptgt-end-fail-indirect.mk,v 1.2 2020/12/06 21:22:04 rillig Exp $
|
||||
#
|
||||
# Tests for an error in a dependency of the .END node.
|
||||
#
|
||||
# Before 2020-11-25, an error in the .END target did not print the "Stop."
|
||||
# and exited with status 0. The cause for this was a missing condition in
|
||||
# Compat_Run in the handling of the .END node.
|
||||
|
||||
all:
|
||||
: $@
|
||||
|
||||
.END: failing
|
||||
: Making ${.TARGET} from ${.ALLSRC}.
|
||||
|
||||
failing: .PHONY
|
||||
false
|
163
unit-tests/deptgt-end-fail.exp
Normal file
163
unit-tests/deptgt-end-fail.exp
Normal file
@ -0,0 +1,163 @@
|
||||
Test case all=ok all-dep=ok end=ok end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
: Making end-dep out of nothing.
|
||||
: Making .END from end-dep.
|
||||
exit status 0
|
||||
|
||||
|
||||
Test case all=ok all-dep=ok end=ok end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
: Making end-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ok end=ERR end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
: Making end-dep out of nothing.
|
||||
: Making .END from end-dep.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ok end=ERR end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
: Making end-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ERR end=ok end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ERR end=ok end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ERR end=ERR end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ok all-dep=ERR end=ERR end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ok end=ok end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ok end=ok end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ok end=ERR end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ok end=ERR end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
: Making all from all-dep.
|
||||
*** Error code 1 (continuing)
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ERR end=ok end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ERR end=ok end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ERR end=ERR end-dep=ok.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
Test case all=ERR all-dep=ERR end=ERR end-dep=ERR.
|
||||
: Making all-dep out of nothing.
|
||||
*** Error code 1 (continuing)
|
||||
`all' not remade because of errors.
|
||||
|
||||
Stop.
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
||||
|
||||
exit status 0
|
69
unit-tests/deptgt-end-fail.mk
Normal file
69
unit-tests/deptgt-end-fail.mk
Normal file
@ -0,0 +1,69 @@
|
||||
# $NetBSD: deptgt-end-fail.mk,v 1.6 2020/12/07 01:04:07 rillig Exp $
|
||||
#
|
||||
# Tests for an errors in the main target, its dependencies,
|
||||
# the .END node and its dependencies.
|
||||
#
|
||||
# Before 2020-11-25, an error in the .END target did not print the "Stop.",
|
||||
# even though this was intended. The cause for this was a missing condition
|
||||
# in Compat_Run, in the code handling the .END node.
|
||||
|
||||
test: .PHONY
|
||||
|
||||
# The default stop-on-error mode is not as interesting to test since it
|
||||
# stops right after the first error.
|
||||
.MAKEFLAGS: -k
|
||||
|
||||
.for all in ok ERR
|
||||
. for all-dep in ok ERR
|
||||
. for end in ok ERR
|
||||
. for end-dep in ok ERR
|
||||
. for target in ${all}-${all-dep}-${end}-${end-dep}
|
||||
test: ${target}
|
||||
${target}: .PHONY .SILENT
|
||||
echo Test case all=${all} all-dep=${all-dep} end=${end} end-dep=${end-dep}.
|
||||
${MAKE} -r -f ${MAKEFILE} \
|
||||
all=${all} all-dep=${all-dep} \
|
||||
end=${end} end-dep=${end-dep} \
|
||||
all; \
|
||||
echo "exit status $$?"
|
||||
echo
|
||||
echo
|
||||
. endfor
|
||||
. endfor
|
||||
. endfor
|
||||
. endfor
|
||||
.endfor
|
||||
|
||||
.if make(all)
|
||||
|
||||
all all-dep end-dep: .PHONY
|
||||
|
||||
CMD.ok= true
|
||||
CMD.ERR= false
|
||||
|
||||
all: all-dep
|
||||
: Making ${.TARGET} from ${.ALLSRC}.
|
||||
@${CMD.${all}}
|
||||
|
||||
all-dep:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
@${CMD.${all-dep}}
|
||||
|
||||
.END: end-dep
|
||||
: Making ${.TARGET} from ${.ALLSRC}.
|
||||
@${CMD.${end}}
|
||||
|
||||
end-dep:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
@${CMD.${end-dep}}
|
||||
|
||||
.endif
|
||||
|
||||
# Until 2020-12-07, several of the test cases printed "`all' not remade
|
||||
# because of errors.", followed by "exit status 0", which contradicted
|
||||
# each other.
|
||||
|
||||
# Until 2020-12-07, '.END' was even made if 'all' failed, but if a dependency
|
||||
# of 'all' failed, it was skipped. This inconsistency was not needed for
|
||||
# anything and thus has been dropped. To run some commands on error, use the
|
||||
# .ERROR target instead, see deptgt-error.mk.
|
@ -3,5 +3,31 @@
|
||||
# To:
|
||||
# From:
|
||||
# Search Path: . ..
|
||||
# ".src-left" (num 2, ref 2)
|
||||
# To: .tgt-right
|
||||
# From:
|
||||
# Search Path:
|
||||
# ".tgt-right" (num 3, ref 2)
|
||||
# To:
|
||||
# From: .src-left
|
||||
# Search Path:
|
||||
# ".tgt-left" (num 4, ref 2)
|
||||
# To:
|
||||
# From: .src-right
|
||||
# Search Path:
|
||||
# ".src-right" (num 5, ref 2)
|
||||
# To: .tgt-left
|
||||
# From:
|
||||
# Search Path:
|
||||
#*** Transformations:
|
||||
.src-left.tgt-right:
|
||||
: Making ${.TARGET} from ${.IMPSRC}.
|
||||
|
||||
.src-right.tgt-left:
|
||||
: Making ${.TARGET} from ${.IMPSRC}.
|
||||
|
||||
: Making deptgt-suffixes.src-left out of nothing.
|
||||
: Making deptgt-suffixes.tgt-right from deptgt-suffixes.src-left.
|
||||
: Making deptgt-suffixes.src-right out of nothing.
|
||||
: Making deptgt-suffixes.tgt-left from deptgt-suffixes.src-right.
|
||||
exit status 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: deptgt-suffixes.mk,v 1.3 2020/08/28 04:05:35 rillig Exp $
|
||||
# $NetBSD: deptgt-suffixes.mk,v 1.4 2020/11/21 21:54:42 rillig Exp $
|
||||
#
|
||||
# Tests for the special target .SUFFIXES in dependency declarations.
|
||||
#
|
||||
@ -8,11 +8,28 @@
|
||||
|
||||
.MAKEFLAGS: -dg1
|
||||
|
||||
.MAIN: all
|
||||
|
||||
.SUFFIXES: .custom-null
|
||||
|
||||
# TODO: What is the effect of this? How is it useful?
|
||||
.NULL: .custom-null
|
||||
.PATH.custom-null: . ..
|
||||
|
||||
all:
|
||||
@:;
|
||||
# The order in which the suffixes are listed doesn't matter.
|
||||
# Here, they are listed from source to target, just like in the transformation
|
||||
# rule below it.
|
||||
.SUFFIXES: .src-left .tgt-right
|
||||
deptgt-suffixes.src-left:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
.src-left.tgt-right:
|
||||
: Making ${.TARGET} from ${.IMPSRC}.
|
||||
all: deptgt-suffixes.tgt-right
|
||||
|
||||
# Here, the target is listed earlier than the source.
|
||||
.SUFFIXES: .tgt-left .src-right
|
||||
deptgt-suffixes.src-right:
|
||||
: Making ${.TARGET} out of nothing.
|
||||
.src-right.tgt-left:
|
||||
: Making ${.TARGET} from ${.IMPSRC}.
|
||||
all: deptgt-suffixes.tgt-left
|
||||
|
@ -8,6 +8,7 @@ ParseDoDependency(: empty-source)
|
||||
ParseReadLine (37): ' : command for empty targets list'
|
||||
ParseReadLine (38): '.MAKEFLAGS: -d0'
|
||||
ParseDoDependency(.MAKEFLAGS: -d0)
|
||||
make: "deptgt.mk" line 46: Unknown modifier 'Z'
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
# $NetBSD: deptgt.mk,v 1.9 2020/11/15 11:57:00 rillig Exp $
|
||||
# $NetBSD: deptgt.mk,v 1.10 2020/12/27 18:20:26 rillig Exp $
|
||||
#
|
||||
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
|
||||
# declarations.
|
||||
@ -37,5 +37,13 @@ ${:U}: empty-source
|
||||
: command for empty targets list
|
||||
.MAKEFLAGS: -d0
|
||||
|
||||
# Just to show that a malformed expression is only expanded once in
|
||||
# ParseDependencyTargetWord. The only way to produce an expression that
|
||||
# is well-formed on the first expansion and ill-formed on the second
|
||||
# expansion would be to use the variable modifier '::=' to modify the
|
||||
# targets. This in turn would be such an extreme and unreliable edge case
|
||||
# that nobody uses it.
|
||||
$$$$$$$${:U:Z}:
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
@ -1,17 +1,21 @@
|
||||
make: "directive-elif.mk" line 7: begin .elif misspellings tests, part 1
|
||||
make: "directive-elif.mk" line 9: 1-then
|
||||
make: "directive-elif.mk" line 18: begin .elif misspellings tests, part 2
|
||||
make: "directive-elif.mk" line 29: begin .elif misspellings tests, part 3
|
||||
make: "directive-elif.mk" line 41: which branch is taken on misspelling after false?
|
||||
make: "directive-elif.mk" line 49: else
|
||||
make: "directive-elif.mk" line 52: which branch is taken on misspelling after true?
|
||||
make: "directive-elif.mk" line 54: 1-then
|
||||
make: "directive-elif.mk" line 55: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 56: 1-elsif
|
||||
make: "directive-elif.mk" line 57: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 58: 2-elsif
|
||||
make: "directive-elif.mk" line 64: if-less elif
|
||||
make: "directive-elif.mk" line 69: warning: extra elif
|
||||
make: "directive-elif.mk" line 47: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 52: This branch is taken.
|
||||
make: "directive-elif.mk" line 60: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 63: This branch is taken.
|
||||
make: "directive-elif.mk" line 69: This branch is taken.
|
||||
make: "directive-elif.mk" line 89: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 90: This misspelling is detected.
|
||||
make: "directive-elif.mk" line 91: This branch is taken because of the .else.
|
||||
make: "directive-elif.mk" line 109: What happens on misspelling in a skipped branch?
|
||||
make: "directive-elif.mk" line 119: else
|
||||
make: "directive-elif.mk" line 122: What happens on misspelling in a taken branch?
|
||||
make: "directive-elif.mk" line 124: 1-then
|
||||
make: "directive-elif.mk" line 125: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 126: 1-elsif
|
||||
make: "directive-elif.mk" line 127: Unknown directive "elsif"
|
||||
make: "directive-elif.mk" line 128: 2-elsif
|
||||
make: "directive-elif.mk" line 134: if-less elif
|
||||
make: "directive-elif.mk" line 139: warning: extra elif
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,55 +1,125 @@
|
||||
# $NetBSD: directive-elif.mk,v 1.6 2020/11/12 19:46:36 rillig Exp $
|
||||
# $NetBSD: directive-elif.mk,v 1.7 2020/12/19 19:49:01 rillig Exp $
|
||||
#
|
||||
# Tests for the .elif directive.
|
||||
#
|
||||
# Misspellings of the .elif directive are not always detected. They are only
|
||||
# detected if the conditional branch directly above it is taken. In all other
|
||||
# cases, make skips over the skipped branch as fast as possible, looking only
|
||||
# at the initial '.' of the line and whether the directive is one of the known
|
||||
# conditional directives. All other directives are silently ignored, as they
|
||||
# could be variable assignments or dependency declarations as well, and
|
||||
# deciding this would cost time.
|
||||
|
||||
|
||||
# TODO: Implementation
|
||||
|
||||
.info begin .elif misspellings tests, part 1
|
||||
|
||||
# Misspelling '.elsif' below an .if branch that is not taken.
|
||||
.if 0
|
||||
. info This branch is not taken.
|
||||
# As of 2020-12-19, the misspelling is not recognized as a conditional
|
||||
# directive and is thus silently skipped.
|
||||
#
|
||||
# Since the .if condition evaluated to false, this whole branch is not taken.
|
||||
.elsif 0
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info This branch is not taken.
|
||||
# Even if the misspelling were detected, the branch would not be taken
|
||||
# since the condition of the '.elsif' evaluates to false as well.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspelling '.elsif' below an .if branch that is not taken.
|
||||
.if 0
|
||||
. info This branch is not taken.
|
||||
# As of 2020-12-19, the misspelling is not recognized as a conditional
|
||||
# directive and is thus silently skipped. Since the .if condition evaluated
|
||||
# to false, this whole branch is not taken.
|
||||
.elsif 1
|
||||
. info XXX: This misspelling is not detected.
|
||||
# If the misspelling were detected, this branch would be taken.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspelling '.elsif' below an .if branch that is taken.
|
||||
.if 1
|
||||
. info 1-then
|
||||
.elif 1 # ok
|
||||
. info 1-elif
|
||||
.elsif 1 # oops: misspelled
|
||||
. info 1-elsif
|
||||
.elseif 1 # oops: misspelled
|
||||
. info 1-elseif
|
||||
# This misspelling is in an active branch and is therefore detected.
|
||||
.elsif 0
|
||||
# The only thing that make detects here is a misspelled directive, make
|
||||
# doesn't recognize that it was meant to be a conditional directive.
|
||||
# Therefore the branch continues here, even though the '.elsif' condition
|
||||
# evaluates to false.
|
||||
. info This branch is taken.
|
||||
.endif
|
||||
|
||||
.info begin .elif misspellings tests, part 2
|
||||
|
||||
# Misspelling '.elsif' below an .if branch that is taken.
|
||||
.if 1
|
||||
# As of 2020-12-19, the misspelling is in an active branch and is therefore
|
||||
# detected.
|
||||
.elsif 1
|
||||
# Since both conditions evaluate to true, this branch is taken no matter
|
||||
# whether make detects a misspelling or not.
|
||||
. info This branch is taken.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspelling '.elsif' in a skipped branch below a branch that was taken.
|
||||
.if 1
|
||||
. info This branch is taken.
|
||||
.elif 0
|
||||
. info This branch is not taken.
|
||||
.elsif 1
|
||||
. info XXX: This misspelling is not detected.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspelling '.elsif' in an .else branch that is not taken.
|
||||
.if 1
|
||||
.else
|
||||
. info This branch is not taken.
|
||||
.elsif 1
|
||||
. info XXX: This misspelling is not detected.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspelling '.elsif' in an .else branch that is taken.
|
||||
.if 0
|
||||
. info 0-then
|
||||
.else
|
||||
.elsif 1
|
||||
. info This misspelling is detected.
|
||||
. info This branch is taken because of the .else.
|
||||
.endif
|
||||
|
||||
|
||||
# Misspellings for .elif in a .elif branch that is not taken.
|
||||
.if 0
|
||||
. info This branch is not taken.
|
||||
.elif 0 # ok
|
||||
. info 0-elif
|
||||
.elsif 0 # oops: misspelled
|
||||
. info 0-elsif
|
||||
.elseif 0 # oops: misspelled
|
||||
. info 0-elseif
|
||||
. info This branch is not taken.
|
||||
.elsif 0
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info This branch is not taken.
|
||||
.elseif 0
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info This branch is not taken.
|
||||
.endif
|
||||
|
||||
.info begin .elif misspellings tests, part 3
|
||||
.if 0
|
||||
. info 0-then
|
||||
.elsif 0 # oops: misspelled
|
||||
. info 0-elsif
|
||||
.endif
|
||||
.if 0
|
||||
. info 0-then
|
||||
.elseif 0 # oops: misspelled
|
||||
. info 0-elseif
|
||||
.endif
|
||||
|
||||
.info which branch is taken on misspelling after false?
|
||||
.info What happens on misspelling in a skipped branch?
|
||||
.if 0
|
||||
. info 0-then
|
||||
.elsif 1
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info 1-elsif
|
||||
.elsif 2
|
||||
. info XXX: This misspelling is not detected.
|
||||
. info 2-elsif
|
||||
.else
|
||||
. info else
|
||||
.endif
|
||||
|
||||
.info which branch is taken on misspelling after true?
|
||||
.info What happens on misspelling in a taken branch?
|
||||
.if 1
|
||||
. info 1-then
|
||||
.elsif 1
|
||||
@ -65,7 +135,7 @@
|
||||
|
||||
.if 1
|
||||
.else
|
||||
# Expect: "warning: if-less elif"
|
||||
# Expect: "warning: extra elif"
|
||||
.elif
|
||||
.endif
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
make: "directive-else.mk" line 11: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 12: ok
|
||||
make: "directive-else.mk" line 16: ok
|
||||
make: "directive-else.mk" line 17: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 22: if-less else
|
||||
make: "directive-else.mk" line 28: ok
|
||||
make: "directive-else.mk" line 29: warning: extra else
|
||||
make: "directive-else.mk" line 42: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 14: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 15: ok
|
||||
make: "directive-else.mk" line 19: ok
|
||||
make: "directive-else.mk" line 21: The .else directive does not take arguments.
|
||||
make: "directive-else.mk" line 26: if-less else
|
||||
make: "directive-else.mk" line 32: ok
|
||||
make: "directive-else.mk" line 33: warning: extra else
|
||||
make: "directive-else.mk" line 45: The .else directive does not take arguments.
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,19 +1,23 @@
|
||||
# $NetBSD: directive-else.mk,v 1.6 2020/11/13 09:01:59 rillig Exp $
|
||||
# $NetBSD: directive-else.mk,v 1.7 2020/12/14 22:17:11 rillig Exp $
|
||||
#
|
||||
# Tests for the .else directive.
|
||||
#
|
||||
# Since 2020-11-13, an '.else' followed by extraneous text generates a parse
|
||||
# error in -dL (lint) mode.
|
||||
#
|
||||
# Since 2020-12-15, an '.else' followed by extraneous text always generates
|
||||
# a parse error.
|
||||
|
||||
.MAKEFLAGS: -dL # To enable the check for ".else <cond>"
|
||||
|
||||
# The .else directive does not take any arguments.
|
||||
# As of 2020-08-29, make doesn't warn about this.
|
||||
.if 0
|
||||
. warning must not be reached
|
||||
# The .else directive does not take any arguments.
|
||||
.else 123
|
||||
. info ok
|
||||
.endif
|
||||
|
||||
.if 1
|
||||
. info ok
|
||||
# The .else directive does not take any arguments.
|
||||
.else 123
|
||||
. warning must not be reached
|
||||
.endif
|
||||
@ -37,7 +41,6 @@
|
||||
.endif
|
||||
|
||||
# A variable expression does count as an argument, even if it is empty.
|
||||
# XXX: This should be a parse error.
|
||||
.if 0
|
||||
.else ${:U}
|
||||
.endif
|
||||
|
4
unit-tests/directive-endfor.exp
Normal file
4
unit-tests/directive-endfor.exp
Normal file
@ -0,0 +1,4 @@
|
||||
make: "directive-endfor.mk" line 9: for-less endfor
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
9
unit-tests/directive-endfor.mk
Normal file
9
unit-tests/directive-endfor.mk
Normal file
@ -0,0 +1,9 @@
|
||||
# $NetBSD: directive-endfor.mk,v 1.1 2020/12/30 14:50:08 rillig Exp $
|
||||
#
|
||||
# Test for the directive .endfor, which ends a .for loop.
|
||||
#
|
||||
# See also:
|
||||
# directive-for.mk
|
||||
|
||||
# An .endfor without a corresponding .for is a parse error.
|
||||
.endfor
|
@ -1 +1,8 @@
|
||||
exit status 0
|
||||
make: "directive-endif.mk" line 18: The .endif directive does not take arguments.
|
||||
make: "directive-endif.mk" line 23: The .endif directive does not take arguments.
|
||||
make: "directive-endif.mk" line 33: The .endif directive does not take arguments.
|
||||
make: "directive-endif.mk" line 39: The .endif directive does not take arguments.
|
||||
make: "directive-endif.mk" line 45: Unknown directive "endifx"
|
||||
make: Fatal errors encountered -- cannot continue
|
||||
make: stopped in unit-tests
|
||||
exit status 1
|
||||
|
@ -1,7 +1,10 @@
|
||||
# $NetBSD: directive-endif.mk,v 1.3 2020/11/12 22:40:11 rillig Exp $
|
||||
# $NetBSD: directive-endif.mk,v 1.5 2020/12/14 21:56:17 rillig Exp $
|
||||
#
|
||||
# Tests for the .endif directive.
|
||||
#
|
||||
# Since 2020-12-15, the .endif directive no longer accepts arguments.
|
||||
# The manual page had never allowed that, but the code didn't check it.
|
||||
#
|
||||
# See also:
|
||||
# Cond_EvalLine
|
||||
|
||||
@ -10,18 +13,37 @@
|
||||
.MAKEFLAGS: -dL
|
||||
|
||||
# Error: .endif does not take arguments
|
||||
# XXX: Missing error message
|
||||
.if 0
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif 0
|
||||
|
||||
# Error: .endif does not take arguments
|
||||
# XXX: Missing error message
|
||||
.if 1
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif 1
|
||||
|
||||
# Comments are allowed after an '.endif'.
|
||||
.if 2
|
||||
.endif # comment
|
||||
|
||||
# Only whitespace and comments are allowed after an '.endif', but nothing
|
||||
# else.
|
||||
.if 1
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif0
|
||||
|
||||
# Only whitespace and comments are allowed after an '.endif', but nothing
|
||||
# else.
|
||||
.if 1
|
||||
# Since 2020-12-15, complain about the extra text after the 'endif'.
|
||||
.endif/
|
||||
|
||||
# After an '.endif', no other letter must occur. This 'endifx' is not
|
||||
# parsed as an 'endif', therefore another '.endif' must follow to balance
|
||||
# the directives.
|
||||
.if 1
|
||||
.endifx
|
||||
.endif # to close the preceding '.if'
|
||||
|
||||
all:
|
||||
@:;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user